使用 GitHub Actions CI/CD 工作流发布包发行版本#
GitHub Actions CI/CD 允许你在 GitHub 平台上发生事件时运行一系列命令。一种流行的选择是拥有一个由 push
事件触发的 workflow。本指南展示了如何在你推送标记提交时发布 Python 发行版。它将使用 pypa/gh-action-pypi-publish GitHub Action 进行发布。它还使用 GitHub 的 upload-artifact 和 download-artifact 操作来临时存储和下载源包。
注意
本指南假设你已经有一个项目,你知道如何为其构建发行版,并且它位于 GitHub 上。本指南还避免了构建特定于平台的项目的详细信息。如果你有二进制组件,请查看 cibuildwheel 的 GitHub Action 示例。
配置受信任的发布#
本指南依赖于 PyPI 的 受信任的发布 实现来连接到 GitHub Actions CI/CD。出于安全原因,建议这样做,因为生成的令牌是为你的每个项目单独创建的,并且会自动过期。否则,你需要为 PyPI 和 TestPyPI 生成 API 令牌。如果要发布到第三方索引,如 devpi,你可能需要提供用户名/密码组合。
由于本指南将演示如何上传到 PyPI 和 TestPyPI,因此我们需要配置两个受信任的发布者。以下步骤将指导你为你的新 PyPI 项目 创建“待定”发布者。但是,如果你拥有某个预先存在的项目,也可以向其添加 受信任的发布。
注意
如果你按照本指南的早期版本进行操作,则已创建了机密 PYPI_API_TOKEN
和 TEST_PYPI_API_TOKEN
以直接访问 PyPI 和 TestPyPI。现在这些已过时,如果你要使用新设置替换旧设置,则应将其从 GitHub 存储库中删除,并在 PyPI 和 TestPyPI 帐户设置中撤销它们。
让我们开始吧!🚀
填写你希望在其下发布你的新 PyPI 项目 的名称(
setup.cfg
或pyproject.toml
中的name
值),GitHub 存储库所有者的名称(组织或用户)和存储库名称,以及.github/
文件夹下的发布工作流文件名称,请参阅 创建工作流定义。最后,添加我们将在你的存储库下设置的 GitHub 环境(pypi
)的名称。注册受信任的发布者。现在,转到 https://test.pypi.org/manage/account/publishing/ 并重复第二步,但这次,输入
testpypi
作为 GitHub 环境的名称。您的“待定”发布者现在已准备好首次使用,并且在您首次使用它们后会自动创建您的项目。
注意
如果您没有 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,以及一个额外的作业来构建发行版包。
首先,我们将定义一个作业来构建项目的 dist 包并将其存储以供以后使用
jobs:
build:
name: Build distribution 📦
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
这会将您的存储库下载到 CI 运行程序中,然后安装并激活最新的可用 Python 3 版本。
现在我们可以从源构建 dist 并存储它们。在此示例中,我们将使用 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@v3
with:
name: python-package-distributions
path: dist/
定义工作流作业环境#
现在,让我们为将发布到 PyPI 的作业添加初始设置。这是一个将执行我们稍后定义的命令的过程。在本指南中,我们将使用 GitHub Actions 提供的最新稳定 Ubuntu LTS 版本。这也为作业定义了一个 GitHub 环境,以便在其上下文中运行,以及一个 URL,以便在 GitHub 的 UI 中很好地显示。此外,它允许获取 OpenID Connect 令牌,pypi-publish
操作需要它来实现对 PyPI 的无密钥可信发布。
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@v3
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 操作:在存储的发行版包被 download-artifact 操作下载后,它无条件地将 dist/
文件夹的内容上传到 PyPI。
对发行版包进行签名#
以下作业使用 Sigstore 对发行版包进行签名,Sigstore 是用于对 CPython 进行签名的相同工件签名系统 。
首先,它使用 sigstore/gh-action-sigstore-python GitHub 操作 对发行版包进行签名。在下一步中,使用 gh
CLI 从当前标记创建了一个空的 GitHub 版本。请注意,此步骤可以进一步自定义。请参阅 gh 版本文档 作为参考。
提示
您可能需要管理您的 GITHUB_TOKEN
权限以启用创建 GitHub 版本。请参阅 GitHub 文档 以获取说明。具体来说,令牌需要 contents: write
权限。
最后,将签名的发行版上传到 GitHub 版本。
github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and upload them to GitHub Release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--notes ""
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
注意
这是 GPG 签名的替代方案,PyPI 已 从 PyPI 中删除 了对它的支持。但是,此作业对于上传到 PyPI 并不是必需的,可以省略。
单独的工作流用于发布到 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@v3
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
- 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@v3
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@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and upload them to GitHub Release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--notes ""
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
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@v3
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 项目大小限制。可以增加限制,但更好的解决方案可能是将 PyPI 兼容服务器(如 pypiserver)用于测试目的的 CI。
注意
建议将集成的 GitHub Actions 保持在最新版本,并经常更新它们。