[技术复盘] Windows Python 打包实战:Nuitka 环境踩坑总结与 CI 自动化构建全指南
环境检测(是否依靠 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) # Correct |
Pyinstaller 打包是通过在 sys 下设置 frozen = True,所以如果你想要更强的兼容性
# Ultimate Solution |
import sys |
def isExe() -> bool: |
isNuitkaExe = globals().get("__compiled__", False) |
isPyInstallerExe = getattr(sys, "frozen", False) |
return isNuitkaExe or isPyInstallerExe |
CI 配置
如果要使用 Github Actions 构建 Nuitka 应用,仅仅将本地命令搬上去很可能会报错。
需要添加 --assume-yes-for-downloads 命令参数:--standalone 模式在 Windows 上需要 Dependency Walker。本地可能已在交互环境配置完毕;而 CI 非交互环境下默认选 "no" 导致 FATAL。
环境变量注入
Meta Assistant 是一款托盘工具,用于图形化地用子进程启动特定目录下的 Python 脚本而不需要每次进入命令行输入,其使用了 Tkinter 的文件目录选择器功能。
然而,诡异的事情发生了:
- 环境:本地 Python 3.11;Github CI Python 3.12
- 本地直接用 python 运行脚本:能够成功启动所有脚本
- 本地 Nuitka 构建:能够成功启动所有脚本
- 运行从 CI 下载的打包产物:所有调用子进程 Tkinter 的功能均告失效!
经过数小时的排查,最终确认:
Nuitka exe 注入了环境变量,随 Popen 注入到下游子脚本,与其他脚本的环境不一致,造成崩溃!
罪魁祸首:
TCL_LIBRARY = E:\META-A~1\tcl(Tcl 8.6.12) |
TK_LIBRARY = E:\META-A~1\tk(Tk 8.6.12) |
这个故事告诉我们,Nuitka 构建后如果设计“运行子进程”,务必先检查自动注入的环境变量,及时去除,防止崩溃!
对于接入了 Tkinter 的工具,推荐加入下面的函数:
import os |
import subprocess |
def get_clean_env(): |
env = os.environ.copy() |
# 移除 Nuitka 强制注入的环境变量,让子进程回归系统默认 Tcl/Tk 查找逻辑 |
env.pop("TCL_LIBRARY", None) |
env.pop("TK_LIBRARY", None) |
return env |
subprocess.Popen(["python", "sub_script.py"], env=get_clean_env()) |
后记:SKILL.md
我一直坚持手动复制 SKILL 喂给 AI,这个 SKILL 文档浓缩了各种一路上 AI 犯过的错误,供读者放到本地复用,节省一个小时。
--- |
name: nuitka-build |
description: Please read this if you are configuring GitHub Actions for Python project builds. |
--- |
## Dependency Walker 下载被拒 |
`--standalone` 模式在 Windows 上需要 Dependency Walker,CI 非交互环境下默认选 "no" 导致 FATAL。 |
**必须添加 `--assume-yes-for-downloads` 或在环境中设置 `NUITKA_ASSUME_YES_FOR_DOWNLOADS=1`。** |
## 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__`: |
```python |
if not globals().get("__compiled__", False): |
return # 开发模式,跳过 exe 专属逻辑 |
``` |
## `--windows-installer` 不存在 |
Nuitka**没有** `--windows-installer` 这个选项。需要用安装包时,分两步: |
1. Nuitka 编译为 `--standalone`(产生 `.dist` 目录) |
2. 用 Inno Setup(`iscc`)配合 `.iss` 脚本打包 |
```yaml |
- run: python -m nuitka --standalone ... --output-dir=dist ... |
- 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 要用真 UUID |
AppId 不能随手编,每个项目必须用以下命令生成新的唯一 ID: |
```powershell |
python -c "import uuid; print(str(uuid.uuid4()).upper())" |
``` |
生成后固定写死到 `.iss`。 |
## `--windows-icon-from-ico` |
Nuitka 真实选项,给 exe 设图标,支持 `.ico` 或 `.png` 格式,Nuitka 自动转换: |
```yaml |
--windows-icon-from-ico=assistant.ico |
更多推荐



所有评论(0)