Skip to content

ci: reload-build runner input as free-form string - #6360

Closed
lawrencecchen wants to merge 2 commits into
mainfrom
reload-build-runner-string
Closed

lawrencecchen wants to merge 2 commits into
mainfrom
reload-build-runner-string

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #6354. Makes the runner input a free-form string so the cloud reload scripts can dispatch the build onto any macOS runner label (Blacksmith, our self-hosted cmux-macos-26 fleet, warp, depot) without enumerating choices. Enables a Blacksmith-vs-self-hosted queue/build timing comparison through one workflow. workflow_dispatch only; committed [skip ci].


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
CI-only, manually dispatched workflow with read-only repo permissions; it does not run on push/PR and does not change app runtime or release pipelines.

Overview
Adds a workflow_dispatch-only reload-build workflow so cloud reload scripts can build tagged macOS apps or unsigned iOS archives on a remote Mac runner and download the artifact—without triggering on push/PR.

The runner input is a free-form string (default blacksmith-6vcpu-macos-26) so dispatchers can target Blacksmith, self-hosted labels, warp, or depot without maintaining a fixed choice list; the label is echoed in timings.json and the job summary for queue/build comparisons.

Dispatch inputs cover tag, optional ref, platform (macos / ios), and a nonce in run-name so callers can find the run. Concurrency cancels in-flight builds per tag+platform. macOS builds use ./scripts/reload.sh; iOS runs an unsigned xcodebuild archive with dev bundle id/display name from the tag.

Reviewed by Cursor Bugbot for commit 80fe9e5. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds a dispatch-only reload-build GitHub Actions workflow to build a tagged dev macOS app or an unsigned iOS archive and upload the artifact. The runner input is now a free-form string so scripts can target any macOS runner label (e.g., blacksmith-6vcpu-macos-26, cmux-macos-26) and compare queue/build times in one workflow.

  • New Features
    • Runs via workflow_dispatch only; no push/PR triggers.
    • Concurrency keyed by tag+platform; run name includes tag, platform, and nonce.
    • Emits timings.json and a step summary; uploads artifacts with 3-day retention.

Written for commit 80fe9e5. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • Added a new automated build workflow to the development infrastructure for generating and uploading development artifacts across supported platforms. The workflow enables manual triggering of builds with customizable configurations. Generated artifacts are automatically managed with appropriate retention policies.

lawrencecchen and others added 2 commits June 17, 2026 15:40
…ilder

[skip ci]

reload-cloud.sh / reload-cloud-ios.sh --builder blacksmith dispatch this to
build a tagged dev macOS app or unsigned iOS archive on a Blacksmith macOS
runner and upload it for local download. workflow_dispatch only, so it never
joins the push/PR CI fan-out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[skip ci]

Lets the cloud reload scripts dispatch the build onto any macOS runner label
(Blacksmith, our self-hosted cmux-macos-26 fleet, warp, depot) without
enumerating choices, so a Blacksmith-vs-self-hosted queue/build timing
comparison can target both through the same workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 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 Jun 17, 2026 11:04pm
cmux-staging Building Building Preview, Comment Jun 17, 2026 11:04pm

@coderabbitai

coderabbitai Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d1bbacb-f399-44e7-9e9c-c8d6813f2174

📥 Commits

Reviewing files that changed from the base of the PR and between 449a83e and 80fe9e5.

📒 Files selected for processing (1)
  • .github/workflows/reload-build.yml

📝 Walkthrough

Walkthrough

A new workflow_dispatch-only GitHub Actions workflow reload-build.yml is added. It accepts inputs for tag, ref, platform (macOS or iOS), runner, and nonce, then checks out the repo, sets up Xcode and Zig, conditionally builds either a macOS app zip or an unsigned iOS xcarchive zip, records timing data, and uploads all artifacts with a 3-day retention.

Changes

reload-build CI Workflow

