1+ name : Release to PyPI
2+
3+ on :
4+ push :
5+ tags :
6+ - ' v*'
7+ workflow_dispatch :
8+ inputs :
9+ version :
10+ description : ' Version to release (e.g., 0.1.0)'
11+ required : true
12+
13+ jobs :
14+ test :
15+ runs-on : ubuntu-latest
16+ strategy :
17+ matrix :
18+ python-version : ['3.10', '3.11', '3.12', '3.13']
19+
20+ steps :
21+ - uses : actions/checkout@v4
22+
23+ - name : Install uv
24+ uses : astral-sh/setup-uv@v2
25+ with :
26+ version : " latest"
27+
28+ - name : Set up Python ${{ matrix.python-version }}
29+ run : uv python install ${{ matrix.python-version }}
30+
31+ - name : Install dependencies
32+ run : |
33+ uv sync --all-extras
34+
35+ - name : Run tests
36+ run : |
37+ uv run pytest tests/ -v --cov=src/structured_output_cookbook --cov-report=xml
38+
39+ - name : Run linting
40+ run : |
41+ uv run black --check src/ tests/
42+ uv run ruff check src/ tests/
43+
44+ build :
45+ needs : test
46+ runs-on : ubuntu-latest
47+
48+ steps :
49+ - uses : actions/checkout@v4
50+ with :
51+ fetch-depth : 0
52+
53+ - name : Install uv
54+ uses : astral-sh/setup-uv@v2
55+ with :
56+ version : " latest"
57+
58+ - name : Set up Python
59+ run : uv python install 3.11
60+
61+ - name : Install build dependencies
62+ run : |
63+ uv tool install build
64+ uv tool install twine
65+
66+ - name : Build package
67+ run : |
68+ uvx build
69+
70+ - name : Check package
71+ run : |
72+ uvx twine check dist/*
73+
74+ - name : Upload build artifacts
75+ uses : actions/upload-artifact@v4
76+ with :
77+ name : dist
78+ path : dist/
79+
80+ publish :
81+ needs : build
82+ runs-on : ubuntu-latest
83+ if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
84+ environment : pypi
85+ permissions :
86+ id-token : write # For trusted publishing
87+
88+ steps :
89+ - name : Download build artifacts
90+ uses : actions/download-artifact@v4
91+ with :
92+ name : dist
93+ path : dist/
94+
95+ - name : Publish to PyPI
96+ uses : pypa/gh-action-pypi-publish@release/v1
97+ with :
98+ skip-existing : true
99+
100+ create-release :
101+ needs : publish
102+ runs-on : ubuntu-latest
103+ if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
104+ permissions :
105+ contents : write
106+
107+ steps :
108+ - uses : actions/checkout@v4
109+ with :
110+ fetch-depth : 0
111+
112+ - name : Generate release notes
113+ id : release_notes
114+ run : |
115+ # Extract version from tag
116+ VERSION=${GITHUB_REF#refs/tags/v}
117+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
118+
119+ # Extract changelog section for this version
120+ if grep -q "## \[${VERSION}\]" CHANGELOG.md; then
121+ sed -n "/## \[${VERSION}\]/,/## \[/p" CHANGELOG.md | sed '$d' > release_notes.md
122+ else
123+ echo "Release ${VERSION}" > release_notes.md
124+ echo "" >> release_notes.md
125+ echo "See [CHANGELOG.md](CHANGELOG.md) for details." >> release_notes.md
126+ fi
127+
128+ - name : Create GitHub Release
129+ uses : actions/create-release@v1
130+ env :
131+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
132+ with :
133+ tag_name : ${{ github.ref }}
134+ release_name : Release v${{ steps.release_notes.outputs.version }}
135+ body_path : release_notes.md
136+ draft : false
137+ prerelease : false
0 commit comments