Skip to content

Opinionated GitHub action to fully automate publishing packages to PyPI - using Poetry and GitHub releases

License

Notifications You must be signed in to change notification settings

code-specialist/pypi-poetry-publish

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 

Repository files navigation

pypi-poetry-publish

Opinionated GitHub action to fully automate publishing packages to PyPI - using Poetry and GitHub releases.

We published this action because we use it in our projects and thought it would be useful to others as well. This action is open to any kind of collaboration and contribution - We're happy to receive feedback, issues, pull requests or just kudos.

Features

  • Automatically publish your package to PyPI when you create a new release on GitHub
  • Publish to the public PyPI, the TestPyPI, or a custom PyPI registry
  • Runs on private GitHub Actions runners
  • Support for private PyPI repository dependencies
  • Configurable Python version, Poetry, and Poetry Core version for specific needs
  • Configurable branch e.g. main, master, beta, etc.

Jump to example: - Publish to public PyPI - With private dependencies - To a private PyPI - With private dependencies

Prerequisites

This action assumes you use poetry as your package manager and have the pyproject.toml and poetry.lock files in the root directory of your repository.

my_project/
├─ example_package/
│  ├─ __init__.py
├─ pyproject.toml
├─ poetry.lock

ℹ️ If you do not use a custom runner, you may use the builtin functionality GITHUB_TOKEN with write permissions as the ACCESS_TOKEN as seen in the first example. See [https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow](https://docs.github. com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow)

⚠️ We recommend you to use this workflow with the test PyPI registry e.g. PUBLISH_REGISTRY: "https://test.pypi.org/simple/" until you can confirm your workflow works as expected.

Minimal example

name: Build and publish python package

on:
  release:
    types: [ published ]

jobs:
  publish-service-client-package:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Publish PyPi package
        uses: code-specialist/pypi-poetry-publish@v1
        with:
          ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PYPI_TOKEN }}

Process

  1. You create a new Release and Tag e.g. 1.0.0 (Trigger)
  2. The action will:
    1. build your package
    2. adjust the version in the pyproject.toml and __init__.py in the package directory according to the tag
    3. publish the package to a PyPI registry

Example process

Before the release

./pyproject.toml

[tool.poetry]
name = "code-specialist-example-package"
version = "0.1.0"
description = "Example package"
authors = ["Code Specialist"]
packages = [{include = "example_package"}]


[tool.poetry.dependencies]
python = "^3.10"

./example_package/init.py

__version__ = "0.1.0"

After GitHub Release 1.0.0

The action will alter the content of your repository to avoid conflicts and version mismatches:

pyproject.toml

[tool.poetry]
name = "code-specialist-example-package"
version = "1.0.0" # adjusted to 1.0.0
description = "Example package"
authors = ["Code Specialist"]
packages = [{include = "example_package"}]


[tool.poetry.dependencies]
python = "^3.10"

./example_package/init.py

__version__ = "1.0.0"  # adjusted to 1.0.0

Inputs

The inputs marked with (✓) are required if the POETRY_DEPENDENCY_REGISTRY_URL is set.

Name Description Mandatory Default
ACCESS_TOKEN GitHub token with write access to the repository, to adjust the version
PUBLISH_REGISTRY_PASSWORD Either a password for the registry user or a token in combination with __token__ as the PUBLISH_REGISTRY_USERNAME
PUBLISH_REGISTRY_USERNAME The username for the pypi registry __token__
PACKAGE_DIRECTORY The directory the package is located in e.g. ./src/, ./example_package './'
POETRY_VERSION The Poetry version to perform the build with 1.1.8
POETRY_CORE_VERSION The Poetry Code version to perform the build with 1.0.4
PYTHON_VERSION The Python version to perform the build with 3.10
BRANCH The branch to publish from master
PUBLISH_REGISTRY The registry to publish to e.g.https://test.pypi.org/simple/ https://pypi.org/simple/
POETRY_DEPENDENCY_REGISTRY_URL Allows to define a custom registry to be used by Poetry for dependency installation e.g. https://pypi.code-specialist.com/simple/
POETRY_DEPENDENCY_REGISTRY_NAME The name used for the custom registry in the dependencies. Must match the name in the pyproject.toml (✓)
POETRY_DEPENDENCY_REGISTRY_USERNAME The username for the custom registry (✓)
POETRY_DEPENDENCY_REGISTRY_PASSWORD The password for the custom registry (✓)
POETRY_CUSTOM_REGISTRY_AUTH The authentication type for the custom registry http-basic

Examples

Each example requires you to:

  1. Create a workflow file e.g. .github/workflows/publish.yaml
  2. Create a new release and tag e.g. 1.0.0 and the action will be triggered and publishes your package

