# Automating Python Package Releases to PyPI with GitHub Actions Automating the release process of your Python packages to the Python Package Index (PyPI) can save you time and help ensure a consistent and reliable release process. In this guide, we'll walk you through the steps to automate the release of a Python package using GitHub Actions. We'll cover everything from setting up GitHub Actions and PyPI to configuring tokens and creating release workflows. ## Prerequisites Before you begin, make sure you have the following prerequisites in place: - A Python package that you want to release. - A GitHub repository for your package. - A PyPI account (https://pypi.org/account/register/). ## Step 1: Configure GitHub Secrets To securely store and use sensitive information like API tokens, we'll configure GitHub Secrets. Secrets are encrypted environment variables that can be accessed in your GitHub Actions workflows. 1. Navigate to your GitHub repository. 2. Click on the "Settings" tab. 3. In the left sidebar, click on "Secrets." 4. Click the "New repository secret" button. 5. Create two secrets: - `GITHUB_TOKEN`: Your GitHub Personal Access Token with the necessary permissions to create releases. - `PYPI_TOKEN`: Your PyPI API token for uploading packages. ## Step 2: Create a Release Workflow In your repository, create a new file named `release.yaml` in the `.github/workflows` directory. This YAML file will define your GitHub Actions workflow for automated releases. ```yaml name: Release on: workflow_dispatch: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest defaults: run: shell: bash -l {0} steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up Miniconda uses: conda-incubator/setup-miniconda@v2 with: miniconda-version: "latest" mamba-version: "*" environment-file: conda/dev.yaml channels: conda-forge,nodefaults activate-environment: yourpackage use-mamba: true miniforge-variant: Mambaforge - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: 18 - name: Test release if: ${{ github.event_name != 'workflow_dispatch' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: make release-dry - name: Release if: ${{ github.event_name == 'workflow_dispatch' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} run: | poetry config pypi-token.pypi ${PYPI_TOKEN} make release ``` This workflow defines the following steps: - It runs on every push to the `main` branch, pull requests to the `main` branch, and manually triggered workflow dispatches. - It sets up the environment, including Miniconda and Node.js, as required by your package. - It tests the release (dry run) when triggered by a push. - It performs the actual release when triggered manually. ## Step 3: Configure Semantic Release Semantic Release is a tool that automates versioning and package publishing based on commit messages. You'll need to configure Semantic Release in your project for automated versioning. ### `.release.json` Create a `.release.json` file in your project root to configure Semantic Release. This file defines the release process, including how versions are determined and what actions are taken during the release. ```json { "branches": ["main"], "tagFormat": "${version}", "plugins": [ [ "@semantic-release/commit-analyzer", { "preset": "conventionalcommits" } ], [ "semantic-release-replace-plugin", { "replacements": [ { "files": ["yourpackage/__init__.py"], "from": "return \".*\" # changed by semantic-release", "to": "return \"${nextRelease.version}\" # changed by semantic-release", "results": [ { "file": "yourpackage/__init__.py", "hasChanged": true, "numMatches": 1, "numReplacements": 1 } ], "countMatches": true }, { "files": ["pyproject.toml"], "from": "version = \".*\" # changed by semantic-release", "to": "version = \"${nextRelease.version}\" # changed by semantic-release", "results": [ { "file": "pyproject.toml", "hasChanged": true, "numMatches": 1, "numReplacements": 1 } ], "countMatches": true } ] } ], [ "@semantic-release/release-notes-generator", { "preset": "conventionalcommits" } ], [ "@semantic-release/changelog", { "changelogTitle": "Release Notes\n---", "changelogFile": "CHANGELOG.md" } ], [ "@semantic-release/exec", { "prepareCmd": "poetry build", "publishCmd": "poetry publish" } ], [ "@semantic-release/github", { "assets": ["dist/*.whl", "dist/*.tar.gz"] } ], [ "@semantic-release/git", { "assets": ["pyproject.toml", "CHANGELOG.md", "yourpackage/__init__.py"], "message": "chore(release): ${nextRelease.version}" } ] ] } ``` This configuration file defines the release process, including how version numbers are determined, how the changelog is generated, and what commands are executed during the release. ## Step 4: Makefile and Versioning Your `Makefile` plays a crucial role in managing the release process. Here's an example of a `Makefile` with key release-related targets: ```make PYTHON = poetry run python SEMANTIC_RELEASE = npx --yes \ -p semantic-release \ -p conventional-changelog-conventionalcommits \ -p "@semantic-release/commit-analyzer" \ -p "@semantic-release/release-notes-generator" \ -p "@semantic-release/changelog" \ -p "@semantic-release/exec" \ -p "@semantic-release/github" \ -p "semantic-release-replace-plugin" \ semantic-release ... # ... (other targets) .PHONY: release release: ## Make release $(SEMANTIC_RELEASE) --ci .PHONY: release-dry release-dry: ## Test make release $(SEMANTIC_RELEASE) --dry-run ``` The `release` and `release-dry` targets run Semantic Release with the `--ci` and `--dry-run` flags, respectively. ## Step 5: Create a PyPI Package Before you can automate package releases, ensure that you've created a Python package, possibly using a tool like Poetry or setuptools. You should have a `pyproject.toml` or `setup.py` file that defines your package's metadata. ## Step 6: Create a Manual Release To trigger the initial release and tag your repository with the first version, you need to create a manual release in your GitHub repository. Follow these steps: 1. In your GitHub repository, go to the "Releases" section. 2. Click the "Draft a new release" button. 3. Fill in the tag version (e.g., `v1.0.0`). 4. Provide a title and description for the release. 5. Attach any release assets if needed (e.g., wheels, source distributions). 6. Click the "Publish release" button. This manual release will establish the initial version and tag in your repository. ## Conclusion Automating the release process of your Python packages to PyPI using GitHub Actions and Semantic Release can save you time and ensure consistency in your release workflow. By configuring GitHub Secrets, creating a release workflow, setting up Semantic Release, and creating a PyPI package, you can streamline the process of publishing new versions of your package to PyPI with confidence.
{"title":"Automating Python Package Releases to PyPI with GitHub Actions","description":"Automating the release process of your Python packages to the Python Package Index (PyPI) can save you time and help ensure a consistent and reliable release process. In this guide, we’ll walk you through the steps to automate the release of a Python package using GitHub Actions. We’ll cover everything from setting up GitHub Actions and PyPI to configuring tokens and creating release workflows.","contributors":"[{\"id\":\"caf2ffec-1f75-4520-b174-3c7cce0ac7f9\",\"add\":7838,\"del\":0}]"}
Expand menu