-
Notifications
You must be signed in to change notification settings - Fork 2
363 lines (312 loc) · 12 KB
/
Copy pathrelease.yml
File metadata and controls
363 lines (312 loc) · 12 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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
defaults:
run:
shell: bash
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
build-and-package:
runs-on: ubuntu-24.04
container:
image: debian:13@sha256:2477d9ee0ead4370c778ce3aa42258a0b07684d1a84ded8f4af518383fbc3f2d # debian:13 linux/amd64
env:
NFPM_VERSION: v2.46.3
outputs:
version: ${{ steps.release.outputs.version }}
tag: ${{ steps.release.outputs.tag }}
commit: ${{ steps.release.outputs.commit }}
steps:
- name: Install workflow prerequisites
run: |
set -euo pipefail
apt-get update
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
git \
golang-go \
libclang-dev \
libpipewire-0.3-dev \
libwayland-dev \
libx11-dev \
libx11-xcb-dev \
libxext-dev \
libxfixes-dev \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libxinerama-dev \
libxkbcommon-dev \
libfontconfig1-dev \
libfreetype6-dev \
libvulkan-dev \
pkg-config \
xz-utils \
zstd
rm -rf /var/lib/apt/lists/*
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- name: Mark workspace as safe for Git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Validate release tag and prepare notes
id: release
env:
TAG_NAME: ${{ github.ref_name }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
cargo_value() {
local key="$1"
awk -F'"' -v key="$key" '
/^[[:space:]]*\[package\][[:space:]]*$/ { in_pkg = 1; next }
/^[[:space:]]*\[/ { in_pkg = 0 }
in_pkg && $0 ~ "^[[:space:]]*" key "[[:space:]]*=" { print $2; exit }
'
}
version="$(cargo_value version < Cargo.toml)"
if [ -z "$version" ]; then
echo "Could not parse package.version from Cargo.toml" >&2
exit 1
fi
tag="${TAG_NAME}"
expected_tag="v${version}"
if [ "$tag" != "$expected_tag" ]; then
echo "Release tag ${tag} does not match Cargo.toml package.version (${version}); expected ${expected_tag}." >&2
exit 1
fi
head_commit="$(git rev-parse HEAD)"
tag_commit="$(git rev-parse "${tag}^{commit}")"
if [ "$tag_commit" != "$head_commit" ]; then
echo "Release tag ${tag} points at ${tag_commit}, but checkout is ${head_commit}." >&2
exit 1
fi
default_branch="${DEFAULT_BRANCH:-main}"
default_branch_ref="origin/${default_branch}"
if ! git rev-parse --verify "${default_branch_ref}^{commit}" >/dev/null; then
echo "Default branch ref ${default_branch_ref} is unavailable from checkout." >&2
exit 1
fi
if ! git merge-base --is-ancestor "$head_commit" "$default_branch_ref"; then
echo "Release commit ${head_commit} is not contained in ${default_branch_ref}." >&2
exit 1
fi
rust_toolchain="$(cargo_value rust-version < Cargo.toml)"
rust_toolchain="${rust_toolchain:-stable}"
previous_tag="$(git tag --merged "$head_commit" --sort=-v:refname --list 'v[0-9]*.[0-9]*.[0-9]*' \
| grep -Fvx "$tag" \
| head -n 1 || true)"
changelog_range="${head_commit}"
if [ -n "$previous_tag" ] && git rev-parse "${previous_tag}^{commit}" >/dev/null 2>&1; then
changelog_range="${previous_tag}..${head_commit}"
fi
changelog="$(git log --no-merges --pretty=format:'%h %s' "$changelog_range" 2>/dev/null || true)"
if [ -z "$changelog" ]; then
changelog="(no changelog entries)"
fi
notes_file="release-notes.md"
{
echo "## Changelog"
echo
echo '```'
printf '%s\n' "$changelog"
echo '```'
} > "$notes_file"
{
echo "version=${version}"
echo "rust_toolchain=${rust_toolchain}"
echo "tag=${tag}"
echo "commit=${head_commit}"
echo "notes_file=${notes_file}"
} >> "$GITHUB_OUTPUT"
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: ${{ steps.release.outputs.rust_toolchain }}
components: rustfmt, clippy
- name: Check formatting
run: cargo fmt --all -- --check
- name: Cache Cargo builds
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
- name: Lint
run: cargo clippy --workspace --locked --all-targets -- -D warnings
- name: Test
run: cargo test --workspace --locked --all-targets
- name: Locate Go caches
id: go-cache
run: |
set -euo pipefail
{
echo "build=$(go env GOCACHE)"
echo "modules=$(go env GOMODCACHE)"
echo "version=$(go env GOVERSION)"
} >> "$GITHUB_OUTPUT"
- name: Cache Go builds
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ steps.go-cache.outputs.build }}
${{ steps.go-cache.outputs.modules }}
key: debian-13-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-nfpm-${{ env.NFPM_VERSION }}
- name: Build and package release assets
run: |
set -euo pipefail
glibc_version="$(ldd --version | awk 'NR==1 {print $NF}')"
echo "glibc version: ${glibc_version}"
if ! dpkg --compare-versions "${glibc_version}" ge 2.41; then
echo "Debian release pipeline requires glibc >= 2.41"
exit 1
fi
go install "github.com/goreleaser/nfpm/v2/cmd/nfpm@${NFPM_VERSION}"
export PATH="${HOME}/go/bin:${PATH}"
cargo build --locked --release
make -C packaging all
version="${{ steps.release.outputs.version }}"
dist_dir="${PWD}/dist"
shopt -s nullglob
assets=(
"${dist_dir}"/openmeters-"${version}"-*.tar.xz
"${dist_dir}"/openmeters_"${version}"-*.deb
"${dist_dir}"/openmeters-"${version}"-*.rpm
"${dist_dir}"/SHA256SUMS
)
shopt -u nullglob
if [ "${#assets[@]}" -ne 4 ]; then
echo "Expected 4 release artifacts in ${dist_dir}, found ${#assets[@]}." >&2
for asset in "${assets[@]}"; do
echo " ${asset}" >&2
done
exit 1
fi
- name: Upload release assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets
path: |
dist/openmeters-${{ steps.release.outputs.version }}-*.tar.xz
dist/openmeters_${{ steps.release.outputs.version }}-*.deb
dist/openmeters-${{ steps.release.outputs.version }}-*.rpm
dist/SHA256SUMS
if-no-files-found: error
retention-days: 7
- name: Upload release notes
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-notes
path: ${{ steps.release.outputs.notes_file }}
if-no-files-found: error
retention-days: 7
publish:
needs: build-and-package
runs-on: ubuntu-24.04
permissions:
contents: write
env:
VERSION: ${{ needs.build-and-package.outputs.version }}
TAG_NAME: ${{ needs.build-and-package.outputs.tag }}
COMMIT: ${{ needs.build-and-package.outputs.commit }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GH_REPO: ${{ github.repository }}
steps:
- name: Validate remote release target
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
tag_ref="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG_NAME}" --jq '.object.type + " " + .object.sha')"
read -r object_type object_sha <<< "$tag_ref"
case "$object_type" in
commit)
tag_commit="$object_sha"
;;
tag)
tag_target="$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${object_sha}" --jq '.object.type + " " + .object.sha')"
read -r target_type tag_commit <<< "$tag_target"
if [ "$target_type" != "commit" ]; then
echo "Release tag ${TAG_NAME} ultimately points at a ${target_type}, not a commit." >&2
exit 1
fi
;;
*)
echo "Release tag ${TAG_NAME} points at unsupported git object type ${object_type}." >&2
exit 1
;;
esac
if [ "$tag_commit" != "$COMMIT" ]; then
echo "Remote release tag ${TAG_NAME} points at ${tag_commit}, but validated build commit is ${COMMIT}." >&2
exit 1
fi
default_branch="${DEFAULT_BRANCH:-main}"
compare_status="$(gh api "repos/${GITHUB_REPOSITORY}/compare/${COMMIT}...${default_branch}" --jq '.status')"
case "$compare_status" in
identical|ahead)
;;
*)
echo "Default branch ${default_branch} does not contain release commit ${COMMIT}; compare status: ${compare_status}." >&2
exit 1
;;
esac
- name: Download release assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-assets
path: dist
- name: Download release notes
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-notes
path: release-notes
- name: Publish GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
notes_file="${PWD}/release-notes/release-notes.md"
if [ ! -s "$notes_file" ]; then
echo "Release notes file ${notes_file} is missing or empty." >&2
exit 1
fi
dist_dir="${PWD}/dist"
(cd "$dist_dir" && sha256sum --check SHA256SUMS)
shopt -s nullglob
assets=(
"${dist_dir}"/openmeters-"${VERSION}"-*.tar.xz
"${dist_dir}"/openmeters_"${VERSION}"-*.deb
"${dist_dir}"/openmeters-"${VERSION}"-*.rpm
"${dist_dir}"/SHA256SUMS
)
shopt -u nullglob
if [ "${#assets[@]}" -ne 4 ]; then
echo "Expected 4 downloaded release artifacts in ${dist_dir}, found ${#assets[@]}." >&2
for asset in "${assets[@]}"; do
echo " ${asset}" >&2
done
exit 1
fi
if is_draft="$(gh release view "$TAG_NAME" --json isDraft --jq .isDraft 2>/dev/null)"; then
if [ "$is_draft" = "true" ]; then
echo "Deleting existing draft release ${TAG_NAME} before recreating it."
gh release delete "$TAG_NAME" --yes
else
echo "Release ${TAG_NAME} already exists and is not a draft; refusing to overwrite it." >&2
exit 1
fi
fi
gh release create "$TAG_NAME" \
--draft \
--target "$COMMIT" \
--title "openmeters ${VERSION}" \
--notes-file "$notes_file" \
--verify-tag
gh release upload "$TAG_NAME" "${assets[@]}"
gh release edit "$TAG_NAME" --draft=false