Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 51 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,8 @@ jobs:
# transitive skip otherwise marks every macOS job skipped even when
# linux-preflight itself succeeds. Require the direct needs explicitly.
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.linux-preflight.result == 'success' && needs.changes.outputs.macos == 'true' && needs.changes.outputs.full_suite == 'true' }}
outputs:
release_archs: ${{ steps.release-archs.outputs.archs }}
# Build the release helper with SDK 15, then run package tests with SDK 26.
runs-on: ${{ vars.MACOS_RUNNER_DUAL_XCODE || 'blacksmith-6vcpu-macos-15' }}
timeout-minutes: 40
Expand Down Expand Up @@ -2231,8 +2233,21 @@ jobs:
# "Select package tests" diffs against.
fetch-depth: 2

# Resolve once for the helper producer and Release consumer. Nightly
# still builds the shipped universal app independently of this policy.
- name: Resolve Release check architectures
id: release-archs
if: ${{ needs.changes.outputs.release_build == 'true' }}
env:
REQUESTED_ARCHS: ${{ inputs.release_archs != 'default' && inputs.release_archs || vars.CI_RELEASE_BUILD_ARCHS }}
run: |
set -euo pipefail
archs="$(./scripts/ci/release-build-archs.sh "$REQUESTED_ARCHS")"
echo "Release check architectures: $archs"
echo "archs=$archs" >> "$GITHUB_OUTPUT"

# Only release-build consumes this artifact. Package/app-host tests use
# the prebuilt GhosttyKit framework and do not need the universal CLI.
# the prebuilt GhosttyKit framework and do not need the Release CLI.
- name: Select helper Xcode
if: ${{ needs.changes.outputs.release_build == 'true' }}
run: |
Expand All @@ -2253,22 +2268,32 @@ jobs:
key: zig-packages-${{ hashFiles('ghostty/build.zig.zon', 'ghostty/build.zig.zon.json') }}
restore-keys: zig-packages-

- name: Build universal Ghostty CLI helper
- name: Build Release Ghostty CLI helper
if: ${{ needs.changes.outputs.release_build == 'true' }}
env:
RELEASE_ARCHS: ${{ steps.release-archs.outputs.archs }}
run: |
set -euo pipefail
mkdir -p ghostty-cli-helper
./scripts/build-ghostty-cli-helper.sh --universal --output ghostty-cli-helper/ghostty
lipo ghostty-cli-helper/ghostty -verify_arch arm64 x86_64
for arch in arm64 x86_64; do
thin="ghostty-cli-helper/ghostty-$arch"
lipo ghostty-cli-helper/ghostty -thin "$arch" -output "$thin"
case "$RELEASE_ARCHS" in
arm64) helper_args=(--target aarch64-macos) ;;
"arm64 x86_64") helper_args=(--universal) ;;
*) echo "unsupported Release helper architectures: $RELEASE_ARCHS" >&2; exit 1 ;;
esac
./scripts/build-ghostty-cli-helper.sh "${helper_args[@]}" --output ghostty-cli-helper/ghostty
./scripts/ci/verify-binary-archs.sh "$RELEASE_ARCHS" ghostty-cli-helper/ghostty
Comment thread
teamleaderleo marked this conversation as resolved.
for arch in $RELEASE_ARCHS; do
thin="ghostty-cli-helper/ghostty"
if [[ "$RELEASE_ARCHS" == "arm64 x86_64" ]]; then
thin="ghostty-cli-helper/ghostty-$arch"
lipo ghostty-cli-helper/ghostty -thin "$arch" -output "$thin"
fi
HELPER_SDK_VERSION="$(otool -l "$thin" | awk '/LC_BUILD_VERSION/ { in_version=1; next } in_version && /sdk / { print $2; exit }')"
echo "Ghostty helper $arch SDK version: $HELPER_SDK_VERSION"
[[ "$HELPER_SDK_VERSION" == 15.* ]]
done

- name: Upload universal Ghostty CLI helper
- name: Upload Release Ghostty CLI helper
if: ${{ needs.changes.outputs.release_build == 'true' }}
id: upload-ghostty-cli-helper
continue-on-error: true
Expand All @@ -2279,7 +2304,7 @@ jobs:
if-no-files-found: error
retention-days: 1

- name: Retry universal Ghostty CLI helper upload
- name: Retry Release Ghostty CLI helper upload
if: ${{ needs.changes.outputs.release_build == 'true' && steps.upload-ghostty-cli-helper.outcome == 'failure' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down Expand Up @@ -3211,24 +3236,15 @@ jobs:
go-version: '1.26.x'
cache: false

# Nightly always builds the shipped universal app. A maintainer can set
# CI_RELEASE_BUILD_ARCHS=arm64 to drop the Intel whole-module compile from
# this pre-merge check, leaving Intel-only compile breaks to nightly.
- name: Resolve Release check architectures
id: release-archs
env:
REQUESTED_ARCHS: ${{ inputs.release_archs != 'default' && inputs.release_archs || vars.CI_RELEASE_BUILD_ARCHS }}
run: |
set -euo pipefail
archs="$(./scripts/ci/release-build-archs.sh "$REQUESTED_ARCHS")"
echo "Release check architectures: $archs"
echo "archs=$archs" >> "$GITHUB_OUTPUT"

- name: Build app (Release)
env:
RELEASE_ARCHS: ${{ steps.release-archs.outputs.archs }}
RELEASE_ARCHS: ${{ needs.swift-package-tests.outputs.release_archs }}
run: |
set -euo pipefail
case "$RELEASE_ARCHS" in
arm64|"arm64 x86_64") ;;
*) echo "missing or invalid producer architectures: $RELEASE_ARCHS" >&2; exit 1 ;;
esac
CMUX_SKIP_ZIG_BUILD=1 xcodebuild -project cmux.xcodeproj -scheme cmux -configuration Release -derivedDataPath build-universal \
-destination 'generic/platform=macOS' \
-clonedSourcePackagesDirPath .spm-cache \
Expand All @@ -3239,14 +3255,15 @@ jobs:
COMPILER_INDEX_STORE_ENABLE=NO \
CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly build

- name: Download universal Ghostty CLI helper
- name: Download Release Ghostty CLI helper
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: cmux-ghostty-cli-helper
path: ghostty-cli-helper

- name: Install universal Ghostty CLI helper
- name: Install Release helpers
env:
RELEASE_ARCHS: ${{ needs.swift-package-tests.outputs.release_archs }}
# install-cmux-tui-client.sh verifies the manifest's build-provenance
# attestation with gh before trusting it.
GH_TOKEN: ${{ github.token }}
Expand All @@ -3255,12 +3272,17 @@ jobs:
/bin/bash ./tests/test_install_cmux_tui_client.sh
./scripts/install-prebuilt-ghostty-cli-helper.sh \
ghostty-cli-helper/ghostty \
build-universal/Build/Products/Release/cmux.app
./scripts/install-cmux-tui-client.sh build-universal/Build/Products/Release/cmux.app
build-universal/Build/Products/Release/cmux.app --archs "$RELEASE_ARCHS"
case "$RELEASE_ARCHS" in
arm64) client_arch=arm64 ;;
"arm64 x86_64") client_arch=universal ;;
*) echo "unsupported Release client architectures: $RELEASE_ARCHS" >&2; exit 1 ;;
esac
./scripts/install-cmux-tui-client.sh build-universal/Build/Products/Release/cmux.app --arch "$client_arch"

- name: Validate Release artifact slices
env:
RELEASE_ARCHS: ${{ steps.release-archs.outputs.archs }}
RELEASE_ARCHS: ${{ needs.swift-package-tests.outputs.release_archs }}
run: |
set -euo pipefail
APP_BINARY="build-universal/Build/Products/Release/cmux.app/Contents/MacOS/cmux"
Expand All @@ -3272,17 +3294,13 @@ jobs:
test -x "$CLI_BINARY"
TUI_CLIENT="build-universal/Build/Products/Release/cmux.app/Contents/Resources/bin/cmux-tui"
test -x "$TUI_CLIENT"
lipo "$TUI_CLIENT" -verify_arch arm64 x86_64
test -x "$HELPER_BINARY"
test -x "$CMUX_CUA_BINARY"
test -x "$DIFF_SIDECAR"
file "$APP_BINARY" "$CLI_BINARY" "$HELPER_BINARY" "$CMUX_CUA_BINARY" "$DIFF_SIDECAR"
SDK_VERSION="$(otool -l "$APP_BINARY" | awk '/LC_BUILD_VERSION/ { in_version=1; next } in_version && /sdk / { print $2; exit }')"
echo "App SDK version: $SDK_VERSION"
# The helper and the TUI client are prebuilt downloads, so they are
# universal whichever architectures this check compiled.
lipo "$HELPER_BINARY" -verify_arch arm64 x86_64
./scripts/ci/verify-binary-archs.sh "$RELEASE_ARCHS" "$APP_BINARY" "$CLI_BINARY" "$CMUX_CUA_BINARY"
./scripts/ci/verify-binary-archs.sh "$RELEASE_ARCHS" "$APP_BINARY" "$CLI_BINARY" "$CMUX_CUA_BINARY" "$HELPER_BINARY" "$TUI_CLIENT"
codesign --verify --strict --verbose=4 "$CMUX_CUA_BINARY"
./scripts/verify-diff-sidecar-artifact.sh "$DIFF_SIDECAR" --archs "$RELEASE_ARCHS"
[[ "$SDK_VERSION" == 26.* ]]
Expand Down
2 changes: 2 additions & 0 deletions scripts/ci/select_package_tests.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
GLOBAL_INPUTS = (
".github/workflows/ci.yml",
"scripts/build-ghostty-cli-helper.sh",
"scripts/ci/release-build-archs.sh",
"scripts/ci/run-swift-testing-suites.sh",
"scripts/ci/run_with_timeout.py",
"scripts/ci/select_package_tests.py",
"scripts/ci/verify-binary-archs.sh",
"scripts/download-prebuilt-ghosttykit.sh",
"scripts/install-rust-ci.sh",
"scripts/install-zig-ci.sh",
Expand Down
17 changes: 10 additions & 7 deletions scripts/install-prebuilt-ghostty-cli-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ set -euo pipefail

usage() {
cat <<'EOF'
usage: scripts/install-prebuilt-ghostty-cli-helper.sh <helper-path> <app-path>
usage: scripts/install-prebuilt-ghostty-cli-helper.sh <helper-path> <app-path> [--archs "arm64 x86_64"]
EOF
}

if [[ $# -ne 2 ]]; then
if [[ $# -ne 2 && ( $# -ne 4 || "${3:-}" != --archs ) ]]; then
usage >&2
exit 1
fi

HELPER_PATH="$1"
APP_PATH="$2"
EXPECTED_ARCHS="${4-arm64 x86_64}"
case "$EXPECTED_ARCHS" in
arm64|x86_64|"arm64 x86_64"|"x86_64 arm64") ;;
*) echo "error: unsupported helper architectures: $EXPECTED_ARCHS" >&2; exit 1 ;;
esac
DEST_PATH="$APP_PATH/Contents/Resources/bin/ghostty"

if [[ ! -f "$HELPER_PATH" ]]; then
Expand All @@ -29,8 +34,6 @@ fi
mkdir -p "$(dirname "$DEST_PATH")"
install -m 755 "$HELPER_PATH" "$DEST_PATH"

# One arch per invocation: some lipo builds (Xcode 27 beta 4) consume only one
# arch after -verify_arch and read the second as an extra input file, failing
# with "requires exactly one input file".
for arch in arm64 x86_64; do lipo "$DEST_PATH" -verify_arch "$arch"; done
echo "Installed universal Ghostty CLI helper at $DEST_PATH"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
"$SCRIPT_DIR/ci/verify-binary-archs.sh" "$EXPECTED_ARCHS" "$DEST_PATH"
echo "Installed Ghostty CLI helper ($EXPECTED_ARCHS) at $DEST_PATH"
14 changes: 6 additions & 8 deletions tests/test_ci_change_areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1522,18 +1522,16 @@ def test_required_macos_topology_collapses_display_and_release_helper_jobs() ->
assert "/Applications/Xcode_16.4.app" not in package_block
assert "Select helper Xcode" in package_block
assert "CMUX_CI_REQUIRED_MACOS_SDK_MAJOR=15" in package_block
assert "Build universal Ghostty CLI helper" in package_block
assert "./scripts/build-ghostty-cli-helper.sh --universal --output ghostty-cli-helper/ghostty" in package_block
assert "Build Release Ghostty CLI helper" in package_block
assert '[[ "$HELPER_SDK_VERSION" == 15.* ]]' in package_block
assert "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" in package_block
assert package_block.index("Select helper Xcode") < package_block.index("Build universal Ghostty CLI helper")
assert package_block.index("Build universal Ghostty CLI helper") < package_block.index("Select Xcode")
assert package_block.index("Upload universal Ghostty CLI helper") < package_block.index("Select Xcode")
assert package_block.index("Select helper Xcode") < package_block.index("Build Release Ghostty CLI helper")
assert package_block.index("Build Release Ghostty CLI helper") < package_block.index("Select Xcode")
assert package_block.index("Upload Release Ghostty CLI helper") < package_block.index("Select Xcode")
assert " - swift-package-tests" in release_block
assert "Download universal Ghostty CLI helper" in release_block
assert "Download Release Ghostty CLI helper" in release_block
assert "actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131" in release_block
assert "Install universal Ghostty CLI helper" in release_block
assert "./scripts/build-ghostty-cli-helper.sh --universal --output ghostty-cli-helper/ghostty" not in release_block
assert "Install Release helpers" in release_block


def test_remote_tmux_layout_identity_uses_a_nontolerant_focused_gate() -> None:
Expand Down
22 changes: 2 additions & 20 deletions tests/test_ci_release_build_archs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,6 @@ for bad in "x86_64" "arm64 x86_64" "ARM64" "arm64;rm -rf /"; do
fi
done

if ! awk '
/^ release-build:/ { in_job=1; next }
in_job && /^ [a-zA-Z0-9_-]+:/ { in_job=0 }
in_job && /scripts\/ci\/release-build-archs\.sh/ { saw_resolver=1 }
in_job && /ARCHS="\$RELEASE_ARCHS"/ { saw_build=1 }
in_job && /ARCHS="arm64/ { saw_literal=1 }
END { exit !(saw_resolver && saw_build && !saw_literal) }
' "$CI_FILE"; then
echo "FAIL: release-build must take its architectures from scripts/ci/release-build-archs.sh"
exit 1
fi

# Slice verification is exact: an arm64 check must reject a universal binary,
# or a change that brings the Intel compile back would pass unnoticed.
Expand Down Expand Up @@ -82,19 +71,12 @@ if verify "arm64"; then
exit 1
fi

if ! awk '
/^ release-build:/ { in_job=1; next }
in_job && /^ [a-zA-Z0-9_-]+:/ { in_job=0 }
in_job && /verify-binary-archs\.sh "\$RELEASE_ARCHS" "\$APP_BINARY" "\$CLI_BINARY" "\$CMUX_CUA_BINARY"/ { saw=1 }
END { exit !saw }
' "$CI_FILE"; then
echo "FAIL: release-build must check the built binaries against the exact resolved architectures"
exit 1
fi

if grep -n -E 'CI_RELEASE_BUILD_ARCHS|release-build-archs\.sh|release_archs' "$NIGHTLY_FILE"; then
echo "FAIL: nightly builds what ships and must stay universal unconditionally"
exit 1
fi

python3 "$ROOT_DIR/tests/test_ci_release_helper_archs.py"

echo "PASS: the CI Release check defaults to universal, arm64 is opt-in, and nightly cannot be narrowed"
Loading
Loading