From 2435474fad764ab1263f89fe5194717a0fa4ee47 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 00:36:25 +0900 Subject: [PATCH 01/57] test: ci v0 --- .github/workflows/build.yml | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ea7c2de --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,75 @@ +name: iOS CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: macos-14 # iOS 16 시뮬레이터 런타임 포함 + steps: + - uses: actions/checkout@v4 + + - name: Select iOS Simulator Runtime (>= 16.0) + id: pick_ios + shell: bash + run: | + set -euo pipefail + + MIN_IOS="16.0" + + # 설치된 iOS Simulator 런타임 버전들 추출 (예: 17.2, 18.0 ...) + # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. + mapfile -t versions < <( + xcrun simctl runtime list 2>/dev/null \ + | grep -Eo 'iOS ([0-9]+(\.[0-9]+)?)' \ + | awk '{print $2}' \ + | sort -V \ + | uniq + ) + + if [ "${#versions[@]}" -eq 0 ]; then + echo "No iOS simulator runtimes detected. Falling back to MIN_IOS=${MIN_IOS}" + chosen="$MIN_IOS" + else + highest="${versions[-1]}" + # chosen = max(MIN_IOS, highest) + if [ "$(printf "%s\n%s\n" "$MIN_IOS" "$highest" | sort -V | tail -n 1)" = "$highest" ]; then + chosen="$highest" + else + chosen="$MIN_IOS" + fi + fi + + echo "Detected runtimes: ${versions[*]:-}" + echo "Chosen iOS runtime version: $chosen" + + echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" + + - name: Build + shell: bash + run: | + set -euo pipefail + IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" + + # 스킴 목록 수집 (가장 짧은 이름 선택) + SCHEMES_JSON=$(xcodebuild -list -json) + mapfile -t SCHEMES < <(python3 -c 'import json, os; data=json.loads(os.environ["SCHEMES_JSON"]); schemes=data.get("workspace", {}).get("schemes") or data.get("project", {}).get("schemes") or []; print("\n".join(schemes))') + + if [ "${#SCHEMES[@]}" -eq 0 ]; then + echo "No schemes found." >&2 + exit 1 + fi + + # 가장 짧은 이름(동일 길이면 사전순) 선택 + SCHEME=$(printf '%s\n' "${SCHEMES[@]}" | awk '{ print length, $0 }' | sort -n -k1,1 -k2,2 | head -n 1 | cut -d" " -f2-) + + echo "Detected schemes: ${SCHEMES[*]}" + echo "Chosen scheme: $SCHEME" + + # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 + xcodebuild \ + -scheme "$SCHEME" \ + -configuration Debug \ + -destination "platform=iOS Simulator,OS=${IOS_VER},name=iPhone 14" \ + build From 9e0c3222bf500b56d6cf8e53d33cbc2279ca1d4c Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 00:40:32 +0900 Subject: [PATCH 02/57] test: ci v1 --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea7c2de..f47456c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,10 @@ jobs: # 설치된 iOS Simulator 런타임 버전들 추출 (예: 17.2, 18.0 ...) # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. - mapfile -t versions < <( + versions=() + while IFS= read -r v; do + versions+=("$v") + done < <( xcrun simctl runtime list 2>/dev/null \ | grep -Eo 'iOS ([0-9]+(\.[0-9]+)?)' \ | awk '{print $2}' \ From 56da50b6a3b11245cc9fe41ca7cd9deceb7dce70 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 00:42:37 +0900 Subject: [PATCH 03/57] test: ci v2 --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f47456c..4ce158b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,8 @@ jobs: echo "No iOS simulator runtimes detected. Falling back to MIN_IOS=${MIN_IOS}" chosen="$MIN_IOS" else - highest="${versions[-1]}" + last_index=$(( ${#versions[@]} - 1 )) + highest="${versions[$last_index]}" # chosen = max(MIN_IOS, highest) if [ "$(printf "%s\n%s\n" "$MIN_IOS" "$highest" | sort -V | tail -n 1)" = "$highest" ]; then chosen="$highest" From c4e38965639441b1214fa78873eb35e2ee965e09 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 00:46:52 +0900 Subject: [PATCH 04/57] test: ci v3 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ce158b..177aba7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: jobs: build: - runs-on: macos-14 # iOS 16 시뮬레이터 런타임 포함 + runs-on: macos-13 # iOS 16 시뮬레이터 런타임 포함 steps: - uses: actions/checkout@v4 From 8ee13966cd0f357b863a16c89b868a6eecb35a49 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 00:49:54 +0900 Subject: [PATCH 05/57] test: ci v4 --- .github/workflows/build.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 177aba7..7c4cfe0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,22 +1,21 @@ name: iOS CI on: - push: pull_request: jobs: build: - runs-on: macos-13 # iOS 16 시뮬레이터 런타임 포함 + runs-on: macos-14 # iOS 17 시뮬레이터 런타임 포함 steps: - uses: actions/checkout@v4 - - name: Select iOS Simulator Runtime (>= 16.0) + - name: Select iOS Simulator Runtime (>= 17.0) id: pick_ios shell: bash run: | set -euo pipefail - MIN_IOS="16.0" + MIN_IOS="17.0" # 설치된 iOS Simulator 런타임 버전들 추출 (예: 17.2, 18.0 ...) # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. @@ -58,7 +57,10 @@ jobs: # 스킴 목록 수집 (가장 짧은 이름 선택) SCHEMES_JSON=$(xcodebuild -list -json) - mapfile -t SCHEMES < <(python3 -c 'import json, os; data=json.loads(os.environ["SCHEMES_JSON"]); schemes=data.get("workspace", {}).get("schemes") or data.get("project", {}).get("schemes") or []; print("\n".join(schemes))') + SCHEMES=() + while IFS= read -r s; do + SCHEMES+=("$s") + done < <(python3 -c 'import json, os; data=json.loads(os.environ["SCHEMES_JSON"]); schemes=data.get("workspace", {}).get("schemes") or data.get("project", {}).get("schemes") or []; print("\n".join(schemes))') if [ "${#SCHEMES[@]}" -eq 0 ]; then echo "No schemes found." >&2 From a60152ab6c7796a1151ba74bbfe1fbf518d4a3c6 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 01:45:50 +0900 Subject: [PATCH 06/57] test: ci v5 --- .github/workflows/build.yml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c4cfe0..1a3a2fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,11 @@ name: iOS CI on: pull_request: +permissions: + contents: read + issues: write + pull-requests: write + jobs: build: runs-on: macos-14 # iOS 17 시뮬레이터 런타임 포함 @@ -31,17 +36,20 @@ jobs: ) if [ "${#versions[@]}" -eq 0 ]; then - echo "No iOS simulator runtimes detected. Falling back to MIN_IOS=${MIN_IOS}" + echo "No iOS simulator runtimes detected." >&2 + exit 1 + fi + + # MIN_IOS 이상 중 가장 낮은 버전 선택 (우선 MIN_IOS) + if printf '%s\n' "${versions[@]}" | grep -qx "$MIN_IOS"; then chosen="$MIN_IOS" else - last_index=$(( ${#versions[@]} - 1 )) - highest="${versions[$last_index]}" - # chosen = max(MIN_IOS, highest) - if [ "$(printf "%s\n%s\n" "$MIN_IOS" "$highest" | sort -V | tail -n 1)" = "$highest" ]; then - chosen="$highest" - else - chosen="$MIN_IOS" - fi + chosen=$(printf '%s\n' "${versions[@]}" | awk -v min="$MIN_IOS" '$0+0 >= min+0 {print}' | sort -V | head -n 1 || true) + fi + + if [ -z "${chosen:-}" ]; then + echo "No iOS runtime >= ${MIN_IOS} found. Available: ${versions[*]}" >&2 + exit 1 fi echo "Detected runtimes: ${versions[*]:-}" From 676f29f6ecfe240a7820c9e10879086ec5828f63 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 01:47:59 +0900 Subject: [PATCH 07/57] test: ci v6 --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a3a2fc..d467c1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,11 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest + - name: Select iOS Simulator Runtime (>= 17.0) id: pick_ios shell: bash From d3406cb2b39260c49b1880d11fc73441022cd033 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 01:54:44 +0900 Subject: [PATCH 08/57] test: ci v7 --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d467c1c..dde639f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,8 +66,11 @@ jobs: shell: bash run: | set -euo pipefail + set -x IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" + xcodebuild -version + # 스킴 목록 수집 (가장 짧은 이름 선택) SCHEMES_JSON=$(xcodebuild -list -json) SCHEMES=() @@ -87,8 +90,11 @@ jobs: echo "Chosen scheme: $SCHEME" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 + set -o pipefail xcodebuild \ -scheme "$SCHEME" \ -configuration Debug \ -destination "platform=iOS Simulator,OS=${IOS_VER},name=iPhone 14" \ - build + -showBuildTimingSummary \ + build \ + | cat From cc3c16add74f84aea1621012b33878e802c0f8fc Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 01:56:39 +0900 Subject: [PATCH 09/57] test: ci v8 --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dde639f..357dc0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,9 +70,10 @@ jobs: IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" xcodebuild -version + ls -la # 스킴 목록 수집 (가장 짧은 이름 선택) - SCHEMES_JSON=$(xcodebuild -list -json) + SCHEMES_JSON=$(xcodebuild -list -json -project DevLog.xcodeproj) SCHEMES=() while IFS= read -r s; do SCHEMES+=("$s") From e515821493b699b5e5dcdb3180927cec839f7413 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 01:58:49 +0900 Subject: [PATCH 10/57] Update build.yml --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 357dc0f..a8750bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,7 +73,9 @@ jobs: ls -la # 스킴 목록 수집 (가장 짧은 이름 선택) - SCHEMES_JSON=$(xcodebuild -list -json -project DevLog.xcodeproj) + # 스킴 조회 시 SPM 해상도로 멈추는 경우가 있어 비활성화 + SCHEMES_JSON=$(XCODEBUILD_DISABLE_PACKAGE_RESOLUTION=YES \ + xcodebuild -list -json -project DevLog.xcodeproj -disableAutomaticPackageResolution) SCHEMES=() while IFS= read -r s; do SCHEMES+=("$s") From 3a118d8090fd329ccb9071cf7a79630236035931 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:01:17 +0900 Subject: [PATCH 11/57] Update build.yml --- .github/workflows/build.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8750bf..558e4e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,13 +73,20 @@ jobs: ls -la # 스킴 목록 수집 (가장 짧은 이름 선택) - # 스킴 조회 시 SPM 해상도로 멈추는 경우가 있어 비활성화 - SCHEMES_JSON=$(XCODEBUILD_DISABLE_PACKAGE_RESOLUTION=YES \ - xcodebuild -list -json -project DevLog.xcodeproj -disableAutomaticPackageResolution) + # 스킴 조회 시 -json 단계에서 멈추는 경우가 있어 텍스트 출력으로 파싱 + echo "Listing schemes..." + SCHEMES_RAW=$(XCODEBUILD_DISABLE_PACKAGE_RESOLUTION=YES \ + xcodebuild -list -project DevLog.xcodeproj -disableAutomaticPackageResolution -skipPackageUpdates) + echo "Finished listing schemes." + SCHEMES=() while IFS= read -r s; do - SCHEMES+=("$s") - done < <(python3 -c 'import json, os; data=json.loads(os.environ["SCHEMES_JSON"]); schemes=data.get("workspace", {}).get("schemes") or data.get("project", {}).get("schemes") or []; print("\n".join(schemes))') + [ -n "$s" ] && SCHEMES+=("$s") + done < <(printf '%s\n' "$SCHEMES_RAW" | awk ' + /^Schemes:/ {in_schemes=1; next} + in_schemes && NF==0 {exit} + in_schemes {sub(/^\s+/, ""); print} + ') if [ "${#SCHEMES[@]}" -eq 0 ]; then echo "No schemes found." >&2 From b0db5eec259854a7539c13e22a10ad4be8628e2c Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:07:39 +0900 Subject: [PATCH 12/57] Update build.yml --- .github/workflows/build.yml | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 558e4e2..a9cde27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,9 @@ name: iOS CI on: pull_request: +env: + SCHEME: DevLog + permissions: contents: read issues: write @@ -72,32 +75,7 @@ jobs: xcodebuild -version ls -la - # 스킴 목록 수집 (가장 짧은 이름 선택) - # 스킴 조회 시 -json 단계에서 멈추는 경우가 있어 텍스트 출력으로 파싱 - echo "Listing schemes..." - SCHEMES_RAW=$(XCODEBUILD_DISABLE_PACKAGE_RESOLUTION=YES \ - xcodebuild -list -project DevLog.xcodeproj -disableAutomaticPackageResolution -skipPackageUpdates) - echo "Finished listing schemes." - - SCHEMES=() - while IFS= read -r s; do - [ -n "$s" ] && SCHEMES+=("$s") - done < <(printf '%s\n' "$SCHEMES_RAW" | awk ' - /^Schemes:/ {in_schemes=1; next} - in_schemes && NF==0 {exit} - in_schemes {sub(/^\s+/, ""); print} - ') - - if [ "${#SCHEMES[@]}" -eq 0 ]; then - echo "No schemes found." >&2 - exit 1 - fi - - # 가장 짧은 이름(동일 길이면 사전순) 선택 - SCHEME=$(printf '%s\n' "${SCHEMES[@]}" | awk '{ print length, $0 }' | sort -n -k1,1 -k2,2 | head -n 1 | cut -d" " -f2-) - - echo "Detected schemes: ${SCHEMES[*]}" - echo "Chosen scheme: $SCHEME" + echo "Using scheme: $SCHEME" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 set -o pipefail From 9ace23cf68f4b378dd2e63326fd78f4c92b33f22 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:13:23 +0900 Subject: [PATCH 13/57] Update build.yml --- .github/workflows/build.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9cde27..cce22e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,16 +73,34 @@ jobs: IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" xcodebuild -version - ls -la echo "Using scheme: $SCHEME" + # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 + DEVICE_NAME=$(xcrun simctl list devices available | awk -v ver="$IOS_VER" ' + $0 ~ "^== iOS " ver " ==" {in=1; next} + $0 ~ "^== " {in=0} + in && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { + sub(/^[[:space:]]+/, "", $0) + gsub(/ \(.*/, "", $0) + print $0 + exit + } + ') + + if [ -z "${DEVICE_NAME:-}" ]; then + echo "No available simulator device found for iOS ${IOS_VER}." >&2 + exit 1 + fi + + echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" + # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 set -o pipefail xcodebuild \ -scheme "$SCHEME" \ -configuration Debug \ - -destination "platform=iOS Simulator,OS=${IOS_VER},name=iPhone 14" \ + -destination "platform=iOS Simulator,OS=${IOS_VER},name=${DEVICE_NAME}" \ -showBuildTimingSummary \ build \ | cat From 23e50023bad159f2c3ce8d9fe648c8a843814a45 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:14:42 +0900 Subject: [PATCH 14/57] Update build.yml --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cce22e7..5f275d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,9 +78,9 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 DEVICE_NAME=$(xcrun simctl list devices available | awk -v ver="$IOS_VER" ' - $0 ~ "^== iOS " ver " ==" {in=1; next} - $0 ~ "^== " {in=0} - in && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { + $0 ~ "^== iOS " ver " ==" {flag=1; next} + $0 ~ "^== " {flag=0} + flag && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { sub(/^[[:space:]]+/, "", $0) gsub(/ \(.*/, "", $0) print $0 From b0b1a1b8f944224e9d6c362eb7067c2a91e09891 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:18:22 +0900 Subject: [PATCH 15/57] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f275d2..4f686f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 DEVICE_NAME=$(xcrun simctl list devices available | awk -v ver="$IOS_VER" ' - $0 ~ "^== iOS " ver " ==" {flag=1; next} + $0 ~ "^== iOS " ver "(\\.[0-9]+)? ==" {flag=1; next} $0 ~ "^== " {flag=0} flag && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { sub(/^[[:space:]]+/, "", $0) From 30d007aa05bd3fbd54b96f16b465b0895941353c Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:19:25 +0900 Subject: [PATCH 16/57] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f686f8..881af16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 DEVICE_NAME=$(xcrun simctl list devices available | awk -v ver="$IOS_VER" ' - $0 ~ "^== iOS " ver "(\\.[0-9]+)? ==" {flag=1; next} + $0 ~ "^== iOS " ver "(\\.[0-9]+)* ==" {flag=1; next} $0 ~ "^== " {flag=0} flag && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { sub(/^[[:space:]]+/, "", $0) From 12fd48ba09809ed7aad01cd1f0dc993b1d3ee335 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:21:28 +0900 Subject: [PATCH 17/57] Update build.yml --- .github/workflows/build.yml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 881af16..8fffe77 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,17 +76,23 @@ jobs: echo "Using scheme: $SCHEME" - # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 - DEVICE_NAME=$(xcrun simctl list devices available | awk -v ver="$IOS_VER" ' - $0 ~ "^== iOS " ver "(\\.[0-9]+)* ==" {flag=1; next} - $0 ~ "^== " {flag=0} - flag && $0 ~ /^[[:space:]]/ && $0 ~ /iPhone/ { - sub(/^[[:space:]]+/, "", $0) - gsub(/ \(.*/, "", $0) - print $0 - exit - } - ') + # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) + DEVICE_NAME=$(python3 - <<'PY' + import json, os, subprocess, sys + ios_ver = os.environ['IOS_VER'] + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + j = json.loads(data) + for runtime, devices in j.get("devices", {}).items(): + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + print(d["name"]) + sys.exit(0) + sys.exit(1) + PY + ) if [ -z "${DEVICE_NAME:-}" ]; then echo "No available simulator device found for iOS ${IOS_VER}." >&2 From 52109c0c569421338e726818c57d85966e6760d0 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:22:18 +0900 Subject: [PATCH 18/57] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fffe77..f053bbf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,7 @@ jobs: set -euo pipefail set -x IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" + export IOS_VER xcodebuild -version From 9ed6e2f5748ac100f650baf71856d24c536e6b94 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:27:28 +0900 Subject: [PATCH 19/57] Update build.yml --- .github/workflows/build.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f053bbf..ada02e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: echo "Using scheme: $SCHEME" # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) - DEVICE_NAME=$(python3 - <<'PY' + DEVICE_INFO=$(python3 - <<'PY' import json, os, subprocess, sys ios_ver = os.environ['IOS_VER'] prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" @@ -89,13 +89,18 @@ jobs: continue for d in devices: if d.get("isAvailable") and "iPhone" in d.get("name", ""): - print(d["name"]) + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") sys.exit(0) sys.exit(1) PY ) - if [ -z "${DEVICE_NAME:-}" ]; then + IFS='|' read -r DEVICE_NAME DEVICE_OS <<< "$DEVICE_INFO" + IOS_VER="$DEVICE_OS" + export IOS_VER + + if [ -z "${DEVICE_NAME:-}" ] || [ -z "${IOS_VER:-}" ]; then echo "No available simulator device found for iOS ${IOS_VER}." >&2 exit 1 fi From 773e5cdf53712c2c0c9d05905264a6f66e2f57ca Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:29:51 +0900 Subject: [PATCH 20/57] Update build.yml --- .github/workflows/build.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ada02e4..2a722a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,20 +79,20 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) DEVICE_INFO=$(python3 - <<'PY' - import json, os, subprocess, sys - ios_ver = os.environ['IOS_VER'] - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) - j = json.loads(data) - for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): + import json, os, subprocess, sys + ios_ver = os.environ['IOS_VER'] + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + j = json.loads(data) + for runtime, devices in j.get("devices", {}).items(): + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): os_ver = runtime.split("iOS-")[-1].replace("-", ".") print(f"{d['name']}|{os_ver}") - sys.exit(0) - sys.exit(1) + sys.exit(0) + sys.exit(1) PY ) From 85a5d903dec03e9ea9619ab3475a68994d826614 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:30:30 +0900 Subject: [PATCH 21/57] Update build.yml --- .github/workflows/build.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a722a3..cdf5f43 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,20 +79,20 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) DEVICE_INFO=$(python3 - <<'PY' - import json, os, subprocess, sys - ios_ver = os.environ['IOS_VER'] - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) - j = json.loads(data) - for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) - sys.exit(1) + import json, os, subprocess, sys + ios_ver = os.environ['IOS_VER'] + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + j = json.loads(data) + for runtime, devices in j.get("devices", {}).items(): + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") + sys.exit(0) + sys.exit(1) PY ) From 2b964c2201ed50b9d9cd07eb9e670cc52c467647 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:39:55 +0900 Subject: [PATCH 22/57] Update build.yml --- .github/workflows/build.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cdf5f43..e885477 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,14 +22,12 @@ jobs: with: xcode-version: latest - - name: Select iOS Simulator Runtime (>= 17.0) + - name: Select iOS Simulator Runtime (installed) id: pick_ios shell: bash run: | set -euo pipefail - MIN_IOS="17.0" - # 설치된 iOS Simulator 런타임 버전들 추출 (예: 17.2, 18.0 ...) # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. versions=() @@ -48,17 +46,9 @@ jobs: exit 1 fi - # MIN_IOS 이상 중 가장 낮은 버전 선택 (우선 MIN_IOS) - if printf '%s\n' "${versions[@]}" | grep -qx "$MIN_IOS"; then - chosen="$MIN_IOS" - else - chosen=$(printf '%s\n' "${versions[@]}" | awk -v min="$MIN_IOS" '$0+0 >= min+0 {print}' | sort -V | head -n 1 || true) - fi - - if [ -z "${chosen:-}" ]; then - echo "No iOS runtime >= ${MIN_IOS} found. Available: ${versions[*]}" >&2 - exit 1 - fi + # 실제 존재하는 런타임 중 가장 낮은 버전 선택 + MIN_IOS="${versions[0]}" + chosen="$MIN_IOS" echo "Detected runtimes: ${versions[*]:-}" echo "Chosen iOS runtime version: $chosen" From ff1ffca5b7b7d51dc04f9a0b9bd53967903edbf6 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:43:53 +0900 Subject: [PATCH 23/57] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e885477..eb635cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,7 @@ jobs: versions+=("$v") done < <( xcrun simctl runtime list 2>/dev/null \ - | grep -Eo 'iOS ([0-9]+(\.[0-9]+)?)' \ + | grep -Eo 'iOS ([0-9]+(\.[0-9]+)*)' \ | awk '{print $2}' \ | sort -V \ | uniq From 1cb2499d1da8ccf9d54e9fd04fb3c9c6a434715a Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:49:17 +0900 Subject: [PATCH 24/57] Update build.yml --- .github/workflows/build.yml | 47 ++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb635cb..a308e62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,30 +28,39 @@ jobs: run: | set -euo pipefail - # 설치된 iOS Simulator 런타임 버전들 추출 (예: 17.2, 18.0 ...) - # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. - versions=() - while IFS= read -r v; do - versions+=("$v") - done < <( - xcrun simctl runtime list 2>/dev/null \ - | grep -Eo 'iOS ([0-9]+(\.[0-9]+)*)' \ - | awk '{print $2}' \ - | sort -V \ - | uniq + # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 + chosen=$(python3 - <<'PY' + import json, subprocess + + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + j = json.loads(data) + + versions = [] + for runtime, devices in j.get("devices", {}).items(): + if "iOS-" not in runtime: + continue + # iPhone 디바이스가 있는 런타임만 + if not any(d.get("isAvailable") and "iPhone" in d.get("name", "") for d in devices): + continue + ver = runtime.split("iOS-")[-1].replace("-", ".") + versions.append(ver) + + def ver_key(v): + return [int(x) for x in v.split(".")] + + versions = sorted(set(versions), key=ver_key) + if not versions: + raise SystemExit(1) + print(versions[0]) + PY ) - if [ "${#versions[@]}" -eq 0 ]; then - echo "No iOS simulator runtimes detected." >&2 + if [ -z "${chosen:-}" ]; then + echo "No iPhone simulator runtimes detected." >&2 exit 1 fi - # 실제 존재하는 런타임 중 가장 낮은 버전 선택 - MIN_IOS="${versions[0]}" - chosen="$MIN_IOS" - - echo "Detected runtimes: ${versions[*]:-}" - echo "Chosen iOS runtime version: $chosen" + echo "Chosen iOS runtime version (iPhone): $chosen" echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" From 9047e1c23c8e01b644163814a964f1c009818133 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:51:46 +0900 Subject: [PATCH 25/57] Update build.yml --- .github/workflows/build.yml | 61 ++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a308e62..784d2c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,32 +28,36 @@ jobs: run: | set -euo pipefail - # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 - chosen=$(python3 - <<'PY' - import json, subprocess + # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) + chosen=$(python3 - <<'PY' + import json, re, subprocess - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) - j = json.loads(data) + runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) + versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) + def ver_key(v): + return [int(x) for x in v.split(".")] + versions = sorted(set(versions), key=ver_key) - versions = [] - for runtime, devices in j.get("devices", {}).items(): - if "iOS-" not in runtime: - continue - # iPhone 디바이스가 있는 런타임만 - if not any(d.get("isAvailable") and "iPhone" in d.get("name", "") for d in devices): - continue - ver = runtime.split("iOS-")[-1].replace("-", ".") - versions.append(ver) + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True)).get("devices", {}) - def ver_key(v): - return [int(x) for x in v.split(".")] + def has_iphone(version): + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" + for runtime, devs in devices.items(): + if not runtime.startswith(prefix): + continue + for d in devs: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + return True + return False - versions = sorted(set(versions), key=ver_key) - if not versions: - raise SystemExit(1) - print(versions[0]) + for v in versions: + if has_iphone(v): + print(v) + raise SystemExit(0) + + raise SystemExit(1) PY - ) + ) if [ -z "${chosen:-}" ]; then echo "No iPhone simulator runtimes detected." >&2 @@ -79,18 +83,19 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) DEVICE_INFO=$(python3 - <<'PY' import json, os, subprocess, sys + ios_ver = os.environ['IOS_VER'] prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) j = json.loads(data) for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") + sys.exit(0) sys.exit(1) PY ) From dc05757be909ac5fb86a66033b5bba1c54db2e5d Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 02:59:44 +0900 Subject: [PATCH 26/57] Update build.yml --- .github/workflows/build.yml | 68 +++++++++++++++---------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 784d2c6..e02d262 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,43 +28,30 @@ jobs: run: | set -euo pipefail - # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) - chosen=$(python3 - <<'PY' - import json, re, subprocess - - runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) - versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) - def ver_key(v): - return [int(x) for x in v.split(".")] - versions = sorted(set(versions), key=ver_key) - - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True)).get("devices", {}) - - def has_iphone(version): - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" - for runtime, devs in devices.items(): - if not runtime.startswith(prefix): - continue - for d in devs: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - return True - return False - - for v in versions: - if has_iphone(v): - print(v) - raise SystemExit(0) - - raise SystemExit(1) - PY - ) + # 설치된 iOS Simulator 런타임 버전들 추출 + # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. + versions=() + while IFS= read -r v; do + versions+=("$v") + done < <( + xcrun simctl runtime list 2>/dev/null \ + | grep -Eo 'iOS ([0-9]+(\.[0-9]+)*)' \ + | awk '{print $2}' \ + | sort -V \ + | uniq + ) - if [ -z "${chosen:-}" ]; then - echo "No iPhone simulator runtimes detected." >&2 + if [ "${#versions[@]}" -eq 0 ]; then + echo "No iOS simulator runtimes detected." >&2 exit 1 fi - echo "Chosen iOS runtime version (iPhone): $chosen" + # 실제 존재하는 런타임 중 가장 낮은 버전 선택 + MIN_IOS="${versions[0]}" + chosen="$MIN_IOS" + + echo "Detected runtimes: ${versions[*]:-}" + echo "Chosen iOS runtime version: $chosen" echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" @@ -83,19 +70,18 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) DEVICE_INFO=$(python3 - <<'PY' import json, os, subprocess, sys - ios_ver = os.environ['IOS_VER'] prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) j = json.loads(data) for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") + sys.exit(0) sys.exit(1) PY ) From 35d5ff9c72eb3b7bbca85e86ef53963c6496e73a Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:03:42 +0900 Subject: [PATCH 27/57] Update build.yml --- .github/workflows/build.yml | 77 ++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e02d262..36c148f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,30 +28,48 @@ jobs: run: | set -euo pipefail - # 설치된 iOS Simulator 런타임 버전들 추출 - # 출력 포맷이 달라질 수 있어, "iOS " 패턴에서 숫자만 뽑습니다. - versions=() - while IFS= read -r v; do - versions+=("$v") - done < <( - xcrun simctl runtime list 2>/dev/null \ - | grep -Eo 'iOS ([0-9]+(\.[0-9]+)*)' \ - | awk '{print $2}' \ - | sort -V \ - | uniq + # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) + chosen=$(python3 - <<'PY' + import json, re, subprocess + + runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) + versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) + + def ver_key(v): + return [int(x) for x in v.split(".")] + + versions = sorted(set(versions), key=ver_key) + + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + + def is_available(d): + return d.get("isAvailable") or d.get("availability") == "(available)" + + def has_iphone(version): + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" + for runtime, devs in devices.items(): + if not runtime.startswith(prefix): + continue + for d in devs: + if is_available(d) and "iPhone" in d.get("name", ""): + return True + return False + + for v in versions: + if has_iphone(v): + print(v) + raise SystemExit(0) + + raise SystemExit(1) + PY ) - if [ "${#versions[@]}" -eq 0 ]; then - echo "No iOS simulator runtimes detected." >&2 + if [ -z "${chosen:-}" ]; then + echo "No iPhone simulator runtimes detected." >&2 exit 1 fi - # 실제 존재하는 런타임 중 가장 낮은 버전 선택 - MIN_IOS="${versions[0]}" - chosen="$MIN_IOS" - - echo "Detected runtimes: ${versions[*]:-}" - echo "Chosen iOS runtime version: $chosen" + echo "Chosen iOS runtime version (iPhone): $chosen" echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" @@ -70,18 +88,23 @@ jobs: # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) DEVICE_INFO=$(python3 - <<'PY' import json, os, subprocess, sys + ios_ver = os.environ['IOS_VER'] prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True) j = json.loads(data) + + def is_available(d): + return d.get("isAvailable") or d.get("availability") == "(available)" + for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) + if not runtime.startswith(prefix): + continue + for d in devices: + if is_available(d) and "iPhone" in d.get("name", ""): + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") + sys.exit(0) sys.exit(1) PY ) From 7e19e1d418c2291ca5117a0a6470fd335b7186aa Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:06:13 +0900 Subject: [PATCH 28/57] Update build.yml --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 36c148f..2ad4939 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,10 @@ jobs: versions = sorted(set(versions), key=ver_key) - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + print("Runtime keys:") + for k in sorted(devices.keys()): + print(f"- {k}") def is_available(d): return d.get("isAvailable") or d.get("availability") == "(available)" From af52365af427f5be735892b163e65997e47b9b07 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:08:01 +0900 Subject: [PATCH 29/57] Update build.yml --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ad4939..35aa5c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,10 +40,10 @@ jobs: versions = sorted(set(versions), key=ver_key) - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) - print("Runtime keys:") - for k in sorted(devices.keys()): - print(f"- {k}") + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + print("Runtime keys:") + for k in sorted(devices.keys()): + print(f"- {k}") def is_available(d): return d.get("isAvailable") or d.get("availability") == "(available)" From fb8ecb1eeeb971d1839ea6e8d6d05db1e34ff92f Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:12:01 +0900 Subject: [PATCH 30/57] Update build.yml --- .github/workflows/build.yml | 116 ++++++++++++++---------------------- 1 file changed, 44 insertions(+), 72 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35aa5c9..ddf311a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,99 +28,71 @@ jobs: run: | set -euo pipefail - # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) - chosen=$(python3 - <<'PY' - import json, re, subprocess - - runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) - versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) - - def ver_key(v): - return [int(x) for x in v.split(".")] - - versions = sorted(set(versions), key=ver_key) + # iOS x.y 형태만 사용하고, iPhone 기기 중 첫 번째 선택 + RESULT=$(python3 - <<'PY' + import json, subprocess, sys devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) - print("Runtime keys:") - for k in sorted(devices.keys()): - print(f"- {k}") def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - def has_iphone(version): - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" - for runtime, devs in devices.items(): - if not runtime.startswith(prefix): - continue - for d in devs: - if is_available(d) and "iPhone" in d.get("name", ""): - return True - return False - - for v in versions: - if has_iphone(v): - print(v) - raise SystemExit(0) - - raise SystemExit(1) + return d.get("isAvailable") or d.get("availability") == "(available)" + + def parse_version(runtime_key): + # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> (17, 2) + if "iOS-" not in runtime_key: + return None + parts = runtime_key.split("iOS-")[-1].split("-") + if len(parts) < 2: + return None + try: + return int(parts[0]), int(parts[1]) + except ValueError: + return None + + runtimes = [] + for runtime_key in devices.keys(): + ver = parse_version(runtime_key) + if ver is None: + continue + runtimes.append((ver, runtime_key)) + + for (_, runtime_key) in sorted(runtimes, key=lambda x: x[0]): + for d in devices.get(runtime_key, []): + if is_available(d) and "iPhone" in d.get("name", ""): + major, minor = parse_version(runtime_key) + version = f"{major}.{minor}" + print(f"{version}|{d['name']}") + sys.exit(0) + + sys.exit(1) PY ) - if [ -z "${chosen:-}" ]; then - echo "No iPhone simulator runtimes detected." >&2 - exit 1 + if [ -z "${RESULT:-}" ]; then + echo "No iPhone simulator devices detected." >&2 + exit 1 fi - echo "Chosen iOS runtime version (iPhone): $chosen" + IFS='|' read -r IOS_VER DEVICE_NAME <<< "$RESULT" + + echo "Chosen iOS runtime version (iPhone): $IOS_VER" + echo "Chosen simulator: $DEVICE_NAME" - echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" + echo "ios_version=$IOS_VER" >> "$GITHUB_OUTPUT" + echo "device_name=$DEVICE_NAME" >> "$GITHUB_OUTPUT" - name: Build shell: bash run: | set -euo pipefail set -x - IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" - export IOS_VER + IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" + DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" xcodebuild -version echo "Using scheme: $SCHEME" - # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) - DEVICE_INFO=$(python3 - <<'PY' - import json, os, subprocess, sys - - ios_ver = os.environ['IOS_VER'] - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True) - j = json.loads(data) - - def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if is_available(d) and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) - sys.exit(1) - PY - ) - - IFS='|' read -r DEVICE_NAME DEVICE_OS <<< "$DEVICE_INFO" - IOS_VER="$DEVICE_OS" - export IOS_VER - - if [ -z "${DEVICE_NAME:-}" ] || [ -z "${IOS_VER:-}" ]; then - echo "No available simulator device found for iOS ${IOS_VER}." >&2 - exit 1 - fi - echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 From c502d7b81890c1eb2aaf8d170797c82fb52f0841 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:16:27 +0900 Subject: [PATCH 31/57] Update build.yml --- .github/workflows/build.yml | 53 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddf311a..de634f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,49 +28,46 @@ jobs: run: | set -euo pipefail - # iOS x.y 형태만 사용하고, iPhone 기기 중 첫 번째 선택 + # OS 버전(마이너 포함) 오름차순으로 정렬 후, iPhone 기기 중 첫 번째 선택 RESULT=$(python3 - <<'PY' import json, subprocess, sys devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" + return d.get("isAvailable") or d.get("availability") == "(available)" def parse_version(runtime_key): - # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> (17, 2) - if "iOS-" not in runtime_key: - return None - parts = runtime_key.split("iOS-")[-1].split("-") - if len(parts) < 2: - return None - try: - return int(parts[0]), int(parts[1]) - except ValueError: - return None + # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> [17, 2] + if "iOS-" not in runtime_key: + return None + parts = runtime_key.split("iOS-")[-1].split("-") + try: + return [int(p) for p in parts] + except ValueError: + return None runtimes = [] for runtime_key in devices.keys(): - ver = parse_version(runtime_key) - if ver is None: - continue - runtimes.append((ver, runtime_key)) - - for (_, runtime_key) in sorted(runtimes, key=lambda x: x[0]): - for d in devices.get(runtime_key, []): - if is_available(d) and "iPhone" in d.get("name", ""): - major, minor = parse_version(runtime_key) - version = f"{major}.{minor}" - print(f"{version}|{d['name']}") - sys.exit(0) + ver = parse_version(runtime_key) + if ver is None: + continue + runtimes.append((ver, runtime_key)) + + for (ver, runtime_key) in sorted(runtimes, key=lambda x: x[0]): + for d in devices.get(runtime_key, []): + if is_available(d) and "iPhone" in d.get("name", ""): + version = ".".join(str(x) for x in ver) + print(f"{version}|{d['name']}") + sys.exit(0) sys.exit(1) PY ) if [ -z "${RESULT:-}" ]; then - echo "No iPhone simulator devices detected." >&2 - exit 1 + echo "No iPhone simulator devices detected." >&2 + exit 1 fi IFS='|' read -r IOS_VER DEVICE_NAME <<< "$RESULT" @@ -86,8 +83,8 @@ jobs: run: | set -euo pipefail set -x - IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" - DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" + IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" + DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" xcodebuild -version From 8e752915dda12af876f41ecf94b31c338fa77829 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:19:17 +0900 Subject: [PATCH 32/57] Update build.yml --- .github/workflows/build.yml | 65 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de634f4..14bdde7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,40 +28,45 @@ jobs: run: | set -euo pipefail - # OS 버전(마이너 포함) 오름차순으로 정렬 후, iPhone 기기 중 첫 번째 선택 + # iPhone 기기들만 모은 뒤, OS 버전(마이너 포함) 오름차순으로 가장 낮은 버전 선택 RESULT=$(python3 - <<'PY' - import json, subprocess, sys + import json, re, subprocess, sys - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + data = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)) + devices = data.get("devices", {}) def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - def parse_version(runtime_key): - # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> [17, 2] - if "iOS-" not in runtime_key: - return None - parts = runtime_key.split("iOS-")[-1].split("-") - try: - return [int(p) for p in parts] - except ValueError: - return None - - runtimes = [] - for runtime_key in devices.keys(): - ver = parse_version(runtime_key) - if ver is None: - continue - runtimes.append((ver, runtime_key)) - - for (ver, runtime_key) in sorted(runtimes, key=lambda x: x[0]): - for d in devices.get(runtime_key, []): - if is_available(d) and "iPhone" in d.get("name", ""): - version = ".".join(str(x) for x in ver) - print(f"{version}|{d['name']}") - sys.exit(0) - - sys.exit(1) + return d.get("isAvailable") or d.get("availability") == "(available)" + + def runtime_to_version(runtime_key): + # runtime 키가 아니라, 내부에서 OS 버전 문자열을 찾기 위해 fallback 사용 + # e.g., com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> 17.2 + m = re.search(r'iOS-([0-9]+(?:-[0-9]+)*)', runtime_key) + if not m: + return None + return m.group(1).replace('-', '.') + + def ver_key(v): + return [int(x) for x in v.split('.')] + + candidates = [] + for runtime_key, devs in devices.items(): + for d in devs: + if not is_available(d): + continue + if "iPhone" not in d.get("name", ""): + continue + ver = runtime_to_version(runtime_key) + if not ver: + continue + candidates.append((ver_key(ver), ver, d["name"])) + + if not candidates: + sys.exit(1) + + candidates.sort(key=lambda x: x[0]) + _, ver, name = candidates[0] + print(f"{ver}|{name}") PY ) From 08e6ff1f42a70df3049146f3d16e6a938cdd945f Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:21:30 +0900 Subject: [PATCH 33/57] Update build.yml --- .github/workflows/build.yml | 51 ++++++++----------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14bdde7..624e070 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,46 +29,17 @@ jobs: set -euo pipefail # iPhone 기기들만 모은 뒤, OS 버전(마이너 포함) 오름차순으로 가장 낮은 버전 선택 - RESULT=$(python3 - <<'PY' - import json, re, subprocess, sys - - data = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)) - devices = data.get("devices", {}) - - def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - def runtime_to_version(runtime_key): - # runtime 키가 아니라, 내부에서 OS 버전 문자열을 찾기 위해 fallback 사용 - # e.g., com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> 17.2 - m = re.search(r'iOS-([0-9]+(?:-[0-9]+)*)', runtime_key) - if not m: - return None - return m.group(1).replace('-', '.') - - def ver_key(v): - return [int(x) for x in v.split('.')] - - candidates = [] - for runtime_key, devs in devices.items(): - for d in devs: - if not is_available(d): - continue - if "iPhone" not in d.get("name", ""): - continue - ver = runtime_to_version(runtime_key) - if not ver: - continue - candidates.append((ver_key(ver), ver, d["name"])) - - if not candidates: - sys.exit(1) - - candidates.sort(key=lambda x: x[0]) - _, ver, name = candidates[0] - print(f"{ver}|{name}") - PY - ) + # 런타임 키를 쓰지 않고 simctl 텍스트 출력에서 버전/기기명 파싱 + RESULT=$(xcrun simctl list devices | awk ' + /^== iOS / {ver=$3; next} + /\(unavailable\)/ {next} + ver && $0 ~ /iPhone/ { + line=$0 + sub(/^[[:space:]]+/, "", line) + sub(/ \(.*/, "", line) + print ver "|" line + } + ' | sort -t'|' -k1,1V | head -n 1) if [ -z "${RESULT:-}" ]; then echo "No iPhone simulator devices detected." >&2 From 75e6d38bfe78a8e06a5ed77f04efb91d8bdc7240 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:25:29 +0900 Subject: [PATCH 34/57] Update build.yml --- .github/workflows/build.yml | 49 ++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 624e070..51d3e19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,18 +28,43 @@ jobs: run: | set -euo pipefail - # iPhone 기기들만 모은 뒤, OS 버전(마이너 포함) 오름차순으로 가장 낮은 버전 선택 - # 런타임 키를 쓰지 않고 simctl 텍스트 출력에서 버전/기기명 파싱 - RESULT=$(xcrun simctl list devices | awk ' - /^== iOS / {ver=$3; next} - /\(unavailable\)/ {next} - ver && $0 ~ /iPhone/ { - line=$0 - sub(/^[[:space:]]+/, "", line) - sub(/ \(.*/, "", line) - print ver "|" line - } - ' | sort -t'|' -k1,1V | head -n 1) + # 런타임 키를 오름차순으로 정렬하고, iPhone 시뮬레이터가 있으면 선택 + RESULT=$(python3 - <<'PY' + import json, subprocess, sys + + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + + def is_available(d): + return d.get("isAvailable") or d.get("availability") == "(available)" + + def parse_runtime(runtime_key): + # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> ([17, 2], "17.2") + if "iOS-" not in runtime_key: + return None + parts = runtime_key.split("iOS-")[-1].split("-") + try: + nums = [int(p) for p in parts] + except ValueError: + return None + return nums, ".".join(str(n) for n in nums) + + runtimes = [] + for key in devices.keys(): + parsed = parse_runtime(key) + if parsed is None: + continue + nums, ver = parsed + runtimes.append((nums, ver, key)) + + for _, ver, key in sorted(runtimes, key=lambda x: x[0]): + for d in devices.get(key, []): + if is_available(d) and "iPhone" in d.get("name", ""): + print(f"{ver}|{d['name']}") + sys.exit(0) + + sys.exit(1) + PY + ) if [ -z "${RESULT:-}" ]; then echo "No iPhone simulator devices detected." >&2 From 5e8d2dd800de4c3472f8663feadd32dea51216fd Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:26:27 +0900 Subject: [PATCH 35/57] Update build.yml --- .github/workflows/build.yml | 66 ++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51d3e19..99e7237 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,39 +30,39 @@ jobs: # 런타임 키를 오름차순으로 정렬하고, iPhone 시뮬레이터가 있으면 선택 RESULT=$(python3 - <<'PY' - import json, subprocess, sys - - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) - - def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - def parse_runtime(runtime_key): - # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> ([17, 2], "17.2") - if "iOS-" not in runtime_key: - return None - parts = runtime_key.split("iOS-")[-1].split("-") - try: - nums = [int(p) for p in parts] - except ValueError: - return None - return nums, ".".join(str(n) for n in nums) - - runtimes = [] - for key in devices.keys(): - parsed = parse_runtime(key) - if parsed is None: - continue - nums, ver = parsed - runtimes.append((nums, ver, key)) - - for _, ver, key in sorted(runtimes, key=lambda x: x[0]): - for d in devices.get(key, []): - if is_available(d) and "iPhone" in d.get("name", ""): - print(f"{ver}|{d['name']}") - sys.exit(0) - - sys.exit(1) + import json, subprocess, sys + + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) + + def is_available(d): + return d.get("isAvailable") or d.get("availability") == "(available)" + + def parse_runtime(runtime_key): + # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> ([17, 2], "17.2") + if "iOS-" not in runtime_key: + return None + parts = runtime_key.split("iOS-")[-1].split("-") + try: + nums = [int(p) for p in parts] + except ValueError: + return None + return nums, ".".join(str(n) for n in nums) + + runtimes = [] + for key in devices.keys(): + parsed = parse_runtime(key) + if parsed is None: + continue + nums, ver = parsed + runtimes.append((nums, ver, key)) + + for _, ver, key in sorted(runtimes, key=lambda x: x[0]): + for d in devices.get(key, []): + if is_available(d) and "iPhone" in d.get("name", ""): + print(f"{ver}|{d['name']}") + sys.exit(0) + + sys.exit(1) PY ) From 82a926442c3d2ec6c70b0251efe2692112b1af80 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:29:37 +0900 Subject: [PATCH 36/57] Update build.yml --- .github/workflows/build.yml | 110 +++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99e7237..aab6675 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,56 +28,45 @@ jobs: run: | set -euo pipefail - # 런타임 키를 오름차순으로 정렬하고, iPhone 시뮬레이터가 있으면 선택 - RESULT=$(python3 - <<'PY' - import json, subprocess, sys - - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "-j"], text=True)).get("devices", {}) - - def is_available(d): - return d.get("isAvailable") or d.get("availability") == "(available)" - - def parse_runtime(runtime_key): - # com.apple.CoreSimulator.SimRuntime.iOS-17-2 -> ([17, 2], "17.2") - if "iOS-" not in runtime_key: - return None - parts = runtime_key.split("iOS-")[-1].split("-") - try: - nums = [int(p) for p in parts] - except ValueError: - return None - return nums, ".".join(str(n) for n in nums) - - runtimes = [] - for key in devices.keys(): - parsed = parse_runtime(key) - if parsed is None: - continue - nums, ver = parsed - runtimes.append((nums, ver, key)) - - for _, ver, key in sorted(runtimes, key=lambda x: x[0]): - for d in devices.get(key, []): - if is_available(d) and "iPhone" in d.get("name", ""): - print(f"{ver}|{d['name']}") - sys.exit(0) - - sys.exit(1) + # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) + chosen=$(python3 - <<'PY' + import json, re, subprocess + + runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) + versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) + def ver_key(v): + return [int(x) for x in v.split(".")] + versions = sorted(set(versions), key=ver_key) + + devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True)).get("devices", {}) + + def has_iphone(version): + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" + for runtime, devs in devices.items(): + if not runtime.startswith(prefix): + continue + for d in devs: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + return True + return False + + for v in versions: + if has_iphone(v): + print(v) + raise SystemExit(0) + + raise SystemExit(1) PY - ) + ) - if [ -z "${RESULT:-}" ]; then - echo "No iPhone simulator devices detected." >&2 + if [ -z "${chosen:-}" ]; then + echo "No iPhone simulator runtimes detected." >&2 exit 1 fi - IFS='|' read -r IOS_VER DEVICE_NAME <<< "$RESULT" + echo "Chosen iOS runtime version (iPhone): $chosen" - echo "Chosen iOS runtime version (iPhone): $IOS_VER" - echo "Chosen simulator: $DEVICE_NAME" - - echo "ios_version=$IOS_VER" >> "$GITHUB_OUTPUT" - echo "device_name=$DEVICE_NAME" >> "$GITHUB_OUTPUT" + echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" - name: Build shell: bash @@ -85,12 +74,41 @@ jobs: set -euo pipefail set -x IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" - DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" + export IOS_VER xcodebuild -version echo "Using scheme: $SCHEME" + # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) + DEVICE_INFO=$(python3 - <<'PY' + import json, os, subprocess, sys + + ios_ver = os.environ['IOS_VER'] + prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" + data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) + j = json.loads(data) + for runtime, devices in j.get("devices", {}).items(): + if not runtime.startswith(prefix): + continue + for d in devices: + if d.get("isAvailable") and "iPhone" in d.get("name", ""): + os_ver = runtime.split("iOS-")[-1].replace("-", ".") + print(f"{d['name']}|{os_ver}") + sys.exit(0) + sys.exit(1) + PY + ) + + IFS='|' read -r DEVICE_NAME DEVICE_OS <<< "$DEVICE_INFO" + IOS_VER="$DEVICE_OS" + export IOS_VER + + if [ -z "${DEVICE_NAME:-}" ] || [ -z "${IOS_VER:-}" ]; then + echo "No available simulator device found for iOS ${IOS_VER}." >&2 + exit 1 + fi + echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 @@ -101,4 +119,4 @@ jobs: -destination "platform=iOS Simulator,OS=${IOS_VER},name=${DEVICE_NAME}" \ -showBuildTimingSummary \ build \ - | cat + | cat \ No newline at end of file From c06721cc5b427c52a0f4f5025d060376c4a90b3f Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:36:13 +0900 Subject: [PATCH 37/57] Update build.yml --- .github/workflows/build.yml | 88 ++++++++++--------------------------- 1 file changed, 22 insertions(+), 66 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aab6675..ccf8ce4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ permissions: jobs: build: - runs-on: macos-14 # iOS 17 시뮬레이터 런타임 포함 + runs-on: macos-latest steps: - uses: actions/checkout@v4 @@ -28,45 +28,30 @@ jobs: run: | set -euo pipefail - # iPhone 시뮬레이터가 존재하는 런타임 중 가장 낮은 버전 선택 (패치 버전 포함) - chosen=$(python3 - <<'PY' - import json, re, subprocess - - runtime_text = subprocess.check_output(["xcrun", "simctl", "runtime", "list"], text=True, stderr=subprocess.DEVNULL) - versions = re.findall(r'iOS ([0-9]+(?:\.[0-9]+)*)', runtime_text) - def ver_key(v): - return [int(x) for x in v.split(".")] - versions = sorted(set(versions), key=ver_key) - - devices = json.loads(subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True)).get("devices", {}) - - def has_iphone(version): - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{version.replace('.', '-') }" - for runtime, devs in devices.items(): - if not runtime.startswith(prefix): - continue - for d in devs: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - return True - return False - - for v in versions: - if has_iphone(v): - print(v) - raise SystemExit(0) - - raise SystemExit(1) - PY - ) - - if [ -z "${chosen:-}" ]; then - echo "No iPhone simulator runtimes detected." >&2 + # simctl 텍스트 헤더 버전 기준으로 최신 iOS 버전의 iPhone 선택 + RESULT=$(xcrun simctl list devices | awk ' + /^== iOS / {ver=$3; next} + /\(unavailable\)/ {next} + ver && $0 ~ /iPhone/ { + line=$0 + sub(/^[[:space:]]+/, "", line) + sub(/ \(.*/, "", line) + print ver "|" line + } + ' | sort -t'|' -k1,1Vr | head -n 1) + + if [ -z "${RESULT:-}" ]; then + echo "No iPhone simulator devices detected." >&2 exit 1 fi - echo "Chosen iOS runtime version (iPhone): $chosen" + IFS='|' read -r IOS_VER DEVICE_NAME <<< "$RESULT" - echo "ios_version=$chosen" >> "$GITHUB_OUTPUT" + echo "Chosen iOS runtime version (iPhone): $IOS_VER" + echo "Chosen simulator: $DEVICE_NAME" + + echo "ios_version=$IOS_VER" >> "$GITHUB_OUTPUT" + echo "device_name=$DEVICE_NAME" >> "$GITHUB_OUTPUT" - name: Build shell: bash @@ -74,41 +59,12 @@ jobs: set -euo pipefail set -x IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" - export IOS_VER + DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" xcodebuild -version echo "Using scheme: $SCHEME" - # 선택된 iOS 런타임에서 'iPhone'이 포함된 첫 번째 시뮬레이터 이름 선택 (JSON 파싱) - DEVICE_INFO=$(python3 - <<'PY' - import json, os, subprocess, sys - - ios_ver = os.environ['IOS_VER'] - prefix = f"com.apple.CoreSimulator.SimRuntime.iOS-{ios_ver.replace('.', '-') }" - data = subprocess.check_output(["xcrun", "simctl", "list", "devices", "available", "-j"], text=True) - j = json.loads(data) - for runtime, devices in j.get("devices", {}).items(): - if not runtime.startswith(prefix): - continue - for d in devices: - if d.get("isAvailable") and "iPhone" in d.get("name", ""): - os_ver = runtime.split("iOS-")[-1].replace("-", ".") - print(f"{d['name']}|{os_ver}") - sys.exit(0) - sys.exit(1) - PY - ) - - IFS='|' read -r DEVICE_NAME DEVICE_OS <<< "$DEVICE_INFO" - IOS_VER="$DEVICE_OS" - export IOS_VER - - if [ -z "${DEVICE_NAME:-}" ] || [ -z "${IOS_VER:-}" ]; then - echo "No available simulator device found for iOS ${IOS_VER}." >&2 - exit 1 - fi - echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 From 4e6aff884bf80b0785df6479e9d71d834264579e Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:42:34 +0900 Subject: [PATCH 38/57] Update build.yml --- .github/workflows/build.yml | 41 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ccf8ce4..b154b8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,16 +29,37 @@ jobs: set -euo pipefail # simctl 텍스트 헤더 버전 기준으로 최신 iOS 버전의 iPhone 선택 - RESULT=$(xcrun simctl list devices | awk ' - /^== iOS / {ver=$3; next} - /\(unavailable\)/ {next} - ver && $0 ~ /iPhone/ { - line=$0 - sub(/^[[:space:]]+/, "", line) - sub(/ \(.*/, "", line) - print ver "|" line - } - ' | sort -t'|' -k1,1Vr | head -n 1) + RESULT=$(python3 - <<'PY' + import re, subprocess, sys + + text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) + + current_ver = None + candidates = [] # (version_tuple, version_str, device_name) + + def ver_key(v): + return tuple(int(x) for x in v.split('.')) + + for line in text.splitlines(): + m = re.match(r"^== iOS ([0-9]+(?:\.[0-9]+)*) ==$", line.strip()) + if m: + current_ver = m.group(1) + continue + if "(unavailable)" in line: + continue + if current_ver and "iPhone" in line: + name = line.strip() + name = re.sub(r" \(.*", "", name) + candidates.append((ver_key(current_ver), current_ver, name)) + + if not candidates: + sys.exit(1) + + candidates.sort(reverse=True) + _, ver, name = candidates[0] + print(f"{ver}|{name}") + PY + ) if [ -z "${RESULT:-}" ]; then echo "No iPhone simulator devices detected." >&2 From 5c8298f0d01c14dada1fb9cc3802e227640c477a Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:45:19 +0900 Subject: [PATCH 39/57] Update build.yml --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b154b8a..4c15964 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,9 @@ jobs: set -euo pipefail # simctl 텍스트 헤더 버전 기준으로 최신 iOS 버전의 iPhone 선택 + echo "== simctl list devices ==" + xcrun simctl list devices + RESULT=$(python3 - <<'PY' import re, subprocess, sys @@ -53,6 +56,7 @@ jobs: candidates.append((ver_key(current_ver), current_ver, name)) if not candidates: + print("No iPhone candidates found", file=sys.stderr) sys.exit(1) candidates.sort(reverse=True) From b13080a07e02553d43ed5aaaffd814b26e2918aa Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:47:58 +0900 Subject: [PATCH 40/57] Update build.yml --- .github/workflows/build.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c15964..2f2a4e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,9 +29,6 @@ jobs: set -euo pipefail # simctl 텍스트 헤더 버전 기준으로 최신 iOS 버전의 iPhone 선택 - echo "== simctl list devices ==" - xcrun simctl list devices - RESULT=$(python3 - <<'PY' import re, subprocess, sys @@ -44,9 +41,9 @@ jobs: return tuple(int(x) for x in v.split('.')) for line in text.splitlines(): - m = re.match(r"^== iOS ([0-9]+(?:\.[0-9]+)*) ==$", line.strip()) - if m: - current_ver = m.group(1) + header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) + if header: + current_ver = header.group(1) continue if "(unavailable)" in line: continue @@ -63,7 +60,7 @@ jobs: _, ver, name = candidates[0] print(f"{ver}|{name}") PY - ) + ) if [ -z "${RESULT:-}" ]; then echo "No iPhone simulator devices detected." >&2 From 515cf174ecd47df7aedc4f7ffff19db204502284 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:56:41 +0900 Subject: [PATCH 41/57] Update build.yml --- .github/workflows/build.yml | 78 ++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f2a4e2..addb5ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,39 +28,55 @@ jobs: run: | set -euo pipefail - # simctl 텍스트 헤더 버전 기준으로 최신 iOS 버전의 iPhone 선택 - RESULT=$(python3 - <<'PY' - import re, subprocess, sys - - text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) - - current_ver = None - candidates = [] # (version_tuple, version_str, device_name) - - def ver_key(v): - return tuple(int(x) for x in v.split('.')) - - for line in text.splitlines(): - header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) - if header: - current_ver = header.group(1) - continue - if "(unavailable)" in line: - continue - if current_ver and "iPhone" in line: - name = line.strip() - name = re.sub(r" \(.*", "", name) - candidates.append((ver_key(current_ver), current_ver, name)) - - if not candidates: - print("No iPhone candidates found", file=sys.stderr) + # macOS 메인 버전에 맞는 iOS 버전 중 최신 버전의 iPhone 선택 + RESULT=$(python3 - <<'PY' + import re, subprocess, sys + + mac_ver = subprocess.check_output(["sw_vers", "-productVersion"], text=True).strip() + mac_major = mac_ver.split('.')[0] + + text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) + lines = text.splitlines() + + def ver_key(v): + return tuple(int(x) for x in v.split('.')) + + # 1) 최신 iOS 버전(해당 mac 메이저) 찾기 + latest_ver = None + for line in lines: + header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) + if not header: + continue + ver = header.group(1) + if not ver.startswith(f"{mac_major}."): + continue + if latest_ver is None or ver_key(ver) > ver_key(latest_ver): + latest_ver = ver + + if latest_ver is None: + print(f"No iOS versions found for macOS major {mac_major}", file=sys.stderr) + sys.exit(1) + + # 2) 해당 버전 섹션에서 첫 iPhone 찾고 즉시 종료 + current_ver = None + for line in lines: + header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) + if header: + current_ver = header.group(1) + continue + if current_ver != latest_ver: + continue + if "(unavailable)" in line: + continue + if "iPhone" in line: + name = line.strip() + print(f"{latest_ver}|{name}") + sys.exit(0) + + print(f"No iPhone candidates found for iOS {latest_ver}", file=sys.stderr) sys.exit(1) - - candidates.sort(reverse=True) - _, ver, name = candidates[0] - print(f"{ver}|{name}") PY - ) + ) if [ -z "${RESULT:-}" ]; then echo "No iPhone simulator devices detected." >&2 From c7aedea19850657e81ca13f0757e6c7b4ee6bdcb Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 03:57:28 +0900 Subject: [PATCH 42/57] Update build.yml --- .github/workflows/build.yml | 92 ++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index addb5ca..f72f991 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,53 +28,53 @@ jobs: run: | set -euo pipefail - # macOS 메인 버전에 맞는 iOS 버전 중 최신 버전의 iPhone 선택 - RESULT=$(python3 - <<'PY' - import re, subprocess, sys - - mac_ver = subprocess.check_output(["sw_vers", "-productVersion"], text=True).strip() - mac_major = mac_ver.split('.')[0] - - text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) - lines = text.splitlines() - - def ver_key(v): - return tuple(int(x) for x in v.split('.')) - - # 1) 최신 iOS 버전(해당 mac 메이저) 찾기 - latest_ver = None - for line in lines: - header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) - if not header: - continue - ver = header.group(1) - if not ver.startswith(f"{mac_major}."): - continue - if latest_ver is None or ver_key(ver) > ver_key(latest_ver): - latest_ver = ver - - if latest_ver is None: - print(f"No iOS versions found for macOS major {mac_major}", file=sys.stderr) - sys.exit(1) - - # 2) 해당 버전 섹션에서 첫 iPhone 찾고 즉시 종료 - current_ver = None - for line in lines: - header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) - if header: - current_ver = header.group(1) - continue - if current_ver != latest_ver: - continue - if "(unavailable)" in line: - continue - if "iPhone" in line: - name = line.strip() - print(f"{latest_ver}|{name}") - sys.exit(0) - - print(f"No iPhone candidates found for iOS {latest_ver}", file=sys.stderr) + # macOS 메인 버전에 맞는 iOS 버전 중 최신 버전의 iPhone 선택 + RESULT=$(python3 - <<'PY' + import re, subprocess, sys + + mac_ver = subprocess.check_output(["sw_vers", "-productVersion"], text=True).strip() + mac_major = mac_ver.split('.')[0] + + text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) + lines = text.splitlines() + + def ver_key(v): + return tuple(int(x) for x in v.split('.')) + + # 1) 최신 iOS 버전(해당 mac 메이저) 찾기 + latest_ver = None + for line in lines: + header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) + if not header: + continue + ver = header.group(1) + if not ver.startswith(f"{mac_major}."): + continue + if latest_ver is None or ver_key(ver) > ver_key(latest_ver): + latest_ver = ver + + if latest_ver is None: + print(f"No iOS versions found for macOS major {mac_major}", file=sys.stderr) sys.exit(1) + + # 2) 해당 버전 섹션에서 첫 iPhone 찾고 즉시 종료 + current_ver = None + for line in lines: + header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) + if header: + current_ver = header.group(1) + continue + if current_ver != latest_ver: + continue + if "(unavailable)" in line: + continue + if "iPhone" in line: + name = line.strip() + print(f"{latest_ver}|{name}") + sys.exit(0) + + print(f"No iPhone candidates found for iOS {latest_ver}", file=sys.stderr) + sys.exit(1) PY ) From ca0cf8c80c0555467f47f6d2e7c69313154c0048 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 04:01:16 +0900 Subject: [PATCH 43/57] Update build.yml --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f72f991..bd43380 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,12 @@ jobs: mac_ver = subprocess.check_output(["sw_vers", "-productVersion"], text=True).strip() mac_major = mac_ver.split('.')[0] + try: + mac_major_num = int(mac_major) + except ValueError: + mac_major_num = None + if mac_major_num is not None and mac_major_num <= 15: + mac_major = "26" text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) lines = text.splitlines() From ab0fe51ed69778ce5814a5f91e861620af4ee982 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 04:11:20 +0900 Subject: [PATCH 44/57] Update build.yml --- .github/workflows/build.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd43380..b5bc445 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,7 +75,20 @@ jobs: if "(unavailable)" in line: continue if "iPhone" in line: - name = line.strip() + raw = line.strip() + # key:value 형태면 딕셔너리로 파싱해서 name만 사용 + if "platform:" in raw and "name:" in raw and "OS:" in raw: + kv = {} + for part in raw.split(","): + if ":" not in part: + continue + k, v = part.split(":", 1) + kv[k.strip()] = v.strip() + name = kv.get("name", raw) + else: + name = raw + # UUID/상태만 제거하고 모델명 괄호는 유지 + name = re.sub(r"\s+\([0-9A-Fa-f-]{36}\)\s+\(.*\)$", "", name) print(f"{latest_ver}|{name}") sys.exit(0) From a212cc25224e2f985b3279c467198ad053d9df9e Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 04:13:36 +0900 Subject: [PATCH 45/57] Update build.yml --- .github/workflows/build.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5bc445..3422e1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,14 +32,14 @@ jobs: RESULT=$(python3 - <<'PY' import re, subprocess, sys - mac_ver = subprocess.check_output(["sw_vers", "-productVersion"], text=True).strip() - mac_major = mac_ver.split('.')[0] + xcode_ver = subprocess.check_output(["xcodebuild", "-version"], text=True).splitlines()[0].strip() + xcode_major = xcode_ver.split()[1].split('.')[0] try: - mac_major_num = int(mac_major) + xcode_major_num = int(xcode_major) except ValueError: - mac_major_num = None - if mac_major_num is not None and mac_major_num <= 15: - mac_major = "26" + xcode_major_num = None + if xcode_major_num is not None and xcode_major_num <= 15: + xcode_major = "26" text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) lines = text.splitlines() @@ -54,13 +54,13 @@ jobs: if not header: continue ver = header.group(1) - if not ver.startswith(f"{mac_major}."): + if not ver.startswith(f"{xcode_major}."): continue if latest_ver is None or ver_key(ver) > ver_key(latest_ver): latest_ver = ver if latest_ver is None: - print(f"No iOS versions found for macOS major {mac_major}", file=sys.stderr) + print(f"No iOS versions found for Xcode major {xcode_major}", file=sys.stderr) sys.exit(1) # 2) 해당 버전 섹션에서 첫 iPhone 찾고 즉시 종료 From 40499741f3d42f51aa9a2bc802096d0c6648d372 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 09:42:29 +0900 Subject: [PATCH 46/57] Update build.yml --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3422e1e..71a4169 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -130,6 +130,8 @@ jobs: -scheme "$SCHEME" \ -configuration Debug \ -destination "platform=iOS Simulator,OS=${IOS_VER},name=${DEVICE_NAME}" \ + -skipPackagePluginValidation \ + -skipMacroValidation \ -showBuildTimingSummary \ build \ | cat \ No newline at end of file From 4190bc2ca82a10f0456810780bbbac937bd06a20 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 10:27:39 +0900 Subject: [PATCH 47/57] =?UTF-8?q?fix:=20ContentView=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Setting/SettingView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/DevLog/UI/Setting/SettingView.swift b/DevLog/UI/Setting/SettingView.swift index 089f195..f2506ce 100644 --- a/DevLog/UI/Setting/SettingView.swift +++ b/DevLog/UI/Setting/SettingView.swift @@ -115,7 +115,6 @@ struct SettingView: View { ) ) case .account: - ContentView(text: "계정 연동 화면") // AccountView(viewModel: viewModel) } } From 028877305668e10bf96f4f90f60c02d3bacc0680 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 10:31:04 +0900 Subject: [PATCH 48/57] =?UTF-8?q?chore:=20TempView=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/App/TempView.swift | 16 ++++++++++++++++ DevLog/UI/Setting/SettingView.swift | 1 + 2 files changed, 17 insertions(+) create mode 100644 DevLog/App/TempView.swift diff --git a/DevLog/App/TempView.swift b/DevLog/App/TempView.swift new file mode 100644 index 0000000..6e171a6 --- /dev/null +++ b/DevLog/App/TempView.swift @@ -0,0 +1,16 @@ +// +// TempView.swift +// DevLog +// +// Created by 최윤진 on 1/31/26. +// + +import SwiftUI + +struct TempView: View { + let text: String + + var body: some View { + Text(text) + } +} diff --git a/DevLog/UI/Setting/SettingView.swift b/DevLog/UI/Setting/SettingView.swift index f2506ce..ae33e8b 100644 --- a/DevLog/UI/Setting/SettingView.swift +++ b/DevLog/UI/Setting/SettingView.swift @@ -115,6 +115,7 @@ struct SettingView: View { ) ) case .account: + TempView(text: "AccountView") // AccountView(viewModel: viewModel) } } From aa4d6e3863d6f5035eadc51a5afd505de4851342 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:00:43 +0900 Subject: [PATCH 49/57] Update build.yml --- .github/workflows/build.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71a4169..0ca7659 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,12 @@ jobs: with: xcode-version: latest + - name: Install xcpretty + shell: bash + run: | + set -euo pipefail + sudo gem install xcpretty + - name: Select iOS Simulator Runtime (installed) id: pick_ios shell: bash @@ -125,6 +131,7 @@ jobs: echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 + mkdir -p build/test-results set -o pipefail xcodebuild \ -scheme "$SCHEME" \ @@ -134,4 +141,11 @@ jobs: -skipMacroValidation \ -showBuildTimingSummary \ build \ - | cat \ No newline at end of file + | tee build.log \ + | xcpretty -r junit -o build/test-results/junit.xml + + - name: Comment build failure on PR + uses: mikepenz/action-junit-report@v4 + if: always() + with: + report_paths: 'build/test-results/junit.xml' From 2f68ab317f24fe39c68234efe07ae0e7899901ad Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:00:47 +0900 Subject: [PATCH 50/57] Update SettingView.swift --- DevLog/UI/Setting/SettingView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DevLog/UI/Setting/SettingView.swift b/DevLog/UI/Setting/SettingView.swift index ae33e8b..35ea619 100644 --- a/DevLog/UI/Setting/SettingView.swift +++ b/DevLog/UI/Setting/SettingView.swift @@ -115,7 +115,7 @@ struct SettingView: View { ) ) case .account: - TempView(text: "AccountView") +// TempView(text: "AccountView") // AccountView(viewModel: viewModel) } } From ae0afeaff62849913c58f6c67d7f3ce2bfc8179f Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:10:36 +0900 Subject: [PATCH 51/57] Update build.yml --- .github/workflows/build.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ca7659..4f991f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -133,6 +133,7 @@ jobs: # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 mkdir -p build/test-results set -o pipefail + set +e xcodebuild \ -scheme "$SCHEME" \ -configuration Debug \ @@ -141,11 +142,18 @@ jobs: -skipMacroValidation \ -showBuildTimingSummary \ build \ - | tee build.log \ - | xcpretty -r junit -o build/test-results/junit.xml + | tee build.log + XC_STATUS=${PIPESTATUS[0]} + set -e + + if [ -f build.log ]; then + xcpretty -r junit -o build/test-results/junit.xml < build.log || true + fi + + exit $XC_STATUS - name: Comment build failure on PR uses: mikepenz/action-junit-report@v4 if: always() with: - report_paths: 'build/test-results/junit.xml' + report_paths: 'build/test-results/junit.xml' \ No newline at end of file From b344516daae0fbe5dba74bc5c7d297e54001c646 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:19:43 +0900 Subject: [PATCH 52/57] Update build.yml --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f991f6..61eec4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,7 @@ permissions: contents: read issues: write pull-requests: write + checks: write jobs: build: @@ -142,7 +143,8 @@ jobs: -skipMacroValidation \ -showBuildTimingSummary \ build \ - | tee build.log + | tee build.log \ + | xcpretty XC_STATUS=${PIPESTATUS[0]} set -e @@ -154,6 +156,6 @@ jobs: - name: Comment build failure on PR uses: mikepenz/action-junit-report@v4 - if: always() + if: always() && github.event.pull_request.head.repo.fork == false with: report_paths: 'build/test-results/junit.xml' \ No newline at end of file From f9b3cb2e1b10d27e4f3a48e61ede73bf27bec04d Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:35:20 +0900 Subject: [PATCH 53/57] Update build.yml --- .github/workflows/build.yml | 69 ++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61eec4d..1480e7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,11 +23,6 @@ jobs: with: xcode-version: latest - - name: Install xcpretty - shell: bash - run: | - set -euo pipefail - sudo gem install xcpretty - name: Select iOS Simulator Runtime (installed) id: pick_ios @@ -132,7 +127,6 @@ jobs: echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 - mkdir -p build/test-results set -o pipefail set +e xcodebuild \ @@ -143,19 +137,70 @@ jobs: -skipMacroValidation \ -showBuildTimingSummary \ build \ - | tee build.log \ - | xcpretty + | tee build.log XC_STATUS=${PIPESTATUS[0]} set -e if [ -f build.log ]; then - xcpretty -r junit -o build/test-results/junit.xml < build.log || true + echo "== error: lines ==" + grep -i "error:" build.log || true fi exit $XC_STATUS - name: Comment build failure on PR - uses: mikepenz/action-junit-report@v4 - if: always() && github.event.pull_request.head.repo.fork == false + if: failure() && github.event.pull_request.head.repo.fork == false + uses: actions/github-script@v7 with: - report_paths: 'build/test-results/junit.xml' \ No newline at end of file + script: | + const fs = require('fs'); + const path = 'build.log'; + let body = '❌ iOS CI build failed.\n\n'; + if (fs.existsSync(path)) { + const log = fs.readFileSync(path, 'utf8'); + const lines = log.split(/\r?\n/); + const errorLines = lines.filter((line) => /error:/i.test(line)); + if (errorLines.length > 0) { + body += "Lines containing 'error:':\n\n```\n" + errorLines.join('\n') + '\n```\n'; + + const repoRoot = process.env.GITHUB_WORKSPACE || process.cwd(); + const pathMod = require('path'); + const snippets = []; + for (const line of errorLines) { + const match = line.match(/^(.*?):(\d+):(\d+):\s+error:/); + if (!match) continue; + const filePath = match[1]; + const lineNum = parseInt(match[2], 10); + const absPath = filePath.startsWith('/') ? filePath : pathMod.join(repoRoot, filePath); + if (!fs.existsSync(absPath)) continue; + const fileLines = fs.readFileSync(absPath, 'utf8').split(/\r?\n/); + const start = Math.max(0, lineNum - 3); + const end = Math.min(fileLines.length, lineNum + 2); + const snippet = fileLines + .slice(start, end) + .map((l, idx) => { + const ln = start + idx + 1; + return `${ln.toString().padStart(4, ' ')}| ${l}`; + }) + .join('\n'); + snippets.push(`File: ${filePath}:${lineNum}\n${snippet}`); + } + if (snippets.length > 0) { + body += "\nCode excerpts:\n\n```\n" + snippets.join('\n\n') + "\n```\n"; + } + } else { + body += "No lines containing 'error:' were found in build.log."; + } + } else { + body += 'build.log not found.'; + } + if (!context.payload.pull_request) { + core.info('No PR context; skipping comment.'); + return; + } + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body + }); \ No newline at end of file From 6a70d32ecf1dc11a6d7d8f3e8b791a6c1ff93fb0 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:45:39 +0900 Subject: [PATCH 54/57] Update build.yml --- .github/workflows/build.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1480e7d..61042c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,7 @@ permissions: jobs: build: runs-on: macos-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v4 @@ -23,6 +24,18 @@ jobs: with: xcode-version: latest + - name: Cache SwiftPM + uses: actions/cache@v4 + with: + path: | + ~/.swiftpm + ~/Library/Caches/org.swift.swiftpm + ~/Library/Developer/Xcode/SourcePackages + .spm + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- + - name: Select iOS Simulator Runtime (installed) id: pick_ios @@ -119,25 +132,35 @@ jobs: set -x IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" + SPM_DIR="$GITHUB_WORKSPACE/.spm" + mkdir -p "$SPM_DIR" xcodebuild -version echo "Using scheme: $SCHEME" echo "Using simulator: $DEVICE_NAME (iOS ${IOS_VER})" - - # 예시: 시뮬레이터 대상으로 빌드/테스트 시 destination에서 OS 지정 + set -o pipefail set +e + echo "== Resolving Swift Package dependencies ==" + xcodebuild \ + -scheme "$SCHEME" \ + -configuration Debug \ + -clonedSourcePackagesDirPath "$SPM_DIR" \ + -resolvePackageDependencies + echo "== Starting xcodebuild build ==" xcodebuild \ -scheme "$SCHEME" \ -configuration Debug \ -destination "platform=iOS Simulator,OS=${IOS_VER},name=${DEVICE_NAME}" \ + -clonedSourcePackagesDirPath "$SPM_DIR" \ -skipPackagePluginValidation \ -skipMacroValidation \ -showBuildTimingSummary \ build \ | tee build.log + echo "== xcodebuild finished ==" XC_STATUS=${PIPESTATUS[0]} set -e From 77d9c352769f910976f7e5bfb539d70592e3c045 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:55:33 +0900 Subject: [PATCH 55/57] Update build.yml --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61042c1..e55a133 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -166,7 +166,9 @@ jobs: if [ -f build.log ]; then echo "== error: lines ==" - grep -i "error:" build.log || true + if grep -i "error:" build.log; then + XC_STATUS=1 + fi fi exit $XC_STATUS From 55592b828342e6f1a52745b277876a21b04e61b7 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 11:58:03 +0900 Subject: [PATCH 56/57] Update build.yml --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e55a133..64c65d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -167,7 +167,9 @@ jobs: if [ -f build.log ]; then echo "== error: lines ==" if grep -i "error:" build.log; then - XC_STATUS=1 + if [ "$XC_STATUS" -eq 0 ]; then + XC_STATUS=1 + fi fi fi From 0f5be5f0302a5981ca666602a425bb4d4fc0ae3a Mon Sep 17 00:00:00 2001 From: opficdev Date: Sat, 31 Jan 2026 12:05:07 +0900 Subject: [PATCH 57/57] Update SettingView.swift --- DevLog/UI/Setting/SettingView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DevLog/UI/Setting/SettingView.swift b/DevLog/UI/Setting/SettingView.swift index 35ea619..ae33e8b 100644 --- a/DevLog/UI/Setting/SettingView.swift +++ b/DevLog/UI/Setting/SettingView.swift @@ -115,7 +115,7 @@ struct SettingView: View { ) ) case .account: -// TempView(text: "AccountView") + TempView(text: "AccountView") // AccountView(viewModel: viewModel) } }