Reference

Releasing

Git tagging, automated GitHub Actions workflows, and distribution packaging.

imbox docs

How a version of Imbox is cut. A release is a git tag; the rest is done by .github/workflows/release.yml.

Versions

Imbox uses Semantic Versioning. While the number starts with 0:

  • Middle number (0.2.0): something needs care when you update. A migration that cannot be undone, a setting that was renamed or removed, a feature taken out.
  • Last number (0.1.1): a fix or a small addition that needs nothing from the person updating.
  • 1.0.0 is when the upgrade path from one release to the next is promised to keep working without notes.

A version with a dash (0.2.0-rc.1) is a pre-release. It is built and published, but it is not the latest image tag and GitHub does not offer it as the latest release, so the update notice in Settings never shows it.

Cut a release

  1. Merge what should be in it to main and let CI pass.

  2. Write the changelog. In CHANGELOG.md, rename ## [Unreleased] to ## [0.2.0] - 2026-11-01, add a fresh empty ## [Unreleased] above it, and make sure the "Upgrade notes" say what a person has to do. This text becomes the release notes that people see in the app and on GitHub, so write it for them.

  3. Set the version to the same number in package.json, package-lock.json (npm version 0.2.0 --no-git-tag-version does both) and apps/extension/manifest.json. The extension takes only the plain number: for 0.2.0-rc.1 it stays 0.2.0.

  4. Check it with npm run release:check -- v0.2.0. It says what disagrees.

  5. Commit and merge the changelog and the version bump to main.

  6. Tag it and push the tag:

    git switch main && git pull
    git tag v0.2.0
    git push origin v0.2.0

The workflow then:

  1. checks that the tag, the files and the changelog agree and that the tagged commit is on main;
  2. runs the pull request checks (type check, lint, the tests, the server tests on PostgreSQL);
  3. builds imbox-0.2.0.tgz (the built web app, the Node bundle and the server files), imbox-extension-0.2.0.zip and SHA256SUMS;
  4. builds and pushes ghcr.io/fr3on/imbox:0.2.0, :0.2, and :latest for a stable version, for amd64 and arm64;
  5. creates the GitHub release with the changelog section as its text and the files attached.

If a step fails, nothing is published: the release is only created at the end, after the files and the image exist. Fix the cause, delete the tag (git push origin :v0.2.0, git tag -d v0.2.0) and tag again. Do not move a tag that has been published.

What a build says about itself

The version, commit and build time shown in Settings → About and by /api/version are put in at build time (scripts/version.mjs): the version from package.json, the commit from git or the GIT_COMMIT build argument, the time from the build. The code reads them from src/shared/buildInfo.ts. Nothing is edited or committed per release.

Docker tags

TagMoves when
:0.2.0never
:0.2a new 0.2.x is released
:latesta stable release is published
:edgesomething is merged to main
:sha-abc1234never; one per commit on main

Run a pinned tag (:0.2, or :0.2.0) in production, and read the release notes before moving to the next middle number.

Publish a Railway template

A template is what makes a Deploy on Railway button. It is made in your Railway account, so it can't be created from the repository. First make the repository or the ghcr.io/fr3on/imbox image public, because Railway can only pull what a stranger can pull.

  1. Deploy once by hand, following Railway, then choose Create template from the project's menu.
  2. Add the PostgreSQL database and the service. Keep the three variables as written in the Railway page. Railway fills in the ${{ }} parts for each person who deploys.
  3. Publish it and copy the address, https://railway.com/template/<code>.
  4. Put that address on the Railway card in src/pages/Features.tsx of the website repository, in place of "Coming soon".