-
Notifications
You must be signed in to change notification settings - Fork 0
81 lines (74 loc) · 3.47 KB
/
Copy pathrelease.yml
File metadata and controls
81 lines (74 loc) · 3.47 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
name: Release
# Tag-driven: pushing a semver tag publishes to PyPI and creates the matching
# GitHub Release. Replaces the old `on: release: published` publish.yml — that
# trigger is removed so the published Release this workflow creates can't re-fire
# it (double-publish). The tag is the sole entry point; by convention a tag is
# only cut off a green main, so there is no in-workflow CI gate.
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+' # stable: 2.7.2
- '[0-9]+.[0-9]+.[0-9]+[a-z]+[0-9]+' # pre-release: 2.0.0rc1, 4.0.0a2
# contents: write -> create the GitHub Release; id-token: write -> OIDC for PyPI Trusted Publishing.
permissions:
contents: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
environment: pypi # scopes the PyPI Trusted Publisher; hook for approval rules
steps:
- uses: actions/checkout@v6
- uses: extractions/setup-just@v4
- uses: astral-sh/setup-uv@v7
# Curated release notes are MANDATORY for a stable tag. This runs BEFORE
# `just publish` (which is irreversible) so a missing notes file aborts
# the release before anything reaches PyPI — rather than silently shipping
# with GitHub's auto-generated notes. Pre-release tags (a letter in the
# name, e.g. 2.0.0rc1) are exempt and keep the auto-generated fallback.
- name: Require curated release notes (stable tags)
run: |
set -euo pipefail
if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then
echo "Pre-release ${GITHUB_REF_NAME}: curated notes not required."
exit 0
fi
notes="planning/releases/${GITHUB_REF_NAME}.md"
if [ ! -f "$notes" ]; then
echo "::error::Stable tag ${GITHUB_REF_NAME} has no curated release notes at ${notes}. Write the notes, commit to main, and re-tag." >&2
exit 1
fi
echo "Found curated release notes: ${notes}"
# PyPI is irreversible, so it runs FIRST: if it fails the job stops and no
# GitHub Release is created advertising a version that never reached PyPI.
# `just publish` derives the version from $GITHUB_REF_NAME (the tag name).
# Auth via PyPI Trusted Publishing (OIDC); no PYPI_TOKEN. Needs a Trusted
# Publisher on the httpware PyPI project (env: pypi, workflow: release.yml).
- run: just publish
# Description source: planning/releases/<tag>.md if present (verbatim, no
# auto-changelog appended); otherwise GitHub's generated notes. A tag with
# a letter (2.0.0rc1) is a pre-release -> flagged so GitHub won't mark it
# "Latest".
- name: Resolve release metadata
id: meta
run: |
set -euo pipefail
notes="planning/releases/${GITHUB_REF_NAME}.md"
if [ -f "$notes" ]; then
echo "body_path=$notes" >> "$GITHUB_OUTPUT"
echo "generate_notes=false" >> "$GITHUB_OUTPUT"
else
echo "generate_notes=true" >> "$GITHUB_OUTPUT"
fi
if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then
echo "prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "prerelease=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish GitHub Release
uses: softprops/action-gh-release@v3
with:
body_path: ${{ steps.meta.outputs.body_path }}
generate_release_notes: ${{ steps.meta.outputs.generate_notes }}
prerelease: ${{ steps.meta.outputs.prerelease }}
draft: false