UNPKG

semantic-release-pypi

Version:

semantic-release plugin to publish a python package to PyPI

220 lines (172 loc) 12.7 kB
# 📦🐍 semantic-release-pypi [semantic-release](https://semantic-release.gitbook.io/semantic-release/) plugin to publish a python package to PyPI <a href="https://www.npmjs.com/package/semantic-release-pypi"> <img alt="npm latest version" src="https://img.shields.io/npm/v/semantic-release-pypi"> </a> <a href="https://github.com/abichinger/semantic-release-pypi/actions?query=workflow%3ATest+branch%3Amain"> <img alt="Build states" src="https://github.com/abichinger/semantic-release-pypi/actions/workflows/test.yml/badge.svg?branch=main"> </a> ## CI environment - [Node.js](https://semantic-release.gitbook.io/semantic-release/support/node-version) >= 18.0.0 - Python >= 3.9 ## Build System Interfaces `semantic-release-pypi` support two [build system interfaces](https://pip.pypa.io/en/stable/reference/build-system/#) - `pyproject.toml` based (Recommended) - `version` will be set inside `pyproject.toml` - [PEP 621](https://peps.python.org/pep-0621/) - The build backend can be specified inside `pyproject.toml` (defaults to `setuptools`) - [PEP 518](https://peps.python.org/pep-0518/) <br /> - `setup.py` based (Legacy interface) - `setuptools` is required, other packaging tools like Poetry or Hatch are not supported when using this interface - `version` will be set inside `setup.cfg` - `version` must **not be set** inside `setup.py` ## Steps | Step | Description |------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ```verifyConditions``` | <ul><li>verify the environment variable ```PYPI_TOKEN```</li><li>verify ```PYPI_TOKEN``` is authorized to publish on the specified repository</li><li>check if the packages `setuptools`, `wheel` and `twine` are installed</li></ul> | ```prepare``` | Update the version in `pyproject.toml` (legacy: `setup.cfg`) and create the distribution packages | ```publish``` | Publish the python package to the specified repository (default: pypi) ## Environment variables | Variable | Description | Required | Default |---------------------|------------------------------------------------------------|----------|------------------------- | ```PYPI_TOKEN``` | [API token](https://test.pypi.org/help/#apitoken) for PyPI | true | | ```PYPI_USERNAME``` | Username for PyPI | false | ```__token__``` | ```PYPI_REPO_URL``` | Repo URL for PyPI | false | See [Options](#options) ## Usage The plugin can be configured in the [**semantic-release ** configuration file](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration). Here is a minimal example: ```json { "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "semantic-release-pypi" ] } ``` Note that this plugin modifies the version inside of `pyproject.toml` (legacy: `setup.cfg`). Make sure to commit `pyproject.toml` using the `@semantic-release/git` plugin, if you want to save the changes: ```json { "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "semantic-release-pypi", [ "@semantic-release/git", { "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}", "assets": [ "pyproject.toml" ] } ] ] } ``` Working examples using Github Actions can be found here: - [semantic-release-pypi-pyproject](https://github.com/abichinger/semantic-release-pypi-pyproject) - [semantic-release-pypi-setup](https://github.com/abichinger/semantic-release-pypi-setup) ## Options | Option | Type | Default | Description | | -------------------- | --------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ```srcDir``` | str | ```.``` | source directory (defaults to current directory) | | ```distDir``` | str | ```dist``` | directory to put the source distribution archive(s) in, relative to ```srcDir``` | | ```repoUrl``` | str | ```https://upload.pypi.org/legacy/``` | The repository (package index) to upload the package to. | | ```repoUsername``` | str | ```__token__``` | The repository username. | | ```repoToken``` | str | | The repository token. It's safer to set via PYPI_TOKEN environment variable. | | ```pypiPublish``` | bool | ```true``` | Whether to publish the python package to the pypi registry. If false the package version will still be updated. | | ```gpgSign``` | bool | ```false``` | Whether to sign the package using GPG. A valid PGP key must already be installed and configured on the host. | | ```gpgIdentity``` | str | ```null``` | When ```gpgSign``` is true, set the GPG identify to use when signing files. Leave empty to use the default identity. | | ```envDir``` | string \| ```false``` | ```.venv``` | directory to create the virtual environment in, if set to `false` no environment will be created | | ```installDeps``` | bool | ```true``` | wether to automatically install python dependencies | | ```versionCmd``` | string \| array | ```undefined``` | Run a custom command to update the version (e.g. `hatch version ${version}`). `srcDir` is used as working directory. `versionCmd` is required if the version is set [dynamically](https://packaging.python.org/en/latest/specifications/pyproject-toml/#dynamic) | | ```skipIfConflict``` | bool | ```false``` | When true, a 409 Conflict response from the repository (e.g. package version already exists) is treated as success instead of a failure. Useful for immutable registries where re-running a partially failed release would otherwise fail on already-published packages. | ## Publishing with a Trusted Publisher [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) lets you publish to PyPI without a long-lived API token, using short-lived OIDC credentials issued by your CI provider instead. When neither `PYPI_TOKEN` nor `repoToken` is set, **semantic-release-pypi** automatically attempts Trusted Publishing. Supported publishers: **GitHub Actions** and **GitLab CI/CD**. ### GitHub Actions Grant the `id-token: write` permission so GitHub can issue an OIDC token, then add a [Trusted Publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) on PyPI for your repository. No token secrets are needed. ```yaml jobs: release: runs-on: ubuntu-latest environment: pypi permissions: id-token: write # required for Trusted Publishing steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: "3.x" - uses: actions/setup-node@v4 with: node-version: "lts/*" - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # No PYPI_TOKEN needed — Trusted Publishing is used automatically ``` ### GitLab CI/CD Use the [`id_tokens`](https://docs.gitlab.com/ee/ci/yaml/index.html#id_tokens) keyword to request an OIDC token with audience `pypi` (or `testpypi` for TestPyPI) and expose it as `PYPI_ID_TOKEN`. You must also configure a [Trusted Publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) on PyPI for your GitLab project. ```yaml release: stage: release image: node:lts id_tokens: PYPI_ID_TOKEN: aud: pypi # use "testpypi" when publishing to TestPyPI before_script: - apt-get install -y python3 python3-pip - pip3 install --upgrade pip script: - npx semantic-release variables: GITLAB_TOKEN: $GITLAB_TOKEN # No PYPI_TOKEN needed — PYPI_ID_TOKEN is used automatically ``` *Note: This GitLab CI/CD configuration was not tested. Contributions to verify and improve it are welcome!* ## Publishing to multiple repositories Using `release.config.js` you can read repository credentials from environment variables and publish to multiple repositories. ```js module.exports = { "plugins": [ [ "semantic-release-pypi", { "repoUrl": process.env['REPOSITORY_1_URL'], "repoUsername": process.env['REPOSITORY_1_USERNAME'], "repoToken": process.env['REPOSITORY_1_TOKEN'] } ], [ "semantic-release-pypi", { "repoUrl": process.env['REPOSITORY_2_URL'], "repoUsername": process.env['REPOSITORY_2_USERNAME'], "repoToken": process.env['REPOSITORY_2_TOKEN'] } ] ] } ``` ## Development ### Pre-requisites - pyenv >= 2.1.0 ```shell source init.sh ``` ### Contribute - Fork from this repository - Run `source init.sh` - Add your changes - Make sure your code passes all tests by running `npm run test:all` - Run `npm run lint` to ensure your code adheres to the linting rules - Issue a PR ## Alternatives [Python Semantic Release](https://github.com/python-semantic-release/python-semantic-release)