Skip to content

feat: add verify-branch.sh for pre-tag release readiness checks#15621

Open
jamesfredley wants to merge 1 commit into8.0.xfrom
feat/verify-branch
Open

feat: add verify-branch.sh for pre-tag release readiness checks#15621
jamesfredley wants to merge 1 commit into8.0.xfrom
feat/verify-branch

Conversation

@jamesfredley
Copy link
Copy Markdown
Contributor

Summary

Add etc/bin/verify-branch.sh, a local-only pre-tag release readiness checker that runs every gate release.yml's publish job runs (and a couple more) against the working tree, without requiring a tag, GitHub release, or staged artifacts. Closes the documented gap that the existing verify.sh only works after staging.

Why

verify.sh answers 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

Step Check Time Equivalent to
1 dependencies.gradle does not contain -SNAPSHOT < 1 s RELEASE.md Prerequisites
2 KEYS content matches canonical copy at dist.apache.org/repos/dist/release/grails/KEYS ~ 1 s content-aware version of verify-keys.sh
3 Apache RAT license audit (./gradlew rat) ~ 5 min release.yml publish (and CI)
4 Code style (./gradlew codeStyle - Checkstyle + CodeNarc) ~ 3-5 min codestyle.yml
5 grails-core assemble (./gradlew assemble -PgithubBranch=...) ~ 10 min release.yml publish
6 grails-forge assemble ~ 5 min release.yml publish
7 grails-doc build ~ 3 min release.yml publish

Optional flags add slower checks:

  • --include-reproducibility runs etc/bin/test-reproducible-builds.sh (30-60+ min)
  • --include-tests runs ./gradlew test
  • --include-all adds both
  • --skip-build cuts 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_EPOCH is set from git log -1 --pretty=%ct so any reproducibility-sensitive task that runs here uses the same epoch release.yml's publish job will use.
  • Unclean-tree warning: RAT scans every file regardless of git tracking. If the working tree has untracked or modified files (AI tool configs, IDE state, temporary work directories), they would falsely fail RAT even though the tracked branch is clean. The script warns explicitly and points you at git worktree add if you want a one-command path to a clean check.
  • CRLF resilience: on a Windows working tree checked out under core.autocrlf=true before .gitattributes (fix: enforce LF line endings for shell scripts, gradlew, KEYS #15620) was in place, the on-disk gradlew and sibling scripts have CRLF and are unusable on Linux. The script transparently sed-strips CR-only copies under build/branch-verify-tmp and uses those, so it remains runnable from a Windows checkout. On Linux/macOS the sed step is a no-op rewrite.
  • Container parity: runs unchanged inside the existing verification container documented in RELEASE.md Appendix - just cd /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.sh invocations on host and inside the container, with a clear pointer that after staging completes you still run verify.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 text
  • etc/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 a cyclonedx-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

Out of scope

  • The verification container does not currently include 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 adding git to etc/bin/Dockerfile would let the cleanliness warning fire inside the container too. Not bundled here to keep this PR focused.

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
Copilot AI review requested due to automatic review settings May 2, 2026 14:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread etc/bin/verify-branch.sh
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):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add the valid dependency versions task to this and release.yml since that makes sure our versions resolve to the bom versions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

mostly to pre smoke test it before waiting for this to complete. And this is possible now, this just tries to wrap it into a single script.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@bito-code-review
Copy link
Copy Markdown

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.

@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented May 2, 2026

✅ All tests passed ✅

🏷️ Commit: 2cce2e5
▶️ Tests: 32391 executed
⚪️ Checks: 39/39 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants