-
Notifications
You must be signed in to change notification settings - Fork 118
451 lines (384 loc) · 17 KB
/
Cache_Multi_Platform(V6).yml
File metadata and controls
451 lines (384 loc) · 17 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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
#
# Copyright (c) 2022-2026 SMALLPROGRAM <https://github.com/smallprogram/OpenWrtAction>
#
# This is free software, licensed under the MIT License.
# See /LICENSE for more information.
#
# https://github.com/smallprogram/OpenWrtAction
# Description: Build Cache OpenWrt using GitHub Actions
#
name: Cache_Multi_Platform(V6)
on:
repository_dispatch:
types: [openwrt_cache_update_v6]
workflow_dispatch:
inputs:
ssh:
description: 'SSH connection to Actions'
required: false
default: 'false'
force_rebuild:
description: 'Build from scratch'
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
actions: write # 允许脚本去触发重试工作流
env:
UPLOAD_BIN_DIR: false
UPLOAD_FIRMWARE: true
UPLOAD_ARTIFACT: true
UPLOAD_RELEASE: true
TZ: Asia/Shanghai
jobs:
job_init:
runs-on: ubuntu-latest
name: Init
outputs:
platforms: ${{ steps.read-platforms.outputs.matrix }}
platforms_source: ${{ steps.read-platforms.outputs.source_matrix_json }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Read Platforms From File
id: read-platforms
run: |
bash compile_script/platforms.sh
job_source_init:
needs: job_init
runs-on: ${{ matrix.value.OS }}
name: Source-Init-${{ matrix.source_code_platform }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.job_init.outputs.platforms_source) }}
steps:
- name: Init System
id: init
run: |
sudo timedatectl set-timezone "$TZ"
sudo mkdir -p /workdir
sudo chown -R $USER:$GROUPS /workdir
cd /workdir
sudo mkdir -p output
sudo chown -R $USER:$GROUPS /workdir/output
ln -sf /workdir/output $GITHUB_WORKSPACE/output
df -hT
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Clone Source Code
working-directory: /workdir
run: |
git clone -b ${{matrix.value.REPO_BRANCH}} --single-branch ${{ matrix.value.REPO_URL }} openwrt
ln -sf /workdir/openwrt $GITHUB_WORKSPACE/openwrt
- name: Run DIY Part 1 Script
run: |
chmod +x ${{ matrix.value.DIY_P1_SH }}
cd openwrt
$GITHUB_WORKSPACE/${{ matrix.value.DIY_P1_SH }}
echo "feeds_Conf:------------------------"
cat feeds.conf.default
- name: Update Feeds
run: cd openwrt && ./scripts/feeds update -a
- name: Install Feeds
run: cd openwrt && ./scripts/feeds install -a
- name: Run DIY Part 2 Script
run: |
chmod +x ${{ matrix.value.DIY_P2_SH }}
cd openwrt
$GITHUB_WORKSPACE/${{ matrix.value.DIY_P2_SH }}
- name: Generate Source Packages
working-directory: /workdir
id: generate_image
run: |
tar -czf output/output.tar.gz openwrt/
echo "SOURCE_PATH=$PWD" >> $GITHUB_OUTPUT
- name: Upload Source To Artifact
uses: actions/upload-artifact@v7
if: steps.generate_image.outcome == 'success'
with:
name: Source_${{ matrix.source_code_platform }}
path: ${{ steps.generate_image.outputs.SOURCE_PATH }}/output/output.tar.gz
retention-days: 5
job_cache_compile_and_upload:
needs: [job_init, job_source_init]
runs-on: ${{ matrix.value.OS }}
if: ${{ always() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.job_init.outputs.platforms) }}
name: Build-${{ matrix.source_code_platform }}-${{ matrix.platform }}-Cache
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Matrix Status
id: matrix_status
run: |
chmod +x compile_script/matrix_job_status.sh
$GITHUB_WORKSPACE/compile_script/matrix_job_status.sh "${{ github.token }}" "${{ github.repository }}" "${{ github.run_id }}" "${{ fromJSON(github.run_attempt) }}" "Build-${{ matrix.source_code_platform }}-${{ matrix.platform }}"
- name: Server Info
if: steps.matrix_status.outputs.status != 'success'
run: |
echo "---------------------CPU Info--------------------"
lscpu
echo "---------------------RAM Info--------------------"
free -h
- name: Initialization Environment
if: steps.matrix_status.outputs.status != 'success'
run: |
sudo timedatectl set-timezone "$TZ"
sudo mkdir -p /workdir
sudo chown -R $USER:$GROUPS /workdir
df -hT
- name: Maximize Build Space
uses: smallprogram/maximize-build-diskspace@main
if: steps.matrix_status.outputs.status != 'success'
with:
root-reserve-mb: 3072
swap-size-mb: 4096
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'
build-mount-path: '/workdir'
- name: Install Packages
if: steps.matrix_status.outputs.status != 'success'
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo -E apt-get -qq update
sudo -E apt-get -qq install $(curl -fsSL https://github.com/smallprogram/OpenWrtAction/raw/main/diy_script/${{ matrix.source_code_platform }}_dependence)
sudo timedatectl set-timezone "$TZ"
- name: Initialization Directory
working-directory: /workdir
id: init_directory
if: steps.matrix_status.outputs.status != 'success'
run: |
sudo mkdir -p openwrt
sudo mkdir -p download
sudo chown -R $USER:$GROUPS /workdir/openwrt
sudo chown -R $USER:$GROUPS /workdir/download
ln -sf /workdir/openwrt $GITHUB_WORKSPACE/openwrt
ln -sf /workdir/download $GITHUB_WORKSPACE/download
- name: Relocate Docker to /workdir
if: steps.matrix_status.outputs.status != 'success'
run: |
echo "==== 1. 停止 Docker 服务 ===="
sudo systemctl stop docker
echo "==== 2. 迁移 Docker 数据目录到宽敞的 /workdir ===="
sudo mkdir -p /workdir/docker
echo '{"data-root": "/workdir/docker"}' | sudo tee /etc/docker/daemon.json
echo "==== 3. 重启 Docker 服务 ===="
sudo systemctl start docker
echo "==== 4. 验证迁移是否成功 ===="
docker info | grep "Docker Root Dir"
- name: Login to GitHub Container Registry
if: steps.matrix_status.outputs.status != 'success'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Download Source From Artifacts
id : download
if: steps.matrix_status.outputs.status != 'success'
uses: actions/download-artifact@v8
with:
name: Source_${{ matrix.source_code_platform }}
path: download
- name: Extract Pristine Source
if: steps.matrix_status.outputs.status != 'success'
working-directory: /workdir
run: |
tar -xzf download/output.tar.gz
rm -rf download/output.tar.gz
sudo chown -R $USER:$GROUPS /workdir/openwrt
- name: Load Configuration
if: steps.matrix_status.outputs.status != 'success'
run: |
[ -e ${{ matrix.value.CONFIGS }}/${{ matrix.platform }}.config ] && cp -r ${{ matrix.value.CONFIGS }}/${{ matrix.platform }}.config openwrt/.config
cd openwrt
make defconfig
- name: Download Package
if: steps.matrix_status.outputs.status != 'success'
run: |
cd $GITHUB_WORKSPACE/openwrt
# 设置重试次数,比如 3 次
for i in {1..3}; do
echo "开始第 $i 次尝试执行 make download..."
timeout 8m make download -j8 && break || echo "第 $i 次尝试超时或失败,准备重试..."
# 在最后一次尝试失败后,手动抛出错误退出
if [ $i -eq 3 ]; then
echo "经过 3 次尝试后依然失败,停止执行。"
exit 1
fi
done
- name: Git Restore Mtime (True SSoT) And DL Restore Time
if: steps.matrix_status.outputs.status != 'success'
run: |
cd /workdir/openwrt
echo "==== 遍历所有 Git 仓库并精准还原真实提交时间 ===="
find . -name ".git" -type d | while read gitdir; do
repo_dir=$(dirname "$gitdir")
echo "-> 正在处理 Git 仓库: $repo_dir"
cd "/workdir/openwrt/$repo_dir"
git log --format=%at --name-only | perl -ane '
if (/^(\d+)$/) { $t = $1; }
elsif (/^(\S+)$/) {
$f = $1;
if (-e $f && !-d $f && !$seen{$f}) {
utime($t, $t, $f);
$seen{$f} = 1;
}
}
'
cd /workdir/openwrt
done
find /workdir/openwrt/dl -type f | xargs -r touch -t 200001010000
echo "✅ dl/ 时间戳逆转完成!"
- name: Restore Entire OpenWrt Cache from GHCR
id: restore_cache
if: steps.matrix_status.outputs.status != 'success' && github.event.inputs.force_rebuild != 'true'
run: |
df -hT
REPO_OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
PLATFORM_NAME=$(echo "${{ matrix.source_code_platform }}-${{ matrix.platform }}" | tr '[:upper:]' '[:lower:]')
IMAGE_NAME="ghcr.io/smallprogram/openwrt-base-cache-$PLATFORM_NAME:latest"
if docker pull $IMAGE_NAME; then
echo "==== 发现缓存,开始极速提取 ===="
cd /workdir
docker create --name cache_container $IMAGE_NAME /bin/true
# 导出整个容器的打包文件
docker export cache_container > cache_exported.tar
docker rm cache_container
docker rmi $IMAGE_NAME -f
# 直接提取原名分块
tar -xf cache_exported.tar --wildcards "op_cache_raw_*" || true
if ls op_cache_raw_* 1> /dev/null 2>&1; then
echo "==== 按顺序合并分块并还原到 OpenWrt ===="
# 直接 cat 合并,极其清爽
cat op_cache_raw_* | tar -I "zstd -T0" -xf - -C /workdir/openwrt/
rm -f /workdir/cache_exported.tar /workdir/op_cache_raw_*
echo "✅ 缓存合并恢复完成!"
else
echo "⚠️ 未找到有效缓存分块!"
fi
else
echo "No cache found on GHCR. This will be a clean build."
fi
df -hT
- name: Compile Base (Tools, Toolchain, Kernel, LongTime Packages)
id: compile_base
if: steps.matrix_status.outputs.status != 'success'
run: |
cd openwrt
touch /workdir/base_compilation_marker
echo "==== 1. 编译系统工具 ===="
time make tools/compile -j$(nproc) || exit 1
echo "==== 2. 编译交叉工具链 ===="
time make toolchain/compile -j$(nproc) || exit 1
# echo "==== 3. 编译 Linux 内核 ===="
# time make target/compile -j$(nproc) || exit 1
echo "==== 4. 编译长期存在的包 ===="
LONG_TIME_TARGETS=(
"package/feeds/packages/golang/host/compile"
"package/feeds/packages/rust/host/compile"
"package/feeds/packages/ruby/host/compile"
"package/libs/gettext-full/host/compile"
)
for target in "${LONG_TIME_TARGETS[@]}"; do
time make $target -j$(nproc) || exit 1
done
- name: Checkpoint Cache (Save Base to GHCR)
if: steps.matrix_status.outputs.status != 'success' && steps.compile_base.outcome == 'success'
run: |
echo "==== 智能探测 Base 是否更新 ===="
cd /workdir
# 严谨的正则过滤:
# 1. 必须在 staging_dir/host/stamp/ 目录下,且以 .tools_compile_ 开头
# 2. 必须在 staging_dir/target-*/stamp/ 目录下,且名字严格叫 .target_prereq
IGNORE_PATTERN="staging_dir/host/stamp/\.tools_compile_|staging_dir/target-[^/]+/stamp/\.target_prereq$|staging_dir/host/stamp/\.(zstd|flock|libdeflate|pkgconf|xz|tar|patch|m4)_"
CHANGED_BASE=$(find openwrt/build_dir openwrt/staging_dir -type f -newer /workdir/base_compilation_marker -path "*/stamp/*" 2>/dev/null | \
grep -E -v "$IGNORE_PATTERN" | \
wc -l)
echo "--- Found $CHANGED_BASE newer stamp files ---"
if [ "$CHANGED_BASE" -eq 0 ]; then
echo "🎉 Base 阶段命中旧缓存,跳过本次 Checkpoint 存档!"
exit 0
fi
find openwrt/build_dir openwrt/staging_dir -type f -newer /workdir/base_compilation_marker -path "*/stamp/*" 2>/dev/null | \
grep -E -v "$IGNORE_PATTERN"
# (防止新下载的 tarball 比缓存 stamp 更新导致重编译)
# OpenWrt stamp 依赖链:.installed→.built→.configured→.preparedXXX→dl/source.tar.gz
# make download 每次都会重新下载 tarball,mtime = 现在,而缓存 stamp 时间是过去,
# make 看到 dl/ 比 stamp 新就会重编。将 dl/ 所有文件回拨到远古时间即可解决。
# PKG_HASH 若真的变了,stamp 文件名(含 hash)也变了,旧 stamp 不匹配,仍会正常重编。
echo "==== 回拨 dl/ 时间戳===="
find /workdir/openwrt/dl -type f | xargs -r touch -t 200001010000
echo "✅ dl/ 时间戳逆转完成!"
echo "⚡ 检测到 $CHANGED_BASE 个新 stamp,触发缓存更新存档..."
df -hT
echo "==== 自动修剪冗余版本 ===="
for linux_dir in openwrt/build_dir/target-*/linux-*/; do
[ -d "$linux_dir" ] && (cd "$linux_dir" && ls -dt linux-* 2>/dev/null | tail -n +2 | xargs -I {} rm -rf "{}")
done
[ -d "openwrt/build_dir" ] && (cd openwrt/build_dir && ls -dt toolchain-* 2>/dev/null | tail -n +2 | xargs -I {} rm -rf "{}")
if [ -d "openwrt/staging_dir" ]; then
(cd openwrt/staging_dir && ls -dt target-* 2>/dev/null | tail -n +2 | xargs -I {} rm -rf "{}")
(cd openwrt/staging_dir && ls -dt toolchain-* 2>/dev/null | tail -n +2 | xargs -I {} rm -rf "{}")
fi
echo "==== 打包与 5GB 分块 ===="
cd /workdir/openwrt
# 使用 split 临时切片为 op_cache_raw_00, op_cache_raw_01... (每块最大 5000M)
tar -I "zstd -T0 -10" -cf - build_dir staging_dir dl | split -a 3 -d -b 5000M - /workdir/op_cache_raw_
echo "==== 批量重命名并创建 Layer 隔离目录 ===="
cd /workdir
echo "FROM scratch" > Dockerfile
# 遍历默认分块,并分配到独立的 layerN 文件夹中
count=1
for f in op_cache_raw_*; do
layer_dir="layer$count"
mkdir -p "$layer_dir"
# 直接把原文件扔进 layer 文件夹,不改名!
mv "$f" "$layer_dir/"
echo "COPY $layer_dir /" >> Dockerfile
count=$((count + 1))
done
echo "==== 开始构建与推送多层镜像 ===="
df -hT
REPO_OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
PLATFORM_NAME=$(echo "${{ matrix.source_code_platform }}-${{ matrix.platform }}" | tr '[:upper:]' '[:lower:]')
IMAGE_NAME="ghcr.io/smallprogram/openwrt-base-cache-$PLATFORM_NAME:latest"
docker build -t $IMAGE_NAME .
docker push $IMAGE_NAME
echo "✅ 分块多层缓存推送完毕!"
# 清理临时数据文件
echo "==== 清理临时数据文件 ===="
rm -rf layer* Dockerfile
docker rmi $IMAGE_NAME -f
docker builder prune -a -f
docker system prune -a -f
echo "✅ 清理临时数据文件完毕!"
df -hT
rerun-failed-jobs:
runs-on: ubuntu-latest
needs: [job_init, job_source_init, job_cache_compile_and_upload]
if: failure() && fromJSON(github.run_attempt) < 3 && !cancelled()
steps:
- name: Rerun failed jobs in the current workflow
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
echo "Retry failed jobs for the ${{ fromJSON(github.run_attempt) }} time, Retry 3 times in total"
gh workflow run Retry_Failure_Jobs.yml -F run_id=${{ github.run_id }}