Skip to content

ci: add PR version-suggestion comment (precursor to auto-release) - #6

Merged
aesslinger merged 1 commit into
mainfrom
ci/version-suggestion-comment
Aug 11, 2026
Merged

ci: add PR version-suggestion comment (precursor to auto-release)#6
aesslinger merged 1 commit into
mainfrom
ci/version-suggestion-comment

Conversation

@aesslinger

@aesslinger aesslinger commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

A precursor to eventually automating tag/release creation. When a PR's title implies a version bump, CI now posts a comment suggesting the next tag/version — nothing is created automatically, purely informational.

Rebased onto current main now that #5 (release-version validation, pr-title job) has merged — this PR builds directly on both.

Design decisions (each resolved directly rather than assumed)

  • Type → bump mapping: standard Conventional Commits (feat→minor, fix/refactor/perf→patch, docs/style/chore/test/ci/build→none, !/BREAKING CHANGE:→major).
  • Beta-phase behavior: while the baseline version carries a -beta.N/-rc.N suffix, a release-worthy PR just increments that stage's counter (1.0.0-beta.1-beta.2), not a full patch/minor/major recompute — there's no shipped stable version yet to protect a SemVer contract against.
  • Prerelease channel (alpha/beta/rc/stable): not signaled in the PR title. Checked precedent first — semantic-release/release-please control channel via labels or release-train branches, not embedded title syntax; no Tabularis repo does this either. Settled on a required prerelease:alpha|beta|rc|stable PR label. Missing label is a hard error — the job fails with a clear message rather than guessing a default channel.
  • Version baseline: latest git tag on mainmain currently has real tags (v1.0.0-beta.1, v1.0.0-beta.2), so git describe --tags resolves directly; the .tabularium fallback remains for the (now past) no-tags-yet case.
  • "Meaningful change" trigger: only re-comments when the PR's derived classification (type + breaking + channel) changes, not on every title edit — tracked via a hidden HTML marker in the comment body.
  • Marking old suggestions outdated: GitHub's real minimizeComment GraphQL mutation with classifier: OUTDATED — the same action available via the web UI's "..." menu → "Hide comment" → "Outdated".
  • Trigger gap found and fixed: the existing pull_request: block had no types:, defaulting to opened/synchronize/reopened — a title-only edit never even re-ran CI. Widened to include edited/labeled/unlabeled (shared by all pull_request-triggered jobs, including the existing pr-title job, which also now correctly re-validates on title edits).

Checked precedent before implementing: no sibling Tabularis plugin repo or tabularis itself has anything like this. A separate internal repo has a fuller PR-title-driven auto-tag/auto-release pipeline; decided against porting that whole pipeline here since it's a much larger behavioral change (replaces manual tagging entirely) with no Tabularis-org precedent — this PR stays comment-only.

Testing — thorough, before trusting any of this in real CI

  • Title classification: extracted the embedded bash regex and ran it in a real bash subshell (not zsh, which handles BASH_REMATCH differently — caught this the hard way) against 8 title cases. All classified correctly, including a deliberately unparseable title correctly erroring.
  • Version arithmetic: extracted the embedded JS and unit-tested it standalone against 7 cases — beta-counter increment, stage transitions (beta→rc), graduation to stable from both beta and rc baselines, stable→prerelease restart. All passed.
  • Comment orchestration: wrote a full mock harness (fake github.rest.issues.listComments/createComment and github.graphql) and ran 8 end-to-end scenarios: fresh suggestion, idempotent no-op on unchanged classification, minimize-and-repost on changed classification, silent on non-release-worthy titles, and the "no-release-needed" transition in both directions.
    • This caught a real bug before it ever reached CI: the "already said none, still none" case incorrectly reposted because the idempotency check compared against a hardcoded string "none" instead of the full classification string. Fixed and reverified all 8 scenarios pass.
  • Both workflow YAML files re-validated after every edit.

What does NOT change

  • No tag created, no release published, no version file edited by this job.
  • The existing pr-title and validate-manifest/release.yml validate jobs are unchanged in behavior — only the shared pull_request: trigger's types: list gains entries.

Docs

Added a "Contributing: PR Titles & Versioning" section to the README (cross-referenced from CLAUDE.md) so future contributors/maintainers understand the convention without reading the workflow YAML.

Test plan

  • Title classification logic verified against 8 cases in a real bash subshell
  • Version-arithmetic logic unit-tested against 7 cases, all pass
  • Comment-orchestration logic mock-tested against 8 end-to-end scenarios — found and fixed one real bug (idempotency check comparing against the wrong value)
  • Label-validation logic tested against 5 cases (no label, wrong label, valid labels, garbage channel value)
  • Version-baseline resolution confirmed against the actual current repo state (real tags now, not the fallback path)
  • cargo build/test --lib --bins (82/82)/clippy/fmt --check all pass
  • git diff --exit-code Cargo.lock: no drift
  • markdownlint-cli "**/*.md": clean
  • Both workflow YAML files parse as valid YAML

