Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/actions/npm-publish-hardened/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ exposed. So this action only supports tarballs; the caller is
responsible for the `npm pack` step and for uploading the same
tarball to the GitHub release.

## New-package preflight

Before publishing anything, the action verifies every package in the
queue already exists on the registry. Trusted publishing cannot create
a brand-new package (npm requires the package to exist before a
trusted publisher can be configured), so a first-ever publish would
otherwise fail late with an opaque `ENEEDAUTH` after sibling packages
already published. A missing package fails the run immediately with
bootstrap instructions:

1. `npm publish` a placeholder manually (e.g. `0.0.1-placeholder.0`
with `--tag placeholder`),
2. add a trusted publisher in the package settings on npmjs.com
(allow "publish"),
3. re-run the workflow.

Transient registry errors during the preflight only warn — the
publish loop has its own retries.
Comment on lines +36 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the Stella GitHub Development Guidelines, we should avoid using em dashes and instead prefer semicolons or other punctuation.

Suggested change
Transient registry errors during the preflight only warn the
publish loop has its own retries.
Transient registry errors during the preflight only warn; the
publish loop has its own retries.
References
  1. Vary punctuation: prefer colons, semicolons, commas, and parentheses over em dashes (link)


## What it does

1. Hard-fails if `NPM_TOKEN` or `NODE_AUTH_TOKEN` is in the
Expand Down
43 changes: 43 additions & 0 deletions .github/actions/npm-publish-hardened/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,49 @@ done
PKG_JSON_FILE="${RUNNER_TEMP:-/tmp}/npm-publish-hardened-pkg-$$.json"
trap 'rm -f "${PKG_JSON_FILE}"' EXIT

pkg_name_from_tarball() {
local tarball="$1"
tar -xOf "${tarball}" package/package.json > "${PKG_JSON_FILE}"
node -e '
const j = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
console.log(j.name ?? "");
' "${PKG_JSON_FILE}"
}

# Preflight: every package in the queue must already exist on the
# registry. OIDC trusted publishing cannot create a brand-new package —
# npm requires a package to exist before a trusted publisher can be
# configured for it — so a first-ever publish would otherwise burn all
Comment on lines +98 to +100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the Stella GitHub Development Guidelines, we should avoid using em dashes and instead prefer parentheses or other punctuation.

Suggested change
# registry. OIDC trusted publishing cannot create a brand-new package
# npm requires a package to exist before a trusted publisher can be
# configured for it so a first-ever publish would otherwise burn all
# registry. OIDC trusted publishing cannot create a brand-new package
# (npm requires a package to exist before a trusted publisher can be
# configured for it), so a first-ever publish would otherwise burn all
References
  1. Vary punctuation: prefer colons, semicolons, commas, and parentheses over em dashes (link)

# retries and fail late with an opaque ENEEDAUTH, after sibling
# packages already published. Fail fast, before publishing anything,
# with bootstrap instructions instead.
declare -a PREFLIGHT_MISSING=()
for tarball in "${PUBLISH_QUEUE[@]}"; do
preflight_name=$(pkg_name_from_tarball "${tarball}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Under set -e, if the command substitution pkg_name_from_tarball fails (e.g., if the tarball is corrupt or package.json is missing), the script will exit immediately. This prevents the custom error message and bootstrap instructions on line 108 from being displayed. Appending || preflight_name="" ensures the failure is caught gracefully and the helpful error message is printed.

Suggested change
preflight_name=$(pkg_name_from_tarball "${tarball}")
preflight_name=$(pkg_name_from_tarball "${tarball}") || preflight_name=""

if [[ -z "${preflight_name}" || "${preflight_name}" == "null" ]]; then
printf '::error::Failed to read package name from %s.\n' "${tarball}"
exit 2
fi
if view_output=$(npm view "${preflight_name}" name 2>&1); then
continue
fi
if grep -q 'E404' <<<"${view_output}"; then
PREFLIGHT_MISSING+=("${preflight_name}")
else
# Transient registry error must not block an otherwise valid
# release; the publish loop below has its own retries.
printf '::warning::Could not verify %s exists on the registry; continuing.\n' \
"${preflight_name}"
fi
done
if (( ${#PREFLIGHT_MISSING[@]} > 0 )); then
for preflight_name in "${PREFLIGHT_MISSING[@]}"; do
printf '::error::%s has never been published. Trusted publishing cannot create new packages. Bootstrap it first: (1) npm publish a placeholder manually (e.g. version 0.0.1-placeholder.0 with --tag placeholder), (2) add a trusted publisher in the package settings on npmjs.com (allow "publish"), then re-run this workflow. Nothing was published in this run.\n' \
"${preflight_name}"
done
exit 2
fi

publish_one() {
local tarball="$1"
local package_name package_version
Expand Down
Loading