Skip to content

Commit 87300d4

Browse files
committed
Add a windows package job to the release workflow
Windows is the one platform code-server has no build for, so `tode`, and anything else that wants a server on that machine, has nowhere to get one. This adds a third job beside package-linux and package-macos that produces a windows-x64 tarball the same way they produce theirs: on that platform's own runner, with the native modules compiled there, uploaded to the same draft release. Most of it is the macos job with a different runner. What follows is every place windows needed something else, and why. The default shell is set to bash for the job rather than on each step, since windows is the one runner whose default is not bash. Git rewrites line endings on checkout on windows. That turns every shell script the build is made of into one bash cannot read, and every name in patches/series into one with a stray return on the end. OS is answered up front. ci/lib.sh works the system out only when OS is empty, and windows sets OS to Windows_NT for every process, so the detection never runs and every question the build asks about the system gets Windows_NT instead -- which launchers to fix up, what the archive is called. The check for an existing value is what makes answering it up front the intended way; a fix inside ci/lib.sh is possible but belongs in its own change. quilt has no windows build, so the patches are applied with git. They are ordinary -p1 diffs against the repository root and go on in the order series names them. Worth knowing for anyone who prefers the symmetry: msys2 packages quilt, and this job already installs one package from it, so `quilt push -a` here is plausible. It is untried on a runner, so it is not what this does. npm hands every script it runs to cmd, which cannot run the shell scripts this repository is built out of, so npm_config_script_shell points it at the same bash the steps use. jq is handed a process substitution when the build merges json, which bash presents as a file under /dev/fd. The jq on this image is a windows program and cannot open those: it reads the second input as nothing and the merge fails silently. The shim copies those arguments to real files. signtool has to be findable. Stamping version details into the native binaries clears any signature first and asks signtool whether there is one, which only reads and removes -- no certificate, nothing signed. rsync exists in the MSYS2 already on the image but not on the path, and it is reached through a forwarder rather than by putting msys2's /usr/bin in front. Measured, because the obvious way fails strangely: npm on the path is a shell script whose shebang reads /usr/bin/env bash, so with msys2 first it is msys2's bash that runs it, and crossing into a second msys runtime does not carry the environment -- 93 variables arrived as 7, PATH rebuilt from msys2's defaults. npm then saw no script-shell and fell back to cmd, and KEEP_MODULES was dropped by the same crossing, which would have produced a release tree with no node_modules and no node beside it and said nothing about it. The archive step asserts it has GNU tar. Of the two on this image only git bash's can rename the tree's top directory as it archives; the windows bsdtar is built without substitution support and refuses -s outright. They are interchangeable everywhere except in exactly this, so it is checked rather than assumed. This depends on the windows arms of the build scripts being correct. Without those fixes the release step fails on a missing node, and with them this job has produced a tarball that unpacks and runs: the server serves, the extension host starts, and a terminal in the workbench round-trips a command. Two things deliberately left out. There is no node-gyp header cache, which existed while this was being brought up and only saved downloads. And npm run test:native is not here, because it has not been run on windows; it would be a small addition once the job exists to run it in.
1 parent 62284ed commit 87300d4

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

