Automating releases with GitHub Actions and Semantic Release
Automating the release process supports consistent versioning and integrates with Semantic Commits. This guide explains how to set up a GitHub Actions workflow, inspired by this project’s release.yml, to handle version bumping, changelog generation, GitHub releases, and PyPI publishing using the semantic-release tool.
Prerequisites
Section titled “Prerequisites”- Semantic commits: your project uses Semantic commit messages.
semantic-releaserelies on these messages to determine the next version number and generate changelogs. - UV project: this guide assumes a Python project managed with UV, ready for building and publishing.
- Testing workflow: a separate GitHub Actions workflow (e.g.,
run-tests.ymlas described in the Automated testing guide) that runs your tests. The release workflow will trigger upon successful completion of the test workflow. - PyPI token: a PyPI API token stored as a secret (e.g.,
PYPI_TOKEN) in your GitHub repository settings (Settings > Secrets and variables > Actions).
Core idea: semantic-release
Section titled “Core idea: semantic-release”semantic-release automates the entire package release workflow:
- Analyzes commits since the last release (using the Semantic Commit format).
- Determines the next version number (patch, minor, major).
- Generates release notes/changelog based on the commits.
- Creates a Git tag for the new version.
- Creates a GitHub Release associated with the tag.
- (Optionally) Publishes packages to registries like PyPI.
Configuration (semantic-release)
Section titled “Configuration (semantic-release)”semantic-release needs configuration, typically defined in a .releaserc.json file in your project root. This configuration specifies plugins for different steps (analyzing commits, generating notes, publishing, etc.) and their options.
A typical configuration might include plugins like:
@semantic-release/commit-analyzer: determines release type based on commits.@semantic-release/release-notes-generator: generates changelog content.@semantic-release/changelog: updates aCHANGELOG.mdfile.@semantic-release/npm(if publishing JS packages) or@semantic-release/exec(to run custom commands likeuv publish).@semantic-release/github: creates GitHub releases and comments on related issues/PRs.@semantic-release/git: commits changes likepackage.jsonorCHANGELOG.mdand pushes the Git tag.
Example snippet for .releaserc.json (adapt as needed):
{ "branches": ["master"], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", [ "@semantic-release/exec", { "prepareCmd": "uv version ${nextRelease.version}" } ], "@semantic-release/github", [ "@semantic-release/git", { "assets": ["CHANGELOG.md", "pyproject.toml"], "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" } ] ]}(Note: In this example, @semantic-release/exec uses prepareCmd to update the pyproject.toml with the new version via uv version (see the UV guide for versioning commands). The updated pyproject.toml (along with CHANGELOG.md) is typically committed by the @semantic-release/git plugin as specified in the assets.)
Example workflow: release after tests pass
Section titled “Example workflow: release after tests pass”This workflow triggers when the Run tests workflow completes successfully on the master branch. It also includes a check (if: contains(...)) which looks for [release] in the commit message that triggered the test workflow. This acts as a safeguard or manual confirmation step.
Create a file named .github/workflows/release.yml:
name: Release
on: workflow_run: workflows: ["Run tests"] # Replace "Run tests" with the actual name of your test workflow types: - completed branches: [master] # Or your main release branch
jobs: release: name: Release runs-on: ubuntu-latest # Condition: only run if the test workflow succeeded AND # the commit message that triggered the tests contained '[release]' if: github.event.workflow_run.conclusion == 'success' && contains(github.event.workflow_run.head_commit.message, '[release]') permissions: contents: write # Needed for semantic-release to push tags/commits issues: write # Needed for semantic-release to comment on issues pull-requests: write # Needed for semantic-release to comment on PRs
steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch all history for semantic-release analysis
- name: Install UV uses: astral-sh/setup-uv@v6 with: enable-cache: true python-version: "3.10" # Or your project's Python version
- name: Install Python run: uv python install
- name: Install dependencies run: uv sync --locked
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "lts/*" # Use long-term support version
# Install semantic-release and necessary plugins globally in the runner - name: Install semantic-release plugins run: | npm install -g semantic-release \ @semantic-release/commit-analyzer \ @semantic-release/release-notes-generator \ @semantic-release/changelog \ @semantic-release/github \ @semantic-release/git \ @semantic-release/exec
# Run semantic-release - name: Run semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Provided by Actions PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} # Your PyPI token secret # Use npx to run the globally installed package run: npx semantic-release
- name: Build and publish to PyPI if: success() # Typically check if semantic-release created a new version env: UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} run: | uv build uv publish --token $UV_PUBLISH_TOKEN # See the UV guide for more on building and publishing: # /repository_setup_guides/virtual_environments/uv_setup/#building-and-publishing-packagesExplanation:
Section titled “Explanation:”on.workflow_run: triggers when the specified workflow (Run tests) completes on themasterbranch (see GitHub Actions basics for more on triggers).if: github.event.workflow_run.conclusion == 'success' && contains(...): runs the job only if tests passed and the triggering commit message included[release].permissions: grants permissions forsemantic-releaseto push commits/tags and interact with GitHub releases/issues.- Checkout: fetches the code.
fetch-depth: 0is required forsemantic-releaseto analyze all relevant history. - Install UV and dependencies: installs UV with caching enabled, installs Python, and syncs project dependencies using UV.
- Setup Node.js: installs Node.js, as
semantic-releaseis a Node.js application. - Install semantic-release plugins: installs
semantic-releaseand its configured plugins globally usingnpm. - Run semantic-release: executes
semantic-releaseusingnpx. Environment variablesGITHUB_TOKEN(automatically provided by Actions) and yourPYPI_TOKENsecret are made available.semantic-releasereads its configuration, analyzes commits, performs versioning, generates notes, tags, creates the GitHub release, and triggers configured publishing steps (like the@semantic-release/execcommand or other plugins).
This setup provides automated release workflow integration with semantic commits.