Skip to content

ci(macos): add fail-closed release candidate gate - #801

Merged
meiiie merged 3 commits into
mainfrom
ci/macos-release-candidate-validation
Aug 8, 2026
Merged

ci(macos): add fail-closed release candidate gate#801
meiiie merged 3 commits into
mainfrom
ci/macos-release-candidate-validation

Conversation

@meiiie

@meiiie meiiie commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds a manual, candidate-only macOS distribution gate for x64 and arm64. It accepts only the canonical repository's exact current main SHA, builds before exposing Apple credentials, forces Developer ID signing and notarization, and always packages with --publish never.

The shared verifier fails closed on publisher identity, Team ID, secure timestamp, hardened runtime, an exact root-entitlement allowlist, nested Mach-O signatures/architecture, stapled tickets, Gatekeeper, syspolicy, DMG integrity/read-only mounting, and apps extracted from both DMG and ZIP. Candidate artifacts expire after three days.

Motivation

The existing release job relies mainly on packaging success and historical credential evidence. That is not enough to prove that current artifacts are signed, notarized, stapled, and accepted by macOS without risking a public release. This PR creates a reversible validation layer; it does not change the release workflow, create tags/releases, publish updater metadata, or claim physical macOS runtime readiness.

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other: release engineering / CI validation

Related Issue(s)

No issue closed. Physical ScreenCaptureKit, TCC, microphone, system-audio, and multi-display acceptance remains a separate follow-up on real Macs.

Screenshots / Video

Not applicable; no product UI changes.

Testing Guide

  • npm test — 107 files passed; 1005 tests passed, 1 skipped.
  • npx tsc --noEmit and npm run i18n:check passed.
  • Focused Biome, Node syntax, YAML parsing, and checksum-verified actionlint 1.7.12 passed.
  • Focused distribution-policy tests passed 10/10.
  • Three complete security diff scans found no reportable findings after the entitlement policy was hardened to an exact allowlist.

After review and merge, dispatch the new workflow with the exact merged main SHA. Treat its output as temporary distribution evidence only; a public macOS beta still requires physical Mac runtime acceptance and a separate release decision.

Checklist

  • I have performed a self-review of my code.
  • Screenshots or videos are not applicable.
  • No changelog entry is needed for this non-user-facing candidate gate.

Summary by CodeRabbit

  • New Features

    • Added automated macOS release-candidate builds for Intel and Apple silicon.
    • Added comprehensive verification for signing, entitlements, architectures, notarization, Gatekeeper approval, DMG integrity, and ZIP extraction.
    • Added JSON and optional Markdown verification reports with pass/fail results and timing details.
    • Added an npm command to run macOS distribution verification.
  • Tests

    • Added coverage for macOS signing metadata, entitlements, team IDs, and architecture validation.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds macOS distribution policies, a release artifact verifier, and a manually dispatched workflow that builds, signs, notarizes, tests, and evaluates x64 and arm64 release candidates.

Changes

macOS distribution validation

Layer / File(s) Summary
Distribution policy contracts and tests
scripts/macos-distribution-policy.mjs, electron/macosDistributionPolicy.test.mjs
Defines validation for Apple signing metadata, entitlements, Team IDs, and Mach-O architectures. Tests cover valid and invalid metadata.
Packaged artifact verification
scripts/verify-macos-distribution.mjs, package.json
Adds CLI argument handling, app and archive inspection, signature and notarization checks, report generation, cleanup, and the verify:macos-distribution script.
Authorized release-candidate builds
.github/workflows/macos-release-candidate.yml
Adds source authorization, parallel x64 and arm64 builds, signing, notarization, smoke tests, artifact verification, checksums, uploads, and a final verdict job.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Dispatcher
  participant SourceAuthorization
  participant BuildJobs
  participant MacOSDistributionVerifier
  participant Verdict
  Dispatcher->>SourceAuthorization: dispatch with source SHA
  SourceAuthorization->>SourceAuthorization: validate repository, branch, SHA, and current main commit
  SourceAuthorization->>BuildJobs: provide authorized SHA
  BuildJobs->>BuildJobs: build, sign, and notarize x64 and arm64 candidates
  BuildJobs->>MacOSDistributionVerifier: verify DMG and ZIP artifacts
  MacOSDistributionVerifier-->>BuildJobs: return verification report
  BuildJobs->>Verdict: report architecture results
  Verdict->>Verdict: evaluate authorization and build results
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the macOS release-candidate gate and its fail-closed CI purpose.
Description check ✅ Passed The description covers purpose, motivation, change type, issue context, testing steps, and checklist items.
✨ 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 ci/macos-release-candidate-validation

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.

