Skip to content

Commit cc279f3

Browse files
committed
ucrt64: add workflow that drives the MINGW64 -> UCRT64 transition
MSYS2 has deprecated MINGW64. Upstream no longer accepts new packages targeting that environment and is winding down updates for the ones we already ship, so anything we do not migrate is going to rot in place. UCRT64 is the supported successor for the x86_64 target, and Git for Windows therefore has to follow. Rather than spin up a separate `git-sdk-ucrt-64` repository just for the new environment, we want to keep everything in this repo and make the cutover happen on a `ucrt64` branch that eventually becomes `main`. To produce the converted tree without running `pacman` manually, many times, and committing the resulting churn, this commit adds a one-shot workflow that performs exactly that work inside the PR opened from the `ucrt64` branch: it installs the UCRT64 counterpart of every currently-installed MINGW64 package, then uninstalls every MINGW64 package, then uninstalls every MINGW32 package. Each phase becomes one giant commit like the ones produced by the `sync` workflow. This approach was brain-stormed and documented in git-for-windows/git#6018 (reply in thread). Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 476819c commit cc279f3

1 file changed

Lines changed: 270 additions & 0 deletions

File tree

.github/workflows/ucrt64.yml

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
name: Convert to UCRT64
2+
3+
# One-shot workflow that performs the MINGW64 -> UCRT64 transition for this
4+
# SDK inside the PR opened from the `ucrt64` branch. It computes the UCRT64
5+
# counterparts of every installed MINGW64 package (skipping those that have
6+
# no UCRT64 equivalent in any configured Pacman repository, such as
7+
# `mingw-w64-cv2pdb` at the time of writing), installs them, then removes
8+
# all MINGW64 and finally all MINGW32 packages in three large commits and
9+
# pushes the resulting state back to the PR branch. The set of skipped
10+
# packages is reported as a PR comment for follow-up by a human.
11+
12+
on:
13+
pull_request:
14+
branches: [main]
15+
types: [opened, reopened, synchronize]
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: write
20+
pull-requests: write
21+
22+
env:
23+
GIT_CONFIG_PARAMETERS: "'user.name=Git for Windows Build Agent' 'user.email=ci@git-for-windows.build' 'windows.sdk64.path=${{ github.workspace }}' 'windows.sdk32.path=' 'http.sslbackend=schannel' 'core.autocrlf=false' 'checkout.workers=16'"
24+
HOME: "${{ github.workspace }}\\home\\git-ci"
25+
MSYSTEM: MSYS
26+
27+
jobs:
28+
convert:
29+
name: Convert MINGW64 to UCRT64 in-place
30+
if: |
31+
github.repository == 'git-for-windows/git-sdk-64' &&
32+
(
33+
(github.event_name == 'pull_request' &&
34+
github.head_ref == 'ucrt64' &&
35+
github.event.pull_request.head.repo.full_name == github.repository) ||
36+
(github.event_name == 'workflow_dispatch' &&
37+
github.ref_name == 'ucrt64')
38+
)
39+
runs-on: windows-latest
40+
steps:
41+
- name: clone git-sdk-64
42+
uses: actions/checkout@v6
43+
with:
44+
ref: ${{ github.head_ref || github.ref_name }}
45+
persist-credentials: true
46+
47+
- name: use git-sdk-64's Bash and git.exe
48+
run: "usr\\bin\\bash.exe -lc 'cygpath -aw /usr/bin >>$GITHUB_PATH && cygpath -aw /cmd >>$GITHUB_PATH'"
49+
50+
- name: compute UCRT64 package lists
51+
id: lists
52+
shell: bash
53+
run: |
54+
set -e
55+
workdir="$(cygpath -m "$RUNNER_TEMP")/ucrt64-conversion"
56+
mkdir -p "$workdir"
57+
echo "workdir=$workdir" >>"$GITHUB_OUTPUT"
58+
59+
# Refresh the package databases so the latest UCRT64 packages are
60+
# visible to `pacman -Sl`.
61+
pacman -Sy --noconfirm
62+
63+
# Names (without the architecture prefix) of all installed MINGW64
64+
# packages.
65+
pacman -Qq | sed -n 's/^mingw-w64-x86_64-//p' | sort -u \
66+
>"$workdir/mingw64-names.txt"
67+
68+
if ! test -s "$workdir/mingw64-names.txt"
69+
then
70+
echo 'No MINGW64 packages installed; conversion is already done.'
71+
echo 'skip=true' >>"$GITHUB_OUTPUT"
72+
exit 0
73+
fi
74+
75+
# Names (without prefix) of every available UCRT64 package across
76+
# all configured Pacman repositories (upstream `ucrt64` today,
77+
# plus `git-for-windows-ucrt-x86_64` once it exists).
78+
pacman -Sl | awk '{print $2}' \
79+
| sed -n 's/^mingw-w64-ucrt-x86_64-//p' | sort -u \
80+
>"$workdir/ucrt64-available.txt"
81+
82+
# The UCRT64 counterparts that exist and will be installed.
83+
comm -12 "$workdir/mingw64-names.txt" "$workdir/ucrt64-available.txt" \
84+
| sed 's/^/mingw-w64-ucrt-x86_64-/' \
85+
>"$workdir/ucrt64-to-install.txt"
86+
87+
# MINGW64 packages with no UCRT64 counterpart anywhere in the
88+
# configured repositories; these are skipped and reported via a
89+
# PR comment.
90+
comm -23 "$workdir/mingw64-names.txt" "$workdir/ucrt64-available.txt" \
91+
| sed 's/^/mingw-w64-x86_64-/' \
92+
>"$workdir/ucrt64-skipped.txt"
93+
94+
to_install_count=$(wc -l <"$workdir/ucrt64-to-install.txt")
95+
skipped_count=$(wc -l <"$workdir/ucrt64-skipped.txt")
96+
echo "::group::UCRT64 packages to install ($to_install_count)"
97+
cat "$workdir/ucrt64-to-install.txt"
98+
echo "::endgroup::"
99+
echo "::group::MINGW64 packages without UCRT64 counterpart ($skipped_count skipped)"
100+
cat "$workdir/ucrt64-skipped.txt"
101+
echo "::endgroup::"
102+
103+
- name: drop MINGW64 git-for-windows-addons before installing the UCRT64 set
104+
if: steps.lists.outputs.skip != 'true'
105+
shell: bash
106+
run: |
107+
# `git-bash.exe` and a handful of other top-level launchers are
108+
# owned by the architecture-specific `git-for-windows-addons`
109+
# package. Both the MINGW64 and the UCRT64 variant claim the
110+
# same paths, so the MINGW64 one must come out first; otherwise
111+
# the UCRT64 install would either fail with a file-conflict or
112+
# silently clobber the MINGW64 launcher when `--overwrite` is
113+
# in play. Uninstalling it here also keeps the install step
114+
# below honest: any other file conflict the UCRT64 set
115+
# introduces will now surface as a `pacman` error instead of
116+
# being papered over.
117+
pkg=mingw-w64-x86_64-git-for-windows-addons
118+
if pacman -Qq "$pkg" >/dev/null 2>&1
119+
then
120+
pacman -Rdd --noscriptlet --noconfirm "$pkg"
121+
fi
122+
123+
- name: install UCRT64 counterparts of all MINGW64 packages
124+
if: steps.lists.outputs.skip != 'true'
125+
shell: bash
126+
run: |
127+
set -e
128+
workdir='${{ steps.lists.outputs.workdir }}'
129+
test -s "$workdir/ucrt64-to-install.txt" || {
130+
echo 'No UCRT64 counterparts available; nothing to install.'
131+
exit 0
132+
}
133+
xargs -a "$workdir/ucrt64-to-install.txt" \
134+
pacman -S --noconfirm --needed
135+
# Make the just-installed UCRT64 git.exe (a pure Win32
136+
# program, like the MINGW64 git.exe was) outrank
137+
# /usr/bin/git.exe on PATH for subsequent steps. After the
138+
# MINGW64 git package is uninstalled below, the next git
139+
# invocation must not fall through to /usr/bin/git.exe,
140+
# because that one is built against the MSYS2 runtime and
141+
# would try to add Cygwin's emulated /dev/ tree to the
142+
# index, blowing up the second and third commit.
143+
cygpath -aw /ucrt64/bin >>"$GITHUB_PATH"
144+
145+
- name: commit "Install UCRT64 counterparts of all MINGW64 packages"
146+
if: steps.lists.outputs.skip != 'true'
147+
shell: bash
148+
run: |
149+
set -e
150+
workdir='${{ steps.lists.outputs.workdir }}'
151+
{
152+
echo 'Install UCRT64 counterparts of all MINGW64 packages'
153+
echo
154+
if test -s "$workdir/ucrt64-to-install.txt"
155+
then
156+
echo 'Installed UCRT64 packages:'
157+
sed 's/^/ /' "$workdir/ucrt64-to-install.txt"
158+
fi
159+
if test -s "$workdir/ucrt64-skipped.txt"
160+
then
161+
echo
162+
echo 'No UCRT64 counterpart in the configured Pacman repositories (skipped):'
163+
sed 's/^/ /' "$workdir/ucrt64-skipped.txt"
164+
fi
165+
} >"$workdir/msg.txt"
166+
git add -A .
167+
git diff-index --quiet --cached HEAD -- \
168+
':(exclude)var/lib/pacman/sync/' \
169+
':(exclude)etc/rebase.db*' \
170+
|| git commit -s -q -F "$workdir/msg.txt"
171+
172+
- name: uninstall all MINGW64 packages
173+
if: steps.lists.outputs.skip != 'true'
174+
shell: bash
175+
run: |
176+
set -e
177+
workdir='${{ steps.lists.outputs.workdir }}'
178+
pacman -Qq | grep '^mingw-w64-x86_64-' \
179+
>"$workdir/mingw64-installed.txt" || true
180+
test -s "$workdir/mingw64-installed.txt" || {
181+
echo 'No MINGW64 packages left to remove.'
182+
exit 0
183+
}
184+
# -dd: skip dep checks (we are removing the whole stack at once and
185+
# they all depend on each other); --noscriptlet: do not run remove
186+
# scripts that themselves rely on the MINGW64 toolchain we are
187+
# about to delete.
188+
xargs -a "$workdir/mingw64-installed.txt" \
189+
pacman -Rdd --noscriptlet --noconfirm
190+
191+
- name: commit "Uninstall all MINGW64 packages"
192+
if: steps.lists.outputs.skip != 'true'
193+
shell: bash
194+
run: |
195+
set -e
196+
workdir='${{ steps.lists.outputs.workdir }}'
197+
test -s "$workdir/mingw64-installed.txt" || exit 0
198+
{
199+
echo 'Uninstall all MINGW64 packages'
200+
echo
201+
echo 'Removed MINGW64 packages:'
202+
sed 's/^/ /' "$workdir/mingw64-installed.txt"
203+
} >"$workdir/msg.txt"
204+
git add -A .
205+
git diff-index --quiet --cached HEAD -- \
206+
':(exclude)var/lib/pacman/sync/' \
207+
':(exclude)etc/rebase.db*' \
208+
|| git commit -s -q -F "$workdir/msg.txt"
209+
210+
- name: uninstall all MINGW32 packages
211+
if: steps.lists.outputs.skip != 'true'
212+
shell: bash
213+
run: |
214+
set -e
215+
workdir='${{ steps.lists.outputs.workdir }}'
216+
pacman -Qq | grep '^mingw-w64-i686-' \
217+
>"$workdir/mingw32-installed.txt" || true
218+
test -s "$workdir/mingw32-installed.txt" || {
219+
echo 'No MINGW32 packages left to remove.'
220+
exit 0
221+
}
222+
xargs -a "$workdir/mingw32-installed.txt" \
223+
pacman -Rdd --noscriptlet --noconfirm
224+
225+
- name: commit "Uninstall all MINGW32 packages"
226+
if: steps.lists.outputs.skip != 'true'
227+
shell: bash
228+
run: |
229+
set -e
230+
workdir='${{ steps.lists.outputs.workdir }}'
231+
test -s "$workdir/mingw32-installed.txt" || exit 0
232+
{
233+
echo 'Uninstall all MINGW32 packages'
234+
echo
235+
echo 'Removed MINGW32 packages:'
236+
sed 's/^/ /' "$workdir/mingw32-installed.txt"
237+
} >"$workdir/msg.txt"
238+
git add -A .
239+
git diff-index --quiet --cached HEAD -- \
240+
':(exclude)var/lib/pacman/sync/' \
241+
':(exclude)etc/rebase.db*' \
242+
|| git commit -s -q -F "$workdir/msg.txt"
243+
244+
- name: push converted branch
245+
if: steps.lists.outputs.skip != 'true'
246+
shell: bash
247+
env:
248+
TARGET_REF: ${{ github.head_ref || github.ref_name }}
249+
run: |
250+
set -e
251+
git push origin "HEAD:refs/heads/$TARGET_REF"
252+
253+
- name: comment on PR with skipped packages
254+
if: steps.lists.outputs.skip != 'true' && github.event_name == 'pull_request'
255+
uses: actions/github-script@v7
256+
env:
257+
SKIPPED_FILE: ${{ steps.lists.outputs.workdir }}/ucrt64-skipped.txt
258+
with:
259+
script: |
260+
const fs = require('fs');
261+
const list = fs.readFileSync(process.env.SKIPPED_FILE, 'utf8').trim();
262+
const body = list.length === 0
263+
? 'Every installed MINGW64 package has a UCRT64 counterpart in the configured Pacman repositories; nothing was skipped.'
264+
: 'The following MINGW64 packages have no UCRT64 counterpart in the configured Pacman repositories and were skipped:\n\n```\n' + list + '\n```';
265+
await github.rest.issues.createComment({
266+
owner: context.repo.owner,
267+
repo: context.repo.repo,
268+
issue_number: context.issue.number,
269+
body,
270+
});

0 commit comments

Comments
 (0)