使用 GitHub Actions CI/CD 工作流发布包分发版本

GitHub Actions CI/CD 允许您在 GitHub 平台上发生事件时运行一系列命令。一种流行的选择是创建一个由 push 事件触发的工作流。本指南将向您展示如何在推送带有标签的提交时发布 Python 发行版。它将使用 pypa/gh-action-pypi-publish GitHub Action 进行发布。它还使用 GitHub 的 upload-artifactdownload-artifact 操作来临时存储和下载源包。

注意

本指南假设您已经有一个项目,并且您知道如何为其构建发行版,并且它位于 GitHub 上。本指南还避免了构建特定于平台项目的细节。如果您有二进制组件,请查看 cibuildwheel 的 GitHub Action 示例。

配置可信发布

本指南依赖 PyPI 的 可信发布 实现来连接 GitHub Actions CI/CD。出于安全原因,建议这样做,因为生成的令牌是为您的每个项目单独创建的,并且会自动过期。否则,您需要为 PyPI 和 TestPyPI 生成一个 API 令牌。如果是发布到像 devpi 这样的第三方索引,您可能需要提供用户名/密码组合。

由于本指南将演示同时上传到 PyPI 和 TestPyPI,因此我们需要配置两个可信发布者。以下步骤将引导您为新的 PyPI 项目 创建“待定”发布者。但是,如果您是所有者,也可以将 可信发布 添加到任何现有项目。

注意

如果您遵循了本指南的早期版本,您已经为直接访问 PyPI 和 TestPyPI 创建了 secret PYPI_API_TOKENTEST_PYPI_API_TOKEN。这些现在已过时,如果您要用新设置替换旧设置,则应将它们从 GitHub 仓库中删除,并在 PyPI 和 TestPyPI 账户设置中撤销它们。

我们开始吧!🚀

  1. 前往 https://pypi.ac.cn/manage/account/publishing/

  2. 填写您希望发布新 PyPI 项目 的名称(setup.cfgpyproject.toml 中的 name 值)、GitHub 仓库所有者的名称(组织或用户)、仓库名称,以及 .github/ 文件夹下的发布工作流文件的名称,请参阅 创建工作流定义。最后,添加我们将在您的仓库下设置的 GitHub 环境名称(pypi)。注册可信发布者。

  3. 现在,前往 https://test.pypi.org/manage/account/publishing/ 并重复第二步,但这次,输入 testpypi 作为 GitHub 环境的名称。

  4. 您的“待定”发布者现在已准备好首次使用,一旦您首次使用它们,它们将自动创建您的项目。

    注意

    如果您没有 TestPyPI 账户,您需要创建一个。它与常规 PyPI 账户不同。

    注意

    出于安全原因,您必须要求对 pypi 环境的每次运行都进行手动批准

创建工作流定义

GitHub CI/CD 工作流在存储在仓库的 .github/workflows/ 目录中的 YAML 文件中声明。

让我们创建一个 .github/workflows/publish-to-test-pypi.yml 文件。

以一个有意义的名称开始,并定义应使 GitHub 运行此工作流的事件

name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI

on: push

检出项目并构建发行版

我们将不得不定义两个作业分别发布到 PyPI 和 TestPyPI,以及一个额外的作业来构建发行包。

首先,我们将定义用于构建项目分发包并将其存储以备后用的作业

jobs:
  build:
    name: Build distribution 📦
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
      with:
        persist-credentials: false
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: "3.x"

这将把您的仓库下载到 CI 运行器中,然后安装并激活最新的可用 Python 3 版本。

现在我们可以从源代码构建发行版并存储它们。在这个例子中,我们将使用 build 包。所以将其添加到步骤列表中

    - name: Install pypa/build
      run: >-
        python3 -m
        pip install
        build
        --user
    - name: Build a binary wheel and a source tarball
      run: python3 -m build
    - name: Store the distribution packages
      uses: actions/upload-artifact@v4
      with:
        name: python-package-distributions
        path: dist/

定义工作流作业环境

现在,让我们为将发布到 PyPI 的作业添加初始设置。这是一个将执行我们稍后定义的命令的过程。在本指南中,我们将使用 GitHub Actions 提供的最新稳定的 Ubuntu LTS 版本。这还为作业定义了一个 GitHub 环境,以便在其上下文中运行,以及一个将在 GitHub UI 中漂亮地显示的 URL。此外,它允许获取 pypi-publish 动作实现无秘密可信发布到 PyPI 所需的 OpenID Connect 令牌。

  publish-to-pypi:
    name: >-
      Publish Python 🐍 distribution 📦 to PyPI
    if: startsWith(github.ref, 'refs/tags/')  # only publish to PyPI on tag pushes
    needs:
    - build
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.ac.cn/p/<package-name>  # Replace <package-name> with your PyPI project name
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing

这还将确保 PyPI 发布工作流仅在当前提交带有标签时触发。

将发行版发布到 PyPI

最后,在末尾添加以下步骤

    steps:
    - name: Download all the dists
      uses: actions/download-artifact@v4
      with:
        name: python-package-distributions
        path: dist/
    - name: Publish distribution 📦 to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

此步骤使用 pypa/gh-action-pypi-publish GitHub Action:在 download-artifact action 下载存储的发行包后,它无条件地将 dist/ 文件夹的内容上传到 PyPI。

提示

v1.11.0 版本开始,pypa/gh-action-pypi-publish 默认生成并上传 PEP 740 兼容的每个发行版证明。不需要额外的手动签名步骤。

独立工作流发布到 TestPyPI

现在,重复这些步骤,并在 jobs 部分下创建另一个用于发布到 TestPyPI 包索引的作业

  publish-to-testpypi:
    name: Publish Python 🐍 distribution 📦 to TestPyPI
    needs:
    - build
    runs-on: ubuntu-latest

    environment:
      name: testpypi
      url: https://test.pypi.org/p/<package-name>

    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing

    steps:
    - name: Download all the dists
      uses: actions/download-artifact@v4
      with:
        name: python-package-distributions
        path: dist/
    - name: Publish distribution 📦 to TestPyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        repository-url: https://test.pypi.org/legacy/

提示

testpypi GitHub 环境中要求手动批准通常是不必要的,因为它旨在在每次提交到主分支时运行,并且通常用于指示健康的发布管道。

完整的 CI/CD 工作流

本段展示了遵循上述指南后的完整工作流。

点击此处显示完整的 GitHub Actions CI/CD 工作流定义
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI

on: push

jobs:
  build:
    name: Build distribution 📦
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
      with:
        persist-credentials: false
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: "3.x"
    - name: Install pypa/build
      run: >-
        python3 -m
        pip install
        build
        --user
    - name: Build a binary wheel and a source tarball
      run: python3 -m build
    - name: Store the distribution packages
      uses: actions/upload-artifact@v4
      with:
        name: python-package-distributions
        path: dist/

  publish-to-pypi:
    name: >-
      Publish Python 🐍 distribution 📦 to PyPI
    if: startsWith(github.ref, 'refs/tags/')  # only publish to PyPI on tag pushes
    needs:
    - build
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.ac.cn/p/<package-name>  # Replace <package-name> with your PyPI project name
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing

    steps:
    - name: Download all the dists
      uses: actions/download-artifact@v4
      with:
        name: python-package-distributions
        path: dist/
    - name: Publish distribution 📦 to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-testpypi:
    name: Publish Python 🐍 distribution 📦 to TestPyPI
    needs:
    - build
    runs-on: ubuntu-latest

    environment:
      name: testpypi
      url: https://test.pypi.org/p/<package-name>

    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing

    steps:
    - name: Download all the dists
      uses: actions/download-artifact@v4
      with:
        name: python-package-distributions
        path: dist/
    - name: Publish distribution 📦 to TestPyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        repository-url: https://test.pypi.org/legacy/

就这些了,各位!

现在,每当您将带标签的提交推送到 GitHub 上的 Git 仓库远程端时,此工作流都会将其发布到 PyPI。并且它会将任何推送发布到 TestPyPI,这对于向您的 alpha 用户提供测试版本以及确保您的发布管道保持健康非常有用!

注意

如果您的仓库提交活动频繁,并且每次推送都按所述上传到 TestPyPI,则项目可能会超出 PyPI 项目大小限制。可以增加限制,但更好的解决方案可能是使用像 pypiserver 这样的 PyPI 兼容服务器在 CI 中进行测试。

注意

建议将集成的 GitHub Actions 保持在最新版本,并经常更新它们。