Releasing & Versioning
This document defines how freeboard is versioned and how a release is cut. It is the single source of truth for release process; the changelog records the outcome of following it.
Versioning policy (SemVer)
freeboard follows Semantic Versioning: MAJOR.MINOR.PATCH. A single version applies across the workspace packages (they are versioned and released together).
What each bump means for this project:
- MAJOR — a breaking change to a public contract a consumer or deployment depends on:
- the portable
DashboardDocumentcontract in a way existing documents cannot migrate through (see the schema-version note below), - the GraphQL API (removed/renamed/retyped fields, changed semantics),
- the deployment contract (required env vars, datastore schema requiring manual intervention, container/runtime expectations).
- the portable
- MINOR — backward-compatible additions: new widgets or datasources, new optional document fields, additive GraphQL fields, new deployment profiles or opt-in features.
- PATCH — backward-compatible bug fixes, dependency and security patches, docs, and internal refactors with no contract change.
When a change could be argued either way, pick the higher bump. Stability of the published contracts is valued over a low version number.
Two version numbers — keep them distinct
The document schemaVersion (currently 1) is the data contract version of the portable DashboardDocument. It is independent of the application release version (3.x):
- A new
schemaVersionis introduced only for a breaking document-shape change, and ships with amigrate()step in@freeboard/core. As long as older documents migrate forward cleanly, that is not an app MAJOR by itself. - Conversely, app MAJOR/MINOR/PATCH releases happen without touching
schemaVersionwhenever the document contract is unchanged.
Treat schemaVersion as "what shape is persisted" and the release version as "what the software is."
Pre-releases
Use SemVer pre-release identifiers when a soak is wanted before a final tag: X.Y.Z-rc.N (release candidate) or X.Y.Z-beta.N. Pre-release tags are not promoted automatically; cut the final X.Y.Z once validated.
Release process
Green main. Ensure
mainis green in CI.Readiness gate. Run the full release-readiness matrix locally:
bashnpm run check:releaseThis runs the DB schema lifecycle, format/lint/typecheck, all package tests, the end-to-end smoke and static (Lite) E2E flows, and
build:verifyagainst a throwaway Postgres. See Postgres Release Readiness.Lock version, date, scope. Decide the version per the policy above. Confirm every workspace
package.jsoncarries that version.Finalize the changelog. Move the
Unreleasednotes inCHANGELOG.mdinto a new dated entry,## X.Y.Z — YYYY-MM-DD, and resetUnreleasedto empty.Tag. Create an annotated tag and push it:
bashgit tag -a vX.Y.Z -m "freeboard vX.Y.Z" git push origin vX.Y.ZTags are
vX.Y.Z(with the leadingv).GitHub Release. Publish a GitHub Release from the tag, using the new changelog entry as the notes.
Open the next cycle. Subsequent changes accrue under
Unreleaseduntil the next release.
Changelog discipline
- Keep a human-curated
Unreleasedsection current as meaningful changes land — group by what a reader cares about, not by commit. - The changelog is the public record; write entries for users and operators, not as a raw git log.