Commit Graph
21 Commits
Author SHA1 Message Date
AskaEth a219a21db8 fix(m190): r2 复审——dalek strict 口径照实测写正、补小阶 R 用例、锁里 toml_datetime 退回 1.1.2
三条 P2 都是「文档/注释与事实不符 + 一条上报事实错了」,代码行为没变。

P2-1 dalek 的 strict vs 非 strict 口径改成正测试出来的样子:
* `VerifyingKey::from_bytes` **不**校验编码规范性(`y = p` 的非规范写法返回 Ok,`is_weak() == true`),
  先前写的「要求规范编码」是错的;
* 非 strict 的 `verify` **也**查 `S` 规范性 —— 先前把它写成「不做可锻检查」是半错;
* 于是这批拒绝类用例里,strict 与非 strict 唯一的分歧是**小阶点**:承重的是
  `small_order_public_key_is_rejected`,其余几条(malleable / non_canonical_r / empty)换掉 strict 后
  **仍然绿**,文件头的表与用例注释都改成这个口径,别让人误以为它们也是守卫。
* 新补 `small_order_r_is_rejected_only_by_strict`:R = 单位元 + S = k·a mod L 的伪造,**两边都断言**
  —— dalek 的非 strict `verify` 接受它、我们的 `verify` 拒绝。这是第二条「只由 strict 拒」的判据。
* `non_canonical_r_encoding_is_rejected` 里那条「非规范公钥编码」原来对两种实现都绿(等于没测),
  现在断言 dalek 的**真实**行为(`from_bytes(...).is_ok()` + `is_weak()`),dalek 升级改口径会红。

P2-2 `packaging/sign_manifest.py` 里指向已删除 `ed25519/{field,group}.rs` 的旧注释删掉(那段还写着
「same formulas」,而两套实现现在是**互相独立**才构成交叉验证)。

P2-3 `Cargo.lock`:`toml_datetime` 从 1.1.3 **退回 1.1.2**(`cargo update -p toml_datetime --precise
1.1.2` 实测可降、`cargo metadata --locked` 自洽)。先前报告里「被 `toml` 拒绝所以无法避免」的理由
**不成立**,已在报告里更正。现在相对 `44a0c87` 的锁变更是**纯新增**:16 个包、零既有版本变动。

变异证明(本轮按要求重跑):把 `verify_strict` 换成 `verify`(并补 `Verifier` trait 使其可编译)
⇒ `test result: FAILED. 110 passed; 2 failed`,红的两条正是
`small_order_public_key_is_rejected` 与 `small_order_r_is_rejected_only_by_strict`;其余拒绝类用例全绿
(与修正后的口径一致)。恢复后 `112 passed; 0 failed`。
2026-11-09 02:33:08 +08:00
AskaEth 918d826645 fix(m190): r1 复审——0x01 修成 \1 + YAML 门禁、被调方声明 secret、拆两个超限测试文件、验签换 ed25519-dalek
四条 P1 + 四条 P2 一并处置(不变的是判据,变的是「谁在承重」)。

P1-1 `.github/workflows/release-build.yml` 的 sed 替换串是裸 0x01(本意 `\1`):GitHub 解析该文件会
失败 ⇒ 整个 run 起不来;即便容忍,版本号也会被求值成单字节,PE 判据报「与 Cargo.toml 不同源?」把
排障引向错方向。修成字面 `\1`,并新增 `packaging/check_workflows_yaml.py`(真 YAML 解析器 parse
全部 workflow + 裸 C0 控制字符字节判据;带 --self-test 注入 0x01/坏结构断言必红),接进
`release-guard.yml` 两步。文本扫描型守卫看不见这类字节,这道门禁专门挡它。

P1-2 `release-publish.yml` 没在 `on.workflow_call.secrets` 里声明 `ULL_MANIFEST_SIGNING_KEY`,调用方
却传了:可复用工作流只拿得到自己声明过的 secret ⇒ 用户配好钥也会在签名步红,而错误文案反过来指控
Secret 没配。补声明(required: false,fail-closed 由步内 ::error:: + exit 1 承担,同
ULL_MSA_CLIENT_ID 的模式)。

P1-3 两个测试支撑文件越过 400 行守卫:签名夹具拆到 `tests/support_sign.rs`(support.rs 458→392),
CLI e2e 的签名部分拆到 `support/update_e2e_sign.rs`(update_e2e.rs 406→348)。纯搬家。

