From c161e9c286016efa0807830625775f436f796919 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Mon, 24 Jul 2023 14:58:06 +0100 Subject: [PATCH] Publish releases to PyPI Implement the PyPI trusted publisher OIDC flow within GitHub Actions. --- .github/workflows/create-release.yml | 77 ++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 12ac825fb71..cc4756c632c 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -4,20 +4,87 @@ on: push: tags: - "v*.*.*" + workflow_dispatch: permissions: contents: read jobs: - create-release: + publish-pypi: + runs-on: ubuntu-latest + name: PyPI Release permissions: - contents: write # for softprops/action-gh-release to create GitHub release + id-token: write # for PyPI trusted publishing + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3 + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install build dependencies (pypa/build, twine) + run: | + pip install -U pip + pip install build + + - name: Build distribution + run: python -m build + + - name: Mint PyPI API token + id: mint-token + uses: actions/github-script@v6 + with: + script: | + // retrieve the ambient OIDC token + const oidc_token = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN; + const oidc_url = process.env.ACTIONS_ID_TOKEN_REQUEST_URL; + const resp = await fetch(`${oidc_url}&audience=testpypi`, { + headers: {Authorization: `bearer ${oidc_token}`}, + } + ); + const oidc_token = (await response.json()).value; + + // exchange the OIDC token for an API token + const resp = await fetch('https://pypi.org/_/oidc/github/mint-token', { + method: 'post', + body: '{"token": "oidc_token"}' , + headers: {'Content-Type': 'application/json'}, + } + ); + const api_token = (await response.json()).token; + + // mask the newly minted API token, so that we don't accidentally leak it + core.setSecret(api_token) + core.setOutput('api-token', api_token) + + - name: Upload to Test PyPI + env: + TWINE_NON_INTERACTIVE: "true" + TWINE_USERNAME: "__token__" + TWINE_PASSWORD: "${{ steps.mint-token.outputs.api-token }}" + TWINE_REPOSITORY_URL: "https://test.pypi.org/legacy/" + run: | + twine check dist/* + twine upload dist/* + + github-release: runs-on: ubuntu-latest + name: GitHub release + permissions: + contents: write # for softprops/action-gh-release to create GitHub release steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Release + - uses: actions/checkout@v3 + - name: Get release version + id: get_version + uses: actions/github-script@v6 + with: + script: core.setOutput('version', context.ref.replace("refs/tags/v", "")) + + - name: Create GitHub release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: + name: "Sphinx ${{ steps.get_version.outputs.version }}" body: "Changelog: https://www.sphinx-doc.org/en/master/changes.html"