Base automatically changed from ci/semver-checks to main August 11, 2026 17:37
A precursor to eventually automating tag/release creation: when a PR's
title implies a version bump, CI now posts a comment suggesting the next
tag/version — without creating anything. Several real design decisions,
each resolved with the user rather than assumed:

- Type -> bump mapping: standard Conventional Commits (feat->minor,
  fix/refactor/perf->patch, docs/style/chore/test/ci/build->none,
  !/BREAKING CHANGE->major).
- Beta-phase behavior: while the baseline version carries a -beta.N/-rc.N
  suffix, a release-worthy PR just increments that stage's counter, not a
  full patch/minor/major recompute -- nothing has shipped stable yet.
- Prerelease channel: NOT signaled in the title. Checked precedent first
  (semantic-release/release-please control channel via labels or release-
  train branches, not embedded title syntax; no Tabularis repo does this
  either) and settled on a required prerelease:alpha|beta|rc|stable PR
  label. Missing label is a hard error -- the job fails rather than
  guessing a default channel.
- Version baseline: latest git tag on main, falling back to main's
  .tabularium version field if no tag exists (true right now).
- "Meaningful change" trigger: only re-comments when the PR's *derived
  classification* (type+breaking+channel) changes, not on every title
  edit -- tracked via a hidden HTML marker in the comment body, robust
  across opened/edited/reopened/synchronize/labeled/unlabeled.
- Marking old suggestions outdated: GitHub's real minimizeComment GraphQL
  mutation with classifier: OUTDATED -- the same action available via the
  web UI's "..." menu -> "Hide comment" -> "Outdated".
- Found and fixed a real trigger gap: the existing pull_request: block had
  no types:, defaulting to opened/synchronize/reopened -- a title-only
  edit never even re-ran CI. Widened to include edited/labeled/unlabeled
  (shared by all pull_request-triggered jobs, including the existing
  pr-title job, which also now re-validates on edited).

Checked precedent before implementing: no sibling Tabularis plugin repo
or tabularis itself has anything like this. A separate internal repo has
a fuller PR-title-driven auto-tag/auto-release pipeline; decided against
porting that whole pipeline here since it's a much larger behavioral
change (replaces manual tagging entirely) with no Tabularis-org
precedent -- this stays comment-only.

Verified thoroughly before trusting the logic in CI:
- Extracted the embedded classification regex and ran it in a real bash
  subshell (not zsh, which handles BASH_REMATCH differently) against 8
  title cases -- all classified correctly, including a deliberately
  unparseable title correctly erroring.
- Extracted the embedded version-arithmetic JS and unit-tested it
  standalone against 7 cases (beta-counter increment, stage transitions,
  graduation to stable from both beta and rc, stable->prerelease
  restart) -- all passed.
- Wrote a full mock harness for the comment-orchestration logic (fake
  github.rest.issues.listComments/createComment and github.graphql) and
  ran 8 end-to-end scenarios covering: fresh suggestion, idempotent no-op
  on unchanged classification, minimize-and-repost on changed
  classification, silent on non-release-worthy titles, and the
  "no-release-needed" transition in both directions. This caught a real
  bug before it ever reached CI: the "already said none, still none"
  case incorrectly reposted because the idempotency check compared
  against a hardcoded string instead of the full classification --
  fixed and reverified all 8 scenarios pass.
- Confirmed the baseline resolves correctly against the actual current
  repo state: main now has real tags (v1.0.0-beta.1, v1.0.0-beta.2), so
  `git describe --tags` resolves directly against origin/main rather
  than the PR branch; the .tabularium fallback remains for the
  no-tags-yet case this repo has already moved past.
- Both workflow YAML files re-validated after every edit.

Also added contributor-facing docs (README's "Contributing: PR Titles &
Versioning" section, cross-referenced from CLAUDE.md) so future
maintainers understand the convention without reading the workflow YAML.

Standard verification: cargo build/test (82/82)/clippy/fmt all pass, no
Cargo.lock drift, markdownlint clean across the whole repo, both workflow
files parse as valid YAML.

Rebased onto main after #5 merged (adds the pr-title job and release
validate job this PR builds on) — no functional changes beyond the
rebase itself, CHANGELOG merge conflict resolved by keeping both
entries.
@aesslinger
aesslinger force-pushed the ci/version-suggestion-comment branch from 6ae469b to 88dbd83 Compare August 11, 2026 17:44
@aesslinger aesslinger added prerelease:beta Version suggestion targets a beta prerelease and removed prerelease:beta Version suggestion targets a beta prerelease labels Aug 11, 2026
@aesslinger
aesslinger merged commit b6f6115 into main Aug 11, 2026
38 of 42 checks passed
@aesslinger
aesslinger deleted the ci/version-suggestion-comment branch August 11, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease:beta Version suggestion targets a beta prerelease

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant