From b6583111ec69cec96d2aa4867a79a5f96570198a Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 17:26:38 -0500 Subject: [PATCH 01/14] Build with 4.7 --- gradle/wrapper/gradle-wrapper.jar | Bin 54333 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c44b679acd3f794ddbb3aa5e919244914911014a..f6b961fd5a86aa5fbfe90f707c3138408be7c718 100644 GIT binary patch delta 757 zcmY+CUr3Wt7{+&AW7C-VS!CNcr4&o~L{lkNk~%LkW0;eMX_UJtQ_yA@6BN7{7(@fv zcr37JCEXarC;}s-BKOa2&dpgDb!E_vP@r@Z1<~oSzVg7~Jiq6C&dcHDyqe2-IhW%# z=T){xzBg+$8oSS8*KUn$jWCT@em4K>?d}|n&8o}YXqx`Eht~`FMoX|5pBJnCU4kKa zmfgiru#UZkKX{BbH1I|C;%iW$VkTqZw7eqDyDQ&yYg* z^0L-0tK8#zZM)uL{zF5q1h z($xq&6Fp9*qeA+lW}0Xd@@bo%H<6ILB$H$n)`O*d*sKQhE-56pXRStRwF^0BFM$+O z=unTqo*o&49O;6yq78mu+V3OW1t*?xP*#?Ovxd*oyBk@ zTG%DbCb}p%j8DUryqAQJE=kYlon3L4NN$emaBaDc=0~$wiuGlMtm9eK<4Dp>mI*cN zO`fDlw~9ZK9dvL~MdwN@IXo&p^q6pKMZ%7hOc$qA+dsvhm{O?Ir&@uOh5Dye^iOFK zm@ecg2@PqPF5lT!^3G^+BV9|UXSUhvX$vbu>8gW`!M!>}FJ`l4Egpr7P?3@8_H%d^ z%FvgorK<~E)`-!(&&cdLHZ%R~6uQ^0@Xz&Tt={ziFE#v~n>Yude^;dqE&hSu9GWc- delta 705 zcmYL{-%Ha`7{+(rjm?$j*GjXaTd)e{D~3xfrA01^mTOixDlKYW%oMDZpcjQmAYF7} zlSc~EjINT1VkqRYY;&8=`Ey%dMEwKRMHJLc5FJmp&dvEe&-1=7oWmO&)xH_k`u7`q zHjI_nH5$#C{#tj|=k4D+zI8B-p9jW&hx1k|b(&L7Lzb!?I-4(QwjexR_rC*MBModA ztr05=;X$ODeZh5o0X{`kGBbpu`EW6grwIk_D5rCJVVU9A8_+nbz+q4}ijnKbhETMb z9vVf73AM3`wNbH)>;qO}9(D)TnI^JY9IHGZm|eR$6X{y|)@K&nf1U)-PW+ObaS4bPBl>?F&|#=#u!Oi{qn6TsNdFTeQ*PZizkJMIuq~a*@;R zThjWuc#=wb1s=lNq=I`(oHZk`)JB*3WbW_R%JAv zT5x*BL(aiW*f+RVuYi)Ab`4A0o;*M|MckdvyQ`(p@ Date: Mon, 28 May 2018 17:27:26 -0500 Subject: [PATCH 02/14] Update dependencies --- global.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.lock b/global.lock index 4c642217..a9765263 100644 --- a/global.lock +++ b/global.lock @@ -24,7 +24,7 @@ "org.ajoberstar.reckon:reckon-gradle", "org.ajoberstar.reckon:reckon-gradle" ], - "locked": "24.1-jre" + "locked": "25.1-jre" }, "junit:junit": { "firstLevelTransitive": [ From 731c6d9dd60e2262c10fd232b6bad868aedf23ad Mon Sep 17 00:00:00 2001 From: Eduard Szente Date: Tue, 15 May 2018 17:52:11 +0200 Subject: [PATCH 03/14] test for rebuilding claimed version --- .../core/strategy/ScopeNormalStrategyTest.groovy | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy index 1b662514..9a812e27 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy @@ -21,6 +21,22 @@ import org.ajoberstar.reckon.core.VcsInventory import spock.lang.Specification class ScopeNormalStrategyTest extends Specification { + def 'rebuilding claimed version succeeds, if repo is clean'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('0.0.0'), + Version.valueOf('0.0.0'), + 1, + [] as Set, + [Version.valueOf('0.1.0'), Version.valueOf('0.1.1'), Version.valueOf('0.2.0')] as Set + ) + expect: + new ScopeNormalStrategy({ Optional.empty() }).reckonNormal(inventory).toString() == '0.1.0' + } + def 'if scope supplier returns null, throw'() { given: def inventory = new VcsInventory( From 72c649f42f1d7f1b5d54c3ef5cc1c4468f18a3e8 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 19:44:15 -0500 Subject: [PATCH 04/14] Use a custom Version class Continues using JSemver internally, but the API now leverages a custom Version class. Main benefit is that it speaks the domain of Reckon, e.g. stages. More refactoring should probably be done to take advantage of this. --- .../reckon/core/NormalStrategy.java | 2 - .../reckon/core/PreReleaseStrategy.java | 2 - .../org/ajoberstar/reckon/core/Reckoner.java | 4 +- .../org/ajoberstar/reckon/core/Scope.java | 16 +++ .../ajoberstar/reckon/core/VcsInventory.java | 5 +- .../org/ajoberstar/reckon/core/Version.java | 131 ++++++++++++++++++ .../org/ajoberstar/reckon/core/Versions.java | 65 --------- .../reckon/core/git/GitInventorySupplier.java | 11 +- .../core/strategy/ScopeNormalStrategy.java | 9 +- .../strategy/SnapshotPreReleaseStrategy.java | 4 +- .../strategy/StagePreReleaseStrategy.java | 28 +--- .../reckon/core/ReckonerTest.groovy | 1 - .../ajoberstar/reckon/core/VersionTest.groovy | 66 +++++++++ .../reckon/core/VersionsTest.groovy | 65 --------- .../core/git/GitInventorySupplierTest.groovy | 30 ++-- .../strategy/ScopeNormalStrategyTest.groovy | 2 +- .../SnapshotPreReleaseStrategyTest.groovy | 2 +- .../StagePreReleaseStrategyTest.groovy | 2 +- .../reckon/gradle/ReckonExtension.java | 2 +- 19 files changed, 253 insertions(+), 194 deletions(-) create mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/Versions.java create mode 100644 reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy delete mode 100644 reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionsTest.groovy diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java index 4f536e71..bfc8ff42 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java @@ -1,7 +1,5 @@ package org.ajoberstar.reckon.core; -import com.github.zafarkhaja.semver.Version; - @FunctionalInterface public interface NormalStrategy { Version reckonNormal(VcsInventory inventory); diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java index dd053ee7..91efe58e 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java @@ -1,7 +1,5 @@ package org.ajoberstar.reckon.core; -import com.github.zafarkhaja.semver.Version; - @FunctionalInterface public interface PreReleaseStrategy { Version reckonTargetVersion(VcsInventory inventory, Version targetNormal); 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 f66d6585..f25b97a3 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 @@ -1,7 +1,5 @@ package org.ajoberstar.reckon.core; -import com.github.zafarkhaja.semver.Version; - public final class Reckoner { private Reckoner() { // do not instantiate @@ -21,7 +19,7 @@ public static String reckon(VcsInventorySupplier inventorySupplier, NormalStrate } inventory.getCurrentVersion().ifPresent(current -> { - if (inventory.isClean() && Versions.isNormal(current) && !Versions.isNormal(reckoned)) { + if (inventory.isClean() && current.isFinal() && !reckoned.isFinal()) { throw new IllegalStateException("Cannot re-release a final version " + current + " as a pre-release: " + reckoned); } }); diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java index 6a744da1..08820926 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java @@ -1,6 +1,7 @@ package org.ajoberstar.reckon.core; import java.util.Arrays; +import java.util.Optional; import java.util.stream.Collectors; public enum Scope { @@ -18,4 +19,19 @@ public static Scope from(String value) { throw new IllegalArgumentException(message, e); } } + + public static Optional infer(Version before, Version after) { + int major = after.getVersion().getMajorVersion() - before.getVersion().getMajorVersion(); + int minor = after.getVersion().getMinorVersion() - before.getVersion().getMinorVersion(); + int patch = after.getVersion().getPatchVersion() - before.getVersion().getPatchVersion(); + if (major == 1 && after.getVersion().getMinorVersion() == 0 && after.getVersion().getPatchVersion() == 0) { + return Optional.of(Scope.MAJOR); + } else if (major == 0 && minor == 1 && after.getVersion().getPatchVersion() == 0) { + return Optional.of(Scope.MINOR); + } else if (major == 0 && minor == 0 && patch == 1) { + return Optional.of(Scope.PATCH); + } else { + return Optional.empty(); + } + } } diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java index e0cb2514..e0a7c423 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java @@ -4,7 +4,6 @@ import java.util.Optional; import java.util.Set; -import com.github.zafarkhaja.semver.Version; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -36,8 +35,8 @@ public VcsInventory( this.commitId = commitId; this.clean = clean; this.currentVersion = currentVersion; - this.baseVersion = Optional.ofNullable(baseVersion).orElse(Versions.VERSION_0); - this.baseNormal = Optional.ofNullable(baseNormal).orElse(Versions.VERSION_0); + this.baseVersion = Optional.ofNullable(baseVersion).orElse(Version.IDENTITY); + this.baseNormal = Optional.ofNullable(baseNormal).orElse(Version.IDENTITY); this.commitsSinceBase = commitsSinceBase; this.parallelNormals = Optional.ofNullable(parallelNormals) .map(Collections::unmodifiableSet) diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java new file mode 100644 index 00000000..5df7cb68 --- /dev/null +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java @@ -0,0 +1,131 @@ +package org.ajoberstar.reckon.core; + +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.github.zafarkhaja.semver.ParseException; + +public final class Version implements Comparable { + public static final Version IDENTITY = new Version(com.github.zafarkhaja.semver.Version.forIntegers(0, 0, 0)); + + private final com.github.zafarkhaja.semver.Version version; + private final Version normal; + private final Stage stage; + + private Version(com.github.zafarkhaja.semver.Version version) { + this.version = version; + if (version.getPreReleaseVersion().isEmpty()) { + this.normal = this; + } else { + this.normal = new Version(com.github.zafarkhaja.semver.Version.forIntegers(version.getMajorVersion(), version.getMinorVersion(), version.getPatchVersion())); + } + this.stage = Stage.valueOf(version); + } + + com.github.zafarkhaja.semver.Version getVersion() { + return version; + } + + public Version getNormal() { + return normal; + } + + public Optional getStage() { + return Optional.ofNullable(stage); + } + + public boolean isFinal() { + return version.getPreReleaseVersion().isEmpty(); + } + + public boolean isSignificant() { + return isFinal() || getStage() + .filter(stage -> !"SNAPSHOT".equals(stage.getName())) + .filter(stage -> version.getBuildMetadata().isEmpty()) + .isPresent(); + } + + public Version incrementNormal(Scope scope) { + switch (scope) { + case MAJOR: + return new Version(version.incrementMajorVersion()); + case MINOR: + return new Version(version.incrementMinorVersion()); + case PATCH: + return new Version(version.incrementPatchVersion()); + default: + throw new AssertionError("Invalid scope: " + scope); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj instanceof Version) { + return Objects.equals(this.version, ((Version) obj).version); + } else { + return false; + } + } + + @Override + public int hashCode() { + return version.hashCode(); + } + + @Override + public int compareTo(Version that) { + return this.version.compareTo(that.version); + } + + @Override + public String toString() { + return version.toString(); + } + + public static final class Stage { + private static final Pattern STAGE_REGEX = Pattern.compile("^(?\\w+)(?:\\.(?\\d+))?"); + + private final String name; + private final int num; + + private Stage(String name, int num) { + this.name = name; + this.num = num; + } + + public String getName() { + return name; + } + + public int getNum() { + return num; + } + + private static Stage valueOf(com.github.zafarkhaja.semver.Version version) { + Matcher matcher = STAGE_REGEX.matcher(version.getPreReleaseVersion()); + if (matcher.find()) { + String name = matcher.group("name"); + int num = Optional.ofNullable(matcher.group("num")).map(Integer::parseInt).orElse(0); + return new Stage(name, num); + } else { + return null; + } + } + } + + public static Version valueOf(String versionString) { + return new Version(com.github.zafarkhaja.semver.Version.valueOf(versionString)); + } + + public static Optional parse(String versionString) { + try { + return Optional.of(new Version(com.github.zafarkhaja.semver.Version.valueOf(versionString))); + } catch (IllegalArgumentException | ParseException e) { + return Optional.empty(); + } + } +} diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Versions.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Versions.java deleted file mode 100644 index 29630eac..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Versions.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.ajoberstar.reckon.core; - -import java.util.Optional; - -import com.github.zafarkhaja.semver.ParseException; -import com.github.zafarkhaja.semver.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class Versions { - private static final Logger logger = LoggerFactory.getLogger(Versions.class); - - public static final Version VERSION_0 = Version.forIntegers(0, 0, 0); - - private Versions() { - // do not instantiate - } - - public static Optional valueOf(String version) { - try { - return Optional.of(Version.valueOf(version)); - } catch (IllegalArgumentException | ParseException e) { - logger.debug("Cannot parse {} as version.", version, e); - return Optional.empty(); - } - } - - public static boolean isNormal(Version version) { - return version.getPreReleaseVersion().isEmpty(); - } - - public static Version getNormal(Version version) { - return Version.forIntegers( - version.getMajorVersion(), version.getMinorVersion(), version.getPatchVersion()); - } - - public static Version incrementNormal(Version version, Scope scope) { - switch (scope) { - case MAJOR: - return version.incrementMajorVersion(); - case MINOR: - return version.incrementMinorVersion(); - case PATCH: - return version.incrementPatchVersion(); - default: - throw new AssertionError("Invalid scope: " + scope); - } - } - - public static Optional inferScope(Version before, Version after) { - int major = after.getMajorVersion() - before.getMajorVersion(); - int minor = after.getMinorVersion() - before.getMinorVersion(); - int patch = after.getPatchVersion() - before.getPatchVersion(); - if (major == 1 && after.getMinorVersion() == 0 && after.getPatchVersion() == 0) { - return Optional.of(Scope.MAJOR); - } else if (major == 0 && minor == 1 && after.getPatchVersion() == 0) { - return Optional.of(Scope.MINOR); - } else if (major == 0 && minor == 0 && patch == 1) { - return Optional.of(Scope.PATCH); - } else { - logger.debug("Invalid increment between the following versions. Cannot infer scope between: {} -> {}", before, after); - return Optional.empty(); - } - } -} diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java index b7838d72..65a81530 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java @@ -12,10 +12,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.github.zafarkhaja.semver.Version; import org.ajoberstar.reckon.core.VcsInventory; import org.ajoberstar.reckon.core.VcsInventorySupplier; -import org.ajoberstar.reckon.core.Versions; +import org.ajoberstar.reckon.core.Version; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -47,7 +46,7 @@ public GitInventorySupplier(Repository repo, Function> this.repo = repo; this.tagParser = ref -> { String tagName = Repository.shortenRefName(ref.getName()); - return tagSelector.apply(tagName).flatMap(Versions::valueOf); + return tagSelector.apply(tagName).flatMap(Version::parse); }; } @@ -158,7 +157,7 @@ private TaggedVersion findBase(RevWalk walk, RevCommit head, Stream findParallelCandidates(RevWalk walk, RevCommit head, Set candidates) { @@ -198,7 +197,7 @@ private Optional findParallel(RevWalk walk, RevCommit head, TaggedVersi && !taggedSinceMergeBase && !mergeBase.equals(head) && !mergeBase.equals(candidate.getCommit())) { - return Optional.of(Versions.getNormal(candidate.getVersion())); + return Optional.of(candidate.getVersion().getNormal()); } else { return Optional.empty(); } @@ -225,7 +224,7 @@ public RevCommit getCommit() { } public boolean isNormal() { - return Versions.isNormal(version); + return version.isFinal(); } @Override diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java index c6f7b577..ed377a75 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java @@ -3,11 +3,10 @@ import java.util.Optional; import java.util.function.Function; -import com.github.zafarkhaja.semver.Version; import org.ajoberstar.reckon.core.NormalStrategy; import org.ajoberstar.reckon.core.Scope; import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.Versions; +import org.ajoberstar.reckon.core.Version; public class ScopeNormalStrategy implements NormalStrategy { private final Function> scopeCalc; @@ -24,15 +23,15 @@ public Version reckonNormal(VcsInventory inventory) { if (providedScope.isPresent()) { scope = providedScope.get(); } else { - Optional inferredScope = Versions.inferScope(inventory.getBaseNormal(), inventory.getBaseVersion()); + Optional inferredScope = Scope.infer(inventory.getBaseNormal(), inventory.getBaseVersion()); scope = inferredScope.orElse(Scope.MINOR); } - Version targetNormal = Versions.incrementNormal(inventory.getBaseNormal(), scope); + Version targetNormal = inventory.getBaseNormal().incrementNormal(scope); // if a version's already being developed on a parallel branch we'll skip it if (inventory.getParallelNormals().contains(targetNormal)) { - targetNormal = Versions.incrementNormal(targetNormal, scope); + targetNormal = targetNormal.incrementNormal(scope); } if (inventory.getClaimedVersions().contains(targetNormal)) { diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java index 3fa1a2d2..315c64d5 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java @@ -3,9 +3,9 @@ import java.util.Optional; import java.util.function.BiFunction; -import com.github.zafarkhaja.semver.Version; import org.ajoberstar.reckon.core.PreReleaseStrategy; import org.ajoberstar.reckon.core.VcsInventory; +import org.ajoberstar.reckon.core.Version; public final class SnapshotPreReleaseStrategy implements PreReleaseStrategy { public static final String FINAL_STAGE = "final"; @@ -27,7 +27,7 @@ public Version reckonTargetVersion(VcsInventory inventory, Version targetNormal) } else { String stage = maybeStage.orElse(SNAPSHOT_STAGE); if (stage.equals(SNAPSHOT_STAGE)) { - return targetNormal.setPreReleaseVersion("SNAPSHOT"); + return Version.valueOf(targetNormal + "-SNAPSHOT"); } else if (!stage.equals(FINAL_STAGE)) { throw new IllegalArgumentException(String.format("Stage \"%s\" is not one of: [snapshot, final]", stage)); } else if (!inventory.isClean()) { diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java index ec63c097..852ac9ae 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java @@ -3,13 +3,11 @@ import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.github.zafarkhaja.semver.Version; import org.ajoberstar.reckon.core.PreReleaseStrategy; import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.Versions; +import org.ajoberstar.reckon.core.Version; public final class StagePreReleaseStrategy implements PreReleaseStrategy { public static final String FINAL_STAGE = "final"; @@ -55,32 +53,20 @@ public Version reckonTargetVersion(VcsInventory inventory, Version targetNormal) return current; } - Version targetBase = targetNormal.equals(Versions.getNormal(inventory.getBaseVersion())) ? inventory.getBaseVersion() : targetNormal; - - String baseStageName; - int baseStageNum; - Matcher matcher = STAGE_REGEX.matcher(targetBase.getPreReleaseVersion()); - if (matcher.find()) { - baseStageName = matcher.group("name"); - baseStageNum = Optional.ofNullable(matcher.group("num")).map(Integer::parseInt).orElse(0); - } else { - baseStageName = defaultStage; - baseStageNum = 0; - } + Version targetBase = targetNormal.equals(inventory.getBaseVersion().getNormal()) ? inventory.getBaseVersion() : targetNormal; + String baseStageName = targetBase.getStage().map(Version.Stage::getName).orElse(defaultStage); + int baseStageNum = targetBase.getStage().map(Version.Stage::getNum).orElse(0); if (stage == null) { String buildMetadata = inventory.getCommitId() .map(sha -> inventory.isClean() ? sha : sha + ".uncommitted") .orElse("uncommitted"); - return targetBase - .setPreReleaseVersion(baseStageName + "." + baseStageNum + "." + inventory.getCommitsSinceBase()) - .setBuildMetadata(buildMetadata); + return Version.valueOf(String.format("%s-%s.%d.%d+%s", targetBase.getNormal(), baseStageName, baseStageNum, inventory.getCommitsSinceBase(), buildMetadata)); } else if (stage.equals(baseStageName)) { - int num = baseStageNum > 0 ? baseStageNum + 1 : 1; - return targetBase.setPreReleaseVersion(stage + "." + num); + return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), baseStageName, baseStageNum + 1)); } else { - return targetBase.setPreReleaseVersion(stage + ".1"); + return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), stage, 1)); } } } diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index be10960e..3bb86c15 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -15,7 +15,6 @@ */ package org.ajoberstar.reckon.core -import com.github.zafarkhaja.semver.Version import spock.lang.Specification import org.ajoberstar.reckon.core.strategy.ScopeNormalStrategy; import org.ajoberstar.reckon.core.strategy.StagePreReleaseStrategy; diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy new file mode 100644 index 00000000..bce50ff3 --- /dev/null +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy @@ -0,0 +1,66 @@ +/* + * Copyright 2015-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ajoberstar.reckon.core + +import spock.lang.Specification +import spock.lang.Unroll + +class VersionTest extends Specification { + @Unroll + def 'parse returns empty if argument is not valid semantic version'(String version) { + expect: + Version.parse(version) == Optional.empty() + where: + version << [null, 'not a version', '1.0-345'] + } + + def 'parse returns version if argument is valid version'() { + expect: + Version.parse('1.0.0-rc.1').map { it.toString() } == Optional.of('1.0.0-rc.1') + } + + def 'isFinal returns true only if the version does not have a pre-release component'() { + expect: + !Version.valueOf('1.0.0-rc.1').isFinal() + Version.valueOf('1.0.0').isFinal() + } + + def 'getNormal returns the normal component of the version only'() { + expect: + Version.valueOf('1.2.3').getNormal() == Version.valueOf('1.2.3') + Version.valueOf('1.2.3-rc.1').getNormal() == Version.valueOf('1.2.3') + Version.valueOf('1.2.3+other.stuff').getNormal() == Version.valueOf('1.2.3') + } + + def 'incrementNormal correctly uses the scope'() { + given: + def base = Version.valueOf('1.2.3-rc.1') + expect: + base.incrementNormal(Scope.MAJOR) == Version.valueOf('2.0.0') + base.incrementNormal(Scope.MINOR) == Version.valueOf('1.3.0') + base.incrementNormal(Scope.PATCH) == Version.valueOf('1.2.4') + } + + def 'inferScope correctly finds the scope'() { + expect: + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('1.2.4-milestone.1')) == Optional.of(Scope.PATCH) + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('1.3.0-milestone.1')) == Optional.of(Scope.MINOR) + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('2.0.0-milestone.1')) == Optional.of(Scope.MAJOR) + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('0.4.0')) == Optional.empty() + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('2.1.0')) == Optional.empty() + Scope.infer(Version.valueOf('1.2.3'), Version.valueOf('1.2.5')) == Optional.empty() + } +} diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionsTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionsTest.groovy deleted file mode 100644 index 56f6defd..00000000 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionsTest.groovy +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ajoberstar.reckon.core - -import com.github.zafarkhaja.semver.Version -import spock.lang.Specification -import spock.lang.Unroll - -class VersionsTest extends Specification { - @Unroll - def 'valueOf returns empty if argument is not valid semantic version'(String version) { - expect: - Versions.valueOf(version) == Optional.empty() - where: - version << [null, 'not a version', '1.0-345'] - } - - def 'valueOf returns version if argument is valid version'() { - expect: - Versions.valueOf('1.0.0-rc.1').map { it.toString() } == Optional.of('1.0.0-rc.1') - } - - def 'isNormal returns true only if the version does not have a pre-release component'() { - expect: - !Versions.isNormal(Version.valueOf('1.0.0-rc.1')) - Versions.isNormal(Version.valueOf('1.0.0')) - } - - def 'getNormal returns the normal component of the version only'() { - expect: - Versions.getNormal(Version.valueOf('1.2.3')) == Version.valueOf('1.2.3') - Versions.getNormal(Version.valueOf('1.2.3-rc.1')) == Version.valueOf('1.2.3') - Versions.getNormal(Version.valueOf('1.2.3+other.stuff')) == Version.valueOf('1.2.3') - } - - def 'incrementNormal correctly uses the scope'() { - expect: - Versions.incrementNormal(Version.valueOf('1.2.3-rc.1'), Scope.MAJOR) == Version.valueOf('2.0.0') - Versions.incrementNormal(Version.valueOf('1.2.3-rc.1'), Scope.MINOR) == Version.valueOf('1.3.0') - Versions.incrementNormal(Version.valueOf('1.2.3-rc.1'), Scope.PATCH) == Version.valueOf('1.2.4') - } - - def 'inferScope correctly finds the scope'() { - expect: - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('1.2.4-milestone.1')) == Optional.of(Scope.PATCH) - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('1.3.0-milestone.1')) == Optional.of(Scope.MINOR) - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('2.0.0-milestone.1')) == Optional.of(Scope.MAJOR) - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('0.4.0')) == Optional.empty() - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('2.1.0')) == Optional.empty() - Versions.inferScope(Version.valueOf('1.2.3'), Version.valueOf('1.2.5')) == Optional.empty() - } -} diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy index e5f0a657..2ab49137 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy @@ -19,7 +19,7 @@ import java.nio.file.Files import java.security.SecureRandom import org.ajoberstar.grgit.Grgit import org.ajoberstar.reckon.core.VcsInventory -import org.ajoberstar.reckon.core.Versions +import org.ajoberstar.reckon.core.Version import spock.lang.Shared import spock.lang.Specification @@ -43,28 +43,28 @@ class GitInventorySupplierTest extends Specification { given: checkout('head-single-tag') expect: - supplier.getInventory().currentVersion == Versions.valueOf('0.1.0-milestone.1') + supplier.getInventory().currentVersion == Version.parse('0.1.0-milestone.1') } def 'if multiple tagged version on HEAD, the max is current version'() { given: checkout('head-multi-tag') expect: - supplier.getInventory().currentVersion == Versions.valueOf('0.1.0') + supplier.getInventory().currentVersion == Version.parse('0.1.0') } def 'if no tagged finals in HEAD\'s history, base normal is 0.0.0'() { given: checkout('final-unreachable') expect: - supplier.getInventory().baseNormal == Versions.valueOf('0.0.0').get() + supplier.getInventory().baseNormal == Version.parse('0.0.0').get() } def 'if tagged finals in HEAD\'s history, base normal is max of finals which have no other final between them and HEAD'() { given: checkout('final-reachable') expect: - supplier.getInventory().baseNormal == Versions.valueOf('1.0.0').get() + supplier.getInventory().baseNormal == Version.parse('1.0.0').get() } def 'if tagged finals on head, base normal and version are same as current version'() { @@ -81,14 +81,14 @@ class GitInventorySupplierTest extends Specification { given: checkout('version-unreachable') expect: - supplier.getInventory().baseVersion == Versions.valueOf('0.0.0').get() + supplier.getInventory().baseVersion == Version.parse('0.0.0').get() } def 'if tagged versions in HEAD\'s history, base version is max of versions which have no other version between them and HEAD'() { given: checkout('version-reachable') expect: - supplier.getInventory().baseVersion == Versions.valueOf('0.3.0-milestone.1').get() + supplier.getInventory().baseVersion == Version.parse('0.3.0-milestone.1').get() } def 'if tagged versions on head, base version is same as current version'() { @@ -139,19 +139,19 @@ class GitInventorySupplierTest extends Specification { given: checkout('parallel-untagged-since-merge') expect: - supplier.getInventory().parallelNormals == [Versions.valueOf('0.2.0').get()] as Set + supplier.getInventory().parallelNormals == [Version.parse('0.2.0').get()] as Set } def 'all tagged versions treated as claimed versions'() { expect: supplier.getInventory().claimedVersions == [ - Versions.valueOf('0.1.0-milestone.1').get(), - Versions.valueOf('0.1.0-rc.1').get(), - Versions.valueOf('0.1.0').get(), - Versions.valueOf('0.2.0-rc.1').get(), - Versions.valueOf('0.3.0-milestone.1').get(), - Versions.valueOf('0.3.0').get(), - Versions.valueOf('1.0.0').get() + Version.parse('0.1.0-milestone.1').get(), + Version.parse('0.1.0-rc.1').get(), + Version.parse('0.1.0').get(), + Version.parse('0.2.0-rc.1').get(), + Version.parse('0.3.0-milestone.1').get(), + Version.parse('0.3.0').get(), + Version.parse('1.0.0').get() ] as Set } diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy index 9a812e27..f705cb55 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy @@ -15,9 +15,9 @@ */ package org.ajoberstar.reckon.core.strategy -import com.github.zafarkhaja.semver.Version import org.ajoberstar.reckon.core.Scope import org.ajoberstar.reckon.core.VcsInventory +import org.ajoberstar.reckon.core.Version import spock.lang.Specification class ScopeNormalStrategyTest extends Specification { diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy index 42f04114..31a82c35 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy @@ -15,8 +15,8 @@ */ package org.ajoberstar.reckon.core.strategy -import com.github.zafarkhaja.semver.Version import org.ajoberstar.reckon.core.VcsInventory +import org.ajoberstar.reckon.core.Version import spock.lang.Specification class SnapshotPreReleaseStrategyTest extends Specification { diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy index ebfa3dc4..58571422 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy @@ -15,8 +15,8 @@ */ package org.ajoberstar.reckon.core.strategy -import com.github.zafarkhaja.semver.Version import org.ajoberstar.reckon.core.VcsInventory +import org.ajoberstar.reckon.core.Version import spock.lang.Specification import spock.lang.Unroll 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 faf70d3f..15ba7d80 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 @@ -7,13 +7,13 @@ import java.util.function.Function; import java.util.stream.Collectors; -import com.github.zafarkhaja.semver.Version; import org.ajoberstar.grgit.Grgit; import org.ajoberstar.reckon.core.NormalStrategy; import org.ajoberstar.reckon.core.PreReleaseStrategy; import org.ajoberstar.reckon.core.Reckoner; import org.ajoberstar.reckon.core.VcsInventory; import org.ajoberstar.reckon.core.VcsInventorySupplier; +import org.ajoberstar.reckon.core.Version; import org.ajoberstar.reckon.core.git.GitInventorySupplier; import org.ajoberstar.reckon.core.strategy.ScopeNormalStrategy; import org.ajoberstar.reckon.core.strategy.SnapshotPreReleaseStrategy; From 7c9661c511030df815872297cfaff53da3ef8c22 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 19:48:57 -0500 Subject: [PATCH 05/14] Remove copyright headers --- .../ajoberstar/reckon/core/ReckonerTest.groovy | 15 --------------- .../org/ajoberstar/reckon/core/VersionTest.groovy | 15 --------------- .../core/git/GitInventorySupplierTest.groovy | 15 --------------- .../core/strategy/ScopeNormalStrategyTest.groovy | 15 --------------- .../SnapshotPreReleaseStrategyTest.groovy | 15 --------------- .../strategy/StagePreReleaseStrategyTest.groovy | 15 --------------- 6 files changed, 90 deletions(-) diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index 3bb86c15..21ac4da5 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core import spock.lang.Specification diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy index bce50ff3..1900d48d 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/VersionTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core import spock.lang.Specification diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy index 2ab49137..2c62f19f 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core.git import java.nio.file.Files diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy index f705cb55..a6a35ca9 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core.strategy import org.ajoberstar.reckon.core.Scope diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy index 31a82c35..8eaeb61d 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core.strategy import org.ajoberstar.reckon.core.VcsInventory diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy index 58571422..00423818 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2015-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.ajoberstar.reckon.core.strategy import org.ajoberstar.reckon.core.VcsInventory From 5d48046d07760c28d15066c49e3e0fae6ecf4461 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 20:31:30 -0500 Subject: [PATCH 06/14] Consolidate strategy tests into reckoner Prepare for refactoring to remove the strategy classes and build them into the Reckoner. This puts all of the strategy tests at the Reckoner level, so it ensures that they'll apply when the logic from the strategies themselves is moved in there. --- .../reckon/core/ReckonerTest.groovy | 400 +++++++++++++++++- .../strategy/ScopeNormalStrategyTest.groovy | 144 ------- .../SnapshotPreReleaseStrategyTest.groovy | 80 ---- .../StagePreReleaseStrategyTest.groovy | 115 ----- 4 files changed, 378 insertions(+), 361 deletions(-) delete mode 100644 reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy delete mode 100644 reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy delete mode 100644 reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index 21ac4da5..0ed1b042 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -1,6 +1,7 @@ package org.ajoberstar.reckon.core import spock.lang.Specification +import spock.lang.Unroll import org.ajoberstar.reckon.core.strategy.ScopeNormalStrategy; import org.ajoberstar.reckon.core.strategy.StagePreReleaseStrategy; import org.ajoberstar.reckon.core.strategy.SnapshotPreReleaseStrategy; @@ -58,7 +59,7 @@ class ReckonerTest extends Specification { def 'if target version is normal, current version is ignored'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3-milestone.1'), @@ -69,12 +70,12 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3-milestone.1')] as Set ) expect: - reckonStage(inventory2, 'major', 'final') == '2.0.0' + reckonStage(inventory, 'major', 'final') == '2.0.0' } def 'if current version is present and pre-release, repo is clean, and no input provided, this is a rebuild'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3-milestone.1'), @@ -85,12 +86,12 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3-milestone.1')] as Set ) expect: - reckonStage(inventory2, null, null) == '1.2.3-milestone.1' + reckonStage(inventory, null, null) == '1.2.3-milestone.1' } def 'if current version is present and normal, repo is clean, and no input provided, this is a rebuild'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3'), @@ -101,13 +102,13 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3-milestone.1')] as Set ) expect: - reckonStage(inventory2, null, null) == '1.2.3' - reckonSnapshot(inventory2, null, null) == '1.2.3' + reckonStage(inventory, null, null) == '1.2.3' + reckonSnapshot(inventory, null, null) == '1.2.3' } def 'if current version is present and pre-release, repo is dirty, and no input provided, this is not a rebuild'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', false, Version.valueOf('1.2.3-milestone.1'), @@ -118,12 +119,12 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3-milestone.1')] as Set ) expect: - reckonStage(inventory2, null, null) == '1.2.3-milestone.1.1+abcdef.uncommitted' + reckonStage(inventory, null, null) == '1.2.3-milestone.1.1+abcdef.uncommitted' } def 'if current version is present and normal, repo is dirty, and no input provided, this is not a rebuild'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', false, Version.valueOf('1.2.3'), @@ -134,13 +135,13 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3')] as Set ) expect: - reckonStage(inventory2, null, null) == '1.3.0-beta.0.1+abcdef.uncommitted' - reckonSnapshot(inventory2, null, null) == '1.3.0-SNAPSHOT' + reckonStage(inventory, null, null) == '1.3.0-beta.0.1+abcdef.uncommitted' + reckonSnapshot(inventory, null, null) == '1.3.0-SNAPSHOT' } def 'if current version is present and normal, repo is clean, allowed to release an incremented final'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3'), @@ -151,13 +152,13 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3')] as Set ) expect: - reckonStage(inventory2, 'minor', 'final') == '1.3.0' - reckonSnapshot(inventory2, 'major', 'final') == '2.0.0' + reckonStage(inventory, 'minor', 'final') == '1.3.0' + reckonSnapshot(inventory, 'major', 'final') == '2.0.0' } def 'if current version is present and normal, repo is clean, not allowed to release an incremented pre-release stage'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3'), @@ -168,14 +169,14 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3')] as Set ) when: - reckonStage(inventory2, null, 'rc') + reckonStage(inventory, null, 'rc') then: thrown(IllegalStateException) } def 'if current version is present and normal, repo is clean, not allowed to release an incremented snapshot'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3'), @@ -186,14 +187,14 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3')] as Set ) when: - reckonSnapshot(inventory2, null, 'snapshot') + reckonSnapshot(inventory, null, 'snapshot') then: thrown(IllegalStateException) } def 'if current version is present and pre-release, repo is clean, allowed to release a higher normal pre-release'() { given: - VcsInventory inventory2 = new VcsInventory( + VcsInventory inventory = new VcsInventory( 'abcdef', true, Version.valueOf('1.2.3-milestone.1'), @@ -204,12 +205,367 @@ class ReckonerTest extends Specification { [Version.valueOf('1.2.2'), Version.valueOf('1.2.3-milestone.1')] as Set ) expect: - reckonStage(inventory2, 'minor', 'rc') == '1.3.0-rc.1' + reckonStage(inventory, 'minor', 'rc') == '1.3.0-rc.1' + } + + def 'rebuilding claimed version succeeds, if repo is clean'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('0.0.0'), + Version.valueOf('0.0.0'), + 1, + [] as Set, + [Version.valueOf('0.1.0'), Version.valueOf('0.1.1'), Version.valueOf('0.2.0')] as Set + ) + expect: + reckonStage(inventory, null, null) == '0.1.0' + } + + def 'if scope supplier returns invalid scope, throw'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + when: + reckonStage(inventory, 'general', 'beta') + then: + def e = thrown(IllegalArgumentException) + e.getMessage() == 'Scope "general" is not one of: major, minor, patch' + } + + def 'if supplier returns empty, scope defaults to minor if base version is base normal'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.2'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, 'final') == '1.3.0' + } + + def 'if supplier returns empty, scope defaults to scope used by base version'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, 'final') == '1.2.3' + } + + def 'if no conflict with parallel or claimed, incremented version is returned'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, 'major', 'final') == '2.0.0' + } + + def 'if incremented version is in the parallel normals, increment again'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [Version.valueOf('2.0.0')] as Set, + [] as Set + ) + expect: + reckonStage(inventory, 'major', 'final') == '3.0.0' + } + + def 'if target normal is in the claimed versions, throw'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [Version.valueOf('2.0.0')] as Set + ) + when: + reckonStage(inventory, 'major', 'final') + then: + thrown(IllegalStateException) + } + + def 'if stage supplier returns an invalid stage, throw'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + when: + reckonStage(inventory, 'major', 'not') + then: + thrown(IllegalArgumentException) + } + + def 'final stage will return the target normal'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, 'major', 'final') == '2.0.0' + } + + def 'if target does not contain stage and stage is null, use the default stage and add num commits and commit id'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, 'major', null) == '2.0.0-beta.0.5+abcdef' + } + + def 'if target does not contain stage and stage is an empty string, use the default stage and add num commits and commit id'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, 'major', '') == '2.0.0-beta.0.5+abcdef' + } + + def 'if target does not contain stage and stage is present, add num commits and commit id'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, null) == '1.2.3-milestone.2.5+abcdef' + } + + def 'if target contains stage and stage matches, increment'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, 'milestone') == '1.2.3-milestone.3' + } + + def 'if target contains stage and stage differs, start from 1'() { + given: + VcsInventory inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, 'rc') == '1.2.3-rc.1' + reckonStage(inventory, 'major', 'rc') == '2.0.0-rc.1' + } + + def 'if repo has no commits, show build metadata as uncommitted'() { + given: + def inventory = new VcsInventory( + null, + false, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, null) == '1.2.3-milestone.2.5+uncommitted' + + } + + def 'if repo has uncommitted changes, show build metadata as uncommitted'() { + given: + def inventory = new VcsInventory( + 'abcdef', + false, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + expect: + reckonStage(inventory, null, null) == '1.2.3-milestone.2.5+abcdef.uncommitted' + } + + @Unroll + def 'if repo has uncommitted changes, fail when calculating a #stage stage'(String stage) { + given: + def inventory = new VcsInventory( + 'abcdef', + false, + null, + Version.valueOf('1.2.3-milestone.2'), + Version.valueOf('1.2.2'), + 5, + [] as Set, + [] as Set + ) + when: + reckonStage(inventory, null, stage) + then: + thrown(IllegalStateException) + where: + stage << ['rc', 'final'] + } + + def 'if stage supplier returns an invalid stage, throw'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + when: + reckonSnapshot(inventory, 'major', 'not') + then: + thrown(IllegalArgumentException) + } + + def 'if stage is final, return the target normal'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + expect: + reckonSnapshot(inventory, 'major', 'final') == '2.0.0' + } + + def 'if stage is snapshot or null, set pre-release to snapshot'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + expect: + reckonSnapshot(inventory, 'major', 'snapshot') == '2.0.0-SNAPSHOT' + reckonSnapshot(inventory, 'major', null) == '2.0.0-SNAPSHOT' + } + + def 'if repo has uncommitted changes, fail if stage is final'() { + given: + def inventory = new VcsInventory( + 'abcdef', + false, + null, + Version.valueOf('1.2.3-milestone.1'), + Version.valueOf('1.2.2'), + 1, + [] as Set, + [] as Set + ) + when: + reckonSnapshot(inventory, 'major', 'final') + then: + thrown(IllegalStateException) } private String reckonStage(inventory, scope, stage) { ScopeNormalStrategy normal = new ScopeNormalStrategy({ i -> Optional.ofNullable(scope) }) - StagePreReleaseStrategy preRelease = new StagePreReleaseStrategy(['beta', 'rc', 'final'] as Set, { i, v -> Optional.ofNullable(stage) }) + StagePreReleaseStrategy preRelease = new StagePreReleaseStrategy(['beta', 'milestone', 'rc', 'final'] as Set, { i, v -> Optional.ofNullable(stage) }) return Reckoner.reckon({ -> inventory }, normal, preRelease) } diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy deleted file mode 100644 index a6a35ca9..00000000 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategyTest.groovy +++ /dev/null @@ -1,144 +0,0 @@ -package org.ajoberstar.reckon.core.strategy - -import org.ajoberstar.reckon.core.Scope -import org.ajoberstar.reckon.core.VcsInventory -import org.ajoberstar.reckon.core.Version -import spock.lang.Specification - -class ScopeNormalStrategyTest extends Specification { - def 'rebuilding claimed version succeeds, if repo is clean'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('0.0.0'), - Version.valueOf('0.0.0'), - 1, - [] as Set, - [Version.valueOf('0.1.0'), Version.valueOf('0.1.1'), Version.valueOf('0.2.0')] as Set - ) - expect: - new ScopeNormalStrategy({ Optional.empty() }).reckonNormal(inventory).toString() == '0.1.0' - } - - def 'if scope supplier returns null, throw'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - when: - new ScopeNormalStrategy({ null }).reckonNormal(inventory) - then: - thrown(NullPointerException) - } - - def 'if scope supplier returns invalid scope, throw'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - when: - new ScopeNormalStrategy({ Optional.of("general") }).reckonNormal(inventory) - then: - def e = thrown(IllegalArgumentException) - e.getMessage() == 'Scope "general" is not one of: major, minor, patch' - } - - def 'if supplier returns empty, scope defaults to minor if base version is base normal'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.2'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - expect: - new ScopeNormalStrategy({ Optional.empty() }).reckonNormal(inventory).toString() == '1.3.0' - } - - def 'if supplier returns empty, scope defaults to scope used by base version'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - expect: - new ScopeNormalStrategy({ Optional.empty() }).reckonNormal(inventory).toString() == '1.2.3' - } - - def 'if no conflict with parallel or claimed, incremented version is returned'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - expect: - new ScopeNormalStrategy({ Optional.of('major') }).reckonNormal(inventory).toString() == '2.0.0' - } - - def 'if incremented version is in the parallel normals, increment again'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [Version.valueOf('2.0.0')] as Set, - [] as Set - ) - expect: - new ScopeNormalStrategy({ Optional.of('major') }).reckonNormal(inventory).toString() == '3.0.0' - } - - def 'if target normal is in the claimed versions, throw'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [Version.valueOf('2.0.0')] as Set - ) - when: - new ScopeNormalStrategy({ Optional.of('major') }).reckonNormal(inventory) - then: - thrown(IllegalStateException) - } - -} diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy deleted file mode 100644 index 8eaeb61d..00000000 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategyTest.groovy +++ /dev/null @@ -1,80 +0,0 @@ -package org.ajoberstar.reckon.core.strategy - -import org.ajoberstar.reckon.core.VcsInventory -import org.ajoberstar.reckon.core.Version -import spock.lang.Specification - -class SnapshotPreReleaseStrategyTest extends Specification { - def 'if stage supplier returns an invalid stage, throw'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - when: - strategy('not').reckonTargetVersion(inventory, Version.valueOf('2.0.0')) - then: - thrown(IllegalArgumentException) - } - - def 'if stage is final, return the target normal'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - expect: - strategy('final').reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0' - } - - def 'if stage is snapshot or null, set pre-release to snapshot'() { - given: - def inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - expect: - strategy('snapshot').reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0-SNAPSHOT' - strategy(null).reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0-SNAPSHOT' - } - - def 'if repo has uncommitted changes, fail if stage is final'() { - given: - def inventory = new VcsInventory( - 'abcdef', - false, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [] as Set, - [] as Set - ) - when: - strategy('final').reckonTargetVersion(inventory, Version.valueOf('2.0.0')) - then: - thrown(IllegalStateException) - } - - private SnapshotPreReleaseStrategy strategy(stage) { - return new SnapshotPreReleaseStrategy({ i, v -> Optional.ofNullable(stage) }) - } -} diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy deleted file mode 100644 index 00423818..00000000 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategyTest.groovy +++ /dev/null @@ -1,115 +0,0 @@ -package org.ajoberstar.reckon.core.strategy - -import org.ajoberstar.reckon.core.VcsInventory -import org.ajoberstar.reckon.core.Version -import spock.lang.Specification -import spock.lang.Unroll - -class StagePreReleaseStrategyTest extends Specification { - VcsInventory inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.2'), - Version.valueOf('1.2.2'), - 5, - [] as Set, - [] as Set - ) - - def 'if stage supplier returns an invalid stage, throw'() { - when: - strategy('not').reckonTargetVersion(inventory, Version.valueOf('2.0.0')) - then: - thrown(IllegalArgumentException) - } - - def 'final stage will return the target normal'() { - expect: - strategy('final').reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0' - } - - def 'if target does not contain stage and stage is null, use the default stage and add num commits and commit id'() { - expect: - strategy(null).reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0-initial.0.5+abcdef' - } - - def 'if target does not contain stage and stage is an empty string, use the default stage and add num commits and commit id'() { - expect: - strategy('').reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0-initial.0.5+abcdef' - } - - def 'if target does not contain stage and stage is present, add num commits and commit id'() { - expect: - strategy(null).reckonTargetVersion(inventory, Version.valueOf('1.2.3')).toString() == '1.2.3-milestone.2.5+abcdef' - } - - def 'if target contains stage and stage matches, increment'() { - expect: - strategy('milestone').reckonTargetVersion(inventory, Version.valueOf('1.2.3')).toString() == '1.2.3-milestone.3' - } - - def 'if target contains stage and stage differs, start from 1'() { - expect: - strategy('rc').reckonTargetVersion(inventory, Version.valueOf('1.2.3')).toString() == '1.2.3-rc.1' - strategy('rc').reckonTargetVersion(inventory, Version.valueOf('2.0.0')).toString() == '2.0.0-rc.1' - } - - def 'if repo has no commits, show build metadata as uncommitted'() { - given: - def inventoryUncommitted = new VcsInventory( - null, - false, - null, - Version.valueOf('1.2.3-milestone.2'), - Version.valueOf('1.2.2'), - 5, - [] as Set, - [] as Set - ) - expect: - strategy(null).reckonTargetVersion(inventoryUncommitted, Version.valueOf('1.2.3')).toString() == '1.2.3-milestone.2.5+uncommitted' - - } - - def 'if repo has uncommitted changes, show build metadata as uncommitted'() { - given: - def inventoryUncommitted = new VcsInventory( - 'abcdef', - false, - null, - Version.valueOf('1.2.3-milestone.2'), - Version.valueOf('1.2.2'), - 5, - [] as Set, - [] as Set - ) - expect: - strategy(null).reckonTargetVersion(inventoryUncommitted, Version.valueOf('1.2.3')).toString() == '1.2.3-milestone.2.5+abcdef.uncommitted' - } - - @Unroll - def 'if repo has uncommitted changes, fail when calculating a #stage stage'(String stage) { - given: - def inventoryUncommitted = new VcsInventory( - 'abcdef', - false, - null, - Version.valueOf('1.2.3-milestone.2'), - Version.valueOf('1.2.2'), - 5, - [] as Set, - [] as Set - ) - when: - strategy(stage).reckonTargetVersion(inventoryUncommitted, Version.valueOf('1.2.3')) - then: - thrown(IllegalStateException) - where: - stage << ['rc', 'final'] - } - - private StagePreReleaseStrategy strategy(String stage) { - return new StagePreReleaseStrategy(['initial', 'milestone', 'rc', 'final'] as Set, { i, v -> Optional.ofNullable(stage) }) - } -} From 2e0303ad8349f7c3a1b84ccf5c4ce820275012a6 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 20:53:00 -0500 Subject: [PATCH 07/14] Strategies refactored into Reckoner Consolidated into one class to handle all of the inference. --- .../reckon/core/NormalStrategy.java | 6 - .../reckon/core/PreReleaseStrategy.java | 6 - .../org/ajoberstar/reckon/core/Reckoner.java | 149 +++++++++++++++++- .../core/strategy/ScopeNormalStrategy.java | 43 ----- .../strategy/SnapshotPreReleaseStrategy.java | 40 ----- .../strategy/StagePreReleaseStrategy.java | 72 --------- .../reckon/core/ReckonerTest.groovy | 57 ++----- 7 files changed, 158 insertions(+), 215 deletions(-) delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java delete mode 100644 reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java deleted file mode 100644 index bfc8ff42..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/NormalStrategy.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.ajoberstar.reckon.core; - -@FunctionalInterface -public interface NormalStrategy { - Version reckonNormal(VcsInventory inventory); -} diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java deleted file mode 100644 index 91efe58e..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/PreReleaseStrategy.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.ajoberstar.reckon.core; - -@FunctionalInterface -public interface PreReleaseStrategy { - Version reckonTargetVersion(VcsInventory inventory, Version targetNormal); -} 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 f25b97a3..ee914b7a 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 @@ -1,14 +1,35 @@ package org.ajoberstar.reckon.core; +import java.util.Arrays; +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; + public final class Reckoner { - private Reckoner() { - // do not instantiate + public static final String FINAL_STAGE = "final"; + public static final String SNAPSHOT_STAGE = "snapshot"; + + private final VcsInventorySupplier inventorySupplier; + private final Function> scopeCalc; + private final BiFunction> stageCalc; + private final Set stages; + private final String defaultStage; + + private Reckoner(VcsInventorySupplier inventorySupplier, Function> scopeCalc, BiFunction> stageCalc, Set stages, String defaultStage) { + this.inventorySupplier = inventorySupplier; + this.scopeCalc = scopeCalc; + this.stageCalc = stageCalc; + this.stages = stages; + this.defaultStage = defaultStage; } - public static String reckon(VcsInventorySupplier inventorySupplier, NormalStrategy normalStrategy, PreReleaseStrategy preReleaseStrategy) { + public Version reckon() { VcsInventory inventory = inventorySupplier.getInventory(); - Version targetNormal = normalStrategy.reckonNormal(inventory); - Version reckoned = preReleaseStrategy.reckonTargetVersion(inventory, targetNormal); + Version targetNormal = reckonNormal(inventory); + Version reckoned = reckonTargetVersion(inventory, targetNormal); if (inventory.getClaimedVersions().contains(reckoned) && !inventory.getCurrentVersion().map(reckoned::equals).orElse(false)) { throw new IllegalStateException("Reckoned version " + reckoned + " has already been released."); @@ -24,6 +45,122 @@ public static String reckon(VcsInventorySupplier inventorySupplier, NormalStrate } }); - return reckoned.toString(); + return reckoned; + } + + private Version reckonNormal(VcsInventory inventory) { + Optional providedScope = scopeCalc.apply(inventory).filter(value -> !value.isEmpty()).map(Scope::from); + + Scope scope; + if (providedScope.isPresent()) { + scope = providedScope.get(); + } else { + Optional inferredScope = Scope.infer(inventory.getBaseNormal(), inventory.getBaseVersion()); + scope = inferredScope.orElse(Scope.MINOR); + } + + Version targetNormal = inventory.getBaseNormal().incrementNormal(scope); + + // if a version's already being developed on a parallel branch we'll skip it + if (inventory.getParallelNormals().contains(targetNormal)) { + targetNormal = targetNormal.incrementNormal(scope); + } + + if (inventory.getClaimedVersions().contains(targetNormal)) { + throw new IllegalStateException("Reckoned target normal version " + targetNormal + " has already been released."); + } + + return targetNormal; + } + + private Version reckonTargetVersion(VcsInventory inventory, Version targetNormal) { + String stage = stageCalc.apply(inventory, targetNormal) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .orElse(null); + + if (stage != null && !stages.contains(stage)) { + String message = String.format("Stage \"%s\" is not one of: %s", stage, stages); + throw new IllegalArgumentException(message); + } + + if (stage != null && !inventory.isClean()) { + throw new IllegalStateException("Cannot release a final or significant stage without a clean repo."); + } + + if (FINAL_STAGE.equals(stage)) { + return targetNormal; + } + + // rebuild behavior + if (inventory.isClean() && inventory.getCurrentVersion().isPresent() && stage == null) { + Version current = inventory.getCurrentVersion().get(); + return current; + } + + Version targetBase = targetNormal.equals(inventory.getBaseVersion().getNormal()) ? inventory.getBaseVersion() : targetNormal; + String baseStageName = targetBase.getStage().map(Version.Stage::getName).orElse(defaultStage); + int baseStageNum = targetBase.getStage().map(Version.Stage::getNum).orElse(0); + + if (SNAPSHOT_STAGE.equals(defaultStage)) { + return Version.valueOf(String.format("%s-%s", targetBase.getNormal(), "SNAPSHOT")); + } else if (stage == null) { + String buildMetadata = inventory.getCommitId() + .map(sha -> inventory.isClean() ? sha : sha + ".uncommitted") + .orElse("uncommitted"); + + return Version.valueOf(String.format("%s-%s.%d.%d+%s", targetBase.getNormal(), baseStageName, baseStageNum, inventory.getCommitsSinceBase(), buildMetadata)); + } else if (stage.equals(baseStageName)) { + return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), baseStageName, baseStageNum + 1)); + } else { + return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), stage, 1)); + } + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private VcsInventorySupplier inventorySupplier; + private Function> scopeCalc; + private BiFunction> stageCalc; + private Set stages; + private String defaultStage; + + public Builder vcs(VcsInventorySupplier inventorySupplier) { + this.inventorySupplier = inventorySupplier; + return this; + } + + public Builder scopeCalc(Function> scopeCalc) { + this.scopeCalc = scopeCalc; + return this; + } + + public Builder stages(String... stages) { + this.stages = Arrays.stream(stages).collect(Collectors.toSet()); + this.defaultStage = this.stages.stream() + .filter(name -> !FINAL_STAGE.equals(name)) + .sorted() + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No non-final stages provided.")); + return this; + } + + public Builder snapshots() { + this.stages = Stream.of(Reckoner.FINAL_STAGE, Reckoner.SNAPSHOT_STAGE).collect(Collectors.toSet()); + this.defaultStage = Reckoner.SNAPSHOT_STAGE; + return this; + } + + public Builder stageCalc(BiFunction> stageCalc) { + this.stageCalc = stageCalc; + return this; + } + + public Reckoner build() { + return new Reckoner(inventorySupplier, scopeCalc, stageCalc, stages, defaultStage); + } } } diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java deleted file mode 100644 index ed377a75..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/ScopeNormalStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.ajoberstar.reckon.core.strategy; - -import java.util.Optional; -import java.util.function.Function; - -import org.ajoberstar.reckon.core.NormalStrategy; -import org.ajoberstar.reckon.core.Scope; -import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.Version; - -public class ScopeNormalStrategy implements NormalStrategy { - private final Function> scopeCalc; - - public ScopeNormalStrategy(Function> scopeCalc) { - this.scopeCalc = scopeCalc; - } - - @Override - public Version reckonNormal(VcsInventory inventory) { - Optional providedScope = scopeCalc.apply(inventory).filter(value -> !value.isEmpty()).map(Scope::from); - - Scope scope; - if (providedScope.isPresent()) { - scope = providedScope.get(); - } else { - Optional inferredScope = Scope.infer(inventory.getBaseNormal(), inventory.getBaseVersion()); - scope = inferredScope.orElse(Scope.MINOR); - } - - Version targetNormal = inventory.getBaseNormal().incrementNormal(scope); - - // if a version's already being developed on a parallel branch we'll skip it - if (inventory.getParallelNormals().contains(targetNormal)) { - targetNormal = targetNormal.incrementNormal(scope); - } - - if (inventory.getClaimedVersions().contains(targetNormal)) { - throw new IllegalStateException("Reckoned target normal version " + targetNormal + " has already been released."); - } - - return targetNormal; - } -} diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java deleted file mode 100644 index 315c64d5..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/SnapshotPreReleaseStrategy.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.ajoberstar.reckon.core.strategy; - -import java.util.Optional; -import java.util.function.BiFunction; - -import org.ajoberstar.reckon.core.PreReleaseStrategy; -import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.Version; - -public final class SnapshotPreReleaseStrategy implements PreReleaseStrategy { - public static final String FINAL_STAGE = "final"; - public static final String SNAPSHOT_STAGE = "snapshot"; - - private final BiFunction> stageCalc; - - public SnapshotPreReleaseStrategy(BiFunction> stageCalc) { - this.stageCalc = stageCalc; - } - - @Override - public Version reckonTargetVersion(VcsInventory inventory, Version targetNormal) { - Optional maybeStage = stageCalc.apply(inventory, targetNormal); - - if (inventory.isClean() && inventory.getCurrentVersion().isPresent() && !maybeStage.isPresent()) { - // rebuild - return inventory.getCurrentVersion().get(); - } else { - String stage = maybeStage.orElse(SNAPSHOT_STAGE); - if (stage.equals(SNAPSHOT_STAGE)) { - return Version.valueOf(targetNormal + "-SNAPSHOT"); - } else if (!stage.equals(FINAL_STAGE)) { - throw new IllegalArgumentException(String.format("Stage \"%s\" is not one of: [snapshot, final]", stage)); - } else if (!inventory.isClean()) { - throw new IllegalStateException("Cannot release a final version without a clean repo."); - } else { - return targetNormal; - } - } - } -} diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java deleted file mode 100644 index 852ac9ae..00000000 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/strategy/StagePreReleaseStrategy.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.ajoberstar.reckon.core.strategy; - -import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.regex.Pattern; - -import org.ajoberstar.reckon.core.PreReleaseStrategy; -import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.Version; - -public final class StagePreReleaseStrategy implements PreReleaseStrategy { - public static final String FINAL_STAGE = "final"; - private static final Pattern STAGE_REGEX = Pattern.compile("^(?\\w+)(?:\\.(?\\d+))?"); - - private final Set stages; - private final String defaultStage; - private final BiFunction> stageCalc; - - public StagePreReleaseStrategy(Set stages, BiFunction> stageCalc) { - this.stages = stages; - this.defaultStage = stages.stream() - .filter(name -> !FINAL_STAGE.equals(name)) - .sorted() - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No non-final stages provided.")); - this.stageCalc = stageCalc; - } - - @Override - public Version reckonTargetVersion(VcsInventory inventory, Version targetNormal) { - String stage = stageCalc.apply(inventory, targetNormal) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .orElse(null); - - if (stage != null && !stages.contains(stage)) { - String message = String.format("Stage \"%s\" is not one of: %s", stage, stages); - throw new IllegalArgumentException(message); - } - - if (stage != null && !inventory.isClean()) { - throw new IllegalStateException("Cannot release a final or significant stage without a clean repo."); - } - - if (FINAL_STAGE.equals(stage)) { - return targetNormal; - } - - // rebuild behavior - if (inventory.isClean() && inventory.getCurrentVersion().isPresent() && stage == null) { - Version current = inventory.getCurrentVersion().get(); - return current; - } - - Version targetBase = targetNormal.equals(inventory.getBaseVersion().getNormal()) ? inventory.getBaseVersion() : targetNormal; - String baseStageName = targetBase.getStage().map(Version.Stage::getName).orElse(defaultStage); - int baseStageNum = targetBase.getStage().map(Version.Stage::getNum).orElse(0); - - if (stage == null) { - String buildMetadata = inventory.getCommitId() - .map(sha -> inventory.isClean() ? sha : sha + ".uncommitted") - .orElse("uncommitted"); - - return Version.valueOf(String.format("%s-%s.%d.%d+%s", targetBase.getNormal(), baseStageName, baseStageNum, inventory.getCommitsSinceBase(), buildMetadata)); - } else if (stage.equals(baseStageName)) { - return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), baseStageName, baseStageNum + 1)); - } else { - return Version.valueOf(String.format("%s-%s.%d", targetBase.getNormal(), stage, 1)); - } - } -} diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index 0ed1b042..2c572bf6 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -2,9 +2,6 @@ package org.ajoberstar.reckon.core import spock.lang.Specification import spock.lang.Unroll -import org.ajoberstar.reckon.core.strategy.ScopeNormalStrategy; -import org.ajoberstar.reckon.core.strategy.StagePreReleaseStrategy; -import org.ajoberstar.reckon.core.strategy.SnapshotPreReleaseStrategy; class ReckonerTest extends Specification { def 'if version is claimed, throw'() { @@ -19,40 +16,8 @@ class ReckonerTest extends Specification { [Version.valueOf('1.3.0')] as Set, [Version.valueOf('2.0.0-rc.1')] as Set ) - - VcsInventorySupplier inventorySupplier = Mock() - inventorySupplier.getInventory() >> inventory - NormalStrategy normal = Mock() - normal.reckonNormal(inventory) >> Version.valueOf('2.0.0') - PreReleaseStrategy preRelease = Mock() - preRelease.reckonTargetVersion(inventory, Version.valueOf('2.0.0')) >> Version.valueOf('2.0.0-rc.1') - when: - Reckoner.reckon(inventorySupplier, normal, preRelease) - then: - thrown(IllegalStateException) - } - - def 'if version is not greater than base, throw'() { - given: - VcsInventory inventory = new VcsInventory( - 'abcdef', - true, - null, - Version.valueOf('1.2.3-milestone.1'), - Version.valueOf('1.2.2'), - 1, - [Version.valueOf('1.3.0')] as Set, - [Version.valueOf('2.0.0-rc.1')] as Set - ) - - VcsInventorySupplier inventorySupplier = Mock() - inventorySupplier.getInventory() >> inventory - NormalStrategy normal = Mock() - normal.reckonNormal(inventory) >> Version.valueOf('1.0.0') - PreReleaseStrategy preRelease = Mock() - preRelease.reckonTargetVersion(inventory, Version.valueOf('1.0.0')) >> Version.valueOf('1.0.0-rc.1') when: - Reckoner.reckon(inventorySupplier, normal, preRelease) + reckonStage(inventory, 'major', 'rc') then: thrown(IllegalStateException) } @@ -564,14 +529,22 @@ class ReckonerTest extends Specification { } private String reckonStage(inventory, scope, stage) { - ScopeNormalStrategy normal = new ScopeNormalStrategy({ i -> Optional.ofNullable(scope) }) - StagePreReleaseStrategy preRelease = new StagePreReleaseStrategy(['beta', 'milestone', 'rc', 'final'] as Set, { i, v -> Optional.ofNullable(stage) }) - return Reckoner.reckon({ -> inventory }, normal, preRelease) + return Reckoner.builder() + .vcs { -> inventory } + .scopeCalc { i -> Optional.ofNullable(scope) } + .stages('beta', 'milestone', 'rc', 'final') + .stageCalc { i, v -> Optional.ofNullable(stage) } + .build() + .reckon(); } private String reckonSnapshot(inventory, scope, stage) { - ScopeNormalStrategy normal = new ScopeNormalStrategy({ i -> Optional.ofNullable(scope) }) - SnapshotPreReleaseStrategy preRelease = new SnapshotPreReleaseStrategy({ i, v -> Optional.ofNullable(stage) }) - return Reckoner.reckon({ -> inventory }, normal, preRelease) + return Reckoner.builder() + .vcs { -> inventory } + .scopeCalc { i -> Optional.ofNullable(scope) } + .snapshots() + .stageCalc { i, v -> Optional.ofNullable(stage) } + .build() + .reckon(); } } From 4008e3c5f2c1537545b41abefc9db14f4265233d Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Mon, 28 May 2018 21:38:04 -0500 Subject: [PATCH 08/14] Refactor plugin to use new reckoner This includes a partial fix for #78. --- .../org/ajoberstar/reckon/core/Reckoner.java | 15 +++- .../reckon/core/ReckonerTest.groovy | 2 +- .../reckon/gradle/BaseCompatTest.groovy | 14 ++-- .../reckon/gradle/ReckonExtension.java | 70 ++++++++----------- .../reckon/gradle/ReckonPlugin.java | 2 +- 5 files changed, 54 insertions(+), 49 deletions(-) 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 ee914b7a..1779f581 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 @@ -1,6 +1,7 @@ package org.ajoberstar.reckon.core; import java.util.Arrays; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; @@ -8,6 +9,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.ajoberstar.reckon.core.git.GitInventorySupplier; +import org.eclipse.jgit.lib.Repository; + public final class Reckoner { public static final String FINAL_STAGE = "final"; public static final String SNAPSHOT_STAGE = "snapshot"; @@ -66,7 +70,7 @@ private Version reckonNormal(VcsInventory inventory) { targetNormal = targetNormal.incrementNormal(scope); } - if (inventory.getClaimedVersions().contains(targetNormal)) { + if (inventory.getClaimedVersions().contains(targetNormal) && !inventory.getCurrentVersion().filter(targetNormal::equals).isPresent()) { throw new IllegalStateException("Reckoned target normal version " + targetNormal + " has already been released."); } @@ -133,6 +137,11 @@ public Builder vcs(VcsInventorySupplier inventorySupplier) { return this; } + public Builder git(Repository repo) { + this.inventorySupplier = new GitInventorySupplier(repo); + return this; + } + public Builder scopeCalc(Function> scopeCalc) { this.scopeCalc = scopeCalc; return this; @@ -160,6 +169,10 @@ public Builder stageCalc(BiFunction> sta } public Reckoner build() { + Objects.requireNonNull(inventorySupplier, "Must provide a vcs."); + Objects.requireNonNull(scopeCalc, "Must provide a scope supplier."); + Objects.requireNonNull(stages, "Must provide set of stages."); + Objects.requireNonNull(stageCalc, "Must provide a stage supplier."); return new Reckoner(inventorySupplier, scopeCalc, stageCalc, stages, defaultStage); } } diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index 2c572bf6..a9b220c2 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -178,7 +178,7 @@ class ReckonerTest extends Specification { def inventory = new VcsInventory( 'abcdef', true, - null, + Version.valueOf('0.1.0'), Version.valueOf('0.0.0'), Version.valueOf('0.0.0'), 1, 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 3eb5c099..a52e49ed 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 @@ -72,7 +72,7 @@ task printVersion { when: def result = buildAndFail('printVersion') then: - result.output.contains('Must provide strategies for normal and preRelease on the reckon extension.') + result.output.contains('Must provide a scope supplier.') } def 'if reckoned version has build metadata no tag created'() { @@ -86,8 +86,8 @@ plugins { } reckon { - normal = scopeFromProp() - preRelease = stageFromProp('alpha','beta', 'final') + scopeFromProp() + stageFromProp('alpha','beta', 'final') } """ local.add(patterns: ['build.gradle']) @@ -111,8 +111,8 @@ plugins { } reckon { - normal = scopeFromProp() - preRelease = stageFromProp('alpha','beta', 'final') + scopeFromProp() + stageFromProp('alpha','beta', 'final') } """ local.add(patterns: ['build.gradle']) @@ -139,8 +139,8 @@ plugins { } reckon { - normal = scopeFromProp() - preRelease = stageFromProp('alpha','beta', 'final') + scopeFromProp() + stageFromProp('alpha', 'beta', 'final') } """ local.add(patterns: ['build.gradle']) 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 15ba7d80..f74c07f8 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 @@ -1,23 +1,10 @@ package org.ajoberstar.reckon.gradle; -import java.util.Arrays; import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; import org.ajoberstar.grgit.Grgit; -import org.ajoberstar.reckon.core.NormalStrategy; -import org.ajoberstar.reckon.core.PreReleaseStrategy; import org.ajoberstar.reckon.core.Reckoner; -import org.ajoberstar.reckon.core.VcsInventory; import org.ajoberstar.reckon.core.VcsInventorySupplier; -import org.ajoberstar.reckon.core.Version; -import org.ajoberstar.reckon.core.git.GitInventorySupplier; -import org.ajoberstar.reckon.core.strategy.ScopeNormalStrategy; -import org.ajoberstar.reckon.core.strategy.SnapshotPreReleaseStrategy; -import org.ajoberstar.reckon.core.strategy.StagePreReleaseStrategy; import org.gradle.api.Project; public class ReckonExtension { @@ -27,44 +14,51 @@ public class ReckonExtension { private Project project; private Grgit grgit; - private VcsInventorySupplier vcsInventory; - private NormalStrategy normal; - private PreReleaseStrategy preRelease; + private Reckoner.Builder reckoner; public ReckonExtension(Project project) { this.project = project; + this.reckoner = Reckoner.builder(); } - public void setVcsInventory(VcsInventorySupplier vcsInventory) { - this.vcsInventory = vcsInventory; + @Deprecated + public void setVcsInventory(VcsInventorySupplier inventorySupplier) { + project.getLogger().warn("reckon.vcsInventory = is deprecated and will be removed in 1.0.0. Call reckon.git instead."); + this.reckoner.vcs(inventorySupplier); } - public void setNormal(NormalStrategy normal) { - this.normal = normal; + @Deprecated + public void setNormal(ReckonExtension ext) { + project.getLogger().warn("reckon.normal = scopeFromProp() is deprecated and will be removed in 1.0.0. Call reckon.scopeFromProp() instead."); + // no op } - public void setPreRelease(PreReleaseStrategy preRelease) { - this.preRelease = preRelease; + @Deprecated + public void setPreRelease(ReckonExtension ext) { + project.getLogger().warn("reckon.preRelease = stageFromProp() or snapshotFromProp() is deprecated and will be removed in 1.0.0. Call reckon.stageFromProp() or reckon.snapshotFromProp() instead."); + // no op } - public VcsInventorySupplier git(Grgit grgit) { + public ReckonExtension git(Grgit grgit) { this.grgit = grgit; - return new GitInventorySupplier(grgit.getRepository().getJgit().getRepository()); + this.reckoner.git(grgit.getRepository().getJgit().getRepository()); + return this; } - public NormalStrategy scopeFromProp() { - Function> supplier = inventory -> findProperty(SCOPE_PROP); - return new ScopeNormalStrategy(supplier); + public ReckonExtension scopeFromProp() { + this.reckoner.scopeCalc(inventory -> findProperty(SCOPE_PROP)); + return this; } - public PreReleaseStrategy stageFromProp(String... stages) { - Set stageSet = Arrays.stream(stages).collect(Collectors.toSet()); - BiFunction> supplier = (inventory, targetNormal) -> findProperty(STAGE_PROP); - return new StagePreReleaseStrategy(stageSet, supplier); + public ReckonExtension stageFromProp(String... stages) { + this.reckoner.stages(stages); + this.reckoner.stageCalc((inventory, targetNormal) -> findProperty(STAGE_PROP)); + return this; } - public PreReleaseStrategy snapshotFromProp() { - BiFunction> supplier = (inventory, targetNormal) -> { + public ReckonExtension snapshotFromProp() { + this.reckoner.snapshots(); + this.reckoner.stageCalc((inventory, targetNormal) -> { Optional stageProp = findProperty(STAGE_PROP); Optional snapshotProp = findProperty(SNAPSHOT_PROP) .map(Boolean::parseBoolean) @@ -75,8 +69,8 @@ public PreReleaseStrategy snapshotFromProp() { }); return stageProp.isPresent() ? stageProp : snapshotProp; - }; - return new SnapshotPreReleaseStrategy(supplier); + }); + return this; } private Optional findProperty(String name) { @@ -91,13 +85,11 @@ Grgit getGrgit() { } String reckonVersion() { - if (vcsInventory == null) { + if (grgit == null) { project.getLogger().warn("No VCS found/configured. Version will be 'unspecified'."); return "unspecified"; - } else if (normal == null || preRelease == null) { - throw new IllegalStateException("Must provide strategies for normal and preRelease on the reckon extension."); } else { - String version = Reckoner.reckon(vcsInventory, normal, preRelease); + String version = reckoner.build().reckon().toString(); project.getLogger().warn("Reckoned version: {}", version); return version; } 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 0fce6e55..3bf6fb70 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 @@ -25,7 +25,7 @@ public void apply(Project project) { project.getPluginManager().withPlugin("org.ajoberstar.grgit", plugin -> { Grgit grgit = (Grgit) project.findProperty("grgit"); if (grgit != null) { - extension.setVcsInventory(extension.git(grgit)); + extension.git(grgit); } }); From f8f51a58baf9e614233d57588959c4edbcb33772 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 19:52:35 -0500 Subject: [PATCH 09/14] Force use of grgit Apply grgit by default, instead of requiring someone to apply it themselves. This has been a confusion point for a few users. Also, it's unlikely that I'll ever support more VCSs, so this just simplifies things for everyone. Additionally this meant starting to hide some methods from public visibility. I think there's more to do on that front. --- .../core/{git => }/GitInventorySupplier.java | 7 +--- .../org/ajoberstar/reckon/core/Reckoner.java | 10 +++-- .../reckon/core/VcsInventorySupplier.java | 2 +- .../{git => }/GitInventorySupplierTest.groovy | 4 +- .../reckon/gradle/BaseCompatTest.groovy | 14 +++---- .../reckon/gradle/ReckonExtension.java | 39 ++++++------------- .../reckon/gradle/ReckonPlugin.java | 24 +++++------- 7 files changed, 40 insertions(+), 60 deletions(-) rename reckon-core/src/main/java/org/ajoberstar/reckon/core/{git => }/GitInventorySupplier.java (96%) rename reckon-core/src/test/groovy/org/ajoberstar/reckon/core/{git => }/GitInventorySupplierTest.groovy (98%) diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java similarity index 96% rename from reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java rename to reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java index 65a81530..77ef84f4 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/git/GitInventorySupplier.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/GitInventorySupplier.java @@ -1,4 +1,4 @@ -package org.ajoberstar.reckon.core.git; +package org.ajoberstar.reckon.core; import java.io.IOException; import java.io.UncheckedIOException; @@ -12,9 +12,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.ajoberstar.reckon.core.VcsInventory; -import org.ajoberstar.reckon.core.VcsInventorySupplier; -import org.ajoberstar.reckon.core.Version; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -32,7 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class GitInventorySupplier implements VcsInventorySupplier { +final class GitInventorySupplier implements VcsInventorySupplier { private static final Logger logger = LoggerFactory.getLogger(GitInventorySupplier.class); private final Repository repo; 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 1779f581..55224356 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 @@ -1,6 +1,7 @@ package org.ajoberstar.reckon.core; import java.util.Arrays; +import java.util.Collections; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -9,7 +10,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.ajoberstar.reckon.core.git.GitInventorySupplier; import org.eclipse.jgit.lib.Repository; public final class Reckoner { @@ -132,13 +132,17 @@ public static final class Builder { private Set stages; private String defaultStage; - public Builder vcs(VcsInventorySupplier inventorySupplier) { + Builder vcs(VcsInventorySupplier inventorySupplier) { this.inventorySupplier = inventorySupplier; return this; } public Builder git(Repository repo) { - this.inventorySupplier = new GitInventorySupplier(repo); + if (repo == null) { + this.inventorySupplier = () -> new VcsInventory(null, false, null, null, null, 0, Collections.emptySet(), Collections.emptySet()); + } else { + this.inventorySupplier = new GitInventorySupplier(repo); + } return this; } diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java index 56f0e69d..89899656 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java @@ -1,6 +1,6 @@ package org.ajoberstar.reckon.core; @FunctionalInterface -public interface VcsInventorySupplier { +interface VcsInventorySupplier { VcsInventory getInventory(); } diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/GitInventorySupplierTest.groovy similarity index 98% rename from reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy rename to reckon-core/src/test/groovy/org/ajoberstar/reckon/core/GitInventorySupplierTest.groovy index 2c62f19f..6a6a26b0 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/git/GitInventorySupplierTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/GitInventorySupplierTest.groovy @@ -1,10 +1,8 @@ -package org.ajoberstar.reckon.core.git +package org.ajoberstar.reckon.core import java.nio.file.Files import java.security.SecureRandom import org.ajoberstar.grgit.Grgit -import org.ajoberstar.reckon.core.VcsInventory -import org.ajoberstar.reckon.core.Version import spock.lang.Shared import spock.lang.Specification 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 a52e49ed..7d57caa0 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 @@ -33,14 +33,18 @@ class BaseCompatTest extends Specification { remote.commit(message: 'second commit') } - def 'if no git repo found, version is unspecified'() { + def 'if no git repo found, version is defaulted'() { given: buildFile << """ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } +reckon { + scopeFromProp() + stageFromProp('alpha','beta', 'final') +} + task printVersion { doLast { println project.version @@ -50,7 +54,7 @@ task printVersion { when: def result = build('printVersion', '-q') then: - result.output.normalize() == 'No git repository found for :. Accessing grgit will cause an NPE.\nunspecified\n' + result.output.normalize() == 'No git repository found for :. Accessing grgit will cause an NPE.\n0.1.0-alpha.0.0+uncommitted\n' } def 'if no strategies specified, build fails'() { @@ -59,7 +63,6 @@ task printVersion { buildFile << """ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } @@ -81,7 +84,6 @@ task printVersion { buildFile << """ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } @@ -106,7 +108,6 @@ reckon { buildFile << """ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } @@ -134,7 +135,6 @@ reckon { buildFile << """ plugins { - id 'org.ajoberstar.grgit' id 'org.ajoberstar.reckon' } 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 f74c07f8..4206cdb0 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 @@ -3,8 +3,9 @@ import java.util.Optional; import org.ajoberstar.grgit.Grgit; +import org.ajoberstar.grgit.Repository; import org.ajoberstar.reckon.core.Reckoner; -import org.ajoberstar.reckon.core.VcsInventorySupplier; +import org.eclipse.jgit.api.Git; import org.gradle.api.Project; public class ReckonExtension { @@ -13,18 +14,17 @@ public class ReckonExtension { private static final String SNAPSHOT_PROP = "reckon.snapshot"; private Project project; - private Grgit grgit; private Reckoner.Builder reckoner; - public ReckonExtension(Project project) { + public ReckonExtension(Project project, Grgit grgit) { this.project = project; this.reckoner = Reckoner.builder(); - } - - @Deprecated - public void setVcsInventory(VcsInventorySupplier inventorySupplier) { - project.getLogger().warn("reckon.vcsInventory = is deprecated and will be removed in 1.0.0. Call reckon.git instead."); - this.reckoner.vcs(inventorySupplier); + org.eclipse.jgit.lib.Repository repo = Optional.ofNullable(grgit) + .map(Grgit::getRepository) + .map(Repository::getJgit) + .map(Git::getRepository) + .orElse(null); + this.reckoner.git(repo); } @Deprecated @@ -39,12 +39,6 @@ public void setPreRelease(ReckonExtension ext) { // no op } - public ReckonExtension git(Grgit grgit) { - this.grgit = grgit; - this.reckoner.git(grgit.getRepository().getJgit().getRepository()); - return this; - } - public ReckonExtension scopeFromProp() { this.reckoner.scopeCalc(inventory -> findProperty(SCOPE_PROP)); return this; @@ -80,18 +74,9 @@ private Optional findProperty(String name) { .map(Object::toString); } - Grgit getGrgit() { - return grgit; - } - String reckonVersion() { - if (grgit == null) { - project.getLogger().warn("No VCS found/configured. Version will be 'unspecified'."); - return "unspecified"; - } else { - String version = reckoner.build().reckon().toString(); - project.getLogger().warn("Reckoned version: {}", version); - return version; - } + String version = reckoner.build().reckon().toString(); + project.getLogger().warn("Reckoned version: {}", version); + return version; } } 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 3bf6fb70..3ce65e07 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 @@ -20,26 +20,22 @@ public void apply(Project project) { if (!project.equals(project.getRootProject())) { throw new IllegalStateException("org.ajoberstar.reckon can only be applied to the root project."); } - ReckonExtension extension = project.getExtensions().create("reckon", ReckonExtension.class, project); + project.getPluginManager().apply("org.ajoberstar.grgit"); - project.getPluginManager().withPlugin("org.ajoberstar.grgit", plugin -> { - Grgit grgit = (Grgit) project.findProperty("grgit"); - if (grgit != null) { - extension.git(grgit); - } - }); + Grgit grgit = (Grgit) project.findProperty("grgit"); + ReckonExtension extension = project.getExtensions().create("reckon", ReckonExtension.class, project, grgit); DelayedVersion sharedVersion = new DelayedVersion(extension::reckonVersion); project.allprojects(prj -> { prj.setVersion(sharedVersion); }); - Task tag = createTagTask(project, extension); - Task push = createPushTask(project, extension, tag); + Task tag = createTagTask(project, extension, grgit); + Task push = createPushTask(project, extension, grgit, tag); push.dependsOn(tag); } - private Task createTagTask(Project project, ReckonExtension extension) { + private Task createTagTask(Project project, ReckonExtension extension, Grgit grgit) { Task task = project.getTasks().create(TAG_TASK); task.setDescription("Tag version inferred by reckon."); task.setGroup("publishing"); @@ -48,7 +44,7 @@ private Task createTagTask(Project project, ReckonExtension extension) { // using the presence of build metadata as the indicator of taggable versions boolean insignificant = version.contains("+"); // rebuilds shouldn't trigger a new tag - boolean alreadyTagged = extension.getGrgit().getTag().list().stream() + boolean alreadyTagged = grgit.getTag().list().stream() .anyMatch(tag -> tag.getName().equals(version)); return !(insignificant || alreadyTagged); }); @@ -56,12 +52,12 @@ private Task createTagTask(Project project, ReckonExtension extension) { Map args = new HashMap<>(); args.put("name", project.getVersion()); args.put("message", project.getVersion()); - extension.getGrgit().getTag().add(args); + grgit.getTag().add(args); }); return task; } - private Task createPushTask(Project project, ReckonExtension extension, Task create) { + private Task createPushTask(Project project, ReckonExtension extension, Grgit grgit, Task create) { Task task = project.getTasks().create(PUSH_TASK); task.setDescription("Push version tag created by reckon."); task.setGroup("publishing"); @@ -69,7 +65,7 @@ private Task createPushTask(Project project, ReckonExtension extension, Task cre task.doLast(t -> { Map args = new HashMap<>(); args.put("refsOrSpecs", Arrays.asList("refs/tags/" + project.getVersion().toString())); - extension.getGrgit().push(args); + grgit.push(args); }); return task; } From 29113c4e87f4abae1b1279d3223c4c6f45584626 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 20:32:04 -0500 Subject: [PATCH 10/14] Cleanup docs for new syntax --- README.md | 29 +++++++++++++++-------------- docs/index.md | 42 ++++++++++++++++++++---------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9d156d6f..539a99a5 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,14 @@ Reckon is two things: - an API to infer your next version from a Git repository - applications of that API in various tools (initially, just Gradle) -### Stage Version Scheme +Two schemes are provided to manage pre-release information. + +- _Stages_ for a more structured approach which is a subset of [SemVer](http://semver.org). +- _Snapshots_ for the classic Maven approach to pre-release versions. -Reckon uses an opinionated subset of [SemVer](http://semver.org), meant to provide more structure around how the -pre-release versions are managed. +### Stage Version Scheme -There are three types of versions: +There are three types of stages: | Type | Scheme | Example | Description | |-------------------|----------------------------------------------------------|---------------------------------------------------------|-------------| @@ -85,10 +87,6 @@ Reckon can alternately use SNAPSHOT versions instead of the stage concept. | **final** | `..` | `1.2.3` | A version ready for end-user consumption | | **snapshot** | `..-SNAPSHOT` | `1.3.0-SNAPSHOT` | An intermediate version before the final release is ready. | -### More Information - -See [How Reckon Works](docs/index.md), which includes examples of how reckon will behave in various scenarios. - ## How do I use it? **NOTE:** Check the [Release Notes](https://github.com/ajoberstar/reckon/releases) for details on compatibility and changes. @@ -99,15 +97,14 @@ See [How Reckon Works](docs/index.md), which includes examples of how reckon wil ```groovy plugins { - id 'org.ajoberstar.grgit' version '' // this is a required dependency unless you plan to implement your own VcsInventorySupplier id 'org.ajoberstar.reckon' version '' } reckon { - normal = scopeFromProp() - preRelease = stageFromProp('milestone', 'rc', 'final') - // alternately - // preRelease = snapshotFromProp() + scopeFromProp() + stageFromProp('milestone', 'rc', 'final') + // alternative to stageFromProp + // snapshotFromProp() } ``` @@ -119,7 +116,7 @@ Execute Gradle providing the properties, as needed: - `reckon.stage` - (if you used `stageFromProp`) one of the values passed to `stageFromProp` (defaults to the first alphabetically) to specify what phase of development you are in - (if you used `snapshotFromProp`) either `snapshot` or `final` (defaults to `snapshot`) to specify what phase of development you are in -- `reckon.snapshot` - (**deprecated**, if you used `snapshotFromProp`) one of `true` or `false` (defaults to `true`) to determine whether a snapshot should be made +- `reckon.snapshot` - **deprecated** (if you used `snapshotFromProp`) one of `true` or `false` (defaults to `true`) to determine whether a snapshot should be made When Gradle executes, the version will be inferred as soon as something tries to access it. This will be output to the console (as below). @@ -146,6 +143,10 @@ It's suggested you add dependencies to these tasks to ensure your project is in reckonTagCreate.dependsOn check ``` +### Examples + +See [How Reckon Works](docs/index.md), which includes examples of how reckon will behave in various scenarios. + ## Contributing Contributions are very welcome and are accepted through pull requests. diff --git a/docs/index.md b/docs/index.md index b7d3c768..b7f7344b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,6 @@ # How Reckon Works -## Inference Algorithm - -### Axioms +## Axioms These are the rules that reckon presumes are true, both informing how it reads a repo's history and how it calculates the next version: @@ -16,7 +14,7 @@ These are the rules that reckon presumes are true, both informing how it reads a 1. **Final versions MUST NOT be re-released as a pre-release.** Once you release a final version (e.g. 1.2.3), that same commit cannot be re-released as a pre-release (e.g. 1.3.0-beta.1). However, the commit can be re-released as a final (e.g. 1.3.0). 1. **Final and significant versions MUST be released from a clean repo.** If there are any uncommitted changes, the version will not be treated as a final or significant. -### Inputs +## Inputs In order to infer the next version, reckon needs two pieces of input: @@ -37,13 +35,13 @@ plugins { // ... reckon { - normal = scopeFromProp() - preRelease = stageFromProp('beta', 'rc', 'final') + scopeFromProp() + stageFromProp('beta', 'rc', 'final') } // ... ``` -**You have some changes in the repository, but no commits yet.** +### You have some changes in the repository, but no commits yet ``` $ ./gradlew build @@ -52,7 +50,7 @@ Reckoned version: 0.1.0-beta.0.0+uncommitted This used the default of `minor` scope and `beta` stage (`beta` is the first stage alphabetically). Since you have some changes in your repo that aren't committed, indicate that in the build -**Now make a commit, but run the same Gradle command.** +### Now make a commit, but run the same Gradle command ``` $ ./gradlew build @@ -61,7 +59,7 @@ Reckoned version: 0.1.0-beta.0.1+e06c68a863eb6ceaf889ee5802f478c10c1464bb The version now shows 1 commit since a normal has been released, and the full commit hash in the build metadata. -**Now make some more changes, but don't commit them** +### Now make some more changes, but don't commit them ``` $ ./gradlew build @@ -70,7 +68,7 @@ Reckoned version: 0.1.0-beta.0.1+e06c68a863eb6ceaf889ee5802f478c10c1464bb.uncomm The version hasn't changed except to indicate that you have uncommitted changes. -**Now commit this and let's release a minor version beta** +### Now commit this and let's release a minor version beta You can specify the scope or leave it off, since `minor` is the default. @@ -81,7 +79,7 @@ Reckoned version: 0.1.0-beta.1 ``` Note that you no longer have a count of commits or a commit hash, since this is a significant version that will result in a tag. -**Now just run the build again** +### Now just run the build again ``` $ ./gradlew build @@ -90,7 +88,7 @@ Reckoned version: 0.1.0-beta.1 The current `HEAD` is tagged and you haven't changed anything, or indicated you wanted a different version by providing scope or stage. Reckon assumes you just want to rebuild the existing version. -**Make a bunch more commits and build again** +### Make a bunch more commits and build again ``` $ ./gradlew build @@ -99,7 +97,7 @@ Reckoned version: 0.1.0-beta.1.8+e06c68a863eb6ceaf889ee5802f478c10c1464bb We're back to an insignificant version, since you didn't indicate a stage. Again we get the commit count and hash. -**Release another beta** +### Release another beta ``` $ ./gradlew build reckonTagPush -Preckon.stage=beta @@ -108,7 +106,7 @@ Reckoned version: 0.1.0-beta.2 While you already could have left the scope of with the default of `minor`, you can also leave it off because you just want to continue development towards the _target_ normal version you've been working on. -**Release this commit as an rc** +### Release this commit as an rc You've decided there's enough features in this release, and you're ready to treat it as a release-candidate. @@ -119,7 +117,7 @@ Reckoned version: 0.1.0-rc.1 Note that the count after the stage resets to 1. -**Make a bug fix but don't commit it yet** +### Make a bug fix but don't commit it yet ``` $ ./gradlew build @@ -128,14 +126,14 @@ Reckoned version: 0.1.0-rc.1.8+e06c68a863eb6ceaf889ee5802f478c10c1464bb.uncommit Note that the commit count does not reset (since it's based on commits since the last normal). -**Commit the change and release another rc** +### Commit the change and release another rc ``` $ ./gradlew build reckonTagPush -Preckon.stage=rc Reckoned version: 0.1.0-rc.2 ``` -**Release this as a final** +### Release this as a final You've decided there aren't any bugs in this release and you're ready to make it official. @@ -144,7 +142,7 @@ $ ./gradlew build reckonTagPush -Preckon.stage=final Reckoned version: 0.1.0 ``` -**Make this the 1.0.0** +### Make this the 1.0.0 You've decided this is feature complete and you're ready to make your 1.0.0 release. @@ -153,7 +151,7 @@ $ ./gradlew build reckonTagPush -Preckon.scope=major -Preckon.stage=final Reckoned version: 1.0.0 ``` -**Make some commits and build** +### Make some commits and build ``` $ ./gradlew build @@ -162,14 +160,14 @@ Reckoned version: 1.1.0-beta.0.4+7836cf7469dd00fe1035ea14ef1faaa7452cc5e0 Note that `minor` was again used as a default, same with `beta`, and that your commit count reset since a normal was released. -**Release this as a patch rc** +### Release this as a patch rc ``` $ ./gradlew build reckonTagPush -Preckon.scope=patch -Preckon.stage=rc Reckoned version: 1.0.1-rc.1 ``` -**Release as a final patch** +### Release as a final patch ``` $ ./gradlew build reckonTagPush -Preckon.stage=final @@ -178,7 +176,7 @@ Reckoned version: 1.0.1 While the default is usually `minor`, if you're already developing towards a `patch` or `major` those will be used as defaults instead. -**Make some changes but don't commit them and run again** +### Make some changes but don't commit them and run again ``` $ ./gradlew build reckonTagPush -Preckon.stage=final From ce28b119e303690359cc7d812afbda471d4ddf26 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 21:27:58 -0500 Subject: [PATCH 11/14] Add Javadoc --- .../reckon/core/GitInventorySupplier.java | 13 ++++- .../org/ajoberstar/reckon/core/Reckoner.java | 43 ++++++++++++++++ .../org/ajoberstar/reckon/core/Scope.java | 24 +++++++++ .../ajoberstar/reckon/core/VcsInventory.java | 33 ++++++++++++- .../reckon/core/VcsInventorySupplier.java | 3 ++ .../org/ajoberstar/reckon/core/Version.java | 49 +++++++++++++++++++ 6 files changed, 163 insertions(+), 2 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 77ef84f4..7956e355 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 @@ -29,6 +29,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Supplies an inventory of a Git repository. + * + * This is intentionally package private. + */ final class GitInventorySupplier implements VcsInventorySupplier { private static final Logger logger = LoggerFactory.getLogger(GitInventorySupplier.class); @@ -49,7 +54,9 @@ public GitInventorySupplier(Repository repo, Function> @Override public VcsInventory getInventory() { + // share this walk throughout to benefit from its caching try (RevWalk walk = new RevWalk(repo)) { + // saves on some performance as we don't really need the commit bodys walk.setRetainBody(false); ObjectId headObjectId = repo.getRefDatabase().getRef("HEAD").getObjectId(); @@ -104,7 +111,7 @@ private boolean isClean() { try { return new Git(repo).status().call().isClean(); } catch (GitAPIException e) { - logger.error("Failed to determine status of repository.", e); + logger.error("Failed to determine status of repository. Assuming not clean.", e); // TODO should this throw up? return false; } @@ -128,6 +135,7 @@ private Set getTaggedVersions(RevWalk walk) throws IOException { private Optional findCurrent(RevCommit head, Stream versions) { return versions .filter(version -> version.getCommit().equals(head)) + // if multiple tags on the head commit, we want the highest precedence one .max(Comparator.comparing(TaggedVersion::getVersion)); } @@ -153,6 +161,9 @@ private TaggedVersion findBase(RevWalk walk, RevCommit head, Stream new VcsInventory(null, false, null, null, null, 0, Collections.emptySet(), Collections.emptySet()); @@ -146,11 +160,23 @@ public Builder git(Repository repo) { return this; } + /** + * Use the given function to determine what scope should be used when inferring the version. + * + * @param scopeCalc the function that provides the scope + * @return this builder + */ public Builder scopeCalc(Function> scopeCalc) { this.scopeCalc = scopeCalc; return this; } + /** + * Use the given stages as valid options during inference. + * + * @param stages the valid stages + * @return this builder + */ public Builder stages(String... stages) { this.stages = Arrays.stream(stages).collect(Collectors.toSet()); this.defaultStage = this.stages.stream() @@ -161,17 +187,34 @@ public Builder stages(String... stages) { return this; } + /** + * Use only the {@code snapshot} and {@code final} stages. Alternative to calling {@code stages()}. + * + * @return this builder + */ public Builder snapshots() { this.stages = Stream.of(Reckoner.FINAL_STAGE, Reckoner.SNAPSHOT_STAGE).collect(Collectors.toSet()); this.defaultStage = Reckoner.SNAPSHOT_STAGE; return this; } + /** + * Use the given function to determine what staged should be used when inferring the version. This + * must return a version contained in {@code stages()}. + * + * @param stageCalc the function that provides the stage + * @return this builder + */ public Builder stageCalc(BiFunction> stageCalc) { this.stageCalc = stageCalc; return this; } + /** + * Builds the reckoner. + * + * @return the reckoner + */ public Reckoner build() { Objects.requireNonNull(inventorySupplier, "Must provide a vcs."); Objects.requireNonNull(scopeCalc, "Must provide a scope supplier."); diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java index 08820926..c08bb408 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Scope.java @@ -4,9 +4,22 @@ import java.util.Optional; import java.util.stream.Collectors; +/** + * Scope is an indication of how large the change between SemVer normal versions is. + */ public enum Scope { MAJOR, MINOR, PATCH; + /** + * Parses a String version of a Scope. This is an alternative to {@code valueOf} which only supports + * literal matches. This method supports mixed case String representations, like Major or minor, + * instead of just PATCH. Additionally, it provides a better error message when an invalid scope is + * provided. + * + * @param value the string to parse as a scope + * @return the matching scope + * @throws IllegalArgumentException if no match was found + */ public static Scope from(String value) { try { return Scope.valueOf(value.toUpperCase()); @@ -20,6 +33,17 @@ public static Scope from(String value) { } } + /** + * Infers the scope between two versions. It looks left-to-right through the components of the + * normal versions looking for the first difference of 1 that it finds. Anything else is considered + * an invalid difference. For example, 1.0.0 to 2.0.0 is MAJOR, 1.0.0 to 1.1.0 is MINOR, 1.0.0 to + * 1.0.1 is PATCH, 1.0.0 to 3.0.0 is invalid. + * + * @param before the earlier version to compare + * @param after the later version to compare + * @return the scope of the change between the two versions, or empty if they are the same or have + * an invalid increment + */ public static Optional infer(Version before, Version after) { int major = after.getVersion().getMajorVersion() - before.getVersion().getMajorVersion(); int minor = after.getVersion().getMinorVersion() - before.getVersion().getMinorVersion(); diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java index e0a7c423..3ea55dbb 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventory.java @@ -9,6 +9,9 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +/** + * An inventory of the state of your VCS and the versions tagged within it. + */ public final class VcsInventory { private final String commitId; private final boolean clean; @@ -19,7 +22,10 @@ public final class VcsInventory { private final Set parallelNormals; private final Set claimedVersions; - public VcsInventory( + /** + * This is intentionally package private. + */ + VcsInventory( String commitId, boolean clean, Version currentVersion, @@ -46,34 +52,59 @@ public VcsInventory( .orElse(Collections.emptySet()); } + /** + * The ID of the current commit, if any, in the active branch of the repository. + */ public Optional getCommitId() { return Optional.ofNullable(commitId); } + /** + * Whether the repository has any uncommitted changes. + */ public boolean isClean() { return clean; } + /** + * Gets the version tagged on the current commit of the repository, if any. + */ public Optional getCurrentVersion() { return Optional.ofNullable(currentVersion); } + /** + * Number of commits between the current commmit and the base normal version tag. + */ public int getCommitsSinceBase() { return commitsSinceBase; } + /** + * The most recent (based on ancestry, not time) tagged version from the current commit. May be a + * pre-release version, but could be the same as baseNormal. + */ public Version getBaseVersion() { return baseVersion; } + /** + * The most recent (based on ancestry, not time) tagged final version from the current commit. + */ public Version getBaseNormal() { return baseNormal; } + /** + * Any normal versions under development in other branches. + */ public Set getParallelNormals() { return parallelNormals; } + /** + * Any versions that have already been released or otherwise claimed. + */ public Set getClaimedVersions() { return claimedVersions; } diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java index 89899656..25fd775d 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/VcsInventorySupplier.java @@ -1,5 +1,8 @@ package org.ajoberstar.reckon.core; +/** + * This is intentionally package private. + */ @FunctionalInterface interface VcsInventorySupplier { VcsInventory getInventory(); diff --git a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java index 5df7cb68..4f5c5ddd 100644 --- a/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java +++ b/reckon-core/src/main/java/org/ajoberstar/reckon/core/Version.java @@ -7,7 +7,13 @@ import com.github.zafarkhaja.semver.ParseException; +/** + * A SemVer-compliant version. + */ public final class Version implements Comparable { + /** + * A base version for use as a default in cases where you don't have an existing version. + */ public static final Version IDENTITY = new Version(com.github.zafarkhaja.semver.Version.forIntegers(0, 0, 0)); private final com.github.zafarkhaja.semver.Version version; @@ -16,6 +22,7 @@ public final class Version implements Comparable { private Version(com.github.zafarkhaja.semver.Version version) { this.version = version; + // need this if logic to avoid stack overflow if (version.getPreReleaseVersion().isEmpty()) { this.normal = this; } else { @@ -24,22 +31,41 @@ private Version(com.github.zafarkhaja.semver.Version version) { this.stage = Stage.valueOf(version); } + /** + * This is intentionally package private. + * + * @return the internal JSemver version + */ com.github.zafarkhaja.semver.Version getVersion() { return version; } + /** + * @return the normal component of the version. + */ public Version getNormal() { return normal; } + /** + * @return the stage of this version, if any. + */ public Optional getStage() { return Optional.ofNullable(stage); } + /** + * @return {@code true} if this is a final version (i.e. doesn't have pre-release information), + * {@code false} otherwise + */ public boolean isFinal() { return version.getPreReleaseVersion().isEmpty(); } + /** + * @return {@code true} if the version is final or any other significant stage, {@code false} if + * insignficant or snapshot + */ public boolean isSignificant() { return isFinal() || getStage() .filter(stage -> !"SNAPSHOT".equals(stage.getName())) @@ -47,6 +73,12 @@ public boolean isSignificant() { .isPresent(); } + /** + * Increments this version using the given scope to get a new normal version. + * + * @param scope the scope to increment the version by + * @return incremented version, with only the normal component + */ public Version incrementNormal(Scope scope) { switch (scope) { case MAJOR: @@ -86,6 +118,9 @@ public String toString() { return version.toString(); } + /** + * Stage of development. + */ public static final class Stage { private static final Pattern STAGE_REGEX = Pattern.compile("^(?\\w+)(?:\\.(?\\d+))?"); @@ -117,10 +152,24 @@ private static Stage valueOf(com.github.zafarkhaja.semver.Version version) { } } + /** + * Gets the version represented by the given string. If the version is not SemVer compliant, an + * exception will be thrown. Use {@code parse} if you don't trust that your input is valid. + * + * @param versionString version to parse + * @return the version + */ public static Version valueOf(String versionString) { return new Version(com.github.zafarkhaja.semver.Version.valueOf(versionString)); } + /** + * Gets the version represented by the given string, if it's SemVer compliant. If not, an empty + * Optional will be returned. + * + * @param versionString version to parse + * @return the version or an empty optional, if the string wasn't SemVer compliant + */ public static Optional parse(String versionString) { try { return Optional.of(new Version(com.github.zafarkhaja.semver.Version.valueOf(versionString))); From 8bf99d57b45ffe628d314fcf6ca3fb4e33aed87b Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 21:44:04 -0500 Subject: [PATCH 12/14] Allow building insignificant when normal claimed On top of already allowing a rebuild of a tagged version that's already claimed, we should allow rebuilding insignificant versions. As noted in the issue, this allows use of git bisect on reckoned repos. This fixes #78. --- .../org/ajoberstar/reckon/core/Reckoner.java | 22 ++++++------ .../reckon/core/ReckonerTest.groovy | 34 +++++++++++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) 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 c7019aea..ec4abe7c 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 @@ -35,7 +35,7 @@ private Reckoner(VcsInventorySupplier inventorySupplier, Function 0) { throw new IllegalStateException("Reckoned version " + reckoned + " is (and cannot be) less than base version " + inventory.getBaseVersion()); } @@ -78,10 +82,6 @@ private Version reckonNormal(VcsInventory inventory) { targetNormal = targetNormal.incrementNormal(scope); } - if (inventory.getClaimedVersions().contains(targetNormal) && !inventory.getCurrentVersion().filter(targetNormal::equals).isPresent()) { - throw new IllegalStateException("Reckoned target normal version " + targetNormal + " has already been released."); - } - return targetNormal; } @@ -147,7 +147,7 @@ Builder vcs(VcsInventorySupplier inventorySupplier) { /** * Use the given JGit repository to infer the state of Git. - * + * * @param repo repository that the version should be inferred from * @return this builder */ @@ -162,7 +162,7 @@ public Builder git(Repository repo) { /** * Use the given function to determine what scope should be used when inferring the version. - * + * * @param scopeCalc the function that provides the scope * @return this builder */ @@ -173,7 +173,7 @@ public Builder scopeCalc(Function> scopeCalc) { /** * Use the given stages as valid options during inference. - * + * * @param stages the valid stages * @return this builder */ @@ -189,7 +189,7 @@ public Builder stages(String... stages) { /** * Use only the {@code snapshot} and {@code final} stages. Alternative to calling {@code stages()}. - * + * * @return this builder */ public Builder snapshots() { @@ -201,7 +201,7 @@ public Builder snapshots() { /** * Use the given function to determine what staged should be used when inferring the version. This * must return a version contained in {@code stages()}. - * + * * @param stageCalc the function that provides the stage * @return this builder */ @@ -212,7 +212,7 @@ public Builder stageCalc(BiFunction> sta /** * Builds the reckoner. - * + * * @return the reckoner */ public Reckoner build() { diff --git a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy index a9b220c2..27007479 100644 --- a/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy +++ b/reckon-core/src/test/groovy/org/ajoberstar/reckon/core/ReckonerTest.groovy @@ -189,6 +189,40 @@ class ReckonerTest extends Specification { reckonStage(inventory, null, null) == '0.1.0' } + def 'if target normal claimed, but building insignificant, succeed'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('0.0.0'), + Version.valueOf('0.0.0'), + 1, + [] as Set, + [Version.valueOf('0.1.0'), Version.valueOf('0.1.1'), Version.valueOf('0.2.0')] as Set + ) + expect: + reckonStage(inventory, null, null) == '0.1.0-beta.0.1+abcdef' + } + + def 'if target normal claimed, and building significant, throw'() { + given: + def inventory = new VcsInventory( + 'abcdef', + true, + null, + Version.valueOf('0.0.0'), + Version.valueOf('0.0.0'), + 1, + [] as Set, + [Version.valueOf('0.1.0'), Version.valueOf('0.1.1'), Version.valueOf('0.2.0')] as Set + ) + when: + reckonStage(inventory, null, 'rc') + then: + thrown(IllegalStateException) + } + def 'if scope supplier returns invalid scope, throw'() { given: def inventory = new VcsInventory( From 9e9eafe951e04f689f5439aed980b01267cc9a45 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 21:54:54 -0500 Subject: [PATCH 13/14] Prevent SNAPSHOTs from being tagged SNAPSHOT should be treated like any other insignificant version. --- .../reckon/gradle/BaseCompatTest.groovy | 25 +++++++++++++++++++ .../reckon/gradle/ReckonExtension.java | 5 ++-- .../reckon/gradle/ReckonPlugin.java | 21 ++++++++++------ 3 files changed, 41 insertions(+), 10 deletions(-) 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 7d57caa0..6f3f601f 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 @@ -102,6 +102,31 @@ reckon { result.task(':reckonTagPush').outcome == TaskOutcome.SKIPPED } + + def 'if reckoned version is SNAPSHOT no tag created'() { + given: + def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + + buildFile << """ +plugins { + id 'org.ajoberstar.reckon' +} + +reckon { + scopeFromProp() + snapshotFromProp() +} +""" + local.add(patterns: ['build.gradle']) + local.commit(message: 'Build file') + when: + def result = build('reckonTagPush') + then: + result.output.contains('Reckoned version: 1.1.0-SNAPSHOT') + result.task(':reckonTagCreate').outcome == TaskOutcome.SKIPPED + result.task(':reckonTagPush').outcome == TaskOutcome.SKIPPED + } + def 'if reckoned version has no build metadata tag created and pushed'() { given: def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) 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 4206cdb0..8c64ebd7 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,6 +5,7 @@ import org.ajoberstar.grgit.Grgit; import org.ajoberstar.grgit.Repository; import org.ajoberstar.reckon.core.Reckoner; +import org.ajoberstar.reckon.core.Version; import org.eclipse.jgit.api.Git; import org.gradle.api.Project; @@ -74,8 +75,8 @@ private Optional findProperty(String name) { .map(Object::toString); } - String reckonVersion() { - String version = reckoner.build().reckon().toString(); + Version reckonVersion() { + Version version = reckoner.build().reckon(); project.getLogger().warn("Reckoned version: {}", version); return version; } 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 3ce65e07..3fee0c28 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 @@ -7,6 +7,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import org.ajoberstar.grgit.Grgit; +import org.ajoberstar.reckon.core.Version; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; @@ -40,13 +41,13 @@ private Task createTagTask(Project project, ReckonExtension extension, Grgit grg task.setDescription("Tag version inferred by reckon."); task.setGroup("publishing"); task.onlyIf(t -> { - String version = project.getVersion().toString(); - // using the presence of build metadata as the indicator of taggable versions - boolean insignificant = version.contains("+"); + Version version = ((DelayedVersion) project.getVersion()).getVersion(); + // rebuilds shouldn't trigger a new tag boolean alreadyTagged = grgit.getTag().list().stream() - .anyMatch(tag -> tag.getName().equals(version)); - return !(insignificant || alreadyTagged); + .anyMatch(tag -> tag.getName().equals(version.toString())); + + return version.isSignificant() && !alreadyTagged; }); task.doLast(t -> { Map args = new HashMap<>(); @@ -71,15 +72,19 @@ private Task createPushTask(Project project, ReckonExtension extension, Grgit gr } private static class DelayedVersion { - private final Supplier reckoner; + private final Supplier reckoner; - public DelayedVersion(Supplier reckoner) { + public DelayedVersion(Supplier reckoner) { this.reckoner = Suppliers.memoize(reckoner); } + public Version getVersion() { + return reckoner.get(); + } + @Override public String toString() { - return reckoner.get(); + return reckoner.get().toString(); } } } From 5a3984de77deb0238e752bcc42a3ed39fe5491a7 Mon Sep 17 00:00:00 2001 From: Andrew Oberstar Date: Tue, 29 May 2018 21:57:52 -0500 Subject: [PATCH 14/14] Verify old extension syntax works Want a test to verify this until we remove the deprecated methods. --- .../reckon/gradle/BaseCompatTest.groovy | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) 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 6f3f601f..e606a3d1 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 @@ -102,7 +102,6 @@ reckon { result.task(':reckonTagPush').outcome == TaskOutcome.SKIPPED } - def 'if reckoned version is SNAPSHOT no tag created'() { given: def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) @@ -127,7 +126,7 @@ reckon { result.task(':reckonTagPush').outcome == TaskOutcome.SKIPPED } - def 'if reckoned version has no build metadata tag created and pushed'() { + def 'if reckoned version is significant tag created and pushed'() { given: def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) @@ -179,6 +178,34 @@ reckon { result.task(':reckonTagPush').outcome == TaskOutcome.SKIPPED } + def 'old syntax of extension does not fail'() { + given: + def local = Grgit.clone(dir: projectDir, uri: remote.repository.rootDir) + + buildFile << """ +plugins { + id 'org.ajoberstar.reckon' +} + +reckon { + normal = scopeFromProp() + preRelease = stageFromProp('alpha','beta', 'final') +} + +task printVersion { + doLast { + println project.version + } +} +""" + local.add(patterns: ['build.gradle']) + local.commit(message: 'Build file') + when: + def result = build('printVersion') + then: + result.output.contains('Reckoned version: 1.1.0-alpha.0') + } + private BuildResult build(String... args = []) { return GradleRunner.create() .withGradleVersion(System.properties['compat.gradle.version'])