Skip to content

Commit 853682a

Browse files
authored
Add ucrt64 architecture axis (#1361)
MSYS2 deprecated MINGW64 [a while back](https://www.msys2.org/news/#2026-03-15-deprecating-the-mingw64-environment); new packages no longer get accepted there, and updates of the ones we already ship have been winding down. Git for Windows therefore has to move the SDK over to UCRT64, and the kickoff for that move on `git-sdk-64` itself happened in git-for-windows/git-sdk-64#117. Several Git for Windows component repositories (notably https://github.com/git-for-windows/git, https://github.com/git-for-windows/build-extra, and https://github.com/git-for-windows/MINGW-packages) materialise their CI environment by calling into this Action. Once they want to start exercising UCRT64 builds, they have to be able to ask for `architecture: ucrt64` and get a UCRT64 SDK back. That is what this PR sets up. The `ucrt64` axis added here lives on a dedicated long-lived branch of `git-sdk-64`, runs under `MSYSTEM=UCRT64`, and keeps its on-disk output directory and cache key distinct from the existing `x86_64` axis so the two variants can coexist on the same runner without clobbering each other. Only `flavor: full` is wired up end-to-end at this point; the other flavors depend on follow-up work in `build-extra` and on the `ci-artifacts` pipeline of `git-sdk-64`, both of which are explicitly tracked in git-for-windows/git-sdk-64#117 (comment). The long-term plan is to "merge --theirs" `ucrt64` into `main` in the git-sdk-64, therefore the `architecture: ucrt64` setting is intentionally transitional (eventually it will be synonymous to `x86_64`). The branch starts with a fresh `AGENTS.md` written against the pre-UCRT64 state of the repository, and only the later commits extend that document as the actual `ucrt64` support lands. That way each commit reads as an accurate snapshot of the tree at that point, which makes the series easier to review one commit at a time. The final commit is the conventional `npm run build && npm run package` regeneration of `dist/index.js`.
2 parents 7a3ee71 + a9d1c57 commit 853682a

10 files changed

Lines changed: 332 additions & 32 deletions

File tree

.github/workflows/matrix.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ jobs:
1515
architecture: i686
1616
- flavor: makepkg-git
1717
architecture: i686
18+
# `ucrt64` is wired up end-to-end only for `flavor: full`; the
19+
# subset flavors depend on follow-up work in `build-extra` and
20+
# the `ci-artifacts` pipeline of `git-sdk-64`. Add the one
21+
# combination we can meaningfully exercise.
22+
include:
23+
- flavor: full
24+
architecture: ucrt64
1825
steps:
1926
- uses: actions/checkout@v6
2027
- name: run in-place

.github/workflows/test.yml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,38 @@ jobs:
3838
name: diff.txt
3939
path: diff.txt
4040
test: # make sure the action works on a clean machine without building
41+
strategy:
42+
fail-fast: false
43+
matrix:
44+
include:
45+
- architecture: x86_64
46+
flavor: minimal
47+
mingw_prefix: /mingw64
48+
# `ucrt64` is wired up end-to-end only for `flavor: full` at
49+
# this point (the subset flavors depend on follow-up work in
50+
# `build-extra` and the `ci-artifacts` pipeline of
51+
# `git-sdk-64`), so that is the one combination we can
52+
# meaningfully exercise on every PR.
53+
- architecture: ucrt64
54+
flavor: full
55+
mingw_prefix: /ucrt64
4156
runs-on: windows-latest
4257
steps:
4358
- uses: actions/checkout@v6
4459
- name: Run this Action in-place
4560
uses: ./
61+
with:
62+
flavor: ${{ matrix.flavor }}
63+
architecture: ${{ matrix.architecture }}
4664
- name: Verify that the Bash of Git for Windows' SDK is used
4765
shell: bash
66+
env:
67+
MINGW_PREFIX: ${{ matrix.mingw_prefix }}
4868
run: |
4969
set -ex
5070
test ! -e .tmp
5171
echo "This is the MSYS2 pseudo root: $(cygpath -aw /)"
52-
test "gcc is /mingw64/bin/gcc" = "$(type gcc)"
72+
test "gcc is $MINGW_PREFIX/bin/gcc" = "$(type gcc)"
5373
test "prove is /usr/bin/core_perl/prove" = "$(type prove)"
5474
prove -V
5575
printf '%s\n' \

AGENTS.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# AGENTS.md
2+
3+
This file provides guidance for AI agents and developers working with the
4+
`git-for-windows/setup-git-for-windows-sdk` repository.
5+
6+
## Repository purpose
7+
8+
This repository contains a GitHub Action that sets up a Git for Windows
9+
SDK (or a subset thereof) on a Windows runner, so that workflows can
10+
build Git for Windows, package its installer, or run its test suite
11+
without first having to install the full SDK by hand. The Action is
12+
published as `git-for-windows/setup-git-for-windows-sdk` and is consumed
13+
by [git-for-windows/git](https://github.com/git-for-windows/git),
14+
[git-for-windows/build-extra](https://github.com/git-for-windows/build-extra),
15+
[git-for-windows/MINGW-packages](https://github.com/git-for-windows/MINGW-packages),
16+
[git-for-windows/MSYS2-packages](https://github.com/git-for-windows/MSYS2-packages),
17+
and assorted other Git for Windows component repositories. Changes here
18+
ripple across that entire ecosystem, so backwards compatibility matters.
19+
20+
The Action is written in TypeScript, bundled with `@vercel/ncc` into a
21+
single `dist/index.js`, and invoked as a Node 24 action declared by
22+
`action.yml`. There is no composite-action layer; everything happens in
23+
`main.ts` and `src/`.
24+
25+
## Repository structure
26+
27+
- `action.yml` -- Action manifest. Declares inputs (`flavor`,
28+
`architecture`, `msys`, `path`, `cleanup`, `verbose`, `cache`,
29+
`github-token`), branding, and points `runs.main` / `runs.post` at
30+
`dist/index.js` under `node24`.
31+
- `main.ts` -- Entry point. Selects between the CI-artifact fast path
32+
and the git-clone path, sets up `PATH`, `MSYSTEM`, and `LC_CTYPE`,
33+
creates `/dev/{fd,stdin,stdout,stderr}` symlinks, and handles the
34+
POST-action cleanup when the `cleanup` input is `true`.
35+
- `src/git.ts` -- The git-clone path. Maps `(flavor, architecture)`
36+
to `(repo, artifactName)` in `getArtifactMetadata`, then either does
37+
a bare clone plus `git worktree add` (for `flavor: full`) or a bare
38+
clone plus `please.sh create-sdk-artifact` (for the other flavors,
39+
using `build-extra`'s `please.sh`).
40+
- `src/ci_artifacts.ts` -- The fast path. Pulls the
41+
`git-sdk-<arch>-<flavor>.tar.{gz,zst}` asset from the
42+
`ci-artifacts` release in the matching `git-sdk-*` repo and pipes
43+
it through `tar.exe`. Only the `minimal` flavor (always) and
44+
`build-installers` (when the runner has Windows 11 24H2's
45+
Zstandard-capable `tar.exe`) take this path.
46+
- `src/downloader.ts`, `src/spawn.ts` -- small utilities used by the
47+
two paths above.
48+
- `src/__tests__/git.test.ts`, `__tests__/main.test.ts` -- Vitest
49+
tests. `main.test.ts` is gated on `RUN_NETWORK_TESTS=true` because
50+
it really downloads an SDK.
51+
- `dist/index.js`, `dist/index.js.map` -- the `ncc`-bundled artifact
52+
that is actually executed at runtime. Always regenerated alongside
53+
source changes, but in a dedicated follow-up commit (see commit
54+
conventions below).
55+
56+
## Build, test, package
57+
58+
The full validation chain is `npm run all`, which runs in order:
59+
`build` -> `format` -> `lint` -> `typecheck` -> `package` -> `test`.
60+
For incremental work, the individual scripts in `package.json` are:
61+
62+
- `npm ci` -- install dependencies (lockfile-strict).
63+
- `npm run build` -- `tsc`, compiling `.ts` into `lib/`.
64+
- `npm run lint` -- `eslint **/*.ts`.
65+
- `npm run typecheck` -- `tsc --noEmit -p tsconfig.eslint.json`.
66+
- `npm run format` / `npm run format-check` -- Prettier.
67+
- `npm run test` -- Vitest. Set `RUN_NETWORK_TESTS=true` to also
68+
exercise `__tests__/main.test.ts`, which downloads a real SDK.
69+
- `npm run package` -- `@vercel/ncc` bundles `lib/main.js` into
70+
`dist/index.js` (with source map).
71+
72+
The `build-test` workflow (`.github/workflows/test.yml`) runs the same
73+
chain on every PR and explicitly verifies that `dist/index.js` is up to
74+
date by checking that `git diff -aw HEAD -- ':(exclude)dist/index.js.map'`
75+
is empty after `npm run package`. Any push that changes `main.ts` or
76+
`src/` must therefore also include the regenerated `dist/index.js` (in
77+
its own follow-up commit, per the commit conventions below) before CI
78+
sees the tip, or CI fails.
79+
80+
For Dependabot PRs, the `npm-run-package` workflow
81+
(`.github/workflows/npm-run-package.yml`) does the regeneration
82+
automatically and pushes a `npm run build && npm run package` commit
83+
back onto the dependabot branch.
84+
85+
## Release model
86+
87+
The repository uses floating release-train tags (`v0`, `v1`, `v2`,
88+
...) plus immutable point tags (`v2.0.0`, `v2.1.0`, ...). The
89+
`release-tag` workflow (`.github/workflows/release-tag.yml`) is
90+
triggered by pushing a `v*` point tag from `main`. It verifies the
91+
tag's GPG signature against `dscho`'s published keys, creates a
92+
GitHub Release, and fast-forwards the matching `v<N>` branch to the
93+
tagged commit. Consumers usually pin to a major-version branch:
94+
95+
```yaml
96+
- uses: git-for-windows/setup-git-for-windows-sdk@v2
97+
```
98+
99+
## Input model: flavors and architectures
100+
101+
The Action supports four flavors of the SDK:
102+
103+
- `minimal` -- the smallest useful set for building and testing Git
104+
for Windows itself. `x86_64` only.
105+
- `makepkg-git` -- adds enough to package `mingw-w64-git`. `x86_64`
106+
only (can cross-package `i686`).
107+
- `build-installers` -- adds the tooling to assemble installers,
108+
Portable Git, MinGit, and friends.
109+
- `full` -- the complete SDK as a user would install it. The only
110+
flavor that exists for every architecture.
111+
112+
The `architecture` input drives both the underlying `git-sdk-*` repo
113+
and the `MSYSTEM` / bin-path layout inside the SDK:
114+
115+
| `architecture` | repo | MSYSTEM | mingw bin path | notes |
116+
| -------------- | --------------- | ------------ | ----------------- | -------------------------------------------------------------------------------------- |
117+
| `i686` | `git-sdk-32` | `MINGW32` | `/mingw32/bin` | only `build-installers` and `full` |
118+
| `x86_64` | `git-sdk-64` | `MINGW64` | `/mingw64/bin` | the default; fast path available for `minimal` |
119+
| `aarch64` | `git-sdk-arm64` | `CLANGARM64` | `/clangarm64/bin` | only `full` for now |
120+
| `ucrt64` | `git-sdk-64` | `UCRT64` | `/ucrt64/bin` | UCRT64 migration; cloned from the `ucrt64` branch of `git-sdk-64`; only `full` for now |
121+
122+
The `ucrt64` axis is part of the larger UCRT64 migration tracked in
123+
https://github.com/git-for-windows/git-sdk-64/pull/117 and its
124+
follow-up comment
125+
https://github.com/git-for-windows/git-sdk-64/pull/117#issuecomment-4642726384.
126+
It shares the `git-sdk-64` repository with `x86_64` but is materialised
127+
from a different long-lived branch, so caches and on-disk directories
128+
must stay distinct (the artifact name is `git-sdk-ucrt64-<flavor>`
129+
rather than `git-sdk-64-<flavor>`). The `ci-artifacts` release of
130+
`git-sdk-64` contains no UCRT64 asset, so the CI-artifacts fast path
131+
is forcibly skipped for this axis; every flavor takes the
132+
`getViaGit` path. `build-extra`'s `please.sh create-sdk-artifact` does
133+
not yet understand `--architecture=ucrt64` either, so right now only
134+
`flavor: full` (which goes straight through `git worktree add`)
135+
produces a working SDK. The non-`full` flavors will start working once
136+
`build-extra` and the `ci-artifacts` pipeline catch up.
137+
138+
## Relationship to other Git for Windows repositories
139+
140+
- [git-for-windows/git-sdk-64](https://github.com/git-for-windows/git-sdk-64),
141+
[git-sdk-32](https://github.com/git-for-windows/git-sdk-32),
142+
[git-sdk-arm64](https://github.com/git-for-windows/git-sdk-arm64) --
143+
the bare-repo SDKs themselves. Their `main` branch (and, for
144+
`git-sdk-64`, the `ucrt64` branch) is what `getViaGit` clones;
145+
their `ci-artifacts` release is what `getViaCIArtifacts` downloads
146+
(no UCRT64 asset there today).
147+
- [git-for-windows/build-extra](https://github.com/git-for-windows/build-extra)
148+
-- provides `please.sh create-sdk-artifact`, used by `getViaGit` to
149+
carve subset flavors (`minimal`, `makepkg-git`, `build-installers`)
150+
out of a full SDK clone.
151+
- [git-for-windows/git](https://github.com/git-for-windows/git),
152+
[git-for-windows/git-for-windows-automation](https://github.com/git-for-windows/git-for-windows-automation),
153+
[git-for-windows/MINGW-packages](https://github.com/git-for-windows/MINGW-packages),
154+
and most other component repositories consume this Action in their
155+
CI. Breaking changes here block their CI, so the v0/v1/v2 release
156+
trains exist to give consumers a stable pin.
157+
158+
## Commit message conventions
159+
160+
- Write flowing prose; no bullet points and no Markdown section
161+
headers in commit messages.
162+
- Stick to plain ASCII and English, avoid technical jargon unless
163+
necessary to bring across a specific aspect.
164+
- Lead with motivation (why), then non-obvious context, then
165+
intention. Only mention implementation details if the diff is not
166+
already obvious. References (PR/issue/discussion URLs) go at
167+
the end, not the top, and are full URLs, never GitHub abbreviations.
168+
- Cite external facts (release notes, upstream behavior, etc.) with
169+
inline URLs so each claim is self-contained.
170+
- Trailers come at the end in this order: first `Assisted-by:`, then
171+
`Signed-off-by:`. The `Assisted-by:` value names the AI model that
172+
helped, not the product or platform (e.g. `Assisted-by: Opus 4.7`,
173+
not `Assisted-by: Copilot CLI`).
174+
- Use one commit per logical change; do not bundle unrelated edits
175+
together even if they touch the same file.
176+
- Commit early, commit often. It is much easier to combine even
177+
incomplete commits into a complete, well-separated commit than to
178+
split morally-separate changes out from a messy commit.
179+
- Regenerated `dist/index.js` goes in a separate commit at the tip of
180+
the topic branch. It **never** goes into the same commit as the source
181+
change that necessitated it. The convention is the same as of the
182+
`npm-run-package` workflow, where the auto-generated subject is
183+
`npm run build && npm run package`.
184+
185+
## Validating changes
186+
187+
For any change to `action.yml`, `main.ts`, or `src/`:
188+
189+
1. Run `npm run all` locally. It must pass cleanly.
190+
2. Verify `dist/index.js` is rebuilt (`git status` should show it
191+
modified) and commit it in a dedicated follow-up commit with
192+
subject `npm run build && npm run package`.
193+
3. The `build-test` workflow on the PR will re-run the full chain and
194+
re-verify the bundled artifact; the matrix workflow can be
195+
triggered manually (`workflow_dispatch`) to exercise the actual
196+
download paths on a Windows runner.
197+
198+
Documentation-only changes (README, AGENTS) do not need the full
199+
chain, but `npm run format-check` should still pass.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Git for Windows SDK comes in variants targeting `x86_64` (AKA "64-bit"), `i686`
6262

6363
Please note that only the `build-installers` and the `full` flavors are available for `i686`.
6464

65+
In addition, the `ucrt64` value selects the UCRT64 variant of `git-sdk-64`. While the [migration from MINGW64 to UCRT64](https://github.com/git-for-windows/git-sdk-64/pull/117) is in progress, this variant lives on a transitional `ucrt64` branch of `git-sdk-64`; once the migration concludes, that branch will replace `main`, at which point `architecture: x86_64` will itself produce a UCRT64 SDK and `ucrt64` becomes a synonym. Until then, this axis runs under `MSYSTEM=UCRT64` with `/ucrt64/bin` on PATH, uses an output directory and cache key that are distinct from `x86_64` so the two variants do not collide on the same runner, and only supports `flavor: full`.
66+
6567
### Verbosity
6668

6769
By default, this Action prints a line whenever 250 items were extracted (this does not work for the `full` flavor, where this Action is silent by default). It can be overridden by setting the input parameter `verbose`; setting it to a number will show updates whenever that many items were extracted. Setting it to `false` will suppress progress updates. Setting it to `true` will print every extracted file (this also works for the `full` flavor).

action.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ inputs:
1111
default: 'minimal'
1212
architecture:
1313
required: false
14-
description: 'The architecture of the SDK: x86_64, i686 or aarch64. Note that "aarch64" only supports the "full" flavor for now.'
14+
description: >
15+
The architecture of the SDK: x86_64, i686, aarch64, or ucrt64.
16+
Note that "aarch64" only supports the "full" flavor for now.
17+
"ucrt64" selects the UCRT64 variant of git-sdk-64; while the
18+
MINGW64-to-UCRT64 migration is in progress it is cloned from a
19+
transitional "ucrt64" branch that will eventually replace "main",
20+
at which point "x86_64" itself will materialise a UCRT64 SDK.
21+
Only the "full" flavor is supported on the "ucrt64" axis for now.
1522
default: 'x86_64'
1623
msys:
1724
required: false

dist/index.js

Lines changed: 24 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)