From 0fc3530ab7dc384e1383375de415c2c44942da57 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 08:52:49 -0500 Subject: [PATCH 1/8] Remove unused imports --- .../java/org/ajoberstar/reckon/core/ReckonerIntegTest.java | 3 +-- .../src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java index e6a01f3..0eacdc9 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java @@ -1,6 +1,6 @@ package org.ajoberstar.reckon.core; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.nio.file.Files; @@ -16,7 +16,6 @@ import org.ajoberstar.grgit.Grgit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java index a8074a0..9448ee2 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java @@ -9,7 +9,6 @@ import java.util.Optional; import java.util.Set; -import net.bytebuddy.implementation.auxiliary.AuxiliaryType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; From 5a5eb776a03c7ea0cecd63cc51bc2ba5c5d035bd Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 09:49:12 -0500 Subject: [PATCH 2/8] Use reckon settings plugin --- build.gradle.kts | 10 ---------- settings.gradle.kts | 19 +++++++++++++++---- 2 files changed, 15 insertions(+), 14 deletions(-) delete mode 100644 build.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index eed48ff..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("org.ajoberstar.reckon") -} - -reckon { - setDefaultInferredScope("patch") - stages("beta", "rc", "final") - setScopeCalc(calcScopeFromProp().or(calcScopeFromCommitMessages())) - setStageCalc(calcStageFromProp()) -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 1456f63..4ee969d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,10 @@ pluginManagement { plugins { - id("org.ajoberstar.defaults.java-library") version "0.17.1" - id("org.ajoberstar.defaults.gradle-plugin") version "0.17.1" + id("org.ajoberstar.defaults.java-library") version "0.17.5" + id("org.ajoberstar.defaults.gradle-plugin") version "0.17.5" - id("org.ajoberstar.grgit") version "5.0.0" - id("org.ajoberstar.reckon") version "0.16.1" + id("org.ajoberstar.grgit") version "5.2.0" + id("org.ajoberstar.reckon.settings") version "0.17.0" id("org.ajoberstar.stutter") version "0.7.1" id("com.diffplug.spotless") version "6.9.1" @@ -15,6 +15,17 @@ pluginManagement { } } +plugins { + id("org.ajoberstar.reckon.settings") +} + +extensions.configure { + setDefaultInferredScope("patch") + stages("beta", "rc", "final") + setScopeCalc(calcScopeFromProp().or(calcScopeFromCommitMessages())) + setStageCalc(calcStageFromProp()) +} + dependencyResolutionManagement { repositories { mavenCentral() From dae336c6a5ae018e99c99f6d90c0828f6d1e5339 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 08:53:10 -0500 Subject: [PATCH 3/8] major: Remove all use of grgit --- reckon-core/build.gradle.kts | 1 - reckon-core/gradle.lockfile | 4 +- .../reckon/core/GitInventorySupplierTest.java | 158 ++++++++---------- .../reckon/core/ReckonerIntegTest.java | 101 +++++------ reckon-gradle/build.gradle.kts | 5 - reckon-gradle/gradle.lockfile | 19 +-- .../reckon/gradle/BaseCompatTest.groovy | 115 +++++-------- .../gradle/CompositeBuildCompatTest.groovy | 29 ++-- .../org/ajoberstar/reckon/gradle/Gits.java | 94 +++++++++++ .../reckon/gradle/SettingsCompatTest.groovy | 112 +++++-------- .../reckon/gradle/ReckonCreateTagTask.java | 134 +++++++++------ .../reckon/gradle/ReckonExtension.java | 46 ++--- .../reckon/gradle/ReckonPlugin.java | 19 +-- .../reckon/gradle/ReckonPushTagTask.java | 77 +++------ .../reckon/gradle/ReckonSettingsPlugin.java | 20 +-- 15 files changed, 471 insertions(+), 463 deletions(-) create mode 100644 reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/Gits.java diff --git a/reckon-core/build.gradle.kts b/reckon-core/build.gradle.kts index 772cbe5..17745ae 100644 --- a/reckon-core/build.gradle.kts +++ b/reckon-core/build.gradle.kts @@ -37,7 +37,6 @@ testing { dependencies { implementation("org.junit.jupiter:junit-jupiter-params") implementation("org.mockito:mockito-core:latest.release") - implementation("org.ajoberstar.grgit:grgit-core:[5.0,6.0[") runtimeOnly("org.slf4j:slf4j-simple:[2.0,3.0[") } } diff --git a/reckon-core/gradle.lockfile b/reckon-core/gradle.lockfile index 026ff3b..4f5c659 100644 --- a/reckon-core/gradle.lockfile +++ b/reckon-core/gradle.lockfile @@ -5,10 +5,8 @@ com.github.zafarkhaja:java-semver:0.9.0=compileClasspath,runtimeClasspath,testCo com.googlecode.javaewah:JavaEWAH:1.1.13=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath net.bytebuddy:byte-buddy-agent:1.14.4=testCompileClasspath,testRuntimeClasspath net.bytebuddy:byte-buddy:1.14.4=testCompileClasspath,testRuntimeClasspath -org.ajoberstar.grgit:grgit-core:5.2.0=testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.12.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath -org.codehaus.groovy:groovy:3.0.17=testCompileClasspath,testRuntimeClasspath org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.9.2=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.9.2=testRuntimeClasspath @@ -23,4 +21,4 @@ org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.7=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-simple:2.0.7=testRuntimeClasspath -empty= +empty=annotationProcessor,signatures,testAnnotationProcessor diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/GitInventorySupplierTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/GitInventorySupplierTest.java index 8a54ea2..d6063a4 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/GitInventorySupplierTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/GitInventorySupplierTest.java @@ -1,9 +1,8 @@ package org.ajoberstar.reckon.core; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; @@ -12,19 +11,15 @@ import java.util.Optional; import java.util.Set; -import org.ajoberstar.grgit.Grgit; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.junit.jupiter.api.*; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class GitInventorySupplierTest { private Path repoDir; - private Grgit grgit; + private Git git; private SecureRandom random = new SecureRandom(); @@ -32,48 +27,48 @@ public class GitInventorySupplierTest { @Test @DisplayName("commit id is abbreviated, not full") - public void commitIdIsAbbreviated() { - assertEquals(Optional.of(grgit.head().getAbbreviatedId()), supplier.getInventory().getCommitId()); + public void commitIdIsAbbreviated() throws IOException { + assertEquals(Optional.of(git.getRepository().resolve("HEAD").abbreviate(7).name()), supplier.getInventory().getCommitId()); } @Test @DisplayName("if HEAD has no tagged versions, current version is empty") - public void headNotTaggedCurrentEmpty() { + public void headNotTaggedCurrentEmpty() throws IOException, GitAPIException { checkout("head-untagged"); assertEquals(Optional.empty(), supplier.getInventory().getCurrentVersion()); } @Test @DisplayName("if single tagged version on HEAD, it is current version") - public void headOneTagIsCurrent() { + public void headOneTagIsCurrent() throws IOException, GitAPIException { checkout("head-single-tag"); assertEquals(Version.parse("0.1.0-milestone.1"), supplier.getInventory().getCurrentVersion()); } @Test @DisplayName("if multiple tagged version on HEAD, the max is current version") - public void headMultiTagMaxIsCurrent() { + public void headMultiTagMaxIsCurrent() throws IOException, GitAPIException { checkout("head-multi-tag"); assertEquals(Version.parse("0.1.0"), supplier.getInventory().getCurrentVersion()); } @Test @DisplayName("if no tagged finals in HEAD\"s history, base normal is 0.0.0") - public void noFinalTagsBaseNormalIdentity() { + public void noFinalTagsBaseNormalIdentity() throws IOException, GitAPIException { checkout("final-unreachable"); assertEquals(Version.parse("0.0.0").get(), supplier.getInventory().getBaseNormal()); } @Test @DisplayName("if tagged finals in HEAD\"s history, base normal is max of finals which have no other final between them and HEAD") - public void finalTagsNormalIsNearestMax() { + public void finalTagsNormalIsNearestMax() throws IOException, GitAPIException { checkout("final-reachable"); assertEquals(Version.parse("1.0.0").get(), supplier.getInventory().getBaseNormal()); } @Test @DisplayName("if tagged finals on head, base normal and version are same as current version") - public void headFinalTagBaseNormalAndVersionAreCurrent() { + public void headFinalTagBaseNormalAndVersionAreCurrent() throws IOException, GitAPIException { checkout("final-current"); var inventory = supplier.getInventory(); assertEquals(inventory.getCurrentVersion().get(), inventory.getBaseNormal()); @@ -82,21 +77,21 @@ public void headFinalTagBaseNormalAndVersionAreCurrent() { @Test @DisplayName("if no tagged versions in HEAD\"s history, base version is 0.0.0") - public void noTagsBaseIdentity() { + public void noTagsBaseIdentity() throws IOException, GitAPIException { checkout("version-unreachable"); assertEquals(Version.parse("0.0.0").get(), supplier.getInventory().getBaseVersion()); } @Test @DisplayName("if tagged versions in HEAD\"s history, base version is max of versions which have no other version between them and HEAD") - public void taggedBaseVersionIsNearestMax() { + public void taggedBaseVersionIsNearestMax() throws IOException, GitAPIException { checkout("version-reachable"); assertEquals(Version.parse("0.3.0-milestone.1").get(), supplier.getInventory().getBaseVersion()); } @Test @DisplayName("if tagged versions on head, base version is same as current version") - public void headTaggedBaseIsCurrent() { + public void headTaggedBaseIsCurrent() throws IOException, GitAPIException { checkout("version-current"); var inventory = supplier.getInventory(); assertEquals(inventory.getCurrentVersion().get(), inventory.getBaseVersion()); @@ -104,7 +99,7 @@ public void headTaggedBaseIsCurrent() { @Test @DisplayName("if current is tagged with final, commits since base is 0") - public void currentIsFInalCommitsIs0() { + public void currentIsFInalCommitsIs0() throws IOException, GitAPIException { checkout("final-current"); assertEquals(0, supplier.getInventory().getCommitsSinceBase()); assertEquals(List.of(), supplier.getInventory().getCommitMessages()); @@ -112,7 +107,7 @@ public void currentIsFInalCommitsIs0() { @Test @DisplayName("if no reachable tagged finals, commits since base is size of log from HEAD") - public void noFinalTagsCommitsIsHistorySize() { + public void noFinalTagsCommitsIsHistorySize() throws IOException, GitAPIException { checkout("final-unreachable"); assertEquals(4, supplier.getInventory().getCommitsSinceBase()); assertEquals(List.of("do", "do", "do", "do"), supplier.getInventory().getCommitMessages()); @@ -120,28 +115,28 @@ public void noFinalTagsCommitsIsHistorySize() { @Test @DisplayName("if reachable tagged finals, commits since base is size of log from HEAD excluding the base normal") - public void finalTagsCommitsIsSizeSinceBase() { + public void finalTagsCommitsIsSizeSinceBase() throws IOException, GitAPIException { checkout("final-reachable"); assertEquals(10, supplier.getInventory().getCommitsSinceBase()); } @Test @DisplayName("if no branches share merge base with HEAD, no parallel versions returned") - public void noMergeBasesNoParallel() { + public void noMergeBasesNoParallel() throws IOException, GitAPIException { checkout("parallel-no-base"); assertEquals(Collections.emptySet(), supplier.getInventory().getParallelNormals()); } @Test @DisplayName("if tagged version between HEAD and merge base, no parallel versions returned") - public void tagsNearerThanMergeBaseNoParallel() { + public void tagsNearerThanMergeBaseNoParallel() throws GitAPIException, IOException { checkout("parallel-tagged-since-merge"); assertEquals(Collections.emptySet(), supplier.getInventory().getParallelNormals()); } @Test @DisplayName("if no tagged version between HEAD and merge base, parallel versions returned") - public void noTagNearerThanMergeBaseHasParallel() { + public void noTagNearerThanMergeBaseHasParallel() throws IOException, GitAPIException { checkout("parallel-untagged-since-merge"); assertEquals(Set.of(Version.parse("0.2.0").get()), supplier.getInventory().getParallelNormals()); } @@ -160,26 +155,24 @@ public void allTagsClaimed() { @Test @DisplayName("if no commits, all results are empty") - public void noCommitsEmpty() { - var emptyGrgit = Grgit.init(op -> { - try { - op.setDir(Files.createTempDirectory("repo2").toFile()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - - var emptySupplier = new GitInventorySupplier(emptyGrgit.getRepository().getJgit().getRepository(), VersionTagParser.getDefault()); + public void noCommitsEmpty() throws IOException, GitAPIException { + var emptyRepoDir = Files.createTempDirectory("repo"); + var emptyGit = Git.init() + .setDirectory(emptyRepoDir.toFile()) + .call(); + + var emptySupplier = new GitInventorySupplier(emptyGit.getRepository(), VersionTagParser.getDefault()); assertEquals(VcsInventory.empty(true), emptySupplier.getInventory()); } @BeforeAll - public void initRepository() throws IOException { + public void initRepository() throws IOException, GitAPIException { repoDir = Files.createTempDirectory("repo"); - grgit = Grgit.init(op -> { - op.setDir(repoDir); - }); - var initialBranch = grgit.getBranch().current().getName(); + git = Git.init() + .setDirectory(repoDir.toFile()) + .call(); + + var initialBranch = git.getRepository().getBranch(); commit(); commit(); @@ -249,72 +242,67 @@ public void initRepository() throws IOException { } @AfterAll - public void cleanupRepo() throws IOException { - grgit.close(); + public void cleanupRepo() { + git.close(); } @BeforeEach public void initSupplier() { - supplier = new GitInventorySupplier(grgit.getRepository().getJgit().getRepository(), VersionTagParser.getDefault()); + supplier = new GitInventorySupplier(git.getRepository(), VersionTagParser.getDefault()); } - private void commit() throws IOException { + private void commit() throws IOException, GitAPIException { var bytes = new byte[128]; random.nextBytes(bytes); var fileName = random.nextInt(); Files.write(repoDir.resolve(fileName + ".txt"), bytes); - grgit.add(op -> { - op.setPatterns(Set.of(fileName + ".txt")); - }); - var commit = grgit.commit(op -> { - op.setMessage("do"); - }); - System.out.println("Created commit: " + commit.getAbbreviatedId()); + + git.add() + .addFilepattern(fileName + ".txt") + .call(); + + var commit = git.commit() + .setMessage("do") + .call(); + + System.out.println("Created commit: " + commit.getId().name()); } - private void branch(String name) { - var currentHead = grgit.head(); - var currentBranch = grgit.getBranch().current(); - var newBranch = grgit.getBranch().add(op -> { - op.setName(name); - }); - var atCommit = grgit.getResolve().toCommit(newBranch.getFullName()); - System.out.println("Added new branch " + name + " at " + atCommit.getAbbreviatedId()); - assertEquals(grgit.getBranch().current(), currentBranch); + private void branch(String name) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var currentBranch = git.getRepository().getBranch(); + var newBranch = git.branchCreate().setName(name).call(); + + var atCommit = git.getRepository().resolve(newBranch.getName()); + + System.out.println("Added new branch " + name + " at " + atCommit.name()); + assertEquals(git.getRepository().getBranch(), currentBranch); assertEquals(atCommit, currentHead); } - private void tag(String name) { + private void tag(String name) throws GitAPIException, IOException { tag(name, true); } - private void tag(String name, boolean annotate) { - var currentHead = grgit.head(); - var newTag = grgit.getTag().add(op -> { - op.setName(name); - op.setAnnotate(annotate); - }); - var atCommit = grgit.getResolve().toCommit(newTag.getFullName()); - System.out.println("Added new tag " + name + " at " + atCommit.getAbbreviatedId()); + private void tag(String name, boolean annotate) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var newTag = git.tag().setName(name).setAnnotated(annotate).call(); + var atCommit = git.getRepository().resolve(newTag.getName() + "^{commit}"); + System.out.println("Added new tag " + name + " at " + atCommit.name()); assertEquals(atCommit, currentHead); } - private void checkout(String name) { - var currentHead = grgit.head(); - grgit.checkout(op -> { - op.setBranch(name); - }); - var atCommit = grgit.getResolve().toCommit(name); - System.out.println("Checked out " + name + " at " + atCommit.getAbbreviatedId()); - assertEquals(grgit.getBranch().current().getName(), name); + private void checkout(String name) throws IOException, GitAPIException { + git.checkout().setName(name).call(); + var atCommit = git.getRepository().resolve(name); + System.out.println("Checked out " + name + " at " + atCommit.name()); + assertEquals(git.getRepository().getBranch(), name); } - private void merge(String name) { - var currentBranch = grgit.getBranch().current().getName(); - grgit.merge(op -> { - op.setHead(name); - }); + private void merge(String name) throws IOException, GitAPIException { + var currentBranch = git.getRepository().getBranch(); + git.merge().include(git.getRepository().resolve(name)).call(); System.out.println("Merged " + name + " into " + currentBranch); - assertEquals(grgit.getBranch().current().getName(), currentBranch); + assertEquals(git.getRepository().getBranch(), currentBranch); } } diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java index 0eacdc9..6d8b09f 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java @@ -10,10 +10,9 @@ import java.time.Instant; import java.time.ZoneId; import java.util.Optional; -import java.util.Set; -import org.ajoberstar.grgit.Commit; -import org.ajoberstar.grgit.Grgit; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -24,17 +23,17 @@ public class ReckonerIntegTest { private static final String TIMESTAMP = "20180704T171826Z"; private Path repoDir; - private Grgit grgit; + private Git git; private String initialBranch; private SecureRandom random = new SecureRandom(); @Test @DisplayName("rebuild works with parallel branches") - public void rebuildOnParallel() throws IOException { - var feature1 = commit(); + public void rebuildOnParallel() throws IOException, GitAPIException { + commit(); tag("0.1.0"); branch("release-0.1"); - var feature2 = commit(); + commit(); tag("0.2.0-rc.1"); commit(); checkout("release-0.1"); @@ -54,23 +53,21 @@ public void rebuildOnParallel() throws IOException { } @BeforeEach - public void setupRepo() throws IOException { + public void setupRepo() throws IOException, GitAPIException { repoDir = Files.createTempDirectory("repo"); - grgit = Grgit.init(op -> { - op.setDir(repoDir); - }); - initialBranch = grgit.getBranch().current().getName(); + git = Git.init().setDirectory(repoDir.toFile()).call(); + initialBranch = git.getRepository().getBranch(); } @AfterEach public void cleanupRepo() { - grgit.close(); + git.close(); } private String reckonStage(Scope scope, String stage) { return Reckoner.builder() .clock(CLOCK) - .git(grgit.getRepository().getJgit().getRepository()) + .git(git.getRepository()) .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) .stages("beta", "milestone", "rc", "final") @@ -82,7 +79,7 @@ private String reckonStage(Scope scope, String stage) { private String reckonSnapshot(Scope scope, String stage) { return Reckoner.builder() .clock(CLOCK) - .git(grgit.getRepository().getJgit().getRepository()) + .git(git.getRepository()) .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) .snapshots() @@ -91,63 +88,57 @@ private String reckonSnapshot(Scope scope, String stage) { .toString(); } - private Commit commit() throws IOException { + private void commit() throws IOException, GitAPIException { var bytes = new byte[128]; random.nextBytes(bytes); var fileName = random.nextInt(); Files.write(repoDir.resolve(fileName + ".txt"), bytes); - grgit.add(op -> { - op.setPatterns(Set.of(fileName + ".txt")); - }); - var commit = grgit.commit(op -> { - op.setMessage("do"); - }); - System.out.println("Created commit: " + commit.getAbbreviatedId()); - return commit; + + git.add() + .addFilepattern(fileName + ".txt") + .call(); + + var commit = git.commit() + .setMessage("do") + .call(); + + System.out.println("Created commit: " + commit.getId().name()); } - private void branch(String name) { - var currentHead = grgit.head(); - var currentBranch = grgit.getBranch().current(); - var newBranch = grgit.getBranch().add(op -> { - op.setName(name); - }); - var atCommit = grgit.getResolve().toCommit(newBranch.getFullName()); - System.out.println("Added new branch " + name + " at " + atCommit.getAbbreviatedId()); - assertEquals(grgit.getBranch().current(), currentBranch); + private void branch(String name) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var currentBranch = git.getRepository().getBranch(); + var newBranch = git.branchCreate().setName(name).call(); + + var atCommit = git.getRepository().resolve(newBranch.getName()); + + System.out.println("Added new branch " + name + " at " + atCommit.name()); + assertEquals(git.getRepository().getBranch(), currentBranch); assertEquals(atCommit, currentHead); } - private void tag(String name) { + private void tag(String name) throws GitAPIException, IOException { tag(name, true); } - private void tag(String name, boolean annotate) { - var currentHead = grgit.head(); - var newTag = grgit.getTag().add(op -> { - op.setName(name); - op.setAnnotate(annotate); - }); - var atCommit = grgit.getResolve().toCommit(newTag.getFullName()); - System.out.println("Added new tag " + name + " at " + atCommit.getAbbreviatedId()); + private void tag(String name, boolean annotate) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var newTag = git.tag().setName(name).setAnnotated(annotate).call(); + var atCommit = git.getRepository().resolve(newTag.getName() + "^{commit}"); + System.out.println("Added new tag " + name + " at " + atCommit.name()); assertEquals(atCommit, currentHead); } - private void checkout(String name) { - var currentHead = grgit.head(); - grgit.checkout(op -> { - op.setBranch(name); - }); - var atCommit = grgit.getResolve().toCommit(name); - System.out.println("Checked out " + name + " at " + atCommit.getAbbreviatedId()); + private void checkout(String name) throws IOException, GitAPIException { + git.checkout().setName(name).call(); + var atCommit = git.getRepository().resolve(name + "^{commit}"); + System.out.println("Checked out " + name + " at " + atCommit.name()); } - private void merge(String name) { - var currentBranch = grgit.getBranch().current().getName(); - grgit.merge(op -> { - op.setHead(name); - }); + private void merge(String name) throws IOException, GitAPIException { + var currentBranch = git.getRepository().getBranch(); + git.merge().include(git.getRepository().resolve(name)).call(); System.out.println("Merged " + name + " into " + currentBranch); - assertEquals(grgit.getBranch().current().getName(), currentBranch); + assertEquals(git.getRepository().getBranch(), currentBranch); } } diff --git a/reckon-gradle/build.gradle.kts b/reckon-gradle/build.gradle.kts index 24aec41..5e99d3e 100644 --- a/reckon-gradle/build.gradle.kts +++ b/reckon-gradle/build.gradle.kts @@ -29,12 +29,7 @@ dependencies { api(project(":reckon-core")) // git - api("org.ajoberstar.grgit:grgit-gradle:[5.0,6.0[") implementation("org.eclipse.jgit:org.eclipse.jgit:[6.0,7.0[") - compatTestImplementation("org.ajoberstar.grgit:grgit-core:[5.0,6.0[") - - // util - implementation("com.google.guava:guava:latest.release") // testing compatTestImplementation(gradleTestKit()) diff --git a/reckon-gradle/gradle.lockfile b/reckon-gradle/gradle.lockfile index 882f165..6aebed4 100644 --- a/reckon-gradle/gradle.lockfile +++ b/reckon-gradle/gradle.lockfile @@ -2,26 +2,17 @@ # Manual edits can break the build and are not advised. # This file is expected to be part of source control. com.github.zafarkhaja:java-semver:0.9.0=runtimeClasspath,testRuntimeClasspath -com.google.code.findbugs:jsr305:3.0.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.errorprone:error_prone_annotations:2.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:failureaccess:1.0.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:guava:31.1-jre=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.j2objc:j2objc-annotations:1.3=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.googlecode.javaewah:JavaEWAH:1.1.13=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ajoberstar.grgit:grgit-core:5.2.0=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ajoberstar.grgit:grgit-gradle:5.2.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.googlecode.javaewah:JavaEWAH:1.1.13=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.12.0=runtimeClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=compatTestCompileClasspath -org.checkerframework:checker-qual:3.12.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy:3.0.17=compatTestCompileClasspath,compatTestRuntimeClasspath -org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy:3.0.12=compatTestCompileClasspath,compatTestRuntimeClasspath +org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit.platform:junit-platform-commons:1.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit.platform:junit-platform-engine:1.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit:junit-bom:5.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.opentest4j:opentest4j:1.2.0=compatTestCompileClasspath,compatTestRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,testCompileClasspath +org.slf4j:slf4j-api:1.7.30=compileClasspath,testCompileClasspath org.slf4j:slf4j-api:2.0.7=runtimeClasspath,testRuntimeClasspath org.spockframework:spock-core:2.3-groovy-3.0=compatTestCompileClasspath,compatTestRuntimeClasspath -empty= +empty=annotationProcessor,compatTestAnnotationProcessor,signatures,testAnnotationProcessor diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy index 3c8907f..29d69b2 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy @@ -1,39 +1,36 @@ package org.ajoberstar.reckon.gradle -import spock.lang.Specification -import spock.lang.TempDir - -import org.ajoberstar.grgit.Grgit -import org.gradle.testkit.runner.GradleRunner +import org.eclipse.jgit.api.Git import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome +import spock.lang.Specification +import spock.lang.TempDir class BaseCompatTest extends Specification { @TempDir File tempDir File projectDir File buildFile - Grgit remote - Grgit remote2 + Git remote + Git remote2 def setup() { projectDir = new File(tempDir, 'project') buildFile = projectFile('build.gradle') def remoteDir = new File(tempDir, 'remote') - remote = Grgit.init(dir: remoteDir) - - remoteFile('.gitignore') << '.gradle/\nbuild/\n' - remoteFile('master.txt') << 'contents here' - remote.add(patterns: ['.']) - remote.commit(message: 'first commit') - remote.tag.add(name: '1.0.0', message: '1.0.0') - remote.tag.add(name: 'project-a/9.0.0', message: '9.0.0') - remoteFile('master.txt') << 'contents here2' - remote.add(patterns: ['.']) - remote.commit(message: 'major: second commit') + remote = Git.init().setDirectory(remoteDir).call() + + Gits.repoFile(remote, '.gitignore') << '.gradle/\nbuild/\n' + Gits.repoFile(remote, 'master.txt') << 'contents here' + Gits.commitAll(remote, 'first commit') + Gits.tag(remote, '1.0.0') + Gits.tag(remote, 'project-a/9.0.0') + Gits.repoFile(remote, 'master.txt') << 'contents here2' + Gits.commitAll(remote, 'major: second commit') def remote2Dir = new File(tempDir, 'remote2') - remote2 = Grgit.clone(dir: remote2Dir, uri: remote.repository.rootDir) + remote2 = Gits.clone(remote2Dir, remote) } def 'if no git repo found, version is defaulted'() { @@ -63,7 +60,7 @@ task printVersion { def 'if no strategies specified, version is unspecified'() { given: - Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -84,7 +81,7 @@ task printVersion { def 'if version evaluated before reckon configured, reckon can still be evaluated after'() { given: - Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -113,7 +110,7 @@ task printVersion { def 'if reckoned version has build metadata no tag created'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -126,8 +123,7 @@ reckon { defaultInferredScope = 'patch' } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -138,7 +134,7 @@ reckon { def 'if reckoned version is SNAPSHOT no tag created'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -150,8 +146,7 @@ reckon { snapshotFromProp() } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -162,7 +157,7 @@ reckon { def 'if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -174,8 +169,7 @@ reckon { stageFromProp('alpha','beta', 'final') } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -183,12 +177,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.1.0-alpha.1' } + Gits.hasTag(remote, '1.1.0-alpha.1') } def 'can use commit messages for scope and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -201,8 +195,7 @@ reckon { stageCalc = calcStageFromProp() } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -210,12 +203,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '2.0.0-alpha.1' } + Gits.hasTag(remote, '2.0.0-alpha.1') } def 'can use commit messages for scope but override with prop and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -228,8 +221,7 @@ reckon { stageCalc = calcStageFromProp() } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.scope=patch', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -237,12 +229,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.0.1-alpha.1' } + Gits.hasTag(remote, '1.0.1-alpha.1') } def 'remote can be overridden and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -255,9 +247,8 @@ reckon { remote = 'other-remote' } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') - local.remote.add(name: 'other-remote', url: remote2.getRepository().getRootDir()) + Gits.commitAll(local) + Gits.remoteAdd(local, 'other-remote', remote2.getRepository().getDirectory()) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -265,13 +256,13 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - !remote.tag.list().find { it.name == '1.1.0-alpha.1' } - remote2.tag.list().find { it.name == '1.1.0-alpha.1' } + !Gits.hasTag(remote, '1.1.0-alpha.1') + Gits.hasTag(remote2, '1.1.0-alpha.1') } def 'tag parser/writer can be overridden and reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -289,8 +280,7 @@ reckon { tagWriter = version -> "project-a/" + version } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -298,12 +288,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == 'project-a/9.1.0-alpha.1' } + Gits.hasTag(remote, 'project-a/9.1.0-alpha.1') } def 'tag message can be overridden and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ plugins { @@ -316,8 +306,7 @@ reckon { tagMessage = version.map(v -> "Version " + v) } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -325,12 +314,13 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.1.0-alpha.1' && it.shortMessage == 'Version 1.1.0-alpha.1' } + Gits.hasTag(remote, '1.1.0-alpha.1') + // TODO also test message } def 'if reckoned version is rebuild, skip tag create, but push'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) buildFile << """ @@ -343,9 +333,8 @@ reckon { stageFromProp('alpha', 'beta', 'final') } """ - local.add(patterns: ['build.gradle']) - local.commit(message: 'Build file') - local.tag.add(name: '1.1.0', message: '1.1.0') + Gits.commitAll(local) + Gits.tag(local, '1.1.0') when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -374,18 +363,6 @@ reckon { .buildAndFail() } - private File remoteFile(String path) { - File file = new File(remote.repository.rootDir, path) - file.parentFile.mkdirs() - return file - } - - private File remote2File(String path) { - File file = new File(remote2.repository.rootDir, path) - file.parentFile.mkdirs() - return file - } - private File projectFile(String path) { File file = new File(projectDir, path) file.parentFile.mkdirs() diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy index 9ad52ea..7e26742 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy @@ -1,12 +1,11 @@ package org.ajoberstar.reckon.gradle -import spock.lang.IgnoreIf -import spock.lang.Specification -import spock.lang.TempDir -import org.ajoberstar.grgit.Grgit -import org.gradle.testkit.runner.GradleRunner +import org.eclipse.jgit.api.Git import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import spock.lang.Specification +import spock.lang.TempDir class CompositeBuildCompatTest extends Specification { @TempDir File tempDir @@ -21,12 +20,11 @@ class CompositeBuildCompatTest extends Specification { build1File = projectFile(project1Dir, 'build.gradle') build2File = projectFile(project2Dir, 'build.gradle') - def grgit1 = Grgit.init(dir: project1Dir) + def git1 = Git.init().setDirectory(project1Dir).call() projectFile(project1Dir, 'settings.gradle') << 'rootProject.name = "project1"' projectFile(project1Dir, '.gitignore') << '.gradle\nbuild\n' build1File << '''\ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } @@ -41,17 +39,15 @@ task printVersion { } } ''' - grgit1.add(patterns: ['.']) - grgit1.commit(message: 'first commit') - grgit1.tag.add(name: '1.3.0', message: 'stuff') - grgit1.close() + Gits.commitAll(git1, 'first commit') + Gits.tag(git1, '1.3.0') + git1.close() - def grgit2 = Grgit.init(dir: project2Dir) + def git2 = Git.init().setDirectory(project2Dir).call(); projectFile(project2Dir, 'settings.gradle') << 'rootProject.name = "project2"' projectFile(project2Dir, '.gitignore') << '.gradle\nbuild\n' build2File << '''\ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } @@ -66,10 +62,9 @@ task printVersion { } } ''' - grgit2.add(patterns: ['.']) - grgit2.commit(message: 'first commit') - grgit2.tag.add(name: '1.0.0-beta.1', message: 'stuff') - grgit2.close() + Gits.commitAll(git2, 'first commit') + Gits.tag(git2, '1.0.0-beta.1') + git2.close() } def 'if build included in composite build, reckon properties are ignored'() { diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/Gits.java b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/Gits.java new file mode 100644 index 0000000..b9220f9 --- /dev/null +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/Gits.java @@ -0,0 +1,94 @@ +package org.ajoberstar.reckon.gradle; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.transport.URIish; + +public final class Gits { + private static final SecureRandom random = new SecureRandom(); + + public static Git clone(File dir, Git origin) throws GitAPIException { + var uri = origin.getRepository().getDirectory().getAbsolutePath(); + return Git.cloneRepository().setDirectory(dir).setURI(uri).setCloneAllBranches(true).setNoCheckout(false).call(); + } + + public static Path repoFile(Git git, String path) throws IOException { + var root = git.getRepository().getWorkTree().toPath(); + var result = root.resolve(path); + Files.createDirectories(result.getParent()); + return result; + } + + public static void commit(Git git, String message) throws IOException, GitAPIException { + var bytes = new byte[128]; + random.nextBytes(bytes); + var fileName = random.nextInt(); + Files.write(repoFile(git, fileName + ".txt"), bytes); + + git.add().addFilepattern(fileName + ".txt").call(); + var commit = git.commit().setMessage(message).call(); + + System.out.println("Created commit: " + commit.getId().name()); + } + + public static void commitAll(Git git) throws IOException, GitAPIException { + commitAll(git, "do"); + } + + public static void commitAll(Git git, String message) throws IOException, GitAPIException { + git.add().addFilepattern(".").call(); + var commit = git.commit().setMessage(message).call(); + System.out.println("Created commit: " + commit.getId().name()); + } + + public static void branch(Git git, String name) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var currentBranch = git.getRepository().getBranch(); + var newBranch = git.branchCreate().setName(name).call(); + + var atCommit = git.getRepository().resolve(newBranch.getName()); + + System.out.println("Added new branch " + name + " at " + atCommit.name()); + } + + public static void tag(Git git, String name) throws GitAPIException, IOException { + tag(git, name, true); + } + + public static void tag(Git git, String name, boolean annotate) throws IOException, GitAPIException { + var currentHead = git.getRepository().resolve("HEAD"); + var newTag = git.tag().setName(name).setAnnotated(annotate).call(); + var atCommit = git.getRepository().resolve(newTag.getName() + "^{commit}"); + System.out.println("Added new tag " + name + " at " + atCommit.name()); + } + + public static void checkout(Git git, String name) throws IOException, GitAPIException { + git.checkout().setName(name).call(); + var atCommit = git.getRepository().resolve(name); + System.out.println("Checked out " + name + " at " + atCommit.name()); + } + + public static void merge(Git git, String name) throws IOException, GitAPIException { + var currentBranch = git.getRepository().getBranch(); + git.merge().include(git.getRepository().resolve(name)).call(); + System.out.println("Merged " + name + " into " + currentBranch); + } + + public static void remoteAdd(Git git, String name, File uri) throws URISyntaxException, GitAPIException { + git.remoteAdd().setName(name).setUri(new URIish(uri.getAbsolutePath())).call(); + git.fetch().setRemote(name).call(); + } + + public static boolean hasTag(Git git, String name) throws GitAPIException { + var tagName = "refs/tags/" + name; + return git.tagList().call().stream() + .anyMatch(tag -> tagName.equals(tag.getName())); + } +} diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy index 65cdf9f..f4e5218 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy @@ -1,20 +1,20 @@ package org.ajoberstar.reckon.gradle -import spock.lang.Specification -import spock.lang.TempDir -import org.ajoberstar.grgit.Grgit -import org.gradle.testkit.runner.GradleRunner +import org.eclipse.jgit.api.Git import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome +import spock.lang.Specification +import spock.lang.TempDir class SettingsCompatTest extends Specification { @TempDir File tempDir File projectDir File settingsFile File buildFile - Grgit remote - Grgit remote2 + Git remote + Git remote2 def setup() { projectDir = new File(tempDir, 'project') @@ -22,20 +22,18 @@ class SettingsCompatTest extends Specification { buildFile = projectFile('build.gradle') def remoteDir = new File(tempDir, 'remote') - remote = Grgit.init(dir: remoteDir) - - remoteFile('.gitignore') << '.gradle/\nbuild/\n' - remoteFile('master.txt') << 'contents here' - remote.add(patterns: ['.']) - remote.commit(message: 'first commit') - remote.tag.add(name: '1.0.0', message: '1.0.0') - remote.tag.add(name: 'project-a/9.0.0', message: '9.0.0') - remoteFile('master.txt') << 'contents here2' - remote.add(patterns: ['.']) - remote.commit(message: 'major: second commit') + remote = Git.init().setDirectory(remoteDir).call() + + Gits.repoFile(remote, '.gitignore') << '.gradle/\nbuild/\n' + Gits.repoFile(remote, 'master.txt') << 'contents here' + Gits.commitAll(remote, 'first commit') + Gits.tag(remote, '1.0.0') + Gits.tag(remote, 'project-a/9.0.0') + Gits.repoFile(remote, 'master.txt') << 'contents here2' + Gits.commitAll(remote, 'major: second commit') def remote2Dir = new File(tempDir, 'remote2') - remote2 = Grgit.clone(dir: remote2Dir, uri: remote.repository.rootDir) + remote2 = Gits.clone(remote2Dir, remote) } def 'if no git repo found, version is defaulted'() { @@ -66,7 +64,7 @@ tasks.register('printVersion') { def 'if no strategies specified, build fails'() { given: - Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -88,7 +86,7 @@ tasks.register('printVersion') { def 'if reckoned version has build metadata no tag created'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -101,8 +99,7 @@ reckon { defaultInferredScope = 'patch' } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -113,7 +110,7 @@ reckon { def 'if reckoned version is SNAPSHOT no tag created'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -125,8 +122,7 @@ reckon { snapshotFromProp() } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -137,7 +133,7 @@ reckon { def 'if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -149,8 +145,7 @@ reckon { stageFromProp('alpha','beta', 'final') } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -158,12 +153,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.1.0-alpha.1' } + Gits.hasTag(remote, '1.1.0-alpha.1') } def 'can use commit messages for scope and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -176,8 +171,7 @@ reckon { stageCalc = calcStageFromProp() } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -185,12 +179,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '2.0.0-alpha.1' } + Gits.hasTag(remote, '2.0.0-alpha.1') } def 'can use commit messages for scope but override with prop and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -203,8 +197,7 @@ reckon { stageCalc = calcStageFromProp() } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.scope=patch', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -212,12 +205,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.0.1-alpha.1' } + Gits.hasTag(remote, '1.0.1-alpha.1') } def 'remote can be overridden and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -230,9 +223,8 @@ reckon { remote = 'other-remote' } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') - local.remote.add(name: 'other-remote', url: remote2.getRepository().getRootDir()) + Gits.commitAll(local) + Gits.remoteAdd(local, 'other-remote', remote2.getRepository().getDirectory()) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -240,13 +232,13 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - !remote.tag.list().find { it.name == '1.1.0-alpha.1' } - remote2.tag.list().find { it.name == '1.1.0-alpha.1' } + !Gits.hasTag(remote, '1.1.0-alpha.1') + Gits.hasTag(remote2, '1.1.0-alpha.1') } def 'tag parser/writer can be overridden and reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -264,8 +256,7 @@ reckon { tagWriter = version -> "project-a/" + version } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -273,12 +264,12 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == 'project-a/9.1.0-alpha.1' } + Gits.hasTag(remote, 'project-a/9.1.0-alpha.1') } def 'tag message can be overridden and if reckoned version is significant tag created and pushed'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ plugins { @@ -291,8 +282,7 @@ reckon { tagMessage = version.map(v -> "Version " + v) } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') + Gits.commitAll(local) when: def result = build('reckonTagPush', '-Preckon.stage=alpha', '--configuration-cache') then: @@ -300,12 +290,13 @@ reckon { result.task(':reckonTagCreate').outcome == TaskOutcome.SUCCESS result.task(':reckonTagPush').outcome == TaskOutcome.SUCCESS and: - remote.tag.list().find { it.name == '1.1.0-alpha.1' && it.shortMessage == 'Version 1.1.0-alpha.1' } + Gits.hasTag(remote, '1.1.0-alpha.1') + // TODO validate the message } def 'if reckoned version is rebuild, skip tag create, but push'() { given: - def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + def local = Gits.clone(projectDir, remote) settingsFile << """ @@ -318,9 +309,8 @@ reckon { stageFromProp('alpha', 'beta', 'final') } """ - local.add(patterns: ['settings.gradle']) - local.commit(message: 'Build file') - local.tag.add(name: '1.1.0', message: '1.1.0') + Gits.commitAll(local) + Gits.tag(local, '1.1.0') when: def result = build('reckonTagPush', '--configuration-cache') then: @@ -349,18 +339,6 @@ reckon { .buildAndFail() } - private File remoteFile(String path) { - File file = new File(remote.repository.rootDir, path) - file.parentFile.mkdirs() - return file - } - - private File remote2File(String path) { - File file = new File(remote2.repository.rootDir, path) - file.parentFile.mkdirs() - return file - } - private File projectFile(String path) { File file = new File(projectDir, path) file.parentFile.mkdirs() diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonCreateTagTask.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonCreateTagTask.java index 1ca088a..c88d2b1 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonCreateTagTask.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonCreateTagTask.java @@ -1,70 +1,110 @@ package org.ajoberstar.reckon.gradle; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + import javax.inject.Inject; -import org.ajoberstar.grgit.gradle.GrgitService; -import org.ajoberstar.reckon.core.Version; -import org.ajoberstar.reckon.core.VersionTagWriter; import org.gradle.api.DefaultTask; -import org.gradle.api.model.ObjectFactory; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.UntrackedTask; +import org.gradle.api.tasks.*; +import org.gradle.process.ExecOperations; @UntrackedTask(because = "Git tracks the state") -public class ReckonCreateTagTask extends DefaultTask { - private Property grgitService; - private Property version; - private Property tagWriter; - private Property tagMessage; - - @Inject - public ReckonCreateTagTask(ObjectFactory objectFactory) { - this.grgitService = objectFactory.property(GrgitService.class); - this.version = objectFactory.property(Version.class); - this.tagWriter = objectFactory.property(VersionTagWriter.class); - this.tagMessage = objectFactory.property(String.class); - } - +public abstract class ReckonCreateTagTask extends DefaultTask { @TaskAction public void create() { - var git = grgitService.get().getGrgit();; - var tagName = tagWriter.get().write(version.get()); - - // rebuilds shouldn't trigger a new tag - var alreadyTagged = git.getTag().list().stream() - .anyMatch(tag -> tag.getName().equals(tagName)); - - if (alreadyTagged || !version.get().isSignificant()) { + if (!getTagName().isPresent()) { setDidWork(false); - } else { - git.getTag().add(op -> { - op.setName(tagName); - op.setMessage(tagMessage.get()); - }); - setDidWork(true); + return; } + + tag(null, null, true); } + @Inject + protected abstract ExecOperations getExecOperations(); + @Internal - public Property getGrgitService() { - return grgitService; - } + public abstract DirectoryProperty getRepoDirectory(); @Input - public Property getVersion() { - return version; - } + @Optional + public abstract Property getTagName(); @Input - public Property getTagWriter() { - return tagWriter; + public abstract Property getTagMessage(); + + private void tag(String userEmail, String userName, boolean allowRetry) { + var input = new ByteArrayInputStream(getTagMessage().get().getBytes(StandardCharsets.UTF_8)); + var output = new ByteArrayOutputStream(); + var error = new ByteArrayOutputStream(); + + var cmd = new ArrayList(); + cmd.add("git"); + + // provide a fallback for CI-like environments that may not have an identity set + if (userEmail != null && userName != null) { + cmd.add("-c"); + cmd.add("user.email=" + userEmail); + cmd.add("-c"); + cmd.add("user.name=" + userName); + } + + cmd.add("tag"); + cmd.add("--annotate"); + + // take message from STDIN + cmd.add("--file"); + cmd.add("-"); + + cmd.add(getTagName().get()); + + var result = getExecOperations().exec(spec -> { + spec.setWorkingDir(getRepoDirectory()); + spec.setCommandLine(cmd); + spec.setStandardInput(input); + spec.setStandardOutput(output); + spec.setErrorOutput(error); + spec.setIgnoreExitValue(true); + }); + + if (result.getExitValue() != 0) { + var errorStr = error.toString(StandardCharsets.UTF_8); + if (errorStr.contains(String.format("fatal: tag '%s' already exists", getTagName().get()))) { + setDidWork(false); + } else if (allowRetry && errorStr.contains(String.format("Committer identity unknown"))) { + var email = getRecentUserEmail(); + var name = getRecentUserName(); + System.err.println(String.format("Tagging as recent committer %s <%s>, as this machine has no git identity set.", name, email)); + tag(email, name, false); + } else { + System.err.println(errorStr); + result.assertNormalExitValue(); + } + } } - @Input - public Property getTagMessage() { - return tagMessage; + private String getRecentUserEmail() { + var output = new ByteArrayOutputStream(); + var result = getExecOperations().exec(spec -> { + spec.setWorkingDir(getRepoDirectory()); + spec.setCommandLine("git", "log", "-n", "1", "--pretty=format:%ae"); + spec.setStandardOutput(output); + }); + return output.toString(); + } + + private String getRecentUserName() { + var output = new ByteArrayOutputStream(); + var result = getExecOperations().exec(spec -> { + spec.setWorkingDir(getRepoDirectory()); + spec.setCommandLine("git", "log", "-n", "1", "--pretty=format:%an"); + spec.setStandardOutput(output); + }); + return output.toString(); } } diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java index a591372..01d2b8e 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java @@ -5,8 +5,9 @@ import javax.inject.Inject; -import org.ajoberstar.grgit.gradle.GrgitService; import org.ajoberstar.reckon.core.*; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; import org.gradle.api.model.ObjectFactory; @@ -17,8 +18,8 @@ public class ReckonExtension { private static Logger logger = Logging.getLogger(ReckonExtension.class); + private final DirectoryProperty repoDirectory; private final Reckoner.Builder reckonerBuilder; - private final Property grgitService; private final Property scope; private final Property stage; private final Property version; @@ -27,15 +28,14 @@ public class ReckonExtension { private VersionTagParser tagParser; private VersionTagWriter tagWriter; - private final Provider tagParserProvider; - private final Provider tagWriterProvider; + private final Provider tagName; private final Property tagMessage; @Inject public ReckonExtension(ObjectFactory objectFactory, ProviderFactory providerFactory) { + this.repoDirectory = objectFactory.directoryProperty(); this.reckonerBuilder = Reckoner.builder(); - this.grgitService = objectFactory.property(GrgitService.class); this.scope = objectFactory.property(String.class); this.stage = objectFactory.property(String.class); this.version = objectFactory.property(Version.class); @@ -49,12 +49,22 @@ public ReckonExtension(ObjectFactory objectFactory, ProviderFactory providerFact this.tagParser = null; this.tagWriter = null; - this.tagParserProvider = providerFactory.provider(() -> this.tagParser); - this.tagWriterProvider = providerFactory.provider(() -> this.tagWriter); + + this.tagName = version.map(v -> { + if (tagWriter == null || !v.isSignificant()) { + return null; + } else { + return tagWriter.write(v); + } + }); this.tagMessage = objectFactory.property(String.class); } + public DirectoryProperty getRepoDirectory() { + return repoDirectory; + } + public void setDefaultInferredScope(String scope) { this.reckonerBuilder.defaultInferredScope(Scope.from(scope)); } @@ -119,16 +129,12 @@ public Property getRemote() { return remote; } - public Provider getTagParser() { - return tagParserProvider; - } - public void setTagParser(VersionTagParser tagParser) { this.tagParser = tagParser; } - public Provider getTagWriter() { - return tagWriterProvider; + public Provider getTagName() { + return tagName; } public void setTagWriter(VersionTagWriter tagWriter) { @@ -143,10 +149,6 @@ public Provider getVersion() { return version; } - Property getGrgitService() { - return grgitService; - } - Property getScope() { return scope; } @@ -157,8 +159,14 @@ Property getStage() { private Version reckonVersion() { try { - var git = grgitService.get().getGrgit(); - var repo = git.getRepository().getJgit().getRepository(); + var builder = new FileRepositoryBuilder(); + builder.readEnvironment(); + builder.findGitDir(getRepoDirectory().getAsFile().get()); + if (builder.getGitDir() == null) { + throw new IllegalStateException("No .git directory found!"); + } + var repo = builder.build(); + reckonerBuilder.git(repo, tagParser); } catch (Exception e) { // no git repo found diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPlugin.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPlugin.java index becbc76..bbc0ea5 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPlugin.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPlugin.java @@ -2,8 +2,6 @@ import java.util.concurrent.atomic.AtomicBoolean; -import org.ajoberstar.grgit.gradle.GrgitServiceExtension; -import org.ajoberstar.grgit.gradle.GrgitServicePlugin; import org.ajoberstar.reckon.core.Version; import org.ajoberstar.reckon.core.VersionTagParser; import org.ajoberstar.reckon.core.VersionTagWriter; @@ -28,13 +26,10 @@ public void apply(Project project) { if (!project.equals(project.getRootProject())) { throw new IllegalStateException("org.ajoberstar.reckon can only be applied to the root project."); } - project.getPluginManager().apply(GrgitServicePlugin.class); - - var grgitServiceExtension = project.getExtensions().getByType(GrgitServiceExtension.class); - var grgitService = grgitServiceExtension.getService(); var extension = project.getExtensions().create("reckon", ReckonExtension.class); - extension.getGrgitService().set(grgitService); + extension.getRepoDirectory().set(project.getLayout().getProjectDirectory()); + extension.getRemote().convention("origin"); extension.setTagParser(VersionTagParser.getDefault()); extension.setTagWriter(VersionTagWriter.getDefault()); extension.getTagMessage().convention(extension.getVersion().map(Version::toString)); @@ -59,9 +54,8 @@ private TaskProvider createTagTask(Project project, ReckonE return project.getTasks().register(TAG_TASK, ReckonCreateTagTask.class, task -> { task.setDescription("Tag version inferred by reckon."); task.setGroup("publishing"); - task.getGrgitService().set(extension.getGrgitService()); - task.getVersion().set(extension.getVersion()); - task.getTagWriter().set(extension.getTagWriter()); + task.getRepoDirectory().set(extension.getRepoDirectory()); + task.getTagName().set(extension.getTagName()); task.getTagMessage().set(extension.getTagMessage()); }); } @@ -70,10 +64,9 @@ private TaskProvider createPushTask(Project project, ReckonEx return project.getTasks().register(PUSH_TASK, ReckonPushTagTask.class, task -> { task.setDescription("Push version tag created by reckon."); task.setGroup("publishing"); - task.getGrgitService().set(extension.getGrgitService()); + task.getRepoDirectory().set(extension.getRepoDirectory()); task.getRemote().set(extension.getRemote()); - task.getVersion().set(extension.getVersion()); - task.getTagWriter().set(extension.getTagWriter()); + task.getTagName().set(extension.getTagName()); }); } diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPushTagTask.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPushTagTask.java index 32548e7..01407c6 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPushTagTask.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonPushTagTask.java @@ -1,76 +1,45 @@ package org.ajoberstar.reckon.gradle; -import java.util.List; +import java.util.ArrayList; import javax.inject.Inject; -import org.ajoberstar.grgit.gradle.GrgitService; -import org.ajoberstar.reckon.core.Version; -import org.ajoberstar.reckon.core.VersionTagWriter; import org.gradle.api.DefaultTask; -import org.gradle.api.model.ObjectFactory; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.UntrackedTask; +import org.gradle.api.tasks.*; +import org.gradle.process.ExecOperations; @UntrackedTask(because = "Git tracks the state") -public class ReckonPushTagTask extends DefaultTask { - private Property grgitService; - private Property remote; - private Property version; - private Property tagWriter; - - @Inject - public ReckonPushTagTask(ObjectFactory objectFactory) { - this.grgitService = objectFactory.property(GrgitService.class); - this.remote = objectFactory.property(String.class); - this.version = objectFactory.property(Version.class); - this.tagWriter = objectFactory.property(VersionTagWriter.class); - } - +public abstract class ReckonPushTagTask extends DefaultTask { @TaskAction public void create() { - var git = grgitService.get().getGrgit(); - var tagName = tagWriter.get().write(version.get()); - - var tagExists = git.getTag().list().stream() - .anyMatch(tag -> tag.getName().equals(tagName)); - - if (tagExists) { - git.push(op -> { - if (remote.isPresent()) { - op.setRemote(remote.get()); - } - op.setRefsOrSpecs(List.of("refs/tags/" + tagName)); - }); - setDidWork(true); - } else { + if (!getTagName().isPresent()) { setDidWork(false); + return; } + getExecOperations().exec(spec -> { + spec.setWorkingDir(getRepoDirectory()); + var cmd = new ArrayList(); + cmd.add("git"); + cmd.add("push"); + cmd.add(getRemote().get()); + cmd.add("refs/tags/" + getTagName().get()); + spec.setCommandLine(cmd); + }); } - @Internal - public Property getGrgitService() { - return grgitService; - } + @Inject + protected abstract ExecOperations getExecOperations(); - @Input - @Optional - public Property getRemote() { - return remote; - } + @Internal + public abstract DirectoryProperty getRepoDirectory(); @Input - public Property getVersion() { - return version; - } + public abstract Property getRemote(); @Input - public Property getTagWriter() { - return tagWriter; - } + @Optional + public abstract Property getTagName(); } diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonSettingsPlugin.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonSettingsPlugin.java index 767ddcf..8bb4ef0 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonSettingsPlugin.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonSettingsPlugin.java @@ -1,6 +1,5 @@ package org.ajoberstar.reckon.gradle; -import org.ajoberstar.grgit.gradle.GrgitService; import org.ajoberstar.reckon.core.Version; import org.ajoberstar.reckon.core.VersionTagParser; import org.ajoberstar.reckon.core.VersionTagWriter; @@ -19,14 +18,9 @@ public class ReckonSettingsPlugin implements Plugin { @Override public void apply(Settings settings) { - Provider grgitService = settings.getGradle().getSharedServices().registerIfAbsent("reckon-grgit", GrgitService.class, spec -> { - spec.getParameters().getCurrentDirectory().set(settings.getSettingsDir()); - spec.getParameters().getInitIfNotExists().set(false); - spec.getMaxParallelUsages().set(1); - }); - var extension = settings.getExtensions().create("reckon", ReckonExtension.class); - extension.getGrgitService().set(grgitService); + extension.getRepoDirectory().set(settings.getSettingsDir()); + extension.getRemote().convention("origin"); extension.setTagParser(VersionTagParser.getDefault()); extension.setTagWriter(VersionTagWriter.getDefault()); extension.getTagMessage().convention(extension.getVersion().map(Version::toString)); @@ -53,9 +47,8 @@ private TaskProvider createTagTask(Project project, ReckonE return project.getTasks().register(TAG_TASK, ReckonCreateTagTask.class, task -> { task.setDescription("Tag version inferred by reckon."); task.setGroup("publishing"); - task.getGrgitService().set(extension.getGrgitService()); - task.getVersion().set(extension.getVersion()); - task.getTagWriter().set(extension.getTagWriter()); + task.getRepoDirectory().set(extension.getRepoDirectory()); + task.getTagName().set(extension.getTagName()); task.getTagMessage().set(extension.getTagMessage()); }); } @@ -64,10 +57,9 @@ private TaskProvider createPushTask(Project project, ReckonEx return project.getTasks().register(PUSH_TASK, ReckonPushTagTask.class, task -> { task.setDescription("Push version tag created by reckon."); task.setGroup("publishing"); - task.getGrgitService().set(extension.getGrgitService()); + task.getRepoDirectory().set(extension.getRepoDirectory()); task.getRemote().set(extension.getRemote()); - task.getVersion().set(extension.getVersion()); - task.getTagWriter().set(extension.getTagWriter()); + task.getTagName().set(extension.getTagName()); }); } From 5ed6f3b690dc1df82283c3b5191fbda18fe60aff Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 14:58:17 -0500 Subject: [PATCH 4/8] patch: Log status when repo is unclean Fixes #91 --- .../reckon/core/GitInventorySupplier.java | 25 ++++++++++--------- .../org/ajoberstar/reckon/core/Reckoner.java | 5 +--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java index 5ecf9f5..c2c6acb 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java @@ -2,13 +2,7 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -19,9 +13,6 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; @@ -32,7 +23,7 @@ /** * Supplies an inventory of a Git repository. - * + *

* This is intentionally package private. */ final class GitInventorySupplier implements VcsInventorySupplier { @@ -105,7 +96,17 @@ public VcsInventory getInventory() { private boolean isClean() { try { - return new Git(repo).status().call().isClean(); + var status = new Git(repo).status().call(); + if (!status.isClean()) { + logger.info("Git repository is not clean: added={}, changed={}, removed={}, untracked={}, modified={}, missing={}", + status.getAdded(), + status.getChanged(), + status.getRemoved(), + status.getUntracked(), + status.getModified(), + status.getMissing()); + } + return status.isClean(); } catch (GitAPIException e) { logger.error("Failed to determine status of repository. Assuming not clean.", e); // TODO should this throw up? diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java index a6ffcc3..47ce0a4 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java @@ -4,12 +4,9 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; -import java.util.Collections; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,7 +57,7 @@ public Version reckon() { var reckoned = reckonTargetVersion(inventory, targetNormal); if (reckoned.isSignificant() && !inventory.isClean()) { - throw new IllegalStateException("Cannot release a final or significant stage without a clean repo."); + throw new IllegalStateException("Cannot release a final or significant stage without a clean repo. Review INFO logs for more details."); } if (inventory.getClaimedVersions().contains(reckoned) && !inventory.getCurrentVersion().map(reckoned::equals).orElse(false)) { From a4490a8b846fbf0697719ab3a9e4d6335fde8834 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 15:12:17 -0500 Subject: [PATCH 5/8] minor: Support the Scope enum on the extension Fixes #175 --- .../ajoberstar/reckon/gradle/ReckonExtension.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java index 01d2b8e..663c413 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java @@ -66,11 +66,19 @@ public DirectoryProperty getRepoDirectory() { } public void setDefaultInferredScope(String scope) { - this.reckonerBuilder.defaultInferredScope(Scope.from(scope)); + setDefaultInferredScope(Scope.from(scope)); + } + + public void setDefaultInferredScope(Scope scope) { + this.reckonerBuilder.defaultInferredScope(scope); } public void setParallelBranchScope(String scope) { - this.reckonerBuilder.parallelBranchScope(Scope.from(scope)); + setParallelBranchScope(Scope.from(scope)); + } + + public void setParallelBranchScope(Scope scope) { + this.reckonerBuilder.parallelBranchScope(scope); } public void stages(String... stages) { From b2f51fb52e206e59a5cfdb5af8c4253dcbb7b35a Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 15:16:38 -0500 Subject: [PATCH 6/8] major: Remove default setting for defaultInferredScope Fixes #195 --- README.md | 4 ++-- .../main/java/org/ajoberstar/reckon/core/Reckoner.java | 2 +- .../org/ajoberstar/reckon/core/ReckonerIntegTest.java | 2 ++ .../java/org/ajoberstar/reckon/core/ReckonerTest.java | 4 ++++ reckon-gradle/build.gradle.kts | 1 + reckon-gradle/gradle.lockfile | 6 +++--- .../org/ajoberstar/reckon/gradle/BaseCompatTest.groovy | 10 ++++++++++ .../reckon/gradle/CompositeBuildCompatTest.groovy | 2 ++ .../ajoberstar/reckon/gradle/SettingsCompatTest.groovy | 9 +++++++++ .../org/ajoberstar/reckon/gradle/ReckonExtension.java | 2 +- 10 files changed, 35 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fa2b0fd..1af4899 100644 --- a/README.md +++ b/README.md @@ -141,10 +141,10 @@ reckon { // snapshotFromProp() // END LEGACY - // omit this to use the default of 'minor' + // required as of 0.18.0 (previously defaulted to 'minor') defaultInferredScope = 'patch' - // omit this to use the deafult of 'patch' + // omit this to use the default of 'patch' // if you use branches like maintenance/1.2.x, set this to 'minor' // if you use branches like maintenance/2.x, set this to 'major' parallelBranchScope = 'minor' diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java index 47ce0a4..f8d583a 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Reckoner.java @@ -157,7 +157,7 @@ public static final class Builder { private VcsInventorySupplier inventorySupplier; private ScopeCalculator scopeCalc; private StageCalculator stageCalc; - private Scope defaultInferredScope = Scope.MINOR; + private Scope defaultInferredScope; private Scope parallelBranchScope = Scope.PATCH; private Set stages; private String defaultStage; diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java index 6d8b09f..d39e147 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerIntegTest.java @@ -71,6 +71,7 @@ private String reckonStage(Scope scope, String stage) { .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) .stages("beta", "milestone", "rc", "final") + .defaultInferredScope(Scope.MINOR) .build() .reckon() .toString(); @@ -82,6 +83,7 @@ private String reckonSnapshot(Scope scope, String stage) { .git(git.getRepository()) .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) + .defaultInferredScope(Scope.MINOR) .snapshots() .build() .reckon() diff --git a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java index 9448ee2..caf441c 100644 --- a/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java +++ b/reckon-core/src/test/java/org/ajoberstar/reckon/core/ReckonerTest.java @@ -45,6 +45,7 @@ public void stagesLowercased(String stage) { .scopeCalc(i -> Optional.empty()) .stages("beTA", "miLEStone", "RC", "Final") .stageCalc(StageCalculator.ofUserString((i, v) -> Optional.of(stage))) + .defaultInferredScope(Scope.MINOR) .build() .reckon(); } @@ -361,6 +362,7 @@ public void doubleConflictingParallelIncrementsHigherScope() { .parallelBranchScope(Scope.MINOR) .scopeCalc(i -> Optional.ofNullable(Scope.PATCH)) .stageCalc((i, v) -> Optional.ofNullable("final")) + .defaultInferredScope(Scope.MINOR) .stages("beta", "milestone", "rc", "final") .build(); @@ -586,6 +588,7 @@ private String reckonStage(VcsInventory inventory, Scope scope, String stage) { .vcs(() -> inventory) .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) + .defaultInferredScope(Scope.MINOR) .stages("beta", "milestone", "rc", "final") .build() .reckon() @@ -598,6 +601,7 @@ private String reckonSnapshot(VcsInventory inventory, Scope scope, String stage) .vcs(() -> inventory) .scopeCalc(i -> Optional.ofNullable(scope)) .stageCalc((i, v) -> Optional.ofNullable(stage)) + .defaultInferredScope(Scope.MINOR) .snapshots() .build() .reckon() diff --git a/reckon-gradle/build.gradle.kts b/reckon-gradle/build.gradle.kts index 5e99d3e..ac64105 100644 --- a/reckon-gradle/build.gradle.kts +++ b/reckon-gradle/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { // testing compatTestImplementation(gradleTestKit()) + compatTestImplementation("org.eclipse.jgit:org.eclipse.jgit:[6.0,7.0[") compatTestImplementation("org.spockframework:spock-core:2.3-groovy-3.0") } diff --git a/reckon-gradle/gradle.lockfile b/reckon-gradle/gradle.lockfile index 6aebed4..f90df9e 100644 --- a/reckon-gradle/gradle.lockfile +++ b/reckon-gradle/gradle.lockfile @@ -2,17 +2,17 @@ # Manual edits can break the build and are not advised. # This file is expected to be part of source control. com.github.zafarkhaja:java-semver:0.9.0=runtimeClasspath,testRuntimeClasspath -com.googlecode.javaewah:JavaEWAH:1.1.13=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.googlecode.javaewah:JavaEWAH:1.1.13=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.12.0=runtimeClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=compatTestCompileClasspath org.codehaus.groovy:groovy:3.0.12=compatTestCompileClasspath,compatTestRuntimeClasspath -org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit.platform:junit-platform-commons:1.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit.platform:junit-platform-engine:1.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.junit:junit-bom:5.9.0=compatTestCompileClasspath,compatTestRuntimeClasspath org.opentest4j:opentest4j:1.2.0=compatTestCompileClasspath,compatTestRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=compileClasspath,testCompileClasspath +org.slf4j:slf4j-api:1.7.30=compatTestCompileClasspath,compatTestRuntimeClasspath,compileClasspath,testCompileClasspath org.slf4j:slf4j-api:2.0.7=runtimeClasspath,testRuntimeClasspath org.spockframework:spock-core:2.3-groovy-3.0=compatTestCompileClasspath,compatTestRuntimeClasspath empty=annotationProcessor,compatTestAnnotationProcessor,signatures,testAnnotationProcessor diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy index 29d69b2..8d9498f 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/BaseCompatTest.groovy @@ -41,6 +41,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') } @@ -91,6 +92,7 @@ plugins { println version reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') } @@ -142,6 +144,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() snapshotFromProp() } @@ -165,6 +168,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') } @@ -190,6 +194,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' stages('alpha','beta', 'final') scopeCalc = calcScopeFromProp().or(calcScopeFromCommitMessages()) stageCalc = calcStageFromProp() @@ -216,6 +221,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' stages('alpha','beta', 'final') scopeCalc = calcScopeFromProp().or(calcScopeFromCommitMessages()) stageCalc = calcStageFromProp() @@ -242,6 +248,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') remote = 'other-remote' @@ -270,6 +277,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') @@ -301,6 +309,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') tagMessage = version.map(v -> "Version " + v) @@ -329,6 +338,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha', 'beta', 'final') } diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy index 7e26742..a3362ef 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/CompositeBuildCompatTest.groovy @@ -29,6 +29,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('beta', 'final') } @@ -52,6 +53,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('beta', 'final') } diff --git a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy index f4e5218..1f21b7e 100644 --- a/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy +++ b/reckon-gradle/src/compatTest/groovy/org/ajoberstar/reckon/gradle/SettingsCompatTest.groovy @@ -44,6 +44,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') } @@ -118,6 +119,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() snapshotFromProp() } @@ -141,6 +143,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') } @@ -166,6 +169,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' stages('alpha','beta', 'final') scopeCalc = calcScopeFromProp().or(calcScopeFromCommitMessages()) stageCalc = calcStageFromProp() @@ -192,6 +196,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' stages('alpha','beta', 'final') scopeCalc = calcScopeFromProp().or(calcScopeFromCommitMessages()) stageCalc = calcStageFromProp() @@ -218,6 +223,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') remote = 'other-remote' @@ -246,6 +252,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') @@ -277,6 +284,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha','beta', 'final') tagMessage = version.map(v -> "Version " + v) @@ -305,6 +313,7 @@ plugins { } reckon { + defaultInferredScope = 'minor' scopeFromProp() stageFromProp('alpha', 'beta', 'final') } diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java index 663c413..1cd4bf7 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java @@ -185,7 +185,7 @@ private Version reckonVersion() { try { reckoner = reckonerBuilder.build(); } catch (Exception e) { - throw new ReckonConfigurationException("Failed to configure Reckon.", e); + throw new ReckonConfigurationException("Failed to configure Reckon: " + e.getMessage(), e); } var version = reckoner.reckon(); From d3d50702b3b5b57c75e0953619719f3ab2bda3b1 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 15:37:36 -0500 Subject: [PATCH 7/8] Update documentation --- README.md | 106 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1af4899..624f516 100644 --- a/README.md +++ b/README.md @@ -98,27 +98,55 @@ Reckon can alternately use SNAPSHOT versions instead of the stage concept. **NOTE:** Check the [Release Notes](https://github.com/ajoberstar/reckon/releases) for details on compatibility and changes. -### Gradle +**IMPORTANT:** It is recommended to apply reckon as a Settings plugin (in settings.gradle/settings.gradle.kts) to ensure it is configured before any other plugin tries to use the project version. -#### Apply the plugin +Reckon overrides the `project.version` property in Gradle to provide maximal compatibility with other plugins that need the version. -**IMPORTANT:** It is recommended to apply reckon as a Settings plugin (in settings.gradle/settings.gradle.kts) to ensure it is configured before any other plugin tries to use the project version. +### Recommended Configuration (Kotlin) -```groovy +**settings.gradle.kts** -// if applying in settings.gradle(.kts) +```kotlin plugins { - id 'org.ajoberstar.reckon.settings' version '' + id("org.ajoberstar.reckon.settings") version "" } -// if applying in build.gradle(.kts) +extensions.configure { + setDefaultInferredScope("patch") + stages("beta", "rc", "final") + setScopeCalc(calcScopeFromProp().or(calcScopeFromCommitMessages())) + setStageCalc(calcStageFromProp()) +} +``` + +### Recommended Configuration (Groovy) + +**settings.gradle** + +```groovy plugins { - id 'org.ajoberstar.reckon' version '' + id('org.ajoberstar.reckon.settings') version ''' +} + +reckon { + defaultInferredScope = 'patch' + stages 'beta', 'rc', 'final' + scopeCalc = calcScopeFromProp().or(calcScopeFromCommitMessages()) + stageCalc = calcStageFromProp() } +``` -// in either case +### Extension Properties (Groovy) + +```groovy reckon { - // START As of 0.16.0 + // required as of 0.18.0 (previously defaulted to 'minor') + defaultInferredScope = 'patch' + // omit this to use the default of 'patch' + // if you use branches like maintenance/1.2.x, set this to 'minor' + // if you use branches like maintenance/2.x, set this to 'major' + parallelBranchScope = 'minor' + // what stages are allowed stages('milestone', 'rc', 'final') // or use snapshots @@ -131,25 +159,7 @@ reckon { scopeCalc = { inventory -> Optional.of(Scope.MAJOR) } stageCalc = { inventory, targetNormal -> Optional.of('beta') } - // END As of 0.16.0 - - // START LEGACY - scopeFromProp() - stageFromProp('milestone', 'rc', 'final') - - // alternative to stageFromProp - // snapshotFromProp() - // END LEGACY - - // required as of 0.18.0 (previously defaulted to 'minor') - defaultInferredScope = 'patch' - - // omit this to use the default of 'patch' - // if you use branches like maintenance/1.2.x, set this to 'minor' - // if you use branches like maintenance/2.x, set this to 'major' - parallelBranchScope = 'minor' - - // omit to use default remote + // omit to use 'origin' remote = 'other-remote' // omit this to use the default of parsing tag names of the form 1.2.3 or v1.2.3 @@ -169,9 +179,35 @@ reckon { } ``` -**NOTE:** Reckon overrides the `project.version` property in Gradle +### Legacy Project Plugin + +This version of the plugin will be retired before 1.0.0 (if that ever comes), due to ordering issues that can occur with other plugins reading the version. + +**build.gradle.kts** + +```kotlin +plugins { + id("org.ajoberstar.reckon") version "" +} + +reckon { + // configure as above +} +``` + +**build.gradle** + +```groovy +plugins { + id 'org.ajoberstar.reckon' version '' +} + +reckon { + // configure as above +} +``` -#### Passing scope/stage as props +### Passing scope/stage as props - `reckon.scope` (allowed if `scopeCalc` includes `calcStageFromProp()` or if you called `scopeFromProp()`) Valid values: `major`, `minor`, `patch` (if not set the scope is inferred by other means) @@ -188,9 +224,9 @@ When Gradle executes, the version will be inferred as soon as something tries to Reckoned version 1.3.0-milestone.1 ``` -#### Reading scope from commit messages +### Reading scope from commit messages -If you want the scope to inferred in a more automated way, consider making use of a commit message convention. This sections describes the out-of-the-box convention supported by Reckon. Others are possible by customizing the `scopeCalc` further. +If you want the scope to infer in a more automated way, consider making use of a commit message convention. This sections describes the out-of-the-box convention supported by Reckon. Others are possible by customizing the `scopeCalc` further. If your `scopeCalc` includes `calcScopeFromCommitMessages()`, the commit messages between your "base normal" (previous final release) and the current `HEAD` are parsed for SemVer indicators. @@ -236,13 +272,13 @@ pqr1234 (HEAD -> main) major: Removed deprecated setNormal method In this case we'd be looking at all commits since the last tagged final version, `1.2.3`. We'd only care about messages that follow our convention of prefixing the message with a scope. Since there's a mix of commits using all 3 scopes, we pick the most severe of the ones we found `major`. -##### Special Case for pre-1.0.0 +#### Special Case for pre-1.0.0 Before 1.0.0, SemVer doesn't really guarantee anything, but a good practice seems to be a `PATCH` increment is for bug fixes, while a `MINOR` increase can be new features or breaking changes. In order to promote the convention of using `major: My message` for breaking changes, before 1.0.0 a `major` in a commit message will be read as `minor`. The goal is to promote you explicitly documenting breaking changes in your commit logs, while requiring the actual 1.0.0 version bump to come via an override with `-Preckon.scope=major`. -##### DISCLAIMER this is not Convention Commits compliant +#### DISCLAIMER this is not Convention Commits compliant While this approach is similar to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), it does not follow their spec, sticking to something more directly applicable to Reckon's scopes. User's can use the `calcScopeFromCommitMessages(Function>)` form if they want to implement Conventional Commits, or any other scheme themselves. From cd28d1e30c54ff53ca26a66ca2c71f3042f20999 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Sat, 22 Apr 2023 16:47:21 -0500 Subject: [PATCH 8/8] patch: Close Git repo when done parsing version --- .../reckon/gradle/ReckonExtension.java | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java index 1cd4bf7..beea867 100644 --- a/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java +++ b/reckon-gradle/src/main/java/org/ajoberstar/reckon/gradle/ReckonExtension.java @@ -6,6 +6,7 @@ import javax.inject.Inject; import org.ajoberstar.reckon.core.*; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.logging.Logger; @@ -166,6 +167,24 @@ Property getStage() { } private Version reckonVersion() { + try (var repo = openRepo()) { + reckonerBuilder.git(repo, tagParser); + + Reckoner reckoner; + try { + reckoner = reckonerBuilder.build(); + } catch (Exception e) { + throw new ReckonConfigurationException("Failed to configure Reckon: " + e.getMessage(), e); + } + + var version = reckoner.reckon(); + logger.warn("Reckoned version: {}", version); + return version; + } + } + + private Repository openRepo() { + Repository repo; try { var builder = new FileRepositoryBuilder(); builder.readEnvironment(); @@ -173,23 +192,10 @@ private Version reckonVersion() { if (builder.getGitDir() == null) { throw new IllegalStateException("No .git directory found!"); } - var repo = builder.build(); - - reckonerBuilder.git(repo, tagParser); + return builder.build(); } catch (Exception e) { // no git repo found - reckonerBuilder.git(null); + return null; } - - Reckoner reckoner; - try { - reckoner = reckonerBuilder.build(); - } catch (Exception e) { - throw new ReckonConfigurationException("Failed to configure Reckon: " + e.getMessage(), e); - } - - var version = reckoner.reckon(); - logger.warn("Reckoned version: {}", version); - return version; } }