ci(release): trigger releases via cron + workflow_dispatch only#32
Conversation
…every merge Previously the release workflow ran on every push to main, which meant that merging any feat/fix PR immediately published a new version. That conflates 'merged to main' with 'shipped' — they should be distinct events: - Merging is cheap and frequent (multiple PRs per day are normal) - Releasing is deliberate (the maintainer wants to review the changeset before users see it) New trigger: - schedule: '0 4 * * *' — nightly at 04:00 UTC. semantic-release inspects commits since the last tag and either publishes or skips silently. - workflow_dispatch: manual trigger from the Actions tab when a release should happen ahead of schedule, with optional dry-run input. To skip a scheduled release, push only commit types that don't bump the version: chore, docs, refactor, test, ci, style, build. Maintainers still do NOT manually tag releases — semantic-release remains the single source of versions and changelogs. Updates: - .github/workflows/release.yml: replace 'on: push' with cron + workflow_dispatch (with dry-run input wired through to semantic- release-action) - CONTRIBUTING.md: replace 'Releases on push to main' section with the new schedule + manual model - docs/decisions/0008-conventional-commits-and-semantic-release.md: same update — ADR is the source of truth for the release pipeline
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request decouples the release process from the merge process by moving away from push-based triggers. By implementing a scheduled nightly release and providing a manual trigger option, the maintainers gain better control over when releases occur, ensuring that frequent merges do not automatically trigger version bumps and deployments. Highlights
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request updates the project's release documentation and architectural decision records to reflect a transition from automated releases on every merge to a scheduled nightly and manual release process. Feedback was provided to correct a technical inaccuracy regarding how releases are skipped; specifically, clarifying that semantic-release evaluates the entire commit history since the last tag, meaning a release is only skipped if no version-bumping commits exist in that history.
|
|
||
| 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. | ||
|
|
||
| **To skip a scheduled release on a given day**, push only commits with types that don't bump the version: `chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`. |
There was a problem hiding this comment.
The instruction suggesting that pushing a non-bumping commit will 'skip' a release is technically inaccurate. semantic-release evaluates the entire history since the last tag. If a version-bumping commit (like feat or fix) is already on main, pushing a chore commit afterwards will not prevent the scheduled release. A release is only skipped if no bumping commits exist in the history since the previous release.
| **To skip a scheduled release on a given day**, push only commits with types that don't bump the version: `chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`. | |
| A release is only published if there are commits with types that trigger a version bump (feat, fix, perf) since the last tag. If only non-bumping types (chore, docs, refactor, test, ci, style, build) are merged, the scheduled run will skip publishing. |
| 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 | ||
|
|
||
| To skip a release on a given day, push only commit types that don't bump the version (`chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`). |
There was a problem hiding this comment.
This sentence implies that a release can be suppressed by pushing a non-bumping commit. In reality, semantic-release will trigger a release if any feat, fix, or perf commits are present since the last tag. It is more accurate to state that a release only occurs when releasable changes are present in the history.
| To skip a release on a given day, push only commit types that don't bump the version (`chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`). | |
| A release is only published if there are commits with types that trigger a version bump (feat, fix, perf) since the last tag. If only non-bumping types (chore, docs, refactor, test, ci, style, build) are merged, the scheduled run skips publishing. |
There was a problem hiding this comment.
Pull request overview
This PR changes the project’s release process so publishing is decoupled from merges to main, making releases occur on a nightly schedule or via a maintainer-triggered manual workflow run, and updates project docs to match that model.
Changes:
- Updated the Release GitHub Actions workflow to trigger via
schedule(04:00 UTC) andworkflow_dispatch(with an optional dry-run), removing thepushtrigger onmain. - Updated ADR 0008 and
CONTRIBUTING.mdto document the new release trigger model and maintainer workflow.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| docs/decisions/0008-conventional-commits-and-semantic-release.md | Updates ADR to describe scheduled + manual release triggering and revised pipeline steps. |
| CONTRIBUTING.md | Updates contributor documentation to explain scheduled releases and manual workflow runs. |
| .github/workflows/release.yml | Removes push-to-main release triggering; adds cron schedule + manual dispatch with a dry-run option. |
| id: semantic | ||
| uses: cycjimmy/semantic-release-action@v6 | ||
| with: | ||
| dry_run: ${{ inputs.dry-run || false }} |
| 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 |
| # To skip a scheduled release on a given day, push commits with types that | ||
| # don't bump the version (chore, docs, refactor, test, ci, style, build). |
|
|
||
| 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. | ||
|
|
||
| **To skip a scheduled release on a given day**, push only commits with types that don't bump the version: `chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`. |
| 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 | ||
|
|
||
| To skip a release on a given day, push only commit types that don't bump the version (`chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`). |
The previous CHANGELOG was generated by an unintended 1.0.0 release that fired on the very first push to main. The release was deleted; the v1.0.0 tag too. A v0.0.0 anchor tag is now in place on the initial commit so the next semantic-release run computes 0.1.0 instead of jumping straight back to 1.0.0. The CHANGELOG itself is now just the header + a paragraph explaining the 0.x development status. Future releases will append notes below the HTML comment marker; semantic-release manages that entirely. Note: pairs with the cron-only release trigger in this same PR — without the trigger change, the next merge would have re-published a release. The combined effect is: tags are now 0.x.y, releases happen on schedule, and the maintainer can dry-run before publishing.
Three findings, all valid, all fixed:
1. workflow_dispatch input syntax error (Copilot)
- 'inputs.dry-run' is parsed as subtraction because of the hyphen
('inputs.dry' minus 'run'). Renamed to 'dry_run' (snake_case) so
the expression resolves correctly.
2. Missing branch guard on release job (Copilot)
- workflow_dispatch can target any ref from the Actions UI, which
would let a maintainer accidentally release from a non-main branch.
Added 'if: github.ref == "refs/heads/main"' on the release job.
3. 'Skip a release by pushing chore commits' is technically wrong
(Gemini + Copilot, three places: ci.yml comment, CONTRIBUTING.md,
ADR 0008)
- semantic-release evaluates the FULL history since the last tag,
not just 'today's' commits. If a feat is already on main from an
earlier merge, adding chore commits afterwards does not suppress
the next release.
- Reworded all three places to: 'a scheduled run only skips when
there are no releasable commits between the last tag and HEAD'.
The only real way to postpone a release window is to defer
version-bumping merges.
ADR 0008 also gains a 'Branch guard' note explaining why the
'if: github.ref == ...' check exists.
|
Thanks both — every finding addressed in 9adfef9. Copilot:
Gemini + Copilot (same finding, three places):
ADR 0008 also gained a 'Branch guard' note explaining the new @gemini-code-assist please re-review. |
There was a problem hiding this comment.
Code Review
This pull request updates the release process documentation and the changelog structure to reflect a shift from per-merge releases to a scheduled nightly cycle. It also clarifies that the project is currently in a pre-1.0 development phase with an unstable API. Feedback was provided regarding a technical inaccuracy in the CHANGELOG.md comment, suggesting that release notes are prepended rather than appended to maintain the intended file layout.
| <!-- | ||
| Release notes are appended below this line by semantic-release on each | ||
| scheduled or manual release run. Do not edit them by hand. | ||
| --> |
There was a problem hiding this comment.
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.
| <!-- | |
| 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. | |
| --> |
…oesn't append Gemini caught this on PR #32: the HTML comment said 'Release notes are appended below this line', but semantic-release with the @semantic-release/ changelog plugin actually PREPENDS new entries (newest releases first, directly under the title). The previous wording would mislead a future maintainer who tries to read the file structure to understand where new entries land. Reworded to: - explain that new entries go directly under the '# Changelog' title - note that the 'Pre-1.0 development' section will sink down the file as new releases land (which is the expected behaviour) - repeat the 'do not edit by hand' rule Refs: Gemini comment on PR #32, CHANGELOG.md:16.
|
Thanks @gemini-code-assist — fixed in dc4fb04. You're right, the marker was wrong:
So the structure after the next release will be: |
|
Thank you for the updates, @strausmann. The documentation in |
…TRIBUTING/AI configs Captures recurring AI-reviewer findings (Gemini Code Assist + Copilot) so contributors and AI tools alike can avoid the same mistakes in the next PR. Initial entries from PR #29 (StatusBlockParser), PR #30 (CI cache fix), and PR #32 (release trigger): Python: - frozen=True dataclasses must not have mutable fields (list, dict, set) - IntFlag is the idiomatic type for combined error/flag states - IntFlag(combined_int) replaces manual bit-mapping loops - Don't include type stubs for forbidden libraries (e.g. types-requests when the project uses httpx exclusively) - Test assertions should be tight (== UNKNOWN, not 'in (A, B) or None') - Catch specific exception types (FrozenInstanceError) not bare Exception GitHub Actions: - workflow_dispatch input names: no hyphens (parsed as subtraction) - workflow_dispatch jobs publishing must have branch guards - semantic-release evaluates full history since last tag, not just 'today's' commits — be precise when describing skip behaviour Process: - Wait for AI reviewers (~1-2 min) even on small PRs; --admin merge before review forfeits the value - Side-effects must be in PR description; reviewers shouldn't have to discover them via line-by-line diff reading Cross-references added in: - CLAUDE.md (point #6 of 'Read these first') - CONTRIBUTING.md (after the §7 Code review section) - .github/copilot-instructions.md (new 'Repeated patterns' section) - .gemini/styleguide.md ('Recurring review patterns' section, asks Gemini to suggest updates to the file rather than re-flagging the same finding) The file is meant to grow: when an AI review surfaces a new recurring pattern, the rule is to add it in the same follow-up commit.
…TRIBUTING/AI configs (#33) Captures recurring AI-reviewer findings (Gemini Code Assist + Copilot) so contributors and AI tools alike can avoid the same mistakes in the next PR. Initial entries from PR #29 (StatusBlockParser), PR #30 (CI cache fix), and PR #32 (release trigger): Python: - frozen=True dataclasses must not have mutable fields (list, dict, set) - IntFlag is the idiomatic type for combined error/flag states - IntFlag(combined_int) replaces manual bit-mapping loops - Don't include type stubs for forbidden libraries (e.g. types-requests when the project uses httpx exclusively) - Test assertions should be tight (== UNKNOWN, not 'in (A, B) or None') - Catch specific exception types (FrozenInstanceError) not bare Exception GitHub Actions: - workflow_dispatch input names: no hyphens (parsed as subtraction) - workflow_dispatch jobs publishing must have branch guards - semantic-release evaluates full history since last tag, not just 'today's' commits — be precise when describing skip behaviour Process: - Wait for AI reviewers (~1-2 min) even on small PRs; --admin merge before review forfeits the value - Side-effects must be in PR description; reviewers shouldn't have to discover them via line-by-line diff reading Cross-references added in: - CLAUDE.md (point #6 of 'Read these first') - CONTRIBUTING.md (after the §7 Code review section) - .github/copilot-instructions.md (new 'Repeated patterns' section) - .gemini/styleguide.md ('Recurring review patterns' section, asks Gemini to suggest updates to the file rather than re-flagging the same finding) The file is meant to grow: when an AI review surfaces a new recurring pattern, the rule is to add it in the same follow-up commit.
## 0.1.0 (2026-05-10) * docs: refactor — ADRs in docs/decisions/, policies in docs/policies/, slim CLAUDE.md ([51c2cf1](51c2cf1)), closes [#1](#1) * docs(ci): document mypy as hard gate; clarify CI gate policy in CONTRIBUTING (#31) ([e1e6f18](e1e6f18)), closes [#31](#31) [#30](#30) [#30](#30) [#30](#30) * docs(decisions): ADR 0006 — PT vs QL ESC i S behaviour from Phase-0 hardware test ([0d12c63](0d12c63)), closes [#12](#12) [#11](#11) * docs(decisions): ADR 0012 — layout management; clarify integration push/pull capabilities ([effcbdf](effcbdf)), closes [#17](#17) [#19](#19) * docs(decisions): ADR 0013 + cart UI spec + AI-review workflow ([c72dc85](c72dc85)), closes [#26](#26) [#27](#27) [#28](#28) * docs(docker): document image tag scheme (latest, 1.0.0, 1.0, 1) ([1f72396](1f72396)) * docs(examples): sample compose files for standalone/Traefik/Pangolin/Caddy ([a2f6f3d](a2f6f3d)) * docs(learnings): add code-review-patterns + reference from CLAUDE/CONTRIBUTING/AI configs (#33) ([0fc61d2](0fc61d2)), closes [#33](#33) [#29](#29) [#30](#30) [#32](#32) [#6](#6) * ci: make Python lint/test job branch-tolerant for dependabot PRs (#30) ([6ee8e16](6ee8e16)), closes [#30](#30) [2-#9](https://github.com/2-/issues/9) [#2](#2) [#3](#3) [#4](#4) [#6](#6) [#7](#7) [#8](#8) [#9](#9) * ci(deps): bump the actions-all group across 1 directory with 14 updates (#9) ([a88a027](a88a027)), closes [#9](#9) * ci(release): trigger releases via cron + workflow_dispatch only (#32) ([9941958](9941958)), closes [#32](#32) [#32](#32) [#32](#32) * chore(deps-dev): bump @commitlint/cli from 19.8.1 to 21.0.0 (#3) ([2642b26](2642b26)), closes [#3](#3) * chore(deps-dev): bump @commitlint/config-conventional (#4) ([44be5f9](44be5f9)), closes [#4](#4) * chore(deps-dev): bump @semantic-release/exec from 6.0.3 to 7.1.0 (#2) ([a8097b0](a8097b0)), closes [#2](#2) * chore(deps-dev): bump @semantic-release/github from 11.0.6 to 12.0.8 (#8) ([098e766](098e766)), closes [#8](#8) * chore(deps-dev): bump conventional-changelog-conventionalcommits (#7) ([9f5370f](9f5370f)), closes [#7](#7) * chore(deps-dev): bump semantic-release from 24.2.9 to 25.0.3 (#6) ([306010f](306010f)), closes [#6](#6) * chore(release): 1.0.0 ([15abeb3](15abeb3)) * feat(status): add Brother 32-byte status block parser (#29) ([ced0ff8](ced0ff8)), closes [#29](#29) [#11](#11) [#19](#19) [#29](#29) [skip ci]
Summary
The release workflow currently runs on every push to `main`. That means every merged PR immediately publishes a release — which is wrong for two reasons:
Change
`release.yml` now triggers on:
NOT on push. Merging still works normally; the next release window picks up the bundled changes.
To skip a scheduled release on a given day, push only commit types that don't bump the version (`chore`, `docs`, `refactor`, `test`, `ci`, `style`, `build`).
Linked issue
(maintainer feedback in chat — no public issue)
Type of change
Hardware tested on
Test coverage
Checklist
Follow-ups