From fad37e430c6f5147f8b9ba50cebb78b69f75ee2f Mon Sep 17 00:00:00 2001 From: liujam <100055751+existyay@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:52:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(build):=20clash-rs=20=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=8D=87=E5=88=B0=20v0.20.23-alpha=EF=BC=8CBUILD-INFO=20?= =?UTF-8?q?=E8=AE=B0=E5=85=A8=E4=BB=A3=E7=90=86=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一、核心钉住值 v0.20.10-alpha -> v0.20.23-alpha feed 在 2026-10-20~10-22 前进了 9 个 commit,其中 67ffca956 把 clash-rs 包从 nikki-rs/files/ 挪到仓库根,并让它的 CI 在构建时追最新 release (scripts/packaging/update-clash-rs.sh 现算每个架构的哈希)。也就是说 feed 的 Makefile 里那份哈希随时会被它的 CI 改写:跟着它走,同一个 feed 提交在不同日子会 构建出不同核心。显式钉住因此比以前更必要,而原来的钉住值已经落后 13 个 release。 升级前核了两件事,都不是推断: * 字段面:v0.20.10 -> v0.20.23,EbpfConfig / EbpfLanConfig / EbpfHostConfig / EbpfTargetConfig / Dns2Config / Dns2UpstreamDef 六个结构体逐字段比对,字段数与 名字完全一致。feed 现在写的 19 个 ebpf 键与 9 个 dns2 键两版核心都认(dns2 upstreams 里的 type 是 pub r#type + #[serde(rename = "type")],不是未知字段)。 * eBPF 功能:v0.20.23 的发布矩阵给每个 Linux 目标都是 extra-features: "jemallocator,ebpf",另外单独有一个 build-ebpf 作业编字节码。 这一条必须查 —— def.rs 里的 ebpf 结构体不受 feature gate 控制,所以 --strict-config 能解析并不能证明功能真的编进去了。 顺带修掉一处会让例行升级变红的地方:checks.yml 原来断言字面量 want_v="0.20.10_alpha",而它自己的注释正写着"bumping this is fine"。改成断言 "want_v 与 want_url 指向同一版本、且不低于 0.20.10(第一个认识 proxy-src-macs 的 release)"。三种情形都跑过:当前版本通过、降到 0.20.9 报错、版本与 URL 不一致报错。 二、BUILD-INFO.txt 补上六个开关 10-19 那个周更镜像只记了 fancontrol / netmode / wwand / mt5700m / upnp / adblock / dockerman,而它实际启用了 mosdns、并且 ENABLE_EBPF_PROXY_KERNEL=true。也就是说从 产物本身看不出一个固件带的是哪套代理前端,排查"这台机器为什么没有某个页面"时第一步 得去翻 70 万行的构建日志。 补上 enable_nikki / enable_openclash / enable_mosdns / enable_homeproxy / enable_adguardhome / enable_ebpf_proxy_kernel 六个键。六个变量在 local-build.sh 里 都有默认值,set -u 下引用是安全的。 本地门禁对着新版本重跑:本仓库钉住 v0.20.23-alpha,五个架构哈希与 release 的 digest 逐条一致;Nikki-RS 四处锚点 4/4 仍在(含挪到仓库根之后的 clash-rs/Makefile)。 --- .github/workflows/checks.yml | 37 ++++++++++++++++++----- docs/engineering.md | 58 ++++++++++++++++++++++++++++-------- scripts/local-build.sh | 37 ++++++++++++++++++----- 3 files changed, 104 insertions(+), 28 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a951f14..8e2168e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -627,13 +627,36 @@ jobs: want scripts/local-build.sh 'pin_clash_rs' \ "local-build.sh has the clash-rs pin" - # The exact version the field-skew analysis concluded with. Bumping - # this is fine; removing it, or dropping to a version without - # proxy-src-macs, is the regression. - want scripts/local-build.sh 'want_v="0.20.10_alpha"' \ - "the pin names a core version that understands proxy-src-macs" - want scripts/local-build.sh 'releases/download/v0.20.10-alpha/' \ - "the pin points at the matching release" + # The pin must name *a* version whose release carries proxy-src-macs, + # and the download URL has to point at that same version — a mismatch + # is what makes the build fetch a core its hashes do not describe. + # + # Deliberately NOT a literal version. The previous form asserted + # `want_v="0.20.10_alpha"`, which turned the routine upgrade to + # 0.20.23 into a red required check even though the check's own + # comment said bumping was fine. What must be caught is a *regression* + # (a pin below the floor, or a URL that disagrees with the version), + # not a version number that moved. + pinned=$(sed -n 's/^ local want_v="\([0-9][0-9.]*\)_alpha"$/\1/p' scripts/local-build.sh | head -n1) + url_ver=$(sed -n 's|^ local want_url=".*/download/v\([0-9.]*\)-alpha/"$|\1|p' scripts/local-build.sh | head -n1) + if [ -z "$pinned" ]; then + echo " FAIL the pin does not name an X.Y.Z_alpha version" + fail=1 + elif [ "$pinned" != "$url_ver" ]; then + echo " FAIL want_v=${pinned} but want_url points at '${url_ver}'" + fail=1 + else + echo " OK the pin is self-consistent: version ${pinned} and its release URL" + # v0.20.10-alpha is the first release that knows proxy-src-macs + # (0.20.1 .. 0.20.9 silently drop it), so it is the floor. + extra=${pinned#0.} + minor=${extra%%.*} + patch=${pinned##*.} + if [ "$minor" -lt 20 ] || { [ "$minor" -eq 20 ] && [ "$patch" -lt 10 ]; }; then + echo " FAIL ${pinned}-alpha is below the proxy-src-macs floor (0.20.10)" + fail=1 + fi + fi # The pin is a no-op if it never runs. want scripts/local-build.sh '^ pin_clash_rs$' \ "pin_clash_rs is called from main" diff --git a/docs/engineering.md b/docs/engineering.md index be8db90..9db43f8 100644 --- a/docs/engineering.md +++ b/docs/engineering.md @@ -289,7 +289,7 @@ ENABLE_WWAND=false ENABLE_MT5700M=true ENABLE_NIKKI=true THREADS=8 ./scripts/loc | `openwrt-mediatek-filogic-hiveton_h5000m-initramfs-kernel.bin` | initramfs 恢复镜像 | | `*-targz-rootfs.tar.gz` / `*-rootfs.tar.gz` | rootfs 压缩包(本配置 `CONFIG_TARGET_ROOTFS_TARGZ=y`) | | `*.manifest` | 镜像内实际安装的包清单(约 274 个) | -| `BUILD-INFO.txt` | 本次上游 revision、kernel 版本与 **kernel ABI** | +| `BUILD-INFO.txt` | 本次上游 revision、kernel 版本与 **kernel ABI**,以及全部功能开关(`enable_fancontrol`…`enable_dockerman`,2026-10-24 起补上 `enable_nikki`/`enable_openclash`/`enable_mosdns`/`enable_homeproxy`/`enable_adguardhome`/`enable_ebpf_proxy_kernel` —— 在此之前从产物里看不出这个镜像带的是哪套代理前端) | | `profiles.json` | 上游生成的版本/内核元数据(ABI 的权威来源) | | `sha256sums` | 校验和 | | `enabled-packages.txt` | 进入最终 `.config` 的符号清单(约 297 个),见下方说明 | @@ -951,9 +951,16 @@ MACs」→ UCI 存下来 → 生成的配置里带着这个键 → 核心把它 直接报错,而不是静默地什么都没钉。 `checks.yml` 里的「Verify the clash-rs pin covers the eBPF fields the feed emits」 -把这些约束变成门禁:pin 函数存在、版本号与 release URL 指向 -`0.20.10-alpha`、`main` 里真的调用了它、feed 仍从移动分支克隆、五个架构哈希都是 -完整 sha256。这是文本级断言 —— 能在 PR 阶段抓住「pin 被回退」,而不需要下载二进制。 +把这些约束变成门禁:pin 函数存在且由 `main` 调用、`want_v` 与 `want_url` 指向**同一个** +版本、该版本不低于 `0.20.10-alpha`(第一个认识 `proxy-src-macs` 的 release)、feed 仍从 +移动分支克隆、五个架构哈希都是完整 sha256。这是文本级断言 —— 能在 PR 阶段抓住「pin 被 +回退」,而不需要下载二进制。 + +断言刻意**不**写死版本号:最初那版断言的是 `want_v="0.20.10_alpha"` 这个字面量,于是 +2026-10-24 把 pin 升到 `0.20.23-alpha` 时,一次例行升级就把必需检查变红 —— 而那段断言 +自己的注释正写着"bumping this is fine"。要抓的是**回退**(低于下限、或 URL 与版本不 +一致),不是版本号本身动了。三种情形都用改后的判定跑过:当前版本通过、降到 `0.20.9` +报错、`want_v` 与 URL 不一致报错。 #### adblock-fast 的两类提示:一类是缺包,一类是缺 DNS 后端 @@ -1963,7 +1970,7 @@ prefix 再用 `PKG_CONFIG_PATH` 指过去;flex/bison 是硬依赖,因为补 | mt76 | `467ae2e3f190baf737e15e318906da0dc7d0ab91` | exit 0,无 offset | | openwrt 树 | `99426e4` | `patches/0001`、`patches/0002` 均 `git apply --check` exit 0 | | Nikki-RS | `CHKayanami/OpenWrt-nikki-rs@main` | 四处锚点都在,且都还没有被上游自己补上 | -| clash-rs | `v0.20.10-alpha` | 五个架构的资产都还在,sha256 与 `pin_clash_rs()` 逐条一致 | +| clash-rs | `v0.20.23-alpha`(2026-10-24 由 `v0.20.10-alpha` 升上来) | 五个架构的资产都还在,sha256 与 `pin_clash_rs()` 逐条一致;Linux 目标的构建矩阵仍是 `extra-features: "jemallocator,ebpf"` | 同一套补丁对 `e1bd479b`(main 在这轮对齐期间又前进的一个 revision)同样全部通过 —— 那两个 revision 的 `nftables` / `libnftnl` 版本与 `firewall4` / `mt76` 的 @@ -2032,14 +2039,37 @@ flags),所以两侧根本不交换这三个属性中的任何一个;内核 规则。`scripts/verify-upstream-integration.sh` 当然也不会替你发现这个 —— 它不是 补丁适用性问题,是补丁本身的语义问题。 -**b) `clash-rs` 的钉住版本刻意落后于上游 release。** +**b) `clash-rs` 的钉住版本与上游 release 的关系(2026-10-24 已升级)。** + +`pin_clash_rs()` 现在钉 `0.20.23_alpha`(当天最新的 release)。上一版这里写的是"刻意 +落后于上游":当时的依据是"钉住的目的正是让内核版本与 feed 当前那份配置相配,而 feed +的 main 自 2026-09-26 起没有再动过"。**那个前提已经不再成立**,所以结论也换了。 -`pin_clash_rs()` 钉的是 `0.20.10_alpha`,而上游已经发到 `0.20.22-alpha`。这不是 -"忘了升":钉住的目的正是**让内核版本与 feed 当前那份配置相配**,而 feed 的 main -自 2026-09-26 起没有再动过。升内核要重新核对配置项(字段可能改名或移除),属于 -需要实测的改动,不是版本号替换。真正的风险不是"有新版",而是**那个 release 被 -撤下** —— 那时构建会在下载时 404,所以检查脚本会把该 release 的五个资产哈希逐条 -核对。 +2026-10-20~10-22,feed 的 main 前进了 9 个 commit,其中三个直接冲着核心来: + +| feed 提交 | 内容 | 对本工程的意义 | +| --- | --- | --- | +| `67ffca956` | always package the latest clash-rs release | `clash-rs/` 从 `nikki-rs/files/` 挪到仓库根,且 feed 的 CI 在构建时**现算**最新 release 的哈希 | +| `90dd011d4` / `392f60357` | 用原生架构打包替换 SDK 矩阵 | 包产物的分发方式变了,钉住的核心来源没变 | +| `a7be43ab1` | feat(dns2): upstream settings + ordered routing rules | `mixin.uc` 现在写 9 个 `dns2.*` 键,需要核心都认识 | + +于是做了三件事,都是可核对的: + +1. **字段面向后兼容**:把 `clash-lib/src/config/def.rs` 的 `EbpfConfig` / + `EbpfLanConfig` / `EbpfHostConfig` / `EbpfTargetConfig` / `Dns2Config` / + `Dns2UpstreamDef` 在 `v0.20.10-alpha` 与 `v0.20.23-alpha` 之间逐字段比对,**结构体 + 与字段数完全一致**(11/5/3/4/13/7)。feed 写的 19 个 ebpf 键与 9 个 dns2 键两版核心 + 都认(`type` 是 `pub r#type` + `#[serde(rename = "type")]`,不是未知字段)。 +2. **eBPF 功能没丢**:`v0.20.23-alpha` 的发布矩阵给每个 Linux 目标都是 + `extra-features: "jemallocator,ebpf"`,另外单独有一个 `build-ebpf` 作业编字节码。 + 这一点必须查,因为 `def.rs` 里的 ebpf 结构体**不受 feature gate 控制** —— 只看 + `--strict-config` 能不能解析,证明不了功能真的编进去了。 +3. **钉住仍然必要,而且比以前更必要**:feed 的 CI 现在会在构建时追最新 release,同一份 + feed 提交在不同日子会产出不同核心。显式钉住是这个工程唯一能保住"可复现"的手段。 + +还没做的那一步:真机/真构建验证。字段与特性是静态核对,运行期行为(eBPF 卸载、fakeip +范围、DNS 缓存)只有开 `ENABLE_NIKKI=true` 跑一次完整构建才说得清 —— 那次构建会把它写进 +`BUILD-INFO.txt`(这一版起 `enable_nikki` 等六个开关也会被记录,见下)。 ### 7. 与上游变化的交互(快照) @@ -2055,7 +2085,9 @@ MLO 系列)是**跟着 main 自动到来**的,本工程不需要"合并"动 | `mt76` 更新到 `467ae2e3` | `mt76-patches/100-mt7996-always-fill-txwi.patch` 的落点 | **已验证**:零 offset 适用 | | `kernel: backport fix for MTK WED 2.0 WDMA TX hang issue`、`generic: restore EEE support on mt753x and mtk_eth_soc` | 直接落在本工程所用 SoC 的以太网路径上 | 跟随 main 自动到来;属于本工程真正关心的"性能/稳定性"合并,**无需动作** | | hostapd / mac80211 的 MLO 系列(约 117 个 commit) | MT7992 是 Wi-Fi 7 | 能力随 main 自动到来;实际生效与否只有真机能答 | -| `CHKayanami/clash-rs` 已发到 `v0.20.22-alpha` | `pin_clash_rs()` 仍钉 `0.20.10_alpha` | **刻意不动**,理由见上面 6.b | +| `CHKayanami/clash-rs` 已发到 `v0.20.23-alpha` | `pin_clash_rs()` 原钉 `0.20.10_alpha` | **2026-10-24 已升到 `0.20.23-alpha`**:字段面两版一致、eBPF feature 仍在,理由见上面 6.b | +| `OpenWrt-nikki-rs` 把 `clash-rs/` 挪到仓库根 | 四处锚点里的 `clash-rs/Makefile` | **无冲突**:本仓库的锚点本来就按 `clash-rs/Makefile` 取,2026-10-24 全量核对 4/4 通过 | +| feed 的 `mixin.uc` 新增 9 个 `dns2.*` 键(`a7be43ab1`) | 核心的 `Dns2Config` / `Dns2UpstreamDef` | **无缺口**:9 个键两版核心都认;`--ebpf-config` 那道检查只覆盖 ebpf,dns2 这一面靠上面的逐字段比对 | 这类"上游变化 → 本工程假设"的核对**不该靠人定期做,因为一定会忘**。§3 的脚本把 其中可机检的部分(补丁适用性、锚点、钉住版本)变成了每个 PR 都会跑的断言; diff --git a/scripts/local-build.sh b/scripts/local-build.sh index 86926e1..ceb0e14 100755 --- a/scripts/local-build.sh +++ b/scripts/local-build.sh @@ -1042,13 +1042,28 @@ pin_sing_box() { # be re-synced on every such commit. Pinning the core keeps the two halves in # step by construction. # +# 2026-10-22 upstream raised the stakes: commit 67ffca956 ("fix: always package +# the latest clash-rs release") moved the package from `nikki-rs/files/clash-rs/` +# to the repository root and made the feed's CI resolve the newest release at +# build time (`scripts/packaging/update-clash-rs.sh` recomputes every hash). So +# the hash written in the feed's Makefile can change under us without a feed +# commit, and following it would make the same feed revision build a different +# core on different days. An explicit pin is therefore the only way to keep a +# build reproducible; upgrading is a decision made here, not by the feed's +# release cadence. The pin was moved forward to v0.20.23-alpha on 2026-10-24 +# (v0.20.10-alpha -> 13 releases behind): field surface is unchanged between +# them (verified against `clash-lib/src/config/def.rs`), and the published +# minimal-* assets still carry the `ebpf` feature — v0.20.23's matrix sets +# `extra-features: "jemallocator,ebpf"` for every Linux target and builds the +# bytecode in a dedicated job, so this is not a feature downgrade. +# # The rewrite is a plain text substitution with a post-check, following # pin_sing_box. It dies loudly rather than silently pinning nothing if upstream # restructures the Makefile. pin_clash_rs() { local mk="${SRC}/package/OpenWrt-nikki-rs/clash-rs/Makefile" - local want_v="0.20.10_alpha" - local want_url="https://github.com/CHKayanami/clash-rs/releases/download/v0.20.10-alpha/" + local want_v="0.20.23_alpha" + local want_url="https://github.com/CHKayanami/clash-rs/releases/download/v0.20.23-alpha/" # Only when the package is actually part of this build. Every ENABLE_NIKKI # path that pulls the front-end also pulls the core, and @@ -1084,11 +1099,11 @@ pin_clash_rs() { # differs from HASH, so the download stage replaces it — but only because # PKG_HASH changes here. That is also why the hash has to be right: a # wrong-but-stable hash would keep the old binary and every check would pass. - local aarch64_h="b6ad0f7014d6fc03a9ab56cd1a61ea61574db9e3e483a637cc04bf022bf6c065" - local x86_64_h="1ac876892965b3249659931c0c6f7e3dc87673daeab90f4a9744ff2caf5fb139" - local armv7_h="33321d4b6311714a91992fe0a5f5f78de1e169653e9603d9556deb01813400c9" - local riscv64_h="bc9d29081f7a665e82135045c30eedb6d37b642013cd20ef056c109f588b35ad" - local i686_h="834cc83b4d34f0c06126f51d4de0584ce74540c0bcf3e1edc67b342e2fbf4136" + local aarch64_h="f5a6143dc46e29a4668d111f229a7eaf55c4348979817e05b7d2e1cb541eb199" + local x86_64_h="50ba876081c35a2f3d4936ad370e64631c5a20a4e0c8af991bc310caaf8960b9" + local armv7_h="938d196fb00edbfd9e5c9145334caf20f189b916bd873f07c7bd26da9a5f3213" + local riscv64_h="0b3bf62f216b6503b7d5c6a322c1a072c8ee598c11f8218d7d09bc87bc91a6d3" + local i686_h="c7e30586bb14e1816caa0011d4e2c74111c74dc9ea4a8b37cf7da458e9420153" # Replace the version, the release URL and every architecture hash. Done # with awk rather than sed because the hash lines are indented and identical @@ -1156,7 +1171,7 @@ pin_clash_rs() { [ "$pinned" -gt 0 ] || die "clash-rs pin matched no architecture block in ${mk}; the Makefile layout changed and pin_clash_rs needs updating" - log "clash-rs pinned to ${want_v} (the feed's eBPF page emits proxy-src-macs and quic, which v0.20.0-alpha lacks)" + log "clash-rs pinned to ${want_v} (the feed's Makefile still says v0.20.0-alpha and its CI now chases the newest release at build time; this pin, not the feed, decides which core the image gets)" return 0 } @@ -3528,6 +3543,12 @@ enable_mt5700m=${ENABLE_MT5700M} enable_upnp=${ENABLE_UPNP} enable_adblock=${ENABLE_ADBLOCK} enable_dockerman=${ENABLE_DOCKERMAN} +enable_nikki=${ENABLE_NIKKI} +enable_openclash=${ENABLE_OPENCLASH} +enable_mosdns=${ENABLE_MOSDNS} +enable_homeproxy=${ENABLE_HOMEPROXY} +enable_adguardhome=${ENABLE_ADGUARDHOME} +enable_ebpf_proxy_kernel=${ENABLE_EBPF_PROXY_KERNEL} EOF log "Wrote ${dest}/BUILD-INFO.txt (kernel ${kver:-?}, abi ${abi:-?})"