Skip to content

ci: publish signed macOS releases from Actions - #100

Merged
steipete merged 1 commit into
mainfrom
ci-release-publisher
Oct 16, 2026
Merged

steipete merged 1 commit into
mainfrom
ci-release-publisher

Conversation

@steipete

Copy link
Copy Markdown
Collaborator

Moves official macOS publication from local scripts into GitHub Actions. Signed annotated version-tag pushes validate metadata and exact-source push CI, build the universal CLI, sign and notarize with the Foundation identity, verify on fresh arm64 and Intel runners, then publish the existing three-asset GitHub Release inventory and hand the exact ZIP URL to steipete/homebrew-tap. The handoff waits for its unique run and verifies the formula URL and SHA-256.

Dispatch defaults to a full signing/notarization dry run without tag, release, or tap writes. The shared Swift archetype at @v1 requires an imsg companion dylib and Linux archive and has no dry-run input; this macOS-only CLI therefore uses a self-contained workflow. The stable identifier, archive/checksum names, and Homebrew route stay intact. Local publishers and obsolete tests for those publishers are removed; read-only historical verification remains. No npm or Sparkle channel is introduced. Real Reminders/TCC and natural-download Gatekeeper checks remain documented product tests; CI smoke does not claim to exercise them.

Validation: actionlint, shellcheck, 12 source-gate regression cases, the retained release signature/artifact harness, docs build, and independent review through P2 with no actionable findings. Signing/notarization and tap credentials are provisioned as repository secrets. The merged-main dry run will prove the real hosted build/sign/verify path. No release or version tag is created by this migration.

@clawsweeper

clawsweeper Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T22:33:16.558167Z a2da80a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@clawsweeper clawsweeper Bot added clawsweeper:intake:feature Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed clawsweeper:intake:feature Preliminary submission classification; full source review pending. labels Oct 16, 2026
@clawsweeper

clawsweeper Bot commented Oct 16, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge.

What this changes

This PR moves macOS release signing, notarization, verification, publication, and Homebrew updates into GitHub Actions.

Example: Publish a future v0.3.9 release.

  • Before: A maintainer runs local signing and publication helpers.
  • After: A signed version-tag push starts the Actions release pipeline.

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The product direction is supported, but publication authority has two concrete defects and lacks final-effect proof; the new persisted inventory schema also lacks upgrade compatibility evidence.
Proof confidence 🧂 unranked krab (1/6) Real behavior proof is necessary before merge. See Before merge.
Patch quality 🧂 unranked krab (1/6) Security review found an item that needs attention.

Product

Kind: Feature · Worth it: Yes · Fix scope: Complete
User problem: Official releases require local signing and publication work before users receive updated GitHub and Homebrew artifacts.
Reason: The release owner explicitly proposes centralizing publication, preserves the macOS distribution contract, and removes the replaced publishers.

Merge readiness

⛔ Blocked before merge - 6 items remain

Keep open: this maintainer-authored migration adds distinct functionality, but two publication trust defects block merging.

Priority: P2
Reviewed head: a2da80a29e7891b3ec1b4ebf54cee51b56404eda

Before merge

  • Add real behavior proof - Authority-chain proof required: demonstrate an authorized hosted signing run, rejection of a repository writer's unapproved branch or tag before credential access, and rejection of replaced release bytes before the tap commit, including delayed or retried handoffs; the supplied body defers hosted verification until after merge and contains no such final-effect evidence. Redacted logs or terminal captures are suitable; remove keys, IPs, and non-public endpoints. After adding proof, update the PR body, then request a new review with @clawsweeper re-review. The PR author or someone with repository write access can request it.
  • Protect signing secrets outside the triggering workflow revision (P1) - A repository writer without the approved tag-signing key can push a branch containing a modified release workflow and dispatch it, or push an unprotected v-prefixed tag pointing at such a revision. Repository secrets are available to that workflow, so it can remove validation and read MACOS_SIGNING_P12, its password, and ASC_PRIVATE_KEY_P8 directly. The current repository has no release environment or tag ruleset providing an independent gate. Move these credentials behind an externally enforced authorization boundary; checks in release-source.cjs and verify-tag cannot protect secrets from the workflow revision that runs those checks.
  • Enforce the expected checksum before the tap commits (P1) - If remindctl-macos.zip is replaced after publication verification but before the tap downloads it, this dispatch passes only the URL. The current tap updater hashes those replacement bytes and commits that checksum to Formula/remindctl.rb. The comparison here runs only after the remote workflow succeeds, so it reports failure after Homebrew already points at unverified bytes. Pass and enforce the retained expected digest before the remote formula commit, or use an update path that writes only that verified digest.
  • Resolve security concern: Repository-secret signing authority - The new credentials grant Apple signing and notarization authority beyond the existing read-only build surface.
  • Resolve security concern: Post-write distribution validation - Detecting checksum drift after a successful tap push leaves a window in which consumers can install bytes outside the retained verification record.
  • Add data-model compatibility proof - The review found that existing stored data may not work after upgrade. Show that existing data still loads and works with this change.

Findings

  • [P1] Protect signing secrets outside the triggering workflow revision — .github/workflows/release.yml:106-110
  • [P1] Enforce the expected checksum before the tap commits — .github/workflows/release.yml:288-289
  • [high] Repository-secret signing authority — .github/workflows/release.yml:108
  • [high] Post-write distribution validation — .github/workflows/release.yml:298

