github-api-helper.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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 {ReposGetArchiveLinkParams} from '@octokit/rest'
  11. import HttpsProxyAgent from 'https-proxy-agent'
  12. const IS_WINDOWS = process.platform === 'win32'
  13. export async function downloadRepository(
  14. authToken: string,
  15. owner: string,
  16. repo: string,
  17. ref: string,
  18. commit: string,
  19. repositoryPath: string
  20. ): Promise<void> {
  21. // Download the archive
  22. let archiveData = await retryHelper.execute(async () => {
  23. core.info('Downloading the archive')
  24. return await downloadArchive(authToken, owner, repo, ref, commit)
  25. })
  26. // Write archive to disk
  27. core.info('Writing archive to disk')
  28. const uniqueId = uuid()
  29. const archivePath = path.join(repositoryPath, `${uniqueId}.tar.gz`)
  30. await fs.promises.writeFile(archivePath, archiveData)
  31. archiveData = Buffer.from('') // Free memory
  32. // Extract archive
  33. core.info('Extracting the archive')
  34. const extractPath = path.join(repositoryPath, uniqueId)
  35. await io.mkdirP(extractPath)
  36. if (IS_WINDOWS) {
  37. await toolCache.extractZip(archivePath, extractPath)
  38. } else {
  39. await toolCache.extractTar(archivePath, extractPath)
  40. }
  41. io.rmRF(archivePath)
  42. // Determine the path of the repository content. The archive contains
  43. // a top-level folder and the repository content is inside.
  44. const archiveFileNames = await fs.promises.readdir(extractPath)
  45. assert.ok(
  46. archiveFileNames.length == 1,
  47. 'Expected exactly one directory inside archive'
  48. )
  49. const archiveVersion = archiveFileNames[0] // The top-level folder name includes the short SHA
  50. core.info(`Resolved version ${archiveVersion}`)
  51. const tempRepositoryPath = path.join(extractPath, archiveVersion)
  52. // Move the files
  53. for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
  54. const sourcePath = path.join(tempRepositoryPath, fileName)
  55. const targetPath = path.join(repositoryPath, fileName)
  56. if (IS_WINDOWS) {
  57. await io.cp(sourcePath, targetPath, {recursive: true}) // Copy on Windows (Windows Defender may have a lock)
  58. } else {
  59. await io.mv(sourcePath, targetPath)
  60. }
  61. }
  62. io.rmRF(extractPath)
  63. }
  64. async function downloadArchive(
  65. authToken: string,
  66. owner: string,
  67. repo: string,
  68. ref: string,
  69. commit: string
  70. ): Promise<Buffer> {
  71. const octokit = createOctokit(authToken)
  72. const params: ReposGetArchiveLinkParams = {
  73. owner: owner,
  74. repo: repo,
  75. archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
  76. ref: commit || ref
  77. }
  78. const response = await octokit.repos.getArchiveLink(params)
  79. if (response.status != 200) {
  80. throw new Error(
  81. `Unexpected response from GitHub API. Status: ${response.status}, Data: ${response.data}`
  82. )
  83. }
  84. return Buffer.from(response.data) // response.data is ArrayBuffer
  85. }
  86. function createOctokit(authToken: string): github.GitHub {
  87. let proxyVar: string =
  88. process.env['https_proxy'] || process.env['HTTPS_PROXY'] || ''
  89. if (!proxyVar) {
  90. return new github.GitHub(authToken)
  91. }
  92. let noProxy: string = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
  93. let bypass: boolean = false
  94. if (noProxy) {
  95. let bypassList = noProxy.split(',')
  96. for (let i = 0; i < bypassList.length; i++) {
  97. let item = bypassList[i]
  98. if (
  99. item &&
  100. typeof item === 'string' &&
  101. item.trim().toLocaleLowerCase() === 'github.com'
  102. ) {
  103. bypass = true
  104. break
  105. }
  106. }
  107. }
  108. if (bypass) {
  109. return new github.GitHub(authToken)
  110. } else {
  111. return new github.GitHub(authToken, {
  112. request: {agent: new HttpsProxyAgent(proxyVar)}
  113. })
  114. }
  115. }