Merge branch 'feat/m147-cifix-repair-ubuntu-red-checks-and'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
This commit is contained in:
commit
1c53c29f76
9 files changed
+203
-10
No files matched your search
@@ -132,7 +132,55 @@ jobs:
|
||||
# 同 clippy:`--workspace` 不含 `unlucky-app`(M118),Slint 退役树不再被 `--all-targets` 拉起。
|
||||
# 也因此**不要**再写 `--exclude unlucky-app`:它已不是成员,那面旗子只会打一条
|
||||
# `excluded package(s) … not found in workspace` 的警告然后空转(实测 exit 0,不报错)。
|
||||
- run: cargo test --workspace --all-targets
|
||||
#
|
||||
# ---- 取证(M147):下面是同一件事的两个平台版本,不是两条不同的测试 ----
|
||||
# M144 的三条红里两条已在 docker 里定位到行,第三条 `test (windows)` 在 run 里只留下
|
||||
# `Process completed with exit code 1`——而 cargo 自己的失败码是 **101**,所以那个 1 的
|
||||
# 来源不明(包装层洗码 / 进程被杀,两说都未排除)。job log 要登录才能读(匿名 403),
|
||||
# 但**注解匿名可读**(`GET /check-runs/<id>/annotations` → 200,M144 实证)。所以这三件
|
||||
# 事各自对准一种「看不见」:
|
||||
# 1) `--no-fail-fast`:cargo 默认在第一个失败的 target 就停(M144 实测 Linux 只跑了
|
||||
# 7 个 test binary 就退出),后面还有没有第二条失败完全看不到;
|
||||
# 2) 把 `error` / `failures:` / panicked 行升成 `::error::` 注解:拿不到 job log 时,
|
||||
# 注解是匿名侧唯一能取到的失败正文;
|
||||
# 3) 显式打印 cargo 的退出码:把「包装层把码洗成 1」「cargo 真失败(101)」「进程被杀
|
||||
# 只给码、零诊断」三种情形分开——这正是 M144 没定的那一半。
|
||||
- name: cargo test --workspace --all-targets(ubuntu,失败升注解)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
log="$RUNNER_TEMP/cargo-test.log"
|
||||
# 默认 shell 带 `-e` + `pipefail`:管道里 cargo 一失败就会在 tee 之后立刻退出,
|
||||
# 下面三行永远跑不到。所以这段先关 `-e`,自己取 `PIPESTATUS[0]`(cargo 的码,
|
||||
# 不是 tee 的码)。
|
||||
set +e
|
||||
cargo test --workspace --all-targets --no-fail-fast 2>&1 | tee "$log"
|
||||
code=${PIPESTATUS[0]}
|
||||
set -e
|
||||
echo "cargo exit=$code"
|
||||
if [ "$code" -ne 0 ]; then
|
||||
# `%` 是 workflow command 的保留字符,要转成 `%25`,否则注解正文会被截断。
|
||||
grep -E '^(error(\[E[0-9]+\])?:|failures:|test result: FAILED|thread .* panicked at| *--> )' "$log" \
|
||||
| head -40 | sed 's/%/%25/g; s/^/::error::/' || true
|
||||
fi
|
||||
exit "$code"
|
||||
- name: cargo test --workspace --all-targets(windows,失败升注解)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
# pwsh 没有 PIPESTATUS(那是 bash 语法):拿 `$LASTEXITCODE` —— Tee-Object 是 cmdlet,
|
||||
# 不会把它改掉,所以它是 cargo 自己的码。
|
||||
$log = Join-Path $env:RUNNER_TEMP 'cargo-test.log'
|
||||
cargo test --workspace --all-targets --no-fail-fast 2>&1 | Tee-Object -FilePath $log
|
||||
$code = $LASTEXITCODE
|
||||
Write-Host "cargo exit=$code"
|
||||
if ($code -ne 0) {
|
||||
# 正则不锚 `error:` 那一支:pwsh 把原生 stderr 折成 ErrorRecord 后行首可能多一截前缀,
|
||||
# 锚死会漏行;`%` 是 workflow command 的保留字符,转成 `%25`。
|
||||
Select-String -Path $log -Pattern '(error(\[E\d+\])?:|^failures:|test result: FAILED|thread .* panicked at|^\s+--> )' |
|
||||
Select-Object -First 40 |
|
||||
ForEach-Object { Write-Host "::error::$($_.Line.Replace('%', '%25'))" }
|
||||
}
|
||||
exit $code
|
||||
|
||||
build:
|
||||
name: build release (${{ matrix.os }})
|
||||
|
||||
@@ -52,6 +52,9 @@ impl Dispatch {
|
||||
|
||||
/// 把 iced 的窗口句柄翻成 Win32 的 `HWND`(`isize`)。非 Windows 平台返回 `None`。
|
||||
pub(super) fn probe_hwnd(handle: &dyn window::Window) -> Option<isize> {
|
||||
// 只有 `RawWindowHandle::Win32` 那条臂用到它,其余平台只剩 `_ => None`
|
||||
// ⇒ 无条件导入在非 Windows 上是 `unused_imports`(M147)。
|
||||
#[cfg(target_os = "windows")]
|
||||
use window::raw_window_handle::RawWindowHandle;
|
||||
|
||||
let handle = handle.window_handle().ok()?;
|
||||
@@ -99,11 +102,21 @@ mod tests {
|
||||
// 无效句柄 + 合法 α:样式补不上(layered=false),FFI 也不会成功——两层都如实报告。
|
||||
let invalid = dispatch(1, Some(0.5));
|
||||
assert!(!invalid.layered, "无效句柄补不上 WS_EX_LAYERED");
|
||||
// 「做不到」的语义随平台不同(M147 实证:这条断言原先按 Windows 写死,Ubuntu 上
|
||||
// 622 passed / 1 failed):Windows 上 FFI 真被调用并失败 ⇒ `Failed`;非 Windows 走
|
||||
// `transparency.rs` 的空实现 ⇒ `Unsupported`。两边都不许表现成 `Applied` / `Opaque`。
|
||||
#[cfg(target_os = "windows")]
|
||||
assert!(
|
||||
matches!(invalid.alpha, AlphaOutcome::Failed { .. }),
|
||||
"{:?}",
|
||||
invalid.alpha
|
||||
);
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
assert_eq!(
|
||||
invalid.alpha,
|
||||
AlphaOutcome::Unsupported,
|
||||
"非 Windows 的空实现必须如实说「这个平台不支持」,不许冒充成功"
|
||||
);
|
||||
}
|
||||
|
||||
/// 本机真窗口的取证由 `App` 启动日志给(见 `app::appearance::log_appearance`),
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
//! 回读拿到的是 **DWM 侧的当前值**——它能抓住「设了 A 属性、读回还是旧值」这类事
|
||||
//! (比如把 `DWMWA_WINDOW_CORNER_PREFERENCE` 写成别的号,回读会给出 `None` 或旧值)。
|
||||
|
||||
// M147:本模块七项(三个属性号 + 四个读函数)只在 Windows 上有使用者——唯一调用方 `probe::run_one`
|
||||
// 的 Windows 版被门着,Linux + `-D warnings` 下就是 dead_code 错。用模块级 `cfg_attr` 而不是
|
||||
// 逐项 `cfg`:`cfg` 会把这些项连同本文件那几条与平台无关的单测一起门掉(逐项 `cfg_attr` 多 7 行,顶破红线)。
|
||||
#![cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
/// `DWMWA_WINDOW_CORNER_PREFERENCE`:窗口圆角偏好的属性号(Win11 / Server 2025,build ≥ 22000)。
|
||||
pub const DWMWA_WINDOW_CORNER_PREFERENCE: u32 = 34;
|
||||
/// `DWMWCP_DONOTROUND`:显式直角。
|
||||
@@ -120,7 +124,7 @@ pub fn set_preference(hwnd: isize, preference: u32) -> CornerOutcome {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = (hwnd, preference);
|
||||
return CornerOutcome::UnsupportedPlatform;
|
||||
CornerOutcome::UnsupportedPlatform
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
//! 「取证窗口真的把命中测试关掉了」的 OS 侧证据——比「出图 hash 不变」有判别力得多
|
||||
//! (实测:同 argv 开/关穿透,hash 完全相同)。
|
||||
|
||||
// 唯一使用者是下面的 `click_through`(它自己按平台门禁),非 Windows 上这个导入没人用
|
||||
// ⇒ `unused_imports` 在 `-D warnings` 下升成错(M147)。
|
||||
#[cfg(target_os = "windows")]
|
||||
use super::raw_ex_style;
|
||||
|
||||
/// `WS_EX_TRANSPARENT`:**点击穿透**位(`0x0000_0020`)。
|
||||
|
||||
@@ -39,9 +39,14 @@
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use super::alpha::{apply_window_alpha, ensure_layered};
|
||||
// `CornerOutcome` 与平台无关(`ProbeRow` 的字段类型、非 Windows 的 `empty_row` 都用它),
|
||||
// 其余九个符号只服务于 Windows 的 `run_one` —— 分开写,否则非 Windows 上整条导入无人使用
|
||||
// (`unused_imports`,M147)。
|
||||
use super::dwm::CornerOutcome;
|
||||
#[cfg(target_os = "windows")]
|
||||
use super::dwm::{
|
||||
corner_preference_hr, extended_frame_bounds, set_preference, visible_border_thickness,
|
||||
window_rect, window_style, CornerOutcome, DWMWCP_DONOTROUND, DWMWCP_ROUND,
|
||||
window_rect, window_style, DWMWCP_DONOTROUND, DWMWCP_ROUND,
|
||||
};
|
||||
|
||||
/// `WS_POPUP`(`0x8000_0000`):**带 owner 的弹出窗**才用它;产品主窗口没有 owner,不用。
|
||||
|
||||
@@ -203,7 +203,11 @@ mod tests {
|
||||
use crate::platform::paths::{MapEnv, Platform};
|
||||
|
||||
const APP_DATA: &str = "C:/Users/u/AppData/Roaming";
|
||||
const LOCAL_APP_DATA: &str = "C:/Users/u/AppData/Local";
|
||||
// 反斜杠原样:本常量经 `%LOCALAPPDATA%` 展开后要与 `CUSTOM_THEME`(反斜杠原文)拼出**同一个
|
||||
// `PathBuf` 键**。写成 `C:/…` 会得到 `C:/Users/u/AppData/Local\Microsoft\…` 这种混合串——
|
||||
// Windows 的 `Path` 把 `/` 与 `\` 都当分隔符,Linux 只认 `/`,于是同一份夹具在 Linux 上永远
|
||||
// 查不到那个文件(M147 实测:CI 的 test(ubuntu) 就红在这条上)。
|
||||
const LOCAL_APP_DATA: &str = r"C:\Users\u\AppData\Local";
|
||||
const DESKTOP_QUERY: &str = "reg.exe query HKCU\\Control Panel\\Desktop /v WallPaper";
|
||||
const THEMES_QUERY: &str =
|
||||
"reg.exe query HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes /v CurrentTheme";
|
||||
|
||||
@@ -374,13 +374,12 @@ mod tests {
|
||||
#[test]
|
||||
fn accent_on_fixed_neutral_only_moves_the_neutral_hue() {
|
||||
let official = Palettes::official_baseline();
|
||||
// 中性色相 = 基线色相时,与官方的 from_seed 完全等价(不放宽算法)。
|
||||
let same = Palettes::accent_on_fixed_neutral(DEFAULT_SEED, NEUTRAL_HUE_DEG, 12.0);
|
||||
// 中性色相取**种子色相的实测值**而不是 `NEUTRAL_HUE_DEG` 字面量:两者只差 `atan2()` 的末位
|
||||
// (见下一条测试),逐位比会在 glibc 与 UCRT 之间翻脸;取实测值两侧同源,断言仍是逐位相等。
|
||||
let per_seed = Palettes::from_seed_with_neutral_chroma(DEFAULT_SEED, 12.0);
|
||||
assert_eq!(
|
||||
same, per_seed,
|
||||
"中性色相取种子色相时必须与 TonalSpot 逐项相同"
|
||||
);
|
||||
let seed_hue = per_seed.neutral.hue_deg();
|
||||
let same = Palettes::accent_on_fixed_neutral(DEFAULT_SEED, seed_hue, 12.0);
|
||||
assert_eq!(same, per_seed, "中性色相取种子色相时须与 TonalSpot 相同");
|
||||
|
||||
// 换一个中性色相:只有中性两族的色相动,彩度与强调族一律不动。
|
||||
let moved = Palettes::accent_on_fixed_neutral(DEFAULT_SEED, 120.0, 12.0);
|
||||
|
||||
@@ -66,6 +66,7 @@ Git Bash 下会静默落进 Docker VM(脚本内部用 `cygpath -m` 归一)
|
||||
| `packaging/check_screenshot.py` | 截图非空白判定(CI 门禁 + 本地手动跑,零第三方依赖) | ≤400 |
|
||||
| `packaging/ci-runtime-smoke.md` | §11 的细节:断言口径/阈值依据/失效模式/复跑 | ≤400 |
|
||||
| `packaging/linux-smoke-docker.sh` | 本地复跑 `runtime-smoke`(Docker + xvfb) | ≤400 |
|
||||
| `packaging/linux-check-docker.sh` | 本地复跑 `ci.yml` 的两条 ubuntu 门禁(clippy / test,Docker) | ≤400 |
|
||||
| `.github/workflows/release.yml` | tag 触发 → 选槽位(`plan`)→ 调下面两个可复用工作流 | ≤400 |
|
||||
| `.github/workflows/release-build.yml` | `workflow_call`:单槽位构建(体检 / cargo / 打包 / 上传 artifact) | ≤400 |
|
||||
| `.github/workflows/release-publish.yml` | `workflow_call`:发布(`PUBLISH_SLOTS` 裁剪 / SHA256SUMS / Release) | ≤400 |
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
# 本地复跑 `.github/workflows/ci.yml` 的两条 **ubuntu 门禁**:`clippy --workspace --all-targets
|
||||
# -- -D warnings` 与 `test --workspace --all-targets`。
|
||||
#
|
||||
# 为什么要有这条脚本(与 `linux-smoke-docker.sh` 同一动机,但受检对象不同):
|
||||
# - smoke 脚本复刻的是 `runtime-smoke` job(能不能真跑起来);本脚本复刻的是**编译期门禁**
|
||||
# (能不能编过 / 测过)。M144 的三条红里有两条(clippy ubuntu、test ubuntu)属于后者,
|
||||
# 而它们只在「Linux + `--all-targets` + `-D warnings`」三件事凑齐时才炸——
|
||||
# 在 Windows 本机永远复现不了(Windows 上那些 `#[cfg(target_os = "windows")]` 分支都有使用者)。
|
||||
# 所以这条脚本是「本机提前挡住那两条红」的唯一通道。
|
||||
#
|
||||
# 用法:
|
||||
# bash packaging/linux-check-docker.sh [仓库根目录]
|
||||
# ULL_CHECK_STAGES=clippy bash packaging/linux-check-docker.sh # 只跑 clippy
|
||||
# ULL_CHECK_TEST_ARGS='-p unlucky-app-iced --lib' \
|
||||
# ULL_CHECK_STAGES=test bash packaging/linux-check-docker.sh # 只跑一个 crate 的单测
|
||||
#
|
||||
# 前置:基础镜像 `ull-linux-smoke:bookworm`(由 `packaging/linux-smoke-docker.sh` 建)。
|
||||
# 本脚本在它之上只加一层 `cmake` + `ninja-build`:`aws-lc-sys`(rustls 的汇编后端)
|
||||
# 缺 cmake 编不过,M144 实测过。
|
||||
#
|
||||
# 诚实边界(别把它当 runner 的等价物):
|
||||
# - 基础镜像是 **Debian 12(glibc 2.36)**,官方 runner 是 **Ubuntu 24.04(glibc 2.39)**;
|
||||
# 两者 rustc 走同一份 `rust-toolchain.toml`(stable),所以**编译期 lint 与单测判定**可对齐,
|
||||
# 但发行版差异面(系统库版本、字体)不受本脚本保护。
|
||||
# - 本脚本只跑 ci.yml 里那两条命令,不跑 fmt / build / runtime-smoke。
|
||||
# - 冷编很贵(全 workspace + `--all-targets`,M144 实测 15–25 分钟)。缓存卷 `ull-linux-check-target`
|
||||
# 第二次起是热的;**别**把宿主机的 target 目录挂进来(路径与指纹都对不上,等于骗自己)。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(cd "${1:-.}" && pwd)
|
||||
base_image=${ULL_SMOKE_IMAGE:-ull-linux-smoke:bookworm}
|
||||
image=${ULL_CHECK_IMAGE:-ull-linux-check:bookworm}
|
||||
cargo_volume=${ULL_CARGO_VOLUME:-ull-cargo-reg}
|
||||
target_volume=${ULL_CHECK_TARGET_VOLUME:-ull-linux-check-target}
|
||||
apt_mirror=${ULL_APT_MIRROR:-https://mirrors.tuna.tsinghua.edu.cn}
|
||||
stages=${ULL_CHECK_STAGES:-clippy test}
|
||||
test_args=${ULL_CHECK_TEST_ARGS:---workspace --all-targets --no-fail-fast}
|
||||
fail_fast=${ULL_CHECK_FAIL_FAST:-0}
|
||||
|
||||
# Git Bash 里路径是 MSYS 虚拟路径:直接喂给 `docker -v` 会被当成容器内路径(M62 踩过)。
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
repo_root=$(cygpath -m "$repo_root")
|
||||
fi
|
||||
echo "== 仓库: $repo_root"
|
||||
echo "== 阶段: $stages(test 参数: $test_args)"
|
||||
|
||||
if ! docker image inspect "$base_image" >/dev/null 2>&1; then
|
||||
echo "!! 缺基础镜像 $base_image —— 先跑一次:" >&2
|
||||
echo " bash packaging/linux-smoke-docker.sh . D:/tmp/ull-smoke" >&2
|
||||
echo " (它建的镜像里已经带了 ci.yml 那批图形栈编译依赖;本脚本只在其上加 cmake/ninja)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# 一层派生镜像:cmake + ninja-build。只在缺镜像或显式 ULL_CHECK_REBUILD_IMAGE=1 时重建。
|
||||
if ! docker image inspect "$image" >/dev/null 2>&1 || [ "${ULL_CHECK_REBUILD_IMAGE:-0}" = "1" ]; then
|
||||
context=$(mktemp -d)
|
||||
trap 'rm -rf "$context"' EXIT
|
||||
cat >"$context/Dockerfile" <<EOF
|
||||
FROM $base_image
|
||||
# 基础镜像已把 apt 源改到 $apt_mirror(见 linux-smoke-docker.sh)。
|
||||
# aws-lc-sys(rustls 的汇编后端)构建时要 cmake + ninja,缺了就是在 C 依赖里报错退出,
|
||||
# 与 Rust 侧代码无关——别被那种红骗去改源码(M144 实测)。
|
||||
RUN apt-get update \\
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\
|
||||
cmake ninja-build \\
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /src
|
||||
EOF
|
||||
docker build -t "$image" "$context"
|
||||
fi
|
||||
|
||||
# 缓存卷挂到 `registry` 子目录:直接挂 /usr/local/cargo 会盖掉镜像里的 cargo 本体(M62 踩过)。
|
||||
MSYS_NO_PATHCONV=1 docker run --rm \
|
||||
-v "$repo_root:/src" \
|
||||
-v "$cargo_volume:/usr/local/cargo/registry" \
|
||||
-v "$target_volume:/target" \
|
||||
-w /src \
|
||||
-e CARGO_TARGET_DIR=/target \
|
||||
-e CARGO_TERM_COLOR=never \
|
||||
-e ULL_CHECK_STAGES="$stages" \
|
||||
-e ULL_CHECK_TEST_ARGS="$test_args" \
|
||||
-e ULL_CHECK_FAIL_FAST="$fail_fast" \
|
||||
"$image" bash -euo pipefail -c '
|
||||
rustc --version && cargo --version && cat /etc/debian_version
|
||||
cmake --version | head -1 && ninja --version
|
||||
|
||||
run_clippy() { cargo clippy --workspace --all-targets -- -D warnings; }
|
||||
# 故意不加引号:ULL_CHECK_TEST_ARGS 是一串参数(默认照抄 ci.yml 的那条命令)。
|
||||
run_test() { cargo test $ULL_CHECK_TEST_ARGS; }
|
||||
|
||||
overall=0
|
||||
for stage in $ULL_CHECK_STAGES; do
|
||||
case "$stage" in
|
||||
clippy) label="cargo clippy --workspace --all-targets -- -D warnings"; run=run_clippy ;;
|
||||
test) label="cargo test $ULL_CHECK_TEST_ARGS"; run=run_test ;;
|
||||
*) echo "!! 未知阶段: $stage(可选 clippy / test)" >&2; exit 2 ;;
|
||||
esac
|
||||
echo "== 开始: $label"
|
||||
set +e
|
||||
"$run"
|
||||
code=$?
|
||||
set -e
|
||||
# 退出码单独成行:一眼能看出是「编译期 lint」还是「单测」,也能与 CI 注解里的码对照。
|
||||
echo "== 结束: $label → exit=$code"
|
||||
if [ "$code" -ne 0 ]; then
|
||||
overall=1
|
||||
if [ "$ULL_CHECK_FAIL_FAST" = "1" ]; then exit "$code"; fi
|
||||
fi
|
||||
done
|
||||
echo "== 汇总: $ULL_CHECK_STAGES → exit=$overall"
|
||||
exit "$overall"
|
||||
'
|
||||
|
||||
echo "== 两条 ubuntu 门禁已跑完(上表 exit=0 才算绿)"
|
||||
Reference in new issue
Block a user