Release on every push to master, and make updating possible
A dsh plugin installed as a bare git URL is pinned to a commit in the profile lockfile. With no tags in the repository there was nothing for an update to move to, which is why updating appeared to do nothing and a reinstall was the only way through. Gitea action on push to master: work out the range since the last tag, decide whether the push releases at all, bump package.json, prepend a CHANGELOG section, commit, tag vX.Y.Z, and publish the release with those notes as the body. The bump is a patch unless a commit says [minor] or [major]; [skip release] publishes nothing. With no tags yet the declared version is what gets tagged, so the first release matches the code. Notes come from commit subjects, grouped under headings only when the range actually uses conventional prefixes. The logic is a script rather than inline YAML so it runs without a runner: npm run test:release covers 31 cases, including an empty range, a repository with no tags, and a patch number already at 9. The empty-range guard is load-bearing: the release commit is itself a push to master. Two bugs the tests caught: an empty note section made '[ -n x ] && printf' return non-zero, which under set -e aborted the whole release, and a bullet beginning with '-' was read by grep as an option. README documents the #semver: spec, which tracks releases rather than every commit, and the two things that look like a failed update: an unfetchable remote, and the profile restart client bundle bytes require.
This commit is contained in:
1 parent
5ff10eaefd
commit
e0813bcbea
6 files changed
+613
-2
No files matched your search
@@ -0,0 +1,121 @@
|
||||
# Publish a release every time master moves.
|
||||
#
|
||||
# What this is for, beyond tidiness: a dsh plugin installed as a bare
|
||||
# `git+https://…/repo.git` is resolved to a commit and pinned in the profile's
|
||||
# lockfile. With no tags, there is nothing for `dsh plugin update` to move to —
|
||||
# which is why updating appeared to do nothing and a reinstall was the only way
|
||||
# through. Tags are what make an update possible; this workflow is what creates
|
||||
# them.
|
||||
#
|
||||
# The work lives in `.gitea/scripts/release.sh` so it can be run and tested
|
||||
# without a runner (`bash .gitea/scripts/release.test.sh`).
|
||||
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
# Manual runs, for the first release or to re-cut one after a failed push.
|
||||
workflow_dispatch:
|
||||
|
||||
# One release at a time. Two pushes in quick succession would otherwise race to
|
||||
# tag the same commit and one of them would lose.
|
||||
concurrency:
|
||||
group: release-master
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
BRANCH: master
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Tags are the whole point, so the clone must carry them.
|
||||
fetch-depth: 0
|
||||
ref: master
|
||||
|
||||
- name: Decide whether this push releases
|
||||
id: decide
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="$(bash .gitea/scripts/release.sh last-tag)"
|
||||
range="$(bash .gitea/scripts/release.sh range)"
|
||||
echo "range=${range}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
verdict="$(bash .gitea/scripts/release.sh should-release "$range")"
|
||||
echo "release=${verdict}" >> "$GITHUB_OUTPUT"
|
||||
echo "last tag: ${tag:-none} range: ${range:-everything} release: ${verdict}"
|
||||
|
||||
- name: Work out the version and the notes
|
||||
if: steps.decide.outputs.release == 'yes'
|
||||
id: version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bump="$(bash .gitea/scripts/release.sh bump "${{ steps.decide.outputs.range }}")"
|
||||
version="$(bash .gitea/scripts/release.sh next-version "$bump")"
|
||||
bash .gitea/scripts/release.sh notes "${{ steps.decide.outputs.range }}" > /tmp/notes.md
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
echo "bump=${bump}" >> "$GITHUB_OUTPUT"
|
||||
echo "releasing v${version} (${bump})"
|
||||
cat /tmp/notes.md
|
||||
|
||||
- name: Bump, changelog, commit and tag
|
||||
if: steps.decide.outputs.release == 'yes'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "release bot"
|
||||
git config user.email "release@notexpectedyet.com"
|
||||
bash .gitea/scripts/release.sh set-version "$VERSION"
|
||||
bash .gitea/scripts/release.sh changelog-prepend "$VERSION" "$(date -u +%Y-%m-%d)" /tmp/notes.md
|
||||
git add package.json CHANGELOG.md
|
||||
# The marker is the second line of defence against releasing this very
|
||||
# commit: the script also refuses an empty range.
|
||||
git commit -m "Release v${VERSION} [skip release]"
|
||||
git tag "v${VERSION}"
|
||||
git push origin "HEAD:${BRANCH}"
|
||||
git push origin "v${VERSION}"
|
||||
|
||||
- name: Publish the release
|
||||
if: steps.decide.outputs.release == 'yes'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
SERVER: ${{ github.server_url }}
|
||||
REPO: ${{ github.repository }}
|
||||
TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The body is built by an encoder rather than by string interpolation:
|
||||
# release notes are multi-line markdown and quoting them into JSON by
|
||||
# hand is a reliable way to publish a malformed release.
|
||||
python3 - <<'PY' > /tmp/release.json
|
||||
import json, os
|
||||
with open('/tmp/notes.md', encoding='utf-8') as handle:
|
||||
body = handle.read()
|
||||
version = os.environ['VERSION']
|
||||
print(json.dumps({
|
||||
'tag_name': 'v' + version,
|
||||
'name': 'v' + version,
|
||||
'body': body,
|
||||
'draft': False,
|
||||
'prerelease': False,
|
||||
}))
|
||||
PY
|
||||
|
||||
status="$(curl -sS -o /tmp/release-response.json -w '%{http_code}' \
|
||||
-X POST "${SERVER}/api/v1/repos/${REPO}/releases" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-binary @/tmp/release.json)"
|
||||
|
||||
if [ "$status" != "201" ] && [ "$status" != "200" ]; then
|
||||
echo "release creation failed (HTTP ${status})" >&2
|
||||
cat /tmp/release-response.json >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "published v${VERSION}"
|
||||
Reference in new issue
Block a user