Repository navigation
ci: add reload-build workflow for cloud reload Blacksmith builder #6354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,198 @@ | ||||||||||||||||||||||||||
| name: reload-build | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Dispatchable tagged dev-build for the cloud reload scripts' Blacksmith builder. | ||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||
| # scripts/reload-cloud.sh --builder blacksmith and scripts/reload-cloud-ios.sh | ||||||||||||||||||||||||||
| # --builder blacksmith (in the cmuxterm-hq control repo) dispatch this workflow | ||||||||||||||||||||||||||
| # against an ephemeral branch holding the caller's working tree, then download the | ||||||||||||||||||||||||||
| # produced artifact and install it locally. This is the Blacksmith alternative to | ||||||||||||||||||||||||||
| # SSH-leasing a fleet Mac. It is workflow_dispatch ONLY: nothing here runs on push | ||||||||||||||||||||||||||
| # or pull_request, so it never adds to the heavy CI fan-out. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||
| tag: | ||||||||||||||||||||||||||
| description: Dev build tag (becomes cmux DEV <tag> / dev.cmux.ios.<tag>) | ||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||
| type: string | ||||||||||||||||||||||||||
| ref: | ||||||||||||||||||||||||||
| description: Source ref to build (branch or SHA). Defaults to the dispatch ref. | ||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||
| default: "" | ||||||||||||||||||||||||||
| type: string | ||||||||||||||||||||||||||
| platform: | ||||||||||||||||||||||||||
| description: Which artifact to build | ||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||
| default: macos | ||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||
| options: | ||||||||||||||||||||||||||
| - macos | ||||||||||||||||||||||||||
| - ios | ||||||||||||||||||||||||||
| runner: | ||||||||||||||||||||||||||
| description: macOS runner label to build on | ||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||
| default: blacksmith-6vcpu-macos-26 | ||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||
| options: | ||||||||||||||||||||||||||
| - blacksmith-6vcpu-macos-26 | ||||||||||||||||||||||||||
| - blacksmith-6vcpu-macos-15 | ||||||||||||||||||||||||||
| - blacksmith-6vcpu-macos-latest | ||||||||||||||||||||||||||
| - warp-macos-26-arm64-6x | ||||||||||||||||||||||||||
| - warp-macos-15-arm64-6x | ||||||||||||||||||||||||||
| - depot-macos-latest | ||||||||||||||||||||||||||
| nonce: | ||||||||||||||||||||||||||
| description: Opaque marker echoed into run-name so the dispatcher can find this run. | ||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||
| default: "" | ||||||||||||||||||||||||||
| type: string | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Surface tag/platform/nonce in the run title so the dispatcher can match its run | ||||||||||||||||||||||||||
| # by the nonce it passed (gh workflow run does not return a run id). | ||||||||||||||||||||||||||
| run-name: "reload-build ${{ inputs.tag }} ${{ inputs.platform }} ${{ inputs.nonce }}" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| concurrency: | ||||||||||||||||||||||||||
| # One in-flight build per tag+platform; a newer dispatch supersedes an older one. | ||||||||||||||||||||||||||
| group: reload-build-${{ inputs.tag }}-${{ inputs.platform }} | ||||||||||||||||||||||||||
| cancel-in-progress: true | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||
| build: | ||||||||||||||||||||||||||
| runs-on: ${{ inputs.runner }} | ||||||||||||||||||||||||||
| timeout-minutes: 60 | ||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||
| # The ghostty CLI helper zig build is skipped; GhosttyKit comes prebuilt. | ||||||||||||||||||||||||||
| CMUX_SKIP_ZIG_BUILD: "1" | ||||||||||||||||||||||||||
| SWIFT_BACKTRACE: "interactive=no,timeout=0s,symbolicate=off,color=no" | ||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||
| - name: Checkout source ref | ||||||||||||||||||||||||||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||
| ref: ${{ inputs.ref || github.ref }} | ||||||||||||||||||||||||||
| submodules: recursive | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Start timer | ||||||||||||||||||||||||||
| id: t0 | ||||||||||||||||||||||||||
| run: echo "epoch=$(date +%s)" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Select Xcode | ||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||
| if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then | ||||||||||||||||||||||||||
| XCODE_DIR="/Applications/Xcode.app/Contents/Developer" | ||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||
| XCODE_APP="$(find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null | sort | tail -n 1 || true)" | ||||||||||||||||||||||||||
| [ -n "$XCODE_APP" ] || { echo "No Xcode.app under /Applications" >&2; exit 1; } | ||||||||||||||||||||||||||
| XCODE_DIR="$XCODE_APP/Contents/Developer" | ||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
| echo "DEVELOPER_DIR=$XCODE_DIR" >> "$GITHUB_ENV" | ||||||||||||||||||||||||||
| export DEVELOPER_DIR="$XCODE_DIR" | ||||||||||||||||||||||||||
| xcodebuild -version | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Install zig | ||||||||||||||||||||||||||
| run: ./scripts/install-zig-ci.sh | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Provision GhosttyKit (macOS) | ||||||||||||||||||||||||||
| if: ${{ inputs.platform == 'macos' }} | ||||||||||||||||||||||||||
| run: ./scripts/download-prebuilt-ghosttykit.sh || ./scripts/ensure-ghosttykit.sh | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Mark deps-ready | ||||||||||||||||||||||||||
| id: t1 | ||||||||||||||||||||||||||
| run: echo "epoch=$(date +%s)" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # --- macOS: build the tagged dev app via the same reload.sh the fleet uses --- | ||||||||||||||||||||||||||
| - 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 | ||||||||||||||||||||||||||
| app_path="$(awk '/^App path:/{getline; sub(/^ /,""); print; exit}' /tmp/reload.log)" | ||||||||||||||||||||||||||
| [ -n "$app_path" ] && [ -d "$app_path" ] || { echo "could not locate built app" >&2; exit 1; } | ||||||||||||||||||||||||||
| echo "app_path=$app_path" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||
| mkdir -p artifact | ||||||||||||||||||||||||||
| ( cd "$(dirname "$app_path")" && ditto -c -k --sequesterRsrc --keepParent "$(basename "$app_path")" "$GITHUB_WORKSPACE/artifact/app.zip" ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # --- iOS: build an UNSIGNED debug archive (signed locally by the caller) --- | ||||||||||||||||||||||||||
| - name: Build unsigned iOS archive | ||||||||||||||||||||||||||
| if: ${{ inputs.platform == 'ios' }} | ||||||||||||||||||||||||||
| id: build_ios | ||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||
| BUILD_TAG: ${{ inputs.tag }} | ||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||
| slug="$(printf '%s' "$BUILD_TAG" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-' | sed 's/-\{2,\}/-/g; s/^-//; s/-$//')" | ||||||||||||||||||||||||||
| bundle_id="dev.cmux.ios.$slug" | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty iOS slug breaks bundleMedium Severity The iOS slug sanitizer can produce an empty string when the dev tag has no alphanumeric characters, yielding bundle id Reviewed by Cursor Bugbot for commit 4b9de05. Configure here. |
||||||||||||||||||||||||||
| display_name="cmux DEV $BUILD_TAG" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Register the iOS platform if the runner only has macOS provisioned. | ||||||||||||||||||||||||||
| ios_ready() { xcrun simctl runtime list 2>/dev/null | grep -qiE "iOS [0-9].*\(Ready\)"; } | ||||||||||||||||||||||||||
| if ! ios_ready; then | ||||||||||||||||||||||||||
| echo "iOS platform not registered; installing via downloadPlatform iOS" | ||||||||||||||||||||||||||
| xcodebuild -downloadPlatform iOS 2>&1 | tr '\r' '\n' | grep -ivE 'Preparing to download|registering download' | tail -8 || true | ||||||||||||||||||||||||||
| ios_ready || { echo "iOS platform still not registered; archive would fail" >&2; exit 1; } | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ios_ready grep never matchesHigh Severity The Reviewed by Cursor Bugbot for commit 4b9de05. Configure here. |
||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ./scripts/ensure-ghosttykit.sh | ||||||||||||||||||||||||||
| out="$GITHUB_WORKSPACE/build" | ||||||||||||||||||||||||||
| mkdir -p "$out" | ||||||||||||||||||||||||||
| archive="$out/cmux-ios-$slug.xcarchive" | ||||||||||||||||||||||||||
| rm -rf "$archive" | ||||||||||||||||||||||||||
| xcodebuild archive \ | ||||||||||||||||||||||||||
| -workspace ios/cmux.xcworkspace \ | ||||||||||||||||||||||||||
| -scheme cmux-ios \ | ||||||||||||||||||||||||||
| -configuration Debug \ | ||||||||||||||||||||||||||
| -destination 'generic/platform=iOS' \ | ||||||||||||||||||||||||||
| -archivePath "$archive" \ | ||||||||||||||||||||||||||
| -derivedDataPath "$RUNNER_TEMP/cmux-ios-dd" \ | ||||||||||||||||||||||||||
| PRODUCT_BUNDLE_IDENTIFIER="$bundle_id" \ | ||||||||||||||||||||||||||
| PRODUCT_DISPLAY_NAME="$display_name" \ | ||||||||||||||||||||||||||
| CMUX_GIT_SHA="$(git rev-parse --short HEAD)" \ | ||||||||||||||||||||||||||
| CMUX_DEV_TAG="$BUILD_TAG" \ | ||||||||||||||||||||||||||
| EXCLUDED_SOURCE_FILE_NAMES=Info.plist \ | ||||||||||||||||||||||||||
| CODE_SIGNING_ALLOWED=NO \ | ||||||||||||||||||||||||||
| CODE_SIGNING_REQUIRED=NO \ | ||||||||||||||||||||||||||
| CODE_SIGN_IDENTITY="" | ||||||||||||||||||||||||||
| [ -d "$archive" ] || { echo "archive not produced: $archive" >&2; exit 1; } | ||||||||||||||||||||||||||
| mkdir -p artifact | ||||||||||||||||||||||||||
| ( cd "$out" && ditto -c -k --keepParent "$(basename "$archive")" "$GITHUB_WORKSPACE/artifact/archive.zip" ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Write timings.json | ||||||||||||||||||||||||||
| if: ${{ always() }} | ||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||
| mkdir -p artifact | ||||||||||||||||||||||||||
| now=$(date +%s) | ||||||||||||||||||||||||||
| t0=${{ steps.t0.outputs.epoch }} | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||
| t1=${{ steps.t1.outputs.epoch || 0 }} | ||||||||||||||||||||||||||
| cat > artifact/timings.json <<JSON | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "tag": "${{ inputs.tag }}", | ||||||||||||||||||||||||||
| "platform": "${{ inputs.platform }}", | ||||||||||||||||||||||||||
| "runner": "${{ inputs.runner }}", | ||||||||||||||||||||||||||
| "ref": "${{ inputs.ref }}", | ||||||||||||||||||||||||||
|
Comment on lines
+171
to
+176
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||
| "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" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - name: Upload artifact | ||||||||||||||||||||||||||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||
| name: reload-${{ inputs.tag }}-${{ inputs.platform }} | ||||||||||||||||||||||||||
| path: artifact/ | ||||||||||||||||||||||||||
| retention-days: 3 | ||||||||||||||||||||||||||
| if-no-files-found: error | ||||||||||||||||||||||||||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
${{ inputs.tag }}directly into the shell command. GitHub Actions evaluates${{ }}expressions before passing the string to the shell, so a tag likefoo" --extra-flagor$(evil)is injected verbatim, turning thereload.shinvocation into arbitrary shell execution. The iOS step already does this correctly by binding the value toBUILD_TAGviaenv:and referencing$BUILD_TAGin the script — apply the same pattern here.