Skip to content

Make stable release build universal - #1133

Closed
lawrencecchen wants to merge 5 commits into
mainfrom
task-universal-stable-release
Closed

lawrencecchen wants to merge 5 commits into
mainfrom
task-universal-stable-release

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Mar 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • force the stable macOS release build to emit a universal app and CLI with explicit ARCHS="arm64 x86_64"
  • verify the built release artifact with lipo and keep a dry-run workflow path that uploads verification artifacts instead of publishing a release
  • keep the local scripts/build-sign-upload.sh helper aligned with the release workflow

Testing

  • bash -n scripts/build-sign-upload.sh
  • ruby -e 'require "yaml"; YAML.load_file(".github/workflows/release.yml")'
  • git diff --check
  • gh workflow run "Release macOS app" --repo manaflow-ai/cmux --ref task-universal-stable-release

Issues

  • Related: stable release artifacts should be universal while preserving a non-publishing verification path

Summary by cubic

Make the stable macOS release universal for Apple Silicon and Intel, and keep a dry-run verification path. Enforce styled, signed DMGs via a new wrapper that prefers the Homebrew create-dmg (legacy layout) and only falls back to modern create-dmg when styling isn't required, fixing the Applications drop target across environments.

  • New Features
    • Build universal app and CLI by forcing ARCHS="arm64 x86_64", ONLY_ACTIVE_ARCH=NO, and -destination 'generic/platform=macOS'.
    • Verify architectures: ensure GhosttyKit is arm64+x86_64, check app and CLI with lipo, and write release-verification.txt.
    • Auto-detect publish vs verify: tag pushes publish and upload assets; other refs upload verification artifacts (cmux-macos.dmg, appcast.xml, release-verification.txt) with dynamic release_tag and artifact name.
    • Align scripts/build-sign-upload.sh with CI: same build flags and checks (including GhosttyKit verification).
    • Standardize DMG packaging via scripts/create_release_dmg.sh: require styled DMGs for signed release/nightly using the Homebrew create-dmg; fall back to modern create-dmg (PATH or npx) when styling isn’t required; workflows provision Homebrew and avoid npm; tests ensure correct tool selection and layout.

Written for commit 9243aa9. Summary will update on new commits.

Summary by CodeRabbit

  • Chores

    • Improved release process with automated multi-architecture verification (arm64/x86_64) and a release-verification artifact.
    • Updated build to produce universal macOS binaries and avoid active-architecture pruning.
    • Reworked notarization to use a dedicated DMG creation step and prevent duplicate artifacts.
    • Added preflight checks for required DMG tooling.
  • New Features

    • Split publish vs verification flows to conditionally upload release or verification assets.
    • Appcast generation now uses the computed release tag.
  • Tests

    • Added tests covering DMG creation paths (modern/legacy and installer fallbacks).

@vercel

vercel Bot commented Mar 10, 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 10, 2026 2:09am

@coderabbitai

coderabbitai Bot commented Mar 10, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a release-mode decision and separate verification flow to CI; builds macOS universal (arm64 + x86_64) artifacts, verifies architectures for GhosttyKit/app/CLI, delegates DMG creation to a new script, emits release-verification metadata, and conditionally uploads publish vs verification artifacts.

Changes

Cohort / File(s) Summary
Release workflow
.github/workflows/release.yml
Adds "Determine release mode" outputs (publish_release, release_tag, artifact_name); gates verification and upload steps; adds architecture verification and release-verification.txt; ensures release_tag used for Sparkle/appcast and separates publish vs verification upload paths.
Nightly workflow
.github/workflows/nightly.yml
Replaces inline DMG creation with ./scripts/create_release_dmg.sh invocation; removes manual create-dmg/temp-file handling; relies on new script-produced DMG for notarization.
Build & upload script
scripts/build-sign-upload.sh
Adds preflight check for create-dmg or npx; enforces macOS build with ARCHS="arm64 x86_64" and ONLY_ACTIVE_ARCH=NO; performs lipo-based architecture checks for GhosttyKit, app, and CLI; switches DMG creation to create_release_dmg.sh; updates notarization/stapling and splits uploads for publish vs verification.
DMG creation script + tests
scripts/create_release_dmg.sh, tests/test_create_release_dmg.sh, tests/test_ci_create_dmg_pinned.sh
Introduces robust DMG creator supporting "modern" and "legacy" create-dmg, optional codesign, npx fallback, env flags to require modern/styled behavior; adds tests mocking create-dmg/npx paths and updates CI check to prefer Homebrew provisioning. Review focus: script heuristics, npx vs brew fallback, and test mock fidelity.

Sequence Diagram

sequenceDiagram
    participant Trigger
    participant GHA as GitHub Actions
    participant Builder as build-sign-upload.sh
    participant Verifier as arch/verifier (lipo)
    participant DMG as create_release_dmg.sh
    participant Storage as Artifact Storage

    Trigger->>GHA: push (tag or commit)
    GHA->>GHA: Determine release mode (publish_release, release_tag, artifact_name)
    GHA->>Builder: Run macOS build (ARCHS=arm64 x86_64)
    Builder->>Verifier: Probe binaries for architectures
    Verifier-->>GHA: Return architectures + write release-verification.txt
    Builder->>DMG: Create DMG (modern/legacy, may call npx or brew)
    DMG-->>Builder: DMG path (or error)
    alt publish_release == true
        GHA->>Storage: Upload release assets (dmg, appcast, zips)
    else
        GHA->>Storage: Upload verification artifacts (cmux-macos.dmg, appcast.xml, release-verification.txt)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰
I hopped through builds at break of dawn,
arm and x86 both carry on,
I rolled a DMG with careful cheer,
Signed and checked — the carrots clear,
Small paws, big release — hip hip hooray!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective: making stable release builds produce universal binaries for both Apple Silicon and Intel architectures.
Description check ✅ Passed The description covers required sections (Summary, Testing) with clear details, though the Demo Video section is not applicable and the Checklist is not completed.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch task-universal-stable-release

Comment @coderabbitai help to get the list of available commands and usage tips.

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

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/build-sign-upload.sh">

<violation number="1" location="scripts/build-sign-upload.sh:82">
P2: The CLI binary path is used unconditionally here, but the codesign step later guards with `if [ -f "$CLI_PATH" ]`, implying it may be absent. Either guard this block similarly, or remove the guard from the codesign step if the CLI is now always expected in release builds.</violation>

<violation number="2" location="scripts/build-sign-upload.sh:87">
P2: These bare `[[ ]]` assertions exit silently under `set -e` with no error message. Add an `|| { echo ...; exit 1; }` clause so operators can immediately see which binary is missing an architecture.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

echo "Build succeeded"

APP_BINARY="$APP_PATH/Contents/MacOS/cmux"
CLI_BINARY="$APP_PATH/Contents/Resources/bin/cmux"

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The CLI binary path is used unconditionally here, but the codesign step later guards with if [ -f "$CLI_PATH" ], implying it may be absent. Either guard this block similarly, or remove the guard from the codesign step if the CLI is now always expected in release builds.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-sign-upload.sh, line 82:

<comment>The CLI binary path is used unconditionally here, but the codesign step later guards with `if [ -f "$CLI_PATH" ]`, implying it may be absent. Either guard this block similarly, or remove the guard from the codesign step if the CLI is now always expected in release builds.</comment>

<file context>
@@ -71,9 +71,22 @@ fi
 echo "Build succeeded"
 
+APP_BINARY="$APP_PATH/Contents/MacOS/cmux"
+CLI_BINARY="$APP_PATH/Contents/Resources/bin/cmux"
+APP_ARCHS="$(lipo -archs "$APP_BINARY")"
+CLI_ARCHS="$(lipo -archs "$CLI_BINARY")"
</file context>
Fix with Cubic

Comment on lines +87 to +88
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: These bare [[ ]] assertions exit silently under set -e with no error message. Add an || { echo ...; exit 1; } clause so operators can immediately see which binary is missing an architecture.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-sign-upload.sh, line 87:

<comment>These bare `[[ ]]` assertions exit silently under `set -e` with no error message. Add an `|| { echo ...; exit 1; }` clause so operators can immediately see which binary is missing an architecture.</comment>

<file context>
@@ -71,9 +71,22 @@ fi
+CLI_ARCHS="$(lipo -archs "$CLI_BINARY")"
+echo "App binary architectures: $APP_ARCHS"
+echo "CLI binary architectures: $CLI_ARCHS"
+[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
+[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]
+
</file context>
Suggested change
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_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; }
Fix with Cubic

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/build-sign-upload.sh`:
- Around line 83-84: The script later calls lipo to compute APP_ARCHS and
CLI_ARCHS but the preflight tool check does not include lipo, so add lipo to the
dependency/tools list used by the preflight check (the same place where other
required tools are validated—e.g., the tools array or
check_tools/check_dependencies function) so the script verifies lipo is present
before the build proceeds; this ensures the lipo binary is validated alongside
the other tools and will fail fast if missing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c60bc82-b44f-46b2-989a-c4f032e2b203

📥 Commits

Reviewing files that changed from the base of the PR and between 18bb11d and 498480b.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/build-sign-upload.sh

Comment on lines +83 to +84
APP_ARCHS="$(lipo -archs "$APP_BINARY")"
CLI_ARCHS="$(lipo -archs "$CLI_BINARY")"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the relevant sections of the build script
cat -n scripts/build-sign-upload.sh | head -100

Repository: manaflow-ai/cmux

Length of output: 3844


🏁 Script executed:

# Also check if lipo is mentioned elsewhere in the file
rg -n "lipo" scripts/build-sign-upload.sh

Repository: manaflow-ai/cmux

Length of output: 147


🏁 Script executed:

# Find the tool gate check section
rg -n "for tool in" scripts/build-sign-upload.sh -A 5

Repository: manaflow-ai/cmux

Length of output: 294


Add lipo to the pre-flight dependency check.

Lines 83-84 use lipo -archs as a hard requirement, but the tool gate check (line 56) doesn't include it. This means a missing lipo will only be caught after the build finishes, not at preflight.

🔧 Suggested fix
-for tool in zig xcodebuild create-dmg xcrun codesign ditto gh; do
+for tool in zig xcodebuild create-dmg xcrun codesign ditto gh lipo; do
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-sign-upload.sh` around lines 83 - 84, The script later calls
lipo to compute APP_ARCHS and CLI_ARCHS but the preflight tool check does not
include lipo, so add lipo to the dependency/tools list used by the preflight
check (the same place where other required tools are validated—e.g., the tools
array or check_tools/check_dependencies function) so the script verifies lipo is
present before the build proceeds; this ensures the lipo binary is validated
alongside the other tools and will fail fast if missing.

