From ca25a57bd0b479b15197b729ddcb6973e103812a Mon Sep 17 00:00:00 2001 From: NuPlay <73557895+NuPlay@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:59:25 +0900 Subject: [PATCH] ci: fix flaky Xcode 26 selection killed by SIGPIPE under pipefail The Xcode selection loop tested each candidate with: xcodebuild -version | grep -q '^Xcode 26' under `set -euo pipefail`. `grep -q` exits the moment it matches, xcodebuild then takes SIGPIPE while it is still writing, dies with an uncaught NSFileHandleOperationException ("Broken pipe", Abort trap: 6), and `pipefail` propagates that as the exit status of the whole pipeline. The condition therefore evaluated false for the candidate that actually *was* Xcode 26, the loop fell through every entry, and the job failed with "Xcode 26 is required for this CI job." Whether xcodebuild finished writing before grep closed the pipe is a race, so this only failed intermittently: of four pull request runs on the same day, two passed with no abort trap and two failed with four abort traps each. Capture the version output into a variable and match it with `case` instead, so no pipe exists and nothing can be signalled mid-write. --- .github/workflows/ci.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ef5313..dfb728f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,10 +37,24 @@ jobs: /Applications/Xcode_26.0.1.app \ /Applications/Xcode.app do - if [ -d "${candidate}" ] && DEVELOPER_DIR="${candidate}/Contents/Developer" xcodebuild -version | grep -q '^Xcode 26'; then - XCODE_APP="${candidate}" - break - fi + [ -d "${candidate}" ] || continue + + # Do not pipe `xcodebuild -version` straight into `grep -q` here. + # `grep -q` exits as soon as it matches, xcodebuild then takes SIGPIPE while + # still writing, dies with an uncaught NSFileHandleOperationException + # ("Broken pipe", Abort trap: 6), and `set -o pipefail` reports the whole + # pipeline as failed - even though the version actually matched. Whether + # xcodebuild finishes writing before grep closes the pipe is a race, so this + # failed intermittently and only ever on the candidate that *was* Xcode 26. + # Capture the output first, then match it without a pipe. + version="$(DEVELOPER_DIR="${candidate}/Contents/Developer" xcodebuild -version 2>/dev/null || true)" + + case "${version}" in + "Xcode 26"*) + XCODE_APP="${candidate}" + break + ;; + esac done if [ -z "${XCODE_APP}" ]; then