diff --git a/.github/workflows/devcontainer-release.yml b/.github/workflows/devcontainer-release.yml index c92dd5c50..ccde6cb3f 100644 --- a/.github/workflows/devcontainer-release.yml +++ b/.github/workflows/devcontainer-release.yml @@ -57,18 +57,32 @@ jobs: runner: ubuntu-latest rust_target: x86_64-unknown-linux-gnu pypi_wheel_builder: manylinux + pypi_manylinux_container: quay.io/pypa/manylinux2014_x86_64 - target: linux-x64-musl runner: ubuntu-latest rust_target: x86_64-unknown-linux-musl pypi_wheel_builder: host + pypi_manylinux_container: '' + - target: linux-arm64 + runner: ubuntu-24.04-arm + rust_target: aarch64-unknown-linux-gnu + pypi_wheel_builder: manylinux + pypi_manylinux_container: quay.io/pypa/manylinux2014_aarch64 + - target: linux-arm64-musl + runner: ubuntu-24.04-arm + rust_target: aarch64-unknown-linux-musl + pypi_wheel_builder: host + pypi_manylinux_container: '' - target: darwin-x64 runner: macos-15-intel rust_target: x86_64-apple-darwin pypi_wheel_builder: host + pypi_manylinux_container: '' - target: darwin-arm64 runner: macos-15 rust_target: aarch64-apple-darwin pypi_wheel_builder: host + pypi_manylinux_container: '' runs-on: ${{ matrix.runner }} env: CARGO_TOML_PATH: cmd/devcontainer/Cargo.toml @@ -84,7 +98,7 @@ jobs: targets: ${{ matrix.rust_target }} - name: Install musl linker - if: matrix.rust_target == 'x86_64-unknown-linux-musl' + if: endsWith(matrix.rust_target, '-unknown-linux-musl') shell: bash run: | set -euo pipefail @@ -136,6 +150,7 @@ jobs: command: build target: ${{ matrix.rust_target }} manylinux: 2014 + container: ${{ matrix.pypi_manylinux_container }} args: --release --out dist/wheels --compatibility pypi - name: Setup uv @@ -314,5 +329,7 @@ jobs: dist/npm/devcontainer-rs-devcontainer-darwin-arm64 \ dist/npm/devcontainer-rs-devcontainer-linux-x64-gnu \ dist/npm/devcontainer-rs-devcontainer-linux-x64-musl \ + dist/npm/devcontainer-rs-devcontainer-linux-arm64-gnu \ + dist/npm/devcontainer-rs-devcontainer-linux-arm64-musl \ dist/npm/devcontainer-rs \ dist/npm/devcontainer-rs-cli diff --git a/build/check-npm-publish-workflow.js b/build/check-npm-publish-workflow.js index b13dd7395..21937f5bc 100644 --- a/build/check-npm-publish-workflow.js +++ b/build/check-npm-publish-workflow.js @@ -73,6 +73,26 @@ assert.match( /node build\/publish-npm-packages\.js /, "npm publish job should use the repo-owned idempotent npm publish helper", ); +assert.match( + buildJob, + /rust_target:\s*aarch64-unknown-linux-gnu\b/, + "release build matrix should include Linux arm64 GNU artifacts", +); +assert.match( + buildJob, + /rust_target:\s*aarch64-unknown-linux-musl\b/, + "release build matrix should include Linux arm64 musl artifacts", +); +assert.match( + npmJob, + /dist\/npm\/devcontainer-rs-devcontainer-linux-arm64-gnu\b/, + "npm publish job should publish the Linux arm64 GNU native package", +); +assert.match( + npmJob, + /dist\/npm\/devcontainer-rs-devcontainer-linux-arm64-musl\b/, + "npm publish job should publish the Linux arm64 musl native package", +); assert.doesNotMatch( npmJob, /\bnpm publish --access public\b/, diff --git a/build/test-npm-package-smoke.js b/build/test-npm-package-smoke.js index 84a908a9d..0b668c894 100644 --- a/build/test-npm-package-smoke.js +++ b/build/test-npm-package-smoke.js @@ -16,3 +16,17 @@ test("npm package smoke selects the musl native package on linux x64 musl", () = "@devcontainer-rs/devcontainer-linux-x64-musl", ); }); + +test("npm package smoke selects the musl native package on linux arm64 musl", () => { + const target = detectHostTarget({ + platform: "linux", + arch: "arm64", + libc: "musl", + }); + + assert.equal(target.target, "linux-arm64-musl"); + assert.equal( + target.packageName, + "@devcontainer-rs/devcontainer-linux-arm64-musl", + ); +}); diff --git a/build/test-npm-wrapper.js b/build/test-npm-wrapper.js index d0b4b44fe..14333dfd3 100644 --- a/build/test-npm-wrapper.js +++ b/build/test-npm-wrapper.js @@ -58,6 +58,32 @@ test("maps linux x64 musl to the musl native package", () => { ); }); +test("maps linux arm64 glibc to the arm64 gnu native package", () => { + const resolved = resolveBinaryPackage({ + platform: "linux", + arch: "arm64", + libc: "gnu", + }); + assert.equal(resolved.target, "linux-arm64-gnu"); + assert.equal( + resolved.packageName, + "@devcontainer-rs/devcontainer-linux-arm64-gnu", + ); +}); + +test("maps linux arm64 musl to the arm64 musl native package", () => { + const resolved = resolveBinaryPackage({ + platform: "linux", + arch: "arm64", + libc: "musl", + }); + assert.equal(resolved.target, "linux-arm64-musl"); + assert.equal( + resolved.packageName, + "@devcontainer-rs/devcontainer-linux-arm64-musl", + ); +}); + test("detects musl when ldd reports on stderr and exits non-zero", () => { const detected = detectLibc({ platform: "linux", @@ -118,6 +144,8 @@ test("runtime config optional dependency set matches supported targets", () => { assert.deepEqual(packageNames.sort(), [ "@devcontainer-rs/devcontainer-darwin-arm64", "@devcontainer-rs/devcontainer-darwin-x64", + "@devcontainer-rs/devcontainer-linux-arm64-gnu", + "@devcontainer-rs/devcontainer-linux-arm64-musl", "@devcontainer-rs/devcontainer-linux-x64-gnu", "@devcontainer-rs/devcontainer-linux-x64-musl", ]); diff --git a/dist-workspace.toml b/dist-workspace.toml index fff9d44bb..e471767eb 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -9,6 +9,8 @@ installers = [] targets = [ "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "aarch64-apple-darwin", ] diff --git a/docs/standalone/distribution.md b/docs/standalone/distribution.md index 338f28ae7..8415b3709 100644 --- a/docs/standalone/distribution.md +++ b/docs/standalone/distribution.md @@ -4,7 +4,7 @@ - GitHub Releases is the active distribution channel. - `dist-workspace.toml` is the release artifact baseline for cargo-dist. -- `.github/workflows/devcontainer-release.yml` builds release archives for Linux x64 (glibc), Linux x64 (musl), macOS x64, and macOS arm64 with cargo-dist. +- `.github/workflows/devcontainer-release.yml` builds release archives for Linux x64 (glibc), Linux x64 (musl), Linux arm64 (glibc), Linux arm64 (musl), macOS x64, and macOS arm64 with cargo-dist. - Each release artifact currently includes a compressed archive and a SHA-256 checksum file. - npm publishes two wrapper entrypoints: - `devcontainer-rs` for `npx devcontainer-rs` @@ -40,6 +40,8 @@ The initial PyPI wheel set matches the standalone release targets: - Linux x64 glibc (`manylinux2014_x86_64`) - Linux x64 musl (`musllinux_1_2_x86_64`) +- Linux arm64 glibc (`manylinux2014_aarch64`) +- Linux arm64 musl (`musllinux_1_2_aarch64`) - macOS x64 - macOS arm64 @@ -58,8 +60,8 @@ Homebrew maps the backing repository `jooh/homebrew-tap` to the tap shorthand `j ## Local build flow -- `scripts/standalone/build.sh ` builds the Rust release binary and places it under `dist/standalone/`. -- `scripts/standalone/build-linux-x64-musl.sh` builds the Linux x64 musl artifact for older-glibc distro compatibility. +- `scripts/standalone/build.sh ` builds the Rust release binary and places it under `dist/standalone/`. Supported Linux targets are `linux-x64`, `linux-x64-musl`, `linux-arm64`, and `linux-arm64-musl`. +- `scripts/standalone/build-linux-x64-musl.sh` and `scripts/standalone/build-linux-arm64-musl.sh` build musl artifacts for older-glibc distro compatibility. - `scripts/standalone/smoke.sh ` runs the repo-owned smoke commands against a built artifact. - `~/.cargo/bin/dist build --artifacts=local --target --allow-dirty` builds the cargo-dist archive into `target/distrib/`. - `node build/prepare-npm-packages.js --artifacts-dir target/distrib --output-dir dist/npm` stages the publishable npm wrapper/native packages from dist outputs. diff --git a/npm/launcher.js b/npm/launcher.js index fd8b11b07..12c27500d 100644 --- a/npm/launcher.js +++ b/npm/launcher.js @@ -95,12 +95,13 @@ function resolveBinaryPackage(system = {}) { if (platform === "darwin" && arch === "arm64") { return runtimeConfig.supportedTargets["darwin-arm64"]; } - if (platform === "linux" && arch === "x64") { + if (platform === "linux" && (arch === "x64" || arch === "arm64")) { + const targetArch = arch === "x64" ? "x64" : "arm64"; const libc = detectLibc(system); if (libc === "musl") { - return runtimeConfig.supportedTargets["linux-x64-musl"]; + return runtimeConfig.supportedTargets[`linux-${targetArch}-musl`]; } - return runtimeConfig.supportedTargets["linux-x64-gnu"]; + return runtimeConfig.supportedTargets[`linux-${targetArch}-gnu`]; } throw new Error( diff --git a/npm/runtime-config.js b/npm/runtime-config.js index cd8250e6f..b6e047798 100644 --- a/npm/runtime-config.js +++ b/npm/runtime-config.js @@ -37,6 +37,26 @@ const supportedTargets = { cpu: "x64", libc: "musl", }, + "linux-arm64-gnu": { + target: "linux-arm64-gnu", + triple: "aarch64-unknown-linux-gnu", + archiveSuffix: "linux-arm64-gnu", + packageName: "@devcontainer-rs/devcontainer-linux-arm64-gnu", + packageSlug: "devcontainer-rs-devcontainer-linux-arm64-gnu", + os: "linux", + cpu: "arm64", + libc: "glibc", + }, + "linux-arm64-musl": { + target: "linux-arm64-musl", + triple: "aarch64-unknown-linux-musl", + archiveSuffix: "linux-arm64-musl", + packageName: "@devcontainer-rs/devcontainer-linux-arm64-musl", + packageSlug: "devcontainer-rs-devcontainer-linux-arm64-musl", + os: "linux", + cpu: "arm64", + libc: "musl", + }, }; const wrapperPackages = { diff --git a/scripts/standalone/build-linux-arm64-musl.sh b/scripts/standalone/build-linux-arm64-musl.sh new file mode 100755 index 000000000..a61e066b9 --- /dev/null +++ b/scripts/standalone/build-linux-arm64-musl.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/build.sh" linux-arm64-musl diff --git a/scripts/standalone/build.sh b/scripts/standalone/build.sh index e83aad164..9e3c9b0a8 100755 --- a/scripts/standalone/build.sh +++ b/scripts/standalone/build.sh @@ -3,7 +3,7 @@ set -euo pipefail if [[ $# -ne 1 ]]; then echo "usage: $0 " >&2 - echo "example targets: linux-x64, linux-x64-musl, darwin-x64, darwin-arm64" >&2 + echo "example targets: linux-x64, linux-x64-musl, linux-arm64, linux-arm64-musl, darwin-x64, darwin-arm64" >&2 exit 2 fi @@ -18,6 +18,12 @@ case "$target" in linux-x64-musl) rust_target="x86_64-unknown-linux-musl" ;; + linux-arm64) + rust_target="aarch64-unknown-linux-gnu" + ;; + linux-arm64-musl) + rust_target="aarch64-unknown-linux-musl" + ;; darwin-x64|darwin-arm64) ;; *) diff --git a/tap b/tap index 6ac426b93..dadcdc5c9 160000 --- a/tap +++ b/tap @@ -1 +1 @@ -Subproject commit 6ac426b93a32ba1d8952622b962c28927e17cc8b +Subproject commit dadcdc5c932b136f685a939b4f2a351fd9a9edc2