From be89812bea9b13a18a614526aacf5497d7fb22d6 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 28 Feb 2026 00:38:46 -0800 Subject: [PATCH] Harden CI unit tests against SwiftPM artifact flakes (#682) --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++-- tests/test_ci_unit_test_spm_retry.sh | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 tests/test_ci_unit_test_spm_retry.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da2d147c..21ff4e40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,9 @@ jobs: - name: Validate create-dmg version pinning run: ./tests/test_ci_create_dmg_pinned.sh + - name: Validate unit-test SwiftPM retry guard + run: ./tests/test_ci_unit_test_spm_retry.sh + web-typecheck: runs-on: ubuntu-latest defaults: @@ -96,13 +99,31 @@ jobs: - name: Run unit tests run: | set -euo pipefail + run_unit_tests() { + xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + -destination "platform=macOS" test 2>&1 + } + # xcodebuild exits 65 even for expected failures (XCTExpectFailure). # Capture output and fail only if there are unexpected failures. set +e - OUTPUT=$(xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ - -destination "platform=macOS" test 2>&1) + OUTPUT=$(run_unit_tests) EXIT_CODE=$? set -e + + # SwiftPM binary artifact resolution can occasionally fail on self-hosted + # runners with "Could not resolve package dependencies". Retry once after + # clearing SwiftPM/DerivedData caches to recover from transient corruption. + if [ "$EXIT_CODE" -ne 0 ] && echo "$OUTPUT" | grep -q "Could not resolve package dependencies"; then + echo "SwiftPM package resolution failed, clearing caches and retrying once" + rm -rf ~/Library/Caches/org.swift.swiftpm + rm -rf ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-* + set +e + OUTPUT=$(run_unit_tests) + EXIT_CODE=$? + set -e + fi + echo "$OUTPUT" if [ "$EXIT_CODE" -ne 0 ]; then SUMMARY=$(echo "$OUTPUT" | grep "Executed.*tests.*with.*failures" | tail -1) diff --git a/tests/test_ci_unit_test_spm_retry.sh b/tests/test_ci_unit_test_spm_retry.sh new file mode 100755 index 00000000..8888a645 --- /dev/null +++ b/tests/test_ci_unit_test_spm_retry.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Regression test for CI unit-test SwiftPM dependency flake handling. +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +WORKFLOW_FILE="$ROOT_DIR/.github/workflows/ci.yml" + +REQUIRED_PATTERNS=( + "run_unit_tests()" + "Could not resolve package dependencies" + "rm -rf ~/Library/Caches/org.swift.swiftpm" + "OUTPUT=\$(run_unit_tests)" +) + +for pattern in "${REQUIRED_PATTERNS[@]}"; do + if ! grep -Fq "$pattern" "$WORKFLOW_FILE"; then + echo "FAIL: Missing pattern in ci.yml: $pattern" + exit 1 + fi +done + +echo "PASS: CI unit-test SwiftPM retry guard is present"