feat: add verify-branch.sh for pre-tag release readiness checks#15621
feat: add verify-branch.sh for pre-tag release readiness checks#15621jamesfredley wants to merge 1 commit into8.0.xfrom
Conversation
The existing verify.sh runs against staged artifacts after the GitHub
release is published, which means the only way to discover that a
branch is not actually ready for release is to tag it, watch the
publish job fail, abort, fix, and re-tag. There is no local-only
counterpart that exercises the same gates against a working tree.
Add etc/bin/verify-branch.sh, the pre-tag counterpart to verify.sh.
It runs every release-readiness check that can be performed locally
without a tag, GitHub release, or staged artifacts:
1. dependencies.gradle does not contain SNAPSHOT versions (per
the Apache Release Policy and the existing Prerequisites
section of RELEASE.md).
2. KEYS file content matches the canonical copy at
dist.apache.org. Comparison is line-ending-insensitive because
the staged release is built on Linux and is shipped as LF
regardless of how the contributor's local working tree was
checked out.
3. Apache RAT license audit (./gradlew rat).
4. Code style (./gradlew codeStyle - Checkstyle plus CodeNarc).
5. grails-core assemble - the same gradle task release.yml's
publish job runs.
6. grails-forge assemble - the same gradle task release.yml's
publish job runs.
7. grails-doc build - the same gradle task release.yml's publish
job runs.
Optional flags add slower checks:
--include-reproducibility etc/bin/test-reproducible-builds.sh
--include-tests ./gradlew test
--include-all both of the above
Quick-triage flag:
--skip-build runs only steps 1-4
The script also:
- Sets SOURCE_DATE_EPOCH from the last git commit timestamp, so any
reproducibility-sensitive task that runs here uses the same epoch
release.yml's publish job will use.
- Warns when the working tree contains untracked or modified files,
because RAT scans every file regardless of git tracking, so local
debris (AI tool configs, temporary work directories, IDE state)
would falsely fail RAT even though the tracked branch is clean.
- Resolves a CR-stripped copy of any sibling script and gradlew it
invokes, so it remains usable on a Windows working tree that was
checked out under core.autocrlf=true before .gitattributes (see
#15620) was in place. On Linux/macOS checkouts
the sed step is a no-op rewrite.
Update RELEASE.md to document the new script in the Prerequisites
section as the recommended pre-tag step.
Assisted-by: claude-code:claude-opus-4-7
jdaugherty
left a comment
There was a problem hiding this comment.
I am ok with this script addition, but by adding this are you suggesting we run this before doing a release? The point of the workflows is so we can run from a common env but this script would be by definition run locally. Why not just stage the release? We basically have 2 code paths to maintain now and thats why we have CI
| current working tree, without requiring a tag, GitHub release, or any staged | ||
| artifacts. Mirrors the locally-runnable surface of release.yml's publish job. | ||
|
|
||
| Default checks (typically 15-30 minutes): |
There was a problem hiding this comment.
We should add the valid dependency versions task to this and release.yml since that makes sure our versions resolve to the bom versions
There was a problem hiding this comment.
I think you responded to the parent comment instead of the suggestion to add the validation dependency task.
As for smoke testing, isn't this only an issue once we create major branches? I'm trying to point out that the benefit to having another script isn't greater than the cost to maintain 2 separate pieces of logic. Plus, you ultimately still can have the workflow fail b/c of environmental configuration.
|
Adding a valid dependency versions check to verify-branch.sh and release.yml is a solid idea. It would ensure that all dependency versions resolve correctly to the BOM versions, catching potential mismatches early before staging. This aligns with the script's goal of pre-tag verification. |
✅ All tests passed ✅🏷️ Commit: 2cce2e5 Learn more about TestLens at testlens.app. |

Summary
Add
etc/bin/verify-branch.sh, a local-only pre-tag release readiness checker that runs every gaterelease.yml'spublishjob runs (and a couple more) against the working tree, without requiring a tag, GitHub release, or staged artifacts. Closes the documented gap that the existingverify.shonly works after staging.Why
verify.shanswers the question "Were the artifacts that release.yml staged equal to the artifacts I would build from this tag?". It needs three things that only exist after the tag is published: a GitHub Release, jars at repository.apache.org, and source/wrapper/cli zips at dist.apache.org. Until the tag is published, none of those exist.That means today the only way to discover that a branch is not actually ready for release is to tag it, watch the publish job fail, run
Release - Abort Release, fix, and re-tag. We need a local-only "is this branch ready to be tagged?" check.What it checks
dependencies.gradledoes not contain-SNAPSHOTKEYScontent matches canonical copy atdist.apache.org/repos/dist/release/grails/KEYSverify-keys.sh./gradlew rat)release.ymlpublish (and CI)./gradlew codeStyle- Checkstyle + CodeNarc)codestyle.yml./gradlew assemble -PgithubBranch=...)release.ymlpublishrelease.ymlpublishrelease.ymlpublishOptional flags add slower checks:
--include-reproducibilityrunsetc/bin/test-reproducible-builds.sh(30-60+ min)--include-testsruns./gradlew test--include-alladds both--skip-buildcuts to steps 1-4 only (fast triage, ~5-10 min)Defaults to steps 1-7 (typically 15-30 min).
Other behaviors worth knowing
SOURCE_DATE_EPOCHis set fromgit log -1 --pretty=%ctso any reproducibility-sensitive task that runs here uses the same epochrelease.yml's publish job will use.git worktree addif you want a one-command path to a clean check.core.autocrlf=truebefore.gitattributes(fix: enforce LF line endings for shell scripts, gradlew, KEYS #15620) was in place, the on-diskgradlewand sibling scripts have CRLF and are unusable on Linux. The script transparently sed-strips CR-only copies underbuild/branch-verify-tmpand uses those, so it remains runnable from a Windows checkout. On Linux/macOS the sed step is a no-op rewrite.RELEASE.mdAppendix - justcd /home/groovy/project && etc/bin/verify-branch.sh.RELEASE.md update
Add a "Pre-tag Branch Verification (Optional but Recommended)" subsection under Prerequisites that walks through
verify-branch.shinvocations on host and inside the container, with a clear pointer that after staging completes you still runverify.sh <tag> ..Local validation
Tested on a Windows machine with
core.autocrlf=true(so the worst-case path) inside the container documented in the existing Dockerfile:etc/bin/verify-branch.sh --help-> usage textetc/bin/verify-branch.sh --skip-build --branch 8.0.x-> SNAPSHOT and KEYS gates pass, RAT runs and correctly fails on local untracked debris (AI tool configs and acyclonedx-plugin-temp/work directory). On a clean checkout RAT will pass cleanly. The script's unclean-tree warning fires before RAT and tells the user exactly why.Related
docs: update RELEASE.md examples for 8.0.0-M1)fix: enforce LF line endings for shell scripts, gradlew, KEYS); this PR works correctly even before fix: enforce LF line endings for shell scripts, gradlew, KEYS #15620 is merged thanks to the on-the-fly CR-strip resolver.Out of scope
git. The unclean-tree pre-flight is therefore a no-op when the script runs inside the container today (it explicitly says so). A follow-up addinggittoetc/bin/Dockerfilewould let the cleanliness warning fire inside the container too. Not bundled here to keep this PR focused.