Skip to content

ci: release automatically from conventional PR titles - #180

Merged
KartikP merged 5 commits into
mainfrom
kp/auto-release
Oct 9, 2026
Merged

KartikP merged 5 commits into
mainfrom
kp/auto-release

Conversation

@KartikP

@KartikP KartikP commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Every qualifying merge to main is versioned and published automatically. There is no bump PR, no approval step and no personal access token.

Release rule (.github/workflows/release.yml, one decision per squash merge)

  1. Path gate: the merge must change a shipped file. For core that means a file under brainscore_core/ or the [project] dependencies in pyproject.toml. Changes only to tests, docs, .github, *.md, sqlite3.db and so on never release, whatever the title says.
  2. Title type: feat gives a minor release; fix and perf give a patch. Every other allowed type gives no release: refactor, docs, test, ci, chore, build, plugin, model, benchmark, data, metric.
  3. Major: needs ! in the title or a BREAKING CHANGE footer, and the major update label on the PR. Without the label it is downgraded to minor.

The workflow computes the level and passes it to python-semantic-release as a forced bump (force: patch|minor|major). python-semantic-release then:

  • updates version in pyproject.toml;
  • commits chore(release): X.Y.Z with the skip-ci marker;
  • tags vX.Y.Z and pushes to main with a GitHub App token (actions/create-github-app-token);
  • builds the sdist and wheel, and creates a GitHub release.

A separate publish job in the pypi environment uploads with pypa/gh-action-pypi-publish through trusted publishing (id-token: write, no stored PyPI token).

The level is forced rather than left to python-semantic-release, because it would otherwise read every commit since the last tag, and a skipped feat (for example, a tests-only merge) would leak into the next release.

PR title check (.github/workflows/pr-title.yml, amannn/action-semantic-pull-request)

  • It runs on pull_request_target (opened, edited, synchronize, reopened, labeled, unlabeled), so fork PRs get it too. It never checks out PR code, and its only permission is pull-requests: write.
  • On an invalid title it posts one sticky comment showing:
    • the failing title;
    • each type with the release it causes;
    • the major-label rule and the path rule;
    • the type(scope): subject format and three examples.
  • The comment updates in place on each edit and is deleted once the title passes.
  • A second sticky comment warns when a breaking title or body has no major update label.

Reuse: both workflows also declare workflow_call. Vision and language call them from main (secrets: inherit), and their only setting is the release-paths regex. For vision, for example: ^brainscore_vision/(?!(models|benchmarks|data|metrics)/), so plugin-only PRs never release.

Local checks

  • Path and title gate on a scratch repo:
Merge Level
feat under brainscore_core/ minor
feat only under tests/ none
fix patch
docs in the package none
build(deps) none
fix(deps) changing dependencies patch
version-only pyproject edit none
feat! without the label minor
feat! with the label major
plugin none
  • python-semantic-release 10.7.0 --noop on current main: forced patch gives 2.3.9, minor gives 2.4.0, major gives 3.0.0.
  • The vision regex keeps model_helpers/ and __init__.py as shipped, and excludes models/, benchmarks/ and metrics/.
  • Neither new workflow runs on this PR (push-only and pull_request_target read from main). Jenkins and metadata-contract.yml are unchanged.

Before merging, complete the one-time setup in the hive note shared/core_auto_release.md: the App, the secrets, a ruleset with the App as bypass and the title check required, squash-only merging with the PR title, and the PyPI trusted publisher.

PyPI trusted publishing does not work from inside a reusable workflow, so
callers get a released output and run the publish job themselves.
@KartikP
KartikP marked this pull request as ready for review October 9, 2026 09:47
@KartikP
KartikP merged commit 8c19332 into main Oct 9, 2026
3 checks passed
KartikP added a commit to brain-score/vision that referenced this pull request Oct 9, 2026
Makes vision releases fully automatic, using the shared workflows added
in brain-score/core#180.

**Depends on brain-score/core#180.** Both workflows here call
`brain-score/core/.github/workflows/*.yml@main`, which do not exist
until that PR merges. Do not merge this first. The human setup steps
(GitHub App, org secrets, `pypi` environment, ruleset, PyPI trusted
publisher) are in the setup guide and are not part of this PR.

## Changes
- `release.yml` (new): on each push to `master`, calls core's reusable
release workflow with `release-paths:
^brainscore_vision/(?!(models|benchmarks|data|metrics)/)`, so
plugin-only merges never release. The PR title type picks the level
(`feat` minor, `fix`/`perf` patch, major only with `!` or `BREAKING
CHANGE` plus the `major update` label). The PyPI publish job runs in
this file, because PyPI trusted publishing does not work inside a
reusable workflow. A manual **Run workflow** with a `level` input cuts
the first release.
- `pr-title.yml` (new): calls core's PR-title check; it comments on a
bad title with the allowed types.
- `bump_version.yml` deleted (already disabled; its last runs failed in
June). Its secrets `APPROVAL_TOKEN` and `WORKFLOW_AUTOMERGE` can be
deleted after merge.
- `pyproject.toml`: `[tool.bumpversion]` replaced by
`[tool.semantic_release]`.
- `docs/source/modules/version_bumping.rst` describes the new process.

## Starting version
PyPI has 2.3.22, `pyproject.toml` says 2.3.22, and the tag `v2.3.23`
exists on an unmerged bump branch but was never published.
python-semantic-release takes the highest `vX.Y.Z` tag in the
repository, even one not on `master`, so the next versions are 2.3.24
(patch) or 2.4.0 (minor); it never re-creates `v2.3.23`. Tested locally
with python-semantic-release 10.7.0 on a scratch repo with the same tag
layout. Recommended first release: manual run with level **minor**,
giving **2.4.0** (master has 127 commits since 2.3.22, including new
helpers). 2.3.23 stays a gap on PyPI.
KartikP added a commit to brain-score/language that referenced this pull request Oct 9, 2026
Makes language releases fully automatic, using the shared workflows
added in brain-score/core#180.

**Depends on brain-score/core#180.** Both workflows here call
`brain-score/core/.github/workflows/*.yml@main`, which do not exist
until that PR merges. Do not merge this first. The human setup steps
(GitHub App, org secrets, `pypi` environment, ruleset, PyPI trusted
publisher) are in the setup guide and are not part of this PR.

## Changes
- `release.yml` (new): on each push to `main`, calls core's reusable
release workflow with `release-paths:
^brainscore_language/(?!(models|benchmarks|data|metrics)/)`, so
plugin-only merges never release. The PR title type picks the level
(`feat` minor, `fix`/`perf` patch, major only with `!` or `BREAKING
CHANGE` plus the `major update` label). The PyPI publish job runs in
this file, because PyPI trusted publishing does not work inside a
reusable workflow. A manual **Run workflow** with a `level` input cuts
the first release.
- `pr-title.yml` (new): calls core's PR-title check; it comments on a
bad title with the allowed types.
- `pyproject.toml`: adds `[tool.semantic_release]`.

## Starting version
PyPI and `pyproject.toml` are at 2.2.1, and `v2.2.1` is the highest tag
and is on `main`, so the first release is 2.2.2 (patch) or 2.3.0 (minor)
with no collision. Pick the level for the manual first run from what
`main` has gained since 2.2.1.
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.

1 participant