fix(cache): 发布侧 key 只取 inputs.os 且 tag run 只读不写(M270 r1 复审 P2)

r1 复审(p2-1items)指出:tag 触发的 run 只能读 main 或同名 tag 的缓存,而 main 上
没有任何 job 跑 ubuntu-22.04 / windows-11-arm 这些标签 ⇒ 发布侧四个槽位每次发布都是冷编,
写出的 target 缓存永远拿不回来,还会占 10 GB 配额把 main 的热缓存 LRU 挤掉。r0 的
「key = inputs.os + inputs.slot」既没解决死缓存,还把 Windows 借 main 热缓存的路掐了。

- 发布侧 key 改成 `${{ inputs.os }}`:linux-x64(22.04) 与 main 的 ubuntu-latest 仍然不同
  key(M269 那条必须断),而 windows-x64(windows-latest) 与 main 的 ci/build 重新同 key
  —— 同镜像,借它的热缓存安全(M270 前 Windows 一直走这条且产物是绿的)。
- 发布侧加 `save-if: ${{ github.ref_type != 'tag' }}`:tag run 一律不写(写出来是死缓存),
  只有 main 上的 dispatch 预热会写 —— 那次 run 的作用域是 main,之后的 tag run 才读得到。
  刻意不用 `cache-targets: false`:那会把 Windows 借热缓存的路一起掐掉。
- ci.yml 侧只修 Nit:`Linux-X64` → `Linux-x64`(node 的 os.arch() 是小写)。
- docs 照实重写:tag 缓存作用域、发布侧每次冷编的必然性、预热怎么做、只读不写防什么、
  「同一镜像标签但底层悄悄升级」这档剩余风险与手动 bump 办法;并补回 M164 MSYS 报错的
  原文(可搜索的指路)。新的 key 演算:改前主 key 相同 2 对(跨镜像 1 对)/ 跨镜像前缀
  命中 2 组;r1 = 1 对(全是同镜像 windows)/ 0 组。
This commit is contained in:
AskaEth committed 2026-11-12 10:31:23 +08:00
1 parent 93dc27bed8
commit a6060e7649
3 files changed
+117 -65

No files matched your search

+3 -3
View File
@@ -4,9 +4,9 @@ name: CI
# 架构守门(core 零 UI、文件 ≤400 行、依赖许可)见 guards.yml。
# 平台基线 Win10+(标准工具链产物),平台矩阵见 AGENTS.md §7。
# (M270)每个 `Swatinem/rust-cache@v2` 都显式写 `key:` = **该 job 的 runner 镜像标签**(不是「更保险」):v2 的 key
# 只有 `os.type()-os.arch()`(`Linux-X64`),**没有镜像标签/glibc** ⇒ 本文件 `build`(ubuntu-latest = 24.04)与
# `release-build.yml` 的 `build`(ubuntu-22.04 = glibc 2.35)共用同一份 `target/`。因果链见 `release-build.yml`
# 那处注释;`cache-targets` 取舍、剩余风险与 key 演算见 packaging/docs/build-and-ci.md §3「构建缓存」。
# 只有 `os.type()-os.arch()`(`Linux-x64`),**没有镜像标签/glibc** ⇒ **改前**本文件 `build`(ubuntu-latest = 24.04)与
# `release-build.yml` 的 `build`(ubuntu-22.04 = glibc 2.35)共用同一份 `target/`(脏产物就是这么串过去的)。因果链见
# `release-build.yml` 那处注释;`cache-targets` 取舍、剩余风险与 key 演算见 packaging/docs/build-and-ci.md §3「构建缓存」。
#
# M118(D26):根 Cargo.toml 已 `exclude = ["crates/app"]`(Slint 退役树,源码原样保留)。
# 所以本文件里所有 `--workspace` 都**不再**覆盖 `unlucky-app`:CI 不再编 slint 1.18 + usvg 0.48
+23 -23
View File
@@ -1,16 +1,13 @@
name: Release / build(可复用)
# Release 流水线的**构建侧**(M145 拆分:`release.yml` 与 `packaging/README.md` 都顶到 400 行红线,下一处改动必越线)。
# M145 时本文件 = 拆分前 `.github/workflows/release.yml` 的 `build` job **逐字搬家**,改动只有四个矩阵键换成同名入参:
# `matrix.slot` → `inputs.slot`、`matrix.os` → `inputs.os`、`matrix.build_dir` → `inputs.build_dir`、
# `matrix.rustflags` → `inputs.rustflags`。
# M145 时本文件 = 拆分前 `.github/workflows/release.yml` 的 `build` job **逐字搬家**,改动只有四个矩阵键换成同名入参
# (`matrix.{slot,os,build_dir,rustflags}` → `inputs.{slot,os,build_dir,rustflags}`)。
# **M164 改了两步**(只这两步):① `Windows ARM64 工具链体检` → `工具链取证`(不再 exit 1,判定权交回编译器;
# 理由见该步上方的长注释);② `cargo build` 步给 arm64 槽位显式备齐 target / `LIB`+`INCLUDE` / arm64 `link.exe`。
# **M184 加了一步**:`linux-x64` 的 xvfb 运行时冒烟(真渲一帧 + `[theme]` / `[backend] final` 日志断言 + 像素判定)——
# 这一档进了 `PUBLISH_SLOTS`,「发出去的包真能跑」从此由 CI 每轮证明;判据只在 `linux-x64` 槽位上跑(arm64 没有
# 对应的运行时证据,不给它加一条没验证过的门禁)。同一轮把 arm64 的失败收尾注解按槽位分支。
# **M270 给 `Swatinem/rust-cache` 加了显式 `key`(镜像 + 槽位)**:缓存会跨镜像串产物,机制、收益/剩余风险与 key 演算见
# `packaging/docs/build-and-ci.md` §3「构建缓存」。
# 进了 `PUBLISH_SLOTS` 就得由 CI 每轮证明「发出去的包真能跑」;只在 `linux-x64` 跑(arm64 没有运行时证据,不造没验证过的门禁)。
# **M270 给 `Swatinem/rust-cache` 加了 `key`(= 镜像标签)与 `save-if`(tag run 不写)**:机制与取舍见 `packaging/docs/build-and-ci.md` §3「构建缓存」。
# 矩阵**仍由调用方** `release.yml` 的 `plan` job 生成并在外层展开(矩阵不能带 `if`,`strategy` 可读 `needs`),
# 本文件只跑**单个槽位**,不持有槽位表 —— 改槽位只改 `release.yml` 里那一处。
# `env` 必须在被调方重复声明:**被调工作流不继承调用方的 workflow 级 `env`**(CARGO_TERM_COLOR / RUST_BACKTRACE
@@ -73,14 +70,19 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
# (M270)`key:` 必须带**镜像维度**,不是「加个 key 更保险」:v2 的 key = `v0-rust-` + 本行 key + job +
# `-Linux-X64`(os.type/os.arch)+ env 哈希 + 锁哈希 —— 里面**没有镜像标签/glibc**;本 job 与 `ci.yml` 的
# `build` 同 job id、rustc 同版、同一份 `Cargo.lock` ⇒ 两边 key 逐字节相同,`ubuntu-latest`(24.04) 编的
# `target/` 会被恢复到 `ubuntu-22.04`(glibc 2.35)。致命处:cargo 的 fingerprint(`.rlib` 的 `-C metadata`)
# **不含 glibc** ⇒ 恢复后判 fresh 不重编,24.04 编的 `aws-lc-sys` `bcm.o`(引 `__isoc23_sscanf`)在 2.35 上
# 链接 ⇒ undefined。`key:` 落**前缀段**,主 key 与兜底 `restoreKey`(前缀去锁哈希)一起分开,演算见 docs §3。
# `-Linux-x64`(os.type/os.arch)+ env 哈希 + 锁哈希 —— 里面**没有镜像标签/glibc**;本 job 与 `ci.yml` 的
# `build` 同 job id、rustc 同版、同一份 `Cargo.lock` ⇒ 曾逐字节相同,`ubuntu-latest`(24.04) 编的 `target/`
# 被恢复到 `ubuntu-22.04`(2.35)。致命处:cargo 的 fingerprint(`.rlib` 的 `-C metadata`)**不含 glibc** ⇒
# 判 fresh 不重编,24.04 编的 `aws-lc-sys` `bcm.o`(引 `__isoc23_sscanf`)在 2.35 上链接 ⇒ undefined。
# 故 key 只取 `inputs.os`:linux-x64(22.04) 与 main 的 `ubuntu-latest` 从此不同 key(要断的就是这条),而
# windows-x64(windows-latest) 与 main 的 `ci/build` **故意同 key** —— 同镜像,借热缓存安全(M270 前 Windows
# 一直走这条且产物是绿的);slot 是冗余维度,每个槽位的 `inputs.os` 本来就互不相同。
# `save-if`:tag run 只能读 main / 同名 tag 的缓存 ⇒ 本侧写出的 target 缓存永远轮不到复用,只贡献冷编 +
# 死缓存 + 挤掉 main 的热缓存。故 tag 不写;留 `ref_type != 'tag'` 这个门是给 main 上 dispatch 预热用的。
- uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.os }}-${{ inputs.slot }}
key: ${{ inputs.os }}
save-if: ${{ github.ref_type != 'tag' }}
# (M141/M142 → **M164 判定权移交**)windows-arm64 跑在**原生** `windows-11-arm` 上。本步**只取证,不判定**:
# M141/M142 的版本用 `find` 猜 SDK 的 `um/arm64/kernel32.lib` 在不在,猜缺就 `exit 1` —— 结果 v0.2.1~v0.2.3
@@ -217,13 +219,12 @@ jobs:
ls -l "$BUILD_DIR"/unlucky-launcher-iced* "$BUILD_DIR"/ull* || true
fi
# (M190)版本资源门禁:**读产物字节**,不信构建日志。`build.rs` 里写了 `CompanyName` 不等于
# 它进了 PE——资源在链接期被丢掉、或被后处理覆盖,都只有读回来才看得见。判据(字段值、四段式
# 版本号、FIXEDFILEINFO 的 dwFileVersion)全在 `packaging/check_pe_version.py`,本机可跑。
# 只在 Windows 槽位跑:Linux/macOS 产物没有 PE 头,那边 `build.rs` 整段不执行(见
# `packaging/pe_version.rs` 文件头)。**工具缺失也 fail**:找不到 python 就等于门禁失效。
# 步骤体本身在 `packaging/ci-pe-version-gate.sh`:读回 + 同源取版本号 + 失败逐条转 `::error::`
# 注解(M264 从本文件拆出,给 400 行硬门禁腾地方;判据没动,仍由检查器 exit 1)。
# (M190)版本资源门禁:**读产物字节**,不信构建日志。`build.rs` 里写了 `CompanyName` 不等于它进了 PE——
# 资源在链接期被丢掉、或被后处理覆盖,都只有读回来才看得见。判据(字段值、四段式版本号、FIXEDFILEINFO
# 的 dwFileVersion)全在 `packaging/check_pe_version.py`,本机可跑。只在 Windows 槽位跑:Linux/macOS 产物
# 没有 PE 头,那边 `build.rs` 整段不执行(见 `packaging/pe_version.rs` 文件头)。**工具缺失也 fail**。
# 步骤体本身在 `packaging/ci-pe-version-gate.sh`:读回 + 同源取版本号 + 失败逐条转 `::error::` 注解
# (M264 从本文件拆出,给 400 行硬门禁腾地方;判据没动,仍由检查器 exit 1)。
- name: PE 版本资源读回(CompanyName / ProductName / 版权 / 版本号真的在产物里)
if: runner.os == 'Windows'
shell: bash
@@ -379,9 +380,8 @@ jobs:
retention-days: 14
# (M142/M164)arm64 若红在别的步骤(cargo / 链接 / 打包 / 导入表门禁),也要有一条注解说清「红在哪」和
# 「不挡发布」——只剩一句 `Process completed with exit code 1.` 的教训见上面那条体检步骤。
# M164 起本槽位的红**只可能来自真实构建链路**(体检已降级为取证、永不 exit 1),所以这条注解同时也是
# 「arm64 到底能不能编出来」的答案所在:红了就把上面的编译器/链接器原文当根因,别再回去改体检。
# 「不挡发布」——只剩一句 `Process completed with exit code 1.` 的教训见上面那条体检步骤。M164 起本槽位的红
# **只可能来自真实构建链路**(体检永不 exit 1):红了就把上面的编译器/链接器原文当根因,别再回去改体检。
# M184:Linux arm64 进来(它现在是 `mode: ci`,tag 推送默认就编),措辞按槽位分支 —— 两个 arm64 都**不在**发布名单里,
# 但理由不同:windows-arm64 等「能不能出 PE」的答案,linux-arm64 等「真机跑不跑得起来」的答案。
- name: arm64 失败收尾注解(红不挡发布)