Skip to content

Build universal binary in stable release workflow - #2287

Merged
austinywang merged 1 commit into
mainfrom
release-universal-binary
Mar 28, 2026
Merged

austinywang merged 1 commit into
mainfrom
release-universal-binary

Conversation

@austinywang

@austinywang austinywang commented Mar 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Updates the stable release workflow to build universal (arm64 + x86_64) binaries, matching the nightly workflow
  • Adds -destination 'generic/platform=macOS', ARCHS="arm64 x86_64", and ONLY_ACTIVE_ARCH=NO to the xcodebuild command
  • Adds a post-build architecture verification step that checks the app binary, CLI binary, and Ghostty helper are all universal
  • Updates all derived data paths from build/ to build-universal/ for consistency

Test plan

  • Trigger a test run of the release workflow via workflow_dispatch on a test tag
  • Verify the "Verify binary architectures" step passes (confirms arm64 + x86_64 in all binaries)
  • Verify codesigning and notarization succeed with the universal binary

🤖 Generated with Claude Code


Summary by cubic

Update the stable release workflow to build a universal macOS app (arm64 + x86_64), matching nightly builds, and verify architectures before signing and notarization.

  • New Features
    • Build universal binaries via xcodebuild with -destination 'generic/platform=macOS', ARCHS="arm64 x86_64", and ONLY_ACTIVE_ARCH=NO.
    • Add post-build checks using lipo -archs for the app binary, CLI, and Ghostty helper.
    • Switch derived data paths from build/ to build-universal/ and update downstream steps (Sparkle plist, CLI test, helper check, codesign/notarize, sentry-cli upload).

Written for commit 1a3cbcf. Summary will update on new commits.

Summary by CodeRabbit

  • Chores
    • Updated release build process to generate universal binaries supporting both Apple Silicon and Intel architectures.
    • Added verification step to ensure released binaries contain both required architecture variants.

Match the nightly workflow's universal build approach so stable releases
support both Apple Silicon and Intel Macs. Adds -destination, ARCHS,
ONLY_ACTIVE_ARCH=NO flags and a post-build architecture verification step.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Mar 28, 2026 10:05am

@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.

No issues found across 1 file

@greptile-apps

greptile-apps Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR updates the stable release workflow to build universal (arm64 + x86_64) binaries by adding the same xcodebuild flags already used in the nightly workflow, introduces a post-build architecture verification step, and mechanically renames all build/ derived-data paths to build-universal/.

  • Build flags added: -destination 'generic/platform=macOS', ARCHS=\"arm64 x86_64\", and ONLY_ACTIVE_ARCH=NO are appended to the xcodebuild invocation, matching nightly.yml exactly (line 205–210 there).
  • New verification step: Calls lipo -archs on the main app binary, the CLI binary, and the Ghostty helper, asserting each contains both arm64 and x86_64 slices.
  • Path updates: All six references to build/Build/Products/Release/ are updated to build-universal/Build/Products/Release/ for consistency.
  • No functional gaps vs. nightly: The only intentional difference retained is the absence of ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly, which is correct for a stable release build.
  • One style note: The bare [[ ... ]] assertions in the verification step fail silently when an architecture check does not pass; adding || { echo \"...\"; exit 1; } error messages would make CI failures immediately actionable (the same pattern exists in nightly.yml).

Confidence Score: 5/5

Safe to merge — changes are a mechanical port of the nightly universal-build approach, with no logic divergence and a new verification gate.

All remaining findings are P2 style suggestions (improving error messages in assertion failures). There are no logic errors, missing path updates, or regressions relative to the nightly workflow. The PR correctly and completely aligns the stable release with the universal-binary pattern.

No files require special attention; .github/workflows/release.yml is the only changed file and the diff is straightforward.

Important Files Changed

Filename Overview
.github/workflows/release.yml Adds universal binary flags (-destination, ARCHS, ONLY_ACTIVE_ARCH=NO) to the stable release xcodebuild command, a new architecture-verification step, and updates all build/ derived-data paths to build-universal/ — mirroring the nightly workflow exactly.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([push tag / workflow_dispatch]) --> B[Guard immutable release assets]
    B -- skip_all=true --> Z([End])
    B -- skip_all=false --> C[Select Xcode + Install deps]
    C --> D[Download pre-built GhosttyKit.xcframework]
    D --> E["xcodebuild universal build\n-destination generic/platform=macOS\nARCHS=arm64 x86_64\nONLY_ACTIVE_ARCH=NO"]
    E --> F["✅ NEW: Verify binary architectures\nlipo -archs app / CLI / ghostty\nassert arm64 + x86_64"]
    F --> G[Build remote daemon assets + inject manifest]
    G --> H[Run CLI version memory guard]
    H --> I[Verify bundled Ghostty helper]
    I --> J[Inject Sparkle keys into Info.plist]
    J --> K[Import signing cert]
    K --> L[Codesign app]
    L --> M[Notarize app + create DMG]
    M --> N[Upload dSYMs to Sentry]
    N --> O[Generate Sparkle appcast]
    O --> P[Attest + Upload release assets]
    P --> Q[Cleanup keychain]
Loading

Reviews (1): Last reviewed commit: "Build universal binary (arm64 + x86_64) ..." | Re-trigger Greptile

Comment on lines +175 to +177
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]
[[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]]

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.

