Skip to content

feat(build)!: derive the version from git, and adopt the verified release flow - #173

Open
jimisola wants to merge 3 commits into
mainfrom
ci/adopt-verified-release-flow
Open

feat(build)!: derive the version from git, and adopt the verified release flow#173
jimisola wants to merge 3 commits into
mainfrom
ci/adopt-verified-release-flow

Conversation

@jimisola

Copy link
Copy Markdown
Member

What & Why

Migrates this repo to the reworked org release flow in
reqstool/.github#66.

The old arrangement stopped at a draft. A human clicked Publish, and only then did
the publish workflows run — so everything that verified a release ran after it was already
public, and a failure meant a bad release was live.

Now the release is created as a prerelease and promoted only once everything that can
fail has succeeded. A prerelease rather than a draft because a draft is invisible to the
verification too (/releases/tags/<tag> 404s unauthenticated), while a prerelease is
readable by exact tag and still excluded from /releases/latest.

prepare  ->  lint + build  ->  [approval]  tag  ->  ...  ->  publish  ->  promote

The confirmation step is an environment approval on the job that tags, reached after
lint and build are already green — so a reviewer approves something verified rather than a
version string.

The version is no longer typed by hand. It is auto-detected from Conventional Commits;
passing one that disagrees requires force, which is the guard that catches "meant 0.3.0,
typed 0.2.0"
before it becomes a tag. prerelease: rc cuts a release candidate — verified
exactly like a release, but never promoted to latest.

dry-run defaults to on: dispatching Release shows the resolved version, the generated
notes, and the raw commits they came from, without tagging or creating anything.

See RELEASING.md for the
full flow.

Author checklist

  • actionlint clean
  • Conventional Commit title, DCO sign-off
  • No behaviour removed without a replacement

Test Plan

Merge order matters: this cannot merge before reqstool/.github#66. The workflows it
calls do not exist on main until that lands.

After merging:

  1. Dispatch Release with dry-run left on. Confirm the job summary shows the resolved
    version, the notes, and the commit list — and that no tag or release exists afterwards.
  2. Dispatch with prerelease: rc and dry-run off. Confirm it pauses for approval, then
    produces a prerelease with its artifacts attached, and that /releases/latest is
    unchanged.
  3. Only then cut a real release with prerelease: none.

Note on pinning

Workflows are referenced at @main rather than a commit SHA, because the SHA does not exist
until #66 is squash-merged. Re-pinning to that SHA is a follow-up.

The version now comes from git

<version> was hand-edited, so the version in the POM and the version in the tag were two facts kept in agreement by hand. The Maveniverse Nisse core extension derives it from git state instead (${nisse.jgit.dynamicVersion}), matching hatch-vcs and poetry-dynamic-versioning on the Python side. There is now no version string in this repo to edit.

Verified locally: tagging 9.9.9 makes mvn help:evaluate -Dexpression=project.version -q -DforceStdout print exactly 9.9.9.

mvn deploy builds from the tag itself, so the release flow has no separate build-and-attach step — the artifacts Central receives are the ones Nisse stamped. java-publish-to-maven.yml asserts the resolved version matches the tag before deploying, which catches the one failure that matters here: a shallow clone, which makes Nisse compute a version rather than fail.

Breaking: the POM no longer carries a literal version. A build from a shallow clone, or from an export without .git, resolves a different version than before.

One thing to know before the next release

The existing 1.0.0 tag is not an ancestor of main. Until the first release from this flow, off-tag builds report 0.1.0-N-SNAPSHOT rather than 1.0.x-N-SNAPSHOT. Nothing publishes snapshots, and git-cliff reads the tag regardless (it proposes 1.1.0), so this is cosmetic and self-corrects at the first release. Flagging it so the number in CI logs is not a surprise.

…ease flow

The old arrangement created a draft, a human clicked Publish, and only then did
the publish workflows run -- so everything that verified a release ran after it
was already public. The release is now created as a prerelease and promoted only
once everything that can fail has succeeded; the confirmation step is an
environment approval on the job that tags, reached after lint and build are
already green.

The version no longer has to be typed: it is auto-detected from Conventional
Commits, and passing one that disagrees needs `force`. `prerelease: rc` cuts a
release candidate, verified exactly like a release but never promoted to latest.

See RELEASING.md in reqstool/.github for the whole flow.

`<version>1.0.0</version>` was hand-edited, so the version in the POM and the
version in the tag were two facts that had to be kept in agreement by hand. The
Maveniverse Nisse core extension derives it from git state instead
(`${nisse.jgit.dynamicVersion}`), matching hatch-vcs and poetry-dynamic-versioning
on the Python side: there is now no version string in this repo to edit.

Verified locally -- tagging 9.9.9 makes `mvn help:evaluate -Dexpression=
project.version` print exactly 9.9.9, and `${project.version}` in the
annotationProcessorPath resolves unchanged.

`mvn deploy` builds from the tag itself, so the release flow has no separate
build-and-attach step; the artifacts Central receives are the ones Nisse stamped.
`java-publish-to-maven.yml` asserts the resolved version matches the tag before
deploying, which catches the one failure that matters here: a shallow clone,
which makes Nisse compute a version rather than fail.

Note for whoever cuts the next release: the existing 1.0.0 tag is not an ancestor
of main, so until the first release from this flow, off-tag builds report
0.1.0-N-SNAPSHOT rather than 1.0.x-N-SNAPSHOT. Nothing publishes snapshots, and
git-cliff reads the tag regardless (it proposes 1.1.0), so this is cosmetic and
self-corrects at the first release.

BREAKING CHANGE: the POM no longer carries a literal version. A build from a
shallow clone or an export without .git resolves a different version than before.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
check-semantic-pr.yml pinned common-check-semantic-pr.yml at
e1d67194373e4da7ccfdf400f46201f18ca14f23. That commit predates the file: the
`common-` rename came later, so the pinned tree has no such workflow. GitHub
cannot resolve a workflow_call to a path that does not exist, so the run failed
at startup with no jobs -- and because that produces no check run, it never
appeared in the PR checks list.

PR titles have therefore not been validated here since the pin was written.
Following @main, as the other callers do, until the org settles on re-pinning.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
common-release-tag.yml now validates the version and the ref itself rather than
trusting that prepare validated the same values (CodeQL flagged the privileged
checkout on an unvalidated ref in reqstool/.github#66). Validating the version
needs to know which format to validate against.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
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