Skip to content

Commit 3f50a2f

Browse files
committed
ci: trigger the release on unprefixed tags too
The 0.1.0 tag produced no packages because the workflow only listened for v*, so GitHub never started a run for it. The repository now has a tag of each kind — 0.1.0 and v0.0.1 — so both spellings trigger, and the version check compares whatever is left after an optional leading v. A manual run can now name an existing tag. It builds that tag rather than the branch and attaches the packages to its release, which is what 0.1.0 needs: the tag predates a working trigger, and its release already exists. For the same reason publishing no longer insists on creating the release. If one is already there the packages are uploaded to it and its notes are left as they are, so a run that dies after building can simply be run again.
1 parent 4c94edb commit 3f50a2f

2 files changed

Lines changed: 86 additions & 28 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,26 @@ name: release
22

33
# Publishes a GitHub release with a .deb per target distribution.
44
#
5-
# Pushing a v* tag builds the packages and publishes them. A manual run builds
6-
# the same packages and leaves them as workflow artifacts without publishing,
7-
# which is how the packaging path gets exercised without spending a tag.
5+
# Pushing a version tag builds the packages and publishes them. Both spellings
6+
# trigger, because the repository has tags of each kind: 0.1.0 and v0.1.0.
7+
#
8+
# A manual run with no tag builds the same packages and leaves them as workflow
9+
# artifacts without publishing, which is how the packaging path gets exercised
10+
# without spending a tag. A manual run naming an existing tag builds that tag
11+
# and attaches the packages to its release, which is how a tag that was pushed
12+
# before this workflow existed, or a run that died after building, gets its
13+
# packages.
814
on:
915
push:
10-
tags: ['v*']
16+
tags:
17+
- 'v[0-9]*'
18+
- '[0-9]*'
1119
workflow_dispatch:
1220
inputs:
21+
tag:
22+
description: 'Existing tag to build and publish (blank: build only, publish nothing)'
23+
required: false
24+
default: ''
1325
deb_revision:
1426
description: 'Debian revision (the -N in 0.1.0-N~deb13)'
1527
required: false
@@ -41,22 +53,42 @@ jobs:
4153
outputs:
4254
version: ${{ steps.check.outputs.version }}
4355
prerelease: ${{ steps.check.outputs.prerelease }}
56+
tag: ${{ steps.check.outputs.tag }}
57+
publish: ${{ steps.check.outputs.publish }}
4458
steps:
4559
- uses: actions/checkout@v4
60+
with:
61+
# Empty on a tag push, where the triggering tag is already the ref.
62+
ref: ${{ inputs.tag }}
4663

4764
- name: Compare the tag with Cargo.toml
4865
id: check
66+
env:
67+
INPUT_TAG: ${{ inputs.tag }}
4968
run: |
5069
set -euo pipefail
5170
version=$(awk -F'"' '/^version[[:space:]]*=/ {print $2; exit}' Cargo.toml)
5271
[ -n "${version}" ] || { echo "no version in Cargo.toml" >&2; exit 1; }
5372

54-
if [ "${GITHUB_REF_TYPE}" = tag ]; then
55-
tag_version="${GITHUB_REF_NAME#v}"
56-
if [ "${tag_version}" != "${version}" ]; then
57-
echo "::error::tag ${GITHUB_REF_NAME} does not match Cargo.toml version ${version}"
73+
# A tag push releases its own tag; a manual run releases the tag it
74+
# was given, and releases nothing if it was given none.
75+
if [ -n "${INPUT_TAG}" ]; then
76+
tag="${INPUT_TAG}"
77+
elif [ "${GITHUB_REF_TYPE}" = tag ]; then
78+
tag="${GITHUB_REF_NAME}"
79+
else
80+
tag=''
81+
fi
82+
83+
# Both 1.2.3 and v1.2.3 are accepted; the version is what is left.
84+
if [ -n "${tag}" ]; then
85+
publish=true
86+
if [ "${tag#v}" != "${version}" ]; then
87+
echo "::error::tag ${tag} does not match Cargo.toml version ${version}"
5888
exit 1
5989
fi
90+
else
91+
publish=false
6092
fi
6193

6294
# 0.2.0-rc1 and friends are published as pre-releases.
@@ -65,9 +97,13 @@ jobs:
6597
*) prerelease=false ;;
6698
esac
6799

68-
echo "version=${version}" >> "$GITHUB_OUTPUT"
69-
echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT"
70-
echo "natstream ${version} (prerelease=${prerelease}, revision=${DEB_REVISION})"
100+
{
101+
echo "version=${version}"
102+
echo "prerelease=${prerelease}"
103+
echo "tag=${tag}"
104+
echo "publish=${publish}"
105+
} >> "$GITHUB_OUTPUT"
106+
echo "natstream ${version} (tag=${tag:-none}, publish=${publish}, prerelease=${prerelease}, revision=${DEB_REVISION})"
71107

