From 1ff69afac99aec8f03026bbb89528b9067595dd5 Mon Sep 17 00:00:00 2001 From: Xin Date: Sat, 3 Oct 2026 13:43:55 +0100 Subject: [PATCH 1/4] ci: extend archive verification to musl prebuilts (musllinux_1_2) --- .github/workflows/verify-v8-archive.yml | 88 +++++++++++++++++++------ 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/.github/workflows/verify-v8-archive.yml b/.github/workflows/verify-v8-archive.yml index 3e627d0..0e9eb22 100644 --- a/.github/workflows/verify-v8-archive.yml +++ b/.github/workflows/verify-v8-archive.yml @@ -1,10 +1,14 @@ name: Verify V8 archive -# Proves the published librusty_v8 archive is actually linkable under -# manylinux_2_28 (glibc 2.28): a whole-archive link into a shared object -# with -Wl,-z,defs forces every object in the archive to be linked and -# every undefined symbol to resolve against glibc 2.28 - the same demand -# the wheel link makes. +# Proves a librusty_v8 static library is actually linkable under the wheel +# target environment: a whole-archive link into a shared object with +# -Wl,-z,defs forces every object in the archive to be linked and every +# undefined symbol to resolve - the same demand the wheel link makes. +# +# gnu legs verify OUR archives (built by build-v8-archive.yml) against +# glibc 2.28; musl legs verify denoland's upstream musl prebuilts (shipped +# since rusty_v8 v150.2.0) against musllinux_1_2, since those are consumed +# directly by the musl wheel builds. on: workflow_dispatch: @@ -13,38 +17,80 @@ permissions: contents: read jobs: - verify-x86_64: + v8-version: runs-on: ubuntu-latest - container: quay.io/pypa/manylinux_2_28_x86_64 + outputs: + version: ${{ steps.v8.outputs.version }} + steps: + - uses: actions/checkout@v5 + - id: v8 + run: | + VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' Cargo.lock) + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "v8 version: $VERSION" + + # Container job uses only `run:` steps: node-based actions (checkout etc.) + # cannot execute inside musl containers (the runner's node is glibc-linked). + verify: + needs: v8-version + runs-on: ${{ matrix.target.runner }} + container: ${{ matrix.target.container }} timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + target: + - name: gnu-x86_64 + runner: ubuntu-latest + container: quay.io/pypa/manylinux_2_28_x86_64 + triple: x86_64-unknown-linux-gnu + source: self + extra_libs: "-lrt -lgcc_s" + - name: musl-x86_64 + runner: ubuntu-latest + container: quay.io/pypa/musllinux_1_2_x86_64 + triple: x86_64-unknown-linux-musl + source: upstream + extra_libs: "" + - name: musl-aarch64 + runner: ubuntu-24.04-arm + container: quay.io/pypa/musllinux_1_2_aarch64 + triple: aarch64-unknown-linux-musl + source: upstream + extra_libs: "" steps: - - name: Show toolchain and glibc version + - name: Show toolchain and libc run: | - ldd --version | head -1 + (ldd --version 2>&1 || true) | head -1 gcc --version | head -1 - - name: Download archive from release + - name: Download archive run: | + VERSION="${{ needs.v8-version.outputs.version }}" + if [ "${{ matrix.target.source }}" = "self" ]; then + BASE="https://github.com/${{ github.repository }}/releases/download/librusty_v8-v${VERSION}" + else + BASE="https://github.com/denoland/rusty_v8/releases/download/v${VERSION}" + fi curl -sSfL -o librusty_v8.a.gz \ - https://github.com/${{ github.repository }}/releases/download/librusty_v8-v150.4.0/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz + "${BASE}/librusty_v8_release_${{ matrix.target.triple }}.a.gz" gunzip librusty_v8.a.gz ls -lh librusty_v8.a - - name: Whole-archive link test (-z,defs on glibc 2.28) + - name: Whole-archive link test (-z,defs) run: | # Some symbols are intentionally undefined in the C++ archive: they # are rusty_v8's and V8-Temporal's C++->Rust FFI hooks, provided by # the Rust side (v8 and temporal_capi crates) at the final cargo - # link. Denoland's own prebuilt has the identical undefined set. - # Strategy: attempt the strict link, collect missing symbols, assert - # ALL of them are first-party FFI names (anything else - e.g. a - # glibc symbol - fails the job), then stub and re-link strictly. + # link. Strategy: attempt the strict link, collect missing symbols, + # assert ALL of them are first-party FFI names (anything else - + # e.g. a libc symbol - fails the job), then stub and re-link. link() { gcc -shared -o /tmp/librusty_v8_test.so \ -Wl,--whole-archive librusty_v8.a -Wl,--no-whole-archive \ "$@" \ -Wl,-z,defs \ - -lpthread -ldl -lm -lrt -lgcc_s + -lpthread -ldl -lm ${{ matrix.target.extra_libs }} } if link 2>link_err.txt; then echo "Linked with no stubs needed" @@ -54,7 +100,7 @@ jobs: echo "Missing symbols: $(wc -l < missing_syms.txt)" NON_FFI=$(grep -vE '^(temporal_rs_|rusty_v8_|v8__|v8_inspector__|cppgc__|crdtp__)' missing_syms.txt || true) if [ -n "${NON_FFI}" ]; then - echo "ERROR: unresolved symbols that are NOT first-party Rust FFI (likely glibc incompatibility):" + echo "ERROR: unresolved symbols that are NOT first-party Rust FFI (likely libc incompatibility):" echo "${NON_FFI}" exit 1 fi @@ -63,11 +109,11 @@ jobs: link stubs.o fi ls -lh /tmp/librusty_v8_test.so - echo "Whole-archive link succeeded under glibc 2.28" + echo "Whole-archive link succeeded on ${{ matrix.target.name }}" - name: Inspect resulting shared object run: | - echo "=== glibc version requirements of the linked .so ===" - objdump -T /tmp/librusty_v8_test.so | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 + echo "=== versioned symbol requirements (empty on musl) ===" + objdump -T /tmp/librusty_v8_test.so | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 || true echo "=== NEEDED libraries ===" objdump -p /tmp/librusty_v8_test.so | grep NEEDED From 1f50f42cd52c812db6fae1837b506749744e906c Mon Sep 17 00:00:00 2001 From: Xin Date: Sat, 3 Oct 2026 13:46:40 +0100 Subject: [PATCH 2/4] feat: build and test musllinux wheels for Alpine/musl platforms denoland ships musl prebuilts of rusty_v8 since v150.2.0 (our deno_core 0.412 bump landed on v150.4.0, just past the threshold), and musl has no glibc-style symbol versioning, so the upstream archives are directly consumable - verified via whole-archive -z,defs link tests inside musllinux_1_2 containers on both architectures. - linux-musl job: musllinux_1_2 wheels for x86_64 + aarch64 on free runners; RUSTFLAGS=-C target-feature=-crt-static since extension modules need dynamic musl. - test-wheels-musl: full 3.10-3.14 matrix inside python:alpine containers via docker run (node-based actions can't execute on musl). - docs: musl/Alpine now listed as supported. --- .github/workflows/CI.yml | 67 +++++++++++++++++++++++++++++++++++++++- docs/quickstart.md | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ef30d6b..1e01277 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -112,6 +112,41 @@ jobs: name: wheels-linux-${{ matrix.platform.arch }} path: dist + # Build musllinux wheels (Alpine and other musl distros). Unlike the gnu + # builds, denoland ships musl prebuilts (since rusty_v8 v150.2.0) with no + # glibc-versioning concerns, so rusty_v8's build script downloads them + # automatically - no self-hosted archive involved. + linux-musl: + runs-on: ${{ matrix.platform.runner }} + strategy: + fail-fast: false + matrix: + platform: + - runner: ubuntu-latest + target: x86_64-unknown-linux-musl + arch: x86_64 + - runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + arch: aarch64 + steps: + - uses: actions/checkout@v5 + - name: Build wheels + uses: PyO3/maturin-action@v1 + env: + # Python extension modules must link musl libc dynamically; Rust's + # musl targets default to a static crt. + RUSTFLAGS: -C target-feature=-crt-static + with: + target: ${{ matrix.platform.target }} + manylinux: musllinux_1_2 + args: --release --out dist + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.platform.arch }} + path: dist + # Test built manylinux wheels against all supported Python versions. # A single cp310-abi3 wheel per architecture covers 3.10+. test-wheels-x86_64: @@ -166,9 +201,39 @@ jobs: pip install pytest pytest-asyncio pytest tests/ -v + # Test musllinux wheels inside Alpine containers (run via docker rather + # than `container:` because node-based actions cannot execute on musl). + test-wheels-musl: + needs: linux-musl + runs-on: ${{ matrix.platform.runner }} + strategy: + fail-fast: false + matrix: + platform: + - runner: ubuntu-latest + arch: x86_64 + - runner: ubuntu-24.04-arm + arch: aarch64 + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] + steps: + - uses: actions/checkout@v5 + - name: Download wheels + uses: actions/download-artifact@v6 + with: + name: wheels-musllinux-${{ matrix.platform.arch }} + path: dist + - name: Test wheel in Alpine container + run: | + WHEEL=$(ls dist/*cp310-abi3*musllinux*${{ matrix.platform.arch }}.whl | head -n 1) + echo "Testing wheel: $WHEEL" + docker run --rm \ + -v "$(pwd):/work" -w /work \ + python:${{ matrix.python-version }}-alpine \ + sh -c "pip install --upgrade pip && pip install $WHEEL pytest pytest-asyncio && pytest tests/ -q" + # Create GitHub Release with all artifacts release: - needs: [macos, sdist, linux, test-wheels-x86_64, test-wheels-aarch64] + needs: [macos, sdist, linux, linux-musl, test-wheels-x86_64, test-wheels-aarch64, test-wheels-musl] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: diff --git a/docs/quickstart.md b/docs/quickstart.md index 2e4309d..584bc8c 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -13,7 +13,7 @@ pip install jsrun # or `uv install jsrun` !!! warning "Platform Support" - Supports macOS (Apple Silicon) and Linux (x86_64, ARM64) with glibc ([manylinux](https://github.com/pypa/manylinux)). Windows and musl-based distributions (e.g., Alpine) are not supported currently. + Supports macOS (Apple Silicon) and Linux (x86_64, ARM64) with glibc ([manylinux](https://github.com/pypa/manylinux)) or musl ([musllinux](https://github.com/pypa/manylinux), e.g. Alpine). Windows is not supported currently. ## Run JavaScript from Python From ad6d13217bf323be6b2379a617eed053249b92de Mon Sep 17 00:00:00 2001 From: Xin Date: Sat, 3 Oct 2026 14:13:42 +0100 Subject: [PATCH 3/4] chore: dedup v8 version parsing, tighten comments, drop redundant toolchain action - Extract the Cargo.lock v8-version parse (4 copies) into .github/scripts/v8-version.sh. - Derive target triple and Dockerfile from the arch name in build-v8-archive.yml, replacing the inline fromJSON matrix objects. - Keep the glibc-drift story in one place (Dockerfile.x86_64); trim the retellings elsewhere to one-liners. - Drop dtolnay/rust-toolchain from the macos job: rust-toolchain.toml + preinstalled rustup already pin the version. --- .github/docker/Dockerfile.x86_64 | 19 +++++------ .github/scripts/build-v8-archive.sh | 42 +++++++------------------ .github/scripts/v8-version.sh | 9 ++++++ .github/workflows/CI.yml | 25 +++++---------- .github/workflows/build-v8-archive.yml | 39 +++++++++-------------- .github/workflows/verify-v8-archive.yml | 28 ++++++----------- rust-toolchain.toml | 6 ++-- 7 files changed, 60 insertions(+), 108 deletions(-) create mode 100755 .github/scripts/v8-version.sh diff --git a/.github/docker/Dockerfile.x86_64 b/.github/docker/Dockerfile.x86_64 index 2294fef..8ccb0dc 100644 --- a/.github/docker/Dockerfile.x86_64 +++ b/.github/docker/Dockerfile.x86_64 @@ -1,11 +1,9 @@ # x86_64 manylinux builder for jsrun Python wheels -# Donor stage for a glibc 2.28 sysroot (AlmaLinux 8 based). The rust-cross -# base image used to ship its own crosstool-ng toolchain + 2.28 sysroot at -# /usr/x86_64-unknown-linux-gnu, but a mid-2026 rebuild of the mutable tag -# left that directory empty, so V8 would otherwise compile against the -# Ubuntu host glibc (2.35) and reference symbols newer than manylinux_2_28 -# allows (e.g. pthread_cond_clockwait, glibc 2.30). +# Donor stage for a glibc 2.28 sysroot (AlmaLinux 8). The rust-cross base +# image's own toolchain/sysroot went missing in a mid-2026 rebuild of its +# mutable tag; without a 2.28 sysroot, V8 compiles against the Ubuntu host +# glibc and references symbols too new for manylinux_2_28 wheels. FROM quay.io/pypa/manylinux_2_28_x86_64 AS sysroot FROM ghcr.io/rust-cross/manylinux_2_28-cross:x86_64 @@ -38,14 +36,11 @@ ENV CXX=clang-19 ENV LIBCLANG_PATH=/usr/lib/llvm-19/lib ENV PATH=/usr/lib/llvm-19/bin:${PATH} -# glibc 2.28 sysroot donated from the official manylinux image, so V8 is -# compiled against 2.28 headers. V8 gates newer glibc APIs behind -# compile-time glibc version checks, so old headers keep the archive -# manylinux_2_28 compatible. +# V8 gates newer glibc APIs behind compile-time version checks, so building +# against 2.28 headers keeps the archive manylinux_2_28 compatible. The gcc +# dir provides crt objects/libgcc needed when linking host tool executables. COPY --from=sysroot /usr/include /opt/manylinux_2_28_sysroot/usr/include COPY --from=sysroot /usr/lib64 /opt/manylinux_2_28_sysroot/usr/lib64 -# GCC installation dir: provides crtbeginS.o/crtendS.o, libgcc.a and the -# libgcc_s.so devel symlink needed when linking host tool executables. COPY --from=sysroot /usr/lib/gcc /opt/manylinux_2_28_sysroot/usr/lib/gcc RUN ln -s usr/lib64 /opt/manylinux_2_28_sysroot/lib64 ENV TARGET_SYSROOT=/opt/manylinux_2_28_sysroot diff --git a/.github/scripts/build-v8-archive.sh b/.github/scripts/build-v8-archive.sh index 853e599..c6e3a8e 100755 --- a/.github/scripts/build-v8-archive.sh +++ b/.github/scripts/build-v8-archive.sh @@ -1,36 +1,23 @@ #!/bin/bash set -euo pipefail -# Build a prebuilt rusty_v8 static library archive for reuse by wheel builds. -# -# Runs inside the manylinux builder containers (.github/docker/Dockerfile.*) -# with the repository mounted at the working directory. Produces the same -# artifact layout as denoland's rusty_v8 releases: -# librusty_v8_release_{target}.a.gz -# src_binding_release_{target}.rs +# Build a prebuilt rusty_v8 static library archive for reuse by wheel builds +# (same artifact layout as denoland's rusty_v8 releases). Runs inside the +# manylinux builder containers with the repository mounted at the workdir. # # Usage: ./build-v8-archive.sh TARGET_TRIPLE [OUTPUT_DIR] -# TARGET_TRIPLE: x86_64-unknown-linux-gnu or aarch64-unknown-linux-gnu -# OUTPUT_DIR: dist-v8 (default) TARGET_ARCH="${1:?Usage: build-v8-archive.sh TARGET_TRIPLE [OUTPUT_DIR]}" OUTPUT_DIR="${2:-dist-v8}" -V8_VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' Cargo.lock) -if [ -z "${V8_VERSION}" ]; then - echo "Failed to determine v8 crate version from Cargo.lock" >&2 - exit 1 -fi +V8_VERSION=$("$(dirname "$0")/v8-version.sh") echo "=== Building rusty_v8 v${V8_VERSION} from source for ${TARGET_ARCH} ===" rustc --version cargo --version -# The rust-cross base images configure CARGO_TARGET_*_LINKER env vars, but -# the mutable image tags drift and the referenced cross-gcc may no longer -# exist (observed with x86_64-unknown-linux-gnu-gcc). Only host build -# scripts are linked here (-p v8 produces an rlib), so fall back to the -# clang installed by our Dockerfiles when a configured linker is missing. +# The mutable base-image tags drift; fall back to our clang when a configured +# linker is missing (only host build scripts are linked; -p v8 yields an rlib). for var in $(env | sed -n 's/^\(CARGO_TARGET_[A-Z0-9_]*_LINKER\)=.*/\1/p'); do linker="${!var}" if ! command -v "${linker}" >/dev/null 2>&1; then @@ -39,13 +26,10 @@ for var in $(env | sed -n 's/^\(CARGO_TARGET_[A-Z0-9_]*_LINKER\)=.*/\1/p'); do fi done -# The crates.io package is missing files required for from-source builds, -# so patch v8 to the matching git tag. +# crates.io package lacks files needed for from-source builds; use the git tag. if ! grep -q "\[patch.crates-io\]" Cargo.toml; then cat >> Cargo.toml </dev/null | sort | uniq -c || echo "(none found)" -# Guard against glibc drift: the archive must stay linkable under -# manylinux_2_28 (glibc 2.28). Fail loudly if the static lib references -# symbols introduced in later glibc versions (the denylist covers known -# offenders from glibc 2.29-2.38; extend it if auditwheel ever complains). +# The archive must stay linkable under manylinux_2_28 (glibc 2.28): fail on +# strong references to newer glibc symbols instead of publishing a broken +# artifact. Weak references are harmless (linker leaves them null). GLIBC_POST_228_SYMBOLS='^(pthread_cond_clockwait|pthread_mutex_clocklock|pthread_rwlock_clockrdlock|pthread_rwlock_clockwrlock|sem_clockwait|pthread_clockjoin_np|gettid|getdents64|__libc_single_threaded|arc4random|arc4random_buf|arc4random_uniform|close_range|__isoc23_.*)$' UNDEFINED_SYMBOLS="" for nm_bin in llvm-nm "${TARGET_ARCH}-nm" nm; do @@ -96,8 +78,6 @@ if [ -z "${UNDEFINED_SYMBOLS}" ]; then echo "ERROR: no nm tool in the image could read ${STATIC_LIB}; refusing to publish unaudited archive" >&2 exit 1 fi -# Only strong undefined references ("U") are fatal: weak ones ("w"/"v") are -# left null by the linker on older glibc and handled by runtime fallbacks. WEAK_MATCHES=$(echo "${UNDEFINED_SYMBOLS}" | awk '$1 == "w" || $1 == "v" {print $2}' | sort -u | grep -E "${GLIBC_POST_228_SYMBOLS}" || true) if [ -n "${WEAK_MATCHES}" ]; then echo "Note: weak references to post-2.28 symbols (harmless): ${WEAK_MATCHES}" diff --git a/.github/scripts/v8-version.sh b/.github/scripts/v8-version.sh new file mode 100755 index 0000000..41a3753 --- /dev/null +++ b/.github/scripts/v8-version.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Print the v8 crate version pinned in Cargo.lock. +set -euo pipefail +VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' "$(dirname "$0")/../../Cargo.lock") +if [ -z "${VERSION}" ]; then + echo "Failed to determine v8 crate version from Cargo.lock" >&2 + exit 1 +fi +echo "${VERSION}" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1e01277..17cf193 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v5 - uses: actions/setup-python@v6 - uses: astral-sh/setup-uv@v7 - - uses: dtolnay/rust-toolchain@stable + # Rust version comes from rust-toolchain.toml via the preinstalled rustup. - name: Test run: make all - name: Build wheels @@ -53,10 +53,8 @@ jobs: name: wheels-sdist path: dist - # Build manylinux wheels on free GitHub runners, consuming the prebuilt V8 - # static library published by the "Build V8 archive" workflow. V8 is only - # compiled from source when the v8 crate version in Cargo.lock changes; - # wheel builds just link against the archive. + # Build manylinux wheels against the prebuilt V8 archive published by the + # "Build V8 archive" workflow (V8 only compiles when Cargo.lock bumps it). linux: runs-on: ${{ matrix.platform.runner }} strategy: @@ -77,11 +75,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' Cargo.lock) - if [ -z "$VERSION" ]; then - echo "::error::Failed to determine v8 crate version from Cargo.lock" - exit 1 - fi + VERSION=$(.github/scripts/v8-version.sh) TAG="librusty_v8-v${VERSION}" echo "Using prebuilt V8 archive release: $TAG" mkdir -p .v8 @@ -101,8 +95,6 @@ jobs: target: ${{ matrix.platform.target }} manylinux: 2_28 args: --release --out dist - # maturin-action forwards RUST*-prefixed env vars into the build - # container; the explicit -e flags are belt and braces. docker-options: -e RUSTY_V8_ARCHIVE -e RUSTY_V8_SRC_BINDING_PATH sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} @@ -112,10 +104,8 @@ jobs: name: wheels-linux-${{ matrix.platform.arch }} path: dist - # Build musllinux wheels (Alpine and other musl distros). Unlike the gnu - # builds, denoland ships musl prebuilts (since rusty_v8 v150.2.0) with no - # glibc-versioning concerns, so rusty_v8's build script downloads them - # automatically - no self-hosted archive involved. + # Build musllinux wheels. denoland ships musl prebuilts (no glibc-style + # symbol versioning), which rusty_v8's build script downloads automatically. linux-musl: runs-on: ${{ matrix.platform.runner }} strategy: @@ -133,8 +123,7 @@ jobs: - name: Build wheels uses: PyO3/maturin-action@v1 env: - # Python extension modules must link musl libc dynamically; Rust's - # musl targets default to a static crt. + # Extension modules need dynamic musl; Rust's musl targets default to static crt. RUSTFLAGS: -C target-feature=-crt-static with: target: ${{ matrix.platform.target }} diff --git a/.github/workflows/build-v8-archive.yml b/.github/workflows/build-v8-archive.yml index 787cba6..0c575b1 100644 --- a/.github/workflows/build-v8-archive.yml +++ b/.github/workflows/build-v8-archive.yml @@ -1,14 +1,10 @@ name: Build V8 archive -# Builds rusty_v8 from source inside the manylinux_2_28 containers and -# publishes the static library + src binding as GitHub release assets. -# The wheel CI consumes these via RUSTY_V8_ARCHIVE/RUSTY_V8_SRC_BINDING_PATH, -# so V8 only needs to be compiled when the v8 crate version in Cargo.lock -# changes (run this workflow manually after such a bump). -# -# Why from source: denoland's prebuilt archives require glibc >= 2.30 -# (e.g. pthread_cond_clockwait), but manylinux_2_28 wheels must link -# against glibc 2.28. +# Compiles rusty_v8 from source inside the manylinux_2_28 containers and +# publishes the static library + src binding as release assets, which wheel +# CI consumes via RUSTY_V8_ARCHIVE. Run manually after the v8 version in +# Cargo.lock changes. (From source because denoland's gnu prebuilts require +# glibc > 2.28; see .github/docker/Dockerfile.x86_64.) on: workflow_dispatch: @@ -32,10 +28,9 @@ jobs: strategy: fail-fast: false matrix: - arch: ${{ fromJSON( - inputs.arch == 'x86_64' && '[{"name":"x86_64","target":"x86_64-unknown-linux-gnu","dockerfile":"Dockerfile.x86_64"}]' - || inputs.arch == 'aarch64' && '[{"name":"aarch64","target":"aarch64-unknown-linux-gnu","dockerfile":"Dockerfile.aarch64"}]' - || '[{"name":"x86_64","target":"x86_64-unknown-linux-gnu","dockerfile":"Dockerfile.x86_64"},{"name":"aarch64","target":"aarch64-unknown-linux-gnu","dockerfile":"Dockerfile.aarch64"}]') }} + arch: ${{ fromJSON(inputs.arch == 'all' && '["x86_64","aarch64"]' || format('["{0}"]', inputs.arch)) }} + env: + TARGET: ${{ matrix.arch }}-unknown-linux-gnu steps: - uses: actions/checkout@v5 @@ -49,19 +44,15 @@ jobs: - name: Read v8 version from Cargo.lock id: v8 run: | - VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' Cargo.lock) - if [ -z "$VERSION" ]; then - echo "::error::Failed to determine v8 crate version from Cargo.lock" - exit 1 - fi + VERSION=$(.github/scripts/v8-version.sh) echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building archive for rusty_v8 v$VERSION" - name: Build builder image run: | docker build \ - -t jsrun-v8-builder:${{ matrix.arch.name }} \ - -f .github/docker/${{ matrix.arch.dockerfile }} \ + -t jsrun-v8-builder:${{ matrix.arch }} \ + -f .github/docker/Dockerfile.${{ matrix.arch }} \ .github/docker/ - name: Build V8 from source @@ -70,16 +61,16 @@ jobs: docker run --rm \ -v "$(pwd):/workdir" \ -w /workdir \ - jsrun-v8-builder:${{ matrix.arch.name }} \ - bash .github/scripts/build-v8-archive.sh ${{ matrix.arch.target }} dist-v8 + jsrun-v8-builder:${{ matrix.arch }} \ + bash .github/scripts/build-v8-archive.sh "$TARGET" dist-v8 - name: Upload artifacts to release env: GH_TOKEN: ${{ github.token }} run: | + # Prerelease, and deliberately not matching the v* pattern that + # triggers release CI. TAG="librusty_v8-v${{ steps.v8.outputs.version }}" - # Prerelease so archive tags never show up as the "latest" jsrun release. - # The tag deliberately does not match the v* pattern that triggers CI. gh release view "$TAG" >/dev/null 2>&1 || \ gh release create "$TAG" \ --prerelease \ diff --git a/.github/workflows/verify-v8-archive.yml b/.github/workflows/verify-v8-archive.yml index 0e9eb22..61bc569 100644 --- a/.github/workflows/verify-v8-archive.yml +++ b/.github/workflows/verify-v8-archive.yml @@ -1,14 +1,9 @@ name: Verify V8 archive -# Proves a librusty_v8 static library is actually linkable under the wheel -# target environment: a whole-archive link into a shared object with -# -Wl,-z,defs forces every object in the archive to be linked and every -# undefined symbol to resolve - the same demand the wheel link makes. -# -# gnu legs verify OUR archives (built by build-v8-archive.yml) against -# glibc 2.28; musl legs verify denoland's upstream musl prebuilts (shipped -# since rusty_v8 v150.2.0) against musllinux_1_2, since those are consumed -# directly by the musl wheel builds. +# Proves a librusty_v8 archive is linkable in the wheel target environment: +# a whole-archive -Wl,-z,defs link makes the same demand the wheel link does. +# gnu legs verify OUR archives against glibc 2.28; musl legs verify +# denoland's upstream prebuilts, which the musl wheel builds consume directly. on: workflow_dispatch: @@ -24,10 +19,7 @@ jobs: steps: - uses: actions/checkout@v5 - id: v8 - run: | - VERSION=$(sed -n '/^name = "v8"$/{n;s/^version = "\(.*\)"/\1/p;}' Cargo.lock) - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "v8 version: $VERSION" + run: echo "version=$(.github/scripts/v8-version.sh)" >> "$GITHUB_OUTPUT" # Container job uses only `run:` steps: node-based actions (checkout etc.) # cannot execute inside musl containers (the runner's node is glibc-linked). @@ -67,6 +59,7 @@ jobs: - name: Download archive run: | VERSION="${{ needs.v8-version.outputs.version }}" + echo "v8 version: $VERSION" if [ "${{ matrix.target.source }}" = "self" ]; then BASE="https://github.com/${{ github.repository }}/releases/download/librusty_v8-v${VERSION}" else @@ -79,12 +72,9 @@ jobs: - name: Whole-archive link test (-z,defs) run: | - # Some symbols are intentionally undefined in the C++ archive: they - # are rusty_v8's and V8-Temporal's C++->Rust FFI hooks, provided by - # the Rust side (v8 and temporal_capi crates) at the final cargo - # link. Strategy: attempt the strict link, collect missing symbols, - # assert ALL of them are first-party FFI names (anything else - - # e.g. a libc symbol - fails the job), then stub and re-link. + # C++->Rust FFI hooks are intentionally undefined (cargo provides + # them at the real link). Collect missing symbols, assert all are + # first-party FFI names (anything else fails), then stub and re-link. link() { gcc -shared -o /tmp/librusty_v8_test.so \ -Wl,--whole-archive librusty_v8.a -Wl,--no-whole-archive \ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 44f7243..f54f22b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,5 @@ -# Single source of truth for the Rust toolchain: rustup picks this up -# automatically for local builds, CI, and the V8 archive builder containers, -# so lint results (clippy -D warnings) stay reproducible everywhere. -# Bump deliberately; `make all` must pass under the new version. +# Single source of truth for the Rust toolchain (local, CI, builder +# containers). Bump deliberately; `make all` must pass under the new version. [toolchain] channel = "1.99.0" components = ["clippy", "rustfmt"] From 38de7cd0f0473a1cb30e1abcc3c904d2b566a765 Mon Sep 17 00:00:00 2001 From: Xin Date: Sat, 3 Oct 2026 14:19:14 +0100 Subject: [PATCH 4/4] chore: bump artifact actions (upload v4->v7, download v6->v8), drop PyPy classifier - upload-artifact@v4 targets deprecated Node 20 (CI annotation); bump both artifact actions to current majors and the stray checkout@v4 in sdist to v5. - Remove the PyPy classifier: abi3 wheels only serve CPython; PyPy would be sdist-only and is untested. --- .github/workflows/CI.yml | 20 ++++++++++---------- pyproject.toml | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 17cf193..72898ff 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -33,7 +33,7 @@ jobs: args: --release --out dist sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: wheels-macos-${{ matrix.platform.target }} path: dist @@ -41,14 +41,14 @@ jobs: sdist: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build sdist uses: PyO3/maturin-action@v1 with: command: sdist args: --out dist - name: Upload sdist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: wheels-sdist path: dist @@ -99,7 +99,7 @@ jobs: sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: wheels-linux-${{ matrix.platform.arch }} path: dist @@ -131,7 +131,7 @@ jobs: args: --release --out dist sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: wheels-musllinux-${{ matrix.platform.arch }} path: dist @@ -151,7 +151,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Download wheels - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: wheels-linux-x86_64 path: dist @@ -177,7 +177,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Download wheels - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: wheels-linux-aarch64 path: dist @@ -207,7 +207,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Download wheels - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: wheels-musllinux-${{ matrix.platform.arch }} path: dist @@ -229,7 +229,7 @@ jobs: - uses: actions/checkout@v5 - name: Download all artifacts - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: artifacts @@ -259,7 +259,7 @@ jobs: id-token: write # Required for PyPI trusted publishing steps: - name: Download all artifacts - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: artifacts diff --git a/pyproject.toml b/pyproject.toml index 0d45b2e..11e37f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ classifiers = [ "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Rust", 'Operating System :: MacOS', 'Operating System :: POSIX :: Linux',