GitHub Actions
Patterns and recipes for GitHub Actions workflows
PyPI Release with Trusted Publishing
Use OIDC trusted publishing to release to PyPI. No API tokens to manage or rotate. GitHub Actions authenticates directly with PyPI using short-lived tokens.
name: Release
on:
push:
tags:
- "v*"
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build
- uses: pypa/gh-action-pypi-publish@release/v1
See Release Tag Patterns for SemVer (v*) and CalVer tag configuration.
Configure trusted publisher in PyPI project settings. Navigate to pypi.org/manage/project/<name>/settings/publishing/:
| Field | Value |
|---|---|
| Owner | GitHub org or username |
| Repository | repo name |
| Workflow | release.yml |
| Environment | leave blank |
For new packages, use pending publisher. Configure at pypi.org/manage/account/publishing/ before the first release.
Private Repository Permissions
Explicit permissions revoke all defaults. When you set permissions: in a workflow, GitHub removes all implicit permissions. Private repo checkout fails without contents: read:
remote: Repository not found.
fatal: repository 'https://github.com/org/repo/' not found
Always include both permissions for private repos:
permissions:
contents: read # checkout access
id-token: write # OIDC token for PyPI
Release Tag Patterns
SemVer pattern - use v prefix:
on:
push:
tags:
- "v*"
git tag v1.2.0
git push origin v1.2.0
CalVer pattern - use 20 prefix, no v:
on:
push:
tags:
- "20[0-9][0-9].[0-9]*.[0-9]*"
git tag 2026.2.0
git push origin 2026.2.0
CalVer tag matches __version__ directly - no mismatch between tag and code. GitHub Actions uses glob, not regex. [0-9]* means “one digit, then anything” - good enough for CalVer. See CalVer Date-based Versioning for version format.