Repository navigation
220 lines (198 loc) · 8.44 KB
/
Copy pathversion-bump.yml
File metadata and controls
220 lines (198 loc) · 8.44 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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
name: Version Bump
on:
push:
branches:
- dev
paths-ignore:
- 'VERSION'
- '.github/workflows/version-bump.yml'
jobs:
bump-version:
runs-on: ubuntu-latest
timeout-minutes: 45
# Skip if commit message contains [skip ci] or [skip version]
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip version]') && !startsWith(github.event.head_commit.message, 'chore: bump version')"
permissions:
actions: read
contents: write
pull-requests: read
statuses: write
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get current version
id: current
run: |
if [ -f VERSION ]; then
VERSION=$(cat VERSION | tr -d '\n\r ')
else
VERSION="0.0.0"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Current version: $VERSION"
- name: Determine bump type
id: bump
env:
# Pass the commit message through the environment, NOT string-
# interpolated into the run script. Splicing
# ${{ github.event.head_commit.message }} directly into the shell
# source means a body containing backticks, $(...) or | is parsed
# as code — which broke this job (commit e3337f0's body had
# `vagrant up | Out-Host`, a syntax error) and is a command-
# injection vector. As an env var it is inert text bash never re-parses.
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
# Major if a [major] tag OR a BREAKING/major: conventional prefix is
# present; minor for [minor] tag OR feat/feature/minor: prefix;
# otherwise patch. Supporting both keeps existing conventional
# commits working while allowing explicit [minor]/[major] tags.
if echo "$COMMIT_MSG" | grep -qiE '\[major\]' || \
echo "$COMMIT_MSG" | grep -qiE '^(BREAKING|breaking!|major):'; then
echo "type=major" >> $GITHUB_OUTPUT
echo "Bump type: major"
elif echo "$COMMIT_MSG" | grep -qiE '\[minor\]' || \
echo "$COMMIT_MSG" | grep -qiE '^(feat|feature|minor):'; then
echo "type=minor" >> $GITHUB_OUTPUT
echo "Bump type: minor"
else
echo "type=patch" >> $GITHUB_OUTPUT
echo "Bump type: patch"
fi
- name: Calculate new version
id: new
run: |
VERSION="${{ steps.current.outputs.version }}"
BUMP_TYPE="${{ steps.bump.outputs.type }}"
# Parse version components
MAJOR=$(echo $VERSION | cut -d. -f1)
MINOR=$(echo $VERSION | cut -d. -f2)
PATCH=$(echo $VERSION | cut -d. -f3)
# Bump appropriate component
case $BUMP_TYPE in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
esac
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
# A GITHUB_TOKEN push deliberately does not start another workflow run.
# Advancing dev immediately therefore used to replace the tested PR head
# with an untested VERSION-only commit. Wait for every sibling run on the
# triggering SHA first; on a red commit, do not advance dev at all.
- name: Wait for sibling CI before advancing dev
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
deadline=$((SECONDS + 2400))
settled_polls=0
while [ "$SECONDS" -lt "$deadline" ]; do
runs_file="$RUNNER_TEMP/serverkit-sibling-runs.json"
gh run list \
--repo "$GITHUB_REPOSITORY" \
--commit "$GITHUB_SHA" \
--limit 100 \
--json workflowName,event,status,conclusion,url \
> "$runs_file"
relevant=$(jq '[.[] | select(
.workflowName != "Version Bump"
and .workflowName != "Discord Notify"
)]' "$runs_file")
failed=$(jq -r '.[] | select(
.status == "completed"
and .conclusion != "success"
and .conclusion != "skipped"
and .conclusion != "neutral"
) | "\(.workflowName) [\(.event)]: \(.conclusion) — \(.url)"' \
<<< "$relevant")
if [ -n "$failed" ]; then
echo "::error::Sibling CI failed; VERSION will not advance."
printf '%s\n' "$failed"
exit 1
fi
pending=$(jq '[.[] | select(.status != "completed")] | length' \
<<< "$relevant")
total=$(jq 'length' <<< "$relevant")
echo "Sibling CI: $total discovered, $pending still running"
if [ "$pending" -eq 0 ]; then
settled_polls=$((settled_polls + 1))
# Three quiet polls prevent a just-created push/PR run from being
# missed before GitHub has indexed it for `gh run list`.
if [ "$settled_polls" -ge 3 ]; then
break
fi
else
settled_polls=0
fi
sleep 10
done
if [ "$SECONDS" -ge "$deadline" ]; then
echo "::error::Timed out waiting for sibling CI; VERSION will not advance."
exit 1
fi
- name: Update VERSION file
run: |
echo "${{ steps.new.outputs.version }}" > VERSION
cat VERSION
- name: Commit and push
id: commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add VERSION
git commit -m "chore: bump version to ${{ steps.new.outputs.version }} [skip ci]"
git push
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
# The bot push cannot fire Main Promotion either. Publish the exact
# required context only after sibling CI passed and an open same-repo
# dev -> main promotion was verified. This keeps strict branch protection
# attached to the generated head instead of the now-parent commit.
- name: Preserve the promotion gate on the generated commit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_SHA: ${{ steps.commit.outputs.sha }}
run: |
owner="${GITHUB_REPOSITORY%%/*}"
pr_number=$(gh api --method GET "repos/$GITHUB_REPOSITORY/pulls" \
-f state=open \
-f base=main \
-f head="$owner:dev" \
--jq '.[0].number // empty')
if [ -z "$pr_number" ]; then
echo "No open dev -> main promotion; no promotion status is needed."
exit 0
fi
gh api --method POST "repos/$GITHUB_REPOSITORY/statuses/$NEW_SHA" \
-f state=success \
-f context='Require dev source' \
-f description='Sibling CI passed; verified same-repository dev -> main promotion' \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
echo "Published Require dev source for PR #$pr_number at $NEW_SHA"
- name: Create summary
env:
# Same injection-safety reason as the bump step: keep the raw
# commit message out of the run script source.
TRIGGER_MSG: ${{ github.event.head_commit.message }}
run: |
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Previous Version | ${{ steps.current.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| New Version | ${{ steps.new.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| Bump Type | ${{ steps.bump.outputs.type }} |" >> $GITHUB_STEP_SUMMARY
# First line only, and via env — keeps a multi-line / special-char
# body from corrupting the markdown table or the script.
echo "| Trigger Commit | $(echo "$TRIGGER_MSG" | head -n1) |" >> $GITHUB_STEP_SUMMARY