diff --git a/README.md b/README.md index ae086ee0..f35d9880 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,13 @@ CI Latest release Maven Central - JitPack Java 17+ PDFBox 3.0 MIT License

> **Release status** — -> ๐ŸŸข **Latest stable**: [v1.6.5](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.6.5) (JitPack) +> ๐ŸŸข **Latest stable**: [v1.6.5](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.6.5) >  ยท  ๐ŸŸก **In develop**: v1.6.6 (Maven Central debut; zero breaking from v1.6.5) >  ยท  โšช **Planned next**: v1.6.7 (dependency cleanup), v1.7.0 (new canonical DSL primitives) >  ยท  See [API stability policy](./docs/api-stability.md) for tier definitions. @@ -94,8 +93,6 @@ GraphCompose uses PDFBox under the hood as the rendering backend — the com ## Installation -### Maven Central (primary, from v1.6.6) - ```xml io.github.demchaav @@ -108,29 +105,12 @@ GraphCompose uses PDFBox under the hood as the rendering backend — the com dependencies { implementation("io.github.demchaav:graphcompose:1.6.6") } ``` -### JitPack (fallback / pre-v1.6.6) - -```xml - - jitpack.iohttps://jitpack.io - - - - com.github.DemchaAV - GraphCompose - v1.6.5 - -``` - -```kotlin -repositories { maven("https://jitpack.io") } -dependencies { implementation("com.github.demchaav:GraphCompose:v1.6.5") } -``` - -> **Distribution status** — currently **JitPack**. **Maven Central** -> ships from **v1.6.6** under coordinates `io.github.demchaav:graphcompose:`; -> hosted Javadocs auto-publish to [javadoc.io/doc/io.github.demchaav/graphcompose](https://javadoc.io/doc/io.github.demchaav/graphcompose) shortly after each Central release. -> JitPack stays available alongside Central for existing callers. +> **Distribution** — Maven Central is the canonical channel from **v1.6.6** onwards +> (`io.github.demchaav:graphcompose:`). Hosted Javadocs auto-publish to +> [javadoc.io/doc/io.github.demchaav/graphcompose](https://javadoc.io/doc/io.github.demchaav/graphcompose) +> shortly after each Central release. The legacy JitPack URL +> (`com.github.DemchaAV:GraphCompose:v`) remains resolvable for callers +> pinned to v1.6.5 and earlier but is no longer the documented install option. > **Upgrading from v1.5?** Core document authoring stays source-compatible — engine, DSL, themes, and backend-neutral records carry v1.5 callers unchanged. **Templates v2** replaces the legacy CV / cover-letter template classes; legacy classes were **deleted**, not deprecated. Read the [migration guide](./docs/roadmaps/migration-v1-5-to-v1-6.md) before upgrading template-heavy code. diff --git a/docs/index.html b/docs/index.html index 17f3d625..a8cbdf48 100644 --- a/docs/index.html +++ b/docs/index.html @@ -57,9 +57,9 @@ "applicationSubCategory": "Library", "operatingSystem": "Cross-platform (JVM 21+)", "programmingLanguage": "Java", - "softwareVersion": "1.6.5", + "softwareVersion": "1.6.6", "url": "https://demchaav.github.io/GraphCompose/", - "downloadUrl": "https://jitpack.io/#DemchaAV/GraphCompose/v1.6.5", + "downloadUrl": "https://central.sonatype.com/artifact/io.github.demchaav/graphcompose/1.6.6", "image": "https://demchaav.github.io/GraphCompose/assets/logo/graphcompose-logo.png", "license": "https://github.com/DemchaAV/GraphCompose/blob/main/LICENSE", "author": { @@ -225,31 +225,22 @@

Tested at every layer

-

JitPack, no extra setup.

+

Maven Central. One dependency.

Maven

-
<repository>
-  <id>jitpack.io</id>
-  <url>https://jitpack.io</url>
-</repository>
-
-<dependency>
-  <groupId>com.github.DemchaAV</groupId>
-  <artifactId>GraphCompose</artifactId>
-  <version>v1.6.5</version>
+
<dependency>
+  <groupId>io.github.demchaav</groupId>
+  <artifactId>graphcompose</artifactId>
+  <version>1.6.6</version>
 </dependency>

Gradle

-
repositories {
-  maven { url 'https://jitpack.io' }
-}
-
-dependencies {
+
dependencies {
   implementation(
-    'com.github.DemchaAV:GraphCompose:v1.6.5'
+    'io.github.demchaav:graphcompose:1.6.6'
   )
 }
diff --git a/scripts/cut-release.ps1 b/scripts/cut-release.ps1 index 41b76744..57a989ae 100644 --- a/scripts/cut-release.ps1 +++ b/scripts/cut-release.ps1 @@ -162,28 +162,49 @@ function Update-ReadmeInstallVersion($readmePath, $newVersion) { $tag = "v$newVersion" $changed = $false - # The README JitPack install snippets pin the git tag (vX.Y.Z). They must - # flip in the SAME commit the release tag is cut from, so a new user who - # copy-pastes the README resolves the version this release actually - # publishes (Phase 2.3 of the release skill: README version flips at - # release-execution time, never earlier). Two snippets carry it: - # Maven: GraphComposevX.Y.Z - # Gradle: implementation("...:GraphCompose:vX.Y.Z") - # Lookbehind/lookahead so only the version token is rewritten. - $mavenRegex = [regex]'(?<=GraphCompose\s*)v?[\w\.\-]+(?=)' - $afterMaven = $mavenRegex.Replace($content, $tag, 1) + # The README Maven Central install snippets pin the published version + # (X.Y.Z, no `v` prefix). They must flip in the SAME commit the release + # tag is cut from, so a new user who copy-pastes the README resolves + # the version this release actually publishes (Phase 2.3 of the + # release skill: README version flips at release-execution time, + # never earlier). Two snippets carry it: + # Maven: graphcomposeX.Y.Z + # Gradle: implementation("io.github.demchaav:graphcompose:X.Y.Z") + # Lookbehind/lookahead so only the version token is rewritten. A + # secondary fallback handles the legacy JitPack format + # (GraphCompose / GraphCompose:vX.Y.Z) so + # the script still works if a future change re-introduces a JitPack + # snippet for documentation purposes. + $mavenCentralRegex = [regex]'(?<=graphcompose\s*)v?[\w\.\-]+(?=)' + $afterMaven = $mavenCentralRegex.Replace($content, $newVersion, 1) if ($content -ne $afterMaven) { $content = $afterMaven $changed = $true - Note "bumped README Maven snippet -> $tag" + Note "bumped README Maven Central snippet -> $newVersion" + } else { + $mavenLegacyRegex = [regex]'(?<=GraphCompose\s*)v?[\w\.\-]+(?=)' + $afterMavenLegacy = $mavenLegacyRegex.Replace($content, $tag, 1) + if ($content -ne $afterMavenLegacy) { + $content = $afterMavenLegacy + $changed = $true + Note "bumped README legacy JitPack Maven snippet -> $tag" + } } - $gradleRegex = [regex]'(?<=:GraphCompose:)v?[\w\.\-]+(?=")' - $afterGradle = $gradleRegex.Replace($content, $tag, 1) + $gradleCentralRegex = [regex]'(?<=io\.github\.demchaav:graphcompose:)v?[\w\.\-]+(?=")' + $afterGradle = $gradleCentralRegex.Replace($content, $newVersion, 1) if ($content -ne $afterGradle) { $content = $afterGradle $changed = $true - Note "bumped README Gradle snippet -> $tag" + Note "bumped README Maven Central Gradle snippet -> $newVersion" + } else { + $gradleLegacyRegex = [regex]'(?<=:GraphCompose:)v?[\w\.\-]+(?=")' + $afterGradleLegacy = $gradleLegacyRegex.Replace($content, $tag, 1) + if ($content -ne $afterGradleLegacy) { + $content = $afterGradleLegacy + $changed = $true + Note "bumped README legacy JitPack Gradle snippet -> $tag" + } } if (-not $changed) { @@ -207,19 +228,20 @@ function Update-IndexHtmlVersion($indexHtmlPath, $newVersion) { $tag = "v$newVersion" $changed = $false - # The GitHub Pages showcase (docs/index.html) hardcodes the version in five - # spots that do NOT inherit from the pom โ€” they previously sat at v1.6.1 - # while the library shipped v1.6.4. VersionConsistencyGuardTest fails the - # verify gate if any lags, so flip all five in lockstep with README + poms: - # JSON-LD softwareVersion (bare), JitPack downloadUrl, hero badge, and the - # Maven + Gradle install snippets (all v-prefixed). Lookbehind/lookahead so - # only the version token is rewritten. + # The GitHub Pages showcase (docs/index.html) hardcodes the version in + # several spots that do NOT inherit from the pom โ€” they previously sat at + # v1.6.1 while the library shipped v1.6.4. VersionConsistencyGuardTest + # fails the verify gate if any lags, so flip them all in lockstep with the + # README + poms. The Maven Central format coordinates use bare semver + # ($newVersion), the hero badge keeps the v-prefix ($tag), and the + # downloadUrl points at the Central artefact page. Lookbehind/lookahead + # so only the version token is rewritten. $replacements = @( - @{ Regex = [regex]'(?<="softwareVersion": ")v?[\w\.\-]+(?=")'; Value = $newVersion; Label = 'JSON-LD softwareVersion' }, - @{ Regex = [regex]'(?<=jitpack\.io/#DemchaAV/GraphCompose/)v?[\w\.\-]+(?=")'; Value = $tag; Label = 'JitPack downloadUrl' }, - @{ Regex = [regex]'(?<=Java · )v?[\w\.\-]+(?= · MIT)'; Value = $tag; Label = 'hero badge' }, - @{ Regex = [regex]'(?<=<version>)v?[\w\.\-]+(?=</version>)'; Value = $tag; Label = 'Maven snippet' }, - @{ Regex = [regex]"(?<=:GraphCompose:)v?[\w\.\-]+(?=')"; Value = $tag; Label = 'Gradle snippet' } + @{ Regex = [regex]'(?<="softwareVersion": ")v?[\w\.\-]+(?=")'; Value = $newVersion; Label = 'JSON-LD softwareVersion' }, + @{ Regex = [regex]'(?<=https://central\.sonatype\.com/artifact/io\.github\.demchaav/graphcompose/)v?[\w\.\-]+(?=")'; Value = $newVersion; Label = 'Central downloadUrl' }, + @{ Regex = [regex]'(?<=Java · )v?[\w\.\-]+(?= · MIT)'; Value = $tag; Label = 'hero badge' }, + @{ Regex = [regex]'(?<=<artifactId>graphcompose</artifactId>\s*<version>)v?[\w\.\-]+(?=</version>)'; Value = $newVersion; Label = 'Maven Central snippet' }, + @{ Regex = [regex]"(?<=io\.github\.demchaav:graphcompose:)v?[\w\.\-]+(?=')"; Value = $newVersion; Label = 'Gradle Central snippet' } ) foreach ($r in $replacements) { diff --git a/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java b/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java index a4cb2d8c..de8943b1 100644 --- a/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java +++ b/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java @@ -11,6 +11,8 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -18,16 +20,26 @@ /** * Guards that the GraphCompose version propagates identically to every module - * and to the README install snippets. + * and to the README + showcase install snippets. * *

This is the safety net behind the aggregator-reactor version model * (see {@code aggregator/pom.xml}): the library {@code pom.xml} is the single * version source, the aggregator bumps every module in lockstep via * {@code versions:set}, and the child modules inherit their version rather than * pinning a literal. This test fails the build the moment a bump leaves any - * module โ€” or the README copy-paste snippet โ€” pointing at a different version, + * module โ€” or a copy-paste install snippet โ€” pointing at a different version, * which is the drift class that previously let the benchmarks module run * against the previous release. + * + *

The snippet checks accept the version as matching either + * the current {@code pom.xml} version or the version named in + * the top {@code CHANGELOG.md} {@code Planned} entry. This makes the + * forward-looking Maven Central snippet (which has to advertise the + * about-to-ship version so users copy a coord that will resolve once the tag + * lands) compatible with the pre-cut window where {@code pom.xml} still carries + * the previous release version. {@code cut-release.ps1} bumps both pom and + * snippet to the same target in the release commit, after which the two paths + * converge and the test continues to pass. */ class VersionConsistencyGuardTest { @@ -72,50 +84,106 @@ void benchmarksDependencyDerivesVersionAndIsNotHardcoded() throws IOException { @Test void readmeInstallSnippetsMatchTheProjectVersion() throws Exception { - String root = effectiveVersion(PROJECT_ROOT.resolve("pom.xml")); + Set targets = acceptableTargets(); String readme = Files.readString(PROJECT_ROOT.resolve("README.md")); - String mavenSnippetVersion = firstGroup(readme, - "GraphCompose\\s*v?([0-9][^<]*)"); - String gradleSnippetVersion = firstGroup(readme, - "GraphCompose:v?([0-9][^\")]*)"); + String mavenSnippetVersion = firstMatchingGroup(readme, INSTALL_SNIPPET_PATTERNS_README_MAVEN); + String gradleSnippetVersion = firstMatchingGroup(readme, INSTALL_SNIPPET_PATTERNS_README_GRADLE); assertThat(mavenSnippetVersion) - .describedAs("README Maven/JitPack snippet must reference the current project version (%s)", root) - .isEqualTo(root); + .describedAs("README Maven install snippet must reference the current pom or CHANGELOG Planned version (one of %s)", targets) + .isIn(targets); assertThat(gradleSnippetVersion) - .describedAs("README Gradle/JitPack snippet must reference the current project version (%s)", root) - .isEqualTo(root); + .describedAs("README Gradle install snippet must reference the current pom or CHANGELOG Planned version (one of %s)", targets) + .isIn(targets); } /** * The GitHub Pages showcase ({@code docs/index.html}) hardcodes the version - * in five spots โ€” JSON-LD {@code softwareVersion}, the JitPack download URL, - * the hero badge, and the Maven + Gradle install snippets. None of these - * inherit from the pom, so without this guard they silently drift (they sat - * at v1.6.1 while the library shipped v1.6.4). {@code cut-release.ps1} flips - * them on release; this fails the verify gate if any spot lags behind. + * in three spots โ€” JSON-LD {@code softwareVersion}, the hero badge, and the + * Maven + Gradle install snippets. None inherit from the pom, so without + * this guard they silently drift. {@code cut-release.ps1} flips them on + * release; this fails the verify gate if any spot lags behind. */ @Test void showcaseSiteVersionMatchesTheProjectVersion() throws Exception { - String root = effectiveVersion(PROJECT_ROOT.resolve("pom.xml")); + Set targets = acceptableTargets(); String site = Files.readString(PROJECT_ROOT.resolve("docs/index.html")); assertThat(firstGroup(site, "\"softwareVersion\":\\s*\"v?([0-9][^\"]*)\"")) - .describedAs("docs/index.html JSON-LD softwareVersion must equal the project version (%s)", root) - .isEqualTo(root); - assertThat(firstGroup(site, "jitpack\\.io/#DemchaAV/GraphCompose/v?([0-9][^\"]*)")) - .describedAs("docs/index.html JitPack downloadUrl must equal the project version (%s)", root) - .isEqualTo(root); + .describedAs("docs/index.html JSON-LD softwareVersion must equal the current pom or planned version (one of %s)", targets) + .isIn(targets); assertThat(firstGroup(site, "v([0-9][0-9.]*)\\s*·\\s*MIT")) - .describedAs("docs/index.html hero version badge must equal the project version (%s)", root) - .isEqualTo(root); - assertThat(firstGroup(site, "<version>v?([0-9][^&]*)</version>")) - .describedAs("docs/index.html Maven install snippet must equal the project version (%s)", root) - .isEqualTo(root); - assertThat(firstGroup(site, "GraphCompose:v?([0-9][^')]*)")) - .describedAs("docs/index.html Gradle install snippet must equal the project version (%s)", root) - .isEqualTo(root); + .describedAs("docs/index.html hero version badge must equal the current pom or planned version (one of %s)", targets) + .isIn(targets); + assertThat(firstMatchingGroup(site, INSTALL_SNIPPET_PATTERNS_SHOWCASE_MAVEN)) + .describedAs("docs/index.html Maven install snippet must equal the current pom or planned version (one of %s)", targets) + .isIn(targets); + assertThat(firstMatchingGroup(site, INSTALL_SNIPPET_PATTERNS_SHOWCASE_GRADLE)) + .describedAs("docs/index.html Gradle install snippet must equal the current pom or planned version (one of %s)", targets) + .isIn(targets); + } + + // โ”€โ”€ Install-snippet patterns โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Each install snippet check tries the Maven Central format first + // (canonical from v1.6.6 onwards) and falls back to the legacy + // JitPack format (kept resolvable for v1.6.5-and-earlier pinned + // consumers and possibly still present during the migration + // window). The first regex with a match wins. + + private static final String[] INSTALL_SNIPPET_PATTERNS_README_MAVEN = { + "graphcompose\\s*v?([0-9][^<]*)", + "GraphCompose\\s*v?([0-9][^<]*)" + }; + private static final String[] INSTALL_SNIPPET_PATTERNS_README_GRADLE = { + "io\\.github\\.demchaav:graphcompose:v?([0-9][^\")]*)", + "GraphCompose:v?([0-9][^\")]*)" + }; + private static final String[] INSTALL_SNIPPET_PATTERNS_SHOWCASE_MAVEN = { + "<artifactId>graphcompose</artifactId>\\s*<version>v?([0-9][^&]*)</version>", + "<version>v?([0-9][^&]*)</version>" + }; + private static final String[] INSTALL_SNIPPET_PATTERNS_SHOWCASE_GRADLE = { + "io\\.github\\.demchaav:graphcompose:v?([0-9][^')]*)", + "GraphCompose:v?([0-9][^')]*)" + }; + + /** + * Returns the set of versions that any install snippet may legitimately + * advertise: the current {@code pom.xml} version always, plus the version + * named in the top {@code CHANGELOG.md} {@code Planned} entry if one + * exists. The Planned entry covers the pre-cut window where {@code pom.xml} + * still carries the previous release version while the README + showcase + * already advertise the about-to-ship version. + */ + private Set acceptableTargets() throws Exception { + Set targets = new LinkedHashSet<>(); + targets.add(effectiveVersion(PROJECT_ROOT.resolve("pom.xml"))); + String changelog = Files.readString(PROJECT_ROOT.resolve("CHANGELOG.md")); + Matcher planned = Pattern.compile("^## v([0-9][^ \\n]*)\\s*[\\u2014\\-]\\s*Planned\\b", Pattern.MULTILINE) + .matcher(changelog); + if (planned.find()) { + targets.add(planned.group(1)); + } + return targets; + } + + /** + * Returns the captured group of the first regex in {@code patterns} that + * matches. Fails with a descriptive message listing every pattern tried if + * none matches. + */ + private static String firstMatchingGroup(String text, String[] patterns) { + for (String regex : patterns) { + Matcher matcher = Pattern.compile(regex).matcher(text); + if (matcher.find()) { + return matcher.group(1); + } + } + assertThat(false) + .describedAs("Expected at least one version token matching one of these patterns: %s", String.join(" | ", patterns)) + .isTrue(); + throw new IllegalStateException("unreachable"); } private static String firstGroup(String text, String regex) {