diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8cc14db..0943ade 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,3 +56,42 @@ jobs: - run: uv sync - name: Python tests (Windows) run: uv run pytest tests/ -v + # v0.2.30 教训(Build Release 31661378619):Test 全绿 ≠ .iss 能编译。 + # make-installer.sh 的 emrg.iss 只在 tag 触发 Build Release 时经 iscc 编译, + # #727 误用 LoadStringFromFile 单参数形式(Inno 真实签名是 2 参数 out-param) + # → iscc "Invalid number of parameters" 发版才暴露。此处用 runner 预装的 + # iscc 编译渲染出的 emrg.iss(stub payload),PR CI 即拦截 .iss 语法/签名错误。 + - name: Inno Setup script compile smoke test + shell: bash + run: | + set -euo pipefail + STAGE="$(mktemp -d)" + mkdir -p "$STAGE/payload/bin" "$STAGE/dist/artifacts" + touch "$STAGE/payload/bin/stop-emrg.cmd" + # icon.ico 是 gen-assets 产物(未入库)——生成最小合法 .ico 供 iscc 编译期 + # SetupIconFile 检查;{app}(={%USERPROFILE}\.emrg\install)引用的文件 + # (UninstallDisplayIcon/[Icons]/[UninstallRun])也需存在。 + uv run python - <<'PY' + import struct + def write_ico(path): + hdr = struct.pack(' .*emrg\.iss.*< "$STAGE/gen.sh" + bash "$STAGE/gen.sh" + command -v iscc >/dev/null 2>&1 || { echo "::error::iscc not on PATH (runner image regression)"; exit 1; } + iscc "$STAGE/emrg.iss" diff --git a/packaging/make-installer.sh b/packaging/make-installer.sh index f2e7328..b73d94a 100755 --- a/packaging/make-installer.sh +++ b/packaging/make-installer.sh @@ -385,7 +385,7 @@ var ResultCode: Integer; StopScript: string; LogFile: string; - LogText: string; + LogText: AnsiString; begin Result := ''; ExtractTemporaryFile('stop-emrg.cmd'); @@ -400,8 +400,16 @@ begin if ResultCode <> 0 then begin LogText := ''; + // LoadStringFromFile 的 Inno Pascal Script 签名是 + // function LoadStringFromFile(const FileName: String; var S: AnsiString): Boolean; + // (6.7.1 → 7.x 全版本一致,见 issrc Shared.ScriptFunc.pas)——不存在单参数 + // 字符串返回形式!v0.2.30 Build Release 31661378619 因此编译失败 + // (iscc "Invalid number of parameters",Test CI 不编译 .iss 未拦住)。 + // 正确用法:out-param 写入 LogText,返回 Boolean 表示成功。 + // 注意:本注释位于未加引号 heredoc 内,禁用反引号与命令替换语法 + // (iscc compile gate 实证捕获),以免破坏 .iss 渲染。 if FileExists(LogFile) then - LogText := LoadStringFromFile(LogFile); + LoadStringFromFile(LogFile, LogText); if Length(LogText) > 2000 then LogText := Copy(LogText, 1, 2000); if LogText <> '' then diff --git a/tests/test_installer_stop.py b/tests/test_installer_stop.py index cc431ed..fcde1de 100644 --- a/tests/test_installer_stop.py +++ b/tests/test_installer_stop.py @@ -146,7 +146,17 @@ def test_make_installer_iss_has_prepare_to_install(): # R125: rant 2026-08-13T09:24:37 — 输出重定向到 {tmp}\stop-emrg.log(2>&1), # 失败时 LoadStringFromFile 读日志展示杀不掉的进程,不再让宿主手动跑诊断 assert '/c ""\' + StopScript + \'" > "\' + LogFile + \'" 2>&1"' in content - assert "LoadStringFromFile(LogFile)" in content + # ⚡ LoadStringFromFile 的 Inno Pascal Script 签名是 2 参数 out-param 形式 + # `(const FileName: String; var S: AnsiString): Boolean`(6.7.1 → 7.x 一致, + # issrc Shared.ScriptFunc.pas)——单参数字符串返回形式不存在,iscc 编译报 + # "Invalid number of parameters"(v0.2.30 Build Release 31661378619 实际失败, + # Test CI 不编译 .iss 未拦住)。正反两态钉死正确调用形态。 + assert "LoadStringFromFile(LogFile, LogText)" in content # 正:out-param 形式 + assert ":= LoadStringFromFile(LogFile)" not in content # 反:1 参数形式不存在 + # ⚡ 2 参形式第 2 参是 var S: AnsiString——LogText 必须声明 AnsiString(Inno 6 + # 的 string=UnicodeString,传 string 变量 → iscc "Type mismatch",门禁实测拦截)。 + assert "LogText: AnsiString;" in content # 正:AnsiString 变量 + assert "LogText: string;" not in content # 反:UnicodeString 不匹配 var AnsiString assert "Length(LogText) > 2000" in content assert "Details from stop-emrg.cmd:" in content assert "SW_HIDE" in content # 批处理执行不弹控制台窗口(#592 纪律)