‎.github/workflows/release.yaml‎

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,156 @@ jobs:
196196
files: ./release-packages/*
197197
tag_name: v${{ env.VERSION }}
198198
name: v${{ env.VERSION }}
199+
200+
package-windows:
201+
name: win32-x64
202+
runs-on: windows-2022
203+
if: >-
204+
(github.event_name == 'workflow_dispatch') ||
205+
(github.event_name == 'pull_request_target' && github.event.pull_request.merged == true && startsWith(github.head_ref, 'update/'))
206+
207+
defaults:
208+
run:
209+
shell: bash
210+
211+
env:
212+
VSCODE_TARGET: win32-x64
213+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
214+
TAG: ${{ inputs.version || github.event.pull_request.head.ref || github.ref_name }}
215+
# Ensure native modules are built from source to avoid prebuilds.
216+
npm_config_build_from_source: true
217+
# Windows sets OS to Windows_NT, and ci/lib.sh works the system out
218+
# only when OS is empty, so every question the build asks about it
219+
# gets that answer instead: which launchers to fix up, what the
220+
# archive is called.
221+
OS: windows
222+
223+
steps:
224+
# Git rewrites line endings on windows by default, which turns every
225+
# shell script the build is made of into one bash cannot read, and
226+
# every name in patches/series into one with a stray return.
227+
- name: Keep line endings as they are in the repository
228+
run: git config --global core.autocrlf false
229+
230+
- name: Strip update/ and v from tag and set major version
231+
run: |
232+
version=${TAG#update/}
233+
version=${version#v}
234+
version=4${version:1}
235+
echo "VERSION=$version" >> $GITHUB_ENV
236+
237+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
238+
with:
239+
submodules: true
240+
241+
# quilt has no windows build. The patches are ordinary -p1 diffs
242+
# against the repository root, so git applies them in series order.
243+
- name: Apply patches
244+
run: |
245+
while read -r patch; do
246+
case "$patch" in '' | '#'*) continue ;; esac
247+
echo "applying $patch"
248+
git apply --whitespace=nowarn "patches/$patch"
249+
done < patches/series
250+
251+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6
252+
with:
253+
node-version-file: .node-version
254+
cache: npm
255+
cache-dependency-path: |
256+
package-lock.json
257+
test/package-lock.json
258+
259+
# npm hands every script it runs to cmd, which cannot run the shell
260+
# scripts this repository is built out of. Point it at the same bash
261+
# the steps here use rather than at a path, which moves between
262+
# images.
263+
- name: Let npm run shell scripts
264+
run: echo "npm_config_script_shell=$(cygpath -w "$(command -v bash)")" >> $GITHUB_ENV
265+
266+
# The build merges json by handing jq a process substitution, which
267+
# bash presents as a file under /dev/fd. The jq on this image is a
268+
# windows program and cannot open those, so it reads the second input
269+
# as nothing and the merge fails. Both the product and the package
270+
# merge go through here.
271+
- name: Let jq read what bash hands it
272+
run: |
273+
mkdir -p "$RUNNER_TEMP/shim"
274+
cat > "$RUNNER_TEMP/shim/jq" <<'SHIM'
275+
#!/usr/bin/env bash
276+
set -euo pipefail
277+
args=()
278+
for arg in "$@"; do
279+
case $arg in
280+
/dev/fd/* | /proc/*/fd/*)
281+
copy=$(mktemp)
282+
cat "$arg" > "$copy"
283+
args+=("$copy")
284+
;;
285+
*) args+=("$arg") ;;
286+
esac
287+
done
288+
exec jq.exe "${args[@]}"
289+
SHIM
290+
chmod +x "$RUNNER_TEMP/shim/jq"
291+
echo "$RUNNER_TEMP/shim" >> $GITHUB_PATH
292+
293+
# Stamping version details into the native binaries clears any
294+
# signature they arrived with and asks signtool whether there is one.
295+
# That only reads and removes, so it wants no certificate and signs
296+
# nothing. It just has to be findable, and the sdk carrying it is not
297+
# on the path.
298+
- name: Put signtool on the path
299+
run: |
300+
sdk=$(ls -d "/c/Program Files (x86)/Windows Kits/10/bin"/*/x64 | sort -V | tail -1)
301+
test -x "$sdk/signtool.exe"
302+
cygpath -w "$sdk" >> $GITHUB_PATH
303+
304+
# build-release.sh copies the tree with rsync, which neither windows
305+
# nor the git bash on this image has. MSYS2 is already here, just not
306+
# on the path.
307+
- name: Install rsync
308+
shell: cmd
309+
run: C:\msys64\usr\bin\pacman -Sy --noconfirm --needed rsync
310+
311+
# Only rsync crosses over. Putting msys2's /usr/bin in front instead
312+
# breaks the release step: npm on the path is a shell script whose
313+
# shebang reads /usr/bin/env bash, so with msys2 first it is msys2's
314+
# bash that runs it, and crossing into a second msys runtime does not
315+
# carry the environment. npm then sees no script-shell and falls back
316+
# to cmd, which cannot run ./ci/build/build-release.sh, and
317+
# KEEP_MODULES is dropped on the way. A forwarder avoids the whole
318+
# class: rsync is a native exe that loads its runtime from beside
319+
# itself, and nothing else on the path moves.
320+
- name: Reach rsync without moving the path
321+
run: |
322+
cat > "$RUNNER_TEMP/shim/rsync" <<'SHIM'
323+
#!/usr/bin/env bash
324+
exec /c/msys64/usr/bin/rsync.exe "$@"
325+
SHIM
326+
chmod +x "$RUNNER_TEMP/shim/rsync"
327+
328+
- run: npm ci
329+
- run: npm run build
330+
- run: npm run build:vscode
331+
- run: KEEP_MODULES=1 npm run release
332+
333+
# Of the two tars on this image it is git bash's GNU one that can
334+
# rename the tree's top directory as it archives; the windows bsdtar
335+
# is built without substitution support. Asserted rather than
336+
# assumed, since the two are interchangeable everywhere except here.
337+
- name: Package
338+
run: |
339+
case "$(tar --version | head -1)" in
340+
*GNU*) ;;
341+
*) echo "expected GNU tar for --transform, got $(tar --version | head -1)" >&2; exit 1 ;;
342+
esac
343+
npm run package
344+
345+
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
346+
with:
347+
draft: true
348+
discussion_category_name: "📣 Announcements"
349+
files: ./release-packages/*
350+
tag_name: v${{ env.VERSION }}
351+
name: v${{ env.VERSION }}

0 commit comments

Comments
 (0)