Releasing byro¶
This page is for byro maintainers. It describes how a release is produced,
what the release pipeline does, and the two duties that come with the
deployment tooling: the stable pointer and the release flags in
deploy/release.env.
How a release is made¶
- Release Drafter keeps
a draft release up to date on GitHub. Every merged pull request adds a line
under the category of its label (
breaking-change,enhancement,bug/fix,maintenance,dependencies,documentation). The drafter also proposes the next tag, but with semantic-versioning rules:breaking-changebumps the first field,enhancementthe second, everything else the third. byro's scheme isvYYYY.MINOR.PATCH, so check the proposed tag and edit it by hand when needed: the first field is always the current year, the first release of a year isvYYYY.1.0, and abreaking-changelabel must not bump the year. - Before publishing, edit the draft: write the introduction (the placeholder at the top) and go through the checklist at the end of this page.
-
Publish the release. GitHub creates the tag, and the tag starts the release pipeline (
.github/workflows/ci-cd.yml, eventrelease: published):- style checks and the test matrix,
- the Python package, uploaded to PyPI (environment
pypi), - the container image
ghcr.io/byro/byro:vYYYY.M.Pforlinux/amd64andlinux/arm64, also taggedlatest, - and, only after both uploads succeeded, the
stablepointer (see below).
The run takes about half an hour; the multi-platform image build is the slow part. Watch it under Actions.
byro has not published pre-releases so far. If you mark a release as
pre-release, the pipeline still runs: package and image are published, and
the image also receives the latest tag, which the deprecated production/
setup pulls unpinned. Only the stable pointer is not moved. Turning the
pre-release into a release later does not run the pipeline again (GitHub sends
released, not published); move stable by hand with the Stable pointer
workflow in that case.
The stable pointer¶
The byroctl bootstrap (Installation with byroctl) starts with:
and byroctl update --check asks the same place which release is current.
stable is a branch of this repository with exactly three files, written by
CI and by nothing else:
stable.envwith one lineBYRO_RELEASE_VERSION=vYYYY.M.P,install.sh, a byte-identical copy ofdeploy/install.shfrom that release tag,- a
README.mdthat explains the branch.
Everything else (byroctl, the Compose files, the image) is fetched from the
immutable release tag, so the branch only says which release is current. The
job Point stable at the release runs as the last step of the pipeline and
calls .github/scripts/update-stable-branch.sh through the workflow
.github/workflows/stable.yml. The script
- refuses anything that is not a
vYYYY.M.Ptag, - checks that the tag contains
deploy/install.shmatching itsSHA256SUMSand that the image exists in the registry, - compares versions numerically and never moves the pointer to an older release on its own (the job then ends green with a notice),
- appends a commit to the branch history and pushes it as a fast-forward; the branch is never force-pushed.
raw.githubusercontent.com caches files for a few minutes, so installers may
still see the previous release for up to about five minutes after the pointer
moved.
Moving the pointer by hand¶
Do not edit or push the stable branch directly. If a release turns out to be
broken, point stable back at the previous release: Actions โ Stable
pointer โ Run workflow, enter the tag and tick force (moving to an older
release is refused otherwise). Then fix the problem and publish a new release;
the pipeline moves the pointer forward again. Consider yanking the broken
version on PyPI as well. The image tag stays available, because byroctl
installations pin the release they installed. byroctl does not downgrade: while
stable names an older release than the installed one, byroctl update and
byroctl update --check on the broken release exit with an error that names
both versions. Those installations wait for the fixed release and update to it
(byroctl update --to vX.Y.Z until stable has moved there).
The workflow pushes with the repository's GITHUB_TOKEN. If stable is
covered by a branch protection rule or a ruleset, that automation actor needs
push or bypass permission for the branch. People do not need it: changes to the
pointer go through the workflow, never through a direct push.
Release flags¶
deploy/release.env carries two flags that byroctl update reads from the
target release before it changes anything:
BYRO_RELEASE_BREAKING=1- Administrators must read the release notes and confirm the update
explicitly (
byroctl updateasks,--yesconfirms). Use it only when an administrator has to act or decide before updating: a configuration that must change, a service that goes away, a manual step. It is not a synonym for thebreaking-changelabel, which also covers code and plugin API changes that need no action from administrators. BYRO_RELEASE_DATA_MIGRATION=1- The release changes files outside the database (documents, uploads, GnuPG
home). The regular pre-update safeguard does not cover them, so
byroctl updaterefuses to continue until the administrator confirms a full backup ofdata/with--data-safeguard-done.
Both flags are 0 on main. For a release that needs one:
- In the pull request that prepares the release, set the flag to
1, run.github/scripts/deploy-checksums.sh --write(release.envis on the checksum list that CI enforces) and write the release-notes section that explains what administrators must do. - Publish the release. The flag is part of the tag, and byroctl reads it there.
- Right afterwards, open a pull request that resets the flag to
0, again with a regeneratedSHA256SUMS.
The CI check [Deploy] release flags (workflow release-flags.yml, every
push to main and every pull request) enforces the format of the file (each
flag exactly once, only 0 or 1, nothing else) and reminds you of step 3:
on main it fails as long as a flag is 1, the latest release tag shipped it
at 1 and no commit since that release changed the flag's line; in pull
requests it only warns. This red phase between publishing and the reset pull
request is intended.
byroctl reads the flags of the release it updates to, nothing else. An
installation that skips a flagged release (from v2026.2.0 straight to
v2026.4.0 when v2026.3.0 carried BYRO_RELEASE_DATA_MIGRATION=1) is not
stopped by that flag, although the migration still runs. When you set a flag,
tell administrators in the release notes of the following releases what those
who skipped the flagged release must do. Do not tag a hotfix from main while
a flag is still 1 unless the hotfix needs it too.
Plugin catalog¶
deploy/plugin-catalog.conf is the list of plugins that
byroctl plugin add <shortname> knows. It is a release artifact like the
Compose files: versioned with the release, listed in deploy/SHA256SUMS,
downloaded and verified by byroctl. It carries metadata only, no versions:
[finance-import-bank-files]
name=Bank file importers
description=Imports file-based bank statements
package=byro-finance-import-bank-files
source=github
repo=https://github.com/byro/byro-finance-import-bank-files
byroctl resolves the version when an administrator adds or updates the plugin:
for source=github the current regular GitHub release of repo, installed by
the commit its tag points to; for source=pypi the current release on PyPI. A
new plugin release therefore needs no change in this repository. Whether a
plugin release works with a byro release is declared by the plugin itself
(dependencies = ["byro>=2026.3"] in its pyproject.toml); the image build
checks it against the installed byro.
To add a plugin:
- The plugin must have a
byro.pluginentry point, anapps.pywithByroPluginMetaand at least one regular GitHub release (or a release on PyPI). Without a releasebyroctl plugin addfails with a clear message. - Add a section with the five keys above. Section names are lower-case
letters, digits and hyphens;
repoishttps://github.com/<owner>/<repo>for GitHub plugins. - Run
.github/scripts/check-plugin-catalog.shand.github/scripts/deploy-checksums.sh --write; both are enforced by CI (job [Deploy] shellcheck, checksums, bats).
Only plugins that work with the current byro release belong in the catalog. Candidates that still need work are tracked as issues, not as commented-out entries.
Notes for administrators¶
The pipeline publishes the release, but only you can tell administrators what to do. Check before publishing:
- Does the release change configuration options, the Compose files or the
behavior of the image? Describe the steps for byroctl users
(
byroctl updatetakes care of new options and Compose files), fordocker composeusers and for bare metal installations (pip install -U byro,migrate,rebuild, restart). - Users of the deprecated
production/setup pullghcr.io/byro/byro:latestand get the new image without pinning. Mention anything that affects them and point toproduction/DEPRECATED.md.
The first release that ships deploy/ deserves an explicit section for
production/ users, because it is the first time the published image changes
its default behavior. It does not need BYRO_RELEASE_BREAKING=1: no byroctl
installation exists yet that could read the flag.
Checklist¶
Before publishing:
- the draft is reviewed, the introduction is written, the categories are complete,
deploy/release.envhas the flags this release needs, and the release notes explain them,- the release notes tell administrators what to do,
deploy/plugin-catalog.conflists only plugins that work with this release and the catalog lint is green,mainis green; if [Deploy] release flags is red, the previous flag has not been reset yet, do that first.
After publishing:
- the pipeline run is green, including Point stable at the release,
stablenames the new release (stable.envon the branch),- a flag that was set is reset to
0in a follow-up pull request.