P2 Silent assertion failures are hard to diagnose

The bare [[ ... ]] assertions will cause the step to exit with code 1 when a check fails, but they print no message to stderr explaining which binary failed and what architectures were actually found. A reader would have to scroll up through the echo output to correlate. Adding an explicit failure message makes CI logs immediately actionable:

Suggested change
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]
[[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]]
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]] || { echo "ERROR: App binary is not universal (got: $APP_ARCHS)" >&2; exit 1; }
[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]] || { echo "ERROR: CLI binary is not universal (got: $CLI_ARCHS)" >&2; exit 1; }
[[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]] || { echo "ERROR: Ghostty helper is not universal (got: $HELPER_ARCHS)" >&2; exit 1; }

Note: nightly.yml uses the same bare-assertion pattern (lines 225–227), so the same suggestion applies there for consistency.

@coderabbitai

coderabbitai Bot commented Mar 28, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The release workflow build process was updated to produce universal macOS binaries spanning both arm64 and x86_64 architectures. The derived data path was changed to build-universal/, build flags for architecture specification were added, a verification step using lipo was inserted, and all downstream artifact references were updated to the new paths.

Changes

Cohort / File(s) Summary
Release Workflow Universal Binary Build
.github/workflows/release.yml
Updated xcodebuild to produce universal binaries by setting ARCHS="arm64 x86_64" and ONLY_ACTIVE_ARCH=NO, changed output path to build-universal/, added lipo-based verification step to confirm both architectures in binaries, and updated all downstream steps (daemon manifest injection, CLI test, Sparkle injection, code signing, notarization, dSYM upload, packaging) to reference build-universal/ paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #1332 — Applies the same universal binary build changes (ARCHS, ONLY_ACTIVE_ARCH, lipo verification, build-universal paths) to the nightly.yml workflow.
  • #1067 — Introduces universal macOS build support with architecture verification, establishing the foundational approach this PR applies to the release workflow.
  • #2283 — Updates the Zig-based ghostty helper build script to coordinate with universal binary compilation strategies.

Poem

🐰 A rabbit hops through universal lands,
Where arm64 and x86_64 join hands,
With lipo verification, both slice together,
One build for all—light as a feather! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: building universal binaries in the stable release workflow, which is the primary focus of the PR.
Description check ✅ Passed The description includes a comprehensive summary of changes and a detailed test plan, but the Testing section is incomplete and lacks manual verification details, and the checklist items are not checked.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release-universal-binary

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 and usage tips.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
.github/workflows/release.yml (2)

162-178: Consider adding explicit failure messages for each binary check.

The verification logic is correct, but if a check fails, the workflow will exit without indicating which binary failed. Adding explicit error messages would improve debuggability.

