Skip to content

Commit 0bbf9f9

Browse files
committed
getViaGit: get minimal flavor from ci-artifacts
As a result of recent changes, the Git SDK `ci-artifacts` are published as GitHub release assets now. This eliminates the need for us to clone and cache the artifacts in this GitHub action. Let's simply download the latest `.tar.gz` from the `ci-artifacts` and extract it. Ref: git-for-windows/git-sdk-64@fdb0cea Ref: git-for-windows/git-sdk-64#87 Signed-off-by: Dennis Ameling <dennis@dennisameling.com>
1 parent ade9582 commit 0bbf9f9

File tree

1 file changed

+136
-35
lines changed

1 file changed

+136
-35
lines changed

src/git.ts

Lines changed: 136 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {ChildProcess, spawn} from 'child_process'
33
import {Octokit} from '@octokit/rest'
44
import {delimiter} from 'path'
55
import * as fs from 'fs'
6+
import https from 'https'
7+
import os from 'os'
68

79
// If present, do prefer the build agent's copy of Git
810
const externalsGitDir = `${process.env.AGENT_HOMEDIRECTORY}/externals/git`
@@ -109,54 +111,36 @@ async function updateHEAD(
109111
})
110112
}
111113

112-
export async function getViaGit(
113-
flavor: string,
114-
architecture: string,
115-
githubToken?: string
116-
): Promise<{
114+
type GetViaGitResult = {
117115
artifactName: string
118116
id: string
119117
download: (
120118
outputDirectory: string,
121119
verbose?: number | boolean
122120
) => Promise<void>
123-
}> {
121+
}
122+
123+
export async function getViaGit(
124+
flavor: string,
125+
architecture: string,
126+
githubToken?: string
127+
): Promise<GetViaGitResult> {
124128
const owner = 'git-for-windows'
125129

126130
const {repo, artifactName} = getArtifactMetadata(flavor, architecture)
127131

128132
const octokit = githubToken ? new Octokit({auth: githubToken}) : new Octokit()
129-
let head_sha: string
133+
130134
if (flavor === 'minimal') {
131-
const info = await octokit.actions.listWorkflowRuns({
132-
owner,
133-
repo,
134-
workflow_id: 938271,
135-
status: 'success',
136-
branch: 'main',
137-
event: 'push',
138-
per_page: 1
139-
})
140-
head_sha = info.data.workflow_runs[0].head_sha
141-
/*
142-
* There was a GCC upgrade to v14.1 that broke the build with `DEVELOPER=1`,
143-
* and `ci-artifacts` was not updated to test-build with `DEVELOPER=1` (this
144-
* was fixed in https://github.com/git-for-windows/git-sdk-64/pull/83).
145-
*
146-
* Work around that by forcing the incorrectly-passing revision back to the
147-
* last one before that GCC upgrade.
148-
*/
149-
if (head_sha === '5f6ba092f690c0bbf84c7201be97db59cdaeb891') {
150-
head_sha = 'e37e3f44c1934f0f263dabbf4ed50a3cfb6eaf71'
151-
}
152-
} else {
153-
const info = await octokit.repos.getBranch({
154-
owner,
155-
repo,
156-
branch: 'main'
157-
})
158-
head_sha = info.data.commit.sha
135+
return getMinimalFlavor(owner, repo, artifactName, octokit, githubToken)
159136
}
137+
138+
const info = await octokit.repos.getBranch({
139+
owner,
140+
repo,
141+
branch: 'main'
142+
})
143+
const head_sha = info.data.commit.sha
160144
const id = `${artifactName}-${head_sha}${head_sha === 'e37e3f44c1934f0f263dabbf4ed50a3cfb6eaf71' ? '-2' : ''}`
161145
core.info(`Got commit ${head_sha} for ${repo}`)
162146

@@ -239,3 +223,120 @@ export async function getViaGit(
239223
}
240224
}
241225
}
226+
227+
async function getMinimalFlavor(
228+
owner: string,
229+
repo: string,
230+
artifactName: string,
231+
octokit: Octokit,
232+
githubToken?: string
233+
): Promise<GetViaGitResult> {
234+
const ciArtifactsResponse = await octokit.repos.getReleaseByTag({
235+
owner,
236+
repo,
237+
tag: 'ci-artifacts'
238+
})
239+
240+
if (ciArtifactsResponse.status !== 200) {
241+
throw new Error(
242+
`Failed to get ci-artifacts release from the ${owner}/${repo} repo: ${ciArtifactsResponse.status}`
243+
)
244+
}
245+
246+
const tarGzArtifact = ciArtifactsResponse.data.assets.find(asset =>
247+
asset.name.endsWith('.tar.gz')
248+
)
249+
250+
if (!tarGzArtifact) {
251+
throw new Error(
252+
`Failed to find a tar.gz artifact in the ci-artifacts release of the ${owner}/${repo} repo`
253+
)
254+
}
255+
256+
return {
257+
artifactName,
258+
id: `ci-artifacts-${tarGzArtifact.updated_at}`,
259+
download: async (
260+
outputDirectory: string,
261+
verbose: number | boolean = false
262+
): Promise<void> => {
263+
const tmpFile = `${os.tmpdir()}/${tarGzArtifact.name}`
264+
core.info(
265+
`Downloading ${tarGzArtifact.browser_download_url} to ${tmpFile}...`
266+
)
267+
try {
268+
await downloadFile(
269+
tarGzArtifact.browser_download_url,
270+
{
271+
headers: {
272+
...(githubToken ? {Authorization: `Bearer ${githubToken}`} : {}),
273+
Accept: 'application/octet-stream'
274+
}
275+
},
276+
tmpFile
277+
)
278+
} catch (e) {
279+
core.error(
280+
`Failed to download ${tarGzArtifact.browser_download_url}: ${e}`
281+
)
282+
throw e
283+
}
284+
core.info('Extracting archive with tar.exe...')
285+
const traceArg = verbose ? ['-v'] : []
286+
const child = spawn('C:\\Windows\\system32\\tar.exe', [
287+
'-xzf',
288+
...traceArg,
289+
tmpFile,
290+
'-C',
291+
outputDirectory
292+
])
293+
return new Promise<void>((resolve, reject) => {
294+
child.on('close', code => {
295+
if (code === 0) {
296+
core.info('Finished extracting archive.')
297+
fs.rm(tmpFile, () => resolve())
298+
} else {
299+
reject(new Error(`tar -xzf process exited with code ${code}`))
300+
}
301+
})
302+
})
303+
}
304+
}
305+
}
306+
307+
async function downloadFile(
308+
url: string,
309+
options: https.RequestOptions,
310+
destination: string
311+
): Promise<void> {
312+
return new Promise((resolve, reject) => {
313+
const file = fs.createWriteStream(destination)
314+
https
315+
.get(url, options, async response => {
316+
if (response.statusCode === 301 || response.statusCode === 302) {
317+
if (!response.headers.location) {
318+
throw new Error(
319+
`Got a 301 or 302 response code, but no location header was set. Cannot continue.`
320+
)
321+
}
322+
return await downloadFile(
323+
response.headers.location,
324+
options,
325+
destination
326+
)
327+
}
328+
if (response.statusCode === 200) {
329+
response.pipe(file)
330+
file.on('finish', resolve)
331+
} else {
332+
file.close()
333+
fs.unlink(destination, () =>
334+
reject(new Error(`Failed to download file: ${response.statusCode}`))
335+
)
336+
}
337+
})
338+
.on('error', err => {
339+
fs.unlink(destination, () => reject(err))
340+
})
341+
})
342+
}

0 commit comments

Comments
 (0)