https://github.com/alcrene/publish-to-pypi
Short guidelines to get a Python project to PyPI as painlessly as possible
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (15.8%) to scientific vocabulary
Repository
Short guidelines to get a Python project to PyPI as painlessly as possible
Basic Info
- Host: GitHub
- Owner: alcrene
- License: other
- Default Branch: main
- Size: 61.5 KB
Statistics
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
- Releases: 0
Metadata Files
README.md
GitHub to PyPI in 30 minutes
The PyPI is a godsend for Python users, placing most packages they need an easy pip install away.
The process of putting code on PyPI however has long been much more convoluted.
The good news is that since cerca 2020–2022, we have seen the result of massive standardization efforts on the Python packaging front.[^standardization] The consequence is that for simple projects, it is now possible to devise an almost completely portable procedure that will work for all of them. The less good news is that with packaging tools still in a bit of flux, a lot of the information you find on the web may seem contradictory, and most of it makes the process more complicated than it now needs to be.
This little resource is my attempt at collating current recommendations, as of August 2023, into the simplest possible path to PyPI publication. It provides workflow files & guidelines that I can reuse across multiple projects. My hope is that they will also be useful to you, either as⁻is or as a starting point for your own packaging adventure.
This is not a tutorial on publishing to PyPI. That would be beside the point, since it would make this short document very much not short. Also, there are already multiple tutorials available, which are more likely to stay up to date than whatever I write here. My goal here is not for completeness, but maximum concision. If this is your first time using these tools, you will need to look up how they work separately.
On the other hand, if you just want to get your package on PyPI as quickly as possible and with minimum fuss, this is the resource for you. Just copy a few files into your repo, add the project to PyPI, and you should be good to go.
[^standardization]: Those results of course are the outcome of mostly unnoticed work begun many years prior to 2020. Have a kind thought for all the people who have the vision and dedication to keep improving the Python ecosystem!
Assumptions & tool choices
There are different ways to get a package on PyPI. This guidebook assumes or uses the following:
- PyPA’s
build- This means that only
pyproject.tomlprojects are supported.
- This means that only
- Version numbers are automatically determined by
setuptools-scm, with SemVer tag names.- In particular, this means the
pyproject.tomlshould not specify the version explicitely, but rather contain a line likedynamic = ["version"].
- In particular, this means the
- Trusted publishing to avoid the need for API tokens.
- GitHub, because it is currently the only Trusted Publisher.
- GitHub Actions
- Once properly configured, this allows publishing literally at the click of a button. (In contrast to e.g.
twine, which requires issuing commands you probably forgot since the last time.)
- Once properly configured, this allows publishing literally at the click of a button. (In contrast to e.g.
- Separate GitHub workflows for building and publishing.
- pypi-publish action
- upload-artifact & download-artifact in order to separate build and deployment into separate workflows.
Step-by-step instructions for publishing
Create accounts
(Skip if you already have accounts on these services.)
Create a GitHub Private Access Token
This is needed for the splitting publication into separate "build" and "publish" workflows. The access token is used to allow the publish workflow to read the artifacts from the "build" workflow. See download-artifact docs and issue discussion.
GitHub docs:
What to do:
- User icon -> Settings -> Developer settings -> Personal access tokens -> Fine-grained tokens
- Generate token
- Fill
- Token name: Publish to PyPI - [repository name]
- Expiration: 1 year
- Description: Allow "publish" GitHub actions to read the artifacts created by "build" actions.
- Resource owner: You
- Repository access: Only select repositories -> Select [repository]
- Permissions
- Repository permissions
- Actions: Read-only
- Repository permissions
- Keep the generated token at hand: you will use it twice below (once for each release environment)
Repo configuration
Do this with the GitHub web UI. (These are my personal preferences; adapt as desired.)
- Settings
- General
- Default branch
- main
- Default branch
- Branches
- Add a rule for branch
main- Allow force push
- Only admin
- Allow force push
- Add a rule for branch
- Rules
- New tag ruleset
- Name: "Protected
v*tags" - Bypass list: Repository admin
- Target criterion (inclusion):
v* - Rules:
- Restrict creations
- Restrict deletions
- Block force pushes
- Name: "Protected
- New tag ruleset
- Environments
- Create 2 environments
release- Wait timer: 15 minutes[^why-timer]
- Deployment branches and tags
- Add environment secret:
- Wait timer: 15 minutes[^why-timer]
release-testpypi- Add environment secret:
- Add environment secret:
- Create 2 environments
- General
[^why-timer]: Once a package is published to PyPI, it cannot be replaced except by publishing a new version with a newer version number. The timer is intended to give you time to realize a mistake and cancel the release action before it runs.
Add GitHub Actions
Copy the three GitHub workflow files from this repo to your project repo under
.github/workflows/There are different ways to do this. A simple way is to just download the files from this repo (either by cloning or downloading the zip). A more sophisticated way is to clone the repo with git-subrepo:
mkdir .github git subrepo clone https://github.com/alcrene/publish-to-pypi .github/workflows
The main advantages of using
git-subrepois that it allows you to update the workflows withgit subrepo pull. You can alsogit subrepo pushchanges, if you use your own repository.Edit
.github/workflows/build.ymlas needed.- In particular, check that the Python version is appropriate.
Description of the workflows
build.yml:- Will build the distribution files (runs
python3 -m build) - Will run:
- when a new release is created with the GitHub UI;
- when triggered manually from the Actions menu.
- Will build the distribution files (runs
publish-on-pypi.yml:- Will place the package on the official PyPI index
- Permanent: Once a package is published with PyPI with a version number, no new packages can be published with the same number.
- Runs within the environment
release. - Will run:
- when triggered manually from the Actions menu.
publish-on-testpypi.yml:- Will place the package on the test PyPI index
- The test index can be used
- to make sure the packaging pipeline works as it should;
- to share pre-release with others to allow them to test them.
- Packages on test PyPI are installed with
pip install -i https://test.pypi.org/simple/ <pkg name>
- Packages on test PyPI are installed with
- Runs within the environment
release-testpypi. - Will run:
- when triggered manually from the Actions menu
Prepare PyPI/TestPyPI
Actions are configured to use Trusted publishing. This avoids the need to generate and store API tokens in the job file, and thus enables (almost) completely generic jobs. What we need to do however is tell PyPI / TestPyPI to expect our new package
From your PyPI user page:
- Publication
- Add a new pending publisher
- Project name: must match the
namefield inpyproject.toml - Workflow name:
publish-on-pypi.yml - Environment name:
release
- Project name: must match the
- Add a new pending publisher
If you intend to use TestPyPI, you need to repeat the procedure there
- Workflow name:
publish-on-testpypi.yml - Environment name:
release-testpypi
Publish a release candidate to TestPyPI
When you are ready to publish a new release, do the following: (The procedure is the same for the first or subsequent releases.)
Tag the latest commit with an RC version number such as
v0.1.0-rc.1- Make sure to use a SemVer so that build tools recognize the version number.
- The GitHub actions will always build and publish the latest commit with a SemVer tag.
- Versions prefixed with
vis the most common standard, and some tools may expect it. - Separating numbers – like
-rc.1– ensures that they order properly with versions like-rc.11. setuptools_scmwill parse the version from the tag name and produce a nice number for the package:0.1.0-rc1.
Push the tag to GitHub
Make a new release with the GitHub UI. This will automatically trigger the
build.ymlworkflow.- Alternatively, if you are not ready to mint a release, you can trigger the
build.ymlaction manually.
- Alternatively, if you are not ready to mint a release, you can trigger the
Inspect the build output (Actions -> Build Python package -> [latest run]) If the build looks OK, proceed to publish on Test PyPI:
- Copy the RUN ID of the build workflow (the page you are on right now).
It will be in the URL as
https://github.com/.../actions/runs/[RUN ID] - Open Settings -> Environments ->
release-testpyi.
Add (or update) the variableBUILD_RUN_IDwith the value you just copied. - Optionally do the same for the
releaseenvironment.
- Copy the RUN ID of the build workflow (the page you are on right now).
It will be in the URL as
Manually trigger the
Publish release on TestPyPIworkflow.- Make sure the
BUILD_RUN_IDenvironment variable points to the latest build.
- Make sure the
Test as needed.
Publish a new official version on PyPI
Tag the latest commit with plain version number such as
v0.1.0- Make sure to use a SemVer so that build tools recognize the version number.
- This must be the latest commit with a SemVer tag.
- This may be the same commit as the latest RC version (and probably should), so the commit will have both an RC version tag and a non-RC version tag.
- Versions prefixed with
vis the most common standard, and some tools may expect it.
Push the tag to GitHub
Make a new release with the GitHub UI. This will automatically trigger the
build.ymlworkflow.Inspect the build output (Actions -> Build Python package -> [latest run]) If the build looks OK, proceed to publish on PyPI:
- Copy the RUN ID of the build workflow (the page you are on right now).
It will be in the URL as
https://github.com/.../actions/runs/[RUN ID] - Open Settings -> Environments ->
release.
Add (or update) the variableBUILD_RUN_IDwith the value you just copied.
- Copy the RUN ID of the build workflow (the page you are on right now).
It will be in the URL as
Manually trigger the
Publish release on PyPIworkflow.- Make sure the
BUILD_RUN_IDenvironment variable points to the latest build. - This will wait 15 minutes before starting. If during this window you realize you forgot something, you may cancel the action from the Action menu.
- Make sure the
Your package is now on PyPI !
Attribution
The GitHub actions are derived from examples from the PyPI docs (Apache-2.0) and the PyPI publish GitHub Action (BSD-3).
Owner
- Name: Alexandre René
- Login: alcrene
- Kind: user
- Repositories: 3
- Profile: https://github.com/alcrene
GitHub Events
Total
- Push event: 1
Last Year
- Push event: 1
Issues and Pull Requests
Last synced: over 1 year ago
All Time
- Total issues: 0
- Total pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Total issue authors: 0
- Total pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0