fix: extract Homebrew cask publishing into dedicated publish-cask job - #82
Merged
jflowers merged 1 commit intoAug 20, 2026
Merged
Conversation
The sign-macos job previously coupled code signing with Homebrew tap publishing, creating a TOCTOU race: re-uploading the signed archive changed its SHA, but a mid-job failure could leave the cask with the pre-signing SHA. When signing secrets were absent, the cask was never published at all. - Add dedicated publish-cask job that downloads the final archive from the GitHub Release and computes SHA from the actual artifact - Remove Homebrew tap logic from sign-macos (now signing-only) - Add SHA format validation (64-char hex) and post-patch verification - Handle both signed (success) and unsigned (skipped) release paths via always() + explicit result checks - Add OpenSpec change artifacts and Dewey learnings Fixes: unbound-force#81 Assisted-by: OpenCode (claude-opus-4) Generated with AI assistance — OpenCode (claude-opus-4)
em-redhat
approved these changes
Aug 20, 2026
em-redhat
left a comment
Contributor
There was a problem hiding this comment.
Review: APPROVE
This is a well-structured, well-specced fix that correctly addresses the root cause identified in #81. The TOCTOU race between signing and cask publishing is cleanly eliminated by extracting cask publishing into a dedicated downstream job that computes the SHA from the actual release artifact.
Constitution Alignment
| Principle | Status | Notes |
|---|---|---|
| I. Autonomous Collaboration | N/A | CI-only change, no MCP tools affected |
| II. Composability First | PASS | Restores independent installability via Homebrew for both signed and unsigned paths |
| III. Observable Quality | PASS | SHA validation regex, grep verification, and explicit error annotations improve observability |
| IV. Testability | PASS | Self-test steps validate SHA format and cask patching at release time |
Spec Alignment
Implementation faithfully follows all 5 design decisions (D1-D5). All 7 spec scenarios are covered.
Convention Pack Compliance
| Rule | Status | Notes |
|---|---|---|
| CI-001 (action pinning) | PASS | Reusable workflows pinned to SHA |
| CI-003 (permissions) | PASS | Top-level permissions: {}, job-level minimal scoping |
| CI-006 (timeout) | PASS | timeout-minutes: 10 on publish-cask |
| CI-009 (set -euo pipefail) | PASS | All shell steps |
| CI-014 (secrets via env) | PASS | Step outputs passed via env: bindings |
| CI-027 (job dependencies) | PASS | Proper needs + if guards |
Security
- Token in git clone URL is a pre-existing pattern (not introduced by this PR), acknowledged in Known Issues
- Step output injection: mitigated by SHA format validation +
env:binding pattern
What works well
- Empty-string guard on
ARM64_SHAcatches silent output-passing failures - SHA format validation (
^[0-9a-f]{64}$) prevents non-SHA propagation - Stricter grep verification (
sha256 "..."vs bare SHA match) - Commit message drops "(signed)" suffix — correct for unsigned path
- Clean
sign-macos/publish-caskseparation - Thorough spec artifacts and learnings
Non-blocking observations
shasumon ubuntu-latest (LOW): Works on GitHub-hosted runners butsha256sumis the more canonical Linux tool. Not worth changing.- Bundled Dewey learnings (INFO): 6 learning files included, some unrelated to this PR (github-config, security-policy, triage). Consider separating in future PRs for cleanliness.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #81 —
brew install unbound-force/tap/replicatorfails on v0.4.0 with a SHA-256 mismatch.The
sign-macosjob previously coupled code signing with Homebrew tap publishing in a single job, creating a TOCTOU race condition: re-uploading the signed archive changed its SHA, but a mid-job failure could leave the Homebrew cask with the pre-signing SHA. When signing secrets were absent, the cask was never published at all.This PR extracts Homebrew cask publishing into a dedicated
publish-caskjob that:awkand verifies the patch withgrepsign-macossuccess) and unsigned (sign-macosskipped) release paths viaalways()+ explicit result checkscontents: read) — write access only to the tap viaHOMEBREW_TAP_TOKENHow to Test
Structural review: Verify
sign-macosno longer contains Homebrew tap logic. Rungrep -n "homebrew\|tap\|cask" .github/workflows/release.yml— all matches should be in thepublish-caskjob section or the header comment.Conditional logic: The
publish-caskif:condition covers all 5 GitHub Actions result states:release=success+sign-macos=success→ runs (signed path)release=success+sign-macos=skipped→ runs (unsigned path)release=success+sign-macos=failure→ does NOT runrelease=success+sign-macos=cancelled→ does NOT runrelease=failure→ does NOT runEnd-to-end: Cut a release and verify
brew install unbound-force/tap/replicatorsucceeds.How to Demo
This is a CI pipeline fix — no user-visible behavior change beyond Homebrew install working correctly again. To demo:
workflow_dispatchwith a test tag (e.g.,v0.5.0-rc.1)preflight → release → sign-macos → publish-caskpublish-caskjob downloads the archive, computes SHA, patches the cask, and pushes to the tapbrew install unbound-force/tap/replicatorand confirm it installs without SHA mismatch errorsKey Files Changed
.github/workflows/release.ymlsign-macosinto dedicatedpublish-caskjob (+92/-16)CHANGELOG.mdopenspec/changes/fix-homebrew-sha-mismatch/.uf/dewey/learnings/Known Issues
The following findings from the review council were acknowledged but not resolved:
This PR was generated by /uf.finale (AI-assisted).