-
Notifications
You must be signed in to change notification settings - Fork 1
197 lines (180 loc) · 7.68 KB
/
Copy pathrelease.yml
File metadata and controls
197 lines (180 loc) · 7.68 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
name: release
on:
# Manual tag push -> create release (fallback / first-time bootstrap).
push:
tags: ["v*"]
# CI passed on main -> auto-bump if bumpable commits exist.
workflow_run:
workflows: ["ci"]
branches: [main]
types: [completed]
permissions:
contents: write
jobs:
# Auto-bump after CI passes on main.
# commitizen inspects conventional commits since the last tag:
# feat -> minor, fix -> patch, BREAKING CHANGE -> major.
# docs / refactor / chore / etc. -> no bump, job exits cleanly.
#
# The bump commit + tag are pushed with GITHUB_TOKEN, which does NOT
# re-trigger workflows -- no infinite loop.
bump-and-release:
if: >-
github.event_name == 'workflow_run'
&& github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.event == 'push'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install commitizen
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Bump version
id: bump
run: |
set -o pipefail
# commitizen exit codes worth distinguishing:
# 0 = bumped successfully
# 3 = NO_COMMITS_FOUND (nothing bumpable since last tag, exit clean)
# 21 = NO_COMMITS_FOUND when no commits found between tags
# Anything else (consistency failure, IO failure, ...) MUST fail the
# job loudly. The previous shape `if cz bump ...; then ... else ...`
# silently swallowed every non-zero exit and quietly skipped the
# release; consistency drift between version_files and the source
# tree went unnoticed for multiple commits.
set +e
cz bump --yes --check-consistency 2>&1 | tee /tmp/cz.log
status=${PIPESTATUS[0]}
set -e
case "$status" in
0)
echo "bumped=true" >> "$GITHUB_OUTPUT"
echo "tag=v$(cz version --project)" >> "$GITHUB_OUTPUT"
;;
3|21)
echo "no bumpable commits since last tag; exiting clean"
echo "bumped=false" >> "$GITHUB_OUTPUT"
;;
*)
echo "::error::cz bump failed with status $status (see log above)"
exit "$status"
;;
esac
- name: Regenerate amalgamation
if: steps.bump.outputs.bumped == 'true'
run: python3 tools/amalgamate.py
- name: Commit amalgamation if changed
if: steps.bump.outputs.bumped == 'true'
run: |
if ! git diff --quiet -- single_include/citor.hpp; then
git add single_include/citor.hpp
git commit --amend --no-edit
git tag -f "${{ steps.bump.outputs.tag }}"
fi
- name: Push
if: steps.bump.outputs.bumped == 'true'
run: |
git push --force-with-lease origin main
git push --force-with-lease origin "${{ steps.bump.outputs.tag }}"
- name: Stage release artifacts
if: steps.bump.outputs.bumped == 'true'
run: |
tag="${{ steps.bump.outputs.tag }}"
mkdir -p release-artifacts
cp single_include/citor.hpp "release-artifacts/citor-${tag}.hpp"
( cd release-artifacts && sha256sum "citor-${tag}.hpp" > "citor-${tag}.hpp.sha256" )
( cd single_include && tar czf "../release-artifacts/citor-${tag}-single-header.tar.gz" citor.hpp )
( cd release-artifacts && sha256sum "citor-${tag}-single-header.tar.gz" > "citor-${tag}-single-header.tar.gz.sha256" )
- name: Create or refresh GitHub release
if: steps.bump.outputs.bumped == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ steps.bump.outputs.tag }}"
PREV=$(git tag -l 'v*' --sort=-version:refname | sed -n '2p')
{
echo "## What's Changed"
echo ""
if [ -n "$PREV" ]; then
git log "${PREV}..${TAG}^" --pretty=format:'- %s' --no-merges
else
git log "${TAG}^" --pretty=format:'- %s' --no-merges --max-count=50
fi
echo ""
if [ -n "$PREV" ]; then
echo "**Full diff**: https://github.com/${{ github.repository }}/compare/${PREV}...${TAG}"
fi
} > /tmp/notes.md
# Idempotent. The bump-and-release path can re-fire on a force-push
# that lands the same tag at a new SHA; the release-on-tag path can
# race with this job. When the release already exists, refresh
# notes and asset payloads in place; otherwise create.
if gh release view "$TAG" >/dev/null 2>&1; then
echo "release $TAG exists; refreshing notes and assets"
gh release edit "$TAG" --notes-file /tmp/notes.md
gh release upload "$TAG" release-artifacts/* --clobber
else
echo "release $TAG missing; creating"
gh release create "$TAG" --title "$TAG" --notes-file /tmp/notes.md release-artifacts/*
fi
# Manual tag release: local `cz bump` + `git push --follow-tags`,
# or any hand-pushed v* tag. Tags pushed by bump-and-release use
# GITHUB_TOKEN so they do NOT trigger this job (no double release).
release-on-tag:
if: github.event_name == 'push'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Regenerate amalgamation
run: python3 tools/amalgamate.py
- name: Stage release artifacts
run: |
tag="${GITHUB_REF_NAME}"
mkdir -p release-artifacts
cp single_include/citor.hpp "release-artifacts/citor-${tag}.hpp"
( cd release-artifacts && sha256sum "citor-${tag}.hpp" > "citor-${tag}.hpp.sha256" )
( cd single_include && tar czf "../release-artifacts/citor-${tag}-single-header.tar.gz" citor.hpp )
( cd release-artifacts && sha256sum "citor-${tag}-single-header.tar.gz" > "citor-${tag}-single-header.tar.gz.sha256" )
- name: Create or refresh GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ github.ref_name }}"
PREV=$(git tag -l 'v*' --sort=-version:refname | sed -n '2p')
{
echo "## What's Changed"
echo ""
if [ -n "$PREV" ]; then
git log "${PREV}..${TAG}" --pretty=format:'- %s' --no-merges
else
git log "${TAG}" --pretty=format:'- %s' --no-merges --max-count=50
fi
echo ""
if [ -n "$PREV" ]; then
echo "**Full diff**: https://github.com/${{ github.repository }}/compare/${PREV}...${TAG}"
fi
} > /tmp/notes.md
# Idempotent. `release-on-tag` fires on every `v*` tag push, which
# includes force-moves (tag rewrites during a history-rewrite
# session). When the release exists, refresh notes and assets in
# place; otherwise create it.
if gh release view "$TAG" >/dev/null 2>&1; then
echo "release $TAG exists; refreshing notes and assets"
gh release edit "$TAG" --notes-file /tmp/notes.md
gh release upload "$TAG" release-artifacts/* --clobber
else
echo "release $TAG missing; creating"
gh release create "$TAG" --title "$TAG" --notes-file /tmp/notes.md release-artifacts/*
fi