Layer / File(s) Summary
Workflow trigger, inputs, permissions, and concurrency
.github/workflows/reload-build.yml
Declares workflow_dispatch with tag, ref, platform (macos|ios), runner, and nonce inputs; sets run name; configures read-only permissions; limits to one in-flight build per tag+platform with cancellation.
Job definition, checkout, and dependency setup
.github/workflows/reload-build.yml
Defines the build job with env flags, checks out the requested ref with recursive submodules, starts a timer, selects the installed Xcode, installs Zig, provisions GhosttyKit for macOS builds, and records dependency timing.
macOS and iOS conditional build paths
.github/workflows/reload-build.yml
macOS path: runs ./scripts/reload.sh, parses the app path from /tmp/reload.log, validates it, zips to artifact/app.zip. iOS path: derives a tag slug, registers the iOS runtime (downloading if missing), builds an unsigned Debug xcarchive for generic iOS, validates and zips to artifact/archive.zip.
Timing report and artifact upload
.github/workflows/reload-build.yml
Always writes artifact/timings.json and appends a timing table to the GitHub step summary; uploads artifact/ as reload-<tag>-<platform> with 3-day retention, failing if no files are found.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • manaflow-ai/cmux#4902: The new reload-build.yml workflow uses the same configurable runner input pattern with blacksmith-6vcpu-macos-* defaults introduced in this macOS CI runner migration PR.
  • manaflow-ai/cmux#4926: This PR reverts macOS workflows from Blacksmith back to WarpBuild runner labels, directly affecting the same runs-on configuration area used by the new reload-build.yml workflow.

Poem

🐇 Hop, hop, I press the button and away,
A tag is set, the platform chosen today.
Xcode wakes up, Zig zips right in,
The archive is packed, the timings begin.
Three days to keep it, then off to the hay —
Another build shipped the rabbit's way! 🗂️

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch reload-build-runner-string
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch reload-build-runner-string

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.

@lawrencecchen
lawrencecchen deleted the reload-build-runner-string branch June 17, 2026 23:04

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 80fe9e5. Configure here.

name: reload-${{ inputs.tag }}-${{ inputs.platform }}
path: artifact/
retention-days: 3
if-no-files-found: error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Artifact upload skipped on failure

Medium Severity

When a build step fails, Write timings.json still runs via always(), but Upload artifact uses the default success() condition, so it is skipped whenever any earlier step failed. Timing data from failed runs is never published, which undercuts runner comparisons on failed or partial builds.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 80fe9e5. Configure here.

"deps_seconds": $(( t1 > t0 ? t1 - t0 : 0 )),
"build_seconds": $(( t1 > 0 ? now - t1 : 0 )),
"post_checkout_total_seconds": $(( now - t0 ))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checkout failure skews timing totals

Low Severity

Write timings.json runs under always(), but t0 comes from Start timer, which never runs if checkout fails. An empty t0 is treated as zero in shell arithmetic, so post_checkout_total_seconds becomes roughly the current Unix epoch instead of a meaningful duration.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 80fe9e5. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a new workflow_dispatch-only workflow (reload-build.yml) for cloud reload scripts to dispatch macOS builds onto any runner label — the runner input is a free-form string instead of an enumerated choice, enabling Blacksmith, self-hosted, warp, or depot targets without changing the workflow file.

  • The overall workflow structure (pinned action SHAs, permissions: contents: read, a 60-minute timeout, and a concurrency guard per tag+platform) is solid, and the iOS step correctly binds inputs.tag to an env var before using it in the script.
  • Two steps — "Build tagged macOS app" and "Write timings.json" — embed free-form ${{ inputs.tag }}, ${{ inputs.runner }}, and ${{ inputs.ref }} directly in run: blocks via GHA expressions. Because GHA splices the value before the shell sees the script, any input containing $(…) or backticks will execute as commands in the runner's shell. The fix is the same pattern already used in the iOS step: bind inputs to step-level env vars and reference those in the shell.

Confidence Score: 3/5

Merging introduces a working CI workflow, but the macOS build step and the timings step both embed free-form user inputs directly into shell commands rather than through env vars, leaving live command-injection paths in code that will be invoked by the cloud reload scripts.

The iOS step already demonstrates the correct pattern, so the fix is straightforward, but the same pattern needs to be applied to two more steps before the workflow is safe to run in production dispatch. The injection surfaces are only reachable by users with write access, so the risk is bounded, but they are present in new code on the critical build path.

.github/workflows/reload-build.yml — specifically the "Build tagged macOS app" step (line 107) and the "Write timings.json" step (lines 166–185).

