This is a composite GitHub Action for building a pure-Python package and publishing it to the Python Package Index (PyPI).
This actions rolls into a single step two tasks that are commonly run together:
- Build a Python package's distribution, accomplished with the PyPA's build tool.
- Upload the distribution to PyPI, using the pypa/gh-action-pypi-publish action, using PyPI's trusted publishers mechanism. New in v2: using trusted publishers is required. Use v1 for the legacy account token authentication method.
Note Since this action uses the build tool, any PEP 517-compatible packaging backend is supported, including setuptools. You can make your project PEP 517-compatible by adding a
project.toml
file and specifying the build backend in a[build-system]
section (see setuptools' tutorial on this).However, building wheels for multiple platforms is not supported by this action. If your package compiles extensions, you'll need to build your own multi-platform GitHub Actions job that builds a wheel on each platform.
There are three steps for implementing this action: set up a trusted publisher on PyPI, set up the GitHub Actions environment, and add this action to your GitHub Actions workflow.
This action uses PyPI's trusted publisher mechanism.
Start by following the PyPI documentation to set up a trusted publisher for your PyPI project.
Note in particular the name of the environment and the workflow file.
In the example below, the environment is pypi
.
The workflow file should match where the YAML workflow file name in your repository.
In your repository's settings, create a GitHub Actions environment. The name of this environment must match the name your specified to PyPI.
If desired, enable "required reviewers" so that only trusted members or teams can publish to PyPI.
Note If you are considering adding a branch restriction, be aware that the PyPI deployment (as recommended in the workflow below) is done in the context of a tag ref, not a branch like
main
. Branch restrictions will not work in this case.
Next, modify the GitHub Actions workflow file in your repository to include this action:
name: Python CI
"on":
push: {}
pull_request: {}
release:
types: [published]
jobs:
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<your-pypi-project-name>
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # full history for setuptools_scm
- name: Build and publish
uses: lsst-sqre/build-and-publish-to-pypi@v2
with:
python-version: "3.11"
Note
- This workflow file must match the name of the workflow file you specified in PyPI as the trusted publisher workflow file.
- Remember to set the
url
in theenvironment
section to your PyPI project's URL.- Ensure the environment's
name
matches the name of the environment you specified in PyPI.- We recommend using GitHub Releases to trigger a PyPI release. To do this, include the
release
event with a type ofpublished
in the workflow'son
section. In the job'sif
section, check that the event is a release publication. See Dry-run mode for package validation below for how to run this action in a general Pull Request workflow without publishing to PyPI.
python-version
(string, required) the Python version.upload
(boolean, optional) a flag to enable PyPI uploads. Default istrue
. Iffalse
, the action skips the upload to PyPI, but also runs additional pre-flight validation withtwine check
.working-directory
(string, optional) the directory containing the package to build and publish. Default is.
.
No outputs.
Typically, you'll want to run the PyPI upload after doing any tests.
In that case, add a needs
field listing those other jobs:
jobs:
lint:
{}
# ...
test:
{}
# ...
docs:
{}
# ...
pypi-publish:
runs-on: ubuntu-latest
needs: [lint, test, docs]
You only want to publish to PyPI in a release event, which is typically for GitHub Release publication or tag pushes.
You can still run this action in a general Pull Request workflow, however, to test and validate the package build without uploading to PyPI.
See how the upload
parameter can be toggled off for non-release events:
name: Python CI
"on":
push: {}
pull_request: {}
release:
types: [published]
jobs:
test-package:
name: Test PyPI package build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # full history for setuptools_scm
- name: Build and publish
uses: lsst-sqre/build-and-publish-to-pypi@v2
with:
python-version: "3.11"
upload: "false"
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<your-pypi-project-name>
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # full history for setuptools_scm
- name: Build and publish
uses: lsst-sqre/build-and-publish-to-pypi@v2
with:
python-version: "3.11"
The test-package
job will run on all events, but the pypi-publish
job will only run on release events.
Since test-package
doesn't perform an upload to PyPI, it should not include the pypi
environment.
Instead of using GitHub Releases, you can also trigger a PyPI release on a tag push.
This changes the on
section of the workflow, as well as the conditional for the pypi-publish
job:
name: Python CI
"on":
push:
tags:
- "*"
pull_request: {}
jobs:
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<your-pypi-project-name>
permissions:
id-token: write
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # full history for setuptools_scm
- name: Build and publish
uses: lsst-sqre/build-and-publish-to-pypi@v2
with:
python-version: "3.11"
This repository provides a composite GitHub Action, a type of action that packages multiple regular actions into a single step. We do this to make the GitHub Actions of all our software projects more consistent and easier to maintain. You can learn more about composite actions in the GitHub documentation.
Create new releases using the GitHub Releases UI and assign a tag with a semantic version, including a v
prefix. Choose the semantic version based on compatibility for users of this workflow. If backwards compatibility is broken, bump the major version.
When a release is made, a new major version tag (i.e. v1
, v2
) is also made or moved using nowactions/update-majorver.
We generally expect that most users will track these major version tags.