-
Notifications
You must be signed in to change notification settings - Fork 0
234 lines (205 loc) · 8.03 KB
/
build.yml
File metadata and controls
234 lines (205 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
name: iOS CI
on:
pull_request:
env:
SCHEME: DevLog
XCODE_VERSION: latest
permissions:
contents: read
issues: write
pull-requests: write
checks: write
jobs:
build:
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- 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
shell: bash
run: |
set -euo pipefail
# macOS 메인 버전에 맞는 iOS 버전 중 최신 버전의 iPhone 선택
RESULT=$(python3 - <<'PY'
import re, subprocess, sys
xcode_ver = subprocess.check_output(["xcodebuild", "-version"], text=True).splitlines()[0].strip()
xcode_major = xcode_ver.split()[1].split('.')[0]
try:
xcode_major_num = int(xcode_major)
except ValueError:
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()
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"{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 Xcode major {xcode_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:
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)
print(f"No iPhone candidates found for iOS {latest_ver}", file=sys.stderr)
sys.exit(1)
PY
)
if [ -z "${RESULT:-}" ]; then
echo "No iPhone simulator devices detected." >&2
exit 1
fi
IFS='|' read -r IOS_VER DEVICE_NAME <<< "$RESULT"
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
run: |
set -euo pipefail
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})"
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
if [ -f build.log ]; then
echo "== error: lines =="
if grep -i "error:" build.log; then
if [ "$XC_STATUS" -eq 0 ]; then
XC_STATUS=1
fi
fi
fi
exit $XC_STATUS
- name: Comment build failure on PR
if: failure() && github.event.pull_request.head.repo.fork == false
uses: actions/github-script@v7
with:
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
});