Files
Unlucky-Launcher/packaging/linux-smoke-docker.sh
T
AskaEth ea5f66cf92 feat(ci): M62 —— Linux runtime-smoke(xvfb 真渲染一帧)+ 像素非空白门禁
「编得过」不等于「跑得起来」:CI 里 Linux/macOS 此前只 build + test,从未创建过真实图形窗口。
本 commit 补第一份 Linux 真实图形环境运行证据,并把它做成不可伪造的门禁。

- ci.yml 新增 runtime-smoke(ubuntu-latest):装 xvfb + 软渲染/字体依赖,用 xvfb-run 起 GUI 进程,
  走既有 `--software-renderer --screenshot` 通道渲出一帧 PNG,再断言 ①退出码 0 ②PNG 宽高 > 0
  ③非空白(唯一色数 / 亮度σ / 强边缘占比三项过阈值),并把截图作为 artifact `runtime-smoke-screenshot` 上传。
  既有 fmt/clippy/test/build 四个 job 与触发器语义零改动(纯追加)。
- ci.yml 新增 runtime-smoke-macos 探针:只在 workflow_dispatch 触发且 continue-on-error。官方 macOS
  runner 处于 LaunchDaemon 上下文、无 WindowServer 会话,大概率建不了窗口;本机无 macOS 且 worker 禁
  push,这条路径尚无实测证据,故不拿它卡 PR 门禁(首跑结论决定升常驻或按 D26 归入 iced 重写)。
- packaging/check_screenshot.py:零第三方依赖的 PNG 解码(zlib + 反过滤 0~4)与三项指标判定,
  `--selftest` 打印阈值依据(人为纯色帧 1/0.0/0.0 必红,真实帧 342/19.45/0.00761 全过线)。
- packaging/linux-smoke-docker.sh:在 Docker 里复刻同一 job(TUNA apt/rustup 源),便于无 Linux 桌面的本地复跑。
- packaging/README.md §11:覆盖平台、阈值依据数值、本地复跑、已知跳过项、迁 iced 后需重新验证。

本机实测(Debian 12 容器 + xvfb + 软渲染):退出码 0,PNG 1180x760,唯一色 342 / σ 19.45 / 强边缘 0.00761。
CI(Ubuntu 24.04 runner)首跑仍未验证——本 job 证明「能出图」,不证明视觉正确。
2026-11-01 16:15:47 +08:00

91 lines
4.2 KiB
Bash
Raw 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:-/tmp/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)
echo "== 仓库: $repo_root"
echo "== 输出: $out_dir"
# 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 再编
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"