diff --git a/docs/contributing/release-process.md b/docs/contributing/release-process.md index c9b33d8d..cbcf5c5f 100644 --- a/docs/contributing/release-process.md +++ b/docs/contributing/release-process.md @@ -136,6 +136,7 @@ Each learning maps to a check above. - **v1.6.0 prep** — slimming the README to a marketing landing renamed the canonical `DocumentSession document = …` example variable to `doc`, which silently broke `DocumentationCoverageTest.readmeShouldUseCanonicalDslAndAvoidLegacyApis` because the test asserts the literal string `document.pageFlow(` is present. *Mitigation*: any rewrite of the README "Hello world" snippet must keep `DocumentSession document` as the variable name and `document.pageFlow(`, `document.buildPdf()`, `GraphCompose.document(` as the literal canonical fingerprints the guard scans for. Renaming the variable is a guard-test break, not a stylistic preference. - **v1.6.0 post-release** — the `examples-generation` CI job introduced after v1.6.0 went red on the first run because `examples/pom.xml` and `benchmarks/pom.xml` declare a `` property used by their `graphcompose` dependency, and `cut-release.ps1` was only flipping the project's own `` tag (the first `` in each file). The subordinate POMs kept `1.6.0-beta.1` after the release commit; CI couldn't resolve a `1.6.0-beta.1` artifact (it never existed on any registry), so `mvnw -f examples/pom.xml clean compile` failed at dependency resolution. *Mitigation*: `Update-PomVersion` in `cut-release.ps1` now flips both the first `` tag *and* a `...` property if present, in the same call. Future agents need not touch this — running the script handles both. - **v1.6.5 prep** — the subordinate-POM `` property flip from the v1.6.0 lesson above is now **superseded**: `examples/` and `benchmarks/` were converted to a reactor under a non-published `aggregator/pom.xml`, so they inherit their version from `graphcompose-build` and declare `${project.version}` instead of a literal. The library `pom.xml` stays standalone, so JitPack coordinates never change. Version drift is now structurally impossible *and* caught by `VersionConsistencyGuardTest` (wired into CI's guard job and the section 0.B gate). `cut-release.ps1` bumps the library pom, the aggregator, both inherited parent refs, and the README install snippets in one commit; `.github/workflows/release.yml` then gates the tag on `verify` and publishes the GitHub Release automatically. *Mitigation*: section 0.D verifies all four version sites agree; the guard fails the verify gate if any hand-edit leaves them out of sync. +- **v1.6.5 cut** — `cut-release.ps1` Step 4 (`ShowcaseSync`) aborted with `Could not find artifact io.github.demchaav:graphcompose:jar:1.6.5 in central` after Step 1 bumped the four pom.xml files to `1.6.5`: the examples module depends on `graphcompose:${project.version}`, the previous release (`1.6.4`) was the only version in the local `~/.m2`, and Step 4 had no install gate to put the just-bumped version there first. Cut had to be finished by hand — install root, re-run ShowcaseSync, verify, commit, tag, push. *Mitigation*: `Run-ShowcaseSync` now runs `./mvnw -B -ntp -DskipTests install -pl .` immediately before `exec:java`, in both Release and PostReleaseOnly modes; the dry-run preview shows both steps. Pre-flight branch / clean / sync gates are now relaxed for `-DryRun` so the script can be previewed from a feature branch while iterating on it. --- diff --git a/scripts/cut-release.ps1 b/scripts/cut-release.ps1 index 04d376d6..41b76744 100644 --- a/scripts/cut-release.ps1 +++ b/scripts/cut-release.ps1 @@ -271,11 +271,30 @@ function Run-ShowcaseSync { # as a single literal argument. $execProp = '"-Dexec.mainClass=com.demcha.examples.support.ShowcaseSync"' if ($DryRun) { + Write-Host " [DRY RUN] $mvnw -B -ntp -DskipTests install -pl ." -ForegroundColor Yellow Write-Host " [DRY RUN] $mvnw -f examples/pom.xml exec:java $execProp" -ForegroundColor Yellow return } Push-Location $repoRoot try { + # ShowcaseSync runs from the examples module, which depends on + # io.github.demchaav:graphcompose:${project.version}. After Step 1 + # bumps the four pom.xml files to the new release version, that + # artifact is not yet in the local m2 cache — only the previous + # release is — so exec:java fails dependency resolution with + # "Could not find artifact ...:graphcompose:jar:". + # Install the root artifact first so the examples module can + # resolve it. Bug surfaced during v1.6.5 cut: Step 4 aborted with + # exit 1; we had to install by hand and resume manually. + Write-Host " > $mvnw -B -ntp -DskipTests install -pl ." -ForegroundColor DarkGray + & $mvnw -B -ntp -DskipTests install -pl . 2>&1 | ForEach-Object { + if ($_ -match 'BUILD SUCCESS|BUILD FAILURE|ERROR') { + Write-Host " $_" -ForegroundColor DarkGray + } + } + if ($LASTEXITCODE -ne 0) { + throw "Install root artifact failed (exit $LASTEXITCODE)" + } & $mvnw -f examples/pom.xml exec:java $execProp 2>&1 | ForEach-Object { if ($_ -match 'Synced|Wrote manifest|BUILD SUCCESS|BUILD FAILURE|ERROR') { Write-Host " $_" -ForegroundColor DarkGray @@ -337,28 +356,37 @@ try { Step 0 "Pre-flight checks" - # 1. On develop branch? + # In -DryRun mode the script never mutates anything, so the branch / + # working-tree / origin-sync gates are relaxed: a maintainer can preview + # what a release cut would do from a feature branch (e.g. while iterating + # on the script itself) without having to switch to develop and back. + # Live cuts still fail these gates loudly. $branch = (git rev-parse --abbrev-ref HEAD).Trim() - if ($branch -ne 'develop') { - throw "Not on develop branch (currently on $branch). Switch to develop first." - } - Note "branch: develop OK" + if ($DryRun) { + Note "branch: $branch (gate relaxed for -DryRun)" + } else { + # 1. On develop branch? + if ($branch -ne 'develop') { + throw "Not on develop branch (currently on $branch). Switch to develop first." + } + Note "branch: develop OK" - # 2. Working tree clean? - $status = git status --porcelain - if ($status) { - throw "Working tree has uncommitted changes. Commit or stash first." - } - Note "working tree: clean OK" - - # 3. In sync with origin? - git fetch origin develop --quiet - $local = (git rev-parse develop).Trim() - $remote = (git rev-parse origin/develop).Trim() - if ($local -ne $remote) { - throw "Local develop ($local) is not in sync with origin/develop ($remote). Pull/push first." + # 2. Working tree clean? + $status = git status --porcelain + if ($status) { + throw "Working tree has uncommitted changes. Commit or stash first." + } + Note "working tree: clean OK" + + # 3. In sync with origin? + git fetch origin develop --quiet + $local = (git rev-parse develop).Trim() + $remote = (git rev-parse origin/develop).Trim() + if ($local -ne $remote) { + throw "Local develop ($local) is not in sync with origin/develop ($remote). Pull/push first." + } + Note "in sync with origin/develop OK" } - Note "in sync with origin/develop OK" # 4. Tag doesn't already exist? $existingTag = git tag -l $tag