Releases and versioning¶
How yapnr is versioned, what a release publishes, and the owner’s checklist for cutting one. The
workflows are .github/workflows/release.yaml and .github/workflows/image.yaml; the container
images are described in containers. Release notes live on
GitHub Releases; there is no CHANGELOG.md.
Versions¶
Release tags are annotated
vMAJOR.MINOR.PATCHtags (SemVer 2.0) on a commit ofmain. Release candidates arevX.Y.Z-rc.N(N from 1), always in that hyphen-dot form: a barevX.Y.Zrc1is not a SemVer version at all, and Bazel’s version ordering would put it abovevX.Y.Z. There are no alpha or beta tags; the workflows ignore tags of any other shape (tools/release/version.pyskips them when it looks for the nearest release).The tag is the only version source.
MODULE.bazelhas noversion, and the sources carry none: the wheel is stamped at build time (--stamp --embed_label=<version>on//release:wheel.dist), andyapnr --versionreads the installed wheel’s metadata. A source tree that is not installed (bazel run //:yapnr) reports0.0.0.dev0.Every other build is a development build, versioned by
tools/release/version.pyfromgit describeso that it sorts after the release it descends from:HEAD
Version (PEP 440)
Example
at
vX.Y.ZX.Y.Z0.3.1at
vX.Y.Z-rc.NX.Y.ZrcN0.4.0rc1N commits after
vX.Y.ZX.Y.(Z+1).devN+g<sha>0.3.2.dev5+gab12cd3N commits after
vX.Y.Z-rc.KX.Y.Zrc(K+1).devN+g<sha>0.4.0rc2.dev3+g...no release tag yet, N commits
0.0.0.devN+g<sha>0.0.0.dev7+g...uncommitted changes (local builds)
...+g<sha>.dirtytools/release/version.py --field pep440prints the version of a checkout; without--fieldit prints everything, including the image tags for a given--ref.
What bumps what¶
Change |
While 0.x |
From 1.0 |
|---|---|---|
Breaking change to the public surface (below) |
MINOR |
MAJOR |
|
MINOR |
MINOR |
Store layout change with an automatic migration |
MINOR |
MINOR |
New feature, backward compatible |
PATCH |
MINOR |
Fix, pin bump without a result change, KiCad patch update in the image |
PATCH |
PATCH |
New default KiCad major version (boards upgrade irreversibly) |
MINOR |
MAJOR |
Documentation only |
none |
none |
SemVer allows anything to change in 0.y.z; yapnr uses MINOR for breaking and results-changing
releases while it is 0.x, so pinning X.Y is safe. No image tag 0 is ever published.
The public surface: the CLI (commands, flags, --json output, exit codes); yapnr.toml
(yapnr-project-v1) and the store layout_version without an automatic migration; the bundle
format yapnr-bundle-v1; the @yapnr//bazel:defs.bzl rules and providers; the telemetry and notes
schemas; and the container interface (entrypoint, environment variables, /project, the viewer
port 8781, the UID 1000 default).
Proposed criteria for 1.0.0: the manifest and store layout unchanged (incompatibly) for two minors, the Bazel ruleset through one full release cycle with Splanc’s boards, and the container interface documented.
Labels and release notes¶
GitHub generates the notes from the merged pull requests, grouped by label
(.github/release.yml); a pull request lands in the first matching category. Every pull request
gets one area label, and two more when they apply:
Label |
Meaning |
|---|---|
|
the area |
|
breaks the public surface (listed first) |
|
same inputs, seed and flags give a different board |
|
leave the pull request out of the notes |
tools/release/create_labels.sh creates them (idempotent). Agents add labels with
gh pr edit <N> --add-label <label>; the pull request template has a reminder.
A header written by tools/release/notes_header.py comes first: docker pull by tag and by
digest, the wheel with the PyTorch CPU index, the Bazel archive_override snippet with its
integrity, the bundled KiCad version, and a link to docs/releases/vX.Y.md when that file
exists. Final releases compare against the previous stable tag, so the notes cover the whole RC
cycle.
What a release publishes¶
What |
Where |
|---|---|
|
release asset: |
|
release asset: the stamped wheel |
|
release asset: the documentation site |
|
release asset: checksums of the above |
|
|
Every release file and image is attested; check one with
gh attestation verify <file or oci://image> -R Studio-Fug/yapnr. The images also carry an SBOM
and provenance. Consume a release from Bazel
through the uploaded archive (GitHub’s automatic /archive/ tarballs are not byte-stable):
bazel_dep(name = "yapnr", version = "0.1.0") # ignored under the override; kept so that
archive_override( # moving to the BCR = deleting the override
module_name = "yapnr",
urls = ["https://github.com/Studio-Fug/yapnr/releases/download/v0.1.0/yapnr-v0.1.0.tar.gz"],
integrity = "sha256-<from the release notes>",
strip_prefix = "yapnr-0.1.0",
)
What the workflows do¶
image.yaml (see containers):
Pull requests: build both images for both architectures on native runners (no push) and smoke-test them. A
planjob skips the rest when nothing that goes into an image changed, so the workflow always reports. Not a required check until v0.1.0.Pushes to
mainthat change what goes into an image: the same, then pushghcr.io/studio-fug/yapnr:edgeand:sha-<7>, with the versionX.Y.(Z+1).devN+g<sha>(X.Y.Zrc(K+1).devN+g<sha>after a release candidate,0.0.0.devN+g<sha>before the first tag). The KiCad base is pushed the first time its tag (docker/yapnr-kicad/TAG) is built: first its-srcimage (both platforms), then the tags, then the attestation. The publish job can be re-run after a failure, and a later run publishes a missing-srcimage.Releases (called by
release.yaml): the same, with the release tags.Every run compares
docker/yapnr-kicad(tools/image/base_context.sh) with theio.github.studio-fug.yapnr.kicad.contextlabel of the published base and fails if they differ: a published base tag is never overwritten, and a release never ships a base that does not match the tagged commit. The yapnr image is built FROM the base by digest.
release.yaml runs when a v* tag is pushed:
verify: the tag is annotated, matches
vX.Y.ZorvX.Y.Z-rc.N, and its commit is onmain(or arelease/X.Ybranch); computes the version, the previous stable tag, and whether the release becomeslatest(a backport such as 0.2.5 never takeslatestfrom 0.3.0).ci: all of
ci.yamlat the tag (lint, test, docs).image:
image.yaml, publishingX.Y.Z,X.Yandlatest(release candidates:X.Y.Z-rc.Nonly).release: assembles the files above, attests them, creates a draft GitHub release with the files and the notes (a prerelease for
-rctags), then publishes it. Nothing follows the publish step, so the workflow works with immutable releases.
Dry run: Actions > Release > Run workflow (on any branch), or
gh workflow run release.yaml --ref <branch>. It runs every job and uploads the release files as a
workflow artifact, writes the release notes into the run summary (the header for the placeholder
tag v0.0.0, and the notes GitHub would generate, through the same API and .github/release.yml),
and publishes nothing: no image tags, no GitHub release.
Release checklist (owner)¶
Only the owner creates v* tags; a tag ruleset enforces it. Agents prepare release pull requests
and never create or push tags.
Release pull request (for minors; optional for patches), titled
release: vX.Y.Z: upgrade notes indocs/releases/vX.Y.mdforbreakingandresults-changeitems, the newX.Ytag in the README and the container docs’ examples, and aWORKLOG.mdentry.Dry run on
mainafter it merges:gh workflow run release.yaml --ref main; it must be green. Wait as well for theImagerun of the commit you are about to tag to finish green onmain: if that commit introduced a new base tag,mainpublishes the base, and a release run racing it would try to publish the same base tag and fail.Release candidate (required for a MINOR with
breakingorresults-changeitems, or a store layout change; patches go straight to step 5):git fetch origin && git checkout --detach origin/main git tag -s v0.4.0-rc.1 -m "yapnr 0.4.0-rc.1" # -a if tags are not signed git push origin v0.4.0-rc.1
Regression on the candidate: the full regression ladder (
//tests/regression:ladder, from PR6b), the nightly macOS KiCad lane, and the published image’s smoke test on both architectures (tools/image/smoke_image.sh ghcr.io/studio-fug/yapnr:0.4.0-rc.1after adocker pull). A fix means a new candidate (-rc.2) frommain.Final tag on the same commit as the last candidate:
git tag -s v0.4.0 -m "yapnr 0.4.0" <commit of v0.4.0-rc.N> git push origin v0.4.0
Check the release: the workflow is green, the GitHub release has four files and the install header,
docker pull ghcr.io/studio-fug/yapnr:0.4works anonymously, andgh attestation verify oci://ghcr.io/studio-fug/yapnr:0.4.0 -R Studio-Fug/yapnrpasses.
A bad release is superseded by the next patch release; tags are never moved or reused. Fix forward
on main; a release/X.Y branch is created only when a backport is actually needed.
One-time repository settings¶
Tag ruleset on
refs/tags/v*(in place): restricts creating, updating and deleting release tags to the owner.Package visibility: new GHCR packages start private. After the first push of
ghcr.io/studio-fug/yapnrandghcr.io/studio-fug/yapnr-kicad(the firstmainbuild after this workflow lands), make both public (package settings > Danger Zone > Change visibility). The organization must allow public packages (Organization settings > Packages > Package creation: Public), or the option is missing. The switch cannot be undone. Until then, anonymousdocker pullfails.Dependabot (
.github/dependabot.yml) proposes updates of the pinned actions and of theubuntuanduvimage digests once a month; see maintaining the images.Labels:
tools/release/create_labels.sh.Immutable releases (Settings > General > Releases): recommended. The release workflow publishes last, so it works with them on.
The
imagecheck becomes required with v0.1.0 (it summarizesimage.yaml).
Maintaining the images¶
The KiCad base is frozen per tag.
yapnr-kicad:<KiCad version>-<N>keeps the Ubuntu packages of the day it was built (every package upgraded to the archive state recorded in/etc/yapnr/ubuntu-snapshot). Security fixes arrive by rebuilding under the nextN: bumpdocker/yapnr-kicad/TAG(and nothing else, or together with a Dependabotubuntudigest update) in a pull request, and the merge publishes the new base.When: at least once a month, when a KiCad patch release lands in the PPA (a new KiCad version,
Nback to 1), and soon after an Ubuntu security notice for a package in the base. The next yapnr patch release then ships the new base;edgehas it right away.Dependabot proposes the
ubuntu:24.04andghcr.io/astral-sh/uvdigests monthly. Anubuntuupdate changesdocker/yapnr-kicad, so its pull request fails theImagecheck untildocker/yapnr-kicad/TAGis bumped in it. A uv update may install a newer python-build-standalone release: the build then fails untilPBS_RELEASEand the twoPBS_FULL_SHA256_*checksums indocker/yapnr/Dockerfileare updated (from that release’sSHA256SUMS).The runtime locks follow
requirements.lock(tools/image/update_runtime_locks.sh); after a numpy or torch update, check the native libraries the new wheels bundle and updatedocker/yapnr/native-libraries-<arch>.txt,THIRD_PARTY.mdandthird_party/image-licenses/.