Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
name: Release

# Releases are triggered on a fixed schedule, not on every merge.
# This separates "code merged to main" from "version published" — merging
# is still cheap and frequent, releasing is a deliberate, reviewable event.
#
# Triggers:
# - schedule: nightly at 04:00 UTC. semantic-release inspects all commits
# since the previous tag and publishes if there's at least one
# releasable commit (feat, fix, perf). Otherwise the run skips silently.
# - workflow_dispatch: manual trigger from the Actions tab when a
# release should happen ahead of schedule.
#
# When a scheduled run skips publishing:
# semantic-release evaluates the ENTIRE history since the last tag, not
# just commits made since the previous run. A scheduled release only
# skips if NO releasable commits (feat/fix/perf) exist between the last
# tag and HEAD. Adding a chore/docs/etc. commit on top of an already-
# present feat does NOT suppress the release — the existing feat will
# still trigger it.
on:
push:
branches: [main]
schedule:
- cron: '0 4 * * *' # daily at 04:00 UTC
workflow_dispatch:
inputs:
dry_run:
description: "Dry run — analyse commits but don't publish"
required: false
default: false
type: boolean
Comment on lines 21 to +30

permissions:
contents: write # create tags + GitHub Releases
Expand All @@ -20,6 +44,9 @@ jobs:
release:
name: semantic-release
runs-on: ubuntu-24.04
# workflow_dispatch can be triggered against any ref via the Actions UI.
# Releases must only ever come from main — refuse to run on anything else.
if: github.ref == 'refs/heads/main'
outputs:
new-release-published: ${{ steps.semantic.outputs.new_release_published }}
new-release-version: ${{ steps.semantic.outputs.new_release_version }}
Expand All @@ -38,6 +65,7 @@ jobs:
id: semantic
uses: cycjimmy/semantic-release-action@v6
with:
dry_run: ${{ inputs.dry_run || false }}
extra_plugins: |
@semantic-release/changelog@6
@semantic-release/git@10
Expand Down
26 changes: 9 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,13 @@ All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org/) and uses [Conventional Commits](https://www.conventionalcommits.org/) via [semantic-release](https://github.com/semantic-release/semantic-release).

## 1.0.0 (2026-05-10)
## Pre-1.0 development

* ci: make Python lint/test job branch-tolerant for dependabot PRs (#30) ([6ee8e16](https://github.com/strausmann/label-printer-hub/commit/6ee8e16)), closes [#30](https://github.com/strausmann/label-printer-hub/issues/30) [2-#9](https://github.com/2-/issues/9) [#2](https://github.com/strausmann/label-printer-hub/issues/2) [#3](https://github.com/strausmann/label-printer-hub/issues/3) [#4](https://github.com/strausmann/label-printer-hub/issues/4) [#6](https://github.com/strausmann/label-printer-hub/issues/6) [#7](https://github.com/strausmann/label-printer-hub/issues/7) [#8](https://github.com/strausmann/label-printer-hub/issues/8) [#9](https://github.com/strausmann/label-printer-hub/issues/9)
* ci(deps): bump the actions-all group across 1 directory with 14 updates (#9) ([a88a027](https://github.com/strausmann/label-printer-hub/commit/a88a027)), closes [#9](https://github.com/strausmann/label-printer-hub/issues/9)
* chore: initial repository skeleton ([0319e7e](https://github.com/strausmann/label-printer-hub/commit/0319e7e))
* chore(deps-dev): bump @commitlint/cli from 19.8.1 to 21.0.0 (#3) ([2642b26](https://github.com/strausmann/label-printer-hub/commit/2642b26)), closes [#3](https://github.com/strausmann/label-printer-hub/issues/3)
* chore(deps-dev): bump @commitlint/config-conventional (#4) ([44be5f9](https://github.com/strausmann/label-printer-hub/commit/44be5f9)), closes [#4](https://github.com/strausmann/label-printer-hub/issues/4)
* chore(deps-dev): bump @semantic-release/exec from 6.0.3 to 7.1.0 (#2) ([a8097b0](https://github.com/strausmann/label-printer-hub/commit/a8097b0)), closes [#2](https://github.com/strausmann/label-printer-hub/issues/2)
* chore(deps-dev): bump @semantic-release/github from 11.0.6 to 12.0.8 (#8) ([098e766](https://github.com/strausmann/label-printer-hub/commit/098e766)), closes [#8](https://github.com/strausmann/label-printer-hub/issues/8)
* chore(deps-dev): bump conventional-changelog-conventionalcommits (#7) ([9f5370f](https://github.com/strausmann/label-printer-hub/commit/9f5370f)), closes [#7](https://github.com/strausmann/label-printer-hub/issues/7)
* chore(deps-dev): bump semantic-release from 24.2.9 to 25.0.3 (#6) ([306010f](https://github.com/strausmann/label-printer-hub/commit/306010f)), closes [#6](https://github.com/strausmann/label-printer-hub/issues/6)
* feat(status): add Brother 32-byte status block parser (#29) ([ced0ff8](https://github.com/strausmann/label-printer-hub/commit/ced0ff8)), closes [#29](https://github.com/strausmann/label-printer-hub/issues/29) [#11](https://github.com/strausmann/label-printer-hub/issues/11) [#19](https://github.com/strausmann/label-printer-hub/issues/19) [#29](https://github.com/strausmann/label-printer-hub/issues/29)
* docs: refactor — ADRs in docs/decisions/, policies in docs/policies/, slim CLAUDE.md ([51c2cf1](https://github.com/strausmann/label-printer-hub/commit/51c2cf1)), closes [#1](https://github.com/strausmann/label-printer-hub/issues/1)
* docs(decisions): ADR 0006 — PT vs QL ESC i S behaviour from Phase-0 hardware test ([0d12c63](https://github.com/strausmann/label-printer-hub/commit/0d12c63)), closes [#12](https://github.com/strausmann/label-printer-hub/issues/12) [#11](https://github.com/strausmann/label-printer-hub/issues/11)
* docs(decisions): ADR 0012 — layout management; clarify integration push/pull capabilities ([effcbdf](https://github.com/strausmann/label-printer-hub/commit/effcbdf)), closes [#17](https://github.com/strausmann/label-printer-hub/issues/17) [#19](https://github.com/strausmann/label-printer-hub/issues/19)
* docs(decisions): ADR 0013 + cart UI spec + AI-review workflow ([c72dc85](https://github.com/strausmann/label-printer-hub/commit/c72dc85)), closes [#26](https://github.com/strausmann/label-printer-hub/issues/26) [#27](https://github.com/strausmann/label-printer-hub/issues/27) [#28](https://github.com/strausmann/label-printer-hub/issues/28)
* docs(docker): document image tag scheme (latest, 1.0.0, 1.0, 1) ([1f72396](https://github.com/strausmann/label-printer-hub/commit/1f72396))
* docs(examples): sample compose files for standalone/Traefik/Pangolin/Caddy ([a2f6f3d](https://github.com/strausmann/label-printer-hub/commit/a2f6f3d))
The project is in active **pre-1.0 development**. The public API (REST endpoints, plugin protocol, configuration shape) may change between minor versions until `1.0.0` ships. Releases on the `0.x.y` track are explicitly *not* covered by SemVer's stable-API contract.

When `1.0.0` ships, that release will mark the first stable, supported version. The expected breaking-change cadence will follow normal SemVer rules from there on.

<!--
Release notes are appended below this line by semantic-release on each
scheduled or manual release run. Do not edit them by hand.
-->
Comment on lines +13 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment states that release notes are "appended below this line". However, the previous state of the changelog (where the 1.0.0 entry was at the top) indicates that semantic-release is configured to prepend new entries. Unless the .releaserc.json configuration is being explicitly changed to an append strategy, this comment is technically inaccurate. Furthermore, if the tool continues to prepend, it will insert new versions at the top of the file, pushing the "Pre-1.0 development" section down, which may not be the intended layout.

Suggested change
<!--
Release notes are appended below this line by semantic-release on each
scheduled or manual release run. Do not edit them by hand.
-->
<!--
Release notes are prepended to this file by semantic-release on each
scheduled or manual release run. Do not edit them by hand.
-->

14 changes: 11 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,18 @@ The CI workflow runs the same. PRs that fail any check won't be reviewed until g

## Releases

Releases are **fully automated** via semantic-release on push to `main`:
- Conventional commit messages → version bump + changelog → tag → GitHub Release → Docker images on GHCR + Docker Hub.
Releases are **scheduled, not on every merge.** Merging to `main` does NOT publish a release — it only bundles changes for the next scheduled release window.

Maintainers do not manually tag releases.
| Trigger | When |
|---|---|
| `cron: '0 4 * * *'` | Nightly at 04:00 UTC. semantic-release analyses commits since the last tag and publishes if there's anything releasable, otherwise skips silently |
| `workflow_dispatch` | Manual button in the Actions tab. Optional `dry-run` input lets a maintainer preview what would be released without publishing |

Behind the scenes, semantic-release reads the Conventional Commit messages on `main` since the previous tag, decides the next semver version, generates the `CHANGELOG.md` entry, creates the Git tag and GitHub Release, and triggers the Docker image push to GHCR and Docker Hub.

**A scheduled run only skips publishing when there are no releasable commits since the last tag.** semantic-release evaluates the entire history since the previous release, not just "today's" commits. If a `feat`, `fix`, or `perf` commit is already on `main` from an earlier merge, the next scheduled run **will** publish — adding a `chore` or `docs` commit on top doesn't suppress that. To genuinely skip a release window, the only options are: rely on there being no releasable commits, or postpone merges that would bump the version.

Maintainers do not manually tag releases. If a hotfix needs to ship before the next scheduled window, use the **Run workflow** button on the Release workflow in the Actions tab. The dry-run input lets a maintainer preview which version would be published before committing.

## Trademarks

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ We adopt **[Conventional Commits 1.0.0](https://www.conventionalcommits.org/)**

Allowed scopes: `printer-models`, `queue`, `status`, `api`, `ui`, `webhook`, `docker`, `ci`, `examples`, `docs`, `integration`, `pwa`, `security`, `release`. Validated by `commitlint` on PR titles.

**Release trigger:** push to `main`. Maintainers do not manually tag releases.
**Release trigger:** **scheduled (nightly 04:00 UTC) + manual via `workflow_dispatch`**. Merges to `main` do NOT publish releases — they only accumulate changes for the next release window. This separation means merging stays cheap and frequent, while releasing remains a deliberate event the maintainer can review or postpone.

Maintainers do not manually tag releases. To trigger an unscheduled release (e.g. for a security hotfix), use the **Run workflow** button on the Release workflow.

**Release pipeline:**
1. Push to `main`
2. `release.yml` workflow runs `semantic-release`
3. semantic-release inspects commits since last tag → decides version bump
4. Updates `CHANGELOG.md`, creates Git tag, creates GitHub Release
1. Schedule fires at 04:00 UTC (or maintainer hits "Run workflow")
2. `release.yml` workflow runs `semantic-release` against `main` HEAD
3. semantic-release inspects commits since last tag → decides version bump (or skips if no releasable commits exist)
4. If a release is due: updates `CHANGELOG.md`, creates Git tag, creates GitHub Release
5. Release-published event triggers `docker-publish.yml` → builds and pushes both backend and frontend images per ADR 0007

**When a scheduled run skips publishing:** semantic-release evaluates *all* commits between the last tag and HEAD, not just commits made since the previous run. A scheduled release only skips when there are no `feat`, `fix`, or `perf` commits in that range. If a previous merge already added a `feat` commit, the next scheduled run will publish — adding `chore` or `docs` commits afterwards does not suppress the release. To intentionally postpone a release window, the only mechanism is to defer merges that would bump the version.

**Branch guard:** the release job runs only when `github.ref == 'refs/heads/main'`. `workflow_dispatch` can be triggered against any branch from the Actions UI; the guard ensures a release can never be published from a non-main ref.

## Options considered

### Option A — Conventional Commits + semantic-release (chosen)
Expand Down
Loading