Repository navigation
253 lines (234 loc) · 10.3 KB
/
Copy pathrelease_draft.yml
File metadata and controls
253 lines (234 loc) · 10.3 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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
name: "RELEASE: Make release candidate"
# Two ways to cut a release:
#
# 1. Automated (recommended): Actions -> "RELEASE: Make release candidate" ->
# "Run workflow". Pick the branch to release from (the "Use workflow from"
# dropdown), enter the version, and optionally a previous tag. This runs the
# FULL integration suite as a gate, then creates the empty "Release vX.Y.Z"
# commit (via an auto-merged PR), tags it, and runs GoReleaser.
#
# 2. Manual escape hatch: push a tag (git push origin vX.Y.Z). That still runs
# GoReleaser directly, exactly as before.
#
# Either way, GoReleaser produces a DRAFT release; you edit the notes and click
# publish.
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-*
workflow_dispatch:
inputs:
version:
description: "Version to release, e.g. v4.44.0 or v4.44.0-rc1"
required: true
type: string
previous_tag:
description: "Previous release tag for the changelog (blank = auto-detect the most recent non-RC release)"
required: false
default: ""
type: string
skip_longtest:
description: "Only do short-test"
required: false
default: false
type: boolean
# NOTE: the branch to release from is chosen via the "Use workflow from"
# dropdown (github.ref) — that is how "release from any branch" works.
# Read-only by default. The `release` job grants itself the write
# permissions it needs (contents + packages) at the job level; preflight
# and verify never needed the workflow-wide write scopes. pull-requests
# was removed entirely: job-level permissions fully replace the
# workflow-level block, so the release job never had it, and this org
# forbids GitHub Actions from creating pull requests anyway.
permissions:
contents: read
jobs:
# preflight: validate the dispatch inputs before doing any real work.
preflight:
name: preflight
if: github.repository == 'DNSControl/dnscontrol' && github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Validate inputs
env:
VERSION: ${{ inputs.version }}
PREVIOUS_TAG: ${{ inputs.previous_tag }}
run: |
set -eu
if ! printf '%s' "$VERSION" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$'; then
echo "::error::Invalid version '$VERSION'. Expected vX.Y.Z or vX.Y.Z-rcN."
exit 1
fi
if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then
echo "::error::Tag $VERSION already exists. Refusing to release."
exit 1
fi
if [ -n "$PREVIOUS_TAG" ] && ! git rev-parse -q --verify "refs/tags/$PREVIOUS_TAG" >/dev/null; then
echo "::error::previous_tag '$PREVIOUS_TAG' does not exist as a tag."
exit 1
fi
echo "Preflight OK: version=$VERSION branch=${GITHUB_REF_NAME} previous_tag=${PREVIOUS_TAG:-<auto>}"
# verify: run the FULL longtest integration suite as a release gate. Uses
# workflow_call, which GITHUB_TOKEN is allowed to trigger (no PAT needed).
# Skipped when the operator opts out via skip_longtest=true.
verify:
name: verify (full longtest)
if: github.repository == 'DNSControl/dnscontrol' && github.event_name == 'workflow_dispatch' && !inputs.skip_longtest
needs: preflight
uses: ./.github/workflows/pr_integration_tests.yml
with:
full: true
secrets: inherit
# release: create the tag (dispatch only) and run GoReleaser.
# Runs for a manual tag push (unchanged behavior) OR after a successful
# dispatch+verify.
release:
name: draft release
needs: [preflight, verify]
# Runs for: a manual tag push (unchanged), OR a dispatch where preflight
# passed AND (the longtest gate passed OR the operator opted out via
# skip_longtest). preflight must always pass, so an invalid version or an
# existing tag still blocks even when skip_longtest is set.
if: >-
always() &&
github.repository == 'DNSControl/dnscontrol' &&
( github.event_name == 'push' ||
( github.event_name == 'workflow_dispatch' &&
needs.preflight.result == 'success' &&
( needs.verify.result == 'success' || inputs.skip_longtest ) ) )
runs-on: ubuntu-latest
permissions:
packages: write
contents: write
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Checkout repo
uses: actions/checkout@v7
with:
fetch-depth: 0
# dispatch: the branch chosen in the UI; tag push: the pushed tag.
ref: ${{ github.ref }}
# Why "fetch-depth: 0"? To generate the release notes, we need the
# full git history. A shallow checkout would make release notes going
# back one commit.
# Make an opted-out longtest gate impossible to miss (annotation + summary).
- name: Warn if longtest gate was skipped
if: github.event_name == 'workflow_dispatch' && inputs.skip_longtest
run: |
echo "::warning title=longtest skipped::The full longtest integration gate was SKIPPED for ${{ inputs.version }} (skip_longtest=true)."
{
echo "## ⚠️ longtest integration gate SKIPPED"
echo ""
echo "Release \`${{ inputs.version }}\` was cut with **skip_longtest=true**"
} >> "$GITHUB_STEP_SUMMARY"
# DISPATCH ONLY: tag the tip of the selected branch and push the tag.
#
# We deliberately do NOT land an empty "Release vX.Y.Z" commit on the
# branch: main is protected (PRs required, enforced for admins) and this
# org forbids GitHub Actions from creating pull requests, so an
# automated, PAT-free PR is impossible. Tagging is unaffected by branch
# protection (no tag protection rules), and the changelog is still correct
# because GORELEASER_PREVIOUS_TAG is computed by version below (it does
# not rely on the tag sitting on its own commit). The annotated tag itself
# carries the "Release vX.Y.Z" message.
#
# Pushing the tag with GITHUB_TOKEN intentionally does NOT re-trigger this
# workflow's tag-push path, so there is no double release.
- name: Create and push tag
if: github.event_name == 'workflow_dispatch'
env:
VERSION: ${{ inputs.version }}
run: |
set -eu
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$VERSION" -m "Release ${VERSION}"
git push origin "refs/tags/${VERSION}"
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: "stable"
# Stringer is needed because .goreleaser includes "go generate ./..."
- name: Install stringer
run: |
go install golang.org/x/tools/cmd/stringer@latest
# Compute the changelog's current/previous tags ourselves.
#
# GoReleaser's default picks the previous tag by commit ancestry, which
# (a) breaks when a release and its release-candidate share a commit (it
# walks past both to an older release), and (b) makes a final release's
# notes span only back to its own -rc tag. Instead:
# * current tag = the version being released.
# * previous tag = the user-supplied `previous_tag` if given, otherwise
# the most recent NON-rc release older than the current version, so
# release notes cover the full range since the last stable release.
- name: Compute changelog previous/current tags
env:
EVENT: ${{ github.event_name }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_PREVIOUS_TAG: ${{ inputs.previous_tag }}
run: |
set -eu
if [ "$EVENT" = "workflow_dispatch" ]; then
cur="$INPUT_VERSION"
else
cur="$GITHUB_REF_NAME"
fi
echo "GORELEASER_CURRENT_TAG=$cur" >> "$GITHUB_ENV"
# Explicit override wins.
if [ -n "${INPUT_PREVIOUS_TAG:-}" ]; then
echo "GORELEASER_PREVIOUS_TAG=$INPUT_PREVIOUS_TAG" >> "$GITHUB_ENV"
echo "current=$cur previous=$INPUT_PREVIOUS_TAG (user-specified)"
exit 0
fi
# Auto-detect: list stable (non-prerelease) release tags plus the
# current base version, version-sort ascending, print the tag
# immediately before the base version.
base="${cur%%-*}" # strip any -rc suffix (v4.43.1-rc1 -> v4.43.1)
prev="$(
{ git tag --list 'v[0-9]*.[0-9]*.[0-9]*'; echo "$base"; } \
| awk '!/-/' \
| sort -V -u \
| awk -v base="$base" '$0==base{print prev; exit} {prev=$0}'
)"
if [ -n "$prev" ]; then
echo "GORELEASER_PREVIOUS_TAG=$prev" >> "$GITHUB_ENV"
echo "current=$cur previous=$prev (most recent non-rc release)"
else
echo "No prior non-rc release found for $cur; letting GoReleaser auto-detect."
fi
# use GoReleaser to build for all platforms.
- name: Goreleaser release
id: release
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: latest
args: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }}
MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }}
MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }}