From 66d5ee29b8dceeeb91974f3de8008fec0ea91f29 Mon Sep 17 00:00:00 2001 From: wit_qq Date: Sun, 6 Sep 2026 00:34:57 +0300 Subject: [PATCH] fix: handle npm 12 registry misses Use npm view exit status for coordinated publication preflight and reject non-URL registry responses. --- .github/workflows/publish-npm.yml | 9 ++++++--- scripts/check-workflows.mjs | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index f58e43b..3d796e4 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -145,9 +145,12 @@ jobs: done < "${package_rows}" while IFS=$'\t' read -r index package_name workspace asset_name expected_sha256 asset_url; do - existing_url="$(npm view "${package_name}@${version}" dist.tarball --json 2>/dev/null || true)" - if [[ -n "${existing_url}" && "${existing_url}" != "null" ]]; then - registry_url="$(node -e 'process.stdout.write(JSON.parse(process.argv[1]))' "${existing_url}")" + if existing_url="$(npm view "${package_name}@${version}" dist.tarball --json 2>/dev/null)"; then + registry_url="$(node -e ' + const value = JSON.parse(process.argv[1]); + if (typeof value !== "string" || !value.startsWith("https://")) throw new Error("registry tarball URL is invalid"); + process.stdout.write(value); + ' "${existing_url}")" registry_tarball="${RUNNER_TEMP}/registry-preflight-${index}.tgz" curl --fail --location --proto '=https' --tlsv1.2 --output "${registry_tarball}" "${registry_url}" registry_sha256="$(shasum -a 256 "${registry_tarball}" | cut -d ' ' -f 1)" diff --git a/scripts/check-workflows.mjs b/scripts/check-workflows.mjs index 548e538..ae4f0d8 100644 --- a/scripts/check-workflows.mjs +++ b/scripts/check-workflows.mjs @@ -54,6 +54,8 @@ for (const required of [ const preflight = runs.indexOf('registry-preflight-${index}.tgz'); const firstPublish = runs.indexOf('npm publish --access public "${asset_url}"'); assert.ok(preflight >= 0 && preflight < firstPublish, 'all existing registry versions are checked before publication'); +assert.ok(runs.includes('if existing_url="$(npm view'), 'registry absence must be decided by npm view exit status'); +assert.ok(!runs.includes('dist.tarball --json 2>/dev/null || true'), 'registry lookup errors must not become JSON values'); for (const forbidden of ['actions/checkout@', 'NPM_TOKEN', 'NODE_AUTH_TOKEN', 'npm ci', 'npm run build', 'npm test', 'npm pack']) { assert.ok(!publishSource.includes(forbidden), `publication must exclude ${forbidden}`); }