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
|
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 {ReposGetArchiveLinkParams} from '@octokit/rest'
import {RequestOptions} from 'https'
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')
// await fs.promises.writeFile(archivePath, raw)
// 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 using the GitHub REST API
core.info('Getting archive URL from GitHub REST API')
const octokit = new github.GitHub(accessToken)
const params: RequestOptions & ReposGetArchiveLinkParams = {
method: 'HEAD',
archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
owner: owner,
repo: repo,
ref: refHelper.getDownloadRef(ref, commit)
}
const response = await octokit.repos.getArchiveLink(params)
console.log('GOT THE RESPONSE')
console.log(response.status)
if (response.status != 302) {
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`
)
// 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
}
// return Buffer.from(response.data) // response.data is ArrayBuffer
})
// // Download the archive
// core.info('Downloading the archive') // Do not print the URL since it contains a token to download the archive
// await downloadFile(archiveUrl, archivePath)
// // console.log(`status=${response.status}`)
// // console.log(`headers=${JSON.stringify(response.headers)}`)
// // console.log(`data=${response.data}`)
// // console.log(`data=${JSON.stringify(response.data)}`)
// // for (const key of Object.keys(response.data)) {
// // console.log(`data['${key}']=${response.data[key]}`)
// // }
// // Write archive to file
// const runnerTemp = process.env['RUNNER_TEMP'] as string
// assert.ok(runnerTemp, 'RUNNER_TEMP not defined')
// const archivePath = path.join(runnerTemp, 'checkout.tar.gz')
// await io.rmRF(archivePath)
// await fs.promises.writeFile(archivePath, raw)
// // await exec.exec(`ls -la "${archiveFile}"`, [], {
// // cwd: repositoryPath
// // } as ExecOptions)
// Extract archive
const extractPath = path.join(
runnerTemp,
`checkout-archive${IS_WINDOWS ? '.zip' : '.tar.gz'}`
)
await io.rmRF(extractPath)
await io.mkdirP(extractPath)
if (IS_WINDOWS) {
await toolCache.extractZip(archivePath, extractPath)
} else {
await toolCache.extractTar(archivePath, extractPath)
}
// await exec.exec(`tar -xzf "${archiveFile}"`, [], {
// cwd: extractPath
// } as ExecOptions)
// 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)
}
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()
})
})
}
|