Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
self-hosted-runner:
labels:
- warp-macos-15-arm64-6x
- warp-macos-26-arm64-6x
- depot-macos-latest
- depot-macos-14
7 changes: 6 additions & 1 deletion .github/workflows/build-ghosttykit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/ci-macos-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ jobs:
# Pick the latest Xcode installed on the runner. GitHub-hosted macos-14
# defaults to Xcode 15.4, but the project needs Xcode 16+ (Swift tools
# version 6.0 required by sentry-cocoa).
XCODE_APP="$(ls -d /Applications/Xcode_*.app 2>/dev/null | sort | tail -n 1)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -z "$XCODE_APP" ]; then
XCODE_APP="/Applications/Xcode.app"
fi
Expand Down
48 changes: 41 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -379,7 +384,7 @@ jobs:
CMUX_CLI_BIN="$CLI_BIN" python3 tests/test_pi_extension_install.py

tests-build-and-lag:
# Build the full cmux scheme and run the lag regression on WarpBuild.
# Build the full cmux scheme and run the lag regression on macOS CI.
# Keep lag validation separate from UI regressions so functional UI failures
# and performance regressions stay isolated. Broader interactive UI suites
# still run via test-e2e.yml on GitHub-hosted runners.
Expand All @@ -397,7 +402,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -585,7 +595,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -655,6 +670,20 @@ jobs:
ONLY_ACTIVE_ARCH=NO \
CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly build

- name: Validate Release artifact slices
run: |
set -euo pipefail
APP_BINARY="build-universal/Build/Products/Release/cmux.app/Contents/MacOS/cmux"
CLI_BINARY="build-universal/Build/Products/Release/cmux.app/Contents/Resources/bin/cmux"
HELPER_BINARY="build-universal/Build/Products/Release/cmux.app/Contents/Resources/bin/ghostty"
test -x "$APP_BINARY"
test -x "$CLI_BINARY"
test -x "$HELPER_BINARY"
file "$APP_BINARY" "$CLI_BINARY" "$HELPER_BINARY"
lipo "$APP_BINARY" -verify_arch arm64 x86_64
lipo "$CLI_BINARY" -verify_arch arm64 x86_64
lipo "$HELPER_BINARY" -verify_arch arm64 x86_64

ui-regressions:
runs-on: warp-macos-15-arm64-6x
timeout-minutes: 25
Expand All @@ -670,7 +699,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -757,7 +791,7 @@ jobs:
echo "VDISPLAY_PERSISTENT_PID=$!" >> "$GITHUB_ENV"

echo "Waiting for persistent virtual display..."
for i in $(seq 1 24); do
for _ in $(seq 1 24); do
if [ -f "$VDISPLAY_READY" ]; then break; fi
sleep 0.5
done
Expand Down Expand Up @@ -820,7 +854,7 @@ jobs:

# Wait for display ready
echo "Waiting for virtual display..."
for i in $(seq 1 24); do
for _ in $(seq 1 24); do
if [ -f "$DISPLAY_READY" ]; then break; fi
sleep 0.5
done
Expand Down
17 changes: 12 additions & 5 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -271,12 +276,14 @@ jobs:
NIGHTLY_BUILD="${NIGHTLY_DATE}000000"
fi
NIGHTLY_MARKETING_VERSION="${BASE_MARKETING}-nightly.${NIGHTLY_BUILD}"
echo "NIGHTLY_BUILD=${NIGHTLY_BUILD}" >> "$GITHUB_ENV"
echo "NIGHTLY_MARKETING_VERSION=${NIGHTLY_MARKETING_VERSION}" >> "$GITHUB_ENV"
echo "NIGHTLY_REMOTE_DAEMON_VERSION=${NIGHTLY_MARKETING_VERSION}" >> "$GITHUB_ENV"

NIGHTLY_DMG_IMMUTABLE="cmux-nightly-macos-${NIGHTLY_BUILD}.dmg"
echo "NIGHTLY_DMG_IMMUTABLE=${NIGHTLY_DMG_IMMUTABLE}" >> "$GITHUB_ENV"
{
echo "NIGHTLY_BUILD=${NIGHTLY_BUILD}"
echo "NIGHTLY_MARKETING_VERSION=${NIGHTLY_MARKETING_VERSION}"
echo "NIGHTLY_REMOTE_DAEMON_VERSION=${NIGHTLY_MARKETING_VERSION}"
echo "NIGHTLY_DMG_IMMUTABLE=${NIGHTLY_DMG_IMMUTABLE}"
} >> "$GITHUB_ENV"

prepare_variant() {
local app_dir="$1"
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/perf-activation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/test-depot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -194,4 +199,4 @@ jobs:
-disableAutomaticPackageResolution \
-destination "platform=macOS" \
-maximum-test-execution-time-allowance "$TEST_TIMEOUT" \
$ONLY_TESTING test
"$ONLY_TESTING" test
15 changes: 12 additions & 3 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: E2E test with video recording
run-name: ${{ inputs.test_filter }} on ${{ inputs.runner || 'depot-macos-latest' }} @ ${{ inputs.ref || github.ref_name }}

on:
workflow_dispatch:
Expand Down Expand Up @@ -28,6 +29,10 @@ on:
- depot-macos-latest
- depot-macos-14

concurrency:
group: e2e-${{ inputs.runner || 'depot-macos-latest' }}-${{ inputs.ref || github.ref_name }}-${{ inputs.test_filter }}
cancel-in-progress: true
Comment on lines +32 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The concurrency group key mixes two different ref formats depending on whether inputs.ref is provided. When left blank (default ""), the fallback is github.ref which produces a full path like refs/heads/main. When a user fills in inputs.ref with a short name (main), the key becomes e2e-<runner>-main-<filter> — a different string than e2e-<runner>-refs/heads/main-<filter>. Two runs targeting the same branch but one with an explicit ref and one with the default would land in separate concurrency groups and not cancel each other, defeating the de-duplication goal.

Suggested change
concurrency:
group: e2e-${{ inputs.runner || 'blacksmith-6vcpu-macos-15' }}-${{ inputs.ref || github.ref }}-${{ inputs.test_filter }}
cancel-in-progress: true
concurrency:
group: e2e-${{ inputs.runner || 'blacksmith-6vcpu-macos-15' }}-${{ inputs.ref || github.ref_name }}-${{ inputs.test_filter }}
cancel-in-progress: true


jobs:
e2e:
runs-on: ${{ inputs.runner || 'depot-macos-latest' }}
Expand All @@ -51,7 +56,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down Expand Up @@ -133,7 +143,6 @@ jobs:

- name: Grant TCC screen recording permission
if: ${{ inputs.record_video }}
continue-on-error: true
run: |
FFMPEG_BIN="${FFMPEG_PATH:-/opt/homebrew/bin/ffmpeg}"

Expand Down Expand Up @@ -254,7 +263,7 @@ jobs:
-disableAutomaticPackageResolution
-destination "platform=macOS"
-maximum-test-execution-time-allowance "$TEST_TIMEOUT"
$ONLY_TESTING
"$ONLY_TESTING"
test
)

Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/tmux-corpus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ jobs:
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | sort | tail -n 1 || true)"
XCODE_APP="$(
find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \
| sort \
| tail -n 1 \
|| true
)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
Expand Down
71 changes: 71 additions & 0 deletions tests/test_ci_self_hosted_guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
CI_FILE="$ROOT_DIR/.github/workflows/ci.yml"
GHOSTTYKIT_FILE="$ROOT_DIR/.github/workflows/build-ghosttykit.yml"
COMPAT_FILE="$ROOT_DIR/.github/workflows/ci-macos-compat.yml"
E2E_FILE="$ROOT_DIR/.github/workflows/test-e2e.yml"

check_warp_runner() {
local file="$1" job="$2"
Expand All @@ -25,6 +26,69 @@ check_warp_runner() {
echo "PASS: $job WarpBuild runner is present"
}

check_e2e_runner_fallbacks() {
if ! awk '
/^run-name:/ {
saw_run_name=1
if ($0 ~ /inputs\.test_filter/ && ($0 ~ /inputs\.runner/ || $0 ~ /depot-macos-latest/) && ($0 ~ /inputs\.ref/ || $0 ~ /github\.ref_name/)) {
saw_run_name_dynamic=1
}
}
/^concurrency:/ { in_concurrency=1; next }
in_concurrency && /^jobs:/ { in_concurrency=0 }
in_concurrency && /cancel-in-progress:[[:space:]]*true/ { saw_cancel=1 }
in_concurrency && (/inputs\.runner/ || /depot-macos-latest/) { saw_runner=1 }
in_concurrency && /inputs\.test_filter/ { saw_test_filter=1 }
in_concurrency && /github\.ref_name/ { saw_ref_name=1 }
END { exit !(saw_run_name && saw_run_name_dynamic && saw_cancel && saw_runner && saw_test_filter && saw_ref_name) }
' "$E2E_FILE"; then
echo "FAIL: test-e2e.yml must dynamically name runs and cancel duplicate queued E2E jobs by runner, normalized ref, and test filter"
exit 1
fi

for label in depot-macos-latest depot-macos-14; do
if ! grep -Eq "^[[:space:]]+- ${label}$" "$E2E_FILE"; then
echo "FAIL: test-e2e.yml must expose runner option ${label}"
exit 1
fi
done

if grep -Eq "^[[:space:]]*continue-on-error:" "$E2E_FILE"; then
echo "FAIL: test-e2e.yml must not mask E2E setup or test failures with continue-on-error"
exit 1
fi

echo "PASS: test-e2e.yml exposes Depot runner choices and duplicate-queue cancellation"
}

check_xcode_selection() {
if grep -R -n "ls -d /Applications/Xcode" "$ROOT_DIR/.github/workflows"; then
echo "FAIL: workflow Xcode selection must use find/sort/tail fallback, not ls/glob ordering"
exit 1
fi

echo "PASS: workflow Xcode selection avoids ls/glob ordering"
}

check_release_build_signal() {
if ! grep -Fq 'lipo "$APP_BINARY" -verify_arch arm64 x86_64' "$CI_FILE"; then
echo "FAIL: release-build must verify the Release app binary stays universal"
exit 1
fi

if ! grep -Fq 'lipo "$CLI_BINARY" -verify_arch arm64 x86_64' "$CI_FILE"; then
echo "FAIL: release-build must verify the bundled CLI stays universal"
exit 1
fi

if ! grep -Fq 'lipo "$HELPER_BINARY" -verify_arch arm64 x86_64' "$CI_FILE"; then
echo "FAIL: release-build must verify the bundled Ghostty helper stays universal"
exit 1
fi

echo "PASS: release-build keeps universal artifact verification"
}

# ci.yml jobs
check_warp_runner "$CI_FILE" "tests"
check_warp_runner "$CI_FILE" "tests-build-and-lag"
Expand All @@ -36,3 +100,10 @@ check_warp_runner "$GHOSTTYKIT_FILE" "build-ghosttykit"

# ci-macos-compat.yml (uses matrix.os with WarpBuild runners)
check_warp_runner "$COMPAT_FILE" "compat-tests"

# test-e2e.yml is manual, so keep the Depot GUI runner choices but cancel
# duplicate queued runs for the same ref/filter/runner.
check_e2e_runner_fallbacks

check_xcode_selection
check_release_build_signal
Loading