-
Notifications
You must be signed in to change notification settings - Fork 1
177 lines (163 loc) · 5.96 KB
/
release.yml
File metadata and controls
177 lines (163 loc) · 5.96 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
name: Release
on:
workflow_dispatch:
inputs:
tag:
description: 'SemVer tag to release (e.g. v0.1.0)'
required: true
type: string
default: 'v0.1.0'
# Allows releases when workflow_dispatch is unavailable (e.g. narrow GitHub tokens):
# create an annotated tag on main and push it — GoReleaser runs for that tag.
push:
tags:
- 'v*.*.*'
permissions:
contents: read
jobs:
tag:
name: Create tag
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require main branch
run: |
if [ "${GITHUB_REF}" != "refs/heads/main" ]; then
echo "Release workflow must be run from main; got ${GITHUB_REF}."
exit 1
fi
- name: Validate version format
env:
TAG: ${{ inputs.tag }}
run: |
if ! echo "$TAG" | grep -qE '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then
echo "Invalid tag format: $TAG"
echo "Expected: vMAJOR.MINOR.PATCH"
exit 1
fi
- name: Ensure tag is reusable or create it
id: tag
env:
TAG: ${{ inputs.tag }}
run: |
HEAD_SHA="$(git rev-parse HEAD)"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
TAG_SHA="$(git rev-list -n1 "$TAG")"
echo "Tag already exists locally: $TAG ($TAG_SHA)"
exit 1
else
REMOTE_SHA="$(git ls-remote --tags origin "refs/tags/$TAG" | awk '{print $1}' | head -n1)"
if [ -n "$REMOTE_SHA" ]; then
echo "Tag already exists on origin: $TAG ($REMOTE_SHA)"
exit 1
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${TAG}" -m "Release ${TAG}"
git push origin "refs/tags/${TAG}"
fi
fi
echo "tag=${TAG}" >> $GITHUB_OUTPUT
goreleaser:
name: GoReleaser
needs:
- tag
# Run after workflow_dispatch tagging succeeds, or when a release tag was pushed
# (the tag job is skipped on push; skipped needs require always() so this if is evaluated).
# always() && !cancelled(): respect workflow cancellation.
# Push path: only initial tag creation, not delete/force-push.
if: |
always() && !cancelled() &&
(github.event_name != 'push' || (github.event.created && !github.event.deleted && !github.event.forced)) &&
(needs.tag.result == 'success' ||
(github.event_name == 'push' && needs.tag.result == 'skipped'))
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: >-
${{
github.event_name == 'workflow_dispatch'
&& needs.tag.outputs.tag
|| github.ref
}}
- name: Validate pushed tag format
if: github.event_name == 'push'
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
if ! echo "$TAG" | grep -qE '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then
echo "Invalid tag format: $TAG"
echo "Expected: vMAJOR.MINOR.PATCH"
exit 1
fi
- name: Determine release tag
id: release_meta
env:
EVENT_NAME: ${{ github.event_name }}
TAG_WORKFLOW_DISPATCH: ${{ needs.tag.outputs.tag }}
TAG_PUSH: ${{ github.ref_name }}
run: |
set -euo pipefail
if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
TAG="${TAG_WORKFLOW_DISPATCH}"
else
TAG="${TAG_PUSH}"
fi
echo "release_tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Require pushed tag to target mainline
if: github.event_name == 'push'
run: |
set -euo pipefail
TAG_COMMIT="$(git rev-parse HEAD)"
git fetch origin "refs/heads/main:refs/remotes/origin/main"
if ! git merge-base --is-ancestor "$TAG_COMMIT" "origin/main"; then
echo "Release tag must point to a commit reachable from origin/main (not a feature-branch-only SHA)."
exit 1
fi
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --config .goreleaser.stable.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
- name: Verify Homebrew formula publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ steps.release_meta.outputs.release_tag }}
run: |
set -euo pipefail
RELEASE_VERSION="${RELEASE_TAG#v}"
for attempt in $(seq 1 18); do
FORMULA_B64="$(gh api repos/git-fire/homebrew-tap/contents/Formula/git-rain.rb --jq '.content' 2>/dev/null || true)"
if [ -n "$FORMULA_B64" ]; then
FORMULA_CONTENT="$(printf '%s' "$FORMULA_B64" | tr -d '\n' | base64 -d)"
if printf '%s' "$FORMULA_CONTENT" | grep -q "version \"${RELEASE_VERSION}\"" && \
printf '%s' "$FORMULA_CONTENT" | grep -q "/download/${RELEASE_TAG}/"; then
echo "Homebrew formula updated for ${RELEASE_TAG}."
exit 0
fi
fi
echo "Homebrew formula not updated for ${RELEASE_TAG} yet (attempt ${attempt}/18)."
sleep 10
done
echo "Homebrew formula did not update for ${RELEASE_TAG}; failing release gate."
exit 1