# -*- coding: utf-8 -*- r'''1.7.1 安装包端到端验收。 用户要求:"所有东西做完记得先测试能不能用" —— 所以不能只测源码,要**真的装一遍**。 流程: 1. 静默安装(带配对码) 2. 确认文件落在 Program Files 3. 确认数据目录权限对(普通用户能写) 4. 确认 --status 能读到配对码 5. 确认进程起来了、端口在听 6. 卸载(静默,避开确认弹窗) ⚠ 踩过的坑: * 不带 /SUPPRESSMSGBOXES 卸载会弹确认框,脚本永远卡住 * /DIR= 的值里有空格,必须整体加引号,否则 Start-Process 会拆开 * Inno 的 UsePreviousAppDir 会记住上次目录,必须显式给 /DIR= ''' from __future__ import annotations import subprocess import sys import time from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") SETUP = ROOT / "dist" / "ykxmz-setup-1.7.1.exe" INSTALL_DIR = r"C:\Program Files\ykxmz" DATA_DIR = r"C:\ProgramData\ykxmz\data" CODE = "739251" PORT = 8877 passed = failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + " " + str(detail)) def run_ps(command: str, timeout: int = 300) -> tuple: result = subprocess.run( ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], capture_output=True, text=True, timeout=timeout, encoding="utf-8", errors="replace") return result.returncode, (result.stdout or "").strip() def cleanup() -> None: """把上次可能留下的装机和注册表清掉,保证是全新安装。""" print(" 零、清理上次残留") run_ps( "Get-Process ykxmz -ErrorAction SilentlyContinue | " "Stop-Process -Force -ErrorAction SilentlyContinue; " "Start-Sleep -Seconds 2; " "Remove-Item 'C:\\Program Files\\ykxmz' -Recurse -Force " "-ErrorAction SilentlyContinue; " "Remove-Item 'C:\\ProgramData\\ykxmz' -Recurse -Force " "-ErrorAction SilentlyContinue; " "Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" "\\{8F3C1D42-7A5E-4B91-9C6D-2E8F4A1B7C30}_is1' -Recurse -Force " "-ErrorAction SilentlyContinue; " "Write-Output cleaned") print(" 已清理") def main() -> int: check("安装包存在", SETUP.is_file(), str(SETUP)) if not SETUP.is_file(): return 1 print(" 安装包 %.2f MB" % (SETUP.stat().st_size / 1024 / 1024)) cleanup() print() print(" 一、静默安装(配对码 %s)" % CODE) # /DIR= 的值有空格,整体加引号 —— 否则 Start-Process 会拆开 arg_list = ( '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART ' '/DIR="C:\\Program Files\\ykxmz" ' '/CODE=%s /TASKS=autostart' % CODE ) start = time.perf_counter() proc = subprocess.run( ["powershell", "-NoProfile", "-Command", "Start-Process -FilePath '%s' -ArgumentList '%s' -Wait -PassThru " "| Select-Object -ExpandProperty ExitCode" % (SETUP, arg_list)], capture_output=True, text=True, timeout=600, encoding="utf-8", errors="replace") elapsed = time.perf_counter() - start exit_code = (proc.stdout or "").strip() check("安装退出码 0", exit_code == "0", "exit=%r" % exit_code) print(" 耗时 %.1f 秒" % elapsed) check("安装没卡死(<120 秒)", elapsed < 120, "%.1f 秒" % elapsed) time.sleep(6) print() print(" 二、文件落地") exe = Path(INSTALL_DIR) / "ykxmz.exe" check("ykxmz.exe 在", exe.is_file(), str(exe)) if exe.is_file(): print(" %.1f MB" % (exe.stat().st_size / 1024 / 1024)) check("_internal 目录在", (Path(INSTALL_DIR) / "_internal").is_dir()) print() print(" 三、数据目录与权限") check("数据目录在", Path(DATA_DIR).is_dir(), DATA_DIR) code_file = Path(DATA_DIR) / "settings.json" check("settings.json 生成了", code_file.is_file()) if code_file.is_file(): import json try: saved = json.loads(code_file.read_text(encoding="utf-8")) got = saved.get("pairing_code") check("配对码写进去了", got == CODE, "实际 %r" % got) except Exception as exc: # noqa: BLE001 check("settings.json 能解析", False, str(exc)) rc, acl = run_ps( "(Get-Acl '%s').Access | Where-Object { $_.IdentityReference -like " "'*Users*' } | Select-Object -First 1 -ExpandProperty " "FileSystemRights" % DATA_DIR) check("普通用户对数据目录有写权限", "Modify" in acl or "FullControl" in acl or "Write" in acl, acl or "(读不到 ACL)") print() print(" 四、--status 能读到配对码") rc, out = run_ps("& '%s' --status" % exe, timeout=120) check("--status 退出码 0", rc == 0, "exit=%s" % rc) check("--status 显示配对码", CODE in out, out[:200]) for line in (out or "").splitlines()[:8]: print(" " + line.strip()) print() print(" 五、进程与端口") rc, procs = run_ps( "(Get-Process ykxmz -ErrorAction SilentlyContinue | " "Measure-Object).Count") try: count = int((procs or "0").strip().splitlines()[-1]) except ValueError: count = 0 check("ykxmz 进程在跑", count > 0, "%d 个" % count) rc, ports = run_ps( "(Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | " "Where-Object { $_.LocalPort -eq 8765 } | Measure-Object).Count") try: listening = int((ports or "0").strip().splitlines()[-1]) except ValueError: listening = 0 check("8765 端口在监听", listening > 0, "%d 个" % listening) print() print(" 六、静默卸载") rc, out = run_ps( "Get-Process ykxmz -ErrorAction SilentlyContinue | " "Stop-Process -Force -ErrorAction SilentlyContinue; " "Start-Sleep -Seconds 2; " "$u = Get-ChildItem 'C:\\Program Files\\ykxmz\\unins*.exe' " "-ErrorAction SilentlyContinue | Select-Object -First 1; " "if ($u) { Start-Process -FilePath $u.FullName " "-ArgumentList '/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART' " "-Wait -PassThru | Select-Object -ExpandProperty ExitCode } " "else { Write-Output 'no-uninstaller' }", timeout=600) check("卸载器执行了", "no-uninstaller" not in out, out[:120]) time.sleep(6) rc, left = run_ps( "if (Test-Path 'C:\\Program Files\\ykxmz') { Write-Output 'exists' } " "else { Write-Output 'gone' }") check("安装目录已清掉", "gone" in left, left) print() print("=" * 54) print(" 通过 %d 项,失败 %d 项" % (passed, failed)) return 0 if failed == 0 else 1 if __name__ == "__main__": sys.exit(main())