github-api-helper.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import * as assert from 'assert'
  2. import * as core from '@actions/core'
  3. import * as fs from 'fs'
  4. import * as github from '@actions/github'
  5. import * as io from '@actions/io'
  6. import * as path from 'path'
  7. import * as retryHelper from './retry-helper'
  8. import * as toolCache from '@actions/tool-cache'
  9. import {v4 as uuid} from 'uuid'
  10. import {getServerApiUrl} from './url-helper'
  11. const IS_WINDOWS = process.platform === 'win32'
  12. export async function downloadRepository(
  13. authToken: string,
  14. owner: string,
  15. repo: string,
  16. ref: string,
  17. commit: string,
  18. repositoryPath: string,
  19. baseUrl?: string
  20. ): Promise<void> {
  21. // Determine the default branch
  22. if (!ref && !commit) {
  23. core.info('Determining the default branch')
  24. ref = await getDefaultBranch(authToken, owner, repo, baseUrl)
  25. }
  26. // Download the archive
  27. let archiveData = await retryHelper.execute(async () => {
  28. core.info('Downloading the archive')
  29. return await downloadArchive(authToken, owner, repo, ref, commit, baseUrl)
  30. })
  31. // Write archive to disk
  32. core.info('Writing archive to disk')
  33. const uniqueId = uuid()
  34. const archivePath = IS_WINDOWS
  35. ? path.join(repositoryPath, `${uniqueId}.zip`)
  36. : path.join(repositoryPath, `${uniqueId}.tar.gz`)
  37. await fs.promises.writeFile(archivePath, archiveData)
  38. archiveData = Buffer.from('') // Free memory
  39. // Extract archive
  40. core.info('Extracting the archive')
  41. const extractPath = path.join(repositoryPath, uniqueId)
  42. await io.mkdirP(extractPath)
  43. if (IS_WINDOWS) {
  44. await toolCache.extractZip(archivePath, extractPath)
  45. } else {
  46. await toolCache.extractTar(archivePath, extractPath)
  47. }
  48. await io.rmRF(archivePath)
  49. // Determine the path of the repository content. The archive contains
  50. // a top-level folder and the repository content is inside.
  51. const archiveFileNames = await fs.promises.readdir(extractPath)
  52. assert.ok(
  53. archiveFileNames.length == 1,
  54. 'Expected exactly one directory inside archive'
  55. )
  56. const archiveVersion = archiveFileNames[0] // The top-level folder name includes the short SHA
  57. core.info(`Resolved version ${archiveVersion}`)
  58. const tempRepositoryPath = path.join(extractPath, archiveVersion)
  59. // Move the files
  60. for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
  61. const sourcePath = path.join(tempRepositoryPath, fileName)
  62. const targetPath = path.join(repositoryPath, fileName)
  63. if (IS_WINDOWS) {
  64. await io.cp(sourcePath, targetPath, {recursive: true}) // Copy on Windows (Windows Defender may have a lock)
  65. } else {
  66. await io.mv(sourcePath, targetPath)
  67. }
  68. }
  69. await io.rmRF(extractPath)
  70. }
  71. /**
  72. * Looks up the default branch name
  73. */
  74. export async function getDefaultBranch(
  75. authToken: string,
  76. owner: string,
  77. repo: string,
  78. baseUrl?: string
  79. ): Promise<string> {
  80. return await retryHelper.execute(async () => {
  81. core.info('Retrieving the default branch name')
  82. const octokit = github.getOctokit(authToken, {
  83. baseUrl: getServerApiUrl(baseUrl)
  84. })
  85. let result: string
  86. try {
  87. // Get the default branch from the repo info
  88. const response = await octokit.rest.repos.get({owner, repo})
  89. result = response.data.default_branch
  90. assert.ok(result, 'default_branch cannot be empty')
  91. } catch (err) {
  92. // Handle .wiki repo
  93. if (
  94. (err as any)?.status === 404 &&
  95. repo.toUpperCase().endsWith('.WIKI')
  96. ) {
  97. result = 'master'
  98. }
  99. // Otherwise error
  100. else {
  101. throw err
  102. }
  103. }
  104. // Print the default branch
  105. core.info(`Default branch '${result}'`)
  106. // Prefix with 'refs/heads'
  107. if (!result.startsWith('refs/')) {
  108. result = `refs/heads/${result}`
  109. }
  110. return result
  111. })
  112. }
  113. async function downloadArchive(
  114. authToken: string,
  115. owner: string,
  116. repo: string,
  117. ref: string,
  118. commit: string,
  119. baseUrl?: string
  120. ): Promise<Buffer> {
  121. const octokit = github.getOctokit(authToken, {
  122. baseUrl: getServerApiUrl(baseUrl)
  123. })
  124. const download = IS_WINDOWS
  125. ? octokit.rest.repos.downloadZipballArchive
  126. : octokit.rest.repos.downloadTarballArchive
  127. const response = await download({
  128. owner: owner,
  129. repo: repo,
  130. ref: commit || ref
  131. })
  132. return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer
  133. }