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就可以实现自动化工作流。

    前置准备

    在开始之前,需要准备以下内容:

    1. Python 项目源代码
    2. requirements.txt 依赖文件
    3. GitHub 仓库
    4. 基本的 Python 开发环境

    配置文件详解

    本地构建脚本 (build.py)

    首先,我们需要一个本地构建脚本来处理依赖检查和构建过程:

    def check_and_install_dependencies():
        required_packages = {
            "customtkinter": "customtkinter",
            "nuitka": "nuitka",
        }
        # ... 检查和安装依赖
    

    这个脚本主要负责:

  • 检查和安装必要的依赖
  • 配置构建参数
  • 处理跨平台差异
  • GitHub Actions 工作流配置

    .github/workflows/build.yml 中配置自动化构建流程:

    name: Build Executables
    
    on:
      push:
        tags:
          - 'v*'  # 触发条件:创建新的版本标签
    

    工作流包含四个主要任务:

    1. Windows 构建任务
    build-windows:
      runs-on: windows-latest
      steps:
        - uses: Nuitka/Nuitka-Action@main
        # ... Windows 特定配置
    
    1. Linux 构建任务
    build-linux:
      runs-on: ubuntu-22.04
      steps:
        # ... Linux 特定配置
    
    1. macOS Intel 构建任务
    build-macos-intel:
      runs-on: macos-latest
      # ... macOS Intel 特定配置
    
    1. 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 Dependencies
      run: |
        brew install tcl-tk
        TCL_PATH=$(brew --prefix tcl-tk)
    
    Linux
    - name: Install System Dependencies
      uses: awalsh128/cache-apt-pkgs-action@latest
      with:
        packages: python3-tk tk-dev
    

    3. 构建产物处理

    使用 GitHub Actions 的 artifacts 功能保存构建结果:

    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: CursorMagic-Platform
        path: dist/*.app  # 或 .exe 等
    

    CustomTkinter 打包注意事项

    1. 必要的打包参数

    使用 Nuitka 打包 CustomTkinter 应用时,需要特别注意以下参数:

    - name: Build with Nuitka
      uses: Nuitka/Nuitka-Action@main
      with:
        nuitka-version: main
        enable-plugins: tk-inter  # 必须启用 tk-inter 插件
        include-package: customtkinter  # 必须包含 customtkinter 包
    

    2. 资源文件处理

    CustomTkinter 的主题和样式文件需要正确打包:

    1. 主题文件

    2. CustomTkinter 的主题文件位于包内的 assets 目录
    3. 需要确保这些文件被正确包含在最终的可执行文件中
    4. 自定义资源

    5. 如果使用了自定义主题或图标,需要使用 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. 依赖处理

    1. 版本控制

      # requirements.txt
      customtkinter==5.2.1  # 建议指定具体版本
      darkdetect>=0.7.1    # CustomTkinter 的依赖
      
    2. 依赖检查

      def check_dependencies():
          required = {
              "customtkinter": "customtkinter>=5.2.0",
              "darkdetect": "darkdetect>=0.7.1"
          }
          # ... 检查依赖
      

    5. 常见问题解决

    1. 主题加载失败

    2. 症状:应用启动后显示默认 tkinter 样式
    3. 解决:确保添加 --include-package=customtkinter 参数
    4. 图片资源丢失

    5. 症状:自定义图标或图片无法显示
    6. 解决:使用 --include-data-dir 指定资源目录
    7. 暗色模式问题

    8. 症状:无法正确响应系统暗色模式
    9. 解决:确保 darkdetect 包被正确打包
    10. macOS 特定问题

      - name: Fix macOS Permissions
        if: runner.os == 'macOS'
        run: |
          chmod +x dist/*.app/Contents/MacOS/*
      

    完整配置示例

    这里提供一个实际的跨平台打包配置示例,包含本地构建脚本和 GitHub Actions 工作流配置。

    本地构建脚本 (build.py)

    import os
    import sys
    import subprocess
    import pkg_resources
    
    def 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",  # 包含 customtkinter
            f"--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":  # macOS
            tcl_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":  # Windows
            base_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 Executables
    
    on:
      push:
        tags:
          - 'v*'  # 添加标签触发条件,匹配 v1.0.0 这样的标签
    
    jobs:
      build-windows:
        runs-on: windows-latest
        
        steps:
          - uses: actions/checkout@v4
          
          - name: Set up Python
            uses: actions/setup-python@v5
            with:
              python-version: '3.x'
              architecture: 'x64'
              cache: 'pip'
              cache-dependency-path: |
                **/requirements*.txt
          
          - name: Install Dependencies
            run: |
              pip install -r requirements.txt
          
          - name: Build Windows Executable
            uses: Nuitka/Nuitka-Action@main
            with:
              nuitka-version: main
              script-name: CursorMagic.py
              mode: onefile
              enable-plugins: tk-inter
              include-package: customtkinter
              include-data-dir: |
                src/utils/turnstilePatch=turnstilePatch
                src/config=src/config
              include-data-files: src/core/names-dataset.txt=names-dataset.txt
              windows-console-mode: disable
              windows-icon-from-ico: src/assets/app_icon.ico
              output-file: CursorMagic
              
          - name: Upload Windows artifact
            uses: actions/upload-artifact@v4
            with:
              name: CursorMagic-Windows
              path: build/*.exe
              include-hidden-files: true
    
      build-linux:
        runs-on: ubuntu-22.04
        
        steps:
        - uses: actions/checkout@v4
        
        - name: Set up Python
          uses: actions/setup-python@v5
          with:
            python-version: '3.x'
            architecture: 'x64'
            cache: 'pip'
            cache-dependency-path: |
              **/requirements*.txt
        
        - name: Install System Dependencies
          uses: awalsh128/cache-apt-pkgs-action@latest
          with:
            packages: python3-tk tk-dev
            version: 1.0
        
        - name: Install Dependencies
          run: |
            pip install -r requirements.txt
            
        - name: Build Linux Executable
          uses: Nuitka/Nuitka-Action@main
          with:
            nuitka-version: main
            script-name: CursorMagic.py
            mode: onefile
            enable-plugins: tk-inter
            include-package: customtkinter
            include-data-dir: |
              src/utils/turnstilePatch=turnstilePatch
              src/config=src/config
            include-data-files: src/core/names-dataset.txt=names-dataset.txt
            output-file: CursorMagic
            
        - name: Upload Linux artifact
          uses: actions/upload-artifact@v4
          with:
            name: CursorMagic-Linux
            path: build/CursorMagic
            include-hidden-files: true
    
      build-macos-intel:
        runs-on: macos-latest
      
        steps:
        - uses: actions/checkout@v4
      
        - name: Set up Python
          uses: actions/setup-python@v5
          with:
            python-version: '3.11'
            architecture: 'x64'
            cache: 'pip'
            cache-dependency-path: |
              **/requirements*.txt
      
        - name: Install Tcl/Tk Dependencies
          run: |
            brew install tcl-tk
            TCL_PATH=$(brew --prefix tcl-tk)
            echo "TCL_PATH=$TCL_PATH" >> $GITHUB_ENV
            echo "LDFLAGS=-L$TCL_PATH/lib" >> $GITHUB_ENV
            echo "CPPFLAGS=-I$TCL_PATH/include" >> $GITHUB_ENV
            echo "PKG_CONFIG_PATH=$TCL_PATH/lib/pkgconfig" >> $GITHUB_ENV
            echo "Using Tcl/Tk from: $TCL_PATH"
      
        - name: Install Dependencies
          run: |
            pip install setuptools
            pip install --upgrade nuitka
            pip install -r requirements.txt
            brew install ccache
          
        - name: Clean Build Directory
          run: rm -rf build
          
        - name: Build with build.py
          run: python build.py
      
        - name: Upload MacOS Intel artifact
          uses: actions/upload-artifact@v4
          with:
            name: CursorMagic-MacOS-Intel
            path: dist/*.app
            include-hidden-files: true
    
      build-macos-arm:
        runs-on: macos-15
        
        steps:
        - uses: actions/checkout@v4
        
        - name: Set up Python
          uses: actions/setup-python@v5
          with:
            python-version: '3.11'
            architecture: 'arm64'
            cache: 'pip'
            cache-dependency-path: |
              **/requirements*.txt
        
        - name: Install Tcl/Tk Dependencies
          run: |
            brew install tcl-tk
            TCL_PATH=$(brew --prefix tcl-tk)
            echo "TCL_PATH=$TCL_PATH" >> $GITHUB_ENV
            echo "LDFLAGS=-L$TCL_PATH/lib" >> $GITHUB_ENV
            echo "CPPFLAGS=-I$TCL_PATH/include" >> $GITHUB_ENV
            echo "PKG_CONFIG_PATH=$TCL_PATH/lib/pkgconfig" >> $GITHUB_ENV
            echo "Using Tcl/Tk from: $TCL_PATH"
        
        - name: Install Dependencies
          run: |
            pip install setuptools
            pip install --upgrade nuitka
            pip install -r requirements.txt
            brew install ccache
            
        - name: Clean Build Directory
          run: rm -rf build
        
        - name: Build with build.py
          run: python build.py
       
        - name: Upload MacOS ARM artifact
          uses: actions/upload-artifact@v4
          with:
            name: CursorMagic-MacOS-ARM
            path: dist/*.app
            include-hidden-files: true
    

    这个完整配置示例展示了:

    1. 本地构建脚本特点

    2. 自动检查和安装依赖
    3. 跨平台构建支持
    4. 详细的构建选项配置
    5. 错误处理和日志输出
    6. GitHub Actions 工作流特点

    7. 支持四个平台的构建(Windows、Linux、macOS Intel、macOS ARM)
    8. 完整的依赖安装配置
    9. 构建缓存优化
    10. 统一的构建产物处理
    11. 关键配置点

    12. CustomTkinter 相关配置
    13. 平台特定的依赖处理
    14. 资源文件打包
    15. 构建产物管理

    常见问题

    1. 依赖问题

    2. 确保 requirements.txt 完整且版本号正确
    3. 使用 --include-package 包含所有必要的包
    4. TCL/TK 相关问题

    5. macOS 需要正确配置 TCL_PATH
    6. Linux 需要安装 python3-tk 和 tk-dev
    7. 路径问题

    8. 使用相对路径
    9. 正确配置 include-data-dirinclude-data-files
    10. 平台特定问题

    11. Windows: 注意图标和控制台模式配置
    12. macOS: 注意 ARM/Intel 架构差异内容已经更新完成,您可以查看完整的文档内容并进行必要的调整和修改。如果您有任何其他问题或需要进一步帮助,请随时告诉我。内容已经更新完成,您可以查看完整的文档内容并进行必要的调整和修改。如果您有任何其他问题或需要进一步帮助,请随时告诉我。
    13. Linux: 注意系统依赖

    结语

    通过合理配置 GitHub Actions 和 Nuitka,我们可以实现 Python 应用的自动化跨平台打包。这不仅提高了开发效率,也确保了构建过程的一致性和可靠性。

    参考资料

  • Nuitka 官方文档
  • GitHub Actions 文档
  • Python 打包指南
  • 作者:是阿臻

    物联沃分享整理
    物联沃-IOTWORD物联网 » GitHub Actions与Nuitka实现Python应用(customtkinter UI库)自动化跨平台打包指南

    发表回复