From e123a05a88314d6d44276cbc5d152305cd257875 Mon Sep 17 00:00:00 2001 From: Scott Holodak Date: Fri, 18 Sep 2026 10:59:42 -0400 Subject: [PATCH 1/3] build: package patched libheif for Cosmos runtimes --- .github/workflows/ci.yml | 148 ++++++------------ .gitignore | 2 + README.md | 27 ++++ build/posix.sh | 14 +- .../plans/2026-09-18-patched-libheif.md | 20 +++ platforms/linux-x64/Dockerfile | 4 +- platforms/linuxmusl-arm64v8/Dockerfile | 4 +- platforms/linuxmusl-x64/Dockerfile | 4 +- scripts/pack-cosmos.mjs | 38 +++++ scripts/smoke.cjs | 19 +++ scripts/test-package.sh | 27 ++++ 11 files changed, 194 insertions(+), 113 deletions(-) create mode 100644 docs/superpowers/plans/2026-09-18-patched-libheif.md create mode 100644 scripts/pack-cosmos.mjs create mode 100644 scripts/smoke.cjs create mode 100644 scripts/test-package.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24b2a6b03..498d5b59e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,118 +1,60 @@ -name: CI +name: Patched native packages on: push: - branches: - - main - - 'temp-*' - tags: - - 'v*' + branches: [main] pull_request: - branches: - - main - types: - - opened - - reopened - - synchronize - - ready_for_review -permissions: {} + branches: [main] + workflow_dispatch: +permissions: + contents: read +concurrency: + group: native-${{ github.ref }} + cancel-in-progress: true jobs: - check-latest-versions: - permissions: - contents: read - runs-on: 'ubuntu-24.04' - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Check latest versions - run: ./check-latest-versions.sh && git diff --quiet build: - permissions: - contents: read - name: build-${{ matrix.platform }} - runs-on: ${{ matrix.os }} - needs: check-latest-versions + timeout-minutes: 90 strategy: fail-fast: false matrix: include: - - os: 'ubuntu-24.04' - platform: 'linux-x64' - - os: 'ubuntu-24.04' - platform: 'linux-armv6' - - os: 'ubuntu-24.04' - platform: 'linuxmusl-x64' - - os: 'ubuntu-24.04' - platform: 'linux-ppc64le' - - os: 'ubuntu-24.04' - platform: 'linux-riscv64' - - os: 'ubuntu-24.04' - platform: 'linux-s390x' - - os: 'ubuntu-24.04' - platform: 'dev-wasm32' - - os: 'ubuntu-24.04' - platform: 'win32-ia32' - - os: 'ubuntu-24.04' - platform: 'win32-x64' - - os: 'ubuntu-24.04' - platform: 'win32-arm64v8' - - os: 'macos-15-intel' - platform: 'darwin-x64' - - os: 'macos-15' - platform: 'darwin-arm64v8' - - os: 'ubuntu-24.04-arm' - platform: 'linux-arm64v8' - - os: 'ubuntu-24.04-arm' - platform: 'linuxmusl-arm64v8' + - runner: ubuntu-24.04 + platform: linux-x64 + - runner: ubuntu-24.04 + platform: linuxmusl-x64 + - runner: ubuntu-24.04-arm + platform: linuxmusl-arm64v8 + - runner: macos-15 + platform: darwin-arm64v8 + runs-on: ${{ matrix.runner }} steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Install dependencies (macOS) + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: '24' + - name: Install macOS build tools if: runner.os == 'macOS' run: | - pipx install meson + pipx install meson==1.10.1 brew install automake nasm - - name: Build ${{ matrix.platform }} - run: ./build.sh ${{ matrix.platform }} - - name: Upload ${{ matrix.platform }} artifact - uses: actions/upload-artifact@v6 + - name: Build native dependencies + env: + BUILD_PLATFORM: ${{ matrix.platform }} + run: ./build.sh "$BUILD_PLATFORM" + - name: Pack Cosmos package + env: + BUILD_PLATFORM: ${{ matrix.platform }} + run: node scripts/pack-cosmos.mjs "$BUILD_PLATFORM" + - name: Verify native package with upstream Sharp + timeout-minutes: 10 + env: + BUILD_PLATFORM: ${{ matrix.platform }} + run: bash scripts/test-package.sh "$BUILD_PLATFORM" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: build-${{ matrix.platform }} - path: '*.tar.gz' - compression-level: 0 - retention-days: 1 + name: npm-${{ matrix.platform }} + path: dist/*.tgz if-no-files-found: error - populate-and-publish-npm-workspace: - permissions: - contents: write - id-token: write - needs: build - runs-on: ubuntu-24.04 - steps: - - name: Install Node.js - uses: actions/setup-node@v6 - with: - node-version: '24' - - name: Checkout - uses: actions/checkout@v6 - - name: Download build artifacts - uses: actions/download-artifact@v7 - with: - merge-multiple: true - - name: Populate npm workspace - run: ./populate-npm-workspace.sh - - name: Create npm workspace tarball - run: tar -vcaf npm-workspace.tar.xz -C npm . - - name: Generate release notes - run: ./generate-release-notes.sh - - name: Create GitHub release for tag - if: startsWith(github.ref, 'refs/tags/v') - uses: ncipollo/release-action@v1 - with: - artifacts: npm-workspace.tar.xz - artifactContentType: application/x-xz - bodyFile: release-notes.md - prerelease: ${{ contains(github.ref, '-rc') }} - makeLatest: ${{ !contains(github.ref, '-rc') }} - - name: Publish npm packages - if: startsWith(github.ref, 'refs/tags/v') - run: cd npm && npm publish --workspaces --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }} + retention-days: 14 diff --git a/.gitignore b/.gitignore index baaaf97dd..b17978165 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ npm/*/* !npm/dev-wasm32/THIRD-PARTY-NOTICES.md npm/img-sharp-libvips-*.tgz release-notes.md + +dist/ diff --git a/README.md b/README.md index 6676201c9..168aaac89 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,30 @@ +# Cosmos patched Sharp native packages + +ENG-14549: this fork builds libheif 1.23.4 with libvips 8.18.6 for upstream Sharp 0.35.4. +The Cosmos CI produces scoped `@cosmos-entity/sharp-libvips-@1.3.3-cosmos.1` +packages. It never publishes to public npm and has no Nexus credentials. + +After CI succeeds, the trusted `cosmos-actions` vendor publisher verifies the build +repository, run, and commit before publishing the exact tarballs to Nexus npm-hosted. +Consumers read through `https://nexus.m.cos.ms/repository/npm-group/` and override +`@img/sharp-libvips-` with the matching scoped Cosmos package. Test every +native addon/platform pairing; upgrading a lockfile alone does not prove the loaded +library changed. Deployments must verify their final artifacts. + +Production targets are glibc x64 (Lambda) and musl arm64 (cosmos-www). CI also builds +musl x64 and macOS arm64. Other platforms are not yet covered by this remediation. +All packages retain upstream notices and a BUILD-PROVENANCE.json with source/run IDs. +The source for modified libraries remains available through the recorded versions; +this repository contains the build recipes. Upstream's uncompressed codec and HEVC +feature choices are preserved; adding HEVC decoding is a separate change (ENG-11148). + +Remove consumer overrides once upstream publishes and verifies equivalent fixes. +Review libheif releases regularly; 1.23.4 is a security baseline, not a guarantee +against future vulnerabilities. Build system packages still track distro updates; +we do not claim bit-for-bit reproducibility. + +## Upstream documentation + # Packaging scripts libvips and its dependencies are provided as pre-compiled shared libraries diff --git a/build/posix.sh b/build/posix.sh index 5088515e5..69700a66f 100755 --- a/build/posix.sh +++ b/build/posix.sh @@ -111,9 +111,9 @@ CURL="curl --silent --location --retry 3 --retry-max-time 30" if [ "$DARWIN" = true ]; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly + | sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly-2026-09-01 export RUSTFLAGS+=" -Zlocation-detail=none -Zfmt-debug=none" - CFLAGS= cargo install cargo-c --locked + CFLAGS= cargo install cargo-c --version 0.10.25 --locked fi if [ "${PLATFORM%-*}" == "linuxmusl" ] || [ "$DARWIN" = true ]; then @@ -187,7 +187,13 @@ AOM_AS_FLAGS="${FLAGS}" cmake -G"Unix Makefiles" \ make install/strip mkdir ${DEPS}/heif -$CURL https://github.com/strukturag/libheif/releases/download/v${VERSION_HEIF}/libheif-${VERSION_HEIF}.tar.gz | tar xzC ${DEPS}/heif --strip-components=1 +$CURL https://github.com/strukturag/libheif/releases/download/v${VERSION_HEIF}/libheif-${VERSION_HEIF}.tar.gz > ${DEPS}/libheif.tar.gz +if [ "$DARWIN" = true ]; then + echo "d0c02b4b0e978f34a1974b6f3eea7975a537bf7a9195ffeea38e7242ff316fdd ${DEPS}/libheif.tar.gz" | shasum -a 256 -c - +else + echo "d0c02b4b0e978f34a1974b6f3eea7975a537bf7a9195ffeea38e7242ff316fdd ${DEPS}/libheif.tar.gz" | sha256sum -c - +fi +tar xzf ${DEPS}/libheif.tar.gz -C ${DEPS}/heif --strip-components=1 cd ${DEPS}/heif CFLAGS="${CFLAGS} -O3" CXXFLAGS="${CXXFLAGS} -O3" cmake -G"Unix Makefiles" \ -DCMAKE_TOOLCHAIN_FILE=${ROOT}/Toolchain.cmake -DCMAKE_INSTALL_PREFIX=${TARGET} -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_BUILD_TYPE=Release \ @@ -477,7 +483,7 @@ printf "{\n\ }" >versions.json # Add third-party notices -$CURL -O https://raw.githubusercontent.com/lovell/sharp-libvips/main/THIRD-PARTY-NOTICES.md +cp "${PACKAGE}/THIRD-PARTY-NOTICES.md" . # Create the tarball ls -al lib diff --git a/docs/superpowers/plans/2026-09-18-patched-libheif.md b/docs/superpowers/plans/2026-09-18-patched-libheif.md new file mode 100644 index 000000000..07e8dc660 --- /dev/null +++ b/docs/superpowers/plans/2026-09-18-patched-libheif.md @@ -0,0 +1,20 @@ +# Patched libheif native packages + +ENG-14549. Approved design: retain upstream Sharp, rebuild its native dependency, +and publish distinct Cosmos packages through the private cosmos-actions publisher. + +- [x] Fork upstream at f5f1839e29ed98c2a1d0030e23bf2d403ab3b699 (libheif 1.23.4, libvips 8.18.6). +- [ ] Build glibc x64 (Lambda), musl arm64 (web production), musl x64 (CI), Darwin arm64 (development). +- [ ] Package exact versions with provenance and third-party notices. +- [ ] Smoke-test clean Sharp 0.35.4 installs using dependency overrides in target runtimes. +- [ ] Publish successful exact-head artifacts through cosmos-actions, verify Nexus download integrity. +- [ ] Verify clean Nexus consumer installs before unblocking ENG-14550 and ENG-14551. + +Local validation: node --check scripts/pack-cosmos.mjs, node --check scripts/smoke.cjs, +bash -n scripts/test-package.sh, actionlint .github/workflows/ci.yml. +Native compilation and image-decoding regression checks belong to CI. + +Do not publish from public fork workflows or expose Nexus credentials there. +Source dependency versions are in versions.properties; runner and distro package +updates mean builds are not claimed to be bit-for-bit reproducible. Record the +exact source SHA, build run, versions and immutable package integrity for consumers. diff --git a/platforms/linux-x64/Dockerfile b/platforms/linux-x64/Dockerfile index 4e09ca1f5..dd4f4b15a 100644 --- a/platforms/linux-x64/Dockerfile +++ b/platforms/linux-x64/Dockerfile @@ -42,9 +42,9 @@ RUN \ curl https://sh.rustup.rs -sSf | sh -s -- -y \ --no-modify-path \ --profile minimal \ - --default-toolchain nightly \ + --default-toolchain nightly-2026-09-01 \ && \ - cargo install cargo-c --locked && \ + cargo install cargo-c --version 0.10.25 --locked && \ pip3.12 install meson ninja # Compiler settings diff --git a/platforms/linuxmusl-arm64v8/Dockerfile b/platforms/linuxmusl-arm64v8/Dockerfile index b8c38d803..8314b8d58 100644 --- a/platforms/linuxmusl-arm64v8/Dockerfile +++ b/platforms/linuxmusl-arm64v8/Dockerfile @@ -45,9 +45,9 @@ RUN \ --no-modify-path \ --profile minimal \ --default-host aarch64-unknown-linux-musl \ - --default-toolchain nightly \ + --default-toolchain nightly-2026-09-01 \ && \ - cargo install cargo-c --locked && \ + cargo install cargo-c --version 0.10.25 --locked && \ pipx install meson # Compiler settings diff --git a/platforms/linuxmusl-x64/Dockerfile b/platforms/linuxmusl-x64/Dockerfile index 1fe08e995..487b12cc7 100644 --- a/platforms/linuxmusl-x64/Dockerfile +++ b/platforms/linuxmusl-x64/Dockerfile @@ -45,9 +45,9 @@ RUN \ curl https://sh.rustup.rs -sSf | sh -s -- -y \ --no-modify-path \ --profile minimal \ - --default-toolchain nightly \ + --default-toolchain nightly-2026-09-01 \ && \ - cargo install cargo-c --locked && \ + cargo install cargo-c --version 0.10.25 --locked && \ pipx install meson # Compiler settings diff --git a/scripts/pack-cosmos.mjs b/scripts/pack-cosmos.mjs new file mode 100644 index 000000000..1d65cead2 --- /dev/null +++ b/scripts/pack-cosmos.mjs @@ -0,0 +1,38 @@ +import { readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'node:fs'; +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; + +const platforms = new Map([ + ['linux-x64', 'linux-x64'], + ['linuxmusl-x64', 'linuxmusl-x64'], + ['linuxmusl-arm64v8', 'linuxmusl-arm64'], + ['darwin-arm64v8', 'darwin-arm64'], +]); +const platform = process.argv[2]; +const target = platforms.get(platform); +if (!target) throw new Error('Unsupported Cosmos platform'); +const directory = resolve('npm', target); +execFileSync('tar', ['xzf', `sharp-libvips-${platform}.tar.gz`, '-C', directory]); +const versions = JSON.parse(readFileSync(`${directory}/versions.json`)); +if (versions.heif !== '1.23.4' || versions.vips !== '8.18.6') { + throw new Error('Unexpected libheif/libvips version'); +} +const pkg = JSON.parse(readFileSync(`${directory}/package.json`)); +pkg.name = `@cosmos-entity/sharp-libvips-${target}`; +pkg.version = '1.3.3-cosmos.1'; +pkg.repository.url = 'git+https://github.com/Cosmos-Entity/sharp-libvips.git'; +delete pkg.publishConfig; +pkg.files = ['lib', 'versions.json', 'BUILD-PROVENANCE.json', 'THIRD-PARTY-NOTICES.md', 'SOURCE-LICENSE']; +pkg.exports['./binary'] = `./lib/${target.startsWith('darwin') ? 'libvips-cpp.8.18.6.dylib' : 'libvips-cpp.so.8.18.6'}`; +writeFileSync(`${directory}/package.json`, `${JSON.stringify(pkg, null, 2)}\n`); +writeFileSync(`${directory}/lib/index.js`, 'module.exports = __dirname;\n'); +copyFileSync('LICENSE', `${directory}/SOURCE-LICENSE`); +writeFileSync(`${directory}/BUILD-PROVENANCE.json`, `${JSON.stringify({ + repository: 'https://github.com/Cosmos-Entity/sharp-libvips', + commit: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(), + upstreamBase: 'f5f1839e29ed98c2a1d0030e23bf2d403ab3b699', + platform, versions, + buildRun: process.env.GITHUB_RUN_ID ?? null, +}, null, 2)}\n`); +mkdirSync('dist', { recursive: true }); +execFileSync('npm', ['pack', directory, '--ignore-scripts', '--pack-destination', resolve('dist')], { stdio: 'inherit' }); diff --git a/scripts/smoke.cjs b/scripts/smoke.cjs new file mode 100644 index 000000000..e9694c0ec --- /dev/null +++ b/scripts/smoke.cjs @@ -0,0 +1,19 @@ +const assert = require('node:assert/strict'); +const sharp = require('sharp'); + +async function main() { + assert.equal(sharp.versions.heif, '1.23.4'); + assert.equal(sharp.versions.vips, '8.18.6'); + const pixels = Buffer.alloc(32 * 32 * 3, 128); + for (const format of ['png', 'jpeg', 'webp', 'avif']) { + const encoded = await sharp(pixels, { raw: { width: 32, height: 32, channels: 3 } }) + .toFormat(format).toBuffer(); + const metadata = await sharp(encoded).metadata(); + assert.equal(metadata.width, 32); + const resized = await sharp(encoded).resize(16, 16).png().toBuffer(); + assert.equal((await sharp(resized).metadata()).width, 16); + } + await assert.rejects(sharp(Buffer.from('not an image')).metadata()); + console.log(JSON.stringify(sharp.versions)); +} +main().catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/scripts/test-package.sh b/scripts/test-package.sh new file mode 100644 index 000000000..5c31e23ef --- /dev/null +++ b/scripts/test-package.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail +platform="$1" +case "$platform" in + linux-x64) runtime_platform=linux-x64 ;; + linuxmusl-x64) runtime_platform=linuxmusl-x64 ;; + linuxmusl-arm64v8) runtime_platform=linuxmusl-arm64 ;; + darwin-arm64v8) runtime_platform=darwin-arm64 ;; + *) exit 2 ;; +esac +package="$PWD/dist/cosmos-entity-sharp-libvips-${runtime_platform}-1.3.3-cosmos.1.tgz" +test_dir="$(mktemp -d)" +trap 'rm -rf "$test_dir"' EXIT +cp "$package" "$test_dir/native.tgz" +cp scripts/smoke.cjs "$test_dir/smoke.cjs" +printf '{"private":true,"dependencies":{"sharp":"0.35.4"},"overrides":{"@img/sharp-libvips-%s":"file:./native.tgz"}}\n' "$runtime_platform" > "$test_dir/package.json" +case "$platform" in + linux-x64) + docker run --rm --entrypoint /bin/bash -v "$test_dir:/var/task" -w /var/task public.ecr.aws/lambda/nodejs:24 -c 'npm install --ignore-scripts && node smoke.cjs' + ;; + linuxmusl-*) + docker run --rm -v "$test_dir:/app" -w /app node:24-alpine sh -c 'npm install --ignore-scripts && node smoke.cjs' + ;; + darwin-arm64v8) + (cd "$test_dir" && npm install --ignore-scripts && node smoke.cjs) + ;; +esac From 5bc03de2ae8148103f944293edd2c2fee403ec30 Mon Sep 17 00:00:00 2001 From: Scott Holodak Date: Fri, 18 Sep 2026 11:01:07 -0400 Subject: [PATCH 2/3] test: bound malformed AVIF decoding and verify recovery --- scripts/smoke.cjs | 10 ++++++++++ scripts/test-package.sh | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/smoke.cjs b/scripts/smoke.cjs index e9694c0ec..06ce1ab5b 100644 --- a/scripts/smoke.cjs +++ b/scripts/smoke.cjs @@ -1,5 +1,10 @@ const assert = require('node:assert/strict'); const sharp = require('sharp'); +const deadline = setTimeout(() => { + console.error('Image decoder smoke test exceeded 60 seconds'); + process.exit(1); +}, 60_000); +deadline.unref(); async function main() { assert.equal(sharp.versions.heif, '1.23.4'); @@ -12,6 +17,11 @@ async function main() { assert.equal(metadata.width, 32); const resized = await sharp(encoded).resize(16, 16).png().toBuffer(); assert.equal((await sharp(resized).metadata()).width, 16); + if (format === 'avif') { + await assert.rejects(sharp(encoded.subarray(0, Math.floor(encoded.length / 2))).raw().toBuffer()); + // A failed HEIF parse must not prevent decoding a subsequent valid image. + assert.equal((await sharp(encoded).raw().toBuffer()).length, pixels.length); + } } await assert.rejects(sharp(Buffer.from('not an image')).metadata()); console.log(JSON.stringify(sharp.versions)); diff --git a/scripts/test-package.sh b/scripts/test-package.sh index 5c31e23ef..b640fb4ae 100644 --- a/scripts/test-package.sh +++ b/scripts/test-package.sh @@ -16,10 +16,10 @@ cp scripts/smoke.cjs "$test_dir/smoke.cjs" printf '{"private":true,"dependencies":{"sharp":"0.35.4"},"overrides":{"@img/sharp-libvips-%s":"file:./native.tgz"}}\n' "$runtime_platform" > "$test_dir/package.json" case "$platform" in linux-x64) - docker run --rm --entrypoint /bin/bash -v "$test_dir:/var/task" -w /var/task public.ecr.aws/lambda/nodejs:24 -c 'npm install --ignore-scripts && node smoke.cjs' + docker run --rm --memory=1g --cpus=2 --pids-limit=256 --entrypoint /bin/bash -v "$test_dir:/var/task" -w /var/task public.ecr.aws/lambda/nodejs:24 -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' ;; linuxmusl-*) - docker run --rm -v "$test_dir:/app" -w /app node:24-alpine sh -c 'npm install --ignore-scripts && node smoke.cjs' + docker run --rm --memory=1g --cpus=2 --pids-limit=256 -v "$test_dir:/app" -w /app node:24-alpine sh -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' ;; darwin-arm64v8) (cd "$test_dir" && npm install --ignore-scripts && node smoke.cjs) From 3cf3a3ec8e051b8c2cfae115d7cae8ef244e319a Mon Sep 17 00:00:00 2001 From: Scott Holodak Date: Fri, 18 Sep 2026 12:53:31 -0400 Subject: [PATCH 3/3] fix: align build tools and container test ownership --- .github/workflows/ci.yml | 2 +- scripts/test-package.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 498d5b59e..f5b1adb29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Install macOS build tools if: runner.os == 'macOS' run: | - pipx install meson==1.10.1 + pipx install meson==1.12.0 brew install automake nasm - name: Build native dependencies env: diff --git a/scripts/test-package.sh b/scripts/test-package.sh index b640fb4ae..737e7f922 100644 --- a/scripts/test-package.sh +++ b/scripts/test-package.sh @@ -16,10 +16,10 @@ cp scripts/smoke.cjs "$test_dir/smoke.cjs" printf '{"private":true,"dependencies":{"sharp":"0.35.4"},"overrides":{"@img/sharp-libvips-%s":"file:./native.tgz"}}\n' "$runtime_platform" > "$test_dir/package.json" case "$platform" in linux-x64) - docker run --rm --memory=1g --cpus=2 --pids-limit=256 --entrypoint /bin/bash -v "$test_dir:/var/task" -w /var/task public.ecr.aws/lambda/nodejs:24 -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' + docker run --rm --user "$(id -u):$(id -g)" -e npm_config_cache=/tmp/npm-cache --memory=1g --cpus=2 --pids-limit=256 --entrypoint /bin/bash -v "$test_dir:/var/task" -w /var/task public.ecr.aws/lambda/nodejs:24 -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' ;; linuxmusl-*) - docker run --rm --memory=1g --cpus=2 --pids-limit=256 -v "$test_dir:/app" -w /app node:24-alpine sh -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' + docker run --rm --user "$(id -u):$(id -g)" -e npm_config_cache=/tmp/npm-cache --memory=1g --cpus=2 --pids-limit=256 -v "$test_dir:/app" -w /app node:24-alpine sh -c 'npm install --ignore-scripts && node --max-old-space-size=256 smoke.cjs' ;; darwin-arm64v8) (cd "$test_dir" && npm install --ignore-scripts && node smoke.cjs)