72108
# One job per target distribution, using the same ./build.sh a developer runs
73109
# locally, so there is no second packaging path that can drift.
@@ -87,6 +123,8 @@ jobs:
87123
dist_tag: ubuntu26.04
88124
steps:
89125
- uses: actions/checkout@v4
126+
with:
127+
ref: ${{ inputs.tag }}
90128

91129
- name: Build the package
92130
run: ./build.sh ${{ matrix.target }}
@@ -121,8 +159,8 @@ jobs:
121159
publish:
122160
name: publish release
123161
needs: [version, package]
124-
# Only a tag publishes; a manual run stops after the packages are built.
125-
if: github.ref_type == 'tag'
162+
# A manual run with no tag stops after the packages are built.
163+
if: needs.version.outputs.publish == 'true'
126164
runs-on: ubuntu-latest
127165
permissions:
128166
contents: write
@@ -160,20 +198,31 @@ jobs:
160198
`systemctl enable --now natstream.service`.
161199
NOTES
162200

163-
- name: Create the release
201+
# Uploading to a release that already exists rather than failing on it:
202+
# the tag may have been released by hand, or by a run that built the
203+
# packages and then died before attaching them. The existing release's
204+
# notes are left alone; only the assets are replaced.
205+
- name: Publish the packages
164206
env:
165207
GH_TOKEN: ${{ github.token }}
166208
VERSION: ${{ needs.version.outputs.version }}
167209
PRERELEASE: ${{ needs.version.outputs.prerelease }}
210+
TAG: ${{ needs.version.outputs.tag }}
168211
run: |
169212
set -euo pipefail
170-
args=(
171-
--repo "${GITHUB_REPOSITORY}"
172-
--title "NatStream ${VERSION}"
173-
--notes-file notes.md
174-
--generate-notes
175-
)
176-
if [ "${PRERELEASE}" = true ]; then
177-
args+=(--prerelease)
213+
if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
214+
echo "release ${TAG} exists; uploading packages to it"
215+
gh release upload "${TAG}" --repo "${GITHUB_REPOSITORY}" --clobber \
216+
dist/*.deb dist/SHA256SUMS
217+
else
218+
args=(
219+
--repo "${GITHUB_REPOSITORY}"
220+
--title "NatStream ${VERSION}"
221+
--notes-file notes.md
222+
--generate-notes
223+
)
224+
if [ "${PRERELEASE}" = true ]; then
225+
args+=(--prerelease)
226+
fi
227+
gh release create "${TAG}" "${args[@]}" dist/*.deb dist/SHA256SUMS
178228
fi
179-
gh release create "${GITHUB_REF_NAME}" "${args[@]}" dist/*.deb dist/SHA256SUMS

‎README.md‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,14 @@ target with `./build.sh`, then publishes them to a GitHub release:
120120

121121
```bash
122122
# Cargo.toml's version must already say 0.2.0 on the commit being tagged.
123-
git tag -a v0.2.0 -m 'NatStream 0.2.0'
124-
git push origin v0.2.0
123+
git tag -a 0.2.0 -m 'NatStream 0.2.0'
124+
git push origin 0.2.0
125125
```
126126

127-
The workflow refuses to build if the tag and `Cargo.toml` disagree, so a
127+
`0.2.0` and `v0.2.0` both trigger it, since the repository has tags of each
128+
kind. The workflow refuses to build if the tag and `Cargo.toml` disagree, so a
128129
mismatch costs a failed job rather than a wrong package. A version with a
129-
suffix (`v0.2.0-rc1`) is published as a pre-release.
130+
suffix (`0.2.0-rc1`) is published as a pre-release.
130131

131132
Before publishing, the Ubuntu 24.04 package is installed and purged on the
132133
runner, which is the same distribution: a maintainer script that fails, a
@@ -135,7 +136,15 @@ build. The release carries the packages and a `SHA256SUMS` over them.
135136

136137
Running the workflow by hand from the Actions tab builds all three packages and
137138
leaves them as workflow artifacts without publishing anything, which is how to
138-
exercise the packaging path without spending a tag.
139+
exercise the packaging path without spending a tag. Giving it a tag instead
140+
builds that tag and attaches the packages to its release, which is how a tag
141+
pushed before this workflow existed gets its packages:
142+
143+
```bash
144+
gh workflow run release.yml --ref master -f tag=0.1.0
145+
```
146+
147+
An existing release keeps its notes; only the packages are attached.
139148

140149
Set the `DEB_MAINTAINER` repository variable to a real `Name <email>`;
141150
without it the packages carry the placeholder maintainer from

0 commit comments

Comments
 (0)