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:
James Mackay committed 2026-09-25 19:03:50 +01:00
1 parent 5ff10eaefd
commit e0813bcbea
6 files changed
+613 -2

No files matched your search

+60
View File
@@ -309,6 +309,66 @@ it but does cover the wiring it cannot work without.
declared dependency — an out-of-tree bundle that depended on an in-box package
would install a second copy that drifts from the host's.
## Releases and updating
### The workflow
`.gitea/workflows/release.yml` runs on every push to `master`:
1. works out the range since the last tag;
2. decides whether the push releases at all;
3. bumps `package.json`, prepends a `CHANGELOG.md` section, commits, and tags
`vX.Y.Z`;
4. creates the Gitea release with those notes as the body.
The bump is a patch unless a commit in the release says otherwise:
| in a commit subject | effect |
|---|---|
| `[minor]` | release as a minor version |
| `[major]` | release as a major version |
| `[skip release]` | this push publishes nothing |
With no tags yet, the version already in `package.json` is what gets tagged — the
first release should match what the code says, not invent a number.
Release notes come from the commit subjects in the range. Conventional prefixes
(`feat:`, `fix:`, `docs:`) are grouped under headings when at least half the
commits carry one; otherwise the notes are a flat list. Prose commit subjects get
no invented headings.
The logic lives in `.gitea/scripts/release.sh` so it runs without a runner, and
`npm run test:release` exercises it — 31 checks, including an empty range, a
repository with no tags yet, and a patch number already at 9.
### Making `dsh plugin update` work
A plugin installed as a bare `git+https://…/repo.git` is resolved to a **commit**
and pinned in the profile's lockfile. Verified against a local remote: `pnpm
update <name>` *does* move that to the branch head, so the mechanism is not
broken — but it tracks every commit, including half-finished ones, and it needs
fetch access to the remote.
Adding a version selector makes it track **releases** instead:
```jsonc
// $DSH_HOME/profiles/<profile>/package.json
"dsh-slopo": "git+https://git.notexpectedyet.com/Not-Expected-Yet/dsh-slopo.git#semver:^0.1.0"
```
`#semver:^0.1.0` resolves the highest tag in range. Verified locally: installed
`v0.1.1` where the branch was already at `v0.1.2`-plus-untagged-work, and
`pnpm update` then moved it to a newly pushed `v0.1.2`.
Two things to know, both of which look like "the update did nothing":
- **The remote must be fetchable.** `pnpm update` cannot see a new tag it cannot
fetch. If `git ls-remote <url>` asks for a username, fix that first — the same
credential push needs.
- **A restart is required.** Client bundle bytes are snapshotted when the profile
activates, so a hot update installs new files but keeps serving the old ones
until `dsh --profile <profile>` comes back up.
## Licence
AGPL-3.0-or-later, matching slopo. The three skill bodies are ports of slopo's