Files
LLeMbas/deploy/lembas-update.service
T
Jaroslav Beneš 0c438a6333 A version somebody can read, instead of a sha nobody can
Updates follow a channel now. `stable` is the newest vX.Y.Z tag; `edge` is the
branch tip, which is what this did before. Stable is the default, because a
branch tip is not a release -- following one means deploying whatever was pushed
five minutes ago, possibly mid-feature, which is right for whoever builds this
and wrong for whoever runs it. The page can now say "running 1.0.0, 1.1.0
available" rather than showing two shas and leaving somebody to guess.

Read with git plumbing and never a forge API, for three reasons in the order
they bite. It would need a token on the deployment host -- a credential that can
reach the repository, sitting on a box, to answer a read-only question about
version numbers. It would tie this to one forge, so a fork on GitHub gets
nothing. And it breaks: checked against the Gitea this is developed on, `tea
whoami` works and `tea releases list` returns a 500 from a server-side panic
about token scopes, so a page resting on that endpoint would have shipped
already broken.

Release notes still travel, inside the annotated tag object, which
`git for-each-ref` reads with no API anywhere.

Two details that are only obvious after getting them wrong. A tag with a suffix
is not a release: git's version sort puts v1.1.0-rc1 *above* v1.1.0, so
accepting one would step a stable host onto a candidate on the strength of a
hyphen. And `--sort=-v:refname` rather than a lexical sort, which puts v1.9.0
above v1.10.0 and does it silently the first time a project reaches ten of
anything -- there is a test.

What is running is `git describe --tags --always`, so it reads "1.0.0" at a tag,
"1.0.0-7-gd4f56d" seven commits past one, and a bare sha before the first
release ever exists. That last case is what `--always` is for. When it lands
exactly on a tag whose name disagrees with __version__, the page says so: a tag
cut before the version bump names a release nobody can identify afterwards, and
the check costs no subprocess because both facts are already in hand.

update.sh resolves the channel the same way and detaches at the tag rather than
resetting -- a `reset --hard <tag>` while on main would move the local branch to
it, which is a rewrite of a ref nobody asked to rewrite. A host with no tags
falls back to the branch and says so, which is every host until the release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 20:42:03 +02:00

39 lines
1.8 KiB
Desktop File

# Runs deploy/update.sh when the web interface asks for it.
#
# install.sh substitutes __PREFIX__, __SERVICE_USER__ and __UPDATE_BRANCH__ and
# writes the result to /etc/systemd/system/lembas-update.service.
#
# **What this grants.** Installing it means anybody who can administer the web
# interface can deploy whatever is on the configured branch, as root, and
# restart the service. That is the point of it, and it is why it is opt-in and
# why the installer says so out loud rather than doing it by default.
#
# **What it deliberately does not grant.** The request file carries nothing that
# reaches this command line: no ref, no branch, no channel, no arguments. Both
# are baked in below from the installer's environment, so pressing the button is
# "deploy the channel this host was configured with" and can never be "deploy
# something else".
[Unit]
Description=Apply a requested LLeMbas update
# Not `After=lembas.service`: this restarts it, and an ordering dependency on
# the thing being restarted is how a one-shot ends up waiting for itself.
[Service]
Type=oneshot
# Deleted first, always. The path unit re-arms on the file existing, so leaving
# it in place would run this again the moment the service came back -- an
# update loop with no obvious cause. `-` so a failure to delete does not stop
# the update, and `ExecStartPre` so it happens even if the script itself fails.
ExecStartPre=-/usr/bin/rm -f __PREFIX__/data/update-requested
Environment=SERVICE_USER=__SERVICE_USER__
Environment=PREFIX=__PREFIX__
Environment=LEMBAS_BRANCH=__UPDATE_BRANCH__
Environment=LEMBAS_CHANNEL=__UPDATE_CHANNEL__
ExecStart=/bin/bash __PREFIX__/app/deploy/update.sh
# The script's own failure path prints the journal and exits non-zero, which is
# what makes `systemctl status lembas-update` say what went wrong.
StandardOutput=journal
StandardError=journal
TimeoutStartSec=600