How Cyber Fidget firmware releases work¶
This page explains the project's versioning philosophy and release flow -- useful if you're a contributor (so you know what's expected of you, or not), a user choosing which firmware to install, or someone trying to understand why a binary you have reports a particular version.
For the firmware-side technical reference (banner format, serial CLI, runtime APIs), see Firmware version.
Versioning philosophy¶
Semantic versioning, decided by humans¶
Cyber Fidget firmware uses SemVer 2.0.0 and the release manager makes the bump decision (major / minor / patch) by hand based on the actual semantic impact of changes since the last release. There's no commit-message-driven auto-bump (yet).
Why this matters to you:
- If you're an app author writing apps for The Archives: the
min_firmware: 1.4.2field on your app pins against a real semver contract. A1.4.xdevice will run any app that requires>=1.4.0; a1.3.xdevice won't. - If you're a firmware contributor: you don't need to think about versioning when opening a PR. Just describe the impact in your PR description (bug fix? feature? breaking change?) and the release manager will fold that into the next bump decision.
- If you're an end user: the version number you see on your device
means something specific.
1.4.2 → 1.4.3is a patch (bug fixes);1.4 → 1.5is a minor (new features, backward-compatible);1.x → 2.0is a major (breaking changes).
Bump-on-release, not bump-per-commit¶
The version number embedded in the firmware advances only when a
release is cut. Between releases, dev/CI builds report the upcoming
release version with a +commit-hash suffix that uniquely identifies
the build:
- A clean release:
1.2.0+abc1234 - A CI build of someone's PR after the 1.2.0 release:
1.2.1+def5678(the version has been pre-bumped to the next-patch target so dev builds advertise where they're heading) - A local dirty build:
1.2.1+abc1234.dirty
This is the firmware-industry default -- it matches how ESP-IDF, Arduino, Raspberry Pi firmware, and Postgres all version themselves. Versions mean something; commit hashes uniquely identify what was actually built.
Release tiers¶
In order of significance (and audience):
| Tier | Tag shape | Audience | When to use it |
|---|---|---|---|
| Stable (also called "GA" or "General Availability" in enterprise contexts) | v1.2.0 |
Everyone (default channel) | "Confident this is ready" |
| Release Candidate (RC) | v1.2.0-rc1, -rc2 |
Bleeding-edge opt-in users | "Feature-complete, want eyes before promoting to Stable" |
| Beta | v1.2.0-beta, -beta.2 |
Wider tester pool | "Feature-mostly-complete, may still tweak" |
| Alpha | v1.2.0-alpha, -alpha.2 |
Internal / insiders | "Early; expect churn" |
The default release channel is Stable only. You'll see Stable releases on the GitHub Releases page without any special opt-in. RCs and below are marked as "Pre-release" by GitHub and are intended for users who explicitly want to test upcoming features and report issues.
Below RC sit non-release builds (nightly, CI, local dev) -- those are internal development scaffolding and aren't normally distributed. See the build-types section in the firmware-version reference for what these mean in serial output.
Where releases live¶
- Stable + Pre-release artifacts: GitHub Releases page on the firmware repo (github.com/CyberFidget/cyberfidget-firmware/releases)
- Latest Stable is the release at the top with no "Pre-release" tag
- To opt into prereleases: scroll past the top Stable release; RCs and betas appear immediately below, marked with a yellow "Pre-release" badge
- App Builder firmware selector (when implemented per T-003): defaults to "Latest Stable" with a checkbox for "Include pre-release builds"
For contributors: what you need to do (and what you don't)¶
If you're submitting a PR to the firmware repo:
Don't:
- Don't edit version.txt. The release manager owns it; touching it in
a PR will get pushback during review.
- Don't push tags. Tag creation is release-manager territory.
- Don't worry about which version your PR will land in. The release
manager folds it into whichever release happens next.
Do:
- Note your change's semver impact in the PR description (bug fix /
feature / breaking). The release manager uses this when deciding the
next bump.
- Test against a CI build of your own PR. Every commit you push to a PR
branch triggers a CI build with a unique identifying string --
something like fw=1.2.1+yourhash type=ci-dev. Flash that build, see
the matching banner on the device, run your tests. The build summary
printed during compile and the device's boot banner will report the
same string character-for-character; if they don't match, something's
wrong (stale cache, wrong serial port, partial flash) -- investigate
before continuing.
- Add a line to the [Unreleased] section of CHANGELOG.md, under
Added / Changed / Fixed / Removed, in the same pull request as your change.
Write it for someone holding the device rather than someone reading the
code: name what the change does, not how it's built. This is what becomes
the summary at the top of the release notes, so writing it while the change
is fresh beats reconstructing it from commit subjects months later.
That's the whole contributor flow. You never touch the release machinery.
For release managers: how releases get cut¶
Two paths exist, both supported. Use whichever you prefer.
Path A - manual via git tag¶
# Bump version
$EDITOR cyberfidget-firmware/version.txt # 1.1.0 → 1.2.0
git add cyberfidget-firmware/version.txt
git commit -m "chore(release): bump to 1.2.0"
git push origin main
# Tag and push
git tag v1.2.0 # or v1.2.0-rc1 for an RC
git push origin v1.2.0 # ditto
# CI does the rest (build → release artifact attached to GitHub Release)
# (For Stable only) post-release bump so dev builds advertise the upcoming version
$EDITOR cyberfidget-firmware/version.txt # 1.2.0 → 1.2.1
git add cyberfidget-firmware/version.txt
git commit -m "chore: post-release bump to 1.2.1"
git push origin main
CI asserts that the tag matches version.txt before doing any compile
work. If they disagree, the build aborts with a clear error in ~1 second.
Path A does not stamp CHANGELOG.md for you. The release notes still pick
up whatever is in [Unreleased], so the notes come out right, but the
changelog keeps calling that section unreleased after it has shipped. Tidy it
by hand afterwards, or use Path B, which does it as part of the run. This is
the tradeoff for Path A being three commands you can run during an incident.
Path B - run the release workflow¶
Instead of typing commands:
- Open github.com/CyberFidget/cyberfidget-firmware/actions
- Click "Build & Release Firmware" in the left sidebar
- Click "Run workflow", and pick the branch to release from
- Fill in:
- Version:
1.2.0(no leadingv) - Release tier:
rc(default),beta,alpha, orreleasefor Stable - Iteration: leave blank to auto-pick the next free one (
rc1, thenrc2, ...), or name it explicitly - Post-release bump:
patch(default) /minor/major/skip. Applies to Stable releases only -- an RC series leavesversion.txtalone - Rehearse: tick to build and print the plan without pushing or publishing anything
- Permit a version lower than the highest released one: leave unticked. It exists so a deliberate rollback is possible, and so a misclick isn't
- Click "Run workflow"
The workflow does the version.txt bump, the changelog stamp, the commit, tag
creation and push, the build, the release with its notes and artifacts, and
(for Stable) the post-release bump -- all in one run. Manual git tag from
Path A continues to work in parallel.
It refuses to publish rather than publish something wrong: a tag that already exists, a version below the highest already-released one, a build whose embedded version string doesn't match the tag, or a changelog with nothing written in it will each stop the run.
Rehearse first when you're unsure¶
Ticking Rehearse runs the whole thing -- version resolution, the bump, the build, the version check -- and stops before anything is pushed or published. It's the cheap way to confirm a branch actually builds, or to see which tag the auto-picker would choose, without committing to a release.
What goes in the release notes¶
Release notes are assembled from two sources:
- The summary at the top comes from the
[Unreleased]section ofCHANGELOG.mdin the firmware repo, written by hand in Added / Changed / Fixed / Removed form. Whoever lands a change adds their line in the same pull request, so the summary is already written when the release is cut. - The full commit list underneath is generated by GitHub from everything since the previous release.
When the release is cut, the workflow stamps the [Unreleased] heading with
the version and date and opens a fresh empty one for the next cycle. A release
with an empty [Unreleased] section fails on purpose: a release nobody can
read is not ready to ship.
Why we chose these specific workflows¶
A few decisions worth making explicit:
- Human-managed semver, not auto-increment: firmware versions are a contract that user-apps depend on. Contracts need human judgment about what's breaking. We can revisit Conventional-Commits-driven automation later if contributor count grows enough to make commit-message discipline pay off.
- Bump-on-release, not bump-per-commit: shipped firmware is a contract; thousands of "versions" with no semantic meaning would be noise. Commit hashes already disambiguate dev builds.
- Stable gets a post-release bump; RC doesn't: an RC series
iterates toward a single Stable target, so version.txt stays put
through
rc1,rc2,rc3. After Stable ships, advancing version.txt keeps subsequent dev builds from advertising the just-shipped version. - Both manual and workflow-dispatch release paths: optionality has near-zero cost. Manual is faster for emergency hotfixes; workflow- dispatch is friendlier for the standard ritual and harder to forget the post-release bump.
- Default release channel is Stable-only: most users want stability. RC/beta/alpha exist but are opt-in.
- Downgrade is supported: if you flash an older firmware over a newer one, the device wipes settings and proceeds. We don't refuse downgrades -- sometimes you want to roll back.
See also¶
- Firmware version reference - banner format, serial CLI commands, runtime APIs, FAQ for common gotchas
- Open Source - repository links and licenses