github-api-helper.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import * as assert from 'assert'
  2. import * as core from '@actions/core'
  3. import * as exec from '@actions/exec'
  4. import * as fs from 'fs'
  5. import * as github from '@actions/github'
  6. import * as https from 'https'
  7. import * as io from '@actions/io'
  8. import * as path from 'path'
  9. import * as refHelper from './ref-helper'
  10. import * as retryHelper from './retry-helper'
  11. import * as toolCache from '@actions/tool-cache'
  12. import {ExecOptions} from '@actions/exec/lib/interfaces'
  13. import {IncomingMessage} from 'http'
  14. import {RequestOptions, ReposGetArchiveLinkParams} from '@octokit/rest'
  15. import {WriteStream} from 'fs'
  16. const IS_WINDOWS = process.platform === 'win32'
  17. export async function downloadRepository(
  18. accessToken: string,
  19. owner: string,
  20. repo: string,
  21. ref: string,
  22. commit: string,
  23. repositoryPath: string
  24. ): Promise<void> {
  25. // Determine archive path
  26. const runnerTemp = process.env['RUNNER_TEMP'] as string
  27. assert.ok(runnerTemp, 'RUNNER_TEMP not defined')
  28. const archivePath = path.join(runnerTemp, 'checkout.tar.gz')
  29. // Ensure file does not exist
  30. core.debug(`Ensuring archive file does not exist: ${archivePath}`)
  31. await io.rmRF(archivePath)
  32. // Download the archive
  33. let archiveData = await retryHelper.execute(async () => {
  34. core.info('Downloading the archive using the REST API')
  35. return await downloadArchive(accessToken, owner, repo, ref, commit)
  36. })
  37. // Write archive to disk
  38. core.info('Writing archive to disk')
  39. await fs.promises.writeFile(archivePath, archiveData)
  40. archiveData = Buffer.from('') // Free memory
  41. // // Get the archive URL using the REST API
  42. // await retryHelper.execute(async () => {
  43. // // Prepare the archive stream
  44. // core.debug(`Preparing the archive stream: ${archivePath}`)
  45. // await io.rmRF(archivePath)
  46. // const fileStream = fs.createWriteStream(archivePath)
  47. // const fileStreamClosed = getFileClosedPromise(fileStream)
  48. // try {
  49. // // Get the archive URL
  50. // core.info('Getting archive URL')
  51. // const archiveUrl = await getArchiveUrl(
  52. // accessToken,
  53. // owner,
  54. // repo,
  55. // ref,
  56. // commit
  57. // )
  58. // // Download the archive
  59. // core.info('Downloading the archive') // Do not print the archive URL because it has an embedded token
  60. // await downloadFile(archiveUrl, fileStream)
  61. // } finally {
  62. // fileStream.end()
  63. // await fileStreamClosed
  64. // }
  65. // })
  66. // Extract archive
  67. const extractPath = path.join(runnerTemp, `checkout`)
  68. await io.rmRF(extractPath)
  69. await io.mkdirP(extractPath)
  70. if (IS_WINDOWS) {
  71. await toolCache.extractZip(archivePath, extractPath)
  72. } else {
  73. await toolCache.extractTar(archivePath, extractPath)
  74. }
  75. // Determine the real directory to copy (ignore extra dir at root of the archive)
  76. const archiveFileNames = await fs.promises.readdir(extractPath)
  77. assert.ok(
  78. archiveFileNames.length == 1,
  79. 'Expected exactly one directory inside archive'
  80. )
  81. const extraDirectoryName = archiveFileNames[0]
  82. core.info(`Resolved ${extraDirectoryName}`) // contains the short SHA
  83. const tempRepositoryPath = path.join(extractPath, extraDirectoryName)
  84. // Move the files
  85. for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
  86. const sourcePath = path.join(tempRepositoryPath, fileName)
  87. const targetPath = path.join(repositoryPath, fileName)
  88. await io.mv(sourcePath, targetPath)
  89. }
  90. await exec.exec(`find .`, [], {
  91. cwd: repositoryPath
  92. } as ExecOptions)
  93. }
  94. async function downloadArchive(
  95. accessToken: string,
  96. owner: string,
  97. repo: string,
  98. ref: string,
  99. commit: string
  100. ): Promise<Buffer> {
  101. const octokit = new github.GitHub(accessToken)
  102. const params: ReposGetArchiveLinkParams = {
  103. owner: owner,
  104. repo: repo,
  105. archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
  106. ref: refHelper.getDownloadRef(ref, commit)
  107. }
  108. const response = await octokit.repos.getArchiveLink(params)
  109. console.log('GOT THE RESPONSE')
  110. console.log(`status=${response.status}`)
  111. console.log(`headers=${JSON.stringify(response.headers)}`)
  112. console.log(`data=${JSON.stringify(response.data)}`)
  113. if (response.status != 200) {
  114. throw new Error(
  115. `Unexpected response from GitHub API. Status: '${response.status}'`
  116. )
  117. }
  118. return Buffer.from(response.data) // response.data is ArrayBuffer
  119. }
  120. // async function getArchiveUrl(
  121. // accessToken: string,
  122. // owner: string,
  123. // repo: string,
  124. // ref: string,
  125. // commit: string
  126. // ): Promise<string> {
  127. // const octokit = new github.GitHub(accessToken)
  128. // const params: RequestOptions & ReposGetArchiveLinkParams = {
  129. // method: 'HEAD',
  130. // owner: owner,
  131. // repo: repo,
  132. // archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
  133. // ref: refHelper.getDownloadRef(ref, commit)
  134. // }
  135. // const response = await octokit.repos.getArchiveLink(params)
  136. // console.log('GOT THE RESPONSE')
  137. // console.log(`status=${response.status}`)
  138. // console.log(`headers=${JSON.stringify(response.headers)}`)
  139. // console.log(`data=${JSON.stringify(response.data)}`)
  140. // if (response.status != 200) {
  141. // throw new Error(
  142. // `Unexpected response from GitHub API. Status: '${response.status}'`
  143. // )
  144. // }
  145. // console.log('GETTING THE LOCATION')
  146. // const archiveUrl = response.headers['Location'] // Do not print the archive URL because it has an embedded token
  147. // assert.ok(
  148. // archiveUrl,
  149. // `Expected GitHub API response to contain 'Location' header`
  150. // )
  151. // return archiveUrl
  152. // }
  153. // function downloadFile(url: string, fileStream: WriteStream): Promise<void> {
  154. // return new Promise((resolve, reject) => {
  155. // try {
  156. // https.get(url, (response: IncomingMessage) => {
  157. // if (response.statusCode != 200) {
  158. // reject(`Request failed with status '${response.statusCode}'`)
  159. // response.resume() // Consume response data to free up memory
  160. // return
  161. // }
  162. // response.on('data', chunk => {
  163. // fileStream.write(chunk)
  164. // })
  165. // response.on('end', () => {
  166. // resolve()
  167. // })
  168. // response.on('error', err => {
  169. // reject(err)
  170. // })
  171. // })
  172. // } catch (err) {
  173. // reject(err)
  174. // }
  175. // })
  176. // }
  177. // function getFileClosedPromise(stream: WriteStream): Promise<void> {
  178. // return new Promise((resolve, reject) => {
  179. // stream.on('error', err => {
  180. // reject(err)
  181. // })
  182. // stream.on('finish', () => {
  183. // resolve()
  184. // })
  185. // })
  186. // }