[技术复盘] Windows Python 打包实战:Nuitka 环境踩坑总结与 CI 自动化构建全指南
2026/6/29 20:38:31
网站开发
环境检测是否依靠 exe 运行有些功能如开机自启动可能需要限制在依靠 exe 运行的情况相信很多开发者第一反应就信手拈来sys.executable侦测isExe sys.executable.lower() not in (python.exe, pythonw.exe) # Wrong!但是Nuitka 构建之后的sys.executable很可能稳定指向python解释器的路径这个方法是不可靠的正确解法Nuitka 构建完毕后会注入变量__compiled__为True。isExe globals().get(__compiled__, False) # CorrectPyinstaller打包是通过在sys下设置frozen True所以如果你想要更强的兼容性# Ultimate Solutionimport sysdef isExe() - bool:isNuitkaExe globals().get(__compiled__, False)isPyInstallerExe getattr(sys, frozen, False)return isNuitkaExe or isPyInstallerExeCI 配置如果要使用 Github Actions 构建 Nuitka 应用仅仅将本地命令搬上去很可能会报错。需要添加--assume-yes-for-downloads命令参数--standalone模式在 Windows 上需要 Dependency Walker。本地可能已在交互环境配置完毕而 CI 非交互环境下默认选 no 导致 FATAL。环境变量注入Meta Assistant 是一款托盘工具用于图形化地用子进程启动特定目录下的 Python 脚本而不需要每次进入命令行输入其使用了 Tkinter 的文件目录选择器功能。然而诡异的事情发生了环境本地 Python 3.11Github CI Python 3.12本地直接用 python 运行脚本能够成功启动所有脚本本地 Nuitka 构建能够成功启动所有脚本运行从 CI 下载的打包产物所有调用子进程 Tkinter 的功能均告失效经过数小时的排查最终确认Nuitka exe 注入了环境变量随Popen注入到下游子脚本与其他脚本的环境不一致造成崩溃罪魁祸首TCL_LIBRARY E:\META-A~1\tclTcl 8.6.12TK_LIBRARY E:\META-A~1\tkTk 8.6.12这个故事告诉我们Nuitka 构建后如果设计“运行子进程”务必先检查自动注入的环境变量及时去除防止崩溃对于接入了 Tkinter 的工具推荐加入下面的函数import osimport subprocessdef get_clean_env():env os.environ.copy()# 移除 Nuitka 强制注入的环境变量让子进程回归系统默认 Tcl/Tk 查找逻辑env.pop(TCL_LIBRARY, None)env.pop(TK_LIBRARY, None)return envsubprocess.Popen([python, sub_script.py], envget_clean_env())后记SKILL.md我一直坚持手动复制 SKILL 喂给 AI这个 SKILL 文档浓缩了各种一路上 AI 犯过的错误供读者放到本地复用节省一个小时。---name: nuitka-builddescription: Please read this if you are configuring GitHub Actions for Python project builds.---## Dependency Walker 下载被拒--standalone 模式在 Windows 上需要 Dependency WalkerCI 非交互环境下默认选 no 导致 FATAL。**必须添加 --assume-yes-for-downloads 或在环境中设置 NUITKA_ASSUME_YES_FOR_DOWNLOADS1。**## Release 403 错误softprops/action-gh-release 创建 Release 时 403原因是默认 GITHUB_TOKEN 只有 contents: read。**修复在 release workflow 顶部添加 permissions: contents: write。**## Windows runner 没有 zip 命令windows-latest 上没有 Unix zip 命令用 zip -r 会报错。**修复使用 PowerShell 的 Compress-Archive -Path source -DestinationPath output.zip。**## sys.executable 在新版 Nuitka 中不可靠编译后 sys.executable 可能指向 Python 解释器而非 exe 路径Path(sys.executable).name 检测失效。**修复**改用 Nuitka 编译时注入的 __compiled__pythonif not globals().get(__compiled__, False):return # 开发模式跳过 exe 专属逻辑## --windows-installer 不存在Nuitka**没有** --windows-installer 这个选项。需要用安装包时分两步1. Nuitka 编译为 --standalone产生 .dist 目录2. 用 Inno Setupiscc配合 .iss 脚本打包yaml- run: python -m nuitka --standalone ... --output-dirdist ...- run: iscc /DAPP_VERSION${{ github.ref_name }} installer.iss## ISS 脚本版本号用预处理器变量GetEnv() 取的是系统环境变量**不是** /D 传入的预处理器变量。正确用法iss; ❌ GetEnv 读不到 /D 参数AppVersion{#APP_VERSION} ; ✓ 预处理器变量Workflow 传值yaml- run: iscc /DAPP_VERSION${{ github.ref_name }} installer.iss## ISS AppId 要用真 UUIDAppId 不能随手编每个项目必须用以下命令生成新的唯一 IDpowershellpython -c import uuid; print(str(uuid.uuid4()).upper())生成后固定写死到 .iss。## --windows-icon-from-icoNuitka 真实选项给 exe 设图标支持 .ico 或 .png 格式Nuitka 自动转换yaml--windows-icon-from-icoassistant.ico