Skip to content

fix: Add resolver-bound recovery dispatch mode to native CLI publish#1797

Merged
hatayama merged 1 commit into
v3-betafrom
fix/native-publish-recovery-target
Jul 15, 2026
Merged

fix: Add resolver-bound recovery dispatch mode to native CLI publish#1797
hatayama merged 1 commit into
v3-betafrom
fix/native-publish-recovery-target

Conversation

@hatayama

@hatayama hatayama commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a recovery-target workflow_dispatch input to native-cli-publish.yml so a pending release whose assets were never published can be rebuilt and tagged at its release commit, while the workflow definition runs from the fixed branch head.

Root cause

The publish trust boundary requires the resolver-derived release target to equal the built commit. When a workflow bug is fixed after a release commit exists but before its assets publish (the current uloop-project-runner-v3.0.0-beta.48 situation), the branch head moves past the release commit and the guard correctly fails every re-dispatch. No path existed that builds the release commit source under the fixed workflow definition.

Fix

  • New recovery-target boolean dispatch input (default false). Push-event and default-dispatch semantics are unchanged.
  • Build job (unprivileged): when recovery is requested, verifies with git merge-base --is-ancestor that the resolver target is an ancestor of the approved event commit, then checks out that target. The existing guard now compares the target against the actual build source commit (git rev-parse HEAD), which is identical to the previous check outside recovery.
  • Publish job (privileged, checkout-free): computes a single RELEASE_SHA for all tag operations. Outside recovery it must equal the event commit exactly as before. In recovery it must equal the build SHA, and ancestry against the event commit is re-verified through the GitHub compare API — an input independent of the unprivileged build artifact.
  • No free-form SHA/ref/tag input exists; the recovery target is always bound to the resolver output. The cli-release environment approval continues to gate publishing.
  • scripts/test-native-cli-publish-workflow.sh gains test_recovery_dispatch_is_bound_to_the_resolver_target covering the input, ancestor checks, ordering, and the RELEASE_SHA anchoring; the existing no-checkout/no-scripts publish assertions still pass.

The dispatcher workflow needs no change: its resolver falls back to the branch head for an absent tag, and the dispatcher source is identical between the release commit and the current head, with the pin gating dispatchers by semver floor.

Verification

  • scripts/test-native-cli-publish-workflow.sh
  • scripts/test-dispatcher-publish-workflow.sh
  • YAML syntax check of the workflow
  • go run ./cmd/check-release-triggers --base origin/v3-beta --head HEAD
  • git diff --check
  • Compare API semantics verified against live data: release-commit-to-head resolves to ahead, self-compare to identical

Recovery sequence after merge (owner approval already granted)

gh workflow run native-cli-publish.yml --ref v3-beta -f dry-run=false -f recovery-target=true
gh workflow run dispatcher-publish.yml --ref v3-beta -f dry-run=false

Expected: native resolver target uloop-project-runner-v3.0.0-beta.48 tagged at 2d8d1b9443843f45b48479134f846b6f540e928b; dispatcher target dispatcher-v3.1.0-beta.13 tagged at the branch head. Both publish jobs then wait for cli-release environment approval.

Review in cubic

The first real release after the publish trust boundary landed exposed a
gap: when a workflow bug is fixed after a release commit exists but before
its assets are published, the branch head no longer equals the resolver
target, and the approved-commit guard correctly refuses to publish. There
was no path that builds the release commit source while running the fixed
workflow definition.

A workflow_dispatch recovery-target input now lets the unprivileged build
job check out the resolver-derived release commit after verifying it is an
ancestor of the approved event commit. The build guard compares the target
against the actual build source commit, which keeps push-event semantics
unchanged. The privileged publish job re-verifies ancestry through the
GitHub compare API instead of trusting the build artifact, and anchors
every tag operation on a single RELEASE_SHA that equals the event commit
outside recovery. No free-form SHA input exists; the target is always
bound to the resolver output, and the cli-release environment approval
still gates publishing.
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 659cdefb-2588-4292-8b66-f16e8eb58a9a

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2bc50 and 103b61f.

📒 Files selected for processing (2)
  • .github/workflows/native-cli-publish.yml
  • scripts/test-native-cli-publish-workflow.sh

📝 Walkthrough

Walkthrough

The release workflow accepts a resolver-derived recovery target, checks its ancestry, builds from the checked-out commit, and propagates the resulting release SHA through validation and publishing. Workflow tests verify the recovery-target constraints and updated mismatch messages.

Changes

Native CLI recovery release

Layer / File(s) Summary
Recovery checkout and build metadata
.github/workflows/native-cli-publish.yml
Adds the recovery-target dispatch input, checks out an ancestry-verified recovery commit, and records the checked-out commit as build_sha.
Release SHA validation and propagation
.github/workflows/native-cli-publish.yml
Branches validation for recovery and normal releases, exports RELEASE_SHA, and uses it for release tag checks, release creation, asset uploads, and final publishing.
Workflow security tests
scripts/test-native-cli-publish-workflow.sh
Adds recovery-target assertions, updates the expected release-commit mismatch message, and registers the new test.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkflowDispatch
  participant BuildJob
  participant PublishJob
  participant GitHubRelease
  WorkflowDispatch->>BuildJob: provide recovery-target
  BuildJob->>BuildJob: verify ancestry and checkout TARGET_SHA
  BuildJob->>PublishJob: pass BUILD_SHA and TARGET_SHA
  PublishJob->>PublishJob: derive and validate RELEASE_SHA
  PublishJob->>GitHubRelease: create and publish release using RELEASE_SHA
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a resolver-bound recovery dispatch mode to native CLI publish.
Description check ✅ Passed The description is directly related to the workflow and test changes, and explains the recovery dispatch behavior in detail.
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.
✨ 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 fix/native-publish-recovery-target

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.

@hatayama
hatayama merged commit db2e7f4 into v3-beta Jul 15, 2026
9 checks passed
@hatayama
hatayama deleted the fix/native-publish-recovery-target branch July 15, 2026 15:04
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