与值得信赖的出版商合作出版
在 PyPI 上配置好受信任的发布者(无论状态为“待审核”还是“正常”)后,即可通过它在关联的平台上发布内容。以下标签页介绍了每个受支持的受信任发布者的设置过程。
最简单的方法
您可以使用 PyPA 的
pypi-publish
操作来发布您的软件包。
这看起来几乎和往常一样,只是你不需要任何显式的用户名、密码或 API 令牌:GitHub 的 OIDC 身份提供程序会为你处理一切:
jobs:
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
# Specifying a GitHub environment is optional, but strongly encouraged
environment: pypi
permissions:
# IMPORTANT: this permission is mandatory for Trusted Publishing
id-token: write
steps:
# retrieve your distributions here
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
如果您要放弃基于密码或 API 令牌的身份验证流程,您的差异可能如下所示:
jobs:
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
+ # Specifying a GitHub environment is optional, but strongly encouraged
+ environment: pypi
+ permissions:
+ # IMPORTANT: this permission is mandatory for Trusted Publishing
+ id-token: write
steps:
# retrieve your distributions here
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- with:
- username: __token__
- password: ${{ secrets.PYPI_TOKEN }}
请注意id-token: write权限要求:您必须在作业级别(强烈建议)或工作流级别(不建议)授予此权限。否则,发布操作将没有足够的权限向 PyPI 进行身份验证。
笔记
强烈建议在职位级别使用权限,因为这样可以减少不必要的凭证泄露。
发布到 PyPI 以外的索引
PyPA 的pypi-publish
操作也支持与其他(非 PyPI)索引进行可信发布,前提是这些索引已启用可信发布(并且您已在这些索引上配置了可信发布者)。例如,以下是如何在
TestPyPI上使用可信发布:
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
手动方式
警告
注意!您可能不需要阅读此部分;它仅用于提供 GitHub Actions 和 PyPI 如何使用 OIDC 进行协调的一些内部细节。如果您是普通用户,强烈建议您改用 PyPA 的
pypi-publish
操作。
警告
以下描述的许多细节都与具体实现有关,既不受标准化流程的约束,也不保证兼容性。它们不属于公共接口,并且可能随时更改。要获得稳定的公共接口,您必须使用该pypi-publish操作。
使用 OIDC 发布器的流程如下:
- 从 OIDC身份提供程序检索OIDC 令牌;
- 将该令牌提交给 PyPI,PyPI 将返回一个有效期较短的 API 密钥;
- 像往常一样使用该 API 密钥(例如,使用……
twine)
以下所有代码均假定在具有相应权限的 GitHub Actions 工作流运行器中运行id-token: write。此权限
至关重要;如果没有此权限,GitHub Actions 将拒绝向您提供 OIDC 令牌。
首先,让我们从 GitHub Actions 获取 OIDC 令牌:
resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
笔记
使用 `using`audience=pypi仅对 PyPI 正确。对于 TestPyPI,正确的受众是 `<audio> testpypi`。更一般地,您可以通过以下端点访问任何实例的预期 OIDC 受众{index}/_/oidc/audience:
$ curl https://pypi.org/_/oidc/audience
{"audience":"pypi"}
响应将是一个 JSON 数据块,其中包含 OIDC 令牌。我们可以使用以下命令提取它jq:
oidc_token=$(jq '.value' <<< "${resp}")
最后,我们可以将该令牌提交给 PyPI,并获得一个有效期较短的 API 令牌:
resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
api_token=$(jq -r '.token' <<< "${resp}")
# tell GitHub Actions to mask the token in any console logs,
# to avoid leaking it
echo "::add-mask::${api_token}"
可以将这个 API 令牌提供给twine任何其他上传客户端:
TWINE_USERNAME=__token__ TWINE_PASSWORD=${api_token} twine upload dist/*
所有这些都可以整合到一个GitHub Actions工作流程中:
on:
release:
types:
- published
name: release
jobs:
pypi:
name: upload release to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: deps
run: python -m pip install -U build
- name: build
run: python -m build
- name: mint API token
id: mint-token
run: |
# retrieve the ambient OIDC token
resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
oidc_token=$(jq -r '.value' <<< "${resp}")
# exchange the OIDC token for an API token
resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
api_token=$(jq -r '.token' <<< "${resp}")
# mask the newly minted API token, so that we don't accidentally leak it
echo "::add-mask::${api_token}"
# see the next step in the workflow for an example of using this step output
echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}"
- name: publish
# gh-action-pypi-publish uses TWINE_PASSWORD automatically
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ steps.mint-token.outputs.api-token }}
您可以使用https://pypi.org/project/id/工具自动检测和生成 Google Cloud 服务上的 OIDC 凭据。
首先,请确保已在您计划发布内容的环境中安装了id和:twine
python -m pip install -U id twine
如果您不确定您的服务所使用的服务帐户的电子邮件地址,可以通过以下方式进行验证:
python -m id pypi -d | jq 'select(.email) | .email'
在环境中生成 OIDC 令牌并将其存储。受众应为 `<index>`pypi或testpypi`<index>`,具体取决于您要发布到的索引:
oidc_token=$(python -m id pypi)
笔记
pypi仅适用于 PyPI。对于 TestPyPI,正确的受众是testpypi。更一般地,您可以通过以下端点访问任何实例的预期 OIDC 受众{index}/_/oidc/audience:
$ curl https://pypi.org/_/oidc/audience
{"audience":"pypi"}
最后,我们可以将该令牌提交给 PyPI,并获得一个有效期较短的 API 令牌:
resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
api_token=$(jq -r '.token' <<< "${resp}")
笔记
这是 PyPI 的 URL。对于 TestPyPI,正确的域名应该是test.pypi.org。
可以将这个 API 令牌提供给twine任何其他上传客户端:
TWINE_USERNAME=__token__ TWINE_PASSWORD=${api_token} twine upload dist/*
ActiveState 平台是一个零配置的持续集成 (CI) 解决方案,可自动构建 PyPI 项目的跨平台 wheel 包。完成平台设置并链接 PyPI 项目后,即可发布。有关 ActiveState 入门的更多信息,请访问此处。开始使用:
将您的软件包发布到 ActiveState 的目录中。这将允许 ActiveState 平台为您构建它。
- 使用 State Tool CLI 运行以下命令:
将上方代码块中的占位符值替换为您的 ActiveState 组织名称(通常为
state publish \ --namespace private/ORGNAME \ --name PKG_NAME PKG_FILENAME \ --depend "builder/python-module-builder@>=0" \ --depend "language/python@>=3" \ --depend "language/python/setuptools@>=43.0.0" \ --depend "language/python/wheel@>=0"USERNAME-orgORGNAME)、软件包名称(PKG_NAME)以及 sdist 或源代码压缩包的文件名(PKG_FILENAME),然后运行命令。记下输出中的时间戳。
笔记
命名空间必须以“.”开头,private/后跟您的组织名称。您还可以根据需要添加其他“文件夹”名称。
- 将软件包发布到 ActiveState 后,您需要创建一个构建脚本文件(
buildscript.as),将其构建成 wheel 包并发布到 PyPI。以下是一个示例脚本。在与您的软件包activestate.yaml文件相同的文件夹中创建一个新的构建脚本文件,并将其命名为 `buildscript.asbuild.js`。粘贴以下代码,并将占位符值替换为您项目中的值:您刚刚发布的软件包的时间戳(`PUBLISHED_TIMESTAMP`)、命名空间名称(即private/USERNAME-org您发布软件包的文件夹,类似于 `<include>/ ...at_time = "PUBLISHED_TIMESTAMP" publish_receipt = pypi_publisher( attempt=1, audience="testpypi", pypi_uri="test.pypi.org", src=wheels ) runtime = state_tool_artifacts(build_flags=[], src=sources) sources = solve( at_time=at_time, platforms=["7c998ec2-7491-4e75-be4d-8885800ef5f2"], requirements=[ Req(namespace="language", name="python", version=Eq(value="3.10.13")), Req(namespace="NAMESPACE", name="PKG_NAME", version=Eq(value="VERSION")), ], solver_version=null, ) wheel_srcs = select_ingredient(namespace="NAMESPACE", name="PKG_NAME", src=sources) wheels = make_wheel(at_time=at_time, python_version="3.10.13", src=wheel_srcs) main = runtime - 然后,在终端中运行命令,将此构建脚本“提交”到系统中
state commit。现在,您就可以发布到 PyPI 了! - 要将你的 wheel 文件发布到 PyPI,请运行:
state eval publish_receipt。就这么简单!
您已成功使用 ActiveState 平台发布了 Python wheel 文件。
笔记
构建脚本技巧:
您可以将所有字段留空pypi_uri,audience直接发布到 PyPI 主仓库。
如果遇到网络超时或其他瞬态错误,可以增加attempt参数值进行重试。
接下来的字符串platforms = [是您要为其构建 wheel 包的受支持平台的 UUID。所有受支持平台的列表可
在此处找到。请从提供的列表中选择所有适用于您项目的平台。
笔记
如果您想在发布前测试您的轮盘,请在运行以下命令之前执行以下步骤state eval publish_receipt:1. 要单独构建您的轮盘,请运行命令state eval wheels
。2. 构建轮盘后,运行命令state builds --all查看所有可用的构建版本。记下HASH_ID您新轮盘的版本号。3. 运行命令state builds dl <HASH_ID>下载并测试您构建的轮盘。
这是一个 GitLab 工作流示例,它使用可信发布功能构建软件包并将其发布到 PyPI。与普通工作流的主要区别在于部署步骤(publish-job):
- 该关键字
id_tokens用于通过名称PYPI_ID_TOKEN和受众 从 GitLab 请求 OIDC 令牌pypi。 - Twine 被调用来上传软件包,但未指定令牌。它将 OIDC 令牌发送给 PyPI 以换取 PyPI API 令牌,然后使用该令牌发布软件包
twine。
build-job:
stage: build
image: python:3-bookworm
script:
- python -m pip install -U build
- cd python_pkg && python -m build
artifacts:
paths:
- "python_pkg/dist/"
publish-job:
stage: deploy
image: python:3-bookworm
dependencies:
- build-job
id_tokens:
PYPI_ID_TOKEN:
# Use "testpypi" if uploading to TestPyPI
aud: pypi
script:
# Install dependencies
- python -m pip install -U twine
# Upload to PyPI, add "--repository testpypi" if uploading to TestPyPI
# With no token specified, twine will use Trusted Publishing
- twine upload python_pkg/dist/*