Skip to content

Commit 905a18b

Browse files
committed
Merge branch 'embargoed-builds-preamble'
This topic branch contains preparatory patches for the huge task of adapting Git for Windows' automation to build embargoed releases. So what _are_ embargoed releases? Every once in a while (seemingly on a semi-annual cadence), security-relevant bugs are discovered in Git, reported responsibly to the git-security mailing list, and if the core Git developers deem it important enough of an issue, patches are developed in secret ("under embargo"). These patches are then reviewed (typically even less enthusiastically than on the Git mailing list because much more is at stake than the question whether the lines are indented correctly or whether the lines have trailing whitespace), and eventually they are tagged (at least if consensus is reached) and then the task begins for Git for Windows to provide installers, portable Gits and MinGit versions well in advance of the publication date ("the lifting of the embargo"), a date that has to be negotiated between all affected parties (not a small feat, I can tell ya). In Git for Windows, I call these artifacts "embargoed build". They are distributed to the stakeholders such as GitHub Desktop and Visual Studio in advance of the publication date so that they can test, and integrate them into their own embargoed builds. Previously, embargoed builds were built in an Azure Pipeline, in a private Azure DevOps project. This has become a bit of a problem in the meantime, for multiple reasons: - The upstream Git project (which enjoyed very active contributions from up to 8 GitHub engineers at its peak) shifted CI builds from Azure Pipelines to GitHub Actions a long time ago. Even though investment into GitHub Actions seems to have been drastically reduced recently, there is too much momentum and the last remnants of Azure Pipelines support has been removed, all but forcing Git for Windows to invest in a private org on GitHub to continue building confidence in embargoed builds by running the CI builds there. This has implications also on _building_ the embargoed versions, as it is better to build where you test. - Even though the hoped-for Windows/ARM64 runners have not materialized in GitHub Actions yet (Windows/ARM64 runners exist, but they are not free, not even for open source), Git for Windows invested substantial efforts (with serious, serious time committment of volunteers, most of all the never-tiring Dennis Ameling) to side-step this short-coming and build a custom infrastructure based on creating custom Azure VMs and registering them as Windows/ARM64 runners. This support is noticeably lacking from Azure Pipelines, too, and Git for Windows would have to invest the same time and work to gain the ability to build Windows/ARM64 binaries in Azure Pipelines. - It is always cumbersome to maintain two totally different things that do virtually the same, and Azure Pipelines and GitHub Actions are just very different even though they started out using the same technology (it does not seem as if a lot of synergy has happened there as a consequence, which is a shame). - For historical reasons, the Azure Pipeline we used was not YAML-based, and is therefore very hard to transport to other repositories/projects. In contrast, the GitHub workflows we established in the Git for Windows projects are very easy to transport to forks or even transmogrify them into totally different projects as needed. To allow for the same mobility with the Azure Pipeline, we would have had to rewrite it in YAML anyway, and it seems a smarter investment to extend the already-existing GitHub workflows to that end. The journey to adapt the GitHub workflows to allow for building and publishing embargoed artifacts is split into these topic branches (with the current one being the first one): - embargoed-builds-preamble: Some preparatory patches, refactoring, that kind of stuff. This is what is actually merged in this here merge commit. - embargoed-branches: Developing a GitHub workflow that initializes "time-traveling" branches, i.e. branches that branch off at the time when the version preceding the embargoed one were done, in the following repositories (which are all used to build Git for Windows' installers, portable Gits, MinGits, etc): - git - build-extra - MINGW-packages - git-sdk-64 - git-sdk-arm64 - git-sdk-32 - embargoed-git-artifacts: Adapting the `git-artifacts` workflow to allow for running it in a private GitHub org. This requires some special care because access tokens are required to access the relevant repositories - embargoed-tag-git: Adapting the `tag-git` workflow to allow for running in a private GitHub org. This workflow is actually the one triggered by the corresponding slash command, as its build artifacts are required to run the `git-artifacts` workflow for all supported CPU architectures. - embargoed-releases: While the regular, non-embargoed release process of Git for Windows calls for the build artifacts of the `git-artifacts` workflow runs to be gathered and released e.g. to a GitHub release in `git-for-windows/git`, the same is not possible in embargoed releases. The problem is not even that the build artifacts would have to be retrieved from a different org, using different access tokens than the GitHub release to which they need to be uploaded, the biggest problem is the time factor: While regular releases have a very short time window between building the artifacts and releasing them, in embargoed releases sometimes months have to pass before the build artifacts can be released, and GitHub's retention policies do not allow for such long time windows. As a consequence, a lot of mechanics are necessary to refactor some of the processing that would historically be done during the `release` workflow to allow for it to be run waaaaay before the actual release. And to counter the retention rules, all embargoed build artifacts are uploaded to a GitHub release in the private org, where retention rules do not apply. One step is missing: Actually releasing the embargoed artifacts to a _public_ GitHub Release in git-for-windows/git, to NuGet.org and to Git for Windows' Pacman repositories, as well as updating the `build-extra` and `git-for-windows.github.io` repositories with the information that is only available once that GitHub Release is available. The reason that this is missing is that I ran out of steam doing it during the (quite stressful) release process of v2.47.2 & friends. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 513b8bb + d7c47eb commit 905a18b

File tree

6 files changed

+63
-9
lines changed

6 files changed

+63
-9
lines changed

.github/workflows/break-pacman-upload-lease.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66

77
jobs:
88
break-lease:
9+
if: github.event.repository.owner.login == 'git-for-windows'
910
runs-on: ubuntu-latest
1011
steps:
1112
- name: Clone build-extra

.github/workflows/build-and-deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ env:
3636

3737
jobs:
3838
build:
39+
if: github.event.repository.owner.login == 'git-for-windows'
3940
runs-on: ${{ github.event.inputs.architecture == 'aarch64' && fromJSON('["Windows", "ARM64"]') || 'windows-latest' }}
4041
steps:
4142
- uses: actions/checkout@v4

.github/workflows/open-pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ env:
2424

2525
jobs:
2626
open-pr:
27+
if: github.event.repository.owner.login == 'git-for-windows'
2728
runs-on: windows-latest
2829
steps:
2930
- name: Determine REPO

.github/workflows/release-git.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ env:
2525
jobs:
2626
setup:
2727
runs-on: ubuntu-latest
28+
if: github.event.repository.owner.login == 'git-for-windows'
2829
outputs:
2930
display-version: ${{ steps.bundle-artifacts.outputs.display-version }}
3031
tag-name: ${{ steps.bundle-artifacts.outputs.tag-name }}

.github/workflows/updpkgsums.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ env:
2424

2525
jobs:
2626
updpkgsums:
27+
if: github.event.repository.owner.login == 'git-for-windows'
2728
runs-on: windows-latest
2829
steps:
2930
- uses: actions/checkout@v4

github-release.js

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,32 @@ const getWorkflowRunArtifactsURLs = async (context, token, owner, repo, workflow
5858
}, {})
5959
}
6060

61-
const downloadAndUnZip = async (token, url, name) => {
61+
const download = async (token, url, outputFile) => {
6262
const { spawnSync } = require('child_process')
63-
const auth = token ? ['-H', `Authorization: Bearer ${token}`] : []
64-
const tmpFile = `${process.env.RUNNER_TEMP || process.env.TEMP || '/tmp'}/${name}.zip`
65-
const curl = spawnSync('curl', [...auth, '-Lo', tmpFile, url])
63+
const headers = token ? ['-H', `Authorization: Bearer ${token}`] : []
64+
if (url.match(/^https:\/\/github.com\/[^/]+\/[^/]+\/releases\/assets\/\d+$/)
65+
|| url.match(/^https:\/\/api\.github.com\/repos\/[^/]+\/[^/]+\/releases\/assets\/\d+$/)) {
66+
headers.push('-H', 'Accept: application/octet-stream')
67+
}
68+
const curl = spawnSync('curl', [...headers, '-fLo', outputFile, url])
6669
if (curl.error) throw curl.error
67-
const { mkdirSync, rmSync } = require('fs')
68-
await mkdirSync(name, { recursive: true })
69-
const unzip = spawnSync('unzip', ['-d', name, tmpFile])
70+
}
71+
72+
const unzip = async (zipFile, outputDirectory) => {
73+
const { mkdirSync } = require('fs')
74+
await mkdirSync(outputDirectory, { recursive: true })
75+
const { spawnSync } = require('child_process')
76+
const unzip = spawnSync('unzip', ['-d', outputDirectory, zipFile])
7077
if (unzip.error) throw unzip.error
78+
}
79+
80+
const getTempFile = (name) => `${process.env.RUNNER_TEMP || process.env.TEMP || '/tmp'}/${name}`
81+
82+
const downloadAndUnZip = async (token, url, name) => {
83+
const tmpFile = getTempFile(`${name}.zip`)
84+
await download(token, url, tmpFile)
85+
await unzip(tmpFile, name)
86+
const { rmSync } = require('fs')
7187
rmSync(tmpFile)
7288
}
7389

@@ -287,17 +303,50 @@ const pushGitTag = (context, setSecret, token, owner, repo, tagName, bundlePath)
287303
context.log('Done pushing tag')
288304
}
289305

306+
const downloadReleaseAssets = async (context, setSecret, appId, privateKey, owner, repo, tagName, filenameMatcher) => {
307+
const { getAccessTokenForRepo } = require('./repository-updates.js')
308+
const token = await getAccessTokenForRepo(context, setSecret, appId, privateKey, owner, repo)
309+
310+
const githubApiRequest = require('./github-api-request.js')
311+
const release = await githubApiRequest(
312+
context,
313+
token,
314+
'GET',
315+
`https://api.github.com/repos/${owner}/${repo}/releases/tags/${tagName}`
316+
)
317+
318+
for (const asset of release.assets) {
319+
if (!filenameMatcher || filenameMatcher(asset.name)) {
320+
context.log(`Downloading ${asset.name}`)
321+
await download(token, asset.url, asset.name)
322+
}
323+
}
324+
}
325+
326+
const downloadReleaseAssetsFromURL = async (context, setSecret, appId, privateKey, releaseURL, filenameMatcher) => {
327+
const [, owner, repo, tagName] = releaseURL.match(
328+
/^https:\/\/github.com\/([^/]+)\/([^/]+)\/releases\/tag\/([^/]+)$/
329+
)
330+
if (!owner || !repo || !tagName) throw new Error(`Invalid release URL: ${releaseURL}`)
331+
return await downloadReleaseAssets(context, setSecret, appId, privateKey, owner, repo, tagName, filenameMatcher)
332+
}
333+
290334
module.exports = {
291335
createRelease,
292336
updateRelease,
293337
uploadReleaseAsset,
294338
getWorkflowRunArtifactsURLs,
339+
download,
340+
unzip,
341+
getTempFile,
295342
downloadAndUnZip,
296343
downloadBundleArtifacts,
297344
getGitArtifacts,
298345
sha256sumsFromReleaseNotes,
299346
calculateSHA256ForFile,
300347
checkSHA256Sums,
301348
uploadGitArtifacts,
302-
pushGitTag
303-
}
349+
pushGitTag,
350+
downloadReleaseAssets,
351+
downloadReleaseAssetsFromURL,
352+
}

0 commit comments

Comments
 (0)