Security Review

  • Command injection via unquoted heredoc (.github/workflows/reload-build.yml, "Write timings.json" step, lines 166–180): ${{ inputs.tag }}, ${{ inputs.runner }}, and ${{ inputs.ref }} are pre-substituted by GHA into a heredoc whose delimiter is unquoted (<<JSON), so any $(…) in an input executes as a shell command. runner is now a free-form string, making this directly reachable.
  • Command injection in double-quoted echo (same step, line 180): ${{ inputs.runner }} is embedded in echo "… \${{ inputs.runner }}`"inside double quotes —$(…)` in the runner label fires as command substitution.
  • Argument/command injection in macOS build step (line 107): ${{ inputs.tag }} is directly spliced into ./scripts/reload.sh --tag "${{ inputs.tag }}", enabling command substitution inside the double-quoted argument.
  • All three vectors require write/dispatch access to the repository to trigger, limiting external exploitability, but represent a present injection pattern that should be fixed before this workflow is used in production dispatch scripts.

Important Files Changed

Filename Overview
.github/workflows/reload-build.yml New workflow_dispatch-only build workflow. Runner input correctly widened to free-form string, but two steps embed free-form inputs directly in shell via GHA expressions rather than env vars, creating command injection vectors in the macOS build step and the timings heredoc/echo.

Reviews (1): Last reviewed commit: "ci: make reload-build runner input a fre..." | Re-trigger Greptile

Comment on lines +102 to +107
- name: Build tagged macOS app
if: ${{ inputs.platform == 'macos' }}
id: build_macos
run: |
set -euo pipefail
./scripts/reload.sh --tag "${{ inputs.tag }}" --swift-frontend-workaround 2>&1 | tee /tmp/reload.log

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.

P1 security GHA expression injection in macOS build step

${{ inputs.tag }} is expanded by the GitHub Actions expression engine and spliced into the shell script as a raw string before the shell sees the code. If the tag contains $(…) or backticks, those are executed as command substitution inside the double-quoted argument. The iOS build step already uses the correct pattern (env: BUILD_TAG: ${{ inputs.tag }} + "$BUILD_TAG" in the run script) — this step should do the same.

Suggested change
- name: Build tagged macOS app
if: ${{ inputs.platform == 'macos' }}
id: build_macos
run: |
set -euo pipefail
./scripts/reload.sh --tag "${{ inputs.tag }}" --swift-frontend-workaround 2>&1 | tee /tmp/reload.log
- name: Build tagged macOS app
if: ${{ inputs.platform == 'macos' }}
id: build_macos
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
./scripts/reload.sh --tag "$INPUT_TAG" --swift-frontend-workaround 2>&1 | tee /tmp/reload.log

Comment on lines +158 to +185
- name: Write timings.json
if: ${{ always() }}
run: |
set -euo pipefail
mkdir -p artifact
now=$(date +%s)
t0=${{ steps.t0.outputs.epoch }}
t1=${{ steps.t1.outputs.epoch || 0 }}
cat > artifact/timings.json <<JSON
{
"tag": "${{ inputs.tag }}",
"platform": "${{ inputs.platform }}",
"runner": "${{ inputs.runner }}",
"ref": "${{ inputs.ref }}",
"deps_seconds": $(( t1 > t0 ? t1 - t0 : 0 )),
"build_seconds": $(( t1 > 0 ? now - t1 : 0 )),
"post_checkout_total_seconds": $(( now - t0 ))
}
JSON
{
echo "### reload-build timings"
echo ""
echo "- runner: \`${{ inputs.runner }}\`"
echo "- platform: \`${{ inputs.platform }}\`"
echo "- deps: $(( t1 > t0 ? t1 - t0 : 0 ))s"
echo "- build: $(( t1 > 0 ? now - t1 : 0 ))s"
echo "- post-checkout total: $(( now - t0 ))s"
} >> "$GITHUB_STEP_SUMMARY"

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.

P1 security GHA expression injection in Write timings.json step — two injection surfaces

The heredoc delimiter <<JSON is unquoted, so the shell processes its body for command substitution. ${{ inputs.tag }}, ${{ inputs.runner }}, and ${{ inputs.ref }} are all pre-expanded by GHA before the shell runs, meaning a tag or runner label containing $(…) or backticks will be executed as commands inside the heredoc body. runner is now a free-form string (the whole point of this PR), making this a concrete path.

The echo "- runner: \${{ inputs.runner }}`"line on the step-summary block has the same issue —runneris double-quote-interpolated intoecho`, so command substitution fires there too.

The recommended fix is to bind all three inputs to step-level env vars (INPUT_TAG, INPUT_RUNNER, INPUT_REF) and reference those variables throughout both the heredoc body and the echo lines. The shell arithmetic $(( … )) expressions are unaffected by that change.

@lawrencecchen
lawrencecchen restored the reload-build-runner-string branch July 18, 2026 10:25

This branch was successfully deployed

1 active deployment
Preview – cmux — 80fe9e55 Deployed Jun 17, 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