P2-2 「唯一收口点」改成结构事实:从 `UpdateSource` 删掉 `fetch_manifest` 默认实现,调用方只能拿
`verified_manifest(&source, channel)`(源只交原文,想绕过验签就得自己重写解析与渠道判据)。
P2-3 文档写的第 ④ 条命令补进流水线:`sign_manifest.py --verify-pinned` 用 trust.rs 里钉住的公钥
回验刚落盘的 .sig(不碰私钥),发布侧因此验证的是「客户端的判据」而不是「签名脚本的自我声明」。
P2-4 随自写实现一起删除(`Fe` 的错误 PartialEq 不再存在)。
P2-5 app-iced/build.rs 注释里那个 windows_subsystem 属性本分支不存在(M196 才有),改为说明
「本文件只写版本资源,子系统由 rustc 属性决定,check_pe_version.py 会把 subsystem 打出来」。
P2-6 坏清单不再带 error.path 是有意降级(来源 id 在 message 里),保持。

裁定项:自写 Ed25519 换成 `ed25519-dalek::verify_strict`(信任锚不许自写密码学;此刻信任表还是空表,
零兼容负担)。删除 `update/ed25519/{field,group,mod}.rs`(含只服务它们的单测),新 `update/ed25519.rs`
只剩一个薄封装;拒绝类用例(可锻 S+L / 小阶公钥 / 非规范 R / 空签名 / 未知 key)**一条没删**,
改成钉 dalek 的 strict 语义——实测 dalek 对全部五类的结论与旧实现一致(非规范 R 的拒绝理由不同:
dalek 走「小阶点」而非「编码非规范」,用例注释里写明)。签名侧留在 `packaging/ed25519_ref.py`
(纯 Python、零 pip 依赖),`--self-test` 用 RFC 8032 同一组向量钉两边。
2026-11-09 02:14:32 +08:00
AskaEth b7d9b99e12 feat(m190): 产物署名(PE VERSIONINFO)+ 清单 Ed25519 多签名与更新前验签
两件事:让「这是谁发的」在产物与清单上都留下可验证的痕迹。

① PE 版本资源(两个 bin):`packaging/pe_version.rs` 是唯一口径(CompanyName
   「Unlucky Launcher Project」/ ProductName、FileDescription「Unlucky Launcher」/
   LegalCopyright「© 2026 … · Apache-2.0」/ 四段式版本号与 Cargo.toml 同源),被
   `crates/{cli,app-iced}/build.rs` 各 include 一次;非 windows 目标整段不执行。
   判据是读产物字节的 `packaging/check_pe_version.py`(CI 的 Windows 槽位)。

② 清单签名:`manifest.json` 旁边多一份 `manifest.json.sig`(分离签名,签的是清单**原始
   字节**),信封是 `{format, signatures:[{alg,key_id,sig,manifest_sha256}]}` —— 多签名数组,
   任意一条命中钉住的钥即放行(换钥窗口)。信任锚是 `crates/core/src/update/trust.rs`
   里的编译期公钥表(正式表为空 = fail-closed,测试钥只在 cfg(test)/update-test-source 里)。
   管线顺序改成「先验签、再解析」:`UpdateSource` 只交原文 + `.sig`,`verified_manifest`
   是唯一收口点,`Job::apply` 因此在取包**之前**就拒。新增五类错误分类
   (signature_missing / malformed / unknown_key / keyring_empty / invalid ),全部不可重试,
   CLI 的 hint 单独说人话。

③ Ed25519 验签自带实现(`update/ed25519/*`,零新依赖):scope 不含 core 的 Cargo.toml,
   用 RFC 8032 §7.1 向量 + 可锻/小阶/非规范编码的拒绝用例钉住;签名侧是
   `packaging/sign_manifest.py`(纯 Python 参考实现,零 pip 依赖,`--self-test` 同向量)。

④ 守门:`release-publish.yml` 加签名步(`set +x`,只回显公钥;没 Secret / 钥没钉 / 签不上
   一律红 ⇒ 不出 Release);`release-build.yml` 加 PE 资源读回;`release-guard.yml` 跑两个
   脚本的 `--self-test`。密钥纪律与轮换(旧钥签新钥)写在 `packaging/docs/signing.md`。

Cargo.lock:新增 winresource(build-dependency)与 toml 系;toml_datetime 1.1.2→1.1.3 是
toml 1.2.1 的硬要求(`cargo update --precise 1.1.2` 已实测被拒)。
2026-11-09 01:59:05 +08:00
AskaEth 7e8b8740c4 fix(m184): r1 —— 证据账不再把「机制会跑」写成「已有运行时证据」,并收 5 条文档漂移
P2-1(唯一阻塞):`packaging/docs/linux-evidence.md` 里 `ull --version` 冒烟那条被写成
「两个槽位都有一份 CLI 运行时证据」——`make_artifacts.py::smoke_version` 只保证**机制**
(`slot == host_slot()` 时真跑),而仓库里找不到任何一次 linux-arm64 成功出包的记录
(`git grep -rn all_slots DevDocs/` 零命中),所以 arm64 侧运行时证据为零。§1 表格与
§5 改成「机制上会真跑 / 截至本分支没有那一次记录」,§6 解锁条件同步补一句。

P3 漂移:
1. `release-publish.yml` 注释 8 条 → 11 条自测回归(与脚本/其它三处一致)。
2. `packaging/docs/profiles-and-measurements.md` 同上 8 → 11。
3. `check_publish_slots.py` 注释示例 `PUBLISH_SLOTS: "windows-x64"` → 两档现值。
4. `release-build.yml` 注释「x64(当前唯一在发的槽位)」→ 两档已发布,并点名
   linux-x64 的证据在自己的 xvfb 冒烟步。
5. `linux-evidence.md` 「20 倍以上余量」→ **8~37 倍**,并点明最紧的是亮度 σ
   (24.06/3.0≈8×,与 check_screenshot.py 自述的 1/5~1/8 一致)。

自查(复审点名的五处之外):`只发 windows-x64` / `Windows only` / `暂未发布` /
`once published` / `8 条` 全仓再扫一遍,release-notes / README ×2 /
artifacts-and-version 剩下的都是准确表述(如「the only Windows asset」指 windows_x64.exe)。

验证:check_publish_slots --self-test EXIT=0、check_test_source_off EXIT=0,另
check_publish_slots(source) / check_release_semantics(+--self-mutation) /
make_manifest --self-test 均 EXIT=0。本轮无 cargo 构建,未创建 CARGO_TARGET_DIR。
2026-11-08 21:35:26 +08:00
AskaEth 2813e2984c ci(m184): linux-x64 进发布名单,两档 Linux 转 mode=ci,arm64 只构建不发布
发布流水线默认从 2 个槽位变 4 个:`linux-x64`(ubuntu-22.04)与 `linux-arm64`
(ubuntu-22.04-arm)的 mode 由 probe 改 ci,tag 推送默认就编。

构建 ≠ 发布仍由 `PUBLISH_SLOTS` 决定,本轮只加 `linux-x64`:
- `linux-x64` 有运行时证据 —— release 产物在 Linux 真机(Ubuntu 22.04 + WSLg 真窗口)
  渲出一帧、[theme]/[backend] final 日志齐全、像素判定通过,真窗口 1180x760 从 Windows
  侧截屏可见;`release-build.yml` 另加一条**同槽位**的 xvfb 运行时门禁(只在
  `inputs.slot == 'linux-x64'` 上跑,不给没验过的 arm64 造红)。
- `linux-arm64` 只有构建证据、运行时零证据,产物照出 artifact 取证但不发布;解锁条件
  (真机渲出一帧 + 主题日志)写进 release-notes 与 §9。

守卫跟着扩(`packaging/check_publish_slots.py`):
- 自测从 8 条扩到 11 条,新增「声明的 linux-x64 构建红了 ⇒ 必须红(挡住发布)」、
  「linux-arm64 有产物但没声明 ⇒ 只警告」、「提前把 linux-arm64 写进名单 ⇒ 缺产物必红」;
- probe 档样例从 linux-x64 换成仍是 probe 的 macos-arm64(否则该用例会从「必须红」
  变成「本来该绿」);
- 新增「mode=ci 却本次没产物 ⇒ ::warning::」:红一档不能只表现为表里少一行。

文档矩阵同步(release-notes §Downloads/§Support matrix、compliance §9/§10、
artifacts §1、build-and-ci §3、README ×2),两份文档都把 Linux 写成
「已构建并发布(x64)/ 已构建未发布(arm64)」,并把新发现写实:Linux 上整窗透明
与圆角没有代码路径(hwnd=none / corner=policy=system),不是「未验」;Linux 上
`ull update apply` 会拒绝 portable-zip(清单里现在有 linux-x64 产物)。

证据账(含实测输出、CI 增量估算、未验清单):packaging/docs/linux-evidence.md。
2026-11-08 21:35:26 +08:00
AskaEth 5ff6cdb38b fix(m164): 复审三条 P2 —— vcvarsall 用 wrapper .bat 真抓到 LIB/INCLUDE、ls 进 arm64 分支、两处数字与一句顺序承诺
P2-1(核心:上一轮的 ② 在 git-bash 下是死代码):`cmd //c "\"${vcvars//\//\}\" arm64 …"`
不行——MSYS 把参数里的 `"` 改写成 `\"` 交给 cmd.exe,cmd 的 `/c` 引号剥离接不住,LIB/INCLUDE
全空,只剩 `set` 的空输出;于是 ③「显式 link.exe」在**没有 LIB** 的状态下生效,正是本步注释里
写的那种会造出 `LNK1181: cannot open input file 'kernel32.lib'` 的组合。改法(复审验过的路线):
往 `$RUNNER_TEMP` 写一个 **CRLF** 的 wrapper `.bat`(`call "<vcvarsall>" arm64 >/dev/null 2>&1` + `set`),
`cmd //c` 只收**不含引号**的相对文件名(先 cd 进去)——引号留在 .bat 内部由 cmd 解析,路径带空格
也不受影响。同源的 ③ 也补齐:`vswhere -find` 的 `vcvarsall.bat` 与两个 `link.exe` 候选都改成
「反斜杠形态 + 正斜杠形态各试一次」(和同文件 dumpbin 步同规矩),不再赌一种形态。

P2-2:cargo build 步末尾那条 `ls -l` 移到 `if [ "$SLOT" = "windows-arm64" ]` 门内(并加 `|| true`):
x64 是当前唯一在发的槽位,不给它加一个失败面。

P2-3:① `release-publish.yml` 步数改对——主线 7 步、本分支 8 步(`release.yml` 与
`packaging/docs/build-and-ci.md` 两处都改,并列出 8 步是哪几步);② `check_publish_slots.py`
的 report() 全部改走 **stdout**:注解(`::error::`/`::warning::`)在 stdout 上同样被 Actions 识别,
而分两个流写会让「表格 → 注解」的顺序不可控(原注释承诺的顺序当时并不成立)。顺带在
`build-and-ci.md` 的 arm64 工具链段补一句「`cmd //c` 别收带引号的路径、要 wrapper .bat」,
免得下一个 agent 再把 ② 写回死代码。

本地证据(同一 worktree 复跑):
- P2-1 新证据:把本步 `run:` 块原样抽出,用 `bash --noprofile --norc -eo pipefail`(与 runner 同
  语义)、假 vswhere + **路径带空格**的假 `vcvarsall.bat`(真 cmd)跑:新写法 LIB/INCLUDE/linker
  三行全部写进 `$GITHUB_ENV`、rc=0;同一套假件跑 e858664 的老写法 ⇒ LIB/INCLUDE 全空、只有 linker
  (与复审结果一致)。降级路径也复测:vswhere 不存在 ⇒ 只 1 条 warning、rc=0;vswhere 不给
  vcvarsall ⇒ ② warning、③ 仍拿到 Hostx64 兜底、rc=0。
- 体检步 5 场景(无 VS / vswhere 全成功 / 报错退出 / 垃圾+非零 / 路径不存在)全部 rc=0、0 条
  `::error::`。
- `check_publish_slots.py --self-test` 8 条 + 警告断言全绿;`--dist` 六场景复测(x64-only 绿 /
  多一份 arm64 绿+warning / 缺 CLI rc=1 / 空 dist rc=1 / `--slots` 漂移 rc=1 / `--dist` 非目录
  rc=1),并且缺产物时输出顺序是「表格 → warning → error → 汇总」,stderr 为空。
- 7 个 workflow `yaml.safe_load` OK;全部 `run:` 块 `bash -n` OK;全仓 400 行扫描 0 个超限
  (release-build.yml 316、check_publish_slots.py 345)。
- `check_release_semantics.py`(+`--self-mutation`)、`release_marker.py --self-test`、
  `make_manifest.py --self-test`、`cargo metadata --no-deps` 全绿。

边界不变:arm64 产物仍**从未被构建过、也从未在真机跑过**;`PUBLISH_SLOTS` 仍是 `"windows-x64"`,
发布语义与 x64 静态 CRT 门禁未动。下一次 tag 的 arm64 仍可能红,红点大概率换到 C 依赖编译
(aws-lc-sys 的 CMake + aarch64 cl.exe)或链接器(LNK1181)——**不是「arm64 修好了」**。
2026-11-08 08:45:02 +08:00
AskaEth e858664fca ci(release): M164 arm64 判定权交回编译器 + 发布名单守卫(不翻牌)
根因(拿到的真实证据,不是猜的):v0.2.2(run 42322062879 / job 128248713926)与 v0.2.3
(run 42429017002 / job 128573547829)两份 check-run 注解逐字一致 —— 失败步骤是第 6 步
`Windows ARM64 工具链体检`,第 7 步 `cargo build` 状态 skipped。也就是说 v0.2.1~v0.2.3 五轮
全红都红在一个**编译前的启发式探针**上,真正的编译器/链接器一次都没说话。该探针的结论还与它
自己打印的快照矛盾:`SDK Lib 版本→um 架构: 10.0.22621.0→um:[arm,arm64,x64,x86,]` 明明在,却
同时报 `kernel32=缺失`(同一个 `find -path` 模式在本地合成目录树上命中)。runner 实况是
Windows 11 Arm64 + Visual Studio 2026 镜像,`VC.Tools.ARM64` 与 `Windows11SDK.22621/26100`
都装好了,M142 的 `setup.exe modify` 反倒返回非 0。

改:
- release-build.yml:探针降级为「只取证」——vswhere 组件查询 + 各目录 `.lib` 计数 +
  arm64 `link.exe` 候选,一条 `::error::` 都不写、永不 exit 1;不再自动补装 VS。红绿只由
  cargo/链接/静态 CRT 导入表门禁决定。cargo build 步给 arm64 显式备齐工具链:
  `rustup target add aarch64-pc-windows-msvc`、`vcvarsall.bat arm64` 取 `LIB`/`INCLUDE`、
  vswhere 定位 arm64 `link.exe` 后导出 `CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER`
  (顺序不能反:只给链接器不给 LIB ⇒ LNK1181 kernel32.lib)。
- 新增 packaging/check_publish_slots.py(344 行,零依赖):名单 ⇄ 产物**两个方向**。
  声明了缺 GUI/CLI ⇒ `::error::` + exit 1;有产物没声明 ⇒ `::warning::` 明说未发布(M142 之后
  这个方向是静默的,裁剪步只 echo 一行 drop:)。另有源码级断言(名单每项必须存在且 mode=ci、
  运行期名单必须等于文件里那行)与 8 条 `--self-test` 注入回归。
- release-publish.yml:裁剪前插一步跑那个守卫,原来内联的齐全性循环搬进脚本(一套判据一处实现);
  重算 SHA256SUMS 的步骤只负责重算。
- release-guard.yml:PR 阶段跑守卫自测 + 源码级断言。
- 文档同步:release.yml / release-publish.yml / release-build.yml 头注释、packaging/README §12、
  §1 矩阵、§3 构建与发布、§9 支持矩阵、§7.5 arm64 取证、release-notes.md、README/README.zh-CN
  的产物矩阵与说明。口径统一为「arm64 构建取证中、未发布」,不写成已支持。

没做的(按 tower 裁决「方案乙」):`PUBLISH_SLOTS` 本轮**不加回** arm64。arm64 从未产出过 PE、
本机也证不了,写进名单就等于拿 x64 的发布去赌它(M142 移出名单正是为避免这个事故)。下一 tag 的
CI 真出包后翻一行即可,守卫与 SHA256SUMS 自动跟着扩。

验证:workflow YAML 全部可被 PyYAML 解析;所有 `run:` 块过 `bash -n`;探针步在本机(无 VS/SDK)
实跑 exit 0 并打出实况注解;守卫 `--self-test` 8 条全绿、正反两个方向都在合成 dist 上实测过;
check_release_semantics.py(含 --self-mutation)/ release_marker / make_manifest 自检全绿;
cargo metadata 正常。**arm64 产物从未被构建过、也从未在真机跑过**——本机 `rustc --target
aarch64-pc-windows-msvc` 直接撞上没有 MSVC 链接器(git-bash 的 GNU `link` 被当成 link.exe),
`cargo check --workspace --target aarch64-pc-windows-msvc` 也死在 aws-lc-sys 的 cc-rs 阶段
(GNU host 上没有该 target 的 C 编译器)。真实结论只能等下一 tag 的 CI。
2026-11-08 08:33:44 +08:00
AskaEth 1d2e84ed61 fix(m163): push tag 默认建稳定版(发布标记语义 + 0.2.3 bump)
发布标记:`release.yml` 的 dispatch `prerelease` 默认值 true → false;`release-publish.yml`
的 create 路径不再塞 `--prerelease`(空串 = push tag ⇒ 稳定版),edit 路径行为不变(空串不碰
标记)。三种触发各创建什么改由 `packaging/release_marker.py` 一张表定义,流水线
`--emit-bash` 出来两个数组直接用,自己不再写 case——v0.2.2 至今 `releases/latest` 404 的根因
就是「push 默认 prerelease」这层叠上「dispatch 默认 true」。

守门:新增 `packaging/check_release_semantics.py`(CI 在 `.github/workflows/release-guard.yml`,
guards.yml 已 398 行不能再挂 job):①标记表逐条对(push tag 的 create 不含 --prerelease);
②把 `--emit-bash` 真灌进 bash 读回两个数组;③publish 的可执行内容里没有 --prerelease 字面量、
必须调用 emit-bash、两个数组各喂 create/edit;④release.yml 的 dispatch 默认值 == false(读
YAML 结构)。`--self-mutation` 注入三项真实回归(默认值改回 true / 塞回字面量 / 表里 push
改成预发布),断言每项都红、基线绿。

版本:0.2.2 → 0.2.3(workspace `[workspace.package]` + 五处 path 依赖 pin + Cargo.lock)。

文档:README 中英两版版本行改 0.2.3,并把本阶段能力补进现有章节(Modrinth 匿名在线内容源、
自更新与稳定通道、整合包导入逐文件决策、实例级启动参数),删掉「没有自动更新通道」「导入只有
两份全局策略」两条过期说法;`release-notes.md` 写 0.2.3 的真实更新摘要(保留 {{VERSION}} /
{{SUMS}});`packaging/README.md` 与 `docs/build-and-ci.md` 里「push 默认仍 prerelease」的旧口径
全部改掉。
2026-11-07 18:56:01 +08:00
AskaEth 3b3430a93e docs(termux): 对外口径升级——CPU 软渲染 + 自编 Mesa GPU 两条路径各真机验证一次(用户拍板 A,四处同步) 2026-11-07 09:33:24 +08:00
AskaEth 394faad611 docs(termux): MC 1.20.4 真机跑通结论与剩余项
- README:MC 标 ✅(glibc JRE + arm64 natives + softpipe);后续收敛为
  zink/DRI3、剪贴板/IME、narrator/音频
- 构建文档 §4.2 补真机启动命令、窗口尺寸与日志位置、非致命项
2026-11-07 09:33:23 +08:00
AskaEth 7eed6c6261 feat(termux): MC 启动接线——Linux arm64 natives + softpipe 子进程环境
- Library::native_classifier:Linux/aarch64 上把 LWJGL3 的 natives-linux
  改写成 natives-linux-arm64(Mojang 清单只有 x86_64)
- install::libraries:arm64 分类器在清单里没有下载信息时,按 Maven 相对路径
  兜底到阿里云镜像(实测 Mojang libraries 404、Aliyun 200);
  单测覆盖 arm64 兜底与 x86_64 不变
- launch::defaults:Termux glibc 构建启动 MC 时默认带
  MESA_LOADER_DRIVER_OVERRIDE=swrast / LIBGL_ALWAYS_SOFTWARE=1(用户可覆盖)
- 文档:JRE patchelf 一次性步骤与接线现状;README 同步
- 验证:patched Temurin java 直接执行;LWJGL arm64+GLFW 探针 gl-ok
2026-11-07 09:33:22 +08:00
AskaEth 02ed09ae49 docs(termux): 补 MC 运行时结论(glibc JRE + LWJGL arm64 + softpipe GL)
- §4.2:Temurin 21 JRE 在 glibc 下跑通;AWT/X11 正常;
  LWJGL 3.3.3 arm64 + GLFW + GLX/softpipe 端到端探针通过(GL 3.3)
- zink 仍卡 DRI3;softpipe 可用(软件 GL)
- README 同步:MC 标 🟡(栈已验证,启动器接线待做),后续改为接线 + DRI3
2026-11-07 09:33:22 +08:00
AskaEth 173a18b842 feat(termux): glibc 构建自动启用自编 Mesa 26 turnip ICD(GPU 打通)
- app/boot/termux_gpu:glibc 构建 + Termux 且随附 ICD 存在时,在探测前设
  VK_ICD_FILENAMES/VK_DRIVER_FILES;用户显式设置不覆盖
- 文档:Mesa 26.2.6 + KGSL 的完整构建/安装/验证步骤(含 shebang、
  --wrap-mode=nofallback、C++/链接参数三处坑)
- 实测:wgpu 选中 Adreno 840(turnip Mesa 26.2.6),截图首帧 61 ms
- README.termux:GPU 标 ✅,MC 标 🟡(glibc JRE+AWT 已验证,GLX 待 GL 构建)
2026-11-07 09:33:21 +08:00
AskaEth bbb316ca08 docs(termux): GPU 结论落地——glibc 侧被驱动版本挡住
实测结论:
- glibc Vulkan loader + 自写 ICD 能加载
- Termux glibc Mesa 24.2.6 不认识 Adreno 840(chip 0x44050A31 unsupported)
- Arch vulkan-freedreno 26.2.6 能加载但 Linux/DRM 构建找不到设备(缺 KGSL,
  /dev/dri 对 untrusted_app 被 SELinux 拒绝)
- GPU 变体会打印具名错误并自动降级 tiny-skia

README.termux 同步:GPU 标 ⛔、MC 走 glibc JVM 的结论与后续路线
2026-11-07 09:33:21 +08:00
AskaEth c434dbe335 feat(termux): 横屏自适应窗口、安装后清理 apt 工作区、补分支 README
- app/boot/termux_window:Termux 上默认窗口按 X 屏幕等比自适应
  (横屏铺满、竖屏不溢出,最大 2×;日志 source=termux-screen),
  ULL_WINDOW_SIZE 覆盖与 1180×760 验收基准不变;5 条单测
- termux::install 成功后清理 $PREFIX/tmp/ull-java-termux(一次安装约 100 MB)
- 构建文档补 clippy/rustfmt 组件、cargo-clippy 目标被带成 android 的坑、
  门禁命令、横屏行为与 openjdk-17 实测
- 新增 README.termux.md(分支定位/已验证矩阵/快速开始/边界/路线图),
  两份主 README 的 Termux 条目链过去
2026-11-07 09:33:21 +08:00
AskaEth 48d1ee6616 feat(java): Termux 上自动安装系统 openjdk(内置镜像 + 隔离 apt 状态)
- 新增 game::java::termux:内置 SAU/TUNA/USTC/官方四个 termux-main 镜像,
  按顺序 apt-get update+install;lists/cache 隔离在 $PREFIX/tmp/ull-java-termux,
  不改用户 apt 配置;装完从 $PREFIX/lib/jvm 定位,幂等(已装则不走网络)
- JdkVendor 增加 termux(17/21/25),--vendor termux 只走系统包;
  Termux 上缺省安装先试系统包,失败再走归档链并带出每一跳原因
- standard_roots 已能扫 $PREFIX/lib/jvm(上一条提交),装完即被 java list 发现
- CLI/presenter 的 vendor 展示与帮助文案同步;设置白名单与契约测试同步
- 文档补 Termux 上的 Java 行为
2026-11-07 09:33:19 +08:00
AskaEth 1e0fc44fed docs(packaging): 记录 Termux glibc 构建与 termux-x11 运行配方
新增 packaging/docs/termux-glibc-build.md:glibc 工具链(Rust gnu 组件 +
patchelf + clang/lld + Debian libgcc crt)、构建环境(LD_PRELOAD/CA/USTC 镜像)、
termux-x11 运行与截图取证、GPU 现状与已知边界。
支持矩阵四处同步:Termux 行从「GUI 未验证」改为「源码自建、CPU 渲染单机验证过」,
仍标注实验级、不在发布矩阵内。
2026-11-07 09:33:19 +08:00
AskaEth 06fe12d811 chore(release): M154 版本升到 0.2.2(第一个稳定通道版本,D51)
- 根 Cargo.toml `[workspace.package] version` 0.2.1 → 0.2.2;`crates/{agent,app-iced,cli,palette-extract,
  presenter}` 里 9 处 intra-workspace path 依赖的 version 约束一并对齐(M137 既定做法)。`crates/app` 是
  workspace `exclude` 的 Slint 退役树,manifest 不参与解析,原样保留。
- Cargo.lock 由 cargo 再生成:只有 8 个 `unlucky-*` 成员条目从 0.2.1 变 0.2.2,第三方依赖(含版本号恰好
  也是 0.2.1 的 hexf-parse / windows-link / windows-threading / xkeysym / yazi)一字未动。这一步不是可选:
  CI 与发布流水线全走 `--locked`(ci.yml:236,274 / release-build.yml:135,142 / make_artifacts.py:50),
  lock 不跟就是「tag 一打就红」。
- packaging 文档里的**示例 tag** 跟到 v0.2.2:docs/artifacts-and-version.md 两处(命名规则示例 + 「tag 说
  X、产物里写 Y」的事故示例)、docs/build-and-ci.md 三处(`--version v…`、两次 `gh workflow run --ref v…`)、
  docs/profiles-and-measurements.md 的「同一槽位现在叫 `unlucky-launcher_0.2.2_windows_x64.exe`」、
  make_manifest.py 四处(Usage 示例 / `normalize_version` docstring / `--version` 的 argparse help)。
- **历史注记一律不动**:docs/runtime-deps.md 的「v0.2.1 首次真跑即红在工具链体检」(M140 事故记录)、
  `.github/workflows/release.yml:71` 同一事故的另一处记录、`crates/core` 更新模块测试里的样本版本串
  (`checker_cases.rs` / `version_cases.rs`,是解析器测试夹具,不是发布事实)、`microsoft.rs:224` 的
  `21.0.13.0.2.1` 负例。
- README.md / README.zh-CN.md、packaging/release-notes.md 核实后**无需改动**:三份文件里没有任何写死的
  版本号——下载表是 `<version>` / `<版本>` 占位符(M153 刚统一的口径,含非发布档位标「暂未发布」的行,
  原样保留未回退),release-notes 正文是 `{{VERSION}}` / `{{SUMS}}` 模板占位符。

验证:`cargo check -p unlucky-cli --all-targets --locked` 过(编译日志里 unlucky-core/agent/cli 均报 v0.2.2)、
`cargo test -p unlucky-cli --lib --locked` 42 passed 0 failed、`cargo metadata --locked --offline` 过(全
workspace manifest + lock 一致)、`python3 packaging/make_manifest.py --self-test` ok、
`ull --version` 实跑输出 `ull 0.2.2(unlucky-cli)`。
2026-11-07 08:13:29 +08:00
AskaEth e3ee20bdcd feat(packaging): M151 发布流水线生成更新清单 manifest.json(D50)
清单挂 GitHub Release 资产,固定文件名 manifest.json(D50)。字段形状的权威是 M146 的
crates/core/src/update/manifest.rs(ReleaseManifest,deny_unknown_fields + 逐字段校验),
生成器只负责把那套形状填对,不另立 schema。

同源,不双写:packaging/make_manifest.py 的每个 sha256 直接读 Release 自己的
SHA256SUMS.txt,size 取自同一批文件的 stat();产物名里的版本段必须等于 --version,
tag 与 Cargo.toml 漂了就没有产物可对,当场失败。CI 在「裁剪发布白名单后重算
SHA256SUMS」之后调它,所以清单描述的是真正要发的资产,不是构建出来的全部。
make_artifacts.py 只加 --manifest 开关,用子进程调同一份脚本(本地跑也产出
manifest.json),本文件继续是产物命名的唯一真相源。

只放启动器产物:DTO 的 Artifact 没有角色字段,Release::artifact_for() 取第一个
target 命中的产物,把 ull 并列写进去会让「覆盖哪个文件」取决于数组顺序;ull 照旧进
SHA256SUMS.txt 与 Release。

url 两种形态:默认裸文件名(DirSource 口径,本地/内网用);CI 传 --asset-base-url
拼绝对地址并钉 tag(不是 latest),清单里每条都指向与它自报版本相符的字节。

自验:--self-test 用 packaging/tests/manifest.golden.json 逐字节对拍(覆盖裸 exe 与
tar.gz 两种形态,并钉住「ull 不进清单」),再跑 14 条负例证明严格解析不是摆设;
发布步会先跑它再产文件。真 DTO 对拍已在本机用 M146 的 ReleaseManifest 实测:
golden 解析通过(2 个产物、min_supported、force 均正确落位),14 条篡改全部被拒。

未决(报告带利弊,未自行改发布默认值):releases/latest/download/* 只指向非 prerelease
的最新 Release,而发布默认 prerelease=true。

Refs: D50, M146
2026-11-06 22:15:58 +08:00
AskaEth 5597a4cc18 feat(release,docs): M149 发布构建烘入微软登录 id(仓库 Secret),文档改为「官方构建开箱即用」
构建侧:release-build.yml 声明 on.workflow_call.secrets.ULL_MSA_CLIENT_ID(required: false),
cargo build 步以 env 注入它,option_env! 把它烘进二进制;release.yml 的 build job 按名显式透传
(与 PUBLISH_TOKEN 同一模式,不用 inherit)。真值放仓库 Secret,不进任何 git 文件;Secret 未录入
或 Actions 之外求值为空串时,client_id.rs 的 blank_env_value_counts_as_missing 保证「空白=没配」,
不会拿空 id 去联网。

文档:README 两版 / help.rs 的 accounts topic / packaging/release-notes.md /
packaging/docs/build-and-ci.md 统一成「官方构建已内置登录所需配置、开箱即用,仍可用环境变量
ULL_MSA_CLIENT_ID 覆盖;自建想自带就构建时注入」的功能性口径 —— 不出现 id 本身,也不出现归属描述。
client_id.rs 的模块注释与测试注释同步(原文写「id 不进仓库(用户定案)」,已随 Secret 方案改写),
并记下「别把 ULL_MSA_CLIENT_ID 导出到跑 cargo test 的 shell」——presenter 的 M132 入口锚会在
已烘焙 id 的构建里故意红。

实测:ULL_MSA_CLIENT_ID=<id> cargo build --bin ull 后,清空 env 跑 ull accounts add msa 打出
用户码与链接(无「未配置」提示);同二进制设一个假 env id 则 AADSTS700016 点名该假 id(env 覆盖生效)。
单测:core auth 28、unlucky-cli 全套、i18n 30、presenter 222 全绿;M132 锚在烘焙构建里按设计红、
在不带 id 的构建里绿。cargo fmt --all --check 通过。
2026-11-06 21:08:53 +08:00
AskaEth 374e168334 docs(packaging): M145 按主题拆分 README(索引 + docs/,节号不变)
packaging/README.md 也是 400 行整,且安装包与自动更新马上要动发布线。按主题分成
「索引 + 5 份承载文件」,每份 ≤124 行:

- README.md 只留真相源声明、§1–§12 的索引表、§11、§12
- docs/artifacts-and-version.md:§1 产物矩阵 + §2 版本来源
- docs/build-and-ci.md:§3 怎么构建
- docs/runtime-deps.md:§4 运行时依赖 + §5 软渲染兜底
- docs/profiles-and-measurements.md:§6 构建档位 + §7 实测
- docs/compliance-support-boundaries.md:§8 合规 + §9 支持矩阵 + §10 已知边界

节号沿用原编号,所以全仓「packaging/README.md §N」式引用(根 README / README.zh-CN /
make_artifacts.py / release-notes.md / ci-runtime-smoke.md / release*.yml 注释)都不用改。
搬家由脚本按原文件切片生成,逐行可查:旧 README 400 行全部出现在新位置,唯一改写的
旧行是 §12 清单里那两行(文件清单必须反映拆分后实况)。新增行 15 条:索引段、§3 的
M145 说明、§12 的 5 份新文件行。
2026-11-06 19:29:47 +08:00