diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index a1b9566..d081786 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -150,11 +150,29 @@ jobs: rustup target list --installed vswhere="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" if [ -x "$vswhere" ]; then - vcvars=$("$vswhere" -latest -products '*' -find 'VC/Auxiliary/Build/vcvarsall.bat' 2>/dev/null | head -n1 || true) + # ② MSVC 的 LIB/INCLUDE。**别把 vcvarsall 的路径直接塞给 `cmd //c "..."`**:MSYS 会把参数里的 + # `"` 改写成 `\"` 再交给 cmd.exe,而 cmd 的 /c 引号剥离接不住这一层,只会报 + # `'\"C:\...\vcvarsall.bat\"' 不是内部或外部命令` —— 结果 LIB/INCLUDE 全空、只剩 `set` 的空输出 + # (M164 复审实测)。故本步改为往 `$RUNNER_TEMP` 写一个 **CRLF** 的 wrapper .bat,引号留在 + # .bat 自己的 `call "..."` 里(由 cmd 解析,路径带空格也没事),`cmd //c` 只收**不含引号**的 + # 相对文件名(先 cd 进去),彻底绕开 MSYS 的引号改写。 + tmp=${RUNNER_TEMP:-${TEMP:-/tmp}} + mkdir -p "$tmp" + vcvars="" + # `-find` 是**单值**参数且官方文档给的是反斜杠形态:两种形态各试一次(与下面 dumpbin 步同规矩), + # 只赌一种就等于再写一个「静默退化成 warning」。 + for pat in 'VC\Auxiliary\Build\vcvarsall.bat' 'VC/Auxiliary/Build/vcvarsall.bat'; do + vcvars=$("$vswhere" -latest -products '*' -find "$pat" 2>/dev/null | head -n1 || true) + [ -n "$vcvars" ] && break + done dump="" if [ -n "$vcvars" ]; then - # `cmd //c` 是 git-bash 调 cmd 的安全写法(`//c` 不会被当成路径);`set` 打出全量环境。 - dump=$(cmd //c "\"${vcvars//\//\\}\" arm64 >nul 2>&1 && set" 2>/dev/null | tr -d '\r') || dump="" + # CRLF 是必须的:cmd 解析只有 LF 的 .bat 会漏行/报错。 + { printf '@echo off\r\n' + printf 'call "%s" arm64 >nul 2>&1\r\n' "${vcvars//\//\\}" + printf 'set\r\n'; } > "$tmp/ull-msvc-arm64.bat" + dump=$(cd "$tmp" && cmd //c ull-msvc-arm64.bat 2>/dev/null | tr -d '\r') || dump="" + echo "wrapper: $tmp/ull-msvc-arm64.bat ← $vcvars(set 输出 ${#dump} 字符)" fi for key in LIB INCLUDE; do value=$(printf '%s\n' "$dump" | sed -n "s/^${key}=//p" | head -n1) @@ -167,9 +185,11 @@ jobs: fi done found="" - for p in 'VC/Tools/MSVC/*/bin/Hostarm64/arm64/link.exe' 'VC/Tools/MSVC/*/bin/Hostx64/arm64/link.exe'; do + for p in 'VC\Tools\MSVC\*\bin\Hostarm64\arm64\link.exe' 'VC/Tools/MSVC/*/bin/Hostarm64/arm64/link.exe' \ + 'VC\Tools\MSVC\*\bin\Hostx64\arm64\link.exe' 'VC/Tools/MSVC/*/bin/Hostx64/arm64/link.exe'; do + [ -n "$found" ] && break hit=$("$vswhere" -latest -products '*' -find "$p" 2>/dev/null | head -n1 || true) - if [ -n "$hit" ]; then found=${hit//\\//}; break; fi + [ -n "$hit" ] && found=${hit//\\//} done if [ -n "$found" ]; then export CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER="$found" @@ -183,7 +203,10 @@ jobs: fi fi cargo build --release --locked --bin unlucky-launcher-iced --bin ull - ls -l "$BUILD_DIR"/unlucky-launcher-iced* "$BUILD_DIR"/ull* + # 产物清单只给 arm64 打:x64(当前唯一在发的槽位)这一步零变化,不额外给它加失败面。 + if [ "$SLOT" = "windows-arm64" ]; then + ls -l "$BUILD_DIR"/unlucky-launcher-iced* "$BUILD_DIR"/ull* || true + fi - name: Windows 单文件自检(导入表不得出现 VC++ 可再发行 DLL) if: runner.os == 'Windows' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cfb1a29..d84f5a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ name: Release # cargo build / 静态 CRT 导入表门禁 / 打包 / 归档自检 / 上传 artifact / arm64 收尾注解),矩阵键换成同名 `inputs.*`。 # - `.github/workflows/release-publish.yml` = 原 `release` job 全流程(`PUBLISH_SLOTS` 白名单 / 裁剪 / 重算 # SHA256SUMS / 渲染说明 / `gh release` 幂等),`secrets.GITHUB_TOKEN` 改为显式透传的 secret。 -# (M164 在裁剪前插了一步「发布名单守卫」,故它是 7 步而不是搬家时的 6 步。) +# (M164 在裁剪前插了一步「发布名单守卫」:主线 7 步,本分支 8 步。) # `on` / `env` / `permissions` / `concurrency` 与 `plan` 的槽位表**一字未动**。 on: diff --git a/packaging/check_publish_slots.py b/packaging/check_publish_slots.py index 18c3ac9..827b4e3 100644 --- a/packaging/check_publish_slots.py +++ b/packaging/check_publish_slots.py @@ -188,15 +188,16 @@ def run_checks(root: Path, dist: Path | None = None, expected: list[str] | None def report(root: Path, dist: Path | None, expected: list[str] | None = None) -> int: errors, warnings, rows = run_checks(root, dist, expected) + # 全部走 **stdout**:Actions 在 stdout 上同样识别 `::error::` / `::warning::`,而分两个流写会让 + # 「表格 → 注解」的顺序在 CI 日志里不可控(stderr 往往整段先刷出来,注解就跑到它解释的那张表前面)。 for row in rows: print(row) for item in warnings: print(f"::warning::{item}") - # 表格之后再看结论:注解必须排在它解释的那张表后面,人读日志的顺序才对得上。 for item in errors: - print(f"::error::{item}", file=sys.stderr) + print(f"::error::{item}") if errors: - print(f"发布名单守卫:{len(errors)} 条不通过", file=sys.stderr) + print(f"发布名单守卫:{len(errors)} 条不通过") return 1 print(f"OK: 发布名单守卫全绿({len(warnings)} 条「未发布」提示)") return 0 diff --git a/packaging/docs/build-and-ci.md b/packaging/docs/build-and-ci.md index bb32a55..dee484b 100644 --- a/packaging/docs/build-and-ci.md +++ b/packaging/docs/build-and-ci.md @@ -53,8 +53,12 @@ python packaging/make_artifacts.py --slot windows-arm64 \ - 每个槽位 `cargo build --release --locked`(**不传 `--target`**:每槽原生编译,交叉槽位没了)。arm64 槽位在编译前 **把工具链显式备齐**(M164):`rustup target add aarch64-pc-windows-msvc`、用微软自己的 `vcvarsall.bat arm64` 取 `LIB`/`INCLUDE`(写进本步环境与 `$GITHUB_ENV`)、用 vswhere 定位 arm64 `link.exe` 后显式导出 - `CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER`(Hostarm64 原生优先)。顺序不能反:只覆盖链接器而不给 `LIB`, - 撞上的就是 `LNK1181: cannot open input file 'kernel32.lib'`。 + `CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER`(Hostarm64 原生优先、Hostx64 次之;`-find` 的斜杠/反斜杠两种形态都试)。 + 顺序不能反:只覆盖链接器而不给 `LIB`,撞上的就是 `LNK1181: cannot open input file 'kernel32.lib'`。 + **`cmd //c` 不能直接收「带引号的 vcvarsall 路径」**:MSYS 会把 `"` 改写成 `\"`,cmd 的 `/c` 剥离规则接不住, + 结果 `LIB`/`INCLUDE` 全空(M164 复审实测;`cmd //c` 只调**不含引号**的路径是好的)。故本步往 `$RUNNER_TEMP` + 写一个 CRLF 的 wrapper `.bat`(`call "" arm64` + `set`),`cmd //c` 只收相对文件名——引号留在 + `.bat` 内部由 cmd 自己解析,路径带空格也不受影响。 - **编译前体检已降级为「只取证」**(M164):M141/M142 的版本用 `find` 猜 `um/arm64/kernel32.lib` 在不在、猜缺就 `exit 1` —— v0.2.1~v0.2.3 五轮全红在**那一步**,`cargo build` 一次都没跑过(永远 skipped),而它跟自己打的快照 自相矛盾(`um/arm64` 明明在)。现在那一步只打印 vswhere 组件查询、各目录 `.lib` **计数**与 `link.exe` 候选, @@ -101,7 +105,8 @@ python packaging/make_artifacts.py --slot windows-arm64 \ 的 secret)。触发(`on`)、`env`、顶层 `permissions`、`concurrency` 与 `plan` 的槽位表在 M145 搬家时**一字未动**; 后来只有 M163 动过 `on` 里 `prerelease` 的默认值(`true` → `false`,见上面那张语义表),M164 改过 `release-build.yml` 的 arm64 体检步与 `cargo build` 步、并在 `release-publish.yml` 裁剪前插了「发布名单守卫」 - (于是它现在是 7 步)。改槽位仍只改 `release.yml` 那一处(`PUBLISH_SLOTS` 白名单在 `release-publish.yml`, + (于是本分支 8 步:checkout / download / **守卫** / 裁剪 / 重算 SHA256SUMS / manifest / 说明 / `gh release`; + 搬家时 6 步、主线 7 步)。改槽位仍只改 `release.yml` 那一处(`PUBLISH_SLOTS` 白名单在 `release-publish.yml`, 翻牌时要改的仍是那两处)。 - **M149 微软登录 id 的烘焙**:`release-build.yml` 的 `cargo build` 步带 `env.ULL_MSA_CLIENT_ID`(值 `${{ secrets.ULL_MSA_CLIENT_ID }}`),`option_env!` 把它烘进二进制,于是发布产物装完即可登录。id **不进