-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathappveyor.yml
More file actions
294 lines (270 loc) · 14.6 KB
/
appveyor.yml
File metadata and controls
294 lines (270 loc) · 14.6 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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
image: macos-sonoma
only_commits:
files:
- appveyor.yml
environment:
matrix:
- MACOS_VERSION_CODE: "20G1427"
MACOS_NAME: "big-sur"
MACOS_DISPLAY_NAME: "Big Sur"
MACOS_VERSION_NUMBER: "11"
CATALOG: "publicrelease"
IMAGE_SIZE: "13270m"
- MACOS_VERSION_CODE: "21H1123"
MACOS_NAME: "monterey"
MACOS_DISPLAY_NAME: "Monterey"
MACOS_VERSION_NUMBER: "12"
CATALOG: "publicrelease"
IMAGE_SIZE: "14275m"
- MACOS_VERSION_CODE: "22H313"
MACOS_NAME: "ventura"
MACOS_DISPLAY_NAME: "Ventura"
MACOS_VERSION_NUMBER: "13"
CATALOG: "publicrelease"
IMAGE_SIZE: "14275m"
- MACOS_VERSION_CODE: "23H311"
MACOS_NAME: "sonoma"
MACOS_DISPLAY_NAME: "Sonoma"
MACOS_VERSION_NUMBER: "14"
CATALOG: "publicrelease"
IMAGE_SIZE: "15700m"
- MACOS_VERSION_CODE: "24C101"
MACOS_NAME: "sequoia"
MACOS_DISPLAY_NAME: "Sequoia"
MACOS_VERSION_NUMBER: "15"
CATALOG: "publicrelease"
IMAGE_SIZE: "22000m"
- MACOS_VERSION_CODE: "26A379"
MACOS_NAME: "tahoe"
MACOS_DISPLAY_NAME: "Tahoe"
MACOS_VERSION_NUMBER: "26"
CATALOG: "publicrelease"
IMAGE_SIZE: "25000m"
matrix:
# fast_finish: false 确保任意一行失败时不取消其他正在运行的矩阵行
fast_finish: false
build_script:
# 全部构建逻辑在同一个 shell 里执行,保证变量作用域和早退(exit 0)正常工作
- |
# ── 由 GitHub Actions 触发时,只构建目标版本,跳过其余矩阵行 ──
if [ -n "$GHA_TARGET_VERSION" ] && [ "$MACOS_NAME" != "$GHA_TARGET_VERSION" ]; then
echo "跳过矩阵行 MACOS_NAME=$MACOS_NAME(目标: $GHA_TARGET_VERSION)"
exit 0
fi
# ── Runner 环境信息 ───────────────────────────────────────────────────
echo "━━━━━━━━━━━━━━━━━━━━━━━ Runner 环境 ━━━━━━━━━━━━━━━━━━━━━━━━"
echo "CPU 逻辑核心 : $(sysctl -n hw.logicalcpu)"
echo "CPU 物理核心 : $(sysctl -n hw.physicalcpu)"
echo "物理内存 : $(( $(sysctl -n hw.memsize) / 1024 / 1024 / 1024 )) GB"
echo "macOS 版本 : $(sw_vers -productVersion) build $(sw_vers -buildVersion)"
echo "━━━ 初始磁盘使用 ━━━"
df -h /
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# ── 清理 Runner 上不必要的大型组件,释放磁盘空间 ─────────────────────────
echo "开始清理不必要的大型组件..."
# Xcode.app:本构建所需命令(hdiutil/diskutil/installer/createinstallmedia)
# 均为 macOS 系统内置或 .app bundle 内部命令,不依赖 Xcode IDE
if [ -d "/Applications/Xcode.app" ]; then
echo " 删除 Xcode.app ($(du -sh /Applications/Xcode.app 2>/dev/null | awk '{print $1}'))..."
sudo rm -rf /Applications/Xcode.app
fi
# iOS / watchOS / tvOS 模拟器运行时(每个数 GB)
if [ -d "/Library/Developer/CoreSimulator/Profiles/Runtimes" ]; then
echo " 删除 Simulator Runtimes ($(du -sh /Library/Developer/CoreSimulator/Profiles/Runtimes 2>/dev/null | awk '{print $1}'))..."
sudo rm -rf /Library/Developer/CoreSimulator/Profiles/Runtimes
fi
sudo rm -rf /Library/Developer/CoreSimulator/Caches 2>/dev/null || true
rm -rf ~/Library/Developer/Xcode/DerivedData 2>/dev/null || true
rm -rf ~/Library/Caches/com.apple.dt.Xcode 2>/dev/null || true
rm -rf ~/.gradle/caches 2>/dev/null || true
rm -rf ~/.npm/_npx 2>/dev/null || true
rm -rf ~/.cargo/registry/cache 2>/dev/null || true
echo "组件清理完成,当前磁盘剩余: $(df -h / | awk 'NR==2{print $4}')"
brew update || true
# p7zip 在 AppVeyor Sonoma 镜像中已预装,brew install 有时因迁移/升级返回非零退出码
# AppVeyor macOS shell 隐式使用 -e,必须加 || true 防止脚本被中断
HOMEBREW_NO_INSTALL_CLEANUP=1 brew install sshpass p7zip || true
# brew 安装后立即清理下载缓存,避免占用额外磁盘空间
brew cleanup --prune=all 2>/dev/null || true
rm -rf "$(brew --cache)" 2>/dev/null || true
echo "brew 缓存清理后磁盘剩余: $(df -h / | awk 'NR==2{print $4}')"
git clone https://github.com/corpnewt/gibMacOS.git gibMacOS
chmod +x gibMacOS/gibMacOS.command
cd gibMacOS
# 使用长参数形式,与 GitHub Actions yml 保持一致;-b 指定唯一构建号
python3 gibMacOS.py --catalog "$CATALOG" --version "$MACOS_VERSION_NUMBER" -b "$MACOS_VERSION_CODE"
cd ..
DOWNLOAD_DIR=$(find "$PWD/gibMacOS/macOS Downloads" -type d -name "*$MACOS_VERSION_CODE*" | head -n1)
if [ -z "$DOWNLOAD_DIR" ]; then
echo "警告: 未找到版本 $MACOS_VERSION_CODE 的下载目录(可能构建号已过期或不在 $CATALOG catalog 中)"
echo "请运行: cd gibMacOS && python3 gibMacOS.py --catalog $CATALOG --version $MACOS_VERSION_NUMBER"
echo "并将输出的构建号填写到 appveyor.yml 的 MACOS_VERSION_CODE 字段"
exit 0
fi
PKG=$(find "$DOWNLOAD_DIR" -maxdepth 1 -name "InstallAssistant.pkg" | head -n1)
if [ -z "$PKG" ]; then
echo "Error: InstallAssistant.pkg not found"; exit 1
fi
sudo installer -pkg "$PKG" -target /Applications
APP="/Applications/Install macOS $MACOS_DISPLAY_NAME.app"
if [ ! -d "$APP" ]; then
echo "Error: $APP not found"; exit 1
fi
# pkg 已安装到 /Applications,gibMacOS 仓库(含 InstallAssistant.pkg,可达 12-14 GB)
# 不再需要,立即删除以释放空间
echo "删除 gibMacOS 仓库和下载缓存..."
rm -rf "$PWD/gibMacOS" 2>/dev/null || true
echo "gibMacOS 删除完成,当前磁盘剩余: $(df -h / | awk 'NR==2{print $4}')"
# ── 创建稀疏镜像前核查可用磁盘空间 ──────────────────────────────────────
AVAIL_KB=$(df / | awk 'NR==2{print $4}')
IMAGE_MB=$(echo "$IMAGE_SIZE" | tr -d 'mMgG')
# 需要:稀疏镜像(IMAGE_SIZE)+ ISO(IMAGE_SIZE)+ 7z 输出(约 IMAGE_SIZE * 0.95)+ 5 GB 余量
NEEDED_KB=$(( IMAGE_MB * 1024 * 3 + 5 * 1024 * 1024 ))
echo "磁盘检查:可用 $(( AVAIL_KB / 1024 / 1024 )) GB,预估需要约 $(( NEEDED_KB / 1024 / 1024 )) GB"
if [ "$AVAIL_KB" -lt "$NEEDED_KB" ]; then
echo "⚠ 警告:磁盘空间可能不足(可用 $(( AVAIL_KB/1024/1024 )) GB < 预估 $(( NEEDED_KB/1024/1024 )) GB),当前详情:"
df -h
fi
echo "Creating sparse image of size $IMAGE_SIZE …"
hdiutil create \
-size "$IMAGE_SIZE" \
-layout GPTSPUD \
-fs HFS+J \
-type SPARSE \
-volname "Install_$MACOS_NAME" \
-o temp_install.sparseimage
ATTACH_OUT=$(hdiutil attach temp_install.sparseimage \
-nobrowse -noverify -noautoopen -readwrite)
echo "挂载信息: $ATTACH_OUT"
ACTUAL_INSTALL_VOL=$(echo "$ATTACH_OUT" | grep "Install_$MACOS_NAME" | awk '{print $3}')
if [ -z "$ACTUAL_INSTALL_VOL" ]; then
echo "Error: 无法找到安装卷挂载点"; exit 1
fi
echo "实际挂载点: $ACTUAL_INSTALL_VOL"
EFI_DEV=$(echo "$ATTACH_OUT" | awk '$2=="EFI"{print $1}')
EFI_MNT=$(echo "$ATTACH_OUT" | awk '$2=="EFI"{print $3}')
INST_DEV=$(echo "$ATTACH_OUT" | grep "Install_$MACOS_NAME" | awk '{print $1}')
echo "安装设备: $INST_DEV"
# 先尝试带资产下载,失败后重新定位挂载点(createinstallmedia 首次运行可能
# 已将卷从 Install_$MACOS_NAME 重命名为 Install macOS $MACOS_DISPLAY_NAME,
# 此时 $ACTUAL_INSTALL_VOL 已失效,必须通过设备节点重新查找当前挂载点)
if ! sudo "$APP/Contents/Resources/createinstallmedia" \
--volume "$ACTUAL_INSTALL_VOL" --nointeraction --downloadassets; then
echo "⚠ --downloadassets 失败,重新定位挂载点后不带资产重试..."
# 通过设备节点获取当前挂载点(解析多词路径,如 /Volumes/Install macOS Sequoia)
RETRY_VOL=$(diskutil info "$INST_DEV" 2>/dev/null \
| awk '/Mount Point/{mp=""; for(i=3;i<=NF;i++){if(i>3)mp=mp" "; mp=mp $i}; if(mp!="")print mp}')
if [ -z "$RETRY_VOL" ]; then
# 卷可能被临时卸载,尝试重新挂载再查找
diskutil mount "$INST_DEV" || true
sleep 2
RETRY_VOL=$(diskutil info "$INST_DEV" 2>/dev/null \
| awk '/Mount Point/{mp=""; for(i=3;i<=NF;i++){if(i>3)mp=mp" "; mp=mp $i}; if(mp!="")print mp}')
fi
if [ -z "$RETRY_VOL" ]; then
RETRY_VOL="$ACTUAL_INSTALL_VOL"
echo "警告:无法重新查找挂载点,回退使用原始路径 $ACTUAL_INSTALL_VOL"
else
echo "重新定位挂载点:$RETRY_VOL"
fi
sudo "$APP/Contents/Resources/createinstallmedia" \
--volume "$RETRY_VOL" --nointeraction
fi
# ── 清理不影响离线安装的冗余缓存文件 ──
echo "清理安装卷上的冗余缓存..."
# createinstallmedia 会把卷名从 Install_$MACOS_NAME 重命名为 "Install macOS $MACOS_DISPLAY_NAME"
# 用 diskutil info 通过设备节点查挂载点(解析含空格的多词路径),找不到时再用 df 兜底
INSTALL_VOL_PATH=$(diskutil info "$INST_DEV" 2>/dev/null \
| awk '/Mount Point/{mp=""; for(i=3;i<=NF;i++){if(i>3)mp=mp" "; mp=mp $i}; if(mp!="")print mp}')
if [ -z "$INSTALL_VOL_PATH" ]; then
INSTALL_VOL_PATH=$(df | grep -F "Install macOS $MACOS_DISPLAY_NAME" | awk '{print $NF}' | head -n1)
fi
if [ -z "$INSTALL_VOL_PATH" ]; then
INSTALL_VOL_PATH=$(df | grep -F "Install_$MACOS_NAME" | awk '{print $NF}' | head -n1)
fi
if [ -z "$INSTALL_VOL_PATH" ]; then
INSTALL_VOL_PATH="$ACTUAL_INSTALL_VOL"
fi
sudo find "$INSTALL_VOL_PATH" -name ".DS_Store" -type f -delete 2>/dev/null || true
sudo rm -rf "$INSTALL_VOL_PATH/.Spotlight-V100" 2>/dev/null || true
sudo rm -rf "$INSTALL_VOL_PATH/.fseventsd" 2>/dev/null || true
sudo rm -rf "$INSTALL_VOL_PATH/.Trashes" 2>/dev/null || true
echo "缓存清理完成"
diskutil mount "$EFI_DEV" || true
ACTUAL_EFI_MNT=$(mount | grep "$EFI_DEV" | awk '{print $3}')
if [ -z "$ACTUAL_EFI_MNT" ]; then
ACTUAL_EFI_MNT="$EFI_MNT"
fi
if [ -d "$ACTUAL_EFI_MNT" ]; then
if [ -d "EFI" ]; then
sudo cp -R EFI/* "$ACTUAL_EFI_MNT/" || echo "警告: EFI 注入失败(分区可能只读或权限不足),跳过"
else
echo "警告: EFI 目录不存在,跳过 EFI 注入"
fi
diskutil unmount "$ACTUAL_EFI_MNT" || true
else
echo "警告: EFI 分区未挂载成功,跳过 EFI 注入"
fi
sleep 5
diskutil list
echo "尝试卸载磁盘镜像..."
# 优先通过父磁盘(diskX)整体卸载,比单独卸载分区更可靠
PARENT_DISK=$(echo "$INST_DEV" | sed 's/s[0-9]*$//')
hdiutil detach "$PARENT_DISK" -force 2>/dev/null \
|| hdiutil detach "$INST_DEV" -force 2>/dev/null \
|| true
if [ -n "$EFI_DEV" ]; then
hdiutil detach "$EFI_DEV" -force 2>/dev/null || true
fi
sleep 3
echo "压缩稀疏镜像以回收空闲空间..."
hdiutil compact temp_install.sparseimage -batteryallowed || true
echo "稀疏镜像压缩完成"
mkdir -p output
hdiutil convert temp_install.sparseimage \
-format UDTO -o output/$MACOS_NAME.cdr \
|| { echo "Error: hdiutil convert 失败,镜像可能仍有分区已挂载"; diskutil list; exit 1; }
mv output/$MACOS_NAME.cdr output/$MACOS_NAME.iso
# ISO 转换完成,稀疏镜像不再需要,立即删除以释放空间
# (避免 sparseimage + ISO + 7z 输出三者同时占用磁盘)
rm -f temp_install.sparseimage
echo "稀疏镜像已删除,7z 压缩前磁盘剩余: $(df -h / | awk 'NR==2{print $4}')"
echo "使用 7z LZMA2 压缩 ISO..."
# -mx=9: ultra 压缩级别(最高)
# -mfb=273: LZMA2 最大 fast-bytes(默认 64,273 是上限,对大块重复内容有效)
# -md=256m: 256 MB 字典(AppVeyor 机器约 8 GB RAM,256m 压缩约用 3 GB,安全)
# -ms=on: 固实压缩(单文件无影响,保留以备将来多文件一起压缩时生效)
# -mmt=on: 多线程(充分利用 CPU)
7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=256m -mmt=on -ms=on \
output/$MACOS_NAME.iso.7z output/$MACOS_NAME.iso
rm output/$MACOS_NAME.iso
echo "压缩完成: $(ls -lh output/$MACOS_NAME.iso.7z)"
echo "7z 压缩后磁盘剩余: $(df -h / | awk 'NR==2{print $4}')"
# ── 产物存储方式一:AppVeyor Artifact(APPVEYOR_ARTIFACT=true 时执行)──
if [ "$APPVEYOR_ARTIFACT" = "true" ]; then
echo "推送 AppVeyor Artifact: output/$MACOS_NAME.iso.7z"
appveyor PushArtifact "output/$MACOS_NAME.iso.7z" -FileName "$MACOS_NAME.iso.7z"
fi
# ── 产物存储方式二:SCP 上传到服务器(IP 已配置时执行)──
if [ -n "$IP" ]; then
# 统一 SSH 选项:禁用主机密钥检查、不写 known_hosts、启用保活(每 60 s 一次,
# 最多 10 次无响应后断开)、连接超时 30 s——防止大文件传输时连接静默挂起
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 -o ConnectTimeout=30"
ssh-keygen -R "$IP"
sshpass -p "$PASSWORD" ssh $SSH_OPTS root@"$IP" "mkdir -p /root/macos && exit"
LOCAL_FILE="output/$MACOS_NAME.iso.7z"
REMOTE_FILE="/root/macos/$MACOS_NAME.iso.7z"
LOCAL_SIZE=$(stat -f%z "$LOCAL_FILE")
# 查询远端文件大小(不存在时返回 0),若与本地一致则跳过上传(去重)
REMOTE_SIZE=$(sshpass -p "$PASSWORD" ssh $SSH_OPTS root@"$IP" \
"stat -c%s \"$REMOTE_FILE\" 2>/dev/null || echo 0")
if [ "$REMOTE_SIZE" = "$LOCAL_SIZE" ]; then
echo "跳过上传:$MACOS_NAME.iso.7z 已存在于远程服务器且大小一致(${LOCAL_SIZE} bytes),无需重复上传"
else
echo "开始上传 $MACOS_NAME.iso.7z(本地: ${LOCAL_SIZE} B,远程已有: ${REMOTE_SIZE} B)..."
sshpass -p "$PASSWORD" scp $SSH_OPTS "$LOCAL_FILE" root@"$IP":"$REMOTE_FILE"
echo "上传完成"
fi
sshpass -p "$PASSWORD" ssh $SSH_OPTS root@"$IP" "ls -lah /root/macos/"
fi