@@ -193,7 +193,6 @@ jobs:
193193 build-musl :
194194 name : ${{ matrix.target }}
195195 runs-on : ${{ matrix.host }}
196- container : node:22-alpine
197196 needs : guard
198197 strategy :
199198 fail-fast : false
@@ -202,39 +201,41 @@ jobs:
202201 - { host: ubuntu-latest, target: x86_64-unknown-linux-musl }
203202 - { host: ubuntu-24.04-arm, target: aarch64-unknown-linux-musl }
204203
205- env :
206- RUSTFLAGS : ' -C target-feature=-crt-static'
207-
204+ # Alpine is entered per step rather than declared as the job's `container:`.
205+ # GitHub runs its JavaScript actions -- checkout and upload-artifact among
206+ # them -- inside the job container, and refuses to do that for an Alpine
207+ # image on an arm64 runner: "JavaScript Actions in Alpine containers are
208+ # only supported on x64 Linux runners". Keeping the actions on the host and
209+ # putting only the build in Alpine is what lets one job serve both
210+ # architectures, and both remain native builds on native hardware.
208211 steps :
209212 - uses : actions/checkout@v7
210213
211- - name : Install toolchain
212- run : apk add --no-cache build-base clang clang-dev llvm-dev rust cargo make bash python3 git
213-
214- - name : Install dependencies
215- run : npm ci --no-audit --no-fund
216- working-directory : ${{ env.NODE_DIR }}
217-
218- - name : Build the C hash engine
219- run : make build-core BUILD_TYPE=release
220-
221- - name : Build native addon
222- run : npm run build
223- working-directory : ${{ env.NODE_DIR }}
224-
225- # The whole point of building in Alpine instead of cross-compiling.
226- - name : Verify the artifact loads and computes
227- run : node scripts/verify-artifact.js
228- working-directory : ${{ env.NODE_DIR }}
229-
230- # A native build is named by the host, so this is also the check that the
231- # runner really was musl and not a glibc image that quietly stood in.
232- - name : Confirm the artifact is the musl one
233- shell : sh
214+ - name : Build and verify inside Alpine
234215 run : |
235- cd "$NODE_DIR"
216+ cat > "$RUNNER_TEMP/build-musl.sh" <<'SCRIPT'
217+ set -eu
218+ apk add --no-cache build-base clang clang-dev llvm-dev rust cargo make bash python3 git
219+ cd "/work/$NODE_DIR"
220+ npm ci --no-audit --no-fund
221+ cd /work
222+ make build-core BUILD_TYPE=release
223+ cd "/work/$NODE_DIR"
224+ npm run build
225+ # The whole point of building in Alpine instead of cross-compiling.
226+ node scripts/verify-artifact.js
227+ # A native build is named by the host, so this is also the check that
228+ # the runner really was musl and not a glibc image standing in.
236229 ls *.node
237- test -f retrigger-nodejs-bindings.$(node -p "process.arch === 'x64' ? 'linux-x64-musl' : 'linux-arm64-musl'").node
230+ test -f "retrigger-nodejs-bindings.$(node -p "process.arch === 'x64' ? 'linux-x64-musl' : 'linux-arm64-musl'").node"
231+ SCRIPT
232+ docker run --rm \
233+ -v "$PWD:/work" \
234+ -v "$RUNNER_TEMP/build-musl.sh:/build-musl.sh:ro" \
235+ -w /work \
236+ -e NODE_DIR="$NODE_DIR" \
237+ -e RUSTFLAGS='-C target-feature=-crt-static' \
238+ node:22-alpine sh /build-musl.sh
238239
239240 - uses : actions/upload-artifact@v7
240241 with :
0 commit comments