Cutting a release¶
A release is one set of artifacts that agree with each other. Building them is mostly make release; what needs a person is coordination, because no single machine can produce all of them and the last two steps depend on every earlier one having arrived.
Read Build the payload first if you have never built one. This page assumes you have.
What a release is¶
| Artifact | Where it goes | Built by |
|---|---|---|
core-macos-arm64.tar.gz |
the origin | a Mac, natively |
core-linux-x86_64.tar.gz |
the origin | a container |
core-linux-arm64.tar.gz |
the origin | a container |
editors.tar.gz |
the origin | any payload build: exactly one |
fonts-core.tar.gz |
the origin | the same one |
manifest.json |
the origin | last, over the directory |
libera-X.Y.Z-py3-none-any.whl |
PyPI | after the manifest, with a copy of it inside |
Libera.app |
a download | a Mac |
libera-X.Y.Z-amd64.flatpak |
a download | an x86_64 Linux machine, payload included |
libera-X.Y.Z-arm64.flatpak |
a download | an arm64 machine, payload included |
The tarballs go to $BUILD_ROOT/out/dist/$PAYLOAD_VERSION, which is 0.1 today and not the application version 0.1.0. build/payload.version and pyproject.toml move independently on purpose: a host fix should not force everyone to re-download 170 MB; a payload rebuilt from new upstream pins should not need a host release.
Which machine can make what, and why¶
Three constraints have each cost somebody a build:
There is no macOS container. A Mac builds its own core natively. Everything else about a Mac release follows from that one fact.
Linux cores do not need a Linux machine. build/docker.sh exists so that a Linux payload never depends on what the host has installed. An Apple Silicon Mac builds linux/arm64 natively and linux/amd64 under Rosetta (not qemu), so both Linux cores come off the Mac at usable speed. On a Linux box, the host's own architecture is just one more container target; do not build it natively there.
Flatpak bundles cannot be cross-built. flatpak-builder runs every build command inside bubblewrap, which installs a seccomp filter compiled for the target architecture. Under emulation the kernel is still the host's, so it is rejected:
Rosetta translates instructions; it leaves the kernel's idea of what architecture a BPF program is for alone. So each bundle is built on a machine of its own architecture, and collected.
Which leaves the division of labour:
| Mac (Apple Silicon) | x86_64 Linux | arm64 Linux | |
|---|---|---|---|
core-macos-arm64 |
only here | no | no |
core-linux-x86_64 |
yes (Rosetta) | yes | no |
core-linux-arm64 |
yes (native) | no | yes |
| editors, fonts, manifest, wheel | yes | yes | yes |
Libera.app |
only here | no | no |
*-arm64.flatpak |
yes | no | yes |
*-amd64.flatpak |
no | only here | no |
So one Mac produces everything except the amd64 Flatpak, with one x86_64 Linux machine contributing that single file. The coordination problem is that small. A release does not need an arm64 Linux box: the Mac covers arm64. Such a box is still the only place the suite gets exercised on real arm64 hardware, outside a container, which is a check to run before you ship.
core-macos-x86_64 has no machine and is deferred: Intel Macs are not a target for this beta.
Before you start¶
Hours, so run the dry run first. It builds nothing and prints what it would do:
It checks the things that have each wasted a build before: a dirty tree (a release records its commit as provenance), Docker actually running, binfmt handlers for any foreign architecture, uv, iconutil, and enough disk. Disk means about 20 GB per architecture that is not already built, and 5 GB for one that is.
Fix everything it names, then read the plan it prints. It will tell you which steps are a rebuild and which are a repackage. Those are different: the core is skippable, the payload is not. A release once shipped an editors.tar.gz eighteen hours older than the licence text inside it, because the core tarball was present, the whole native half was skipped, and dist.sh (which repackages the theme, the branding and the attribution) never ran.
Tag before you build, so the provenance in the manifest is the tag and not a commit that only you have:
1. The Mac¶
Which does, in order:
- the native core if
x2tis missing (hours, since V8 alone is ~30 minutes), then the payload,smoke.shanddist.sh; - each Linux architecture in its container, exported into
$DIST; - the manifest, over everything present;
- the wheel, with the manifest stamped into it;
Libera.app;- the arm64 Flatpak.
A failure in one architecture is recorded, leaving the run to carry on: one platform refusing to build is no reason to throw away the other five artifacts. The run ends with a list of what it failed to produce and the relevant lines from each failing log. Read that list: it is the report.
Expect it to end saying:
still missing: core-macos-x86_64
still missing: libera-0.1.0-amd64.flatpak (build it on a amd64 machine)
Both are correct. The first is deferred; the second is step 2.
2. The x86_64 Linux machine¶
The bundle carries the payload, so this box needs one built before it can make a bundle. That is a change: it used to need only the repository, Docker and uv.
git clone https://github.com/abilian/libera-suite && cd libera-suite
git checkout v0.1.0 # the same tag, or the artifacts do not match
make payload-linux # core and payload in the pinned container (hours, once)
make flatpak # stages them into the bundle, then --diagnose inside it
Or, if the Mac has already built the amd64 artifacts, copy them over and skip the hours: make flatpak takes them from $DIST; DIST=/path/to/artifacts make flatpak points it somewhere else.
Use make flatpak here. A release run works and wastes an afternoon: it repackages a $DIST that is not the release. Only the Mac's is. Expect this box to end up reporting still missing: core-macos-arm64 and the rest, which is correct: it is describing its own directory.
That leaves build/out/libera-0.1.0-amd64.flatpak. Copy it next to the Mac's:
Check what else is in build/out first. Nothing cleans it. Every build writes a new filename, so bundles from earlier versions, including from before the product was renamed, sit there indefinitely. make release now lists those separately, under "NOT part of this release". Delete them, so they are not in the way at upload time.
make flatpak ends by installing the bundle and running libera --diagnose inside the sandbox, which is as far as a container goes, since there is no display in one. It proves three things: the GNOME runtime supplies GTK and WebKit, the application imports, --diagnose says payload 0.1 (bundled). make flatpak-smoke goes one further and converts a document with it.
3. An arm64 Linux machine, if you have one¶
No release depends on this step. It is the only place the suite runs on real arm64 hardware:
make payload-linux # payload in the pinned container
make test-linux # the suite, on Linux
make gui-linux # a real GTK/WebKitGTK window under Xvfb, screenshotted
make flatpak-check # install the bundle, run --diagnose in the sandbox
On a small machine the build is tight on memory before it is slow. build/payload.sh and build/build.sh size themselves from the cgroup limit; a 4 GB box needs swap to finish the core (see Build the payload). The symptom is Killed signal terminated program cc1plus, and the answer is fallocate, not a smaller job count.
4. Bringing it together¶
The manifest describes a directory, so it has to be written after every artifact has arrived, and the wheel after the manifest, because the wheel carries a copy and verifies every download against it. Within one make release those two are already in order. Across machines they are not, so once the amd64 Flatpak is in place, run:
The Flatpak is not in the manifest, because it carries the host, so in practice the amd64 bundle arriving late costs nothing. But if you copy in a core tarball built elsewhere, the manifest and the wheel both have to be redone:
make release checks this agreement at the end and says so:
If it does not, an install would reject its own payload: a hash mismatch on a tester's machine, which is a bad place to find out.
5. Verify before publishing¶
make verify # lint, the whole suite, docs links, the patch queue, the converter
make patches # the queue still applies to the pinned SHAs
Two things remain that no automated check covers:
- Read the attribution out loud. It is a licence obligation. It lives in
build/theme/libera/meta/config.jsonand shows in Help ▸ About. - Install the payload the way a user will, from the artifacts, and open a document with it.
6. Publish¶
Publish in this order, because the parts refer to each other:
- The payload to
https://cdn.abilian.com/libera/: the whole of$DIST, under the payload version. - The upstream mirrors, at the same moment the binaries go out. The AGPL obligation is a commit plus a patch series that provably applies to it;
build/pins.tomlandbuild/patches/are that series. - The wheel to PyPI (
make publish). Libera.appand both.flatpakbundles wherever the release is downloaded from.- Push the tag.
When a step fails¶
exec format error a minute into an image build |
No binfmt handler for a foreign architecture. docker run --privileged --rm tonistiigi/binfmt --install all, or build that architecture on a machine that is one. |
Killed signal terminated program cc1plus |
The OOM killer. Add swap; the build prints the fallocate/mkswap/swapon lines when it detects a small machine. |
FATAL: heap out of memory in a webpack |
V8 sizes its heap from the cgroup limit, ignoring the work: a smaller container aims lower and dies sooner. build/payload.sh sets a floor below 6 GB; see Build the payload. |
| A patch no longer applies | News, and something to investigate. make patches names it; read the upstream change before rebasing. |
| The build ran the JS pipeline unpatched | The source tree was fetched before the patch existed. git -C $BUILD_ROOT/src/<repo> log --oneline answers it in one line. |
INCOMPLETE: no editors.tar.gz |
A Linux-only run whose architectures were all already built, so nothing produced the platform-neutral half. The command to fix it is printed with the message. |
FATAL: artwork in the payload that is not ours |
dist.sh found a logo-shaped file it does not recognise. Look at it before deciding: if it is upstream's it wants removing in build/patches/web-apps/ or writing over in payload.sh; if it is ours, its name goes in OURS in dist.sh. |
Build the payload has the reasoning behind the rest: why the container is pinned the way it is, why arm64 needs clang 13 exactly, and what each of the silent failures cost.