Skip to content

Commit 8e56248

Browse files
lesnik512claude
andauthored
ci: tag-driven release + PyPI publish on tag push (#81)
Replace publish.yml (on: release: published) with a tag-driven release.yml: pushing a semver tag publishes to PyPI and creates the matching GitHub Release in one job. Removing publish.yml prevents a double-publish — the new workflow creates a published Release itself. Ported from modern-python/modern-di. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 379738b commit 8e56248

2 files changed

Lines changed: 60 additions & 17 deletions

File tree

.github/workflows/publish.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Release
2+
3+
# Tag-driven: pushing a semver tag publishes to PyPI and creates the matching
4+
# GitHub Release. Replaces the old `on: release: published` publish.yml — that
5+
# trigger is removed so the published Release this workflow creates can't re-fire
6+
# it (double-publish). The tag is the sole entry point; by convention a tag is
7+
# only cut off a green main, so there is no in-workflow CI gate.
8+
on:
9+
push:
10+
tags:
11+
- '[0-9]+.[0-9]+.[0-9]+' # stable: 2.7.2
12+
- '[0-9]+.[0-9]+.[0-9]+[a-z]+[0-9]+' # pre-release: 2.0.0rc1, 4.0.0a2
13+
14+
# Needed for softprops/action-gh-release to create the GitHub Release.
15+
permissions:
16+
contents: write
17+
18+
jobs:
19+
release:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v6
23+
- uses: extractions/setup-just@v4
24+
- uses: astral-sh/setup-uv@v7
25+
26+
# PyPI is irreversible, so it runs FIRST: if it fails the job stops and no
27+
# GitHub Release is created advertising a version that never reached PyPI.
28+
# `just publish` derives the version from $GITHUB_REF_NAME (the tag name).
29+
- run: just publish
30+
env:
31+
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
32+
33+
# Description source: planning/releases/<tag>.md if present (verbatim, no
34+
# auto-changelog appended); otherwise GitHub's generated notes. A tag with
35+
# a letter (2.0.0rc1) is a pre-release -> flagged so GitHub won't mark it
36+
# "Latest".
37+
- name: Resolve release metadata
38+
id: meta
39+
run: |
40+
set -euo pipefail
41+
notes="planning/releases/${GITHUB_REF_NAME}.md"
42+
if [ -f "$notes" ]; then
43+
echo "body_path=$notes" >> "$GITHUB_OUTPUT"
44+
echo "generate_notes=false" >> "$GITHUB_OUTPUT"
45+
else
46+
echo "generate_notes=true" >> "$GITHUB_OUTPUT"
47+
fi
48+
if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then
49+
echo "prerelease=true" >> "$GITHUB_OUTPUT"
50+
else
51+
echo "prerelease=false" >> "$GITHUB_OUTPUT"
52+
fi
53+
54+
- name: Publish GitHub Release
55+
uses: softprops/action-gh-release@v3
56+
with:
57+
body_path: ${{ steps.meta.outputs.body_path }}
58+
generate_release_notes: ${{ steps.meta.outputs.generate_notes }}
59+
prerelease: ${{ steps.meta.outputs.prerelease }}
60+
draft: false

0 commit comments

Comments
 (0)