🔧 Proposed improvement for better error output
          echo "App binary architectures: $APP_ARCHS"
          echo "CLI binary architectures: $CLI_ARCHS"
          echo "Ghostty helper architectures: $HELPER_ARCHS"
-          [[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
-          [[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]
-          [[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]]
+          [[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]] || { echo "App binary is not universal: $APP_ARCHS" >&2; exit 1; }
+          [[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]] || { echo "CLI binary is not universal: $CLI_ARCHS" >&2; exit 1; }
+          [[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]] || { echo "Ghostty helper is not universal: $HELPER_ARCHS" >&2; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 162 - 178, The three architecture
checks currently use bare conditional expressions so if one fails the job exits
with no context; update the run block that defines APP_BINARY, CLI_BINARY,
HELPER_BINARY and their ARCHS (APP_ARCHS, CLI_ARCHS, HELPER_ARCHS) to test each
architecture check explicitly and emit a clear error and non-zero exit when it
fails (e.g., if [[ "$APP_ARCHS" != *arm64* || "$APP_ARCHS" != *x86_64* ]]; then
echo "ERROR: App binary $APP_BINARY missing expected architectures: $APP_ARCHS"
>&2; exit 1; fi) and do the same for CLI_ARCHS and HELPER_ARCHS so failures
identify which binary and what architectures were found.

155-155: Consider aligning build-sign-upload.sh path with workflow's build-universal/.

The scripts/build-sign-upload.sh script hardcodes build/ as the derived data path (lines 51, 73, 214), while this workflow uses build-universal/. Although the workflow doesn't invoke the script, aligning paths would prevent confusion if the script is run independently.

Consider updating the script to use build-universal/ or parameterizing the path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml at line 155, The script
scripts/build-sign-upload.sh hardcodes derived data path as build/ (occurrences
around the file's references at the current hardcoded instances) which conflicts
with the workflow using build-universal/; update the script to either (A) change
those hardcoded paths to build-universal/ or (B) add a configurable
variable/parameter (e.g., DERIVED_DATA_PATH) at the top of
scripts/build-sign-upload.sh and replace the hardcoded occurrences (the three
hardcoded locations noted) with that variable so callers (including the
workflow) can pass build-universal/ when needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 162-178: The three architecture checks currently use bare
conditional expressions so if one fails the job exits with no context; update
the run block that defines APP_BINARY, CLI_BINARY, HELPER_BINARY and their ARCHS
(APP_ARCHS, CLI_ARCHS, HELPER_ARCHS) to test each architecture check explicitly
and emit a clear error and non-zero exit when it fails (e.g., if [[ "$APP_ARCHS"
!= *arm64* || "$APP_ARCHS" != *x86_64* ]]; then echo "ERROR: App binary
$APP_BINARY missing expected architectures: $APP_ARCHS" >&2; exit 1; fi) and do
the same for CLI_ARCHS and HELPER_ARCHS so failures identify which binary and
what architectures were found.
- Line 155: The script scripts/build-sign-upload.sh hardcodes derived data path
as build/ (occurrences around the file's references at the current hardcoded
instances) which conflicts with the workflow using build-universal/; update the
script to either (A) change those hardcoded paths to build-universal/ or (B) add
a configurable variable/parameter (e.g., DERIVED_DATA_PATH) at the top of
scripts/build-sign-upload.sh and replace the hardcoded occurrences (the three
hardcoded locations noted) with that variable so callers (including the
workflow) can pass build-universal/ when needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b3136908-cb89-41b8-9ff2-3ebe4272dbf5

📥 Commits

Reviewing files that changed from the base of the PR and between f0c3ccc and 1a3cbcf.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

@austinywang
austinywang merged commit f049195 into main Mar 28, 2026
19 checks passed
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…anaflow-ai#2287)

Match the nightly workflow's universal build approach so stable releases
support both Apple Silicon and Intel Macs. Adds -destination, ARCHS,
ONLY_ACTIVE_ARCH=NO flags and a post-build architecture verification step.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 1a3cbcf8 Deployed Mar 28, 2026 by vercel[bot]
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