@meiiie
meiiie marked this pull request as ready for review August 8, 2026 19:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
scripts/verify-macos-distribution.mjs (1)

283-289: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Batch the file invocations to reduce subprocess count.

The loop spawns one file process per regular file. An Electron bundle contains thousands of files, so this step dominates the verification runtime. file accepts multiple paths in a single call and prints one line per path when -b is omitted, so you can classify files in batches.

♻️ Sketch of a batched classification
 		const machOBinaries = [];
-		for (const filePath of walkRegularFiles(appPath)) {
-			const fileType = runProcess("file", ["-b", filePath]).stdout;
-			if (fileType.includes("Mach-O")) {
-				machOBinaries.push(filePath);
-			}
-		}
+		const allFiles = walkRegularFiles(appPath);
+		const batchSize = 200;
+		for (let index = 0; index < allFiles.length; index += batchSize) {
+			const batch = allFiles.slice(index, index + batchSize);
+			const lines = runProcess("file", ["-h", ...batch]).stdout.split(/\r?\n/);
+			for (const line of lines) {
+				if (!line.includes("Mach-O")) {
+					continue;
+				}
+				const separator = line.indexOf(": ");
+				if (separator > 0) {
+					machOBinaries.push(line.slice(0, separator));
+				}
+			}
+		}
🤖 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 `@scripts/verify-macos-distribution.mjs` around lines 283 - 289, Update the
Mach-O discovery loop around walkRegularFiles and runProcess to classify regular
files in batches rather than spawning one file process per path. Pass multiple
file paths to each invocation, omit the -b option so outputs remain associated
one line per input path, and add only paths whose corresponding output
identifies Mach-O to machOBinaries.
🤖 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/macos-release-candidate.yml:
- Around line 161-166: Update the PKCS#12 extraction command in the macOS
release workflow to retry with openssl pkcs12 -legacy when the initial
extraction fails due to legacy RC2/3DES encryption. Preserve the existing
non-legacy attempt first and reuse the same certificate input, password, and
output paths for the fallback.

In `@scripts/macos-distribution-policy.mjs`:
- Around line 76-84: Update the entitlement validation branch for
com.apple.security.get-task-allow so both true and false values are recognized
as expected keys: reject only when the value is true, and continue without
adding an unexpected-entitlement error when it is false.

In `@scripts/verify-macos-distribution.mjs`:
- Around line 455-457: Update the catch block around writeReport so
report-writing failures cannot replace the original verification error: wrap the
writeReport call in its own try/catch, preserve the existing report paths, and
rethrow the original error after handling any reporting failure.
- Around line 411-422: Update the “DMG mounts read-only” check to attach the
image without forcing the -readonly option, then inspect the resulting mount
flags and fail unless the mounted volume is actually read-only; alternatively,
rename the check to accurately describe forced read-only attachment if that
behavior is intended.

---

Nitpick comments:
In `@scripts/verify-macos-distribution.mjs`:
- Around line 283-289: Update the Mach-O discovery loop around walkRegularFiles
and runProcess to classify regular files in batches rather than spawning one
file process per path. Pass multiple file paths to each invocation, omit the -b
option so outputs remain associated one line per input path, and add only paths
whose corresponding output identifies Mach-O to machOBinaries.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 56c55772-f393-4131-b6e2-e68bebe0bc66

📥 Commits

Reviewing files that changed from the base of the PR and between 54ae801 and b9683c8.

📒 Files selected for processing (5)
  • .github/workflows/macos-release-candidate.yml
  • electron/macosDistributionPolicy.test.mjs
  • package.json
  • scripts/macos-distribution-policy.mjs
  • scripts/verify-macos-distribution.mjs

Comment thread .github/workflows/macos-release-candidate.yml Outdated
Comment thread scripts/macos-distribution-policy.mjs
Comment thread scripts/verify-macos-distribution.mjs Outdated
Comment thread scripts/verify-macos-distribution.mjs
@meiiie
meiiie merged commit c80ae87 into main Aug 8, 2026
5 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