Release Pipeline #
Automated release pipeline for lichen. Builds static musl binaries, publishes them as tangled artifacts, pushes Docker images, updates YunoHost and Helm packages.
This repo is checked out inside the lichen repo, as <lichen>/release —
the pipeline builds from its parent directory.
Quick start #
ssh root@release-runner # or ssh root@100.67.126.34
cd /root/lichen
git pull origin main # get latest code
/root/run-release.sh run <version>
Example: /root/run-release.sh run 0.2.0
Locally, release/lmrelease.sh <version> does the tag, build, publish and
Docker steps inside nix develop .#release.
How publishing works #
Tangled has no upload API of its own. A release artifact is an
sh.tangled.repo.artifact record in the releasing account's atproto repo,
with the binary stored as a blob on that account's PDS. The record points at
the sh.tangled.repo record for lichen.page and at the sha1 of the
annotated tag object, and the tangled appview picks it up off the firehose.
That means two things:
- publishing needs an atproto app password, not a forge token
- release tags must be annotated (
git tag -a) — a lightweight tag has no tag object for an artifact to hang off
Artifacts show up at
https://tangled.org/notplants.bsky.social/lichen.page/tags and download from
https://tangled.org/notplants.bsky.social/lichen.page/tags/<tag-object-sha>/download/<file>.
First-time setup on the release runner #
1. Create .env #
cp /root/lichen/release/.env.example /root/lichen/release/.env
Fill in:
TANGLED_APP_PASSWORD=<app password for notplants.bsky.social>
DOCKER_USERNAME=notplants
DOCKER_PASSWORD=<your docker hub token>
To create an app password: https://bsky.app/settings/app-passwords
2. Set up the tangled SSH key #
The pipeline pushes release tags to the tangled and origin remotes, so the
VM needs a key with push access:
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -C "release-runner"
cat /root/.ssh/id_ed25519.pub
# Add this key to your tangled account
The YunoHost package repo still lives on Codeberg, so the ynh step also needs
push access there (SSH key, or CODEBERG_TOKEN in test mode).
Making a release #
1. Prepare #
ssh root@release-runner
cd /root/lichen
git pull origin main
Make sure HEAD is the commit you want to release.
2. Run the pipeline #
/root/run-release.sh run <version>
Pass the version without a v prefix. The pipeline adds it automatically.
Example: /root/run-release.sh run 0.2.0 creates tag v0.2.0.
3. What happens #
The pipeline runs these steps in order:
-
Build — cross-compiles 4 static (musl)
lichenbinaries:minimum(no optional features) × x86_64, aarch64full(shell, git, atproto, oidc, hugo, mdbook) × x86_64, aarch64- Binaries are gzip-compressed
-
Tangled — creates annotated tag
v<version>, pushes it to origin and tangled, uploads the compressed binaries to the PDS and attaches them to the tag assh.tangled.repo.artifactrecords -
Docker — builds and pushes two Docker images to Docker Hub:
notplants/lichen-min:<version>(Alpine + minimum binary)notplants/lichen-full:<version>(Alpine + full binary + hugo, git, bubblewrap)
-
YunoHost — updates
manifest.tomlandapps.jsonin ther-lichen_ynhpackage repo with new version, URLs, and checksums -
Helm — updates
Chart.yamlappVersion andvalues.yamlimage tag, commits and pushes to the main repo -
Tauri (optional) — builds Linux desktop app (.deb, .rpm) if
cargo-tauriis installed
4. If something fails #
The pipeline is idempotent and resumable. Just rerun the same command:
/root/run-release.sh run <version>
It checks state.json and skips completed steps. To force a full re-run:
/root/run-release.sh run <version> --force
5. Verify #
- Tangled artifacts:
https://tangled.org/notplants.bsky.social/lichen.page/tags - Docker:
docker pull notplants/lichen-full:<version> - Helm: check
charts/lichen/values.yamlin the repo
Running individual steps #
/root/run-release.sh build <version> # just compile binaries
/root/run-release.sh publish <version> # just tag + upload tangled artifacts
/root/run-release.sh docker <version> # just build + push docker
/root/run-release.sh ynh <version> # just update yunohost package
/root/run-release.sh helm <version> # just update helm chart
/root/run-release.sh tauri <version> # just build desktop app
Test mode #
To test against disposable test accounts:
RELEASE_MODE=test /root/run-release.sh run 0.0.1-test
This uses the nptest2 Docker Hub account and the nptest Codeberg account for
the YunoHost package. Set TANGLED_HANDLE, TANGLED_REPO and
TANGLED_APP_PASSWORD in .env to point publishing at a throwaway tangled repo.
What's where #
| Path | Purpose |
|---|---|
/root/run-release.sh |
Wrapper script (sets PATH and RELEASE_MODE) |
/root/lichen/release/lmrelease.sh |
Local tag + build + publish + docker run |
/root/lichen/release/.env |
Credentials (not in git) |
/root/lichen/release/release/ |
Python pipeline scripts |
/root/lichen/release/releases/<version>/ |
Built binaries |
/root/lichen/release/state.json |
Pipeline state (which steps completed) |
/root/lichen/release/logs/<version>.log |
Build logs |
/root/lichen/release/docker/Dockerfile.prebuilt |
Docker image definition |
/root/lichen/release/packages/lichen_ynh/ |
Cloned YunoHost package repo |
Production repos #
| What | Location |
|---|---|
| Tangled (code + release artifacts) | notplants.bsky.social/lichen.page |
| Tangled (this repo) | notplants.bsky.social/lichen-releaser |
| Artifact blobs | the PDS of notplants.bsky.social |
| Docker Hub | notplants/lichen-min, notplants/lichen-full |
| YunoHost | notplants/r-lichen_ynh (on Codeberg) |
| Helm chart | charts/lichen/ in the main repo |
Unified executable #
Release assets are lichen-{minimum,full}-{x86_64,aarch64}-linux.gz.
Both variants build lichen-cli --bin lichen --no-default-features; only the
full variant adds the configured optional features. Docker installs
/usr/local/bin/lichen. The minimal image runs lichen server --site /data run;
the full image runs lichen server --multi --root-dir /data run.
Use matching deployment entrypoints when adopting the new assets. The YunoHost
step also migrates the package executable name, service, and install/upgrade/restore
scripts to the unified commands. Existing releases retain their original names.
Run the focused release-contract tests with python3 -m unittest discover -s tests.