alpha

GitHub Actions

Patterns and recipes for GitHub Actions workflows

#github-actions#ci-cd#gha#calver#semver#release

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/:

FieldValue
OwnerGitHub org or username
Repositoryrepo name
Workflowrelease.yml
Environmentleave 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.