Release flow
The flow
Section titled “The flow”PR merge (main) ──→ release-please opens / updates a release PR │ │ (release PR merged) ▼ semver git tag pushed (v0.7.2) │ ▼ docker-publish.yml workflow runs │ ▼ Multi-arch images pushed to Docker Hub: scani/api:0.7.2, :0.7, :0, :latest scani/worker:... scani/data-provider:... scani/frontend-app:...release-please
Section titled “release-please”release-please is a
GitHub Action that watches main for conventional-commit messages.
It maintains a “release PR” that accumulates the next version’s
changelog and bumps the manifest.
| Commit prefix | Triggers release? | Bump |
|---|---|---|
feat: | yes | minor |
fix: | yes | patch |
feat!: or BREAKING CHANGE: | yes | minor pre-1.0, major post-1.0 |
docs:, refactor:, chore:, test:, ci: | no | — |
Configuration: release-please-config.json. Pre-1.0, the
bump-minor-pre-major: true setting promotes breaking changes to
minor bumps so the version number stays in the 0.x series.
When the release PR merges, release-please pushes a git tag
(v0.7.2) which triggers the docker-publish workflow.
Image publish
Section titled “Image publish”.github/workflows/docker-publish.yml builds four images on:
- Pushes to
main— tags:latestand:sha-<short>. - Semver tag pushes — tags
:1.2.3,:1.2,:1, and:latest. - PRs — builds amd64-only (no push) to catch image-build regressions early.
workflow_dispatch— manual trigger.
Architectures: linux/amd64 + linux/arm64 on main/tag pushes.
PRs are amd64-only for speed.
Images published to Docker Hub under the scani/ namespace:
scani/apiscani/workerscani/data-providerscani/frontend-app
The frontend image bakes VITE_API_URL=/api (a relative path) so
nginx can do the backend routing at runtime — no rebuild per
deployment.
Conventional commit prefixes — the honest list
Section titled “Conventional commit prefixes — the honest list”Use the prefix that actually describes the change. release-please trusts it.
| Prefix | Use for | Examples |
|---|---|---|
feat: | New user-visible feature. | Adding a Kraken adapter; adding the vaults dashboard. |
fix: | Bug fix. | Wrong cost basis after a re-import; broken splash hero on mobile. |
refactor: | Code change with no behaviour change. | Renaming a service; moving a helper. |
chore: | Tooling, deps, CI, build. | Bumping Bun version; adding a CI step. |
docs: | Docs-only change. | This entire docs site. |
test: | Tests-only change. | Adding a regression test for a fixed bug. |
ci: | CI / workflow changes. | New GitHub Actions job. |
perf: | Performance improvement. | Two-query transfer matcher replacing N queries. |
feat!: / fix!: / refactor!: + BREAKING CHANGE: footer signal
a breaking change.
DCO sign-off
Section titled “DCO sign-off”Every commit needs a Signed-off-by: trailer — generated by
git commit -s. This is the Developer Certificate of Origin: you
certify you have the right to contribute the code under the
project’s MIT license.
CI rejects PRs with unsigned commits.
What to do when release-please opens a release PR
Section titled “What to do when release-please opens a release PR”- Don’t merge it immediately. Wait until CI is green and you’ve read the auto-generated changelog. The changelog is what users read when they upgrade — fix awkward wording before merging.
- Don’t add commits to the release PR. release-please owns it and will rebase. If the changelog needs a fix, change the commit messages it draws from, not the release PR.
- Don’t merge the release PR while another release PR is open upstream. release-please tracks state via labels; manually closing/reopening can desync.
When you don’t want a release
Section titled “When you don’t want a release”If you accidentally land a feat: commit that shouldn’t trigger
a release (e.g. an internal-only change), follow up with an
empty commit:
git commit --allow-empty -m "chore: re-classify previous as internal"…and edit the release PR’s changelog entry to remove the spurious feature. Awkward but rare.