From 5bc3246f6b748b65f1e7731c114f0352304207e7 Mon Sep 17 00:00:00 2001 From: Arul Sharma <31745423+arul28@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:58:14 -0400 Subject: [PATCH] fix(sdk): parse npm 12 pack JSON when publishing the runtime The publish runner installs npm@latest. npm 12 keys pack --json by package name, so files was undefined and every on-disk path looked missing. Build the packages outside the checkout so the repo gitignore cannot strip native/node_modules. Co-authored-by: Cursor --- .../workflows/publish-runtime-packages.yml | 18 ++++++---- .../scripts/build-runtime-npm-packages.mjs | 34 ++++++++++++++++--- .../build-runtime-npm-packages.test.mjs | 18 ++++++++++ 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish-runtime-packages.yml b/.github/workflows/publish-runtime-packages.yml index 319fd8fea..42ea88a39 100644 --- a/.github/workflows/publish-runtime-packages.yml +++ b/.github/workflows/publish-runtime-packages.yml @@ -168,11 +168,15 @@ jobs: if: steps.token.outputs.present == 'true' run: | set -euo pipefail + # Outside the checkout so the repo `.gitignore` (`node_modules`) cannot + # strip native/node_modules from `npm pack`. + OUT_DIR="${RUNNER_TEMP}/runtime-packages" + echo "RUNTIME_PACKAGES_DIR=$OUT_DIR" >> "$GITHUB_ENV" node apps/ade-cli/scripts/build-runtime-npm-packages.mjs \ --artifacts-dir release-artifacts \ --version "${{ steps.version.outputs.version }}" \ - --out-dir runtime-packages - ls -la runtime-packages + --out-dir "$OUT_DIR" + ls -la "$OUT_DIR" # Between build and publish, never after. npm-packlist honors a # `.gitignore` or `.npmignore` shipped inside a packed subdirectory, so a @@ -182,7 +186,7 @@ jobs: if: steps.token.outputs.present == 'true' run: | set -euo pipefail - for DIR in runtime-packages/runtime-*/; do + for DIR in "$RUNTIME_PACKAGES_DIR"/runtime-*/; do node apps/ade-cli/scripts/verify-runtime-package-contents.mjs "$DIR" done @@ -200,19 +204,19 @@ jobs: # optionalDependencies, so publishing it first would leave a window in # which installing it resolves nothing. ORDER="" - for DIR in runtime-packages/*/; do + for DIR in "$RUNTIME_PACKAGES_DIR"/*/; do case "$DIR" in - runtime-packages/runtime/) ;; + */runtime/) ;; *) ORDER="$ORDER $DIR" ;; esac done - ORDER="$ORDER runtime-packages/runtime/" + ORDER="$ORDER $RUNTIME_PACKAGES_DIR/runtime/" # Skip-if-exists, matching publish-sdk-packages.yml's auto route: a # re-run after a partial failure must publish only what is missing # rather than abort on the packages that already landed. for DIR in $ORDER; do - PKG=$(node -p "require('./${DIR}package.json').name") + PKG=$(node -p "require(require('node:path').resolve('$DIR', 'package.json')).name") if npm view "$PKG@$VERSION" version >/dev/null 2>&1; then echo "$PKG@$VERSION already published; skipping." >> "$GITHUB_STEP_SUMMARY" continue diff --git a/apps/ade-cli/scripts/build-runtime-npm-packages.mjs b/apps/ade-cli/scripts/build-runtime-npm-packages.mjs index eebe54486..4c755102c 100644 --- a/apps/ade-cli/scripts/build-runtime-npm-packages.mjs +++ b/apps/ade-cli/scripts/build-runtime-npm-packages.mjs @@ -386,6 +386,34 @@ function defaultPackRunner(cwd) { * comment claimed: that script walks `packages/` only, and these packages are * built into `runtime-packages/`, which it never sees. */ +/** + * Paths `npm pack --dry-run --json` says it would publish. + * + * npm 10/11 emit an array of entries. npm 12 (what `npm@latest` is on the + * publish runner) emits an object keyed by package name. Treating that object + * as the entry made `files` undefined, so a real native tree reported every + * on-disk path as missing, starting at `bin/ade`. + */ +export function packedPathsFromNpmPackJson(parsed) { + if (parsed == null) return []; + let entry = null; + if (Array.isArray(parsed)) { + entry = parsed[0] ?? null; + } else if (Array.isArray(parsed.files)) { + entry = parsed; + } else { + entry = Object.values(parsed).find((value) => value && Array.isArray(value.files)) ?? null; + } + const files = Array.isArray(entry?.files) ? entry.files : []; + const paths = []; + for (const file of files) { + const raw = typeof file === "string" ? file : file?.path; + if (typeof raw !== "string" || raw.length === 0) continue; + paths.push(raw.replace(/^package\//, "")); + } + return paths; +} + export function verifyPackedRuntimeFiles({ packageDir, runPack = defaultPackRunner }) { // The directory name carries the target, so the launcher's name is decided, // not a choice of two. Accepting either one let a `runtime-win32-x64` @@ -414,8 +442,7 @@ export function verifyPackedRuntimeFiles({ packageDir, runPack = defaultPackRunn `package whose contents were never verified.`, ); } - const entry = Array.isArray(parsed) ? parsed[0] : parsed; - const packed = new Set((Array.isArray(entry?.files) ? entry.files : []).map((file) => file.path)); + const packed = new Set(packedPathsFromNpmPackJson(parsed)); const missing = onDisk.filter((file) => !packed.has(file)); if (missing.length > 0) { throw new Error( @@ -628,8 +655,7 @@ export function verifyPackedMetaFiles({ packageDir, runPack = defaultPackRunner `package whose contents were never verified.`, ); } - const entry = Array.isArray(parsed) ? parsed[0] : parsed; - const packed = new Set((Array.isArray(entry?.files) ? entry.files : []).map((file) => file.path)); + const packed = new Set(packedPathsFromNpmPackJson(parsed)); for (const required of ["LICENSE", EXCEPTION_FILE_NAME, "README.md", "package.json"]) { if (!packed.has(required)) { throw new Error( diff --git a/apps/ade-cli/scripts/build-runtime-npm-packages.test.mjs b/apps/ade-cli/scripts/build-runtime-npm-packages.test.mjs index 808a98051..a17cd68c1 100644 --- a/apps/ade-cli/scripts/build-runtime-npm-packages.test.mjs +++ b/apps/ade-cli/scripts/build-runtime-npm-packages.test.mjs @@ -13,6 +13,7 @@ import { buildMetaPackage, buildRuntimePackage, metaPackageManifest, + packedPathsFromNpmPackJson, parseArgs, readLicenseFiles, runtimeAssetNames, @@ -128,6 +129,21 @@ test("npm pack dry-run buffer is large enough for a real native tree", () => { assert.match(source, /maxBuffer:\s*NPM_PACK_MAX_BUFFER_BYTES/); }); +test("reads npm 12 pack --json objects keyed by package name", () => { + assert.deepEqual( + packedPathsFromNpmPackJson({ + "@ade-dev/runtime-linux-x64": { + files: [{ path: "bin/ade" }, { path: "package/native/manifest.json" }], + }, + }), + ["bin/ade", "native/manifest.json"], + ); + assert.deepEqual( + packedPathsFromNpmPackJson([{ files: [{ path: "bin/ade" }, { path: "package.json" }] }]), + ["bin/ade", "package.json"], + ); +}); + test("Windows native archive is ade-win32-x64.native.tar.gz, not glued onto .exe", () => { assert.deepEqual(runtimeAssetNames("win32-x64"), { binaryAsset: "ade-win32-x64.exe", @@ -145,6 +161,8 @@ test("publish workflow checksum step uses runtimeAssetNames", () => { "utf8", ); assert.match(workflow, /runtimeAssetNames/); + assert.match(workflow, /\$\{RUNNER_TEMP\}/); + assert.match(workflow, /RUNTIME_PACKAGES_DIR/); assert.doesNotMatch(workflow, /ade-win32-x64\\.exe\(\\\.native\\.tar\\.gz\)\?/); });