-
Notifications
You must be signed in to change notification settings - Fork 0
155 lines (141 loc) · 6.56 KB
/
Copy pathchangelog-rollup.yml
File metadata and controls
155 lines (141 loc) · 6.56 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
name: Roll up CHANGELOG after release
# Triggered automatically after `release.yml` succeeds. Performs the
# mechanical CHANGELOG roll-up (move `[Unreleased]` entries under a
# `[<version>]` heading, update footer compare links, bump pyproject.toml +
# uv.lock to the next PATCH). Opens a `chore:` PR against develop for
# owner review + admin-squash-merge.
#
# The script logic + edge cases live in
# `.github/scripts/rollup_changelog.py` with full unit-test coverage in
# `tests/test_rollup_changelog.py`.
on:
workflow_run:
workflows: [Release]
types: [completed]
workflow_dispatch:
inputs:
tag:
description: "Released tag (e.g. v0.3.0) — required only for manual replay"
required: true
type: string
permissions:
contents: write
pull-requests: write
jobs:
rollup:
name: Open rollup PR
runs-on: ubuntu-latest
# `workflow_run` fires regardless of conclusion; gate so we only act on
# successful release runs (skip when release.yml itself failed).
if: >
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: develop
# full history needed for `git describe --abbrev=0 --tags <ref>^`
# to resolve the prior tag.
fetch-depth: 0
# Prefer RELEASE_BOT_TOKEN (a non-GITHUB_TOKEN identity) so the
# resulting branch push fires `pull_request` workflows on the
# auto-PR. Falls back to GITHUB_TOKEN when the secret isn't set —
# the auto-PR still opens, but its CI doesn't run until a user
# pushes a commit on top.
token: ${{ secrets.RELEASE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.14"
- name: Resolve tags + date
id: resolve
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
# release.yml runs on tag pushes; the head_branch on the
# workflow_run payload carries the tag name in that case.
TAG="${{ github.event.workflow_run.head_branch }}"
fi
if [ -z "${TAG}" ]; then
echo "::error::could not resolve released tag from trigger payload"
exit 1
fi
# Resolve the tag's prior tag. `git describe ... <tag>^` errors when
# there is no prior — catch and treat as empty (first release).
if PRIOR=$(git describe --abbrev=0 --tags --match 'v*.*.*' "${TAG}^" 2>/dev/null); then
echo "prior tag: ${PRIOR}"
else
PRIOR=""
echo "no prior tag (first release)"
fi
# Resolve the release date from the released tag's CHANGELOG.md.
# The release PR is the authoritative source — using `date -u` at
# workflow run time can drift if the workflow fires in a different
# UTC day than the release PR was crafted. Falls back to date -u
# with a warning when the heading isn't found, so a degenerate
# CHANGELOG shape doesn't block the workflow.
VERSION="${TAG#v}"
if DATE=$(git show "${TAG}":CHANGELOG.md \
| grep -oE "^## \[${VERSION}\] - [0-9]{4}-[0-9]{2}-[0-9]{2}" \
| head -1 \
| sed 's/.* - //'); then
if [ -z "${DATE}" ]; then
echo "::warning::could not extract date for ${VERSION} from CHANGELOG.md; falling back to date -u"
DATE=$(date -u +%Y-%m-%d)
else
echo "date from CHANGELOG: ${DATE}"
fi
else
echo "::warning::git show ${TAG}:CHANGELOG.md failed; falling back to date -u"
DATE=$(date -u +%Y-%m-%d)
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "prior=${PRIOR}" >> "$GITHUB_OUTPUT"
echo "date=${DATE}" >> "$GITHUB_OUTPUT"
echo "branch=chore/changelog-rollup-${TAG}" >> "$GITHUB_OUTPUT"
- name: Run rollup script
run: |
python .github/scripts/rollup_changelog.py \
--tag "${{ steps.resolve.outputs.tag }}" \
--prior-tag "${{ steps.resolve.outputs.prior }}" \
--date "${{ steps.resolve.outputs.date }}"
- name: Open rollup PR
env:
GH_TOKEN: ${{ secrets.RELEASE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
BRANCH="${{ steps.resolve.outputs.branch }}"
TAG="${{ steps.resolve.outputs.tag }}"
DATE="${{ steps.resolve.outputs.date }}"
# Idempotent: if the branch already exists from a previous replay,
# bail rather than force-push (the existing PR is the source of truth).
if git ls-remote --exit-code --heads origin "${BRANCH}" >/dev/null 2>&1; then
echo "::warning::branch ${BRANCH} already exists; skipping push to avoid clobbering an in-flight rollup PR"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "${BRANCH}"
git add CHANGELOG.md pyproject.toml uv.lock
git commit -m "chore: roll up CHANGELOG [Unreleased] under [${TAG#v}] - ${DATE}"
git push origin "${BRANCH}"
gh pr create \
--base develop \
--head "${BRANCH}" \
--title "chore: roll up CHANGELOG [Unreleased] under [${TAG#v}] - ${DATE}" \
--body "$(cat <<EOF
Auto-opened by [.github/workflows/changelog-rollup.yml](.github/workflows/changelog-rollup.yml) after the ${TAG} release.
## What this PR does
- Inserts \`## [${TAG#v}] - ${DATE}\` heading after \`## [Unreleased]\` in CHANGELOG.md.
- Updates \`[Unreleased]: ...compare/${TAG}...HEAD\` footer link.
- Adds \`[${TAG#v}]: ...compare/...${TAG}\` footer link.
- Bumps \`pyproject.toml\` \`[project].version\` and \`uv.lock\` self-version one PATCH (so develop's \`[Unreleased]\` can accumulate again).
See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for the cycle.
## Review
- [ ] CHANGELOG diff matches the released tag's published notes.
- [ ] Version bump is the next PATCH after \`${TAG#v}\` (no skipped bumps).
- [ ] CI green.
After review: \`gh pr merge <N> --admin --squash\`.
EOF
)"