From eafebec483cec60bf38ac6539cb981bd93c75a37 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 17:22:14 +0000 Subject: [PATCH 01/11] Add julia_publish rootfs image for the release publish toolchain The julia-publish pipeline's publish_all step (re)signs and (re)packages the per-platform Julia release artifacts, but currently runs on a bare agent and fails with 'aws: command not found'. This image carries the full toolchain it needs: AWS CLI v2, python3, p7zip-full (7z), hfsprogs + Mozilla's libdmg-hfsplus (dmg/hfsplus/mkfshfs) for the macOS .dmg, wine64 + a JRE for the Inno Setup .exe compiler, and jsign for Authenticode signing via Azure Trusted Signing. x86_64 only, since publish_all runs only on linux/x86_64. Refs: JuliaCI/julia-buildkite#544 Co-Authored-By: Claude Opus 4.8 --- .github/workflows/linux.yml | 6 ++ linux/julia_publish.jl | 191 ++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 linux/julia_publish.jl diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 0c4716c..ee73c06 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -53,6 +53,12 @@ jobs: - {slug: 'debian_minimal.x86_64', build_on: ''} - {slug: 'debian_minimal.i686', build_on: ''} + # The `julia_publish` image carries the full signing/packaging + # toolchain (AWS CLI, p7zip, libdmg-hfsplus, wine, jsign, ...) used + # by the `julia-publish` pipeline's `publish_all` step. x86_64 only, + # since that step only runs on linux/x86_64. + - {slug: 'julia_publish.x86_64', build_on: ''} + # The `latex` image is used to build the PDF docs - {slug: 'latex.x86_64', build_on: ''} diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl new file mode 100644 index 0000000..2a6bcc9 --- /dev/null +++ b/linux/julia_publish.jl @@ -0,0 +1,191 @@ +# The `julia_publish` image carries the full toolchain used by the +# `julia-publish` Buildkite pipeline's `publish_all` step to (re)sign and +# (re)package the per-platform Julia release artifacts: +# +# * AWS CLI v2 -- download/upload artifacts from/to S3 (the step +# previously failed with `aws: command not found` +# because it ran on a bare agent). +# * python3 -- kms_gpg_sign.py, plist editing, the PE-signature +# checker (stdlib only). +# * p7zip-full (`7z`) -- repack the Windows `.zip`. +# * hfsprogs + the -- build the macOS `.dmg` on Linux (Mozilla's +# libdmg-hfsplus `hfsplus`/`dmg`/`mkfshfs` tools, the same ones used +# `hfsplus`/`dmg`/ to build Firefox DMGs on Linux). +# `mkfshfs` +# * wine64 + JRE -- run the Windows Inno Setup compiler (`ISCC.exe`) to +# build the `.exe` installer. NOTE: the Inno Setup 6 +# Wine prefix is *not* provisioned here; see the long +# comment near the Wine install below. +# * jsign + JRE -- Authenticode-sign the Windows binaries via Azure +# Trusted Signing. +# * git / openssh / -- general plumbing; `curl` + glibc also let the +# curl / tar / ... pipeline fetch the Linux `rcodesign` binary at +# runtime (via get_rcodesign.sh) for macOS notarization. +# +# This image is x86_64-only: the `publish_all` step runs exclusively on +# linux/x86_64, so there is no multi-arch matrix here. + +using RootfsUtils: parse_build_args, upload_gha, test_sandbox +using RootfsUtils: debootstrap +using RootfsUtils: install_awscli +using RootfsUtils: root_chroot + +args = parse_build_args(ARGS, @__FILE__) +arch = args.arch +archive = args.archive +image = args.image + +if arch != "x86_64" + error("The `julia_publish` image is only supported on x86_64 (got arch=$(arch)).") +end + +# Versions of the out-of-distro tools we install from upstream. +const JSIGN_VERSION = "7.4" +const INNO_SETUP_VERSION = "6.7.3" + +# Build a debian (trixie) based image with the following extra packages. +packages = [ + "bash", + "ca-certificates", + "cmake", + "curl", + "default-jre-headless", # JRE for jsign and the Inno Setup compiler + "g++", # build libdmg-hfsplus from source + "gcc", + "git", + "gzip", + "hfsprogs", # provides `mkfs.hfsplus` + "libbz2-dev", # libdmg-hfsplus optional bzip2 support + "libssl-dev", # libdmg-hfsplus crypto + "locales", + "localepurge", + "make", + "openssh-client", + "p7zip-full", # provides `7z` for the Windows `.zip` + "python3", + "tar", + "unzip", + "wine", # Windows emulation for the Inno Setup compiler + "wine64", + "xauth", # for headless Wine prefix init via xvfb + "xvfb", # headless display for `wineboot`/`ISCC.exe` + "xz-utils", + "zlib1g-dev", # libdmg-hfsplus +] + +artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", archive, packages) do rootfs, chroot_ENV + env = copy(chroot_ENV) + env["DEBIAN_FRONTEND"] = "noninteractive" + my_chroot(cmd) = root_chroot(rootfs, "bash", "-eu", "-o", "pipefail", "-c", cmd; ENV=env) + + # ------------------------------------------------------------------ + # AWS CLI v2 (>= 2.19, needed for S3 conditional writes --if-none-match). + # Reuse the repo's shared helper (official v2 installer on x86_64). + # ------------------------------------------------------------------ + install_awscli(rootfs, chroot_ENV, arch) + + # ------------------------------------------------------------------ + # libdmg-hfsplus: Mozilla's `hfsplus` / `dmg` / `mkfshfs` CLI tools for + # assembling a macOS `.dmg` on Linux. Small CMake project, built from + # source in the chroot and installed into /usr/local/bin. + # ------------------------------------------------------------------ + my_chroot(""" + tmpdir="\$(mktemp -d)" + git clone --depth 1 https://github.com/mozilla/libdmg-hfsplus "\$tmpdir/libdmg-hfsplus" + cd "\$tmpdir/libdmg-hfsplus" + cmake . + make -j"\$(nproc)" + # The build produces `dmg/dmg`, `hfs/hfsplus` and `hfs/mkfshfs`. + install -m755 dmg/dmg /usr/local/bin/dmg + install -m755 hfs/hfsplus /usr/local/bin/hfsplus + install -m755 hfs/mkfshfs /usr/local/bin/mkfshfs + cd / + rm -rf "\$tmpdir" + # Sanity check that the binaries at least run. + /usr/local/bin/dmg || true + /usr/local/bin/hfsplus || true + """) + + # ------------------------------------------------------------------ + # jsign (Authenticode signing via Azure Trusted Signing). We install the + # standalone JAR and a tiny `/usr/local/bin/jsign` wrapper so that `jsign` + # is on PATH and uses the image's JRE. (We deliberately avoid the upstream + # `.deb`, whose JRE dependency name is less predictable across Debian + # releases.) + # ------------------------------------------------------------------ + my_chroot(""" + mkdir -p /usr/local/lib /usr/local/bin + curl -fsSL "https://github.com/ebourg/jsign/releases/download/$(JSIGN_VERSION)/jsign-$(JSIGN_VERSION).jar" -o /usr/local/lib/jsign.jar + printf '%s\\n' '#!/bin/sh' 'exec java -jar /usr/local/lib/jsign.jar "\$@"' > /usr/local/bin/jsign + chmod +x /usr/local/bin/jsign + jsign --version + """) + + # ------------------------------------------------------------------ + # Wine + Inno Setup 6. + # + # We install Wine and a JRE so the pipeline can run the Inno Setup compiler + # (`ISCC.exe`). We ALSO attempt to initialize a Wine prefix and install + # Inno Setup 6 silently under xvfb during the image build. This is the + # fragile part: headless Wine prefix initialization inside a debootstrap + # chroot (no real X server, no D-Bus, qemu-less but still a minimal + # environment) is known to be flaky. + # + # If that step fails we do NOT want to fail the whole image build (Wine + # itself is still useful and the prefix can be provisioned later), so the + # Inno Setup install is wrapped in `|| true` and clearly logged. The PR + # description documents that consumers must verify / provision the Inno + # Setup prefix at + # "C:\\Program Files (x86)\\Inno Setup 6" + # before relying on the `.exe` build. + # + # The Wine prefix is created for the `juliaci` (uid 1000) user, since that + # is the user the sandbox runs as. + # ------------------------------------------------------------------ + my_chroot(""" + set +e + export WINEPREFIX=/home/juliaci/.wine + export WINEARCH=win64 + export WINEDEBUG=-all + export HOME=/home/juliaci + mkdir -p "\$WINEPREFIX" + + # Initialize the Wine prefix headlessly. + xvfb-run -a wineboot --init + # Give Wine a moment to settle, then make sure the wineserver is gone. + wineserver -w || true + + # Download and silently install Inno Setup 6. + curl -fsSL "https://github.com/jrsoftware/issrc/releases/download/is-$(replace(INNO_SETUP_VERSION, '.' => '_'))/innosetup-$(INNO_SETUP_VERSION).exe" -o /tmp/innosetup.exe + xvfb-run -a wine /tmp/innosetup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- + wineserver -w || true + rm -f /tmp/innosetup.exe + + # Report whether the install succeeded so it is visible in the build log. + if [ -e "\$WINEPREFIX/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe" ]; then + echo "INNO SETUP: ISCC.exe installed successfully into the Wine prefix." + else + echo "INNO SETUP WARNING: ISCC.exe was NOT found after install; the Wine" + echo "prefix must be provisioned manually before the .exe build will work." + fi + + # Take ownership so the prefix survives the chown in cleanup_rootfs. + chown -R 1000:1000 /home/juliaci/.wine || true + true + """) + + # ------------------------------------------------------------------ + # Final smoke checks for the unambiguous, must-work tools. + # ------------------------------------------------------------------ + my_chroot(""" + aws --version + 7z --help >/dev/null + mkfs.hfsplus -V || true + python3 --version + git --version + java -version + """) +end + +upload_gha(tarball_path) +test_sandbox(artifact_hash) From af19875fab67beeb270094c3286cf0ac2eb4e0cf Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 17:23:52 +0000 Subject: [PATCH 02/11] julia_publish: build libdmg-hfsplus in build/ subdir, locate binaries by name Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 2a6bcc9..acdc23d 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -93,12 +93,20 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch tmpdir="\$(mktemp -d)" git clone --depth 1 https://github.com/mozilla/libdmg-hfsplus "\$tmpdir/libdmg-hfsplus" cd "\$tmpdir/libdmg-hfsplus" - cmake . + mkdir -p build && cd build + cmake .. make -j"\$(nproc)" - # The build produces `dmg/dmg`, `hfs/hfsplus` and `hfs/mkfshfs`. - install -m755 dmg/dmg /usr/local/bin/dmg - install -m755 hfs/hfsplus /usr/local/bin/hfsplus - install -m755 hfs/mkfshfs /usr/local/bin/mkfshfs + # The build produces `dmg/dmg`, `hfs/hfsplus` and `hfs/mkfshfs`. Locate + # them by name rather than hardcoding paths, so the install is robust to + # small layout changes in the project. + for tool in dmg hfsplus mkfshfs; do + found="\$(find . -type f -name "\$tool" -perm -u+x | head -n1)" + if [ -z "\$found" ]; then + echo "ERROR: libdmg-hfsplus build did not produce '\$tool'" >&2 + exit 1 + fi + install -m755 "\$found" "/usr/local/bin/\$tool" + done cd / rm -rf "\$tmpdir" # Sanity check that the binaries at least run. From 0d3a02d08239f1e340e9f180afaf93957e7431a3 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 17:35:57 +0000 Subject: [PATCH 03/11] julia_publish: harden the Wine/Inno Setup install + hard-fail Headless Wine prefix init in a debootstrap chroot is flaky; make it deterministic and fail loudly instead of shipping a silently-broken image: - WINEDLLOVERRIDES="mscoree,mshtml=" disables Wine's Mono/Gecko first-run downloads (Inno Setup needs neither; the prompt otherwise hangs headlessly / fetches over the network). - wineserver -w blocks on the async `wineboot --init` before the installer runs. - the build now HARD-FAILS (exit 1) if ISCC.exe is missing or doesn't run under Wine (runs `ISCC.exe /?` and greps its banner), replacing the previous `set +e`/`|| true` soft warning. Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 63 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index acdc23d..992635e 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -13,9 +13,10 @@ # `hfsplus`/`dmg`/ to build Firefox DMGs on Linux). # `mkfshfs` # * wine64 + JRE -- run the Windows Inno Setup compiler (`ISCC.exe`) to -# build the `.exe` installer. NOTE: the Inno Setup 6 -# Wine prefix is *not* provisioned here; see the long -# comment near the Wine install below. +# build the `.exe` installer. The Inno Setup 6 Wine +# prefix IS provisioned here and the build hard-fails +# if it doesn't take; see the comment at the Wine +# install below. # * jsign + JRE -- Authenticode-sign the Windows binaries via Azure # Trusted Signing. # * git / openssh / -- general plumbing; `curl` + glibc also let the @@ -133,34 +134,37 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch # Wine + Inno Setup 6. # # We install Wine and a JRE so the pipeline can run the Inno Setup compiler - # (`ISCC.exe`). We ALSO attempt to initialize a Wine prefix and install - # Inno Setup 6 silently under xvfb during the image build. This is the - # fragile part: headless Wine prefix initialization inside a debootstrap - # chroot (no real X server, no D-Bus, qemu-less but still a minimal - # environment) is known to be flaky. - # - # If that step fails we do NOT want to fail the whole image build (Wine - # itself is still useful and the prefix can be provisioned later), so the - # Inno Setup install is wrapped in `|| true` and clearly logged. The PR - # description documents that consumers must verify / provision the Inno - # Setup prefix at + # (`ISCC.exe`), and we initialize a Wine prefix + install Inno Setup 6 + # silently under xvfb during the image build, into # "C:\\Program Files (x86)\\Inno Setup 6" - # before relying on the `.exe` build. + # + # Headless Wine prefix init inside a debootstrap chroot is historically + # flaky, so this is hardened: WINEDLLOVERRIDES disables the Mono/Gecko + # first-run downloads, and `wineserver -w` blocks on the async + # `wineboot --init` before the installer runs. We then HARD-FAIL the image + # build if `ISCC.exe` is missing or does not run under Wine -- better a + # red image build than silently shipping an image that can't build the + # Windows `.exe` installer at publish time. # # The Wine prefix is created for the `juliaci` (uid 1000) user, since that # is the user the sandbox runs as. # ------------------------------------------------------------------ my_chroot(""" - set +e + set -euo pipefail export WINEPREFIX=/home/juliaci/.wine export WINEARCH=win64 export WINEDEBUG=-all export HOME=/home/juliaci + # Disable Wine's Mono/Gecko first-run downloads. Inno Setup needs neither, + # and the prompt otherwise hangs headlessly or flakily fetches them over + # the network during prefix init. + export WINEDLLOVERRIDES="mscoree,mshtml=" mkdir -p "\$WINEPREFIX" - # Initialize the Wine prefix headlessly. + # Initialize the Wine prefix headlessly, then BLOCK until the wineserver + # has fully exited -- `wineboot --init` is asynchronous, so without this + # the Inno Setup install below can run against a half-built prefix. xvfb-run -a wineboot --init - # Give Wine a moment to settle, then make sure the wineserver is gone. wineserver -w || true # Download and silently install Inno Setup 6. @@ -169,17 +173,24 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch wineserver -w || true rm -f /tmp/innosetup.exe - # Report whether the install succeeded so it is visible in the build log. - if [ -e "\$WINEPREFIX/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe" ]; then - echo "INNO SETUP: ISCC.exe installed successfully into the Wine prefix." - else - echo "INNO SETUP WARNING: ISCC.exe was NOT found after install; the Wine" - echo "prefix must be provisioned manually before the .exe build will work." + # HARD requirement: ISCC.exe must be present AND actually run under Wine, + # or the image is useless for Windows installer builds. Fail the image + # build loudly rather than silently shipping a broken prefix. + ISCC="\$WINEPREFIX/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe" + if [ ! -e "\$ISCC" ]; then + echo "ERROR: Inno Setup install failed -- ISCC.exe not found at \$ISCC" >&2 + exit 1 + fi + iscc_out="\$(xvfb-run -a wine "\$ISCC" /? 2>&1 || true)" + if ! echo "\$iscc_out" | grep -qi "inno"; then + echo "ERROR: ISCC.exe is present but did not run correctly under Wine:" >&2 + echo "\$iscc_out" >&2 + exit 1 fi + echo "INNO SETUP: ISCC.exe installed and runs under Wine." # Take ownership so the prefix survives the chown in cleanup_rootfs. - chown -R 1000:1000 /home/juliaci/.wine || true - true + chown -R 1000:1000 /home/juliaci/.wine """) # ------------------------------------------------------------------ From 95e396b079e4cdcc30335c039b8b3deb198e5bef Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 17:50:23 +0000 Subject: [PATCH 04/11] julia_publish: drop hfsprogs (gone from trixie); use libdmg-hfsplus mkfshfs The image build failed in CI: 'Package hfsprogs has no installation candidate' -- hfsprogs (mkfs.hfsplus) was removed from Debian after bullseye. The image already builds libdmg-hfsplus's own mkfshfs, so use that for HFS+ creation and drop the hfsprogs apt package. Smoke checks now verify mkfshfs/hfsplus/dmg are on PATH. Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 992635e..986524e 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -8,10 +8,13 @@ # * python3 -- kms_gpg_sign.py, plist editing, the PE-signature # checker (stdlib only). # * p7zip-full (`7z`) -- repack the Windows `.zip`. -# * hfsprogs + the -- build the macOS `.dmg` on Linux (Mozilla's -# libdmg-hfsplus `hfsplus`/`dmg`/`mkfshfs` tools, the same ones used -# `hfsplus`/`dmg`/ to build Firefox DMGs on Linux). -# `mkfshfs` +# * libdmg-hfsplus -- build the macOS `.dmg` on Linux: the `mkfshfs` +# (`mkfshfs`/ (create the HFS+ filesystem), `hfsplus` (populate it) +# `hfsplus`/`dmg`) and `dmg` (convert to a compressed UDIF) tools, the +# same ones Mozilla uses for Firefox DMGs. (Debian's +# `hfsprogs`/`mkfs.hfsplus` was dropped after bullseye +# and is not in trixie, so we use libdmg-hfsplus's own +# `mkfshfs` instead.) # * wine64 + JRE -- run the Windows Inno Setup compiler (`ISCC.exe`) to # build the `.exe` installer. The Inno Setup 6 Wine # prefix IS provisioned here and the build hard-fails @@ -55,7 +58,6 @@ packages = [ "gcc", "git", "gzip", - "hfsprogs", # provides `mkfs.hfsplus` "libbz2-dev", # libdmg-hfsplus optional bzip2 support "libssl-dev", # libdmg-hfsplus crypto "locales", @@ -199,7 +201,9 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch my_chroot(""" aws --version 7z --help >/dev/null - mkfs.hfsplus -V || true + command -v mkfshfs >/dev/null + command -v hfsplus >/dev/null + command -v dmg >/dev/null python3 --version git --version java -version From 9602d90a9bfe39a9a9f7990b14695b2bbbb765d7 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 17:57:20 +0000 Subject: [PATCH 05/11] julia_publish: install JRE as a Temurin tarball (openjdk .deb fails in chroot) Second CI failure: openjdk-21-jre-headless post-install (default-jre- headless -> ca-certificates-java) fails to configure in the debootstrap chroot -- ca-certificates-java needs a working JVM at configure time, which a minimal chroot doesn't provide. Drop default-jre-headless from apt and install a self-contained Temurin JRE 21 tarball (no dpkg hooks), symlinked to /usr/local/bin/java for jsign. (ISCC.exe runs under Wine, not the JVM, so Java is only needed by jsign.) Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 986524e..9afc431 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -15,12 +15,14 @@ # `hfsprogs`/`mkfs.hfsplus` was dropped after bullseye # and is not in trixie, so we use libdmg-hfsplus's own # `mkfshfs` instead.) -# * wine64 + JRE -- run the Windows Inno Setup compiler (`ISCC.exe`) to -# build the `.exe` installer. The Inno Setup 6 Wine -# prefix IS provisioned here and the build hard-fails -# if it doesn't take; see the comment at the Wine -# install below. -# * jsign + JRE -- Authenticode-sign the Windows binaries via Azure +# * wine64 -- run the Windows Inno Setup compiler (`ISCC.exe`, +# a Windows binary) to build the `.exe` installer. +# The Inno Setup 6 Wine prefix IS provisioned here and +# the build hard-fails if it doesn't take; see the +# comment at the Wine install below. +# * Temurin JRE -- runs jsign (below). Installed as a tarball, not the +# openjdk .deb (whose post-install fails in a chroot). +# * jsign -- Authenticode-sign the Windows binaries via Azure # Trusted Signing. # * git / openssh / -- general plumbing; `curl` + glibc also let the # curl / tar / ... pipeline fetch the Linux `rcodesign` binary at @@ -53,7 +55,6 @@ packages = [ "ca-certificates", "cmake", "curl", - "default-jre-headless", # JRE for jsign and the Inno Setup compiler "g++", # build libdmg-hfsplus from source "gcc", "git", @@ -117,12 +118,33 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch /usr/local/bin/hfsplus || true """) + # ------------------------------------------------------------------ + # JRE for jsign. We install a self-contained Temurin (Adoptium) JRE + # tarball rather than the openjdk .deb: the openjdk JRE's post-install + # (via ca-certificates-java) needs a working JVM at configure time, which + # fails in a minimal debootstrap chroot. A tarball has no dpkg hooks. + # (ISCC.exe runs under Wine, not the JVM -- Java is only for jsign.) + # ------------------------------------------------------------------ + my_chroot(""" + set -euo pipefail + mkdir -p /opt + curl -fsSL "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse" -o /tmp/jre.tar.gz + tar -xzf /tmp/jre.tar.gz -C /opt + rm -f /tmp/jre.tar.gz + jredir="\$(find /opt -maxdepth 1 -type d -name 'jdk-21*' | head -n1)" + if [ -z "\$jredir" ] || [ ! -x "\$jredir/bin/java" ]; then + echo "ERROR: Temurin JRE not found after extract" >&2; ls -la /opt >&2; exit 1 + fi + ln -sf "\$jredir/bin/java" /usr/local/bin/java + java -version + """) + # ------------------------------------------------------------------ # jsign (Authenticode signing via Azure Trusted Signing). We install the # standalone JAR and a tiny `/usr/local/bin/jsign` wrapper so that `jsign` - # is on PATH and uses the image's JRE. (We deliberately avoid the upstream - # `.deb`, whose JRE dependency name is less predictable across Debian - # releases.) + # is on PATH and uses the Temurin JRE installed above. (We deliberately + # avoid the upstream `.deb`, whose JRE dependency is the problematic + # openjdk package we just side-stepped.) # ------------------------------------------------------------------ my_chroot(""" mkdir -p /usr/local/lib /usr/local/bin From 46e8a927a62ae30ea32d92463afd2efc0b7e22e3 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 18:16:14 +0000 Subject: [PATCH 06/11] julia_publish: build newfs_hfs from diskdev_cmds (libdmg-hfsplus has no mkfs) mozilla/libdmg-hfsplus builds only the 'dmg' and 'hfsplus' tools -- it has no HFS+ *creation* tool, so the image build was failing on a non-existent 'mkfshfs' binary. Create the filesystem with newfs_hfs (the 'mkfs.hfsplus' from Apple's diskdev_cmds, which Debian shipped as hfsprogs before dropping it after bullseye), built from the same pinned Fedora-hosted tarball Mozilla uses for Firefox DMGs. Only the newfs_hfs target is built (with gcc; the top-level Makefile's clang/-fblocks is only needed for fsck_hfs). Adds uuid-dev for . build_dmg.sh now defaults MKFSHFS_TOOL=newfs_hfs. Also temporarily trims the CI matrix to julia_publish.x86_64 only, to avoid burning resources on the other ~36 images while iterating; revert before merge. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/linux.yml | 72 +++++++++++++++++------------------ linux/julia_publish.jl | 75 +++++++++++++++++++++++++++++-------- 2 files changed, 96 insertions(+), 51 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ee73c06..2424694 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,24 +34,24 @@ jobs: matrix: image: # Build a bunch of different "agent" rootfs images. - - {slug: 'agent_linux.aarch64', build_on: '-arm'} - - {slug: 'agent_linux.armv7l', build_on: ''} - - {slug: 'agent_linux.ppc64le', build_on: ''} - - {slug: 'agent_linux.x86_64', build_on: ''} - - {slug: 'agent_linux.i686', build_on: ''} + # TEMP-TRIM - {slug: 'agent_linux.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'agent_linux.armv7l', build_on: ''} + # TEMP-TRIM - {slug: 'agent_linux.ppc64le', build_on: ''} + # TEMP-TRIM - {slug: 'agent_linux.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'agent_linux.i686', build_on: ''} # The `aws_uploader` image is a `debian`-based image that # contains just `awscli`, for usage in secured pipelines # that need to upload to AWS. - - {slug: 'aws_uploader.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'aws_uploader.x86_64', build_on: ''} # The `debian_minimal` image is a `debian`-based image that # contains no packages. - - {slug: 'debian_minimal.aarch64', build_on: '-arm'} - - {slug: 'debian_minimal.armv7l', build_on: ''} - - {slug: 'debian_minimal.ppc64le', build_on: ''} - - {slug: 'debian_minimal.x86_64', build_on: ''} - - {slug: 'debian_minimal.i686', build_on: ''} + # TEMP-TRIM - {slug: 'debian_minimal.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'debian_minimal.armv7l', build_on: ''} + # TEMP-TRIM - {slug: 'debian_minimal.ppc64le', build_on: ''} + # TEMP-TRIM - {slug: 'debian_minimal.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'debian_minimal.i686', build_on: ''} # The `julia_publish` image carries the full signing/packaging # toolchain (AWS CLI, p7zip, libdmg-hfsplus, wine, jsign, ...) used @@ -60,55 +60,55 @@ jobs: - {slug: 'julia_publish.x86_64', build_on: ''} # The `latex` image is used to build the PDF docs - - {slug: 'latex.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'latex.x86_64', build_on: ''} # The `llvm_passes` image contains `gfortran`, so we use it for our # "from source" (`USE_BINARYBUILDER=0`) jobs. - - {slug: 'llvm_passes.aarch64', build_on: '-arm'} - - {slug: 'llvm_passes.armv7l', build_on: ''} - - {slug: 'llvm_passes.i686', build_on: ''} - - {slug: 'llvm_passes.powerpc64le', build_on: ''} - - {slug: 'llvm_passes.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'llvm_passes.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'llvm_passes.armv7l', build_on: ''} + # TEMP-TRIM - {slug: 'llvm_passes.i686', build_on: ''} + # TEMP-TRIM - {slug: 'llvm_passes.powerpc64le', build_on: ''} + # TEMP-TRIM - {slug: 'llvm_passes.x86_64', build_on: ''} # The `npm_linux` image is a helper for ecosystem jobs that need to build NPM packages - - {slug: 'npm_linux.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'npm_linux.x86_64', build_on: ''} # The `package_linux` images are all `debian`-based. - - {slug: 'package_linux.aarch64', build_on: '-arm'} - - {slug: 'package_linux.armv7l', build_on: ''} - - {slug: 'package_linux.i686', build_on: ''} - - {slug: 'package_linux.powerpc64le', build_on: ''} - - {slug: 'package_linux.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'package_linux.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'package_linux.armv7l', build_on: ''} + # TEMP-TRIM - {slug: 'package_linux.i686', build_on: ''} + # TEMP-TRIM - {slug: 'package_linux.powerpc64le', build_on: ''} + # TEMP-TRIM - {slug: 'package_linux.x86_64', build_on: ''} # The `package_musl` image is `alpine`-based. - - {slug: 'package_musl.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'package_musl.x86_64', build_on: ''} # The `pkgserver_logsync` image is a helper for https://github.com/JuliaPackaging/PkgServerLogAnalysis.jl - - {slug: 'pkgserver_logsync.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'pkgserver_logsync.x86_64', build_on: ''} # The `rr` image is `debian`-based. # It is used for building rr from source and running the rr test suite. - - {slug: 'rr.aarch64', build_on: '-arm'} - - {slug: 'rr.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'rr.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'rr.x86_64', build_on: ''} # The `tester_linux` images are all `debian`-based. # They do not include the compiler toolchain. - - {slug: 'tester_linux.aarch64', build_on: '-arm'} - - {slug: 'tester_linux.armv7l', build_on: ''} - - {slug: 'tester_linux.i686', build_on: ''} - - {slug: 'tester_linux.powerpc64le', build_on: ''} - - {slug: 'tester_linux.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'tester_linux.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'tester_linux.armv7l', build_on: ''} + # TEMP-TRIM - {slug: 'tester_linux.i686', build_on: ''} + # TEMP-TRIM - {slug: 'tester_linux.powerpc64le', build_on: ''} + # TEMP-TRIM - {slug: 'tester_linux.x86_64', build_on: ''} # The `tester_musl` image is `alpine`-based. # It does not include the compiler toolchain. - - {slug: 'tester_musl.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'tester_musl.x86_64', build_on: ''} # The `xvfb` image is used in the CI for the PkgEval.jl repo - - {slug: 'xvfb.aarch64', build_on: '-arm'} - - {slug: 'xvfb.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'xvfb.aarch64', build_on: '-arm'} + # TEMP-TRIM - {slug: 'xvfb.x86_64', build_on: ''} # The `yggdrasil` image is used in the CI for the Yggdrasil repo - - {slug: 'yggdrasil.x86_64', build_on: ''} + # TEMP-TRIM - {slug: 'yggdrasil.x86_64', build_on: ''} steps: - uses: actions/checkout@v6 with: diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 9afc431..3c4819a 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -8,13 +8,14 @@ # * python3 -- kms_gpg_sign.py, plist editing, the PE-signature # checker (stdlib only). # * p7zip-full (`7z`) -- repack the Windows `.zip`. -# * libdmg-hfsplus -- build the macOS `.dmg` on Linux: the `mkfshfs` -# (`mkfshfs`/ (create the HFS+ filesystem), `hfsplus` (populate it) -# `hfsplus`/`dmg`) and `dmg` (convert to a compressed UDIF) tools, the -# same ones Mozilla uses for Firefox DMGs. (Debian's -# `hfsprogs`/`mkfs.hfsplus` was dropped after bullseye -# and is not in trixie, so we use libdmg-hfsplus's own -# `mkfshfs` instead.) +# * HFS+/DMG tooling -- build the macOS `.dmg` on Linux. `hfsplus` (populate +# (`newfs_hfs`/ an HFS+ image) and `dmg` (HFS+ -> compressed UDIF) +# `hfsplus`/`dmg`) come from mozilla/libdmg-hfsplus. The HFS+ *creation* +# tool `newfs_hfs` is NOT part of libdmg-hfsplus; it is +# the `mkfs.hfsplus` from Debian's `hfsprogs` (dropped +# after bullseye, gone from trixie), built here from the +# diskdev_cmds source -- the same toolchain Mozilla uses +# for Firefox DMGs. # * wine64 -- run the Windows Inno Setup compiler (`ISCC.exe`, # a Windows binary) to build the `.exe` installer. # The Inno Setup 6 Wine prefix IS provisioned here and @@ -69,6 +70,7 @@ packages = [ "python3", "tar", "unzip", + "uuid-dev", # newfs_hfs needs (uuid_t) at compile time "wine", # Windows emulation for the Inno Setup compiler "wine64", "xauth", # for headless Wine prefix init via xvfb @@ -89,9 +91,11 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch install_awscli(rootfs, chroot_ENV, arch) # ------------------------------------------------------------------ - # libdmg-hfsplus: Mozilla's `hfsplus` / `dmg` / `mkfshfs` CLI tools for - # assembling a macOS `.dmg` on Linux. Small CMake project, built from - # source in the chroot and installed into /usr/local/bin. + # libdmg-hfsplus: Mozilla's `hfsplus` (populate an HFS+ image) and `dmg` + # (HFS+ -> compressed UDIF) CLI tools for assembling a macOS `.dmg` on + # Linux. Small CMake project, built from source in the chroot and + # installed into /usr/local/bin. (libdmg-hfsplus has NO mkfs tool -- the + # HFS+ *creation* tool `newfs_hfs` is built in the next step.) # ------------------------------------------------------------------ my_chroot(""" tmpdir="\$(mktemp -d)" @@ -100,10 +104,10 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch mkdir -p build && cd build cmake .. make -j"\$(nproc)" - # The build produces `dmg/dmg`, `hfs/hfsplus` and `hfs/mkfshfs`. Locate - # them by name rather than hardcoding paths, so the install is robust to - # small layout changes in the project. - for tool in dmg hfsplus mkfshfs; do + # The build produces `dmg/dmg` and `hfs/hfsplus`. Locate them by name + # rather than hardcoding paths, so the install is robust to small layout + # changes in the project. + for tool in dmg hfsplus; do found="\$(find . -type f -name "\$tool" -perm -u+x | head -n1)" if [ -z "\$found" ]; then echo "ERROR: libdmg-hfsplus build did not produce '\$tool'" >&2 @@ -118,6 +122,47 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch /usr/local/bin/hfsplus || true """) + # ------------------------------------------------------------------ + # newfs_hfs: the HFS+ filesystem creation tool (`mkfs.hfsplus`). This is + # NOT part of libdmg-hfsplus. It comes from Apple's `diskdev_cmds` (the + # source Debian packaged as `hfsprogs`, dropped after bullseye), built from + # the same pinned Fedora-hosted tarball Mozilla uses for Firefox DMGs. + # + # The top-level Makefile hardcodes clang + `-fblocks` (only needed for + # `fsck_hfs`, which we don't build), so we build the `newfs_hfs` target + # directly with gcc and permissive flags for the ~2009-era C. Drop the + # `` includes (gone from modern glibc) first. Links only + # `-lcrypto` (libssl3 is present via ca-certificates); is a + # compile-time-only dependency (uuid-dev), satisfied via the type, not libuuid. + # ------------------------------------------------------------------ + my_chroot(""" + set -euo pipefail + url="https://src.fedoraproject.org/repo/pkgs/hfsplus-tools/diskdev_cmds-540.1.linux3.tar.gz/0435afc389b919027b69616ad1b05709/diskdev_cmds-540.1.linux3.tar.gz" + sha="b01b203a97f9a3bf36a027c13ddfc59292730552e62722d690d33bd5c24f5497" + tmpdir="\$(mktemp -d)" + curl -fsSL "\$url" -o "\$tmpdir/diskdev_cmds.tar.gz" + echo "\$sha \$tmpdir/diskdev_cmds.tar.gz" | sha256sum -c - + tar -xzf "\$tmpdir/diskdev_cmds.tar.gz" -C "\$tmpdir" + cd "\$tmpdir/diskdev_cmds-540.1.linux3" + { grep -rl 'sysctl.h' . || true; } | xargs --no-run-if-empty sed -i '/sysctl.h/d' + cd newfs_hfs.tproj + make -f Makefile.lnx CC=gcc \\ + CFLAGS="-O2 -I../include -D_FILE_OFFSET_BITS=64 -DLINUX=1 -DBSD=1 -Wno-implicit-function-declaration -Wno-implicit-int -fcommon" \\ + LDFLAGS="" \\ + newfs_hfs + install -m755 newfs_hfs /usr/local/bin/newfs_hfs + cd / + rm -rf "\$tmpdir" + # Sanity check: format a scratch image and confirm the HFS+ signature ('H+'). + scratch="\$(mktemp)" + truncate -s 16M "\$scratch" + newfs_hfs -v SmokeTest "\$scratch" + if [ "\$(dd if="\$scratch" bs=1 skip=1024 count=2 2>/dev/null)" != "H+" ]; then + echo "ERROR: newfs_hfs did not produce an HFS+ volume" >&2; exit 1 + fi + rm -f "\$scratch" + """) + # ------------------------------------------------------------------ # JRE for jsign. We install a self-contained Temurin (Adoptium) JRE # tarball rather than the openjdk .deb: the openjdk JRE's post-install @@ -223,7 +268,7 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch my_chroot(""" aws --version 7z --help >/dev/null - command -v mkfshfs >/dev/null + command -v newfs_hfs >/dev/null command -v hfsplus >/dev/null command -v dmg >/dev/null python3 --version From 26cfc5c23d51512b15391874d7b1bb0763e69d24 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 18:22:47 +0000 Subject: [PATCH 07/11] julia_publish: fix Temurin java via wrapper (libjli.so resolution) A bare symlink of the java binary fails at runtime with 'libjli.so: cannot open shared object file' -- the launcher resolves libjli.so relative to argv[0]'s directory (/usr/local/lib) rather than its real install dir. Use a stable /opt/temurin symlink + a wrapper that execs java by its real path so libjli.so resolves correctly. Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 3c4819a..ac424d5 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -180,7 +180,14 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch if [ -z "\$jredir" ] || [ ! -x "\$jredir/bin/java" ]; then echo "ERROR: Temurin JRE not found after extract" >&2; ls -la /opt >&2; exit 1 fi - ln -sf "\$jredir/bin/java" /usr/local/bin/java + # Expose java via a stable path + wrapper. A bare symlink of the `java` + # binary breaks: the launcher resolves libjli.so relative to argv[0]'s + # directory (here /usr/local/lib), not its real install dir, and dies with + # "libjli.so: cannot open shared object file". A wrapper that execs java by + # its real path resolves libjli.so correctly. + ln -sfn "\$jredir" /opt/temurin + printf '%s\\n' '#!/bin/sh' 'exec /opt/temurin/bin/java "\$@"' > /usr/local/bin/java + chmod +x /usr/local/bin/java java -version """) From cd55d3b9034024ec5e725c6fd712758fee88efa8 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 18:28:55 +0000 Subject: [PATCH 08/11] julia_publish: set LD_LIBRARY_PATH in java wrapper (chroot has no /proc) The real cause of the 'libjli.so: cannot open' failure is that the java launcher locates its bundled libjli.so/libjvm.so via an $ORIGIN-relative RPATH, and glibc derives $ORIGIN from /proc/self/exe -- but the debootstrap build chroot mounts no /proc, so $ORIGIN expansion fails and the libraries are never found (independent of symlink vs real-path invocation). Set LD_LIBRARY_PATH=/opt/temurin/lib:/opt/temurin/lib/server in the wrapper so the libs resolve by absolute path. Validated under dash with the Temurin 21 JRE both with LD_LIBRARY_PATH unset and preset. Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index ac424d5..8b198cb 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -180,13 +180,15 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch if [ -z "\$jredir" ] || [ ! -x "\$jredir/bin/java" ]; then echo "ERROR: Temurin JRE not found after extract" >&2; ls -la /opt >&2; exit 1 fi - # Expose java via a stable path + wrapper. A bare symlink of the `java` - # binary breaks: the launcher resolves libjli.so relative to argv[0]'s - # directory (here /usr/local/lib), not its real install dir, and dies with - # "libjli.so: cannot open shared object file". A wrapper that execs java by - # its real path resolves libjli.so correctly. + # Expose java via a stable path + wrapper. The `java` launcher finds its + # bundled libjli.so / libjvm.so only via an `\$ORIGIN`-relative RPATH, and + # glibc computes `\$ORIGIN` by reading /proc/self/exe. The debootstrap build + # chroot has no /proc mounted, so `\$ORIGIN` expansion fails and java dies + # with "libjli.so: cannot open shared object file" regardless of how it is + # invoked (symlink or real path). Set LD_LIBRARY_PATH explicitly so the + # libraries are found by absolute path, independent of /proc / \$ORIGIN. ln -sfn "\$jredir" /opt/temurin - printf '%s\\n' '#!/bin/sh' 'exec /opt/temurin/bin/java "\$@"' > /usr/local/bin/java + printf '%s\\n' '#!/bin/sh' 'exec env LD_LIBRARY_PATH="/opt/temurin/lib:/opt/temurin/lib/server\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" /opt/temurin/bin/java "\$@"' > /usr/local/bin/java chmod +x /usr/local/bin/java java -version """) From 7d39d6a1dc9ed892f6131df974d9812e21a35cb2 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 21:01:57 +0000 Subject: [PATCH 09/11] julia_publish: mount /proc,/dev/pts,/dev/shm for Wine prefix init Wine's preloader reads /proc/self/maps and Wine needs ptys + shared memory, but the bare 'sudo chroot' build environment mounts none of these, so the Inno Setup prefix init failed with 'wine: could not load ntdll.so'. Mount /proc, /dev/pts and /dev/shm just for the Wine step, with a trap that kills wineserver and unmounts them on exit so they never leak into the image tarball. (Wine runs fine as root here -- the prior run reached the preloader, not a root refusal.) Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index 8b198cb..dff033c 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -217,8 +217,12 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch # "C:\\Program Files (x86)\\Inno Setup 6" # # Headless Wine prefix init inside a debootstrap chroot is historically - # flaky, so this is hardened: WINEDLLOVERRIDES disables the Mono/Gecko - # first-run downloads, and `wineserver -w` blocks on the async + # flaky, so this is hardened: we first mount /proc, /dev/pts and /dev/shm + # (the bare `sudo chroot` build env mounts none of these, and Wine's + # preloader reads /proc/self/maps -- without it Wine fails immediately with + # "could not load ntdll.so"), unmounting them again via a trap so they never + # leak into the image tarball; WINEDLLOVERRIDES disables the Mono/Gecko + # first-run downloads; and `wineserver -w` blocks on the async # `wineboot --init` before the installer runs. We then HARD-FAIL the image # build if `ISCC.exe` is missing or does not run under Wine -- better a # red image build than silently shipping an image that can't build the @@ -229,6 +233,21 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch # ------------------------------------------------------------------ my_chroot(""" set -euo pipefail + # Mount the virtual filesystems Wine needs (see comment above); a trap + # tears them down on exit (success or failure) so they never end up in the + # image tarball. Kill wineserver first so nothing holds /proc open. + cleanup_wine_mounts() { + wineserver -k 2>/dev/null || true + umount /dev/shm 2>/dev/null || umount -l /dev/shm 2>/dev/null || true + umount /dev/pts 2>/dev/null || umount -l /dev/pts 2>/dev/null || true + umount /proc 2>/dev/null || umount -l /proc 2>/dev/null || true + } + trap cleanup_wine_mounts EXIT + mount -t proc proc /proc + mkdir -p /dev/pts /dev/shm + mount -t devpts devpts /dev/pts || true + mount -t tmpfs tmpfs /dev/shm || true + export WINEPREFIX=/home/juliaci/.wine export WINEARCH=win64 export WINEDEBUG=-all From c52951f5f101731f4cf18e52e6b1512227db643b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 21:08:04 +0000 Subject: [PATCH 10/11] julia_publish: install 32-bit Wine (wine32:i386) for Inno Setup WoW64 Inno Setup's installer and ISCC.exe are 32-bit Windows apps. With only wine64, the win64 prefix's C:\windows\syswow64 is unpopulated and the installer dies with 'failed to load syswow64\ntdll.dll error c0000135' (DLL_NOT_FOUND). Enable the i386 architecture and install wine32:i386 (pulls libwine:i386 + wine32-preloader) so WoW64 can run the 32-bit PE. Co-Authored-By: Claude Opus 4.8 --- linux/julia_publish.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/linux/julia_publish.jl b/linux/julia_publish.jl index dff033c..fd5766d 100644 --- a/linux/julia_publish.jl +++ b/linux/julia_publish.jl @@ -208,6 +208,23 @@ artifact_hash, tarball_path, = debootstrap(arch, image; release = "trixie", arch jsign --version """) + # ------------------------------------------------------------------ + # 32-bit Wine support (WoW64). Inno Setup -- both its installer and the + # `ISCC.exe` compiler -- is a 32-bit Windows application, so a win64 Wine + # prefix must be able to run 32-bit PE binaries via C:\\windows\\syswow64. + # Debian's `wine64` alone does NOT populate syswow64: without the i386 Wine + # libraries the installer fails with + # wine: failed to load L"...syswow64\\ntdll.dll" error c0000135 (DLL_NOT_FOUND) + # so we enable the i386 architecture and install the 32-bit Wine package. + # ------------------------------------------------------------------ + my_chroot(""" + set -euo pipefail + dpkg --add-architecture i386 + apt-get update + apt-get install -y --no-install-recommends wine32:i386 + rm -rf /var/lib/apt/lists/* + """) + # ------------------------------------------------------------------ # Wine + Inno Setup 6. # From 46bf876fe6b1dd3281f6721915ea140807b3fe1b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 Jun 2026 21:15:19 +0000 Subject: [PATCH 11/11] Revert temporary CI matrix trim (restore all images) The matrix was trimmed to only julia_publish.x86_64 during bring-up to avoid rebuilding the other ~36 images on each iteration. julia_publish now builds green, so restore the full matrix; the only remaining diff vs main is the one added julia_publish.x86_64 entry. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/linux.yml | 72 ++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2424694..ee73c06 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,24 +34,24 @@ jobs: matrix: image: # Build a bunch of different "agent" rootfs images. - # TEMP-TRIM - {slug: 'agent_linux.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'agent_linux.armv7l', build_on: ''} - # TEMP-TRIM - {slug: 'agent_linux.ppc64le', build_on: ''} - # TEMP-TRIM - {slug: 'agent_linux.x86_64', build_on: ''} - # TEMP-TRIM - {slug: 'agent_linux.i686', build_on: ''} + - {slug: 'agent_linux.aarch64', build_on: '-arm'} + - {slug: 'agent_linux.armv7l', build_on: ''} + - {slug: 'agent_linux.ppc64le', build_on: ''} + - {slug: 'agent_linux.x86_64', build_on: ''} + - {slug: 'agent_linux.i686', build_on: ''} # The `aws_uploader` image is a `debian`-based image that # contains just `awscli`, for usage in secured pipelines # that need to upload to AWS. - # TEMP-TRIM - {slug: 'aws_uploader.x86_64', build_on: ''} + - {slug: 'aws_uploader.x86_64', build_on: ''} # The `debian_minimal` image is a `debian`-based image that # contains no packages. - # TEMP-TRIM - {slug: 'debian_minimal.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'debian_minimal.armv7l', build_on: ''} - # TEMP-TRIM - {slug: 'debian_minimal.ppc64le', build_on: ''} - # TEMP-TRIM - {slug: 'debian_minimal.x86_64', build_on: ''} - # TEMP-TRIM - {slug: 'debian_minimal.i686', build_on: ''} + - {slug: 'debian_minimal.aarch64', build_on: '-arm'} + - {slug: 'debian_minimal.armv7l', build_on: ''} + - {slug: 'debian_minimal.ppc64le', build_on: ''} + - {slug: 'debian_minimal.x86_64', build_on: ''} + - {slug: 'debian_minimal.i686', build_on: ''} # The `julia_publish` image carries the full signing/packaging # toolchain (AWS CLI, p7zip, libdmg-hfsplus, wine, jsign, ...) used @@ -60,55 +60,55 @@ jobs: - {slug: 'julia_publish.x86_64', build_on: ''} # The `latex` image is used to build the PDF docs - # TEMP-TRIM - {slug: 'latex.x86_64', build_on: ''} + - {slug: 'latex.x86_64', build_on: ''} # The `llvm_passes` image contains `gfortran`, so we use it for our # "from source" (`USE_BINARYBUILDER=0`) jobs. - # TEMP-TRIM - {slug: 'llvm_passes.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'llvm_passes.armv7l', build_on: ''} - # TEMP-TRIM - {slug: 'llvm_passes.i686', build_on: ''} - # TEMP-TRIM - {slug: 'llvm_passes.powerpc64le', build_on: ''} - # TEMP-TRIM - {slug: 'llvm_passes.x86_64', build_on: ''} + - {slug: 'llvm_passes.aarch64', build_on: '-arm'} + - {slug: 'llvm_passes.armv7l', build_on: ''} + - {slug: 'llvm_passes.i686', build_on: ''} + - {slug: 'llvm_passes.powerpc64le', build_on: ''} + - {slug: 'llvm_passes.x86_64', build_on: ''} # The `npm_linux` image is a helper for ecosystem jobs that need to build NPM packages - # TEMP-TRIM - {slug: 'npm_linux.x86_64', build_on: ''} + - {slug: 'npm_linux.x86_64', build_on: ''} # The `package_linux` images are all `debian`-based. - # TEMP-TRIM - {slug: 'package_linux.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'package_linux.armv7l', build_on: ''} - # TEMP-TRIM - {slug: 'package_linux.i686', build_on: ''} - # TEMP-TRIM - {slug: 'package_linux.powerpc64le', build_on: ''} - # TEMP-TRIM - {slug: 'package_linux.x86_64', build_on: ''} + - {slug: 'package_linux.aarch64', build_on: '-arm'} + - {slug: 'package_linux.armv7l', build_on: ''} + - {slug: 'package_linux.i686', build_on: ''} + - {slug: 'package_linux.powerpc64le', build_on: ''} + - {slug: 'package_linux.x86_64', build_on: ''} # The `package_musl` image is `alpine`-based. - # TEMP-TRIM - {slug: 'package_musl.x86_64', build_on: ''} + - {slug: 'package_musl.x86_64', build_on: ''} # The `pkgserver_logsync` image is a helper for https://github.com/JuliaPackaging/PkgServerLogAnalysis.jl - # TEMP-TRIM - {slug: 'pkgserver_logsync.x86_64', build_on: ''} + - {slug: 'pkgserver_logsync.x86_64', build_on: ''} # The `rr` image is `debian`-based. # It is used for building rr from source and running the rr test suite. - # TEMP-TRIM - {slug: 'rr.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'rr.x86_64', build_on: ''} + - {slug: 'rr.aarch64', build_on: '-arm'} + - {slug: 'rr.x86_64', build_on: ''} # The `tester_linux` images are all `debian`-based. # They do not include the compiler toolchain. - # TEMP-TRIM - {slug: 'tester_linux.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'tester_linux.armv7l', build_on: ''} - # TEMP-TRIM - {slug: 'tester_linux.i686', build_on: ''} - # TEMP-TRIM - {slug: 'tester_linux.powerpc64le', build_on: ''} - # TEMP-TRIM - {slug: 'tester_linux.x86_64', build_on: ''} + - {slug: 'tester_linux.aarch64', build_on: '-arm'} + - {slug: 'tester_linux.armv7l', build_on: ''} + - {slug: 'tester_linux.i686', build_on: ''} + - {slug: 'tester_linux.powerpc64le', build_on: ''} + - {slug: 'tester_linux.x86_64', build_on: ''} # The `tester_musl` image is `alpine`-based. # It does not include the compiler toolchain. - # TEMP-TRIM - {slug: 'tester_musl.x86_64', build_on: ''} + - {slug: 'tester_musl.x86_64', build_on: ''} # The `xvfb` image is used in the CI for the PkgEval.jl repo - # TEMP-TRIM - {slug: 'xvfb.aarch64', build_on: '-arm'} - # TEMP-TRIM - {slug: 'xvfb.x86_64', build_on: ''} + - {slug: 'xvfb.aarch64', build_on: '-arm'} + - {slug: 'xvfb.x86_64', build_on: ''} # The `yggdrasil` image is used in the CI for the Yggdrasil repo - # TEMP-TRIM - {slug: 'yggdrasil.x86_64', build_on: ''} + - {slug: 'yggdrasil.x86_64', build_on: ''} steps: - uses: actions/checkout@v6 with: