Read-only mirror — the public repositories of the Forgejo instance, refreshed every 10 minutes. The live instance (fg.dieschoenewolke.de) is reachable only over the VPN; clone here with https://git.dieschoenewolke.de/git/<owner>/<repo>.git.
summaryrefslogtreecommitdiff
path: root/src/github-api-helper.ts
blob: dc308702e0357cfbcddaa4c5a630e019f2dab29f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import * as assert from 'assert'
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import * as fs from 'fs'
import * as github from '@actions/github'
import * as https from 'https'
import * as io from '@actions/io'
import * as path from 'path'
import * as refHelper from './ref-helper'
import * as retryHelper from './retry-helper'
import * as toolCache from '@actions/tool-cache'
import {ExecOptions} from '@actions/exec/lib/interfaces'
import {IncomingMessage} from 'http'
import {RequestOptions, ReposGetArchiveLinkParams} from '@octokit/rest'
import {WriteStream} from 'fs'

const IS_WINDOWS = process.platform === 'win32'

export async function downloadRepository(
  accessToken: string,
  owner: string,
  repo: string,
  ref: string,
  commit: string,
  repositoryPath: string
): Promise<void> {
  // Determine archive path
  const runnerTemp = process.env['RUNNER_TEMP'] as string
  assert.ok(runnerTemp, 'RUNNER_TEMP not defined')
  const archivePath = path.join(runnerTemp, 'checkout.tar.gz')

  // Ensure file does not exist
  core.debug(`Ensuring archive file does not exist: ${archivePath}`)
  await io.rmRF(archivePath)

  // Download the archive
  let archiveData = await retryHelper.execute(async () => {
    core.info('Downloading the archive using the REST API')
    return await downloadArchive(accessToken, owner, repo, ref, commit)
  })

  // Write archive to disk
  core.info('Writing archive to disk')
  await fs.promises.writeFile(archivePath, archiveData)
  archiveData = Buffer.from('') // Free memory

  // // Get the archive URL using the REST API
  // await retryHelper.execute(async () => {
  //   // Prepare the archive stream
  //   core.debug(`Preparing the archive stream: ${archivePath}`)
  //   await io.rmRF(archivePath)
  //   const fileStream = fs.createWriteStream(archivePath)
  //   const fileStreamClosed = getFileClosedPromise(fileStream)

  //   try {
  //     // Get the archive URL
  //     core.info('Getting archive URL')
  //     const archiveUrl = await getArchiveUrl(
  //       accessToken,
  //       owner,
  //       repo,
  //       ref,
  //       commit
  //     )

  //     // Download the archive
  //     core.info('Downloading the archive') // Do not print the archive URL because it has an embedded token
  //     await downloadFile(archiveUrl, fileStream)
  //   } finally {
  //     fileStream.end()
  //     await fileStreamClosed
  //   }
  // })

  // Extract archive
  const extractPath = path.join(runnerTemp, `checkout`)
  await io.rmRF(extractPath)
  await io.mkdirP(extractPath)
  if (IS_WINDOWS) {
    await toolCache.extractZip(archivePath, extractPath)
  } else {
    await toolCache.extractTar(archivePath, extractPath)
  }

  // Determine the real directory to copy (ignore extra dir at root of the archive)
  const archiveFileNames = await fs.promises.readdir(extractPath)
  assert.ok(
    archiveFileNames.length == 1,
    'Expected exactly one directory inside archive'
  )
  const extraDirectoryName = archiveFileNames[0]
  core.info(`Resolved ${extraDirectoryName}`) // contains the short SHA
  const tempRepositoryPath = path.join(extractPath, extraDirectoryName)

  // Move the files
  for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
    const sourcePath = path.join(tempRepositoryPath, fileName)
    const targetPath = path.join(repositoryPath, fileName)
    await io.mv(sourcePath, targetPath)
  }

  await exec.exec(`find .`, [], {
    cwd: repositoryPath
  } as ExecOptions)
}

async function downloadArchive(
  accessToken: string,
  owner: string,
  repo: string,
  ref: string,
  commit: string
): Promise<Buffer> {
  const octokit = new github.GitHub(accessToken)
  const params: ReposGetArchiveLinkParams = {
    owner: owner,
    repo: repo,
    archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
    ref: refHelper.getDownloadRef(ref, commit)
  }
  const response = await octokit.repos.getArchiveLink(params)
  console.log('GOT THE RESPONSE')
  console.log(`status=${response.status}`)
  console.log(`headers=${JSON.stringify(response.headers)}`)
  console.log(`data=${JSON.stringify(response.data)}`)
  if (response.status != 200) {
    throw new Error(
      `Unexpected response from GitHub API. Status: '${response.status}'`
    )
  }

  return Buffer.from(response.data) // response.data is ArrayBuffer
}

// async function getArchiveUrl(
//   accessToken: string,
//   owner: string,
//   repo: string,
//   ref: string,
//   commit: string
// ): Promise<string> {
//   const octokit = new github.GitHub(accessToken)
//   const params: RequestOptions & ReposGetArchiveLinkParams = {
//     method: 'HEAD',
//     owner: owner,
//     repo: repo,
//     archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
//     ref: refHelper.getDownloadRef(ref, commit)
//   }
//   const response = await octokit.repos.getArchiveLink(params)
//   console.log('GOT THE RESPONSE')
//   console.log(`status=${response.status}`)
//   console.log(`headers=${JSON.stringify(response.headers)}`)
//   console.log(`data=${JSON.stringify(response.data)}`)
//   if (response.status != 200) {
//     throw new Error(
//       `Unexpected response from GitHub API. Status: '${response.status}'`
//     )
//   }
//   console.log('GETTING THE LOCATION')
//   const archiveUrl = response.headers['Location'] // Do not print the archive URL because it has an embedded token
//   assert.ok(
//     archiveUrl,
//     `Expected GitHub API response to contain 'Location' header`
//   )
//   return archiveUrl
// }

// function downloadFile(url: string, fileStream: WriteStream): Promise<void> {
//   return new Promise((resolve, reject) => {
//     try {
//       https.get(url, (response: IncomingMessage) => {
//         if (response.statusCode != 200) {
//           reject(`Request failed with status '${response.statusCode}'`)
//           response.resume() // Consume response data to free up memory
//           return
//         }

//         response.on('data', chunk => {
//           fileStream.write(chunk)
//         })
//         response.on('end', () => {
//           resolve()
//         })
//         response.on('error', err => {
//           reject(err)
//         })
//       })
//     } catch (err) {
//       reject(err)
//     }
//   })
// }

// function getFileClosedPromise(stream: WriteStream): Promise<void> {
//   return new Promise((resolve, reject) => {
//     stream.on('error', err => {
//       reject(err)
//     })
//     stream.on('finish', () => {
//       resolve()
//     })
//   })
// }