github-api-helper.ts 4.1 KB

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