Skip to content

与值得信赖的出版商合作出版

在 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 发布器的流程如下:

  1. 从 OIDC身份提供程序检索OIDC 令牌;
  2. 将该令牌提交给 PyPI,PyPI 将返回一个有效期较短的 API 密钥;
  3. 像往常一样使用该 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 平台为您构建它。

  1. 使用 State Tool CLI 运行以下命令:
    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"
    
    将上方代码块中的占位符值替换为您的 ActiveState 组织名称(通常为USERNAME-orgORGNAME)、软件包名称(PKG_NAME)以及 sdist 或源代码压缩包的文件名(PKG_FILENAME),然后运行命令。记下输出中的时间戳。

笔记

命名空间必须以“.”开头,private/后跟您的组织名称。您还可以根据需要添加其他“文件夹”名称。

  1. 将软件包发布到 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
    
  2. 然后,在终端中运行命令,将此构建脚本“提交”到系统中state commit 。现在,您就可以发布到 PyPI 了!
  3. 要将你的 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/*