8000 feat(release): semantic versions and release notes from Conventional Commits in one workflow run by adeelahmad · Pull Request #17 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(release): semantic versions and release notes from Conventional Commits in one workflow run - #17

Merged
adeelahmad merged 8 commits into
mainfrom
claude/automate-releases-xzhmzp
Oct 2, 2026
Merged

adeelahmad merged 8 commits into
mainfrom
claude/automate-releases-xzhmzp

Conversation

@adeelahmad
@adeelahmad adeelahmad commented Oct 2, 2026 •
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: to release, you bumped two version files and wrote the changelog by hand, then ran the template workflow, which only made a draft for you to publish. Commit messages and PR titles followed no format. The repo had no contributing guide, security policy, issue forms or Dependabot config.

After: PR titles are Conventional Commits (feat(chat): …, fix!: …). A PR title check enforces them on every PR, and a commit-msg hook checks commit messages once you run make hooks. One run of Release (Actions tab, or gh workflow run release.yml) then does the rest:

  • It picks the semantic version from the titles merged since the last release. A breaking change bumps major (minor before 1.0), feat bumps minor, and anything else bumps patch. You can give patch, minor, major or a newer X.Y.Z instead.
  • It writes the new changelog section. Any hand-written ## Unreleased text comes first. After it come the PRs, grouped into Breaking changes, Features, Fixes, Performance, Reverts, Dependencies and Other changes, each linked to its PR. docs, refactor, test, build, ci, chore and style are left out, except build(deps), which goes under Dependencies. The ## Unreleased heading stays in place, so open branches that add bullets under it still merge.
  • It sets the version in pyproject.toml, uv.lock, package.json and CloudronManifest.json (when present).
  • It commits chore(release): vX.Y.Z to main, tags it, and publishes the release with that section as its notes. Only the newest version is marked Latest.
  • It starts the package builds.

Pushing a vX.Y.Z tag by hand also publishes, without the commit. If a run pushes its commit and tag but fails before publishing, running it again (or re-running it) publishes that version instead of cutting another.

The pre-commit hooks already ran ruff, ESLint, Prettier and tsc. They now also check YAML, TOML and the workflows (actionlint). I kept the repo's existing pre-commit framework, which installs through uv, so husky isn't added.

