-
Notifications
You must be signed in to change notification settings - Fork 0
159 lines (134 loc) · 6.58 KB
/
release-python.yml
File metadata and controls
159 lines (134 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# .github/workflows/release-python.yml
# See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
name: Release Python Package
on:
push:
tags:
# Trigger on SemVer tags. Commitizen configured in pyproject.toml (Area 12)
# uses PEP 440 which is SemVer compatible.
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-*" # Include pre-release tags
# Allow workflow to be run manually from the Actions tab for a specific tag
workflow_dispatch:
inputs:
tag:
description: 'Git tag to build and release (e.g., v1.2.3). Must already exist.'
required: true # User must provide a tag when manually dispatching
jobs:
# Job 1: Build the package and publish to TestPyPI
build_and_testpypi:
name: Build & Publish to TestPyPI
# Run on a consistent, standard build environment
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'push' && github.ref || github.event.inputs.tag }}
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: .python-version
- name: Upload built package artifacts
# Upload packages so the publish job can access them.
uses: actions/upload-artifact@v4
with:
name: distribution-packages # Name the artifact
path: dist/ # Path to the built sdist/wheel files
retention-days: 7 # Keep built artifacts for 7 days
- name: Download built package artifacts
uses: actions/download-artifact@v4
with:
name: distribution-packages-{{ github.event.inputs.tag }}
path: dist/
# --- Publish to TestPyPI Step ---
# This step runs within the same job after build.
- name: Publish to TestPyPI
# Execute the Task Automation publish session for TestPyPI.
# This calls uv publish dist/* with TestPyPI credentials/config (Topic 10).
env:
# TestPyPI credentials stored as secrets in GitHub Settings -> Secrets
TWINE_USERNAME: __token__ # Standard username when using API tokens
TWINE_PASSWORD: ${{ secrets.TESTPYPI_API_TOKEN }}{% endraw } # Use GitHub Encrypted Secret
# Optional: If uv publish requires different config for repository URL, pass TWINE_REPOSITORY or similar
run: uvx nox -s publish-package -- --repository testpypi # Call the publish-package session, passing repository arg
# --- Get Changelog Content for Release Notes ---
# Use a standard action to extract changelog content.
# This action requires a standard CHANGELOG.md format.
- name: Get Release Notes from Changelog
id: changelog
uses: simple-changelog/action@v3 # Action to parse CHANGELOG.md
with:
path: CHANGELOG.md # Path to your CHANGELOG.md
tag: { "${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag }}" } # Pass the tag name
# Define outputs from this job that other jobs (like create_github_release) can use.
outputs:
changelog_body:
description: "Release notes body extracted from CHANGELOG.md"
value: {% raw %}${{ steps.changelog.outputs.changes }} # Output the extracted changelog body
# Job 2: Publish to Production PyPI
# This job runs only if Job 1 completes successfully (implicit dependency)
# and only on tag push events (NOT manual dispatch for production).
publish_pypi:
name: Publish to Production PyPI
runs-on: ubuntu-latest
# This job explicitly depends on build_and_testpypi completing successfully
needs: build_and_testpypi
# Only run on tag push events, NOT on manual dispatch for the final PyPI publish
if: { "github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')" }
steps:
- name: Download package artifacts
uses: actions/download-artifact@v4
with:
name: distribution-packages
path: dist/
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: .python-version
- name: Set up uv
uses: astral-sh/setup-uv@v6
# --- Publish to Production PyPI Step ---
- name: Publish to PyPI
# Execute the Task Automation publish session for Production PyPI.
# Calls uv publish dist/* which defaults to pypi.org (Topic 10).
# Configure Production PyPI credentials securely.
env:
# Production PyPI credentials stored as secrets in GitHub Settings -> Secrets
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} # Use GitHub Encrypted Secret
# Optional: TWINE_REPOSITORY if publishing to a custom production index
run: uvx nox -s publish-package # Call the publish-package session (defaults to pypi.org)
# Job 3: Create GitHub Release (Runs regardless of PyPI publish success, relies on build job for info/artifacts)
create_github_release:
name: Create GitHub Release
runs-on: ubuntu-latest
# Needs the build job (which includes getting changelog)
needs: build_and_testpypi
# Only run this job if triggered by a tag push
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- name: Download package artifacts # Get built artifacts for release assets
uses: actions/download-artifact@v4
with:
name: distribution-packages
- name: Get tag name
id: get_tag
run: echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
- name: Create GitHub Release
# Uses a standard action to create a release in GitHub based on the tag.
uses: softprops/action-gh-release@v2
with:
# The Git tag the release is associated with
tag_name: ${{ steps.get_tag.outputs.tag }}
# The name of the release (often the same as the tag)
name: Release ${{ steps.get_tag.outputs.tag }}
# The body of the release notes - access the output from the 'build_and_testpypi' job
body: ${{ needs.build_and_testpypi.outputs.changelog_body }} # Access changelog body from dependent job output
files: dist/* # Attach built sdist and wheel files as release assets
# Optional: Mark as a draft release for manual review before publishing
# draft: true
# Optional: Mark as a pre-release for tags containing hyphens (e.g., v1.0.0-rc1)
prerelease: ${{ contains(steps.get_tag.outputs.tag, '-') }} # Checks if tag contains hyphen (e.g. v1.0.0-rc.1)