Files
AskaEth 7e08e0ccc9 fix(docs): M62 r1 —— 修正「缺字体→空图+exit 0」的错误论据,拆出运行时冒烟细节文档
review r1(p2-1items/fix-then-merge)只要求改文档与注释,门禁逻辑一行未动。

- P2-1(唯一阻塞项):原稿称「字体库缺失 → 合法 PNG + 退出码 0」,这是错的。review 实测两条失效都是
  **硬失败**:完全没有字体 → fontique panic(退出码 101,不产生 PNG);非软渲染(默认 femtovg)→
  `take_snapshot is not supported by this FemtoVG backend`(退出码 1)。改正口径:`--screenshot` 的 PNG
  是进程自己编码写出的,只要走到 `take_snapshot` 就是「合法 PNG + 退出码 0」,像素断言是**纵深防御**,
  守的是「进程活着、PNG 合法、但场景几乎没画」这一档;其余情形本机暂无样本,属防御性设计、不宣称已复现。
  落点:packaging/ci-runtime-smoke.md §1 与 packaging/check_screenshot.py 模块 docstring。
- P3-1:ci.yml 的 runtime-smoke job 头补一行 —— 本 job 绑死 Slint `Window::take_snapshot`,迁 iced 后必须
  提供等价离屏截图通道,否则静默退化成「能起进程」。
- P3-2:阈值余量写明量级(342/64=5.3×、19.45/3.0=6.5×、0.00761/0.001=7.6×,改「5~8 倍」);并写明
  闸门下限 ≈「至少画出一条标题栏」(review 实测切片 174/35.10/0.02623 与右下 400×300 切片仍 pass),
  即只证明「场景被真实渲染过」,不证明元素齐全、更不证明视觉正确。
- P3-3:README 398/400 已贴红线 —— 借 review 给的逃生口**提前拆**:细节搬进 packaging/ci-runtime-smoke.md
  (78 行),README §11 收缩为 12 行索引(374 行),并写明「再增长继续往细节文档写,两文件都 ≤400」。
- P3-4:本地复跑示例改 Windows 安全路径 `D:/tmp/ull-smoke`;脚本对 repo_root/out_dir 统一 `cygpath -m`
  归一并回显真实路径(Git Bash 下 `/tmp` 会静默落进 Docker VM,宿主什么都收不到 —— review 实测踩到)。
- P3-5:两个 `run:` 里的 `set -o pipefail` 保留但加注释说明「当前无管道,为将来加管道先有失败传播」;
  `cargo clean -p unlucky-app || true` 加内联注释(N7 兜底,CI 里没有对应行,别误抄进 ci.yml)。

验证:`--selftest` 三组仍全部 fail(判红能力未被改坏);真实截图仍 pass(1180x760 / 342 / 19.45 / 0.00761);
0 字节与非 PNG 路径 exit 1;`yaml.safe_load` 通过(6 job,Linux 槽位无 continue-on-error,artifact 步
`if: always()`);`cargo fmt --all --check` 退出 0;cygpath 修复后重跑 Docker 冒烟,宿主机 D:/tmp/ull-m62-rev2
拿到 PNG,md5 `156c382a…` 与前两次 run 及 review 的 artifact 完全一致。
2026-11-01 16:15:47 +08:00

98 lines
4.7 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 本地复跑 `.github/workflows/ci.yml` 的 `runtime-smoke` job:Linux + Xvfb + 软渲染截图。
#
# 为什么要有这条脚本:CI 的 runtime-smoke 在 GitHub 的 ubuntu-latest 上跑,
# 但「改动这个 job 的人」需要在没有 Linux 桌面的开发机上先自证一遍——否则改的就是没人验过的 YAML。
# 本脚本在 Docker 里复刻 job 的步骤(同样的 apt 依赖、同样的构建、同样的 xvfb-run 命令),
# 并把截图与判定结果留在宿主目录,供人眼与 `check_screenshot.py` 复核。
#
# 用法:
# bash packaging/linux-smoke-docker.sh [仓库根目录] [输出目录]
# ULL_SMOKE_SKIP_BUILD=1 bash packaging/linux-smoke-docker.sh # 复用缓存的二进制
#
# 说明(诚实边界):
# - 基础镜像 `rust:1-bookworm` 是 **Debian 12(glibc 2.36)**,官方 runner 是 **Ubuntu 24.04(glibc 2.39)**。
# 两者图形栈(X11/xvfb/Mesa 软渲染)来源不同,因此本脚本证明的是「Linux 上这条通道能真跑出图」,
# 不能代替官方 runner 的首跑验证。
# - 国内网络慢时可换 TUNA 镜像:脚本默认就用 TUNA 的 Debian 源(`ULL_APT_MIRROR` 可覆盖)。
set -euo pipefail
repo_root=$(cd "${1:-.}" && pwd)
out_dir=${2:-ull-smoke}
image=${ULL_SMOKE_IMAGE:-ull-linux-smoke:bookworm}
cargo_volume=${ULL_CARGO_VOLUME:-ull-cargo-reg}
target_volume=${ULL_CARGO_TARGET_VOLUME:-ull-linux-target}
apt_mirror=${ULL_APT_MIRROR:-https://mirrors.tuna.tsinghua.edu.cn}
rustup_mirror=${ULL_RUSTUP_MIRROR:-https://mirrors.tuna.tsinghua.edu.cn/rustup}
mkdir -p "$out_dir"
out_dir=$(cd "$out_dir" && pwd)
# Git Bash 里 /tmp、/d/x 都是 MSYS 虚拟路径:直接喂给 `docker -v` 会被当成 Linux VM 内的路径,
# 结果「脚本说成功、宿主机什么都没有」。有 cygpath 就统一转成 Windows 混合路径(D:/x)并回显真实位置。
if command -v cygpath >/dev/null 2>&1; then
repo_root=$(cygpath -m "$repo_root")
out_dir=$(cygpath -m "$out_dir")
fi
echo "== 仓库: $repo_root"
echo "== 输出: $out_dir(截图 $out_dir/runtime-smoke.png)"
# 1) 构建镜像:rust 工具链 + xvfb + 图形栈编译依赖 + 字体(软渲染器要从系统字体库取字形)
if ! docker image inspect "$image" >/dev/null 2>&1 || [ "${ULL_SMOKE_REBUILD_IMAGE:-0}" = "1" ]; then
context=$(mktemp -d)
trap 'rm -rf "$context"' EXIT
cat >"$context/Dockerfile" <<EOF
FROM rust:1-bookworm
# 工具链组件(rustfmt/clippy)在 rust-toolchain.toml 里是必备项,rustup 会去补齐;
# 默认 dist 服务器在国内会挂成「rustc 0.6% CPU 睡 12 分钟」的假死(M62 实测),所以走 TUNA。
ENV RUSTUP_DIST_SERVER=$rustup_mirror \\
RUSTUP_UPDATE_ROOT=$rustup_mirror/rustup
RUN sed -i 's|http://deb.debian.org|$apt_mirror|g' /etc/apt/sources.list.d/debian.sources \\
&& apt-get update \\
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\
xvfb xauth python3 \\
libxkbcommon-dev libxkbcommon-x11-0 libfontconfig1-dev \\
libgl1-mesa-dev libegl1-mesa-dev libwayland-dev libxcb1-dev \\
libx11-6 libxcursor1 libxrandr2 libxi6 \\
fonts-dejavu-core fonts-liberation2 \\
&& rm -rf /var/lib/apt/lists/*
RUN rustup component add rustfmt clippy && rustup toolchain list
WORKDIR /src
EOF
docker build -t "$image" "$context"
fi
# 2) 跑容器:构建 → xvfb 截图 → 像素判定
# 注意缓存卷要挂到 `registry` 子目录:直接挂 /usr/local/cargo 会把镜像里的 `bin/`(cargo 本体)盖掉,
# 变成 `cargo: command not found`(M62 实测踩到)。
MSYS_NO_PATHCONV=1 docker run --rm \
-v "$repo_root:/src" \
-v "$cargo_volume:/usr/local/cargo/registry" \
-v "$target_volume:/target" \
-v "$out_dir:/out" \
-w /src \
-e CARGO_TARGET_DIR=/target \
-e ULL_SMOKE_SKIP_BUILD="${ULL_SMOKE_SKIP_BUILD:-0}" \
-e CARGO_TERM_COLOR=never \
"$image" bash -euo pipefail -c '
rustc --version && cargo --version && cat /etc/debian_version
if [ "$ULL_SMOKE_SKIP_BUILD" != "1" ]; then
# 共享热缓存卷可能喂陈旧产物(踩坑记录 N7):先清掉受检 crate 再编
# 清不掉就算了(N7 兜底用);CI 里**没有**对应两行,别误抄进 ci.yml —— 那边每次都是干净 runner
cargo clean -p unlucky-app || true
cargo build --bin unlucky-launcher --locked -j 6
fi
mkdir -p /out/data
set +e
xvfb-run -a --server-args="-screen 0 1280x1024x24" \
/target/debug/unlucky-launcher --software-renderer \
--screenshot /out/runtime-smoke.png --data-dir /out/data
code=$?
set -e
echo "exit=$code"
python3 packaging/check_screenshot.py /out/runtime-smoke.png
exit $code
'
echo "== 截图与数据目录已留在 $out_dir"