Versioning
The single source of truth in app/__version__.py, how it flows to PyPI, npm, and Docker, the PEP 440 vs SemVer forms, and the three release channels.
Cremind has one version number that every artifact carries. This page covers where it lives, how it propagates, the tag conventions, and the three release channels.
The single source of truth
app/__version__.py's __version__ field is the single source of truth. Cremind is currently at 0.0.1.
__version__ = "0.0.1"
MIN_SUPPORTED_UPGRADE_FROM = "0.0.0"Everything else derives from this one field:
pyproject.tomlreads it via[tool.hatch.version] path = "app/__version__.py".- The runtime
/versionendpoint, thecremind versionCLI, and the upgrader all read it. ui/package.jsonis regenerated byscripts/sync_ui_version.py, wired into thepredev/prebuild/preweb:dev/preweb:buildnpm hooks. Never editui/package.json's version by hand — it gets overwritten.
During an RC build, release-rc.yml rewrites app/__version__.py in-CI only to the PEP 440 dev form (the RC tag with the leading v stripped). The rewrite is never committed. The production workflow uses the value exactly as committed on main, which is why a production tag can't ship until the Core Maintainer commits the bump. See Releasing.
MIN_SUPPORTED_UPGRADE_FROM
MIN_SUPPORTED_UPGRADE_FROM in the same file is the oldest version this build knows how to migrate from. The upgrader refuses to proceed when the live install is older. It's currently 0.0.0, which accepts any install.
Bump it only when explicitly dropping support — that is, when a schema migration genuinely requires a minimum prior schema version — not on every release. See Schema migrations.
Tag conventions
RC tags carry the PEP 440 canonical form verbatim with a leading v — the same string lands on GitHub, Test PyPI, and Docker Hub (modulo the v). The .dev<M> counter is mandatory.
| Tag pattern | PEP 440 | npm / installer | Docker tag | PyPI | Channel |
|---|---|---|---|---|---|
v0.0.2rc1.dev1 | 0.0.2rc1.dev1 | 0.0.2-rc.1.dev.1 | cremind-desktop:0.0.2rc1.dev1 | Test PyPI | test |
v0.0.2 | 0.0.2 | 0.0.2 | cremind-desktop:0.0.2 + :latest | PyPI | production |
Within a version's slate:
rc<N>indexes the PR (rc1for the first PR,rc2for the second, …) and is assigned by the Core Maintainer.dev<M>increments per bug-fix iteration on that PR (dev1,dev2, …).
Hotfixes use the four-segment form: v0.0.2.1 (production) built from v0.0.2.1rc<N>.dev<M> (dev release).
One strict, canonical shape
RC tags must match this regex exactly:
^v(\d+\.\d+\.\d+(?:\.\d+)?)rc(\d+)\.dev(\d+)$The legacy hyphenated form (v<X.Y.Z>-rc.<N>) and the bare v<X.Y.Z>rc<N> without a .dev<M> counter are both rejected — release-rc.yml refuses to publish them, and the in-app updater ignores GitHub prereleases whose tag fails this regex.
PEP 440 vs npm/SemVer
PyPI, GitHub, and Docker Hub use the PEP 440 no-separator form (0.0.2rc1.dev1). npm and electron-builder reject that as a prerelease identifier, so scripts/sync_ui_version.py derives a SemVer form internally (0.0.2-rc.1.dev.1) for ui/package.json. That SemVer string never appears on GitHub, PyPI, or Docker Hub — it exists only inside the UI build.
The three channels
| Channel | Installs from | Used for |
|---|---|---|
production | PyPI (and cremind-desktop:latest) | End users. Stable, tagged vX.Y.Z. |
test | Test PyPI | Validating a dev release before merge — see Releasing. |
dev | Your local checkout (pip install -e .) | Iterating on installer scripts or the Docker bundle — see Dev-channel install. |
Production installs poll for updates and pick up the highest stable version; test installs pick up the highest dev wheel from Test PyPI; dev installs don't auto-upgrade at all (you pull from git).
Next
Releasing
The validate-before-merge release pipeline — RC dev tags from the PR branch, merge-commit, and the production gates that ship to PyPI, GitHub, and Docker Hub.
Schema migrations
Generating, hand-reviewing, and shipping database migrations safely — autogenerate, the additive-then-destructive rule, and testing real upgrades.