Skip to content

fix(release): make publish protected-branch safe - #235

Merged
khaliqgant merged 2 commits into
mainfrom
codex/publish-protected-branch-safe
Aug 11, 2026
Merged

fix(release): make publish protected-branch safe#235
khaliqgant merged 2 commits into
mainfrom
codex/publish-protected-branch-safe

Conversation

@barryollama

Copy link
Copy Markdown
Contributor

Summary

  • sync the recovered 0.1.58 lockfile metadata with package.json
  • make version bumps open a protected-branch-safe release PR and stop before publishing
  • tag only canonical main commits, then publish, with explicit recovery for tag/npm partial states
  • verify existing registry payload contents, provenance, dist-tag, and tag payload before treating a rerun as complete
  • add deterministic release-state and workflow-policy tests

Recovery context

Publish run 31523445478 successfully published @agent-relay/factory@0.1.58 and pushed v0.1.58, then failed pushing its version commit directly to protected main. PR #234 restored the exact package.json version delta. This PR does not publish or change 0.1.58; it closes the lockfile review finding and prevents recurrence.

Verification

  • npm ci
  • npm run build
  • npx vitest run src/release-state.test.ts (8 passed)
  • npm pack --dry-run
  • npm run verify:e2e (6 tests + 11 packed checks passed, head-bound to 6c5e53f)
  • actionlint v1.7.12 .github/workflows/publish.yml
  • YAML parse + git diff --check
  • full npm test: 1539/1542 passed under host contention; the 3 failures were existing timing-sensitive tests and each passed on isolated rerun (dist entrypoint required a 30s timeout on the loaded host)

No npm publish or tag mutation is performed by this PR.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@barryollama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 190c6964-ea1d-492a-ac87-540b176d26e7

📥 Commits

Reviewing files that changed from the base of the PR and between 5971ab4 and 708c177.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • .github/workflows/publish.yml
  • scripts/release-state.mjs
  • scripts/verify-release-payload.sh
  • src/release-state.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/workflows/publish.yml
Comment thread .github/workflows/publish.yml Outdated
Comment thread scripts/release-state.mjs Outdated
Comment thread .github/workflows/publish.yml Outdated
@barryollama

Copy link
Copy Markdown
Contributor Author

Addressed all four Cubic findings in 708c177:

  • live releases now fail closed unless GITHUB_REF is refs/heads/main and GITHUB_SHA equals current origin/main
  • legacy tags are checked by building and packing the tagged commit itself, then comparing the extracted payload to npm
  • payload/provenance verification is centralized in scripts/verify-release-payload.sh and used by both preflight and final verification
  • release-state.mjs now guards a missing process.argv[1], with a subprocess import regression test

Local validation: build, 9 focused tests, actionlint, YAML/bash syntax, current payload comparison, and a full rebuild/pack comparison from orphaned tag v0.1.58 all pass.

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/publish.yml">

<violation number="1" location=".github/workflows/publish.yml:54">
P1: A merge to main during the workflow can make this check stale, while the later tag and npm publish still use `$GITHUB_SHA`; the job can therefore release a commit that is no longer current `origin/main`. Revalidation immediately before both side effects, together with serialization or an atomic head check, would preserve the canonical-main guarantee.</violation>
</file>

<file name="scripts/verify-release-payload.sh">

<violation number="1" location="scripts/verify-release-payload.sh:15">
P2: Release recovery can accept an npm payload whose executable bits differ from this checkout because `diff -qr` does not compare permissions. Comparing file modes as well would prevent a legacy tag or existing release with different executable behavior from being treated as equivalent.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

echo "::error::Live releases must be dispatched from main, not $GITHUB_REF"
exit 1
fi
REMOTE_MAIN=$(git ls-remote origin refs/heads/main | awk '{print $1}')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: A merge to main during the workflow can make this check stale, while the later tag and npm publish still use $GITHUB_SHA; the job can therefore release a commit that is no longer current origin/main. Revalidation immediately before both side effects, together with serialization or an atomic head check, would preserve the canonical-main guarantee.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish.yml, line 54:

<comment>A merge to main during the workflow can make this check stale, while the later tag and npm publish still use `$GITHUB_SHA`; the job can therefore release a commit that is no longer current `origin/main`. Revalidation immediately before both side effects, together with serialization or an atomic head check, would preserve the canonical-main guarantee.</comment>

<file context>
@@ -42,6 +42,21 @@ jobs:
+            echo "::error::Live releases must be dispatched from main, not $GITHUB_REF"
+            exit 1
+          fi
+          REMOTE_MAIN=$(git ls-remote origin refs/heads/main | awk '{print $1}')
+          if [ -z "$REMOTE_MAIN" ] || [ "$REMOTE_MAIN" != "$GITHUB_SHA" ]; then
+            echo "::error::Checked-out SHA $GITHUB_SHA is not current origin/main ($REMOTE_MAIN)"
</file context>

npm pack "$PACKAGE_NAME@$VERSION" --pack-destination "$TMP_DIR/registry" --silent >/dev/null
tar -xzf "$TMP_DIR/local"/*.tgz -C "$TMP_DIR/local-x"
tar -xzf "$TMP_DIR/registry"/*.tgz -C "$TMP_DIR/registry-x"
diff -qr "$TMP_DIR/local-x/package" "$TMP_DIR/registry-x/package"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Release recovery can accept an npm payload whose executable bits differ from this checkout because diff -qr does not compare permissions. Comparing file modes as well would prevent a legacy tag or existing release with different executable behavior from being treated as equivalent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/verify-release-payload.sh, line 15:

<comment>Release recovery can accept an npm payload whose executable bits differ from this checkout because `diff -qr` does not compare permissions. Comparing file modes as well would prevent a legacy tag or existing release with different executable behavior from being treated as equivalent.</comment>

<file context>
@@ -0,0 +1,22 @@
+npm pack "$PACKAGE_NAME@$VERSION" --pack-destination "$TMP_DIR/registry" --silent >/dev/null
+tar -xzf "$TMP_DIR/local"/*.tgz -C "$TMP_DIR/local-x"
+tar -xzf "$TMP_DIR/registry"/*.tgz -C "$TMP_DIR/registry-x"
+diff -qr "$TMP_DIR/local-x/package" "$TMP_DIR/registry-x/package"
+
+PROVENANCE=$(npm view "$PACKAGE_NAME@$VERSION" \
</file context>

@khaliqgant
khaliqgant merged commit 0ef6790 into main Aug 11, 2026
7 checks passed
@khaliqgant
khaliqgant deleted the codex/publish-protected-branch-safe branch August 11, 2026 20:00
@barryollama

Copy link
Copy Markdown
Contributor Author

The two findings from the final Cubic review are addressed in focused follow-up #236 because this PR was merged externally while the fixes were in progress. #236 revalidates current main immediately before both tag/publish side effects and compares packed file modes/types/symlinks in addition to contents.

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.

3 participants