Skip to content

ci(npm-publish): harden version guard, pin npm major#25

Merged
zackkatz merged 1 commit into
mainfrom
develop
Jun 12, 2026
Merged

ci(npm-publish): harden version guard, pin npm major#25
zackkatz merged 1 commit into
mainfrom
develop

Conversation

@zackkatz

@zackkatz zackkatz commented Jun 12, 2026

Copy link
Copy Markdown
Member

Review follow-ups to the npm publish workflow:

  • Fail fast on indeterminate registry state: only a definitive not-found (E404 / no match found) proceeds to publish; any other npm view failure (network, registry outage) logs the output and fails the job instead of slipping past the idempotency guard. Success additionally requires non-empty output, covering npm versions that report a missing version as exit 0 with empty stdout.
  • Pin npm@11 instead of npm@latest so a future npm major can't change publish behavior mid-release-job.

Note: 2.0.1 already published successfully via this workflow (trusted publishing was pre-registered) — merging this PR exercises the guard's skip path live.

Summary by CodeRabbit

  • Chores
    • Enhanced npm publishing workflow with improved package version detection and more defensive error handling to ensure greater release reliability.

💾 Build file (a5cdabe).

…ajor

The version guard treated any 'npm view' failure as 'not published',
letting a transient network or registry error proceed to a publish
attempt. Only a definitive not-found (E404 / no match) may proceed now;
any other failure logs the npm output and fails the job. Success also
requires non-empty output, covering npm versions that report a missing
version as exit 0 with nothing on stdout.

Pin the npm upgrade to the 11.x major so a future npm major release
can't change publish behavior mid-job.
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The npm publish workflow is hardened with a pinned npm major version and a rewritten idempotency guard. The guard now explicitly distinguishes between already-published versions, not-yet-published versions, and transient failures before running npm publish.

Changes

NPM Publish Workflow Hardening

Layer / File(s) Summary
NPM version pinning
.github/workflows/npm-publish.yml
Workflow npm setup pins to major version 11 instead of latest.
Publish idempotency guard
.github/workflows/npm-publish.yml
The guard is rewritten to capture npm view output and exit code, treating successful non-empty responses as "already published", specific 404-like outcomes as "not published", and all other failures as job-blocking errors with diagnostic output.

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • GravityKit/block-mcp#24: Updates the same npm-publish workflow's idempotency guard logic that checks npm view to decide whether to publish.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the two main changes: hardening the version guard logic and pinning npm to a major version.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/npm-publish.yml:
- Around line 64-73: The current guard captures both stdout and stderr into
OUTPUT (OUTPUT="$(npm view ... 2>&1)"), so warnings on stderr can make [ -n
"$OUTPUT" ] true and set published=true incorrectly; change the npm view
invocation to capture stdout and stderr separately (store stdout in OUTPUT and
stderr in a different variable or temp file), preserve the command exit code in
STATUS, then base the published=true check only on the stdout variable (OUTPUT)
being non-empty and STATUS being zero while using the stderr variable only for
logging/diagnosis and for the alternative E404/not-found checks.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8d94a51b-b3e6-455e-9152-a4dcfbf2e176

📥 Commits

Reviewing files that changed from the base of the PR and between 26fae5f and a5cdabe.

📒 Files selected for processing (1)
  • .github/workflows/npm-publish.yml

Comment on lines +64 to 73
if OUTPUT="$(npm view "@gravitykit/block-mcp@$VERSION" version 2>&1)"; then
STATUS=0
else
STATUS=$?
fi
if [ "$STATUS" -eq 0 ] && [ -n "$OUTPUT" ]; then
echo "published=true" >> "$GITHUB_OUTPUT"
echo "@gravitykit/block-mcp@$VERSION is already on npm — nothing to publish."
else
elif [ "$STATUS" -eq 0 ] || printf '%s' "$OUTPUT" | grep -qiE 'E404|not found|no match found'; then
echo "published=false" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify behavior on the CI runner/npm version in use:
# Expected:
# - Existing version => stdout non-empty
# - Missing version => either non-zero + 404-like stderr OR zero + empty stdout
# - stderr notices must not influence stdout-based published check

set -euo pipefail
PKG="`@gravitykit/block-mcp`"
EXISTING="$(npm view "$PKG" version)"
MISSING="0.0.0-coderabbit-verify-$(date +%s)"

for V in "$EXISTING" "$MISSING"; do
  echo "=== Testing $PKG@$V ==="
  OUT="$(npm view "$PKG@$V" version 2>/tmp/npm-view.err || true)"
  STATUS=$?
  ERR="$(cat /tmp/npm-view.err || true)"
  printf 'status=%s\nstdout="%s"\nstderr="%s"\n\n' "$STATUS" "$OUT" "$ERR"
done

Repository: GravityKit/block-mcp

Length of output: 397


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/npm-publish.yml"
echo "== File = $FILE =="
nl -ba "$FILE" | sed -n '1,140p'

Repository: GravityKit/block-mcp

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/npm-publish.yml"
echo "== File = $FILE =="
# show around the referenced lines; use cat -n (nl may not exist)
cat -n "$FILE" | sed -n '40,110p'

Repository: GravityKit/block-mcp

Length of output: 2409


Split stdout/stderr in the npm view guard to prevent incorrect published=true results.

In .github/workflows/npm-publish.yml (lines 64-73), OUTPUT="$(npm view ... version 2>&1)" merges stderr into OUTPUT, but the logic later treats non-empty OUTPUT as evidence the version exists. Any npm warning/error emitted to stderr (even when stdout has no version) can make [ -n "$OUTPUT" ] pass and incorrectly set published=true.

Suggested fix
-          if OUTPUT="$(npm view "`@gravitykit/block-mcp`@$VERSION" version 2>&1)"; then
+          if OUTPUT="$(npm view "`@gravitykit/block-mcp`@$VERSION" version 2>/tmp/npm-view.err)"; then
             STATUS=0
           else
             STATUS=$?
           fi
-          if [ "$STATUS" -eq 0 ] && [ -n "$OUTPUT" ]; then
+          ERR_OUTPUT="$(cat /tmp/npm-view.err || true)"
+          if [ "$STATUS" -eq 0 ] && [ -n "$OUTPUT" ]; then
             echo "published=true" >> "$GITHUB_OUTPUT"
             echo "`@gravitykit/block-mcp`@$VERSION is already on npm — nothing to publish."
-          elif [ "$STATUS" -eq 0 ] || printf '%s' "$OUTPUT" | grep -qiE 'E404|not found|no match found'; then
+          elif [ "$STATUS" -eq 0 ] || printf '%s' "$ERR_OUTPUT" | grep -qiE 'E404|not found|no match found'; then
             echo "published=false" >> "$GITHUB_OUTPUT"
           else
             echo "::error::Could not determine whether `@gravitykit/block-mcp`@$VERSION is published (npm view exit $STATUS):" >&2
-            printf '%s\n' "$OUTPUT" >&2
+            printf '%s\n' "$ERR_OUTPUT" >&2
             exit 1
           fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/npm-publish.yml around lines 64 - 73, The current guard
captures both stdout and stderr into OUTPUT (OUTPUT="$(npm view ... 2>&1)"), so
warnings on stderr can make [ -n "$OUTPUT" ] true and set published=true
incorrectly; change the npm view invocation to capture stdout and stderr
separately (store stdout in OUTPUT and stderr in a different variable or temp
file), preserve the command exit code in STATUS, then base the published=true
check only on the stdout variable (OUTPUT) being non-empty and STATUS being zero
while using the stderr variable only for logging/diagnosis and for the
alternative E404/not-found checks.

@zackkatz zackkatz merged commit f0de697 into main Jun 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant