使用 GitHub Actions 和 Nuitka 实现 Python 应用(customtkinter ui库)的自动化跨平台打包
目录
- 引言
- 前置准备
- 配置文件详解
- 实现细节
- CustomTkinter 打包注意事项
- 完整配置示例
- 常见问题
引言
在 Python 应用开发中,将源代码打包成可执行文件是一个常见需求。本文将详细介绍如何使用 GitHub Actions 和 Nuitka 实现自动化的跨平台打包流程,支持 Windows、Linux 和 macOS(包括 Intel 和 ARM 架构)。打包的过程中也是苦于找不到解决问题的文档,把自己的经历记录一下。
需要注意的是 windows 和 linux 的工作流我用了 Nuitka/Nuitka-Action 这个工作流的包,但是发现真的很慢,而且在打包macos老是报错。于是我把macos的工作流修改为我自己的 build.py 来实现。
下面主要是介绍我的流程,如果想要直接实现可以直接跳转到完整配置示例。当然 build.py 你不能直接使用,因为里面涉及到一些我自定义的配置,你需要甄别之后进行调整。然后配置 github action就可以实现自动化工作流。
前置准备
在开始之前,需要准备以下内容:
- Python 项目源代码
- requirements.txt 依赖文件
- GitHub 仓库
- 基本的 Python 开发环境
配置文件详解
本地构建脚本 (build.py)
首先,我们需要一个本地构建脚本来处理依赖检查和构建过程:
def check_and_install_dependencies():required_packages = {"customtkinter": "customtkinter","nuitka": "nuitka",}# ... 检查和安装依赖
这个脚本主要负责:
- 检查和安装必要的依赖
- 配置构建参数
- 处理跨平台差异
GitHub Actions 工作流配置
在 .github/workflows/build.yml
中配置自动化构建流程:
name: Build Executableson:push:tags:- 'v*' # 触发条件:创建新的版本标签
工作流包含四个主要任务:
- Windows 构建任务
build-windows:runs-on: windows-lateststeps:- uses: Nuitka/Nuitka-Action@main# ... Windows 特定配置
- Linux 构建任务
build-linux:runs-on: ubuntu-22.04steps:# ... Linux 特定配置
- macOS Intel 构建任务
build-macos-intel:runs-on: macos-latest# ... macOS Intel 特定配置
- macOS ARM 构建任务
build-macos-arm:runs-on: macos-15# ... macOS ARM 特定配置
实现细节
1. Nuitka 构建参数配置
关键的 Nuitka 构建参数包括:
--follow-imports
: 跟踪并包含所有导入--enable-plugin=tk-inter
: 启用 Tkinter 支持--include-package
: 包含特定包--include-data-dir
: 包含数据目录--macos-create-app-bundle
: macOS 特定选项--windows-console-mode
: Windows 特定选项
2. 平台特定配置
Windows
windows-console-mode: disable
windows-icon-from-ico: src/assets/app_icon.ico
macOS
- name: Install Tcl/Tk Dependenciesrun: |brew install tcl-tkTCL_PATH=$(brew --prefix tcl-tk)
Linux
- name: Install System Dependenciesuses: awalsh128/cache-apt-pkgs-action@latestwith:packages: python3-tk tk-dev
3. 构建产物处理
使用 GitHub Actions 的 artifacts 功能保存构建结果:
- name: Upload artifactuses: actions/upload-artifact@v4with:name: CursorMagic-Platformpath: dist/*.app # 或 .exe 等
CustomTkinter 打包注意事项
1. 必要的打包参数
使用 Nuitka 打包 CustomTkinter 应用时,需要特别注意以下参数:
- name: Build with Nuitkauses: Nuitka/Nuitka-Action@mainwith:nuitka-version: mainenable-plugins: tk-inter # 必须启用 tk-inter 插件include-package: customtkinter # 必须包含 customtkinter 包
2. 资源文件处理
CustomTkinter 的主题和样式文件需要正确打包:
-
主题文件
- CustomTkinter 的主题文件位于包内的
assets
目录 - 需要确保这些文件被正确包含在最终的可执行文件中
- CustomTkinter 的主题文件位于包内的
-
自定义资源
- 如果使用了自定义主题或图标,需要使用
include-data-dir
确保资源文件被打包
- 如果使用了自定义主题或图标,需要使用
3. 平台特定配置
Windows 平台
windows-console-mode: disable # 禁用控制台窗口
standalone: true # 确保所有依赖被打包
macOS 平台
macos-create-app-bundle: true # 创建 .app 包
enable-plugins: tk-inter
onefile: false # CustomTkinter 在 macOS 上不建议使用 onefile 模式
4. 依赖处理
-
版本控制
# requirements.txt customtkinter==5.2.1 # 建议指定具体版本 darkdetect>=0.7.1 # CustomTkinter 的依赖
-
依赖检查
def check_dependencies():required = {"customtkinter": "customtkinter>=5.2.0","darkdetect": "darkdetect>=0.7.1"}# ... 检查依赖
5. 常见问题解决
-
主题加载失败
- 症状:应用启动后显示默认 tkinter 样式
- 解决:确保添加
--include-package=customtkinter
参数
-
图片资源丢失
- 症状:自定义图标或图片无法显示
- 解决:使用
--include-data-dir
指定资源目录
-
暗色模式问题
- 症状:无法正确响应系统暗色模式
- 解决:确保
darkdetect
包被正确打包
-
macOS 特定问题
- name: Fix macOS Permissionsif: runner.os == 'macOS'run: |chmod +x dist/*.app/Contents/MacOS/*
完整配置示例
这里提供一个实际的跨平台打包配置示例,包含本地构建脚本和 GitHub Actions 工作流配置。
本地构建脚本 (build.py)
import os
import sys
import subprocess
import pkg_resourcesdef check_and_install_dependencies():required_packages = {"customtkinter": "customtkinter","nuitka": "nuitka",}print("检查依赖项...")for package, pip_name in required_packages.items():try:pkg_resources.require(package)print(f"✓ {package} 已安装")except pkg_resources.DistributionNotFound:print(f"安装 {package}...")subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name])def run_command(command):try:subprocess.run(command, shell=True, check=True)print(f"成功执行命令: {command}")except subprocess.CalledProcessError as e:print(f"命令执行失败: {command}")print(f"错误信息: {str(e)}")sys.exit(1)def main():print("开始构建过程...")# 设置输出目录和文件名output_dir = "dist"app_name = "CursorMagic"# 基础命令行选项base_options = ["--follow-imports", # 跟踪导入"--enable-plugin=tk-inter", # 启用 Tkinter 支持"--include-package=customtkinter", # 包含 customtkinterf"--include-data-dir=src/utils/turnstilePatch=turnstilePatch", # 包含数据目录f"--include-data-files=src/core/names-dataset.txt=names-dataset.txt", # 包含具体的 txt 文件f"--include-data-dir=src/config=src/config", # 包含配置目录"--warn-unusual-code", # 警告不寻常的代码"--warn-implicit-exceptions", # 警告隐式异常"--nofollow-import-to=tkinter.test", # 排除测试模块"--nofollow-import-to=PIL.ImageQt", # 排除 Qt 相关"--remove-output", # 删除之前的输出f"--output-dir={output_dir}", # 输出目录f'--output-file="{app_name}"', # 输出文件名]# 根据操作系统添加特定选项if sys.platform == "darwin": # macOStcl_path = os.getenv("TCL_PATH")if not tcl_path:try:tcl_path = subprocess.check_output(["brew", "--prefix", "tcl-tk"]).decode().strip()except:print("警告: 无法找到 Tcl/Tk 路径")sys.exit(1)base_options.extend(["--macos-create-app-bundle", # 创建 macOS 应用包"--macos-app-icon=src/assets/app_icon.icns", # 设置应用图标f"--macos-app-name={app_name}.app", # 设置应用名称])elif sys.platform == "win32": # Windowsbase_options.extend(["--standalone", # 独立可执行文件"--mingw64", # 使用 MinGW64"--windows-console-mode=disable", # Windows 禁用控制台"--windows-icon-from-ico=src/assets/app_icon.ico", # Windows 应用图标])nuitka_command = "python -m nuitka " + " ".join(base_options) + " " + "CursorMagic.py"run_command(nuitka_command)print("\n构建过程完成!")if __name__ == "__main__":main()
GitHub Actions 工作流配置 (.github/workflows/build.yml)
name: Build Executableson:push:tags:- 'v*' # 添加标签触发条件,匹配 v1.0.0 这样的标签jobs:build-windows:runs-on: windows-lateststeps:- uses: actions/checkout@v4- name: Set up Pythonuses: actions/setup-python@v5with:python-version: '3.x'architecture: 'x64'cache: 'pip'cache-dependency-path: |**/requirements*.txt- name: Install Dependenciesrun: |pip install -r requirements.txt- name: Build Windows Executableuses: Nuitka/Nuitka-Action@mainwith:nuitka-version: mainscript-name: CursorMagic.pymode: onefileenable-plugins: tk-interinclude-package: customtkinterinclude-data-dir: |src/utils/turnstilePatch=turnstilePatchsrc/config=src/configinclude-data-files: src/core/names-dataset.txt=names-dataset.txtwindows-console-mode: disablewindows-icon-from-ico: src/assets/app_icon.icooutput-file: CursorMagic- name: Upload Windows artifactuses: actions/upload-artifact@v4with:name: CursorMagic-Windowspath: build/*.exeinclude-hidden-files: truebuild-linux:runs-on: ubuntu-22.04steps:- uses: actions/checkout@v4- name: Set up Pythonuses: actions/setup-python@v5with:python-version: '3.x'architecture: 'x64'cache: 'pip'cache-dependency-path: |**/requirements*.txt- name: Install System Dependenciesuses: awalsh128/cache-apt-pkgs-action@latestwith:packages: python3-tk tk-devversion: 1.0- name: Install Dependenciesrun: |pip install -r requirements.txt- name: Build Linux Executableuses: Nuitka/Nuitka-Action@mainwith:nuitka-version: mainscript-name: CursorMagic.pymode: onefileenable-plugins: tk-interinclude-package: customtkinterinclude-data-dir: |src/utils/turnstilePatch=turnstilePatchsrc/config=src/configinclude-data-files: src/core/names-dataset.txt=names-dataset.txtoutput-file: CursorMagic- name: Upload Linux artifactuses: actions/upload-artifact@v4with:name: CursorMagic-Linuxpath: build/CursorMagicinclude-hidden-files: truebuild-macos-intel:runs-on: macos-lateststeps:- uses: actions/checkout@v4- name: Set up Pythonuses: actions/setup-python@v5with:python-version: '3.11'architecture: 'x64'cache: 'pip'cache-dependency-path: |**/requirements*.txt- name: Install Tcl/Tk Dependenciesrun: |brew install tcl-tkTCL_PATH=$(brew --prefix tcl-tk)echo "TCL_PATH=$TCL_PATH" >> $GITHUB_ENVecho "LDFLAGS=-L$TCL_PATH/lib" >> $GITHUB_ENVecho "CPPFLAGS=-I$TCL_PATH/include" >> $GITHUB_ENVecho "PKG_CONFIG_PATH=$TCL_PATH/lib/pkgconfig" >> $GITHUB_ENVecho "Using Tcl/Tk from: $TCL_PATH"- name: Install Dependenciesrun: |pip install setuptoolspip install --upgrade nuitkapip install -r requirements.txtbrew install ccache- name: Clean Build Directoryrun: rm -rf build- name: Build with build.pyrun: python build.py- name: Upload MacOS Intel artifactuses: actions/upload-artifact@v4with:name: CursorMagic-MacOS-Intelpath: dist/*.appinclude-hidden-files: truebuild-macos-arm:runs-on: macos-15steps:- uses: actions/checkout@v4- name: Set up Pythonuses: actions/setup-python@v5with:python-version: '3.11'architecture: 'arm64'cache: 'pip'cache-dependency-path: |**/requirements*.txt- name: Install Tcl/Tk Dependenciesrun: |brew install tcl-tkTCL_PATH=$(brew --prefix tcl-tk)echo "TCL_PATH=$TCL_PATH" >> $GITHUB_ENVecho "LDFLAGS=-L$TCL_PATH/lib" >> $GITHUB_ENVecho "CPPFLAGS=-I$TCL_PATH/include" >> $GITHUB_ENVecho "PKG_CONFIG_PATH=$TCL_PATH/lib/pkgconfig" >> $GITHUB_ENVecho "Using Tcl/Tk from: $TCL_PATH"- name: Install Dependenciesrun: |pip install setuptoolspip install --upgrade nuitkapip install -r requirements.txtbrew install ccache- name: Clean Build Directoryrun: rm -rf build- name: Build with build.pyrun: python build.py- name: Upload MacOS ARM artifactuses: actions/upload-artifact@v4with:name: CursorMagic-MacOS-ARMpath: dist/*.appinclude-hidden-files: true
这个完整配置示例展示了:
-
本地构建脚本特点
- 自动检查和安装依赖
- 跨平台构建支持
- 详细的构建选项配置
- 错误处理和日志输出
-
GitHub Actions 工作流特点
- 支持四个平台的构建(Windows、Linux、macOS Intel、macOS ARM)
- 完整的依赖安装配置
- 构建缓存优化
- 统一的构建产物处理
-
关键配置点
- CustomTkinter 相关配置
- 平台特定的依赖处理
- 资源文件打包
- 构建产物管理
常见问题
-
依赖问题
- 确保 requirements.txt 完整且版本号正确
- 使用
--include-package
包含所有必要的包
-
TCL/TK 相关问题
- macOS 需要正确配置 TCL_PATH
- Linux 需要安装 python3-tk 和 tk-dev
-
路径问题
- 使用相对路径
- 正确配置
include-data-dir
和include-data-files
-
平台特定问题
- Windows: 注意图标和控制台模式配置
- macOS: 注意 ARM/Intel 架构差异内容已经更新完成,您可以查看完整的文档内容并进行必要的调整和修改。如果您有任何其他问题或需要进一步帮助,请随时告诉我。内容已经更新完成,您可以查看完整的文档内容并进行必要的调整和修改。如果您有任何其他问题或需要进一步帮助,请随时告诉我。
- Linux: 注意系统依赖
结语
通过合理配置 GitHub Actions 和 Nuitka,我们可以实现 Python 应用的自动化跨平台打包。这不仅提高了开发效率,也确保了构建过程的一致性和可靠性。
参考资料
- Nuitka 官方文档
- GitHub Actions 文档
- Python 打包指南