Tests

  • Missing end-to-end proof: Supply allowed and forbidden-ref credential-access evidence plus a changed-asset tap scenario proving rejection before its commit; the added tests exercise only the dry-run source helper.
Agent review details

How this fits together

The release pipeline consumes repository source and signing credentials, produces a notarized universal macOS archive, and publishes GitHub assets and a Homebrew formula update.

flowchart LR
  A[Version tag or dry run] --> B[Validate source and CI]
  B --> C[Build universal binary]
  C --> D[Sign and notarize]
  D --> E[Verify on native runners]
  E --> F[Publish GitHub release]
  F --> G[Update Homebrew formula]
Loading

Technical review

Best possible solution:

Enforce release authorization outside mutable workflow code and bind the verified digest before the tap writes its formula.

Do we have a high-confidence way to reproduce the issue?

The workflow and remote updater establish both failing paths by inspection; no target code or release mutation was executed.

Is this the best way to solve the issue?

A single Actions publisher is consistent with the recorded owner direction, but its authorization checks must protect the final credential and distribution effects.

Full review comments:

  • [P1] Protect signing secrets outside the triggering workflow revision — .github/workflows/release.yml:106-110
    A repository writer without the approved tag-signing key can push a branch containing a modified release workflow and dispatch it, or push an unprotected v-prefixed tag pointing at such a revision. Repository secrets are available to that workflow, so it can remove validation and read MACOS_SIGNING_P12, its password, and ASC_PRIVATE_KEY_P8 directly. The current repository has no release environment or tag ruleset providing an independent gate. Move these credentials behind an externally enforced authorization boundary; checks in release-source.cjs and verify-tag cannot protect secrets from the workflow revision that runs those checks.
    Confidence: 0.97
  • [P1] Enforce the expected checksum before the tap commits — .github/workflows/release.yml:288-289
    If remindctl-macos.zip is replaced after publication verification but before the tap downloads it, this dispatch passes only the URL. The current tap updater hashes those replacement bytes and commits that checksum to Formula/remindctl.rb. The comparison here runs only after the remote workflow succeeds, so it reports failure after Homebrew already points at unverified bytes. Pass and enforce the retained expected digest before the remote formula commit, or use an update path that writes only that verified digest.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.97

AGENTS.md: not found in the target repository.

Codex review notes: model internal, reasoning medium; reviewed against a671703439ab.

Provenance checked

Testing

Proof path: unit tests only.

Security

Needs attention: Signing credential access and the Homebrew write need authorization enforced before their final effects.

Evidence

Security concerns:

  • [high] Repository-secret signing authority — .github/workflows/release.yml:108
    The new credentials grant Apple signing and notarization authority beyond the existing read-only build surface.
    Confidence: 0.97
  • [high] Post-write distribution validation — .github/workflows/release.yml:298
    Detecting checksum drift after a successful tap push leaves a window in which consumers can install bytes outside the retained verification record.
    Confidence: 0.99

What I checked:

  • Pinned change and current main: The introduced diff replaces local publication helpers with an Actions pipeline; the default branch remains a671703. (.github/workflows/release.yml:1, a2da80a29e78)
  • Credential boundary: The workflow consumes repository signing secrets without a protected environment. Repository rulesets protect only the default branch against deletion and non-fast-forward updates; the environment inventory contains only github-pages. (.github/workflows/release.yml:108, a2da80a29e78)
  • Source gates execute from the triggering revision: Validation loads release-source.cjs from the initial checkout, while tag-signature verification follows that script; neither establishes an external boundary for access to repository secrets. (.github/workflows/release.yml:45, a2da80a29e78)
  • Checksum checked after remote mutation: The dispatch sends the artifact URL without its expected digest and compares formula contents only after the remote workflow has completed. (.github/workflows/release.yml:288, a2da80a29e78)
  • Verified dependency contract: The PR directly dispatches this repository's update-formula.yml. Its updater computes the downloaded artifact's checksum and the workflow commits and pushes the resulting formula before returning success; its legacy artifact_url route accepts no expected digest. Root and .github AGENTS.md requests returned 404. (.github/workflows/update-formula.yml:145, 410ff7a7645b)
  • Existing release assets remain mutable: The latest release reports immutable:false, so the current distribution setup does not establish an immutable download boundary. (ea2fb1098a2d)

Likely related people:

  • Peter Steinberger: Raw commit ac09a1c adds scripts/check-release.sh:47 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: ac09a1ca0c61; files: scripts/check-release.sh)
  • Andy Ye: Raw commit e5b5f75 adds scripts/build-macos-universal.sh:28 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: medium; commits: e5b5f7524875; files: scripts/build-macos-universal.sh)

Review metrics

Metric Value Why it matters
Net scope +472 / -1132 lines across 15 files The migration removes the replaced local publishers rather than retaining competing publication paths.

Labels

Label changes:

  • add P2: This is a release infrastructure improvement with blocking review defects, without evidence of an active shipped outage.
  • add merge-risk: 🚨 security-boundary: The change exposes signing authority to repository workflows and delegates a distribution write without enforcing the verified digest before that write.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Label justifications:

  • P2: This is a release infrastructure improvement with blocking review defects, without evidence of an active shipped outage.
  • merge-risk: 🚨 security-boundary: The change exposes signing authority to repository workflows and delegates a distribution write without enforcing the verified digest before that write.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed October 16, 2026, 6:44 PM ET / 22:44 UTC.

@steipete
steipete merged commit ca35c84 into main Oct 16, 2026
11 checks passed
@steipete
steipete deleted the ci-release-publisher branch October 16, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant