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 完全一致。
98 lines
4.7 KiB
Bash
98 lines
4.7 KiB
Bash
#!/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"
|