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 完全一致。
173 lines
7.3 KiB
YAML
173 lines
7.3 KiB
YAML
name: CI
|
||
|
||
# 主干门禁:fmt / clippy / test / build 双平台矩阵。
|
||
# 架构守门(core 零 UI、文件 ≤400 行、依赖许可)见 guards.yml。
|
||
# 平台基线 Win10+(标准工具链产物),平台矩阵见 AGENTS.md §7。
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
pull_request:
|
||
workflow_dispatch:
|
||
|
||
env:
|
||
CARGO_TERM_COLOR: always
|
||
RUST_BACKTRACE: 1
|
||
|
||
concurrency:
|
||
group: ci-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
fmt:
|
||
name: fmt (rustfmt --check)
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
components: rustfmt
|
||
- run: cargo fmt --all --check
|
||
|
||
clippy:
|
||
name: clippy (${{ matrix.os }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
os: [windows-latest, ubuntu-latest]
|
||
runs-on: ${{ matrix.os }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Linux 图形栈构建依赖(winit / femtovg 编译期需要)
|
||
if: runner.os == 'Linux'
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends \
|
||
libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev \
|
||
libwayland-dev libxcb1-dev
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
components: clippy
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo clippy --workspace --all-targets -- -D warnings
|
||
|
||
test:
|
||
name: test (${{ matrix.os }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
os: [windows-latest, ubuntu-latest]
|
||
runs-on: ${{ matrix.os }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Linux 图形栈构建依赖
|
||
if: runner.os == 'Linux'
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends \
|
||
libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev \
|
||
libwayland-dev libxcb1-dev
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo test --workspace --all-targets
|
||
|
||
build:
|
||
name: build release (${{ matrix.os }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
os: [windows-latest, ubuntu-latest]
|
||
runs-on: ${{ matrix.os }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Linux 图形栈构建依赖
|
||
if: runner.os == 'Linux'
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends \
|
||
libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev \
|
||
libwayland-dev libxcb1-dev
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo build --workspace --release
|
||
|
||
# 运行时冒烟(M62):Linux 上**真渲染一帧**,补上「能编译 ≠ 能跑」的空白。
|
||
# 上面四个 job 都只证明代码编得过、测得过,从不创建真实图形窗口;本 job 在 Xvfb 虚拟显示里
|
||
# 启动 GUI 进程,让 Slint 软件渲染器把当前场景渲进内存像素缓冲并编码成 PNG,再对**像素本身**
|
||
# 断言(不是只看退出码:窗口起来了但一帧空白同样是「退出码 0」)。
|
||
# 诚实边界:证明「Linux 上真能跑出非空白的一帧」,**不**证明视觉正确——视觉靠人眼看 artifact。
|
||
# 本 job 绑死 Slint Window::take_snapshot;迁 iced 后必须提供等价的离屏截图通道,否则本 job 静默退化成「能起进程」。
|
||
runtime-smoke:
|
||
name: runtime smoke (ubuntu x64, xvfb)
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 30
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: 无头 X 与图形栈依赖(xvfb + 软渲染 + 字体)
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends \
|
||
xvfb xauth \
|
||
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
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
# 用 debug 而不是 release:本 job 验的是「运行时渲染通路能通」,不是优化后二进制的行为;
|
||
# release 的 lto="fat" + codegen-units=1 会把这一步拖到十几分钟却不带来额外证据。
|
||
- run: cargo build --bin unlucky-launcher --locked
|
||
- name: 在 Xvfb 里跑一帧并截图(退出码非 0 即失败)
|
||
run: |
|
||
set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播
|
||
mkdir -p "$RUNNER_TEMP/smoke/data"
|
||
xvfb-run -a --server-args="-screen 0 1280x1024x24" \
|
||
target/debug/unlucky-launcher --software-renderer \
|
||
--screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \
|
||
--data-dir "$RUNNER_TEMP/smoke/data"
|
||
- name: 断言 PNG 存在且非空白(尺寸 / 唯一色数 / 亮度σ / 强边缘占比)
|
||
run: python3 packaging/check_screenshot.py "$RUNNER_TEMP/smoke/runtime-smoke.png" --json
|
||
- name: 上传截图与数据目录(失败也传,便于复盘)
|
||
if: always()
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: runtime-smoke-screenshot
|
||
path: ${{ runner.temp }}/smoke/
|
||
if-no-files-found: warn
|
||
|
||
# macOS 槽位探针(M62):**默认不在 PR 上跑**,只有手动 dispatch 才触发。
|
||
# 之所以是探针而不是常驻槽位:官方 macOS runner 处于 LaunchDaemon 上下文,没有登录的
|
||
# WindowServer 会话,Slint 的 winit backend 创建窗口大概率直接失败;而 worker 禁 push,
|
||
# 本机也没有 macOS,这条路径**尚无任何实测证据**——不拿未验证的 job 卡住主干门禁。
|
||
# 首跑结论决定去留:绿 → 去掉 if/continue-on-error 升为常驻槽位;红 → 按 D26 归入 iced
|
||
# 重写后再规划,并把错误原文贴进 progress 文档(不静默删 job、不吞失败原因)。
|
||
runtime-smoke-macos:
|
||
name: runtime smoke (macos arm64, probe)
|
||
if: github.event_name == 'workflow_dispatch'
|
||
runs-on: macos-latest
|
||
timeout-minutes: 30
|
||
# 探针阶段保留证据但不算流水线结论:log/artifact 里失败原因照常可见,
|
||
# 解除条件 = 该槽位在真实 runner 上首跑通过并把本行与上面那条 if 一起删掉。
|
||
continue-on-error: true
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo build --bin unlucky-launcher --locked
|
||
- name: 尝试截图(macOS 没有 X 服务器,直接跑;失败即为结论)
|
||
run: |
|
||
set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播
|
||
mkdir -p "$RUNNER_TEMP/smoke/data"
|
||
target/debug/unlucky-launcher --software-renderer \
|
||
--screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \
|
||
--data-dir "$RUNNER_TEMP/smoke/data"
|
||
- name: 断言 PNG 存在且非空白
|
||
run: python3 packaging/check_screenshot.py "$RUNNER_TEMP/smoke/runtime-smoke.png" --json
|
||
- name: 上传截图与数据目录(失败也传,便于复盘)
|
||
if: always()
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: runtime-smoke-screenshot-macos
|
||
path: ${{ runner.temp }}/smoke/
|
||
if-no-files-found: warn
|