Skip to content

Versioning & Releases

Nametag follows Semantic Versioning 2.0.0.

MAJOR.MINOR.PATCH (for example, 1.2.3).

  • MAJOR: breaking changes (incompatible API changes, database migrations requiring manual intervention).
  • MINOR: new features, backward-compatible.
  • PATCH: bug fixes, backward-compatible.

Pre-release versions may use suffixes: 1.0.0-beta.1, 1.0.0-rc.2.

Nametag uses Conventional Commits to automatically determine version bumps.

Format:

<type>[optional scope]: <description>
[optional body]
[optional footer(s)]

Types:

  • feat: new feature (triggers a MINOR bump)
  • fix: bug fix (triggers a PATCH bump)
  • perf: performance improvement (triggers a PATCH bump)
  • docs: documentation changes only
  • style: code style changes (formatting, semicolons, and so on)
  • refactor: code restructuring without feature changes
  • test: adding or updating tests
  • chore: maintenance tasks, dependencies, and so on
  • ci: CI/CD configuration changes

Breaking changes:

Add BREAKING CHANGE: in the footer, or ! after the type, to trigger a MAJOR bump:

feat!: redesign authentication system
BREAKING CHANGE: Users must re-authenticate after upgrade

The whole message has to parse, not just the header

Section titled “The whole message has to parse, not just the header”

release-please runs the entire commit message through a strict grammar. On a squash merge that message is the pull request title with the number appended, followed by the pull request description, so the body counts too.

If the parse fails, release-please skips the commit and still exits green. That commit then contributes nothing at all: no changelog entry, and no version bump either. Nothing turns red, the change simply goes missing from the release.

The usual cause is nested parentheses, because the grammar uses them for the commit scope:

fix(dates): keep year-unknown dates on the day they were saved
Asserts formatDateWithoutYear(parseCalendarDate(stored)) across five zones.
^ second "(" before the first closes

Backticks and code fences do not exempt it. Rewrite the nesting as two flat calls, for example formatDateWithoutYear() over parseCalendarDate().

Whether a given nesting actually trips the grammar depends on where in the line it sits, so a nested call mid-sentence sometimes slips through. Avoid it everywhere rather than trying to predict which ones are safe.

The Commit Message workflow checks this on every pull request, using the same parser version release-please uses, and re-runs when the title or description is edited. scripts/check-pr-commit-message.ts prints the offending line and column.

Nametag uses release-please for automated, PR-based releases.

  1. Commits land on master using conventional commit format.
  2. release-please automatically maintains an open Release PR that accumulates all unreleased changes, auto-suggests the next version based on commit types, updates CHANGELOG.md with grouped entries, and bumps the package.json version.
  3. A maintainer reviews and edits the PR: adjusting the version, rewriting the notes, or accepting it as-is.
  4. Merging the PR creates the release: a git tag and a GitHub Release.
  5. An AI step rewrites the GitHub Release notes into a human-readable summary for end users and self-hosters, based on the pull requests included in the release.
  6. The release triggers Docker image builds and publishing automatically.

The summary step calls the Gemini API and needs a GEMINI_API_KEY repository secret. It only runs on the mattogodoy/nametag repository, so forks are unaffected. It can also be re-run by hand from the Actions tab, using the “Release Please” workflow’s manual trigger and passing an existing tag.

Auto-determined: release-please picks the version from commit types, using the same rules as the table below.

Override the version: add a commit with this footer to force a specific version:

chore: prepare for v1.0.0 release
Release-As: 1.0.0

Skip a release: simply don’t merge the Release PR. It keeps accumulating changes until it’s merged.

Commit typeVersion bumpExample
fix:PATCH1.0.0 to 1.0.1
feat:MINOR1.0.0 to 1.1.0
feat!: or BREAKING CHANGE:MAJOR1.0.0 to 2.0.0
docs:, style:, refactor:, test:, chore:No bump(none)

While the version is 0.x.y (pre-1.0), breaking changes bump MINOR instead of MAJOR.

Versions are tracked in:

  • package.json, the npm package version.
  • .release-please-manifest.json, the release-please version tracker.
  • Git tags, v1.2.3.
  • GitHub Releases, with release notes and changelog.
  • Docker tags, ghcr.io/mattogodoy/nametag:1.2.3 and latest.

The current version is shown in:

  • The app footer, on every page.
  • Settings > About.
  • Docker image tags.
  • The GitHub Releases page.

CHANGELOG.md is maintained automatically by release-please from commit messages, updated as part of the Release PR.

  1. Write good commit messages. They become your changelog.
  2. Use conventional commits, and keep the description parseable too. Avoid nested parentheses.
  3. Tag breaking changes explicitly, with ! or BREAKING CHANGE:.
  4. Don’t commit directly to master. Use PRs.
  5. Squash related commits, one feature per commit.
  6. Test before releasing: npm run verify runs all the checks.