github-api-helper.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. await 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 = undefined
  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. // return Buffer.from(response.data) // response.data is ArrayBuffer
  67. // // Download the archive
  68. // core.info('Downloading the archive') // Do not print the URL since it contains a token to download the archive
  69. // await downloadFile(archiveUrl, archivePath)
  70. // // console.log(`status=${response.status}`)
  71. // // console.log(`headers=${JSON.stringify(response.headers)}`)
  72. // // console.log(`data=${response.data}`)
  73. // // console.log(`data=${JSON.stringify(response.data)}`)
  74. // // for (const key of Object.keys(response.data)) {
  75. // // console.log(`data['${key}']=${response.data[key]}`)
  76. // // }
  77. // // Write archive to file
  78. // const runnerTemp = process.env['RUNNER_TEMP'] as string
  79. // assert.ok(runnerTemp, 'RUNNER_TEMP not defined')
  80. // const archivePath = path.join(runnerTemp, 'checkout.tar.gz')
  81. // await io.rmRF(archivePath)
  82. // await fs.promises.writeFile(archivePath, raw)
  83. // // await exec.exec(`ls -la "${archiveFile}"`, [], {
  84. // // cwd: repositoryPath
  85. // // } as ExecOptions)
  86. // Extract archive
  87. const extractPath = path.join(
  88. runnerTemp,
  89. `checkout-archive${IS_WINDOWS ? '.zip' : '.tar.gz'}`
  90. )
  91. await io.rmRF(extractPath)
  92. await io.mkdirP(extractPath)
  93. if (IS_WINDOWS) {
  94. await toolCache.extractZip(archivePath, extractPath)
  95. } else {
  96. await toolCache.extractTar(archivePath, extractPath)
  97. }
  98. // await exec.exec(`tar -xzf "${archiveFile}"`, [], {
  99. // cwd: extractPath
  100. // } as ExecOptions)
  101. // Determine the real directory to copy (ignore extra dir at root of the archive)
  102. const archiveFileNames = await fs.promises.readdir(extractPath)
  103. assert.ok(
  104. archiveFileNames.length == 1,
  105. 'Expected exactly one directory inside archive'
  106. )
  107. const extraDirectoryName = archiveFileNames[0]
  108. core.info(`Resolved ${extraDirectoryName}`) // contains the short SHA
  109. const tempRepositoryPath = path.join(extractPath, extraDirectoryName)
  110. // Move the files
  111. for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
  112. const sourcePath = path.join(tempRepositoryPath, fileName)
  113. const targetPath = path.join(repositoryPath, fileName)
  114. await io.mv(sourcePath, targetPath)
  115. }
  116. await exec.exec(`find .`, [], {
  117. cwd: repositoryPath
  118. } as ExecOptions)
  119. }
  120. async function downloadArchive(
  121. accessToken: string,
  122. owner: string,
  123. repo: string,
  124. ref: string,
  125. commit: string
  126. ): Promise<Buffer> {
  127. const octokit = new github.GitHub(accessToken)
  128. const params: ReposGetArchiveLinkParams = {
  129. owner: owner,
  130. repo: repo,
  131. archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
  132. ref: refHelper.getDownloadRef(ref, commit)
  133. }
  134. const response = await octokit.repos.getArchiveLink(params)
  135. console.log('GOT THE RESPONSE')
  136. console.log(`status=${response.status}`)
  137. console.log(`headers=${JSON.stringify(response.headers)}`)
  138. console.log(`data=${JSON.stringify(response.data)}`)
  139. if (response.status != 200) {
  140. throw new Error(
  141. `Unexpected response from GitHub API. Status: '${response.status}'`
  142. )
  143. }
  144. return Buffer.from(response.data) // response.data is ArrayBuffer
  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. // async function getArchiveUrl(
  154. // accessToken: string,
  155. // owner: string,
  156. // repo: string,
  157. // ref: string,
  158. // commit: string
  159. // ): Promise<string> {
  160. // const octokit = new github.GitHub(accessToken)
  161. // const params: RequestOptions & ReposGetArchiveLinkParams = {
  162. // method: 'HEAD',
  163. // owner: owner,
  164. // repo: repo,
  165. // archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
  166. // ref: refHelper.getDownloadRef(ref, commit)
  167. // }
  168. // const response = await octokit.repos.getArchiveLink(params)
  169. // console.log('GOT THE RESPONSE')
  170. // console.log(`status=${response.status}`)
  171. // console.log(`headers=${JSON.stringify(response.headers)}`)
  172. // console.log(`data=${JSON.stringify(response.data)}`)
  173. // if (response.status != 200) {
  174. // throw new Error(
  175. // `Unexpected response from GitHub API. Status: '${response.status}'`
  176. // )
  177. // }
  178. // console.log('GETTING THE LOCATION')
  179. // const archiveUrl = response.headers['Location'] // Do not print the archive URL because it has an embedded token
  180. // assert.ok(
  181. // archiveUrl,
  182. // `Expected GitHub API response to contain 'Location' header`
  183. // )
  184. // return archiveUrl
  185. // }
  186. // function downloadFile(url: string, fileStream: WriteStream): Promise<void> {
  187. // return new Promise((resolve, reject) => {
  188. // try {
  189. // https.get(url, (response: IncomingMessage) => {
  190. // if (response.statusCode != 200) {
  191. // reject(`Request failed with status '${response.statusCode}'`)
  192. // response.resume() // Consume response data to free up memory
  193. // return
  194. // }
  195. // response.on('data', chunk => {
  196. // fileStream.write(chunk)
  197. // })
  198. // response.on('end', () => {
  199. // resolve()
  200. // })
  201. // response.on('error', err => {
  202. // reject(err)
  203. // })
  204. // })
  205. // } catch (err) {
  206. // reject(err)
  207. // }
  208. // })
  209. // }
  210. // function getFileClosedPromise(stream: WriteStream): Promise<void> {
  211. // return new Promise((resolve, reject) => {
  212. // stream.on('error', err => {
  213. // reject(err)
  214. // })
  215. // stream.on('finish', () => {
  216. // resolve()
  217. // })
  218. // })
  219. // }