How: .github/scripts/release.py holds the rules (cut, notes, check-title, check-message), so CI, the hook and the release all use one parser. It reads first-parent history:

  • a merge commit stands for its PR, using the title GitHub writes into it;
  • a squash merge is read by its (#N);
  • a direct commit is read by its subject.

Older titles that don't follow the format go under Other changes.

Repository health files

  • CONTRIBUTING.md: setup, hooks, tests, Conventional Commits and the release flow. It links to docs/contributing.md for the full guide. A License section says the license isn't decided yet and that LICENSE.txt is still the template's MIT notice, left untouched. A TODO(license) placeholder marks what to change when Lens goes public.
  • SECURITY.md: while the repo is private, reports go privately to the maintainer, since GitHub's private vulnerability reporting only works on public repos. There's no bug bounty. It lists what matters most for Lens: namespace access, signed media links, auth, stored credentials, SSRF and injection.
  • CODE_OF_CONDUCT.md: Contributor Covenant 2.1, with @adeelahmad as the contact.
  • Issue forms: a bug report form (version, how Lens runs, model server, logs) and a feature request form. Blank issues are off, and a contact link points security reports to SECURITY.md.
  • PR template: asks for a Conventional Commit title and an optional Unreleased note.
  • .github/dependabot.yml: uv and pnpm updates run weekly, with minor and patch updates grouped. Docker images and Actions update monthly. Titles look like build(deps): bump … or ci(deps): ….

Packages

A release published with GITHUB_TOKEN doesn't fire release: published in other workflows. The publish job therefore starts cloudron.yml, qnap.yml and synology.yml itself with gh workflow run <file> --ref vX.Y.Z -f tag=vX.Y.Z. It skips any workflow that isn't in the tag.

You can instead add a RELEASE_TOKEN secret (fine-grained, Contents: write). The workflow then commits and publishes with that token, which also gets past branch protection on main, and the normal event fires. That token's tag push starts a second run. The second run waits for the first (concurrency group) and finds the release already published, so nothing is published twice.

What each packaging PR needs:

Open PRs that edit CHANGELOG.md

They need no change: their Unreleased entries become the top of the next section. They only need a Conventional Commit title before they merge.

Motivation and Context

Releases should be automated, and every packaging PR waits on a published release.

Steps to reproduce (if appropriate):

I tested on a clone of this repo's history, with no tags on it.

  • release.py cut auto with a feat!: commit on top gave 0.4.0, with the right sections and all 4 version files bumped. A build(deps): bump … commit was listed under Dependencies.
  • cut 0.3.0 and cut 0.2.9 are refused because neither is newer than the current version.
  • I ran the cut, commit, tag and atomic push against a local bare remote.
  • check-title accepts conventional and Dependabot titles. It rejects Chat: answers, feature: x and feat:x.
  • In that clone, the commit-msg hook blocked a bad message.
  • pre-commit run and actionlint pass, and GitHub validated dependabot.yml.

No tags or releases were made on this repo.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires documentation updates.
  • I have updated the documentation accordingly.
  • My change requires dependencies updates.
  • I have updated the dependencies accordingly.

🤖 Generated with Claude Code

https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6

Release (workflow_dispatch with patch/minor/major/X.Y.Z) moves the
Unreleased changelog entries under the new version, bumps the version
files, commits and tags vX.Y.Z on main, and publishes the release with
that changelog section as notes. A hand-pushed vX.Y.Z tag is published
the same way. Package workflows are started with gh workflow run, since
a release published with GITHUB_TOKEN doesn't fire release: published;
with a RELEASE_TOKEN secret the event fires and nothing is dispatched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
@adeelahmad adeelahmad self-assigned this Oct 2, 2026
Release defaults to bump=auto: the version and changelog section come
from the Conventional Commit titles merged since the last release, under
any hand-written Unreleased text. PR titles are checked in CI and commit
messages by a commit-msg pre-commit hook (`make hooks`); the hooks also
check YAML, TOML and workflows (actionlint).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
@adeelahmad adeelahmad changed the title Cut and publish releases from one workflow run ci: automated releases with semantic versions, changelog and Conventional Commits Oct 2, 2026
claude added 6 commits October 2, 2026 12:50
…ms, Dependabot

Adds CONTRIBUTING.md, SECURITY.md (private reporting through GitHub),
CODE_OF_CONDUCT.md (Contributor Covenant 2.1), bug and feature issue
forms, a PR template asking for a Conventional Commit title, and
Dependabot for uv, pnpm, Docker and Actions with build(deps)/ci(deps)
titles, which the changelog lists under Dependencies.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
Vulnerabilities go privately to the maintainer while the repository is
private (GitHub's private reporting only works on public ones), with no
bug bounty. CONTRIBUTING says the license is undecided and that
LICENSE.txt is the template's, with a placeholder for when Lens goes
public.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
- The default-branch check reads the ref from env, not interpolated
  into the script.
- A run that pushed its commit and tag but didn't publish is resumed:
  the cut job checks out the branch tip, and when that is an unpublished
  chore(release) commit it publishes that tag instead of cutting. A tag
  push is always published if it isn't yet (the concurrency group and
  the existing-release check replace the commit-message skip).
- An exact version must be newer than the current one, and --latest is
  set only for the newest version.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
…ange once

- For merge commits, BREAKING CHANGE: / BREAKING-CHANGE: footers are read
  from the PR's own commits, so a breaking PR bumps major.
- A PR that wrote its own Unreleased entry isn't listed again from its
  title (unless it's breaking).
- A re-run resumes from the newest unreleased chore(release) tag, even
  after more merges on main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016KX8N4jaryKPQD3n7MZPx6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SNbRrpHTCqLewRKdkmd45X
@adeelahmad adeelahmad changed the title ci: automated releases with semantic versions, changelog and Conventional Commits feat(release): semantic versions and release notes from Conventional Commits in one workflow run Oct 2, 2026
@adeelahmad
adeelahmad marked this pull request as ready for review October 2, 2026 13:14
@adeelahmad
adeelahmad merged commit 15d3a15 into main Oct 2, 2026
0 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0