Conversation
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| - name: Build | ||
| run: | | ||
| if [ "${{ steps.pm.outputs.manager }}" = "pnpm" ]; then | ||
| pnpm build | ||
| else | ||
| npm run build | ||
| fi | ||
|
|
||
| - name: Upgrade npm for trusted publishing |
| - name: Publish or dry-run | ||
| env: | ||
| NPM_CONFIG_PROVENANCE: true | ||
| PACKAGES: ${{ inputs.packages }} | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| PACK: ${{ steps.pm.outputs.manager }} | ||
| run: | | ||
| echo "$PACKAGES" | jq -r '.[] | [.name, .version, .dist_tag] | @tsv' | while IFS=$'\t' read -r name version tag; do | ||
| short="${name#@clerk/}" | ||
| dir="packages/$short" | ||
|
|
||
| if [ ! -d "$dir" ]; then | ||
| echo "::error::Package directory not found: $dir" | ||
| exit 1 | ||
| fi | ||
|
|
||
| pkg_version=$(jq -r .version "$dir/package.json") | ||
| if [ "$pkg_version" != "$version" ]; then | ||
| echo "::error::$dir/package.json has version $pkg_version, expected $version" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "::group::Pack $name@$version" | ||
| if [ "$PACK" = "pnpm" ]; then | ||
| out=$(cd "$dir" && pnpm pack --json 2>/dev/null || true) | ||
| if [ -n "$out" ] && echo "$out" | jq -e . >/dev/null 2>&1; then | ||
| tarball=$(echo "$out" | jq -r '.filename') | ||
| else | ||
| # pnpm pack without --json prints the tarball path on stdout | ||
| tarball=$(cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}") | ||
| fi | ||
| else | ||
| tarball=$(cd "$dir" && npm pack --json | jq -r '.[0].filename') | ||
| fi | ||
| if [ -z "$tarball" ] || [ ! -f "$dir/$tarball" ]; then | ||
| echo "::error::Failed to resolve tarball filename in $dir" | ||
| exit 1 | ||
| fi | ||
| echo "packed: $dir/$tarball" | ||
| echo "::endgroup::" | ||
|
|
||
| if [ "$DRY_RUN" = "true" ]; then | ||
| echo "::notice::DRY RUN: would publish $name@$version --tag $tag" | ||
| else | ||
| echo "::group::Publish $name@$version --tag $tag" | ||
| (cd "$dir" && npm publish "$tarball" --tag "$tag" --provenance) | ||
| echo "::endgroup::" | ||
| fi | ||
| done | ||
|
|
||
| - name: Summary |
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 571-662: The workflow allows checking out arbitrary commits
(inputs.source_ref) and iterating unvalidated package names (PACKAGES) which
enables TOCTOU/execution of untrusted code and path traversal; fix by (1) after
the Checkout step verify that inputs.source_ref is an ancestor of a protected
branch (e.g., origin/main) using git fetch + git merge-base --is-ancestor and
fail the job if not, referencing the checkout/ref logic around
inputs.source_ref, and (2) validate each package name before the existing
PACKAGES parsing loop (the echo "$PACKAGES" | jq ... | while ... read -r name
...) by enforcing the regex ^@clerk/[a-z0-9][a-z0-9-]*$ and rejecting any names
that do not match or contain path traversal (../) before computing
short="${name#@clerk/}" and dir="packages/$short".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: cef47547-72a6-4312-b155-3192e592612e
📒 Files selected for processing (2)
.github/workflows/release.ymlscripts/legacy-release.sh
There was a problem hiding this comment.
🧹 Nitpick comments (2)
.github/workflows/release.yml (2)
626-634: Assumption: package directory name matches npm package short name.Line 628 constructs
dir="packages/$short"whereshortis derived from stripping@clerk/from the package name. This assumes the directory name underpackages/matches the npm short name exactly.While this convention holds for most Clerk packages and aligns with
scripts/legacy-release.sh, it could fail for packages where the directory name differs (e.g., a package@clerk/foo-barinpackages/foobar/).Consider adding a fallback that searches for the package by its
namefield inpackage.jsonif the directory doesn't exist, or document this naming requirement.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 626 - 634, The workflow assumes dir="packages/$short" where short="${name#@clerk/}" matches the filesystem, which can break if package dirs are named differently; update the loop that reads PACKAGES (the echo ... | while IFS=... read -r name version tag block) to, when [ ! -d "$dir" ], attempt a fallback: search under packages/ for a package.json whose "name" equals "$name" and set dir to that matching directory (or fail with a clear error if none/ambiguous), ensuring you reference the variables name, short, dir and the PACKAGES parsing logic when implementing the lookup or alternatively add a clear comment/documentation enforcing the naming requirement.
642-656: pnpm pack fallback logic could be more robust.The fallback at line 648 merges stderr with stdout and takes the last line. If
pnpm packfails with an error message that happens to look like a filename, the subsequent existence check at line 653 would catch it, but the error flow is unclear.Suggested improvement
if [ -n "$out" ] && echo "$out" | jq -e . >/dev/null 2>&1; then tarball=$(echo "$out" | jq -r '.filename') else - # pnpm pack without --json prints the tarball path on stdout - tarball=$(cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}") + # pnpm pack without --json prints the tarball path on stdout (stderr separate) + tarball=$(cd "$dir" && pnpm pack 2>/dev/null | tail -n1 | xargs -I{} basename "{}") fiRedirecting stderr to
/dev/null(or a separate log) keeps error messages from polluting the tarball variable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 642 - 656, The pnpm fallback merges stderr into stdout which can pollute the tarball variable; change the fallback invocation that sets tarball (currently using cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}") to capture only stdout and silence stderr (e.g., cd "$dir" && pnpm pack 2>/dev/null | tail -n1 | xargs -I{} basename "{}") and also check the pnpm exit status before using the result so tarball is only assigned on successful pack; refer to the PACK variable handling and the tarball assignment code when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.github/workflows/release.yml:
- Around line 626-634: The workflow assumes dir="packages/$short" where
short="${name#@clerk/}" matches the filesystem, which can break if package dirs
are named differently; update the loop that reads PACKAGES (the echo ... | while
IFS=... read -r name version tag block) to, when [ ! -d "$dir" ], attempt a
fallback: search under packages/ for a package.json whose "name" equals "$name"
and set dir to that matching directory (or fail with a clear error if
none/ambiguous), ensuring you reference the variables name, short, dir and the
PACKAGES parsing logic when implementing the lookup or alternatively add a clear
comment/documentation enforcing the naming requirement.
- Around line 642-656: The pnpm fallback merges stderr into stdout which can
pollute the tarball variable; change the fallback invocation that sets tarball
(currently using cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename
"{}") to capture only stdout and silence stderr (e.g., cd "$dir" && pnpm pack
2>/dev/null | tail -n1 | xargs -I{} basename "{}") and also check the pnpm exit
status before using the result so tarball is only assigned on successful pack;
refer to the PACK variable handling and the tarball assignment code when making
this change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 023b158a-e078-46e0-9b16-0ecb55a242de
📒 Files selected for processing (1)
.github/workflows/release.yml
| - name: Install dependencies | ||
| env: | ||
| NPM_CONFIG_ENGINE_STRICT: "false" | ||
| run: | | ||
| if [ "${{ steps.pm.outputs.manager }}" = "pnpm" ]; then | ||
| pnpm install --frozen-lockfile | ||
| else | ||
| npm ci --audit=false --fund=false | ||
| fi | ||
|
|
||
| - name: Build |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.github/workflows/release.yml (1)
576-619:⚠️ Potential issue | 🔴 CriticalReject SHAs that are not reachable from a protected branch before install/build.
This still lets an arbitrary 40-char repo SHA reach checkout, dependency installation, and build in a job that can mint provenance and publish. The CodeQL TOCTOU issue is still open for unmerged PR commits.
Suggested guard
- name: Checkout source_ref uses: actions/checkout@v4 with: ref: ${{ inputs.source_ref }} - fetch-depth: 1 + fetch-depth: 0 show-progress: false + + - name: Verify source_ref is reachable from origin/main + env: + SOURCE_REF: ${{ inputs.source_ref }} + run: | + git fetch --no-tags origin main + git merge-base --is-ancestor "$SOURCE_REF" origin/main || { + echo "::error::source_ref must be reachable from origin/main" + exit 1 + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 576 - 619, The workflow currently checks out an arbitrary commit via the "Checkout source_ref" step using inputs.source_ref and proceeds to install/build; add a pre-build validation step (immediately after "Checkout source_ref" and before "Install dependencies"/"Build") that verifies the checked-out commit is reachable from one or more protected branches (e.g., main, release) and fails the job if it is not; implement the check using git reachability commands (for example, fetch the protected branch refs and use git merge-base --is-ancestor or git branch --contains) referencing inputs.source_ref and the checkout produced by actions/checkout@v4 so unmerged or unreachable SHAs cannot continue to the pnpm/npm install and build steps.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 563-575: The Validate packages step currently only rejects
dist_tag == "latest" but doesn't verify the full package object shape; update
the jq validations in the "Validate packages" step (using the PACKAGES env
variable) to assert for each object that it has non-empty "name", "version", and
"dist_tag" fields, that .dist_tag is not "latest", and that .name matches the
`@clerk/`<kebab-case> pattern (and optionally .version matches a semver regex).
Replace the existing jq checks with a single all(.[]; <predicate>) expression
that checks has("name") and has("version") and has("dist_tag") and .name |
test("^@clerk/[a-z0-9][a-z0-9-]*$") and (.version != "" and (.version |
test("^[0-9]+\\.[0-9]+\\.[0-9]+(-.*)?$"))) and (.dist_tag != "" and .dist_tag !=
"latest"), and fail the step with an explicit error if any package fails.
---
Duplicate comments:
In @.github/workflows/release.yml:
- Around line 576-619: The workflow currently checks out an arbitrary commit via
the "Checkout source_ref" step using inputs.source_ref and proceeds to
install/build; add a pre-build validation step (immediately after "Checkout
source_ref" and before "Install dependencies"/"Build") that verifies the
checked-out commit is reachable from one or more protected branches (e.g., main,
release) and fails the job if it is not; implement the check using git
reachability commands (for example, fetch the protected branch refs and use git
merge-base --is-ancestor or git branch --contains) referencing inputs.source_ref
and the checkout produced by actions/checkout@v4 so unmerged or unreachable SHAs
cannot continue to the pnpm/npm install and build steps.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: aa5acf05-fa51-4040-8541-68020f40c122
📒 Files selected for processing (1)
.github/workflows/release.yml
There was a problem hiding this comment.
♻️ Duplicate comments (2)
.github/workflows/release.yml (2)
554-561:⚠️ Potential issue | 🔴 CriticalConstrain
source_refto trusted history before running this path.Line 558 only proves the input looks like a SHA. This job still checks out that ref and runs setup/install/build with
id-token: write, so the dispatch gate controls who can start the run but not what code executes. That is the same TOCTOU path the static analysis is still flagging.🔒 Suggested hardening
- name: Checkout source_ref uses: actions/checkout@v4 with: ref: ${{ inputs.source_ref }} fetch-depth: 1 + persist-credentials: false show-progress: false + - name: Ensure source_ref is reachable from a protected release line + env: + SOURCE_REF: ${{ inputs.source_ref }} + run: | + git fetch --no-tags origin main + git merge-base --is-ancestor "$SOURCE_REF" FETCH_HEAD || { + echo "::error::source_ref must be reachable from origin/main" + exit 1 + }Also applies to: 576-619
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 554 - 561, The current "Validate source_ref is a SHA" step only checks format (SOURCE_REF) but doesn't ensure the SHA exists in trusted repository history before the actions/checkout and subsequent steps run; update the workflow to, after validating format, fetch trusted refs (e.g., origin/main or origin/refs/heads/*) and verify SOURCE_REF is contained in those trusted refs (for example by fetching and using git branch --contains or git merge-base --is-ancestor) before running the actions/checkout and any steps that use id-token: write; reference the "Validate source_ref is a SHA" step and the actions/checkout step when adding the fetch+verify logic so the job aborts if SOURCE_REF is not provably in trusted history.
563-575:⚠️ Potential issue | 🟠 MajorValidate the full
packagespayload here, not justnameand"latest".Line 567 still treats
[]as valid, and missing/emptyversionordist_tagvalues fall through into the publish loop as late failures or silent no-ops. The helper script builds a well-formed payload, but manualworkflow_dispatchcalls bypass that safety net.🧪 Suggested validation
- name: Validate packages env: PACKAGES: ${{ inputs.packages }} run: | - echo "$PACKAGES" | jq -e 'all(.[]; .dist_tag != "latest")' > /dev/null || { - echo "::error::'latest' dist_tag is not allowed on this path"; exit 1; - } - invalid=$(echo "$PACKAGES" | jq -r '.[] | select(.name | test("^@clerk/[a-z0-9][a-z0-9-]*$") | not) | .name') - if [ -n "$invalid" ]; then - echo "::error::Invalid package name(s). Expected `@clerk/`<kebab-case>. Got: $invalid" - exit 1 - fi + echo "$PACKAGES" | jq -e ' + type == "array" and length > 0 and + all(.[]; + type == "object" and + has("name") and has("version") and has("dist_tag") and + (.name | type == "string" and test("^@clerk/[a-z0-9][a-z0-9-]*$")) and + (.version | type == "string" and . != "") and + (.dist_tag | type == "string" and . != "" and . != "latest") + ) + ' > /dev/null || { + echo "::error::packages must be a non-empty JSON array of {name, version, dist_tag} objects" + exit 1 + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 563 - 575, The "Validate packages" step currently only checks package names and forbids dist_tag "latest" but allows an empty array and missing/empty fields; update the PACKAGES validation to fully validate the payload: ensure PACKAGES is a non-empty JSON array and for each item verify .name matches the `@clerk/`<kebab-case> regex, .version exists and is a non-empty string, and .dist_tag exists and is a non-empty string (and not "latest"); implement this by replacing or augmenting the existing jq checks in the Validate packages step (the block using PACKAGES and jq, and the invalid variable computation) with a single jq expression that fails if any of these conditions are not met and surface clear error messages to stop the workflow early.
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)
583-587: Verify thatnpm@11matches the oldest legacy refs this path needs to support.This job takes Node from the checked-out
.nvmrcand then unconditionally upgrades tonpm@11. npm CLI 11 expects a currently supported Node.js line, and as of March 24, 2026 the active Node release lines were 20.x, 22.x, 24.x, and 25.x, so any ref pinned to 18 or older sits outside that support matrix. If older refs are in scope, consider switching back to a modern Node just fornpm publish, or fail early with a clearer message. (github.com)Also applies to: 618-619
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 583 - 587, The CI unconditionally upgrades to npm@11 after using actions/setup-node with node-version-file: .nvmrc, which may break older refs pinned to Node <20; change the workflow so the npm upgrade is gated: detect the resolved Node.js major version (from the setup-node step using node-version-file/.nvmrc) and only run the npm install -g npm@11 step when Node >=20 (or alternatively select an npm version compatible with the resolved Node), and if an older Node is detected either skip the npm@11 step or fail early with a clear message; update the steps referencing actions/setup-node@v4 and the npm@11 install step (lines that install npm@11) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In @.github/workflows/release.yml:
- Around line 554-561: The current "Validate source_ref is a SHA" step only
checks format (SOURCE_REF) but doesn't ensure the SHA exists in trusted
repository history before the actions/checkout and subsequent steps run; update
the workflow to, after validating format, fetch trusted refs (e.g., origin/main
or origin/refs/heads/*) and verify SOURCE_REF is contained in those trusted refs
(for example by fetching and using git branch --contains or git merge-base
--is-ancestor) before running the actions/checkout and any steps that use
id-token: write; reference the "Validate source_ref is a SHA" step and the
actions/checkout step when adding the fetch+verify logic so the job aborts if
SOURCE_REF is not provably in trusted history.
- Around line 563-575: The "Validate packages" step currently only checks
package names and forbids dist_tag "latest" but allows an empty array and
missing/empty fields; update the PACKAGES validation to fully validate the
payload: ensure PACKAGES is a non-empty JSON array and for each item verify
.name matches the `@clerk/`<kebab-case> regex, .version exists and is a non-empty
string, and .dist_tag exists and is a non-empty string (and not "latest");
implement this by replacing or augmenting the existing jq checks in the Validate
packages step (the block using PACKAGES and jq, and the invalid variable
computation) with a single jq expression that fails if any of these conditions
are not met and surface clear error messages to stop the workflow early.
---
Nitpick comments:
In @.github/workflows/release.yml:
- Around line 583-587: The CI unconditionally upgrades to npm@11 after using
actions/setup-node with node-version-file: .nvmrc, which may break older refs
pinned to Node <20; change the workflow so the npm upgrade is gated: detect the
resolved Node.js major version (from the setup-node step using
node-version-file/.nvmrc) and only run the npm install -g npm@11 step when Node
>=20 (or alternatively select an npm version compatible with the resolved Node),
and if an older Node is detected either skip the npm@11 step or fail early with
a clear message; update the steps referencing actions/setup-node@v4 and the
npm@11 install step (lines that install npm@11) accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 7616e008-676e-43ea-b35a-a5f81f2f6d24
📒 Files selected for processing (1)
.github/workflows/release.yml
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 649-666: The workflow is suppressing pnpm pack errors via the `||
true` which hides failures; update the `pnpm pack` invocation used when `PACK`
is "pnpm" (the block that sets `out` and `tarball`) to stop swallowing errors:
run `pnpm pack --json` without `|| true`, capture both stdout/stderr into a
variable, check the command exit status and if non-zero emit a clear `::error::`
with the captured pnpm output and exit non-zero so failures are visible (retain
the existing fallback that parses non-JSON output into `tarball` only when the
pack succeeded).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: dcb0263b-d237-42a9-ba07-dc092df75e88
📒 Files selected for processing (1)
.github/workflows/release.yml
| echo "::group::Pack $name@$version" | ||
| if [ "$PACK" = "pnpm" ]; then | ||
| out=$(cd "$dir" && pnpm pack --json 2>/dev/null || true) | ||
| if [ -n "$out" ] && echo "$out" | jq -e . >/dev/null 2>&1; then | ||
| tarball=$(echo "$out" | jq -r '.filename') | ||
| else | ||
| # pnpm pack without --json prints the tarball path on stdout | ||
| tarball=$(cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}") | ||
| fi | ||
| else | ||
| tarball=$(cd "$dir" && npm pack --json | jq -r '.[0].filename') | ||
| fi | ||
| if [ -z "$tarball" ] || [ ! -f "$dir/$tarball" ]; then | ||
| echo "::error::Failed to resolve tarball filename in $dir" | ||
| exit 1 | ||
| fi | ||
| echo "packed: $dir/$tarball" | ||
| echo "::endgroup::" |
There was a problem hiding this comment.
Error suppression in pnpm pack masks root cause failures.
The || true on line 651 suppresses pnpm pack errors. If pack fails legitimately (e.g., missing build artifacts), the workflow continues to line 661's check and fails with a generic "Failed to resolve tarball" message, obscuring the actual cause.
Suggested improvement
echo "::group::Pack $name@$version"
if [ "$PACK" = "pnpm" ]; then
- out=$(cd "$dir" && pnpm pack --json 2>/dev/null || true)
- if [ -n "$out" ] && echo "$out" | jq -e . >/dev/null 2>&1; then
+ if out=$(cd "$dir" && pnpm pack --json 2>&1); then
tarball=$(echo "$out" | jq -r '.filename')
else
- # pnpm pack without --json prints the tarball path on stdout
- tarball=$(cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}")
+ # Fallback: --json may not be supported in older pnpm
+ tarball=$(cd "$dir" && pnpm pack 2>&1 | tail -n1 | xargs -I{} basename "{}") || {
+ echo "::error::pnpm pack failed in $dir"
+ exit 1
+ }
fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 649 - 666, The workflow is
suppressing pnpm pack errors via the `|| true` which hides failures; update the
`pnpm pack` invocation used when `PACK` is "pnpm" (the block that sets `out` and
`tarball`) to stop swallowing errors: run `pnpm pack --json` without `|| true`,
capture both stdout/stderr into a variable, check the command exit status and if
non-zero emit a clear `::error::` with the captured pnpm output and exit
non-zero so failures are visible (retain the existing fallback that parses
non-JSON output into `tarball` only when the pack succeeded).
Adds a workflow_dispatch trigger to the release workflow for releasing from arbitrary refs.