ℹ️ If there is a use case you would like to see, please open an issue or a pull request.

Publish to Public PyPI

  • Requires GitHub secrets:
    • PUBLISH_REGISTRY_PASSWORD with a valid token

In order to use the GITHUB_TOKEN you also have to provide permissions for the token. In this case you require the contents:write permission.

publish.yaml

name: Build and publish python package

on:
  release:
    types: [ published ]

jobs:
  publish-service-client-package:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Publish PyPi package
        uses: code-specialist/pypi-poetry-publish@v1
        with:
          PACKAGE_DIRECTORY: "./example-package/"
          PYTHON_VERSION: "3.10"
          ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PUBLISH_REGISTRY_PASSWORD }}

Publish Test PyPI

  • Requires GitHub secrets:
    • PUBLISH_REGISTRY_PASSWORD with a valid token
    • ACCESS_TOKEN access token with write access to the GitHub repository

publish.yaml

name: Build and publish python package

on:
  release:
    types: [ published ]

jobs:
  publish-service-client-package:
    runs-on: ubuntu-latest
    steps:
      - name: Publish PyPi package
        uses: code-specialist/pypi-poetry-publish@v1
        with:
          PACKAGE_DIRECTORY: "./example-package/"
          PYTHON_VERSION: "3.10"
          ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
          PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PUBLISH_REGISTRY_PASSWORD }}
          PUBLISH_REGISTRY: "https://test.pypi.org/legacy/"

Publish to a Private PyPI

  • Requires GitHub secrets:
    • PUBLISH_REGISTRY_USER username for the PyPI registry
    • PUBLISH_REGISTRY_PASSWORD with the password for the PUBLISH_REGISTRY_USER user
    • ACCESS_TOKEN access token with write access to the GitHub repository

publish.yaml

name: Build and publish python package

on:
  release:
    types: [ published ]

jobs:
  publish-service-client-package:
    runs-on: ubuntu-latest
    steps:
      - name: Publish PyPI package
        uses: code-specialist/pypi-poetry-publish@v1
        with:
          PACKAGE_DIRECTORY: "./example-package/"
          PYTHON_VERSION: "3.10"
          ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
          PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PUBLISH_REGISTRY_PASSWORD }}
          PUBLISH_REGISTRY_USER: ${{ secrets.PUBLISH_REGISTRY_USER }}
          PUBLISH_REGISTRY: "https://pypi.code-specialist.com/simple/"

With Private Dependencies

  • Requires GitHub secrets:
    • ACCESS_TOKEN access token with write access to the GitHub repository
    • PUBLISH_REGISTRY_USER username for the PyPI registry
    • PUBLISH_REGISTRY_PASSWORD with the password for the PUBLISH_REGISTRY_USER user
    • POETRY_DEPENDENCY_REGISTRY_USERNAME username for the custom registry
    • POETRY_DEPENDENCY_REGISTRY_PASSWORD password for the custom registry

publish.yaml

name: Build and publish python package

on:
  release:
    types: [ published ]

jobs:
  publish-service-client-package:
    runs-on: ubuntu-latest
    steps:
    - name: Publish PyPI package
      uses: code-specialist/pypi-poetry-publish@v1
      with:
        ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
        PUBLISH_REGISTRY_USER: ${{ secrets.PUBLISH_REGISTRY_USER }}
        PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PUBLISH_REGISTRY_PASSWORD }}
        PUBLISH_REGISTRY: "https://pypi.code-specialist.com"
        POETRY_DEPENDENCY_REGISTRY_URL: "https://pypi.code-specialist.com"
        POETRY_DEPENDENCY_REGISTRY_NAME: "codespecialist"
        POETRY_DEPENDENCY_REGISTRY_USERNAME: ${{ secrets.CUSTOM_PUBLISH_REGISTRY_USERNAME }}
        POETRY_DEPENDENCY_REGISTRY_PASSWORD: ${{ secrets.CUSTOM_PUBLISH_REGISTRY_PASSWORD }}									

For this to install the dependency private-code-specialist-example-package from https://pypi.code-specialist.com/simple/, the corresponding pyproject.toml would look like this:

[tool.poetry]
name = "code-specialist-example-package"
version = "1.0.0" # adjusted to 1.0.0
description = "Example package"
authors = ["Code Specialist"]
packages = [{include = "example_package"}]

[[tool.poetry.source]]
name = "codespecialist"
url = "https://pypi.code-specialist.com"

[tool.poetry.dependencies]
python = "^3.10"
private-code-specialist-example-package = {version = "^1.0.0", source = "codespecialist"}