@@ -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.
814on:
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.
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.
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
0 commit comments