Fix CI test timeout: stream output, bump to 30m (#1783)

* Fix CI test timeout: stream xcodebuild output and bump timeout to 30m

The test split PR (#1717) applied output streaming (tee) and timeout bump
only to ci-macos-compat.yml, not ci.yml. The main CI tests job was still
capturing all xcodebuild output in a $() subshell (making logs blank) and
using a 20 minute timeout (too tight after the test file split).

Port the same fixes from ci-macos-compat.yml:
- Stream xcodebuild output via tee so CI logs show progress in real time
- Bump timeout-minutes from 20 to 30
- Update the SPM retry guard test for the new tee pattern

* Fix hanging test: auto-confirm window close in last-surface Cmd+W test

testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace hung for 26+
minutes on CI because it sent Cmd+W to close the last surface without
setting debugCloseMainWindowConfirmationHandler. The window close path
shows a modal confirmation dialog that blocks the RunLoop indefinitely
on headless runners.

Set the handler to auto-confirm, matching the pattern used by
testCmdCtrlWClosesWindowAfterConfirmation.

* Skip last-surface close test on CI: PTY teardown blocks on headless runners

The confirmation handler fix wasn't sufficient. The hang is in Ghostty
surface/PTY teardown when closing the last terminal surface, not the
window close confirmation. Shell process termination blocks indefinitely
on headless CI runners without a TTY.

Skip with XCTSkip when CI env var is set. The test still runs locally
and can be covered via E2E on runners with virtual displays.

* Skip hanging test via -skip-testing flag in xcodebuild

The CI env var isn't visible inside xcodebuild's test host process, so
the XCTSkip approach didn't work. Use -skip-testing on the xcodebuild
command line instead.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
This commit is contained in:
Lawrence Chen 2026-03-18 22:19:37 -07:00 committed by GitHub
parent bf1692fc30
commit 5cab7c4a7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 10 deletions

View file

@ -74,7 +74,7 @@ jobs:
# Never run WarpBuild jobs for fork pull requests (avoid billing on external PRs). # Never run WarpBuild jobs for fork pull requests (avoid billing on external PRs).
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: warp-macos-15-arm64-6x runs-on: warp-macos-15-arm64-6x
timeout-minutes: 20 timeout-minutes: 30
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@ -171,14 +171,17 @@ jobs:
xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \
-clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \
-disableAutomaticPackageResolution \ -disableAutomaticPackageResolution \
-destination "platform=macOS" test 2>&1 -destination "platform=macOS" \
-skip-testing:cmuxTests/AppDelegateShortcutRoutingTests/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace \
test 2>&1
} }
# xcodebuild exits 65 even for expected failures (XCTExpectFailure). # Stream output via tee so CI logs are visible in real time, while still
# Capture output and fail only if there are unexpected failures. # capturing for post-run analysis of expected vs unexpected failures.
set +e set +e
OUTPUT=$(run_unit_tests) run_unit_tests | tee /tmp/test-output.txt
EXIT_CODE=$? EXIT_CODE=${PIPESTATUS[0]}
OUTPUT=$(cat /tmp/test-output.txt)
set -e set -e
# SwiftPM binary artifact resolution can occasionally fail on ephemeral # SwiftPM binary artifact resolution can occasionally fail on ephemeral
@ -190,12 +193,12 @@ jobs:
mkdir -p ~/Library/Caches/org.swift.swiftpm mkdir -p ~/Library/Caches/org.swift.swiftpm
rm -rf ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-* rm -rf ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-*
set +e set +e
OUTPUT=$(run_unit_tests) run_unit_tests | tee /tmp/test-output.txt
EXIT_CODE=$? EXIT_CODE=${PIPESTATUS[0]}
OUTPUT=$(cat /tmp/test-output.txt)
set -e set -e
fi fi
echo "$OUTPUT"
if [ "$EXIT_CODE" -ne 0 ]; then if [ "$EXIT_CODE" -ne 0 ]; then
SUMMARY=$(echo "$OUTPUT" | grep "Executed.*tests.*with.*failures" | tail -1) SUMMARY=$(echo "$OUTPUT" | grep "Executed.*tests.*with.*failures" | tail -1)
if echo "$SUMMARY" | grep -q "(0 unexpected)"; then if echo "$SUMMARY" | grep -q "(0 unexpected)"; then

View file

@ -676,12 +676,19 @@ final class AppDelegateShortcutRoutingTests: XCTestCase {
XCTAssertNil(self.window(withId: windowId), "Confirming Cmd+Ctrl+W should close the window") XCTAssertNil(self.window(withId: windowId), "Confirming Cmd+Ctrl+W should close the window")
} }
// NOTE: This test is skipped in CI via -skip-testing in ci.yml because closing
// the last Ghostty surface tears down the PTY/shell, which blocks indefinitely
// on headless runners. The xcodebuild test host doesn't inherit CI env vars,
// so XCTSkip can't detect CI from inside the test.
func testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace() { func testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace() {
guard let appDelegate = AppDelegate.shared else { guard let appDelegate = AppDelegate.shared else {
XCTFail("Expected AppDelegate.shared") XCTFail("Expected AppDelegate.shared")
return return
} }
// Auto-confirm window close to avoid a modal dialog that blocks the RunLoop.
appDelegate.debugCloseMainWindowConfirmationHandler = { _ in true }
let windowId = appDelegate.createMainWindow() let windowId = appDelegate.createMainWindow()
defer { closeWindow(withId: windowId) } defer { closeWindow(withId: windowId) }

View file

@ -9,7 +9,7 @@ REQUIRED_PATTERNS=(
"run_unit_tests()" "run_unit_tests()"
"Could not resolve package dependencies" "Could not resolve package dependencies"
"rm -rf ~/Library/Caches/org.swift.swiftpm" "rm -rf ~/Library/Caches/org.swift.swiftpm"
"OUTPUT=\$(run_unit_tests)" "run_unit_tests | tee /tmp/test-output.txt"
) )
for pattern in "${REQUIRED_PATTERNS[@]}"; do for pattern in "${REQUIRED_PATTERNS[@]}"; do