-
Notifications
You must be signed in to change notification settings - Fork 0
238 lines (217 loc) · 8.54 KB
/
Copy pathrelease.yml
File metadata and controls
238 lines (217 loc) · 8.54 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
name: release
# Publishes a GitHub release with a .deb per target distribution.
#
# Pushing a version tag builds the packages and publishes them. Both spellings
# trigger, because the repository has tags of each kind: 0.1.0 and v0.1.0.
#
# A manual run with no tag builds the same packages and leaves them as workflow
# artifacts without publishing, which is how the packaging path gets exercised
# without spending a tag. A manual run naming an existing tag builds that tag
# and attaches the packages to its release, which is how a tag that was pushed
# before this workflow existed, or a run that died after building, gets its
# packages.
on:
push:
tags:
- 'v[0-9]*'
- '[0-9]*'
workflow_dispatch:
inputs:
tag:
description: 'Existing tag to build and publish (blank: build only, publish nothing)'
required: false
default: ''
deb_revision:
description: 'Debian revision (the -N in 0.1.0-N~deb13)'
required: false
default: '1'
# The workflow only reads the repository; the publish job asks for write.
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
# The Maintainer field of every package built here. Set the DEB_MAINTAINER
# repository variable to a real "Name <email>"; without it the packages carry
# the placeholder from packaging/build-deb.sh.
MAINTAINER: ${{ vars.DEB_MAINTAINER }}
DEB_REVISION: ${{ inputs.deb_revision || '1' }}
# One release at a time, and never cancel a run that may already have published.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# A tag that disagrees with Cargo.toml would produce packages whose version
# is not the one being released, so stop before anything is built.
version:
name: check version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.check.outputs.version }}
prerelease: ${{ steps.check.outputs.prerelease }}
tag: ${{ steps.check.outputs.tag }}
publish: ${{ steps.check.outputs.publish }}
steps:
- uses: actions/checkout@v4
with:
# Empty on a tag push, where the triggering tag is already the ref.
ref: ${{ inputs.tag }}
- name: Compare the tag with Cargo.toml
id: check
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
version=$(awk -F'"' '/^version[[:space:]]*=/ {print $2; exit}' Cargo.toml)
[ -n "${version}" ] || { echo "no version in Cargo.toml" >&2; exit 1; }
# A tag push releases its own tag; a manual run releases the tag it
# was given, and releases nothing if it was given none.
if [ -n "${INPUT_TAG}" ]; then
tag="${INPUT_TAG}"
elif [ "${GITHUB_REF_TYPE}" = tag ]; then
tag="${GITHUB_REF_NAME}"
else
tag=''
fi
# Both 1.2.3 and v1.2.3 are accepted; the version is what is left.
if [ -n "${tag}" ]; then
publish=true
if [ "${tag#v}" != "${version}" ]; then
echo "::error::tag ${tag} does not match Cargo.toml version ${version}"
exit 1
fi
else
publish=false
fi
# 0.2.0-rc1 and friends are published as pre-releases.
case "${version}" in
*-*) prerelease=true ;;
*) prerelease=false ;;
esac
{
echo "version=${version}"
echo "prerelease=${prerelease}"
echo "tag=${tag}"
echo "publish=${publish}"
} >> "$GITHUB_OUTPUT"
echo "natstream ${version} (tag=${tag:-none}, publish=${publish}, prerelease=${prerelease}, revision=${DEB_REVISION})"
# One job per target distribution, using the same ./build.sh a developer runs
# locally, so there is no second packaging path that can drift.
package:
name: ${{ matrix.target }}
needs: version
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- target: trixie
dist_tag: deb13
- target: '24.04'
dist_tag: ubuntu24.04
- target: '26.04'
dist_tag: ubuntu26.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}
- name: Build the package
run: ./build.sh ${{ matrix.target }}
# The runner is Ubuntu 24.04, so it can install that target's package for
# real: this catches a broken maintainer script or a binary linked against
# a glibc the distro does not have, which dpkg-deb --info would not.
- name: Install and remove it
if: matrix.dist_tag == 'ubuntu24.04'
run: |
set -euo pipefail
deb=$(ls dist/natstream_*~ubuntu24.04_*.deb)
sudo dpkg -i "${deb}"
/usr/sbin/natstream --version
# A fresh install must leave the service alone; enabling it without a
# collector configured would only produce a restart loop.
if systemctl is-enabled --quiet natstream.service; then
echo "::error::a fresh install enabled natstream.service"
exit 1
fi
sudo dpkg --purge natstream
test ! -e /usr/sbin/natstream
test ! -e /etc/default/natstream
- uses: actions/upload-artifact@v4
with:
name: deb-${{ matrix.dist_tag }}
path: dist/*.deb
if-no-files-found: error
retention-days: 14
publish:
name: publish release
needs: [version, package]
# A manual run with no tag stops after the packages are built.
if: needs.version.outputs.publish == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
pattern: deb-*
merge-multiple: true
# GitHub rewrites ~ to . in an uploaded asset's filename, so the packages
# are renamed to the names they will actually carry on the release before
# they are checksummed. Otherwise SHA256SUMS names files that cannot be
# downloaded and `sha256sum -c` fails on every line. The ~ stays where it
# has meaning, in the package's own Version field, which dpkg reads from
# the control file and not from the filename.
- name: Checksums
working-directory: dist
run: |
set -euo pipefail
for deb in *.deb; do
sanitized="${deb//\~/.}"
[ "${deb}" = "${sanitized}" ] || mv -- "${deb}" "${sanitized}"
done
sha256sum -- *.deb | tee SHA256SUMS
# The hand-written part of the notes; gh appends the generated commit and
# contributor list to it.
- name: Release notes
run: |
cat > notes.md <<'NOTES'
## Packages
| Distribution | Package |
|---|---|
| Debian 13 (trixie) | `natstream_*~deb13_amd64.deb` |
| Ubuntu 24.04 LTS | `natstream_*~ubuntu24.04_amd64.deb` |
| Ubuntu 26.04 LTS | `natstream_*~ubuntu26.04_amd64.deb` |
Each is built inside a container based on its own distribution, so it
links that distribution's glibc. `SHA256SUMS` covers all of them.
Installing does not enable the service: set `COLLECTOR` in
`/etc/default/natstream` first, then
`systemctl enable --now natstream.service`.
NOTES
# Uploading to a release that already exists rather than failing on it:
# the tag may have been released by hand, or by a run that built the
# packages and then died before attaching them. The existing release's
# notes are left alone; only the assets are replaced.
- name: Publish the packages
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.version.outputs.version }}
PRERELEASE: ${{ needs.version.outputs.prerelease }}
TAG: ${{ needs.version.outputs.tag }}
run: |
set -euo pipefail
if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
echo "release ${TAG} exists; uploading packages to it"
gh release upload "${TAG}" --repo "${GITHUB_REPOSITORY}" --clobber \
dist/*.deb dist/SHA256SUMS
else
args=(
--repo "${GITHUB_REPOSITORY}"
--title "NatStream ${VERSION}"
--notes-file notes.md
--generate-notes
)
if [ "${PRERELEASE}" = true ]; then
args+=(--prerelease)
fi
gh release create "${TAG}" "${args[@]}" dist/*.deb dist/SHA256SUMS
fi