pypi-publish
ActionsTags
(2)Verified
This action allows you to upload yourPython distribution packagesin thedist/ directory to PyPI.This text suggests a minimalistic usage overview. For more detailedwalkthrough check out thePyPA guide.
If you have any feedback regarding specific action versions, please leavecomments in the correspondingper-release announcement discussions.
Tip
A limited number of usage scenarios is supported, including thePyPA guide example. See thenon-goals for more detail.
Themaster branch version has been sunset. Please, change the GitHubAction version you use frommaster torelease/v1 or use an exacttag, or opt-in touse a full Git commit SHA and Dependabot.
Note
Trusted publishing cannot be used from within a reusable workflow at thistime. It is recommended to instead create a non-reusable workflow that contains ajob calling your reusable workflow, and then do the trusted publishing step froma separate job within that non-reusable workflow. Alternatively, you can stilluse a username/token inside the reusable workflow.
Note
Trusted publishing is sometimes referred to by itsunderlying technology -- OpenID Connect, or OIDC for short.If you see references to "OIDC publishing" in the context of PyPI,this is what they're referring to.
This example jumps right into the current best practice. If you want touse API tokens directly or a less secure username and password, check outhow to specify username and password.
This action supports PyPI'strusted publishingimplementation, which allows authentication to PyPI without a manuallyconfigured API token or username/password combination. To performtrusted publishing with this action, your project'spublisher must already beconfigured on PyPI.
To enter the trusted publishing flow, configure this action's job with theid-token: write permission andwithout an explicit username or password:
# .github/workflows/ci-cd.ymljobs:pypi-publish:name:Upload release to PyPIruns-on:ubuntu-latestenvironment:name:pypiurl:https://pypi.org/p/<your-pypi-project-name>permissions:id-token:write# IMPORTANT: this permission is mandatory for trusted publishingsteps:# retrieve your distributions here -name:Publish package distributions to PyPIuses:pypa/gh-action-pypi-publish@release/v1
Note
Pro tip: instead of using branch pointers, likeunstable/v1, pin versions ofActions that you use to tagged versions or sha1 commit identifiers.This will make your workflows more secure and better reproducible, saving youfrom sudden and unpleasant surprises.
Other indices that support trusted publishing can also be used, like TestPyPI:
-name:Publish package distributions to TestPyPIuses:pypa/gh-action-pypi-publish@release/v1with:repository-url:https://test.pypi.org/legacy/
(don't forget to update the environment name totestpypi or similar!)
Note
Pro tip: only set theid-token: write permission in the job that doespublishing, not globally. Also, try to separate building from publishing— this makes sure that any scripts maliciously injected into the buildor test environment won't be able to elevate privileges while flying underthe radar.
A common use case is to upload packages only on a tagged commit, to do so add afilter to the job:
if:github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
Important
Support for generating and uploadingdigital attestations is currentlylimited to Trusted Publishing flows using PyPI or TestPyPI.
Note
Generating and uploading digital attestations currently requiresauthentication with atrusted publisher.
Generating signeddigital attestations for all the distribution filesand uploading them all together is now on by default for all projectsusing Trusted Publishing. To disable it, setattestations as follows:
with:attestations:false
The attestation objects are created usingSigstore for eachdistribution package, signing them with the identity providedby the GitHub's OIDC token associated with the current workflow. This meansboth the trusted publishing authentication and the attestations are tied to thesame identity.
This GitHub Actionhas nothing to do withbuilding packagedistributions. Users are responsible for preparing dists for uploadby putting them into thedist/ folder prior to running this Action.They are typically expected to do this in aseparate GitHub ActionsCI/CD job running before the one where they call this action and havingrestricted privileges.
Important
Since this GitHub Action is docker-based, it can onlybe used from within GNU/Linux based jobs in GitHub Actions CI/CDworkflows. This is by design and is unlikely to change due to a numberof considerations we rely on.
This should not stop one from publishing platform-specificdistribution packages, though. It is strongly advised to separate jobsfor building the OS-specific wheels from the publish job. This allowsone to (1) test exactly the same artifacts that are about to beuploaded to PyPI, (2) prevent parallel unsynchronized jobs frompublishing only part of the dists asynchronously (in case when part ofthe jobs fail and others succeed ending up with an incomplete releaseon PyPI) and (3) make an atomic upload to PyPI (when part of the distsappear on PyPI, installers like pip will use that version for thedependency resolution but this may cause some environments to usesdists while the wheel for their runtime is not yet available).
To implement this sort of orchestration, please useactions/upload-artifact andactions/download-artifact actions forsharing the built dists across stages and jobs. Then, use theneedssetting to order the build, test and publish stages.
The expected environment for runningpypi-publish is theGitHub-provided Ubuntu VM. We are running a smoke-test againstubuntu-latest in CI but any currently available numbered versionsshould do. We'll consider them supported for as long as GitHub itselfsupports them.
Running the action in a job that has acontainer: set is notsupported. It might work for you but you're on your own when it breaks.If you feel the need to use it, it's likely that you're not followingthe recommendation of invoking the build automation in a separate job,which is considered a security issue (especially, when usingTrustedPublishing that may cause privilege escalation andwould enable the attackers to impersonate the GitHub-backed identity ofthe repository through transitive build dependency poisoning). Thesolution is to have one job (or multiple, in case of projects withC-extensions) for building the distribution packages, followed byanother that publishes them.
Self-hosted runners are best effort, provided no other unsupportedthings influence them. We are unable to test this in CI and they maybreak. This is often the case when using custom runtimes and not theofficial GitHub-provided VMs. In general, if you follow therecommendation of building in a separate job, you shouldn't need to runthis action within a self-hosted runner — it should be possible tobuild your dists in a self-hosted runner, save them as a GitHub Actionsartifact in that job, and then invoke the publishing job that would runwithin GitHub-provided runners, downloading the artifact with the distsand publishing them. Such separation is therecommended/supportedway of handling this scenario.Our understandng is that Trusted publishing is expected to work onself-hosted runners. It is backed by OIDC. If it doesn't work, youshould probably ask GitHub if you missed something. We wouldn't be ableto assist here.
Trusted Publishing cannot be tested in CI at the moment, sadly. It issupported and bugs should be reported but it may take time to sort outas it often requires cross-project collaboration to debug (sometimes,problems occur due to changes in PyPI and not in the action).
The only case that is explicitly unsupported at the moment isTrustedPublishing in reusable workflows. This requiressupport on the PyPI side and is being worked on. Please, do not reportbugs related to this case. The current recommendation is to puteverything else you want into a reusable workflow but keep the jobcallingpypi-publish in a top-level one.
Invokingpypi-publish from composite actions is unsupported. It is nottested. GitHub Runners have limitations and bugs in this case. But moreimportantly, this is usually an indication of using it insecurely. WhenusingTrusted Publishing, it is imperative to keepbuild machinery invocation in a separate job with restrictive privilegesasTrusted Publishing itself requires elevatedpermissions to make use of OIDC. Our observation is that the userssometimes create in-project composite actions that invoke building andpublishing in the same job. As such, we don't seek to support such adangerous configuration in the first place. The solution is pretty muchthe same as with the previous problem — use a separate job withdedicated and scoped privileges just for publishing; and invoke thatin-project composite action from a different job.
And finally, invokingpypi-publish more than once in the same job isnot considered supported. It may work in a limited number of scenariosbut please, don't do this. If you want to publish to several indexes,build the dists in one job and add several publishing jobs, one perupload.
For best results, figure out what kind of workflow fits yourproject's specific needs.
For example, you could implement a parallel job thatpushes every commit to TestPyPI or your own index server,likedevpi. For this, you'd need to (1) specify a customrepository-url value and (2) generate a unique versionnumber for each upload so that they'd not create a conflict.The latter is possible if you usesetuptools_scm package butyou could also invent your own solution based on the distanceto the latest tagged commit.
You'll need to create another token for a separate host and thensave it as aGitHub repo secret under an environment used inyour job. Though, passing a password would disable the secretlesstrustedpublishing so it's better to configure it instead, when publishing to TestPyPIand not something custom.
The action invocation in this case would look like:
-name:Publish package to TestPyPIuses:pypa/gh-action-pypi-publish@release/v1with:password:${{ secrets.TEST_PYPI_API_TOKEN }}repository-url:https://test.pypi.org/legacy/
You can change the default target directory ofdist/to any directory of your liking. The action invocationwould now look like:
-name:Publish package to PyPIuses:pypa/gh-action-pypi-publish@release/v1with:packages-dir:custom-dir/
It is recommended that you runtwine check just after producing your files,but this also runstwine check before upload. You can also disable the twinecheck with:
with:verify-metadata:false
Sometimes, when you publish releases from multiple places, your workflowmay hit race conditions. For example, when publishing from multiple CIsor even having workflows with the same steps triggered within GitHubActions CI/CD for different events concerning the same high-level act.
To facilitate this use-case, you may useskip-existing (disabled bydefault) setting as follows:
with:skip-existing:true
Note
Pro tip: try to avoid enabling this setting where possible. If youhave steps for publishing to both PyPI and TestPyPI, consider only usingit for the latter, having the former fail loudly on duplicates.
Sometimes,twine upload can fail and to debug use theverbose setting as follows:
with:verbose:true
You may want to verify whether the files on PyPI were automatically uploaded by CI script.It will show SHA256, MD5, BLAKE2-256 values of files to be uploaded.
with:print-hash:true
The default username value is__token__. If you publish to a customregistry that does not provide API tokens, likedevpi, you may need tospecify a custom username and password pair. This is how it's done.
with:user:guidopassword:${{ secrets.DEVPI_PASSWORD }}
The secret used in${{ secrets.DEVPI_PASSWORD }} needs to be created on theenvironment page under the settings of your project on GitHub.SeeCreating & using secrets.
In the past, when publishing to PyPI, the most secure way of the access scopingfor automatic publishing was to use theAPI tokens feature ofPyPI. One would make it project-scoped and save as an environment-bound secretin their GitHub repository settings, naming it${{ secrets.PYPI_API_TOKEN }},for example. SeeCreating & using secrets. While still secure,trusted publishing is now encouraged over API tokens as a best practiceon supported platforms (like GitHub).
The Dockerfile and associated scripts and documentation in this projectare released under theBSD 3-clause license.
Resources
pypi-publish is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.
Verified
Tags
(2)Resources
pypi-publish is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.