@greptile-apps

greptile-apps Bot commented Mar 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR forces the stable macOS release build to produce a universal binary by passing ARCHS="arm64 x86_64", ONLY_ACTIVE_ARCH=NO, and -destination 'generic/platform=macOS' to xcodebuild, and verifies the result with lipo. It introduces a "release mode" gate: tag-push events publish a real GitHub release while all other triggers (e.g. workflow_dispatch) go through a dry-run path that uploads build artifacts instead of a release. Both the CI workflow and the local scripts/build-sign-upload.sh are kept in sync.

Key changes:

  • Universal binary build flags added to both release.yml and build-sign-upload.sh
  • New Determine release mode step distinguishes publish vs. verify paths
  • New Verify release binary architectures step confirms both app and CLI are fat binaries
  • Guard immutable release assets now only runs in publish mode
  • New Upload verification artifacts step uploads DMG, appcast, and release-verification.txt for non-publish runs

Issues found:

  • Both scripts/build-sign-upload.sh (lines 87–88) and .github/workflows/release.yml (lines 172–173) exit silently on failed arch assertions, providing no diagnostic indicating which binary failed or what architectures were present.
  • The full notarization pipeline (signing, notarizing, DMG creation) runs in verify mode due to how the guard_release_assets outputs gate downstream steps. If "dry-run" implies skipping side effects, consider adding explicit publish_release checks to skip notarization in verify mode.

Confidence Score: 4/5

  • Safe to merge — universal binary flags are correct, publish/verify branching is sound, and no secrets are leaked or release assets accidentally published.
  • The PR's core logic (xcodebuild flags, lipo verification, publish vs. verify mode split) is correct and well-structured. The two remaining findings are relatively minor: silent arch-assertion errors (style/clarity issue that complicates debugging but doesn't break functionality) and full notarization in verify mode (a design decision that might be intentional). No functional bugs or security issues detected.
  • .github/workflows/release.yml (arch assertion diagnostics, notarization gating in verify mode) and scripts/build-sign-upload.sh (arch assertion diagnostics).

Comments Outside Diff (1)

  1. .github/workflows/release.yml, line 238-239 (link)

    Notarization runs in verify mode despite being a dry-run path

    The Determine release mode step correctly gates the Guard immutable release assets step to publish mode only (line 41). However, the downstream Notarize app, Codesign app, and Generate Sparkle appcast steps remain gated only on steps.guard_release_assets.outputs.skip_all != 'true'. In verify mode (e.g. workflow_dispatch), the guard step is skipped and outputs an empty string, so '' != 'true' evaluates to true, causing the full notarization pipeline to execute unconditionally.

    This means:

    1. Every workflow_dispatch verification run consumes ~10–15 minutes and Apple notarization API quota
    2. All Apple secrets (APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID, etc.) are required even for a build-only smoke test

    If the intent is to skip signing/notarizing in verify mode, add an explicit publish_release check:

    (Apply the same pattern to Codesign app on line 220 and Generate Sparkle appcast on line 297 if skipping verification notarization is intended.)

Last reviewed commit: 498480b

Comment on lines +87 to +88
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_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.

Silent arch assertion failures lack diagnostic output

When either binary is missing an architecture, these [[ ... ]] assertions fail with exit code 1 (due to set -euo pipefail on line 2), but produce no error message. Developers only see a generic "exited with code 1", making triage difficult. This same issue exists in .github/workflows/release.yml lines 172–173.

Adding explicit error messages makes failures immediately diagnosable:

Suggested change
[[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]
[[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]
if ! [[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]]; then
echo "ERROR: app binary is not universal (got: $APP_ARCHS)" >&2; exit 1
fi
if ! [[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]]; then
echo "ERROR: CLI binary is not universal (got: $CLI_ARCHS)" >&2; exit 1
fi

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

2 issues found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/build-sign-upload.sh">

<violation number="1" location="scripts/build-sign-upload.sh:72">
P2: `test -f` will silently abort (via `set -e`) with no error message if the binary is missing. Add a diagnostic so the operator knows *why* the build failed.</violation>

<violation number="2" location="scripts/build-sign-upload.sh:75">
P2: The architecture assertion silently exits if GhosttyKit isn't universal. Add an error message so the failure is self-explanatory.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

test -f "$GHOSTTYKIT_BINARY"
GHOSTTYKIT_ARCHS="$(lipo -archs "$GHOSTTYKIT_BINARY")"
echo "GhosttyKit architectures: $GHOSTTYKIT_ARCHS"
[[ "$GHOSTTYKIT_ARCHS" == *arm64* && "$GHOSTTYKIT_ARCHS" == *x86_64* ]]

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The architecture assertion silently exits if GhosttyKit isn't universal. Add an error message so the failure is self-explanatory.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-sign-upload.sh, line 75:

<comment>The architecture assertion silently exits if GhosttyKit isn't universal. Add an error message so the failure is self-explanatory.</comment>

<file context>
@@ -68,6 +68,12 @@ else
+test -f "$GHOSTTYKIT_BINARY"
+GHOSTTYKIT_ARCHS="$(lipo -archs "$GHOSTTYKIT_BINARY")"
+echo "GhosttyKit architectures: $GHOSTTYKIT_ARCHS"
+[[ "$GHOSTTYKIT_ARCHS" == *arm64* && "$GHOSTTYKIT_ARCHS" == *x86_64* ]]
+
 # --- Build app (Release, unsigned) ---
</file context>
Suggested change
[[ "$GHOSTTYKIT_ARCHS" == *arm64* && "$GHOSTTYKIT_ARCHS" == *x86_64* ]]
[[ "$GHOSTTYKIT_ARCHS" == *arm64* && "$GHOSTTYKIT_ARCHS" == *x86_64* ]] || { echo "ERROR: GhosttyKit is not universal (got: $GHOSTTYKIT_ARCHS)" >&2; exit 1; }
Fix with Cubic

fi

GHOSTTYKIT_BINARY="GhosttyKit.xcframework/macos-arm64_x86_64/libghostty.a"
test -f "$GHOSTTYKIT_BINARY"

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: test -f will silently abort (via set -e) with no error message if the binary is missing. Add a diagnostic so the operator knows why the build failed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-sign-upload.sh, line 72:

<comment>`test -f` will silently abort (via `set -e`) with no error message if the binary is missing. Add a diagnostic so the operator knows *why* the build failed.</comment>

<file context>
@@ -68,6 +68,12 @@ else
 fi
 
+GHOSTTYKIT_BINARY="GhosttyKit.xcframework/macos-arm64_x86_64/libghostty.a"
+test -f "$GHOSTTYKIT_BINARY"
+GHOSTTYKIT_ARCHS="$(lipo -archs "$GHOSTTYKIT_BINARY")"
+echo "GhosttyKit architectures: $GHOSTTYKIT_ARCHS"
</file context>
Suggested change
test -f "$GHOSTTYKIT_BINARY"
test -f "$GHOSTTYKIT_BINARY" || { echo "ERROR: universal GhosttyKit binary not found at $GHOSTTYKIT_BINARY" >&2; exit 1; }
Fix with Cubic

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

316-336: ⚠️ Potential issue | 🟠 Major

Dry-run appcasts point at non-existent GitHub release URLs.

The script at line 19 constructs DOWNLOAD_URL_PREFIX as https://github.com/manaflow-ai/cmux/releases/download/$TAG/, interpolating the provided tag directly. The workflow passes release_tag (which is verify-${SHORT_SHA} in verify mode) as that tag parameter. Since verify-mode runs do not create a GitHub Release at that synthetic tag, the uploaded appcast.xml contains enclosure URLs that will fail to resolve, making the verification appcast unusable for end-to-end Sparkle update testing.

Either override DOWNLOAD_URL_PREFIX for verify-mode runs (e.g., point to the artifact download URL or use a mock URL), or skip generating/uploading the appcast when not publishing a release.

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

In @.github/workflows/release.yml around lines 316 - 336, The appcast generation
uses DOWNLOAD_URL_PREFIX (set in ./scripts/sparkle_generate_appcast.sh) with the
provided release_tag (steps.release_mode.outputs.release_tag), which in verify
mode is a synthetic tag that has no GitHub Release; update the workflow to
either (A) set/override DOWNLOAD_URL_PREFIX to a valid URL for verify-mode runs
(for example the actions artifact download URL or a mock base URL) before
invoking sparkles_generate_appcast.sh when
steps.release_mode.outputs.publish_release != 'true', or (B) skip generating and
uploading the appcast/appcast.xml in verify mode entirely by gating the
sparkle_generate_appcast.sh invocation and the "Upload verification artifacts"
step on steps.release_mode.outputs.publish_release == 'true'; modify the
workflow conditions around the sparkles_generate_appcast.sh call and the upload
steps (references: DOWNLOAD_URL_PREFIX in the script,
release_tag/steps.release_mode.outputs.release_tag, sparkles_generate_appcast.sh
invocation, and the "Upload verification artifacts" upload step) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 316-336: The appcast generation uses DOWNLOAD_URL_PREFIX (set in
./scripts/sparkle_generate_appcast.sh) with the provided release_tag
(steps.release_mode.outputs.release_tag), which in verify mode is a synthetic
tag that has no GitHub Release; update the workflow to either (A) set/override
DOWNLOAD_URL_PREFIX to a valid URL for verify-mode runs (for example the actions
artifact download URL or a mock base URL) before invoking
sparkles_generate_appcast.sh when steps.release_mode.outputs.publish_release !=
'true', or (B) skip generating and uploading the appcast/appcast.xml in verify
mode entirely by gating the sparkle_generate_appcast.sh invocation and the
"Upload verification artifacts" step on
steps.release_mode.outputs.publish_release == 'true'; modify the workflow
conditions around the sparkles_generate_appcast.sh call and the upload steps
(references: DOWNLOAD_URL_PREFIX in the script,
release_tag/steps.release_mode.outputs.release_tag, sparkles_generate_appcast.sh
invocation, and the "Upload verification artifacts" upload step) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09c25462-cb00-4180-8894-ed4f56c02408

📥 Commits

Reviewing files that changed from the base of the PR and between 498480b and f474a03.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/build-sign-upload.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/build-sign-upload.sh

@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 (3)
scripts/create_release_dmg.sh (1)

32-53: Version detection fallback is reasonable but relies on help text stability.

The detection logic first attempts to parse --version output, then falls back to --help text. The heuristic for legacy detection (<output_name.dmg> <source_folder>) is fragile if help text changes across versions.

Consider documenting this heuristic in a comment for future maintainers.

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

In `@scripts/create_release_dmg.sh` around lines 32 - 53, Add a clarifying comment
above the detect_create_dmg_mode function explaining the fallback heuristic: we
first try to parse the major version from the tool's --version output and, if
that fails, we detect "legacy" by checking for the specific help text fragment
"<output_name.dmg> <source_folder>"; note that this is a brittle string match
tied to current help text formatting and should be updated if the tool's --help
output changes or extended to a more robust detection method in the future.
Include references to the variables used (version_output, help_output, major)
and the intended outputs ("legacy" vs "modern") so maintainers know what to
preserve when modifying the logic.
.github/workflows/release.yml (1)

26-38: Consider grouping redirects for cleaner shell output (optional).

Static analysis flagged SC2129 suggesting grouped redirects. While functional as-is, grouping can improve readability:

♻️ Optional refactor
          if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF:-}" == refs/tags/* ]]; then
-            echo "publish_release=true" >> "$GITHUB_OUTPUT"
-            echo "release_tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
-            echo "artifact_name=release-${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
+            {
+              echo "publish_release=true"
+              echo "release_tag=${GITHUB_REF_NAME}"
+              echo "artifact_name=release-${GITHUB_REF_NAME}"
+            } >> "$GITHUB_OUTPUT"
          else
            REF_SLUG="$(printf '%s' "${GITHUB_REF_NAME}" | tr '/[:space:]' '-' | tr -cd '[:alnum:]-_.')"
            SHORT_SHA="${GITHUB_SHA::7}"
-            echo "publish_release=false" >> "$GITHUB_OUTPUT"
-            echo "release_tag=verify-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
-            echo "artifact_name=release-verification-${REF_SLUG}-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
+            {
+              echo "publish_release=false"
+              echo "release_tag=verify-${SHORT_SHA}"
+              echo "artifact_name=release-verification-${REF_SLUG}-${SHORT_SHA}"
+            } >> "$GITHUB_OUTPUT"
          fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 26 - 38, The shell script writes
multiple lines to the same file via repeated echo ... >> "$GITHUB_OUTPUT" which
triggers SC2129; group the redirects inside the run block by writing all outputs
to GITHUB_OUTPUT in a single here-doc or grouped redirection so that publishing
and verification branches set publish_release, release_tag, and artifact_name
together without repeated >> ops; update the conditional branches that currently
echo "publish_release=...", "release_tag=...", and "artifact_name=..." to use a
single grouped write to "$GITHUB_OUTPUT" (referencing GITHUB_OUTPUT,
publish_release, release_tag, artifact_name, and the existing branch logic using
GITHUB_EVENT_NAME/GITHUB_REF_NAME/GITHUB_SHA) so static analysis is satisfied
and shell output is cleaner.
tests/test_create_release_dmg.sh (1)

99-171: Test cases cover the critical paths.

The four test cases validate:

  1. Modern binary uses --overwrite and --identity
  2. Legacy with npx falls back to modern via npx
  3. Legacy without npx uses legacy flags (--app-drop-link, --codesign)
  4. Require modern without npx fails appropriately

Consider adding a test case for the --no-code-sign flag when no signing identity is provided, to ensure the modern path handles unsigned builds correctly.

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

In `@tests/test_create_release_dmg.sh` around lines 99 - 171, Add a new test
function (similar to case_modern_binary) that exercises the modern code path
when no signing identity is available and the script should pass the
--no-code-sign flag; call run_script with FAKE_CREATE_DMG_VERSION="8.0.0" and no
SIGNING-ID/identity environment so the code chooses unsigned behavior, then
assert the DMG was produced, grep the create-dmg log for "--no-code-sign" and
assert npx was not invoked (npx log is empty); reference the existing
case_modern_binary and run_script usage to copy setup/teardown and logging
variable names.
🤖 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 26-38: The shell script writes multiple lines to the same file via
repeated echo ... >> "$GITHUB_OUTPUT" which triggers SC2129; group the redirects
inside the run block by writing all outputs to GITHUB_OUTPUT in a single
here-doc or grouped redirection so that publishing and verification branches set
publish_release, release_tag, and artifact_name together without repeated >>
ops; update the conditional branches that currently echo "publish_release=...",
"release_tag=...", and "artifact_name=..." to use a single grouped write to
"$GITHUB_OUTPUT" (referencing GITHUB_OUTPUT, publish_release, release_tag,
artifact_name, and the existing branch logic using
GITHUB_EVENT_NAME/GITHUB_REF_NAME/GITHUB_SHA) so static analysis is satisfied
and shell output is cleaner.

In `@scripts/create_release_dmg.sh`:
- Around line 32-53: Add a clarifying comment above the detect_create_dmg_mode
function explaining the fallback heuristic: we first try to parse the major
version from the tool's --version output and, if that fails, we detect "legacy"
by checking for the specific help text fragment "<output_name.dmg>
<source_folder>"; note that this is a brittle string match tied to current help
text formatting and should be updated if the tool's --help output changes or
extended to a more robust detection method in the future. Include references to
the variables used (version_output, help_output, major) and the intended outputs
("legacy" vs "modern") so maintainers know what to preserve when modifying the
logic.

In `@tests/test_create_release_dmg.sh`:
- Around line 99-171: Add a new test function (similar to case_modern_binary)
that exercises the modern code path when no signing identity is available and
the script should pass the --no-code-sign flag; call run_script with
FAKE_CREATE_DMG_VERSION="8.0.0" and no SIGNING-ID/identity environment so the
code chooses unsigned behavior, then assert the DMG was produced, grep the
create-dmg log for "--no-code-sign" and assert npx was not invoked (npx log is
empty); reference the existing case_modern_binary and run_script usage to copy
setup/teardown and logging variable names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45d339fd-adf2-4acc-a23c-e22549e4fa01

📥 Commits

Reviewing files that changed from the base of the PR and between f474a03 and aabe87f.

📒 Files selected for processing (5)
  • .github/workflows/nightly.yml
  • .github/workflows/release.yml
  • scripts/build-sign-upload.sh
  • scripts/create_release_dmg.sh
  • tests/test_create_release_dmg.sh

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

2 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/test_create_release_dmg.sh">

<violation number="1" location="tests/test_create_release_dmg.sh:12">
P2: Shadowing the system `TMPDIR` env var causes all child processes (including the script-under-test and its `mktemp` calls) to use the test's working directory as the temp root. Rename to a test-local variable (e.g., `TEST_TMPDIR`) and substitute all references to avoid polluting the child process environment.</violation>
</file>

<file name="scripts/build-sign-upload.sh">

<violation number="1" location="scripts/build-sign-upload.sh:59">
P2: Pre-flight check doesn't match the actual `CMUX_CREATE_DMG_REQUIRE_MODERN=1` requirement. A legacy `create-dmg` (without `npx`) passes this check but `create_release_dmg.sh` will reject it later, after the expensive build/sign/notarize steps have already completed. Consider also checking for a modern `create-dmg` or `npx` here.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

exit 1
fi

TMPDIR="$(mktemp -d)"

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Shadowing the system TMPDIR env var causes all child processes (including the script-under-test and its mktemp calls) to use the test's working directory as the temp root. Rename to a test-local variable (e.g., TEST_TMPDIR) and substitute all references to avoid polluting the child process environment.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_create_release_dmg.sh, line 12:

<comment>Shadowing the system `TMPDIR` env var causes all child processes (including the script-under-test and its `mktemp` calls) to use the test's working directory as the temp root. Rename to a test-local variable (e.g., `TEST_TMPDIR`) and substitute all references to avoid polluting the child process environment.</comment>

<file context>
@@ -0,0 +1,194 @@
+  exit 1
+fi
+
+TMPDIR="$(mktemp -d)"
+trap 'rm -rf "$TMPDIR"' EXIT
+
</file context>
Fix with Cubic

Comment thread scripts/build-sign-upload.sh Outdated
for tool in zig xcodebuild xcrun codesign ditto gh; do
command -v "$tool" >/dev/null || { echo "MISSING: $tool" >&2; exit 1; }
done
if ! command -v create-dmg >/dev/null 2>&1 && ! command -v npx >/dev/null 2>&1; then

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Pre-flight check doesn't match the actual CMUX_CREATE_DMG_REQUIRE_MODERN=1 requirement. A legacy create-dmg (without npx) passes this check but create_release_dmg.sh will reject it later, after the expensive build/sign/notarize steps have already completed. Consider also checking for a modern create-dmg or npx here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-sign-upload.sh, line 59:

<comment>Pre-flight check doesn't match the actual `CMUX_CREATE_DMG_REQUIRE_MODERN=1` requirement. A legacy `create-dmg` (without `npx`) passes this check but `create_release_dmg.sh` will reject it later, after the expensive build/sign/notarize steps have already completed. Consider also checking for a modern `create-dmg` or `npx` here.</comment>

<file context>
@@ -53,9 +53,13 @@ APP_PATH="build/Build/Products/Release/cmux.app"
+for tool in zig xcodebuild xcrun codesign ditto gh; do
   command -v "$tool" >/dev/null || { echo "MISSING: $tool" >&2; exit 1; }
 done
+if ! command -v create-dmg >/dev/null 2>&1 && ! command -v npx >/dev/null 2>&1; then
+  echo "MISSING: create-dmg or npx" >&2
+  exit 1
</file context>
Fix with Cubic

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

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/test_ci_create_dmg_pinned.sh">

<violation number="1" location="tests/test_ci_create_dmg_pinned.sh:15">
P2: This test enforces workflow command strings via grep instead of validating DMG packaging behavior/artifacts, which makes it brittle and violates the repo’s test-quality policy.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

for workflow in "${WORKFLOWS[@]}"; do
if ! grep -Eq 'npm install --global .*create-dmg@' "$workflow"; then
echo "FAIL: $workflow must install create-dmg with an explicit version"
if ! grep -Eq 'brew list create-dmg >/dev/null 2>&1 \|\| brew install create-dmg' "$workflow"; then

@cubic-dev-ai cubic-dev-ai Bot Mar 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This test enforces workflow command strings via grep instead of validating DMG packaging behavior/artifacts, which makes it brittle and violates the repo’s test-quality policy.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_ci_create_dmg_pinned.sh, line 15:

<comment>This test enforces workflow command strings via grep instead of validating DMG packaging behavior/artifacts, which makes it brittle and violates the repo’s test-quality policy.</comment>

<file context>
@@ -11,15 +12,15 @@ WORKFLOWS=(
 for workflow in "${WORKFLOWS[@]}"; do
-  if ! grep -Eq 'npm install --global .*create-dmg@' "$workflow"; then
-    echo "FAIL: $workflow must install create-dmg with an explicit version"
+  if ! grep -Eq 'brew list create-dmg >/dev/null 2>&1 \|\| brew install create-dmg' "$workflow"; then
+    echo "FAIL: $workflow must provision the Homebrew create-dmg formula"
     exit 1
</file context>
Fix with Cubic

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
scripts/build-sign-upload.sh (1)

56-58: ⚠️ Potential issue | 🟡 Minor

Add lipo to the pre-flight dependency check.

The script uses lipo -archs on lines 73, 89, and 90 to verify architectures, but lipo is not included in the tool gate check. A missing lipo would only be caught after the build finishes.

🔧 Suggested fix
-for tool in zig xcodebuild xcrun codesign ditto gh; do
+for tool in zig xcodebuild xcrun codesign ditto gh lipo; do

,

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

In `@scripts/build-sign-upload.sh` around lines 56 - 58, The pre-flight dependency
check loop that iterates over tools ("for tool in zig xcodebuild xcrun codesign
ditto gh; do") is missing lipo, which the script later calls with lipo -archs;
update that tool list to include lipo so command -v verifies its presence before
proceeding (i.e., add "lipo" to the space-separated list used in the for loop).
🧹 Nitpick comments (3)
scripts/create_release_dmg.sh (1)

32-53: Version detection fallback is reasonable but could log the heuristic being used.

The detect_create_dmg_mode function has good fallback logic: first tries --version, then falls back to --help output inspection. Consider adding a debug log when falling back to heuristic detection to aid troubleshooting.

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

In `@scripts/create_release_dmg.sh` around lines 32 - 53, The
detect_create_dmg_mode function should emit a debug message when it falls back
from --version to inspecting --help so callers can see the heuristic used;
update the function (around the branch after computing help_output) to log
(e.g., via echo or the repository's logger) that it's using the help-output
heuristic for the given bin_name and include the inspected snippet or the final
decision ("legacy" vs "modern") alongside bin_name and help_output variable;
keep the existing return values and only add the minimal debug logging before
echoing the detected mode.
.github/workflows/release.yml (1)

23-35: Consider grouping echo statements per shellcheck SC2129.

Static analysis suggests using brace grouping for multiple redirects to the same file.

🔧 Optional: Group redirects
-          if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF:-}" == refs/tags/* ]]; then
-            echo "publish_release=true" >> "$GITHUB_OUTPUT"
-            echo "release_tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
-            echo "artifact_name=release-${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
-          else
+          if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF:-}" == refs/tags/* ]]; then
+            {
+              echo "publish_release=true"
+              echo "release_tag=${GITHUB_REF_NAME}"
+              echo "artifact_name=release-${GITHUB_REF_NAME}"
+            } >> "$GITHUB_OUTPUT"
+          else
             REF_SLUG="$(printf '%s' "${GITHUB_REF_NAME}" | tr '/[:space:]' '-' | tr -cd '[:alnum:]-_.')"
             SHORT_SHA="${GITHUB_SHA::7}"
-            echo "publish_release=false" >> "$GITHUB_OUTPUT"
-            echo "release_tag=verify-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
-            echo "artifact_name=release-verification-${REF_SLUG}-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
+            {
+              echo "publish_release=false"
+              echo "release_tag=verify-${SHORT_SHA}"
+              echo "artifact_name=release-verification-${REF_SLUG}-${SHORT_SHA}"
+            } >> "$GITHUB_OUTPUT"
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 23 - 35, The multiple echo lines
that append to the same output file (writing publish_release, release_tag,
artifact_name to "$GITHUB_OUTPUT") should be grouped to avoid SC2129; replace
the separate echo ... >> "$GITHUB_OUTPUT" calls inside both branches (the block
that checks GITHUB_EVENT_NAME and the else branch that sets REF_SLUG/SHORT_SHA)
with a single grouped redirect (use { ... } >> "$GITHUB_OUTPUT") so all three
variables (publish_release, release_tag, artifact_name) are written in one
redirect for each branch, keeping the same variable names (publish_release,
release_tag, artifact_name) and computed values (GITHUB_REF_NAME, REF_SLUG,
SHORT_SHA).
tests/test_ci_create_dmg_pinned.sh (1)

15-17: Grep pattern is fragile and may miss valid variations.

The exact pattern match brew list create-dmg >/dev/null 2>&1 || brew install create-dmg requires workflows to use this precise syntax. Common variations like 2>&1 >/dev/null, &>/dev/null, or different spacing would cause false failures.

Consider a more flexible pattern:

🔧 Suggested fix
-  if ! grep -Eq 'brew list create-dmg >/dev/null 2>&1 \|\| brew install create-dmg' "$workflow"; then
+  if ! grep -Eq 'brew (list|install).*create-dmg' "$workflow"; then
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_ci_create_dmg_pinned.sh` around lines 15 - 17, The current grep in
tests/test_ci_create_dmg_pinned.sh is too strict (it expects the exact
redirection order and spacing) and causes false negatives; change the check to
be more flexible by ensuring the workflow contains a command that lists or
installs create-dmg rather than matching the exact string: either search for
both "brew list create-dmg" and "brew install create-dmg" separately (e.g., two
greps) or use a single regex that allows any whitespace and common redirection
variants and matches the presence of "brew (list|install) create-dmg"; update
the test around the variable workflow to use that more permissive pattern so
valid variations like "&>/dev/null" or swapped redirections pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/create_release_dmg.sh`:
- Around line 168-173: The final call to create_dmg_modern "$path_bin" is
unreachable dead code because path_bin is either empty (exits) or already
handled earlier; remove this redundant invocation and ensure create_dmg_modern
is only called where the modern path detection branch (the code that checks
path_bin and the modern condition around lines that reference path_bin and
create_dmg_modern) invokes it once—delete the trailing call to create_dmg_modern
and rely on the existing branch that already calls create_dmg_modern when
appropriate.

---

Duplicate comments:
In `@scripts/build-sign-upload.sh`:
- Around line 56-58: The pre-flight dependency check loop that iterates over
tools ("for tool in zig xcodebuild xcrun codesign ditto gh; do") is missing
lipo, which the script later calls with lipo -archs; update that tool list to
include lipo so command -v verifies its presence before proceeding (i.e., add
"lipo" to the space-separated list used in the for loop).

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 23-35: The multiple echo lines that append to the same output file
(writing publish_release, release_tag, artifact_name to "$GITHUB_OUTPUT") should
be grouped to avoid SC2129; replace the separate echo ... >> "$GITHUB_OUTPUT"
calls inside both branches (the block that checks GITHUB_EVENT_NAME and the else
branch that sets REF_SLUG/SHORT_SHA) with a single grouped redirect (use { ... }
>> "$GITHUB_OUTPUT") so all three variables (publish_release, release_tag,
artifact_name) are written in one redirect for each branch, keeping the same
variable names (publish_release, release_tag, artifact_name) and computed values
(GITHUB_REF_NAME, REF_SLUG, SHORT_SHA).

In `@scripts/create_release_dmg.sh`:
- Around line 32-53: The detect_create_dmg_mode function should emit a debug
message when it falls back from --version to inspecting --help so callers can
see the heuristic used; update the function (around the branch after computing
help_output) to log (e.g., via echo or the repository's logger) that it's using
the help-output heuristic for the given bin_name and include the inspected
snippet or the final decision ("legacy" vs "modern") alongside bin_name and
help_output variable; keep the existing return values and only add the minimal
debug logging before echoing the detected mode.

In `@tests/test_ci_create_dmg_pinned.sh`:
- Around line 15-17: The current grep in tests/test_ci_create_dmg_pinned.sh is
too strict (it expects the exact redirection order and spacing) and causes false
negatives; change the check to be more flexible by ensuring the workflow
contains a command that lists or installs create-dmg rather than matching the
exact string: either search for both "brew list create-dmg" and "brew install
create-dmg" separately (e.g., two greps) or use a single regex that allows any
whitespace and common redirection variants and matches the presence of "brew
(list|install) create-dmg"; update the test around the variable workflow to use
that more permissive pattern so valid variations like "&>/dev/null" or swapped
redirections pass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16dbd926-3ec4-496d-bbaf-4de619a14abd

📥 Commits

Reviewing files that changed from the base of the PR and between aabe87f and 9243aa9.

📒 Files selected for processing (6)
  • .github/workflows/nightly.yml
  • .github/workflows/release.yml
  • scripts/build-sign-upload.sh
  • scripts/create_release_dmg.sh
  • tests/test_ci_create_dmg_pinned.sh
  • tests/test_create_release_dmg.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/nightly.yml
  • tests/test_create_release_dmg.sh

Comment on lines +168 to +173
if [ -z "$path_bin" ]; then
echo "create-dmg is required but not found in PATH" >&2
exit 1
fi

create_dmg_modern "$path_bin"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Line 173 appears to be unreachable dead code.

At this point in the control flow:

  • If path_bin is empty, line 169 exits
  • If path_bin is non-empty, it must have been checked at line 158 as modern (since legacy was already checked at line 141), so line 159-160 would have handled it

The final create_dmg_modern "$path_bin" on line 173 cannot be reached.

🔧 Suggested fix
 if [ -z "$path_bin" ]; then
   echo "create-dmg is required but not found in PATH" >&2
   exit 1
 fi
-
-create_dmg_modern "$path_bin"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -z "$path_bin" ]; then
echo "create-dmg is required but not found in PATH" >&2
exit 1
fi
create_dmg_modern "$path_bin"
if [ -z "$path_bin" ]; then
echo "create-dmg is required but not found in PATH" >&2
exit 1
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/create_release_dmg.sh` around lines 168 - 173, The final call to
create_dmg_modern "$path_bin" is unreachable dead code because path_bin is
either empty (exits) or already handled earlier; remove this redundant
invocation and ensure create_dmg_modern is only called where the modern path
detection branch (the code that checks path_bin and the modern condition around
lines that reference path_bin and create_dmg_modern) invokes it once—delete the
trailing call to create_dmg_modern and rely on the existing branch that already
calls create_dmg_modern when appropriate.

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview — 9243aa94 Deployed Mar 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants