diff --git a/coverage/pom.xml b/coverage/pom.xml index ae2ee7a..29bd859 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -19,6 +19,11 @@ + + io.github.sam42r + semver-maven-model + ${project.version} + io.github.sam42r semver-scm-api diff --git a/pom.xml b/pom.xml index 73e9064..b75c1ff 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ https://github.com/Sam42R/semver-maven-plugin + semver-maven-model semver-scm-api semver-scm-git semver-scm-subversion diff --git a/semver-analyzer-api/pom.xml b/semver-analyzer-api/pom.xml index f990a94..484c578 100644 --- a/semver-analyzer-api/pom.xml +++ b/semver-analyzer-api/pom.xml @@ -22,7 +22,7 @@ io.github.sam42r - semver-scm-api + semver-maven-model ${project.version} diff --git a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/CommitAnalyzer.java b/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/CommitAnalyzer.java index 85dbb42..24030cd 100644 --- a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/CommitAnalyzer.java +++ b/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/CommitAnalyzer.java @@ -1,7 +1,9 @@ package io.github.sam42r.semver.analyzer; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.scm.model.Commit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; import lombok.NonNull; import java.util.List; @@ -12,7 +14,7 @@ */ public interface CommitAnalyzer { - @NonNull List analyzeCommits(@NonNull List commits); + @NonNull List analyzeCommits(@NonNull List commits, @NonNull Remote remote, ProviderSpec providerSpec); @NonNull String generateReleaseCommitMessage(@NonNull String version); } diff --git a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/AnalyzedCommit.java b/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/AnalyzedCommit.java deleted file mode 100644 index 99864d3..0000000 --- a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/AnalyzedCommit.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.sam42r.semver.analyzer.model; - -import io.github.sam42r.semver.scm.model.Commit; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -import java.util.List; -import java.util.function.Predicate; - -@Data -@SuperBuilder -@EqualsAndHashCode(callSuper = false) -@NoArgsConstructor -public class AnalyzedCommit extends Commit { - - private String header; - private String body; - private String footer; - - private String type; - private ChangeCategory category; - private String scope; - private String subject; - private SemVerChangeLevel level; - - private List issues; - - public static final Predicate isBugfix = analyzedCommit -> - SemVerChangeLevel.PATCH.equals(analyzedCommit.getLevel()); - public static final Predicate isFeature = analyzedCommit -> - SemVerChangeLevel.MINOR.equals(analyzedCommit.getLevel()); - public static final Predicate isBreaking = analyzedCommit -> - SemVerChangeLevel.MAJOR.equals(analyzedCommit.getLevel()); -} diff --git a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/Configuration.java b/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/Configuration.java index 53bdf75..fc24854 100644 --- a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/Configuration.java +++ b/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/Configuration.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/semver-analyzer-api/src/test/java/io/github/sam42r/semver/analyzer/model/ConfigurationTest.java b/semver-analyzer-api/src/test/java/io/github/sam42r/semver/analyzer/model/ConfigurationTest.java index e90f595..74de068 100644 --- a/semver-analyzer-api/src/test/java/io/github/sam42r/semver/analyzer/model/ConfigurationTest.java +++ b/semver-analyzer-api/src/test/java/io/github/sam42r/semver/analyzer/model/ConfigurationTest.java @@ -1,5 +1,8 @@ package io.github.sam42r.semver.analyzer.model; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; import org.junit.jupiter.api.Test; import java.util.List; @@ -17,13 +20,13 @@ void shouldReadConfigFromClasspath() { Configuration::getRelease, Configuration::getItems) .containsExactly( "R0", List.of( - AnalyzedCommit.builder().type("D1").category(ChangeCategory.DEPRECATED).level(SemVerChangeLevel.NONE).build(), - AnalyzedCommit.builder().type("R1").category(ChangeCategory.REMOVED).level(SemVerChangeLevel.NONE).build(), - AnalyzedCommit.builder().type("F1").category(ChangeCategory.FIXED).level(SemVerChangeLevel.PATCH).build(), - AnalyzedCommit.builder().type("S1").category(ChangeCategory.SECURITY).level(SemVerChangeLevel.PATCH).build(), - AnalyzedCommit.builder().type("A1").category(ChangeCategory.ADDED).level(SemVerChangeLevel.MINOR).build(), - AnalyzedCommit.builder().type("C1").category(ChangeCategory.CHANGED).level(SemVerChangeLevel.MINOR).build(), - AnalyzedCommit.builder().type("B1").category(ChangeCategory.OTHER).level(SemVerChangeLevel.MAJOR).build() + new AnalyzedCommit(null, null, null, null, null, "D1", ChangeCategory.DEPRECATED, null, null, SemVerChangeLevel.NONE, null), + new AnalyzedCommit(null, null, null, null, null, "R1", ChangeCategory.REMOVED, null, null, SemVerChangeLevel.NONE, null), + new AnalyzedCommit(null, null, null, null, null, "F1", ChangeCategory.FIXED, null, null, SemVerChangeLevel.PATCH, null), + new AnalyzedCommit(null, null, null, null, null, "S1", ChangeCategory.SECURITY, null, null, SemVerChangeLevel.PATCH, null), + new AnalyzedCommit(null, null, null, null, null, "A1", ChangeCategory.ADDED, null, null, SemVerChangeLevel.MINOR, null), + new AnalyzedCommit(null, null, null, null, null, "C1", ChangeCategory.CHANGED, null, null, SemVerChangeLevel.MINOR, null), + new AnalyzedCommit(null, null, null, null, null, "B1", ChangeCategory.OTHER, null, null, SemVerChangeLevel.MAJOR, null) ) ); } @@ -38,13 +41,13 @@ void shouldReadConfigFromFile() { Configuration::getRelease, Configuration::getItems) .containsExactly( "R0", List.of( - AnalyzedCommit.builder().type("D1").category(ChangeCategory.DEPRECATED).level(SemVerChangeLevel.NONE).build(), - AnalyzedCommit.builder().type("R1").category(ChangeCategory.REMOVED).level(SemVerChangeLevel.NONE).build(), - AnalyzedCommit.builder().type("F1").category(ChangeCategory.FIXED).level(SemVerChangeLevel.PATCH).build(), - AnalyzedCommit.builder().type("S1").category(ChangeCategory.SECURITY).level(SemVerChangeLevel.PATCH).build(), - AnalyzedCommit.builder().type("A1").category(ChangeCategory.ADDED).level(SemVerChangeLevel.MINOR).build(), - AnalyzedCommit.builder().type("C1").category(ChangeCategory.CHANGED).level(SemVerChangeLevel.MINOR).build(), - AnalyzedCommit.builder().type("B1").category(ChangeCategory.OTHER).level(SemVerChangeLevel.MAJOR).build() + new AnalyzedCommit(null, null, null, null, null, "D1", ChangeCategory.DEPRECATED, null, null, SemVerChangeLevel.NONE, null), + new AnalyzedCommit(null, null, null, null, null, "R1", ChangeCategory.REMOVED, null, null, SemVerChangeLevel.NONE, null), + new AnalyzedCommit(null, null, null, null, null, "F1", ChangeCategory.FIXED, null, null, SemVerChangeLevel.PATCH, null), + new AnalyzedCommit(null, null, null, null, null, "S1", ChangeCategory.SECURITY, null, null, SemVerChangeLevel.PATCH, null), + new AnalyzedCommit(null, null, null, null, null, "A1", ChangeCategory.ADDED, null, null, SemVerChangeLevel.MINOR, null), + new AnalyzedCommit(null, null, null, null, null, "C1", ChangeCategory.CHANGED, null, null, SemVerChangeLevel.MINOR, null), + new AnalyzedCommit(null, null, null, null, null, "B1", ChangeCategory.OTHER, null, null, SemVerChangeLevel.MAJOR, null) ) ); } diff --git a/semver-analyzer-conventional/src/main/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzer.java b/semver-analyzer-conventional/src/main/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzer.java index 7750804..74e7c67 100644 --- a/semver-analyzer-conventional/src/main/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzer.java +++ b/semver-analyzer-conventional/src/main/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzer.java @@ -1,10 +1,13 @@ package io.github.sam42r.semver.analyzer; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; import io.github.sam42r.semver.analyzer.model.Configuration; -import io.github.sam42r.semver.analyzer.model.SemVerChangeLevel; -import io.github.sam42r.semver.scm.model.Commit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; import lombok.AccessLevel; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -13,6 +16,7 @@ import java.io.IOException; import java.io.StringReader; import java.io.UncheckedIOException; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; @@ -25,6 +29,7 @@ public class ConventionalCommitAnalyzer implements CommitAnalyzer { private static final String COMMIT_HEADER_PATTERN = "(?([a-z]*))(?(\\([a-z]*\\)))?(?(!))?(?(: .*))"; + private static final String COMMIT_FOOTER_PATTERN = "(?(#\\d*))"; private final Configuration configuration; @@ -34,23 +39,16 @@ public class ConventionalCommitAnalyzer implements CommitAnalyzer { } @Override - public @NonNull List analyzeCommits(@NonNull List commits) { - return commits.stream().map(this::analyzeCommit).toList(); + public @NonNull List analyzeCommits(@NonNull List commits, @NonNull Remote remote, ProviderSpec providerSpec) { + return commits.stream().map(commit -> analyzeCommit(commit, remote, providerSpec)).toList(); } - @SuppressWarnings("MismatchedQueryAndUpdateOfStringBuilder") - private AnalyzedCommit analyzeCommit(@NonNull Commit commit) { - var analyzedCommitBuilder = AnalyzedCommit.builder() - .id(commit.getId()) - .timestamp(commit.getTimestamp()) - .author(commit.getAuthor()) - .message(commit.getMessage()); - + private AnalyzedCommit analyzeCommit(@NonNull Commit commit, @NonNull Remote remote, ProviderSpec providerSpec) { var headerBuilder = new StringBuilder(); var bodyBuilder = new StringBuilder(); var footerBuilder = new StringBuilder(); - try (var reader = new BufferedReader(new StringReader(commit.getMessage()))) { + try (var reader = new BufferedReader(new StringReader(commit.message()))) { String line; int emptyLinesCounter = 0; while ((line = reader.readLine()) != null) { @@ -74,47 +72,54 @@ private AnalyzedCommit analyzeCommit(@NonNull Commit commit) { var header = headerBuilder.toString().trim(); var body = bodyBuilder.toString().trim(); var footer = footerBuilder.toString().trim(); - analyzedCommitBuilder - .header(header) - .body(body) - .footer(footer); - - if (!header.isEmpty()) { - var pattern = Pattern.compile(COMMIT_HEADER_PATTERN); - var matcher = pattern.matcher(header); - if (matcher.find()) { - var type = matcher.group("TYPE"); - var scope = Optional.ofNullable(matcher.group("SCOPE")) - .map(v -> v.replace("(", "")) - .map(v -> v.replace(")", "")) - .orElse(null); - var breaking = Optional.ofNullable(matcher.group("BREAKING")); - var description = matcher.group("DESCRIPTION").replaceFirst(":", "").trim(); - - analyzedCommitBuilder - .type(type) - .scope(scope) - .subject(description) - .category(getCategory(type)) - .level(breaking.isPresent() ? SemVerChangeLevel.MAJOR : getLevel(type)); - } - } - if (footer.contains("BREAKING CHANGE")) { - analyzedCommitBuilder.level(SemVerChangeLevel.MAJOR); - } + var headerMatcher = Pattern.compile(COMMIT_HEADER_PATTERN).matcher(header); - // TODO search for issues in footer + if (header.isEmpty() || !headerMatcher.find()) { + return new AnalyzedCommit(commit, null, null, null, null, null, null, null, null, null, null); + } - return analyzedCommitBuilder.build(); + var type = headerMatcher.group("TYPE"); + var scope = Optional.ofNullable(headerMatcher.group("SCOPE")) + .map(v -> v.replace("(", "")) + .map(v -> v.replace(")", "")) + .orElse(null); + var breaking = Optional.ofNullable(headerMatcher.group("BREAKING")); + var description = headerMatcher.group("DESCRIPTION").replaceFirst(":", "").trim(); + + var footerMatcher = Pattern.compile(COMMIT_FOOTER_PATTERN).matcher(footer); + + var refs = footerMatcher.find() ? + Optional.ofNullable(footerMatcher.group("REF")) + .map(v -> v.replace("#", "")) + .map(String::trim) + .map(List::of) + .orElse(Collections.emptyList()) : + Collections.emptyList(); + + return new AnalyzedCommit( + commit, + providerSpec.issueUrl(remote, commit.id()), + header, + body, + footer, + type, + getCategory(type), + scope, + description, + breaking.isPresent() || footer.contains("BREAKING CHANGE") ? SemVerChangeLevel.MAJOR : getLevel(type), + refs.stream() + .map(ref -> new Issue(ref, providerSpec.issueUrl(remote, ref))) + .toList() + ); } private ChangeCategory getCategory(String type) { if (type != null) { return configuration.getItems().stream() - .filter(v -> v.getType().equals(type)) + .filter(v -> v.type().equals(type)) .findAny() - .map(AnalyzedCommit::getCategory) + .map(AnalyzedCommit::category) .orElse(ChangeCategory.OTHER); } return ChangeCategory.OTHER; @@ -123,9 +128,9 @@ private ChangeCategory getCategory(String type) { private SemVerChangeLevel getLevel(String type) { if (type != null) { return configuration.getItems().stream() - .filter(v -> v.getType().equals(type)) + .filter(v -> v.type().equals(type)) .findAny() - .map(AnalyzedCommit::getLevel) + .map(AnalyzedCommit::level) .orElse(SemVerChangeLevel.NONE); } return SemVerChangeLevel.NONE; diff --git a/semver-analyzer-conventional/src/test/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzerTest.java b/semver-analyzer-conventional/src/test/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzerTest.java index 63c170c..4b9984f 100644 --- a/semver-analyzer-conventional/src/test/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzerTest.java +++ b/semver-analyzer-conventional/src/test/java/io/github/sam42r/semver/analyzer/ConventionalCommitAnalyzerTest.java @@ -1,9 +1,12 @@ package io.github.sam42r.semver.analyzer; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; -import io.github.sam42r.semver.analyzer.model.SemVerChangeLevel; -import io.github.sam42r.semver.scm.model.Commit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,103 +27,94 @@ void setup() { @Test void shouldFindFix() { var actual = uut.analyzeCommits(List.of( - Commit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(""" - fix(scm): set clean commit message - - * added scope for commit messages - - refs #42 - """) - .build() - )); + new Commit("42", Instant.EPOCH, "JUnit", + """ + fix(scm): set clean commit message + + * added scope for commit messages + + refs #42 + """) + ), + Remote.of("git@github.com:Sam42R/semver-maven-plugin.git"), + new ProviderSpec("%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s...%s") + ); assertThat(actual).containsExactly( - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(""" - fix(scm): set clean commit message - - * added scope for commit messages - - refs #42 - """) - .category(ChangeCategory.FIXED) - .level(SemVerChangeLevel.PATCH) - .header("fix(scm): set clean commit message") - .body("* added scope for commit messages") - .footer("refs #42") - .type("fix") - .scope("scm") - .subject("set clean commit message") - .build() + new AnalyzedCommit( + new Commit("42", Instant.EPOCH, "JUnit", + """ + fix(scm): set clean commit message + + * added scope for commit messages + + refs #42 + """), + "https://github.com/Sam42R/semver-maven-plugin/42", + "fix(scm): set clean commit message", + "* added scope for commit messages", + "refs #42", + "fix", + ChangeCategory.FIXED, + "scm", + "set clean commit message", + SemVerChangeLevel.PATCH, + List.of(new Issue("42", "https://github.com/Sam42R/semver-maven-plugin/42"))) ); } @Test void shouldFindBreakingChanges() { var actual = uut.analyzeCommits(List.of( - Commit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(""" - fix(scm): set clean commit message - - * added scope for commit messages - - BREAKING CHANGE: breaks everything - refs #42 - """) - .build(), - Commit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message("fix(scm)!: set clean commit message") - .build() - )); + new Commit("42", Instant.EPOCH, "JUnit", + """ + fix(scm): set clean commit message + + * added scope for commit messages + + BREAKING CHANGE: breaks everything + refs #42 + """), + new Commit("42", Instant.EPOCH, "JUnit", + "fix(scm)!: set clean commit message") + ), + Remote.of("git@github.com:Sam42R/semver-maven-plugin.git"), + new ProviderSpec("%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s...%s") + ); assertThat(actual).containsExactly( - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(""" - fix(scm): set clean commit message - - * added scope for commit messages - - refs #42 - """) - .category(ChangeCategory.FIXED) - .level(SemVerChangeLevel.MAJOR) - .header("fix(scm): set clean commit message") - .body("* added scope for commit messages") - .footer("BREAKING CHANGE: breaks everything%srefs #42".formatted(System.lineSeparator())) - .type("fix") - .scope("scm") - .subject("set clean commit message") - .build(), - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message("") - .category(ChangeCategory.FIXED) - .level(SemVerChangeLevel.MAJOR) - .header("fix(scm)!: set clean commit message") - .body("") - .footer("") - .type("fix") - .scope("scm") - .subject("set clean commit message") - .build() + new AnalyzedCommit( + new Commit("42", Instant.EPOCH, "JUnit", + """ + fix(scm): set clean commit message + + * added scope for commit messages + + BREAKING CHANGE: breaks everything + refs #42 + """), + "https://github.com/Sam42R/semver-maven-plugin/42", + "fix(scm): set clean commit message", + "* added scope for commit messages", + "BREAKING CHANGE: breaks everything%srefs #42".formatted(System.lineSeparator()), + "fix", + ChangeCategory.FIXED, + "scm", + "set clean commit message", + SemVerChangeLevel.MAJOR, + List.of(new Issue("42", "https://github.com/Sam42R/semver-maven-plugin/42"))), + new AnalyzedCommit( + new Commit("42", Instant.EPOCH, "JUnit", "fix(scm)!: set clean commit message"), + "https://github.com/Sam42R/semver-maven-plugin/42", + "fix(scm)!: set clean commit message", + "", + "", + "fix", + ChangeCategory.FIXED, + "scm", + "set clean commit message", + SemVerChangeLevel.MAJOR, + List.of()) ); } } diff --git a/semver-analyzer-gitmoji/src/main/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzer.java b/semver-analyzer-gitmoji/src/main/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzer.java index 34a871e..ed0ba03 100644 --- a/semver-analyzer-gitmoji/src/main/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzer.java +++ b/semver-analyzer-gitmoji/src/main/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzer.java @@ -1,14 +1,18 @@ package io.github.sam42r.semver.analyzer; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; import io.github.sam42r.semver.analyzer.model.Configuration; -import io.github.sam42r.semver.analyzer.model.SemVerChangeLevel; -import io.github.sam42r.semver.scm.model.Commit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; import lombok.AccessLevel; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; @@ -25,58 +29,60 @@ public class GitMojiCommitAnalyzer implements CommitAnalyzer { private final Configuration configuration; @Override - public @NonNull List analyzeCommits(@NonNull List commits) { - return commits.stream().map(this::analyzeCommit).toList(); + public @NonNull String generateReleaseCommitMessage(@NonNull String version) { + return "%s: bump version %s".formatted(configuration.getRelease(), version); } @Override - public @NonNull String generateReleaseCommitMessage(@NonNull String version) { - return "%s: bump version %s".formatted(configuration.getRelease(), version); + public @NonNull List analyzeCommits(@NonNull List commits, @NonNull Remote remote, ProviderSpec providerSpec) { + return commits.stream().map(commit -> analyzeCommit(commit, remote, providerSpec)).toList(); } - private AnalyzedCommit analyzeCommit(@NonNull Commit commit) { - var analyzedCommitBuilder = AnalyzedCommit.builder() - .id(commit.getId()) - .timestamp(commit.getTimestamp()) - .author(commit.getAuthor()) - .header(commit.getMessage().trim()) - .message(commit.getMessage()); + private AnalyzedCommit analyzeCommit(@NonNull Commit commit, @NonNull Remote remote, ProviderSpec providerSpec) { + var pattern = Pattern.compile(COMMIT_MESSAGE_PATTERN); + var matcher = pattern.matcher(commit.message()); - if (commit.getMessage().startsWith(":")) { - var pattern = Pattern.compile(COMMIT_MESSAGE_PATTERN); - var matcher = pattern.matcher(commit.getMessage()); - if (matcher.find()) { - var intention = matcher.group("INTENTION"); - var scope = Optional.ofNullable(matcher.group("SCOPE")) - .map(v -> v.replace("(", "")) - .map(v -> v.replace(")", "")) - .map(v -> v.replace(":", "")) - .map(String::trim) - .orElse(null); - var message = matcher.group("MESSAGE").trim(); - var ref = Optional.ofNullable(matcher.group("REF")) - .map(v -> v.replace("#", "")) - .map(String::trim) - .map(List::of) - .orElse(null); - analyzedCommitBuilder - .type(intention) - .scope(scope) - .subject(message) - .issues(ref) - .category(getCategory(intention)) - .level(getLevel(intention)); - } + if (!commit.message().startsWith(":") || !matcher.find()) { + return new AnalyzedCommit(commit, null, null, null, null, null, null, null, null, null, null); } - return analyzedCommitBuilder.build(); + + var intention = matcher.group("INTENTION"); + var scope = Optional.ofNullable(matcher.group("SCOPE")) + .map(v -> v.replace("(", "")) + .map(v -> v.replace(")", "")) + .map(v -> v.replace(":", "")) + .map(String::trim) + .orElse(null); + var message = matcher.group("MESSAGE").trim(); + var refs = Optional.ofNullable(matcher.group("REF")) + .map(v -> v.replace("#", "")) + .map(String::trim) + .map(List::of) + .orElse(Collections.emptyList()); + + return new AnalyzedCommit( + commit, + providerSpec.commitUrl(remote, commit.id()), + commit.message(), + null, + null, + intention, + getCategory(intention), + scope, + message, + getLevel(intention), + refs.stream() + .map(ref -> new Issue(ref, providerSpec.issueUrl(remote, ref))) + .toList() + ); } private ChangeCategory getCategory(String intention) { if (intention != null) { return configuration.getItems().stream() - .filter(v -> v.getType().equals(intention)) + .filter(v -> v.type().equals(intention)) .findAny() - .map(AnalyzedCommit::getCategory) + .map(AnalyzedCommit::category) .orElse(ChangeCategory.OTHER); } return ChangeCategory.OTHER; @@ -85,9 +91,9 @@ private ChangeCategory getCategory(String intention) { private SemVerChangeLevel getLevel(String intention) { if (intention != null) { return configuration.getItems().stream() - .filter(v -> v.getType().equals(intention)) + .filter(v -> v.type().equals(intention)) .findAny() - .map(AnalyzedCommit::getLevel) + .map(AnalyzedCommit::level) .orElse(SemVerChangeLevel.NONE); } return SemVerChangeLevel.NONE; diff --git a/semver-analyzer-gitmoji/src/test/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzerTest.java b/semver-analyzer-gitmoji/src/test/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzerTest.java index e1886db..cd2d0a7 100644 --- a/semver-analyzer-gitmoji/src/test/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzerTest.java +++ b/semver-analyzer-gitmoji/src/test/java/io/github/sam42r/semver/analyzer/GitMojiCommitAnalyzerTest.java @@ -1,9 +1,12 @@ package io.github.sam42r.semver.analyzer; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; -import io.github.sam42r.semver.analyzer.model.SemVerChangeLevel; -import io.github.sam42r.semver.scm.model.Commit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,99 +27,88 @@ void setup() { @Test void shouldFindFixAndFeat() { var actual = uut.analyzeCommits(List.of( - Commit.builder() - .id("42.1") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(":bug: (scm) set clean commit message #42") - .build(), - Commit.builder() - .id("42.2") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(":sparkles: add awesome things") - .build() - )); + new Commit("42.1", Instant.EPOCH, "JUnit", ":bug: (scm) set clean commit message #42"), + new Commit("42.2", Instant.EPOCH, "JUnit", ":sparkles: add awesome things") + ), + Remote.of("git@github.com:Sam42R/semver-maven-plugin.git"), + new ProviderSpec("%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s...%s") + ); assertThat(actual).containsExactlyInAnyOrder( - AnalyzedCommit.builder() - .id("42.1") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header(":bug: (scm) set clean commit message #42") - .message(":bug: (scm) set clean commit message #42") - .type(":bug:") - .scope("scm") - .subject("set clean commit message") - .issues(List.of("42")) - .category(ChangeCategory.FIXED) - .level(SemVerChangeLevel.PATCH) - .build(), - AnalyzedCommit.builder() - .id("42.2") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header(":sparkles: add awesome things") - .message(":sparkles: add awesome things") - .type(":sparkles:") - .subject("add awesome things") - .category(ChangeCategory.ADDED) - .level(SemVerChangeLevel.MINOR) - .build() + new AnalyzedCommit( + new Commit("42.1", Instant.EPOCH, "JUnit", ":bug: (scm) set clean commit message #42"), + "https://github.com/Sam42R/semver-maven-plugin/42.1", + ":bug: (scm) set clean commit message #42", + null, + null, + ":bug:", + ChangeCategory.FIXED, + "scm", + "set clean commit message", + SemVerChangeLevel.PATCH, + List.of(new Issue("42", "https://github.com/Sam42R/semver-maven-plugin/42"))), + new AnalyzedCommit( + new Commit("42.2", Instant.EPOCH, "JUnit", ":sparkles: add awesome things"), + "https://github.com/Sam42R/semver-maven-plugin/42.2", + ":sparkles: add awesome things", + null, + null, + ":sparkles:", + ChangeCategory.ADDED, + null, + "add awesome things", + SemVerChangeLevel.MINOR, + List.of()) ); } @Test void shouldFindBreaking() { var actual = uut.analyzeCommits(List.of( - Commit.builder() - .id("42.3") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(":boom: (void): break some glass #42") - .build() - )); + new Commit("42.3", Instant.EPOCH, "JUnit", ":boom: (void): break some glass #42") + ), + Remote.of("git@github.com:Sam42R/semver-maven-plugin.git"), + new ProviderSpec("%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s...%s") + ); assertThat(actual).containsExactly( - AnalyzedCommit.builder() - .id("42.3") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header(":boom: (void): break some glass #42") - .message(":boom: (void): break some glass #42") - .type(":boom:") - .scope("void") - .subject("break some glass") - .issues(List.of("42")) - .category(ChangeCategory.ADDED) - .level(SemVerChangeLevel.MAJOR) - .build() + new AnalyzedCommit( + new Commit("42.3", Instant.EPOCH, "JUnit", ":boom: (void): break some glass #42"), + "https://github.com/Sam42R/semver-maven-plugin/42.3", + ":boom: (void): break some glass #42", + null, + null, + ":boom:", + ChangeCategory.ADDED, + "void", + "break some glass", + SemVerChangeLevel.MAJOR, + List.of(new Issue("42", "https://github.com/Sam42R/semver-maven-plugin/42"))) ); } @Test void shouldFindOther() { var actual = uut.analyzeCommits(List.of( - Commit.builder() - .id("42.4") - .timestamp(Instant.EPOCH) - .author("JUnit") - .message(":white_check_mark: add test for something") - .build() - )); + new Commit("42.4", Instant.EPOCH, "JUnit", ":white_check_mark: add test for something") + ), + Remote.of("git@github.com:Sam42R/semver-maven-plugin.git"), + new ProviderSpec("%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s", "%s://%s/%s/%s/%s...%s") + ); assertThat(actual).containsExactly( - AnalyzedCommit.builder() - .id("42.4") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header(":white_check_mark: add test for something") - .message(":white_check_mark: add test for something") - .type(":white_check_mark:") - .subject("add test for something") - .category(ChangeCategory.OTHER) - .level(SemVerChangeLevel.NONE) - .build() + new AnalyzedCommit( + new Commit("42.4", Instant.EPOCH, "JUnit", ":white_check_mark: add test for something"), + "https://github.com/Sam42R/semver-maven-plugin/42.4", + ":white_check_mark: add test for something", + null, + null, + ":white_check_mark:", + ChangeCategory.OTHER, + null, + "add test for something", + SemVerChangeLevel.NONE, + List.of()) ); } } diff --git a/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/ChangelogRenderer.java b/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/ChangelogRenderer.java index d21d5f3..fda4cff 100644 --- a/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/ChangelogRenderer.java +++ b/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/ChangelogRenderer.java @@ -1,7 +1,7 @@ package io.github.sam42r.semver.changelog; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.changelog.model.VersionInfo; +import io.github.sam42r.semver.model.changelog.VersionInfo; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; import lombok.NonNull; import java.io.InputStream; diff --git a/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/model/VersionInfo.java b/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/model/VersionInfo.java deleted file mode 100644 index 8291cfe..0000000 --- a/semver-changelog-api/src/main/java/io/github/sam42r/semver/changelog/model/VersionInfo.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.github.sam42r.semver.changelog.model; - -public record VersionInfo(String version, String date, String message) { -} diff --git a/semver-changelog-html/src/main/java/io/github/sam42r/semver/changelog/HtmlRenderer.java b/semver-changelog-html/src/main/java/io/github/sam42r/semver/changelog/HtmlRenderer.java index 0b80a0e..3a485d7 100644 --- a/semver-changelog-html/src/main/java/io/github/sam42r/semver/changelog/HtmlRenderer.java +++ b/semver-changelog-html/src/main/java/io/github/sam42r/semver/changelog/HtmlRenderer.java @@ -1,7 +1,7 @@ package io.github.sam42r.semver.changelog; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.changelog.model.VersionInfo; +import io.github.sam42r.semver.model.changelog.VersionInfo; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.apache.commons.codec.digest.DigestUtils; diff --git a/semver-changelog-html/src/main/resources/templates/default/default.html b/semver-changelog-html/src/main/resources/templates/default/default.html index 72f82d2..fc0b6f3 100644 --- a/semver-changelog-html/src/main/resources/templates/default/default.html +++ b/semver-changelog-html/src/main/resources/templates/default/default.html @@ -20,9 +20,8 @@

Changelog

-

- v0.0.1 +

+ v0.0.1

    -
  • - Lorem ipsum +
  • + Lorem ipsum + +  () + + +   +
diff --git a/semver-changelog-html/src/main/resources/templates/missive/missive.html b/semver-changelog-html/src/main/resources/templates/missive/missive.html index 924483c..06ffe36 100644 --- a/semver-changelog-html/src/main/resources/templates/missive/missive.html +++ b/semver-changelog-html/src/main/resources/templates/missive/missive.html @@ -25,9 +25,7 @@

Changelog

- - v0.0.1 - + v0.0.1 (1970-01-01) @@ -62,9 +60,15 @@

Changelog

-
- Lorem ipsum + + Lorem ipsum + + () + + + +
diff --git a/semver-changelog-html/src/main/resources/templates/spinal/spinal.html b/semver-changelog-html/src/main/resources/templates/spinal/spinal.html index f3222cc..86d0eaf 100644 --- a/semver-changelog-html/src/main/resources/templates/spinal/spinal.html +++ b/semver-changelog-html/src/main/resources/templates/spinal/spinal.html @@ -24,9 +24,8 @@

-
- v0.0.1 +
+ v0.0.1
diff --git a/semver-changelog-html/src/test/java/io/github/sam42r/semver/changelog/HtmlRendererTest.java b/semver-changelog-html/src/test/java/io/github/sam42r/semver/changelog/HtmlRendererTest.java index e885cee..d8bfd2d 100644 --- a/semver-changelog-html/src/test/java/io/github/sam42r/semver/changelog/HtmlRendererTest.java +++ b/semver-changelog-html/src/test/java/io/github/sam42r/semver/changelog/HtmlRendererTest.java @@ -1,8 +1,12 @@ package io.github.sam42r.semver.changelog; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; -import io.github.sam42r.semver.changelog.model.VersionInfo; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.changelog.VersionInfo; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.scm.Commit; +import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -64,38 +68,76 @@ void shouldUpdateChangelog(String template) throws IOException { } private VersionInfo release(String version) { - return new VersionInfo(version, LocalDateTime.now().format(DateTimeFormatter.ISO_DATE), ""); + return new VersionInfo( + version, + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE), + "", + "https:///junit.org/test/v0.0.1...%s".formatted(version) + ); } private List analyzedCommits() { return List.of( - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header("feat(scm): Lorem ipsum") - .body("* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam") - .footer("refs #42") - .category(ChangeCategory.ADDED) - .build(), - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header("fix(scm): Lorem ipsum") - .body("* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam") - .footer("refs #42") - .category(ChangeCategory.FIXED) - .build(), - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header("chore(scm): Lorem ipsum") - .body("* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam") - .footer("refs #42") - .category(ChangeCategory.CHANGED) - .build() + new AnalyzedCommit( + new Commit(DigestUtils.sha256Hex("42.1"), Instant.EPOCH, "JUnit", + """ + feat(scm): Lorem ipsum + + * Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + + refs #42 + """), + null, + "feat(scm): Lorem ipsum", + "* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam", + "refs #42", + "feat", + ChangeCategory.ADDED, + "scm", + "Lorem ipsum", + SemVerChangeLevel.MINOR, + null + ), + new AnalyzedCommit( + new Commit(DigestUtils.sha256Hex("42.2"), Instant.EPOCH, "JUnit", + """ + fix(scm): Lorem ipsum + + * Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + + refs #42 + """), + null, + "fix(scm): Lorem ipsum", + "* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam", + "refs #42", + "fix", + ChangeCategory.FIXED, + "scm", + "Lorem ipsum", + SemVerChangeLevel.PATCH, + null + ), + new AnalyzedCommit( + new Commit(DigestUtils.sha256Hex("42.3"), Instant.EPOCH, "JUnit", + """ + chore(scm): Lorem ipsum + + * Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + + refs #42 + """), + "https://junit.org/test/%s".formatted(DigestUtils.sha256Hex("42.3")), + "chore(scm): Lorem ipsum", + "* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam", + "refs #42", + "chore", + ChangeCategory.CHANGED, + "scm", + "Lorem ipsum", + SemVerChangeLevel.NONE, + List.of(new Issue("42", "https://junit.org/test/42")) + ) ); } } diff --git a/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/MarkupRenderer.java b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/MarkupRenderer.java index 16e7f86..f60ec04 100644 --- a/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/MarkupRenderer.java +++ b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/MarkupRenderer.java @@ -1,9 +1,11 @@ package io.github.sam42r.semver.changelog; import com.github.mustachejava.DefaultMustacheFactory; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; -import io.github.sam42r.semver.changelog.model.VersionInfo; +import io.github.sam42r.semver.changelog.model.Link; +import io.github.sam42r.semver.changelog.model.RenderedCommit; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.changelog.VersionInfo; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.apache.commons.codec.digest.DigestUtils; @@ -12,6 +14,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; @@ -55,7 +58,7 @@ public class MarkupRenderer implements ChangelogRenderer { ) { var categorizedCommits = analyzedCommits.stream() .collect(Collectors.toMap( - AnalyzedCommit::getCategory, + AnalyzedCommit::category, List::of, (v1, v2) -> Stream.of(v1, v2).flatMap(List::stream).toList() )); @@ -65,25 +68,32 @@ public class MarkupRenderer implements ChangelogRenderer { context.put("release", versionInfo); context.put("hasAdded", categorizedCommits.containsKey(ChangeCategory.ADDED)); - context.put("added", categorizedCommits.get(ChangeCategory.ADDED)); + context.put("added", categorizedCommits.getOrDefault(ChangeCategory.ADDED, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasChanges", categorizedCommits.containsKey(ChangeCategory.CHANGED)); - context.put("changes", categorizedCommits.get(ChangeCategory.CHANGED)); + context.put("changes", categorizedCommits.getOrDefault(ChangeCategory.CHANGED, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasDeprecated", categorizedCommits.containsKey(ChangeCategory.DEPRECATED)); - context.put("deprecated", categorizedCommits.get(ChangeCategory.DEPRECATED)); + context.put("deprecated", categorizedCommits.getOrDefault(ChangeCategory.DEPRECATED, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasRemoved", categorizedCommits.containsKey(ChangeCategory.REMOVED)); - context.put("removed", categorizedCommits.get(ChangeCategory.REMOVED)); + context.put("removed", categorizedCommits.getOrDefault(ChangeCategory.REMOVED, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasPatches", categorizedCommits.containsKey(ChangeCategory.FIXED)); - context.put("patches", categorizedCommits.get(ChangeCategory.FIXED)); + context.put("patches", categorizedCommits.getOrDefault(ChangeCategory.FIXED, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasSecurity", categorizedCommits.containsKey(ChangeCategory.SECURITY)); - context.put("securities", categorizedCommits.get(ChangeCategory.SECURITY)); + context.put("securities", categorizedCommits.getOrDefault(ChangeCategory.SECURITY, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("hasOthers", categorizedCommits.containsKey(ChangeCategory.OTHER)); - context.put("others", categorizedCommits.get(ChangeCategory.OTHER)); + context.put("others", categorizedCommits.getOrDefault(ChangeCategory.OTHER, Collections.emptyList()) + .stream().map(this::renderedCommit).toList()); context.put("renderHeader", !alreadyExists); context.put("renderFooter", !alreadyExists); @@ -106,4 +116,33 @@ public class MarkupRenderer implements ChangelogRenderer { throw new UncheckedIOException(e); } } + + /** + * Transform {@link AnalyzedCommit} to Mustache compliant {@link RenderedCommit}.
+ * (Since Mustache is more or less logic less we have to use rendering optimized model) + * + * @param analyzedCommit the {@link AnalyzedCommit} to transform + * @return Mustache compliant {@link RenderedCommit} + */ + private RenderedCommit renderedCommit(AnalyzedCommit analyzedCommit) { + return new RenderedCommit( + analyzedCommit.header(), + analyzedCommit.url() == null || analyzedCommit.url().isBlank() ? + null : + new Link(trimToLength(analyzedCommit.commit().id(), 7), analyzedCommit.url()), + analyzedCommit.issues() == null || analyzedCommit.issues().isEmpty() ? + Collections.emptyList() : + analyzedCommit.issues().stream() + .filter(v -> v.url() != null && !v.url().isBlank()) + .map(v -> new Link(v.id(), v.url())) + .toList() + ); + } + + private String trimToLength(String string, int length) { + if (string != null && string.length() > length) { + return string.substring(0, length); + } + return string; + } } diff --git a/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/Link.java b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/Link.java new file mode 100644 index 0000000..3d4a51d --- /dev/null +++ b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/Link.java @@ -0,0 +1,4 @@ +package io.github.sam42r.semver.changelog.model; + +public record Link(String name, String href) { +} diff --git a/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/RenderedCommit.java b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/RenderedCommit.java new file mode 100644 index 0000000..1205f66 --- /dev/null +++ b/semver-changelog-markup/src/main/java/io/github/sam42r/semver/changelog/model/RenderedCommit.java @@ -0,0 +1,6 @@ +package io.github.sam42r.semver.changelog.model; + +import java.util.List; + +public record RenderedCommit(String message, Link reference, List issues) { +} diff --git a/semver-changelog-markup/src/main/resources/io/github/sam42r/semver/changelog/changelog.mustache b/semver-changelog-markup/src/main/resources/io/github/sam42r/semver/changelog/changelog.mustache index 2a34b52..0b1f78c 100644 --- a/semver-changelog-markup/src/main/resources/io/github/sam42r/semver/changelog/changelog.mustache +++ b/semver-changelog-markup/src/main/resources/io/github/sam42r/semver/changelog/changelog.mustache @@ -2,50 +2,50 @@ {{> header}} {{/renderHeader}} -## {{release.version}} - {{release.date}} +## {{#release.url}}[{{/release.url}}{{release.version}}{{#release.url}}]({{.}}){{/release.url}} - {{release.date}} {{release.message}} {{#hasAdded}} ### Added {{/hasAdded}} {{#added}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/added}} {{#hasChanges}} ### Changed {{/hasChanges}} {{#changes}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/changes}} {{#hasDeprecated}} ### Deprecated {{/hasDeprecated}} {{#deprecated}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/deprecated}} {{#hasRemoved}} ### Removed {{/hasRemoved}} {{#removed}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/removed}} {{#hasPatches}} ### Fixed {{/hasPatches}} {{#patches}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/patches}} {{#hasSecurity}} ### Security {{/hasSecurity}} {{#securities}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/securities}} {{#hasOthers}} ### Others {{/hasOthers}} {{#others}} -- {{header}} +- {{message}} {{#issues}}([#{{name}}]({{href}})){{/issues}} {{#reference}}[{{name}}]({{href}}){{/reference}} {{/others}} {{#renderFooter}} diff --git a/semver-changelog-markup/src/test/java/io/github/sam42r/semver/changelog/MarkupRendererTest.java b/semver-changelog-markup/src/test/java/io/github/sam42r/semver/changelog/MarkupRendererTest.java index e38191c..8e882fb 100644 --- a/semver-changelog-markup/src/test/java/io/github/sam42r/semver/changelog/MarkupRendererTest.java +++ b/semver-changelog-markup/src/test/java/io/github/sam42r/semver/changelog/MarkupRendererTest.java @@ -1,8 +1,12 @@ package io.github.sam42r.semver.changelog; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; -import io.github.sam42r.semver.analyzer.model.ChangeCategory; -import io.github.sam42r.semver.changelog.model.VersionInfo; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.analyze.ChangeCategory; +import io.github.sam42r.semver.model.analyze.Issue; +import io.github.sam42r.semver.model.analyze.SemVerChangeLevel; +import io.github.sam42r.semver.model.changelog.VersionInfo; +import io.github.sam42r.semver.model.scm.Commit; +import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -37,7 +41,7 @@ void shouldCreateChangelogFull(@TempDir Path tempDir) throws IOException { assertThat(actual).asString(StandardCharsets.UTF_8) .startsWith("# Changelog") - .contains("## v1.0.0 - %s".formatted(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE))) + .contains("## [v1.0.0](https:///junit.org/test/v0.0.1...v1.0.0) - %s".formatted(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE))) .contains("- fix(scm): set clean commit message") .contains("## Disclaimer"); } @@ -50,15 +54,15 @@ void shouldUpdateChangelog(@TempDir Path tempDir) throws IOException { changelog, """ # Changelog - + header text - + - + ## v0.9.0 - 2024-01-01 - + ## Disclaimer - + footer text """, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING @@ -69,27 +73,43 @@ void shouldUpdateChangelog(@TempDir Path tempDir) throws IOException { assertThat(actual).asString(StandardCharsets.UTF_8) .startsWith("# Changelog") - .contains("## v1.0.0 - %s".formatted(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE))) + .contains("## [v1.0.0](https:///junit.org/test/v0.0.1...v1.0.0) - %s".formatted(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE))) .contains("## v0.9.0 - 2024-01-01") .contains("## Disclaimer"); } } private VersionInfo release(String version) { - return new VersionInfo(version, LocalDateTime.now().format(DateTimeFormatter.ISO_DATE), ""); + return new VersionInfo( + version, + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE), + "", + "https:///junit.org/test/v0.0.1...%s".formatted(version) + ); } private List analyzedCommits() { return List.of( - AnalyzedCommit.builder() - .id("42") - .timestamp(Instant.EPOCH) - .author("JUnit") - .header("fix(scm): set clean commit message") - .body("* added scope for commit messages") - .footer("refs #42") - .category(ChangeCategory.FIXED) - .build() + new AnalyzedCommit( + new Commit(DigestUtils.sha256Hex("42"), Instant.EPOCH, "JUnit", + """ + fix(scm): set clean commit message + + * added scope for commit messages + + refs #42 + """), + "https://junit.org/test/%s".formatted(DigestUtils.sha256Hex("42")), + "fix(scm): set clean commit message", + "* added scope for commit messages", + "refs #42", + "fix", + ChangeCategory.FIXED, + "scm", + "set clean commit message", + SemVerChangeLevel.PATCH, + List.of(new Issue("42", "https://junit.org/test/42")) + ) ); } } diff --git a/semver-maven-model/pom.xml b/semver-maven-model/pom.xml new file mode 100644 index 0000000..3b274f6 --- /dev/null +++ b/semver-maven-model/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + io.github.sam42r + semver-parent + 1.8.0 + + + semver-maven-model + ${project.groupId}:${project.artifactId} + + + 19 + 19 + UTF-8 + + + + + org.projectlombok + lombok + provided + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + + \ No newline at end of file diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/AnalyzedCommit.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/AnalyzedCommit.java new file mode 100644 index 0000000..78b1147 --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/AnalyzedCommit.java @@ -0,0 +1,42 @@ +package io.github.sam42r.semver.model.analyze; + +import io.github.sam42r.semver.model.scm.Commit; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Analyzed {@link Commit} based on commit-message analysis. + * + * @param commit scm {@link Commit} + * @param url provider specific commit URL + * @param header commit header + * @param body commit body + * @param footer commit footer + * @param type commit type + * @param category commit {@link ChangeCategory} + * @param scope commit scope + * @param subject commit subject + * @param level commit {@link SemVerChangeLevel} + * @param issues list of referenced {@link Issue}'s + */ +public record AnalyzedCommit( + Commit commit, + String url, + String header, + String body, + String footer, + String type, + ChangeCategory category, + String scope, + String subject, + SemVerChangeLevel level, + List issues +) { + public static final Predicate isBugfix = analyzedCommit -> + SemVerChangeLevel.PATCH.equals(analyzedCommit.level()); + public static final Predicate isFeature = analyzedCommit -> + SemVerChangeLevel.MINOR.equals(analyzedCommit.level()); + public static final Predicate isBreaking = analyzedCommit -> + SemVerChangeLevel.MAJOR.equals(analyzedCommit.level()); +} diff --git a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/ChangeCategory.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/ChangeCategory.java similarity index 66% rename from semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/ChangeCategory.java rename to semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/ChangeCategory.java index 28c26e0..5949a8d 100644 --- a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/ChangeCategory.java +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/ChangeCategory.java @@ -1,4 +1,4 @@ -package io.github.sam42r.semver.analyzer.model; +package io.github.sam42r.semver.model.analyze; public enum ChangeCategory { ADDED, CHANGED, DEPRECATED, REMOVED, FIXED, SECURITY, OTHER; diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/Issue.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/Issue.java new file mode 100644 index 0000000..a3801fc --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/Issue.java @@ -0,0 +1,4 @@ +package io.github.sam42r.semver.model.analyze; + +public record Issue(String id, String url) { +} diff --git a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/SemVerChangeLevel.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/SemVerChangeLevel.java similarity index 88% rename from semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/SemVerChangeLevel.java rename to semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/SemVerChangeLevel.java index d1e7503..49e0ec7 100644 --- a/semver-analyzer-api/src/main/java/io/github/sam42r/semver/analyzer/model/SemVerChangeLevel.java +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/analyze/SemVerChangeLevel.java @@ -1,4 +1,4 @@ -package io.github.sam42r.semver.analyzer.model; +package io.github.sam42r.semver.model.analyze; /** * The semantic versioning change level. diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/changelog/VersionInfo.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/changelog/VersionInfo.java new file mode 100644 index 0000000..ca46e8e --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/changelog/VersionInfo.java @@ -0,0 +1,4 @@ +package io.github.sam42r.semver.model.changelog; + +public record VersionInfo(String version, String date, String message, String url) { +} diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ProviderSpec.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ProviderSpec.java new file mode 100644 index 0000000..002f67d --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ProviderSpec.java @@ -0,0 +1,18 @@ +package io.github.sam42r.semver.model.release; + +import io.github.sam42r.semver.model.scm.Remote; + +public record ProviderSpec(String issuePattern, String commitPattern, String comparePattern) { + + public String issueUrl(Remote remote, String id) { + return issuePattern().formatted(remote.scheme(), remote.host(), remote.group(), remote.project(), id); + } + + public String commitUrl(Remote remote, String id) { + return commitPattern().formatted(remote.scheme(), remote.host(), remote.group(), remote.project(), id); + } + + public String compareUrl(Remote remote, String first, String second) { + return comparePattern().formatted(remote.scheme(), remote.host(), remote.group(), remote.project(), first, second); + } +} diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ReleaseInfo.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ReleaseInfo.java new file mode 100644 index 0000000..eea7b1f --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/release/ReleaseInfo.java @@ -0,0 +1,6 @@ +package io.github.sam42r.semver.model.release; + +import java.time.LocalDateTime; + +public record ReleaseInfo(String tagName, String name, String description, LocalDateTime time) { +} diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Commit.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Commit.java new file mode 100644 index 0000000..93365ff --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Commit.java @@ -0,0 +1,6 @@ +package io.github.sam42r.semver.model.scm; + +import java.time.Instant; + +public record Commit(String id, Instant timestamp, String author, String message) { +} diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/util/RemoteUtil.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Remote.java similarity index 50% rename from semver-scm-api/src/main/java/io/github/sam42r/semver/scm/util/RemoteUtil.java rename to semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Remote.java index e968e10..1cfb7a5 100644 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/util/RemoteUtil.java +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Remote.java @@ -1,20 +1,14 @@ -package io.github.sam42r.semver.scm.util; - -import io.github.sam42r.semver.scm.model.Remote; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.NonNull; +package io.github.sam42r.semver.model.scm; import java.net.URI; -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class RemoteUtil { +public record Remote(String url, String scheme, String host, String group, String project) { - public static @NonNull Remote parseUrl(@NonNull String url) { - return url.startsWith("http") ? parseHttpRemoteUrl(url) : parseSshRemoteUrl(url); + public static Remote of(String url) { + return url.startsWith("http") ? parseHttpUrl(url) : parseSshUrl(url); } - private static @NonNull Remote parseHttpRemoteUrl(@NonNull String url) { + private static Remote parseHttpUrl(String url) { var uri = URI.create(url); var lastSlashIndex = uri.getPath().lastIndexOf("/"); @@ -28,16 +22,10 @@ public final class RemoteUtil { var hostAndPort = uri.getHost().concat(uri.getPort() > 0 ? ":%d".formatted(uri.getPort()) : ""); - return Remote.builder() - .url(url) - .scheme(uri.getScheme()) - .host(hostAndPort) - .group(group) - .project(project) - .build(); + return new Remote(url, uri.getScheme(), hostAndPort, group, project); } - private static @NonNull Remote parseSshRemoteUrl(@NonNull String url) { + private static Remote parseSshUrl(String url) { var startIndex = url.contains("@") ? url.indexOf("@") + 1 : 0; var lastColonIndex = url.lastIndexOf(":"); @@ -53,12 +41,6 @@ public final class RemoteUtil { project = project.substring(0, project.lastIndexOf(".")); } - return Remote.builder() - .url(url) - .scheme("https") - .host(hostAndPort) - .group(group) - .project(project) - .build(); + return new Remote(url, "https", hostAndPort, group, project); } } diff --git a/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Tag.java b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Tag.java new file mode 100644 index 0000000..f310b9b --- /dev/null +++ b/semver-maven-model/src/main/java/io/github/sam42r/semver/model/scm/Tag.java @@ -0,0 +1,4 @@ +package io.github.sam42r.semver.model.scm; + +public record Tag(String name, String commitId) { +} diff --git a/semver-maven-model/src/test/java/io/github/sam42r/semver/model/scm/RemoteTest.java b/semver-maven-model/src/test/java/io/github/sam42r/semver/model/scm/RemoteTest.java new file mode 100644 index 0000000..0813a1d --- /dev/null +++ b/semver-maven-model/src/test/java/io/github/sam42r/semver/model/scm/RemoteTest.java @@ -0,0 +1,33 @@ +package io.github.sam42r.semver.model.scm; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class RemoteTest { + + @ParameterizedTest + @ValueSource(strings = { + "git@github.com:JUnit/test.git", + "https://github.com/JUnit/test.git" + }) + void shouldReadRemoteGithub(String url) { + var actual = Remote.of(url); + + assertThat(actual).isEqualTo( + new Remote(url, "https", "github.com", "JUnit", "test")); + } + + @ParameterizedTest + @ValueSource(strings = { + "git@gitlab.local:10022:JUnit/subgroup/test.git", + "https://gitlab.local:10022/JUnit/subgroup/test.git" + }) + void shouldReadRemoteGitlab(String url) { + var actual = Remote.of(url); + + assertThat(actual).isEqualTo( + new Remote(url, "https", "gitlab.local:10022", "JUnit/subgroup", "test")); + } +} diff --git a/semver-maven-plugin/pom.xml b/semver-maven-plugin/pom.xml index 26f3ef4..6d52e34 100644 --- a/semver-maven-plugin/pom.xml +++ b/semver-maven-plugin/pom.xml @@ -22,6 +22,12 @@ + + io.github.sam42r + semver-maven-model + ${project.version} + + io.github.sam42r semver-scm-api diff --git a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/SemanticReleaseMojo.java b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/SemanticReleaseMojo.java index eacb425..8d265f6 100644 --- a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/SemanticReleaseMojo.java +++ b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/SemanticReleaseMojo.java @@ -3,19 +3,20 @@ import io.github.sam24r.semver.release.ReleaseException; import io.github.sam24r.semver.release.ReleasePublisher; import io.github.sam24r.semver.release.ReleasePublisherFactory; -import io.github.sam24r.semver.release.model.ReleaseInfo; import io.github.sam42r.semver.analyzer.CommitAnalyzer; import io.github.sam42r.semver.analyzer.CommitAnalyzerFactory; -import io.github.sam42r.semver.analyzer.model.AnalyzedCommit; import io.github.sam42r.semver.changelog.ChangelogRenderer; import io.github.sam42r.semver.changelog.ChangelogRendererFactory; -import io.github.sam42r.semver.changelog.model.VersionInfo; import io.github.sam42r.semver.model.Version; +import io.github.sam42r.semver.model.analyze.AnalyzedCommit; +import io.github.sam42r.semver.model.changelog.VersionInfo; +import io.github.sam42r.semver.model.release.ReleaseInfo; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; import io.github.sam42r.semver.scm.SCMException; import io.github.sam42r.semver.scm.SCMProvider; import io.github.sam42r.semver.scm.SCMProviderFactory; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Tag; import io.github.sam42r.semver.util.PomHelper; import io.github.sam42r.semver.util.TagVersionComparator; import lombok.NonNull; @@ -131,7 +132,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { .orElse(Version.of(0, 0, 0, tagFormat)); getLog().debug("Actual version: '%s'".formatted(latestVersion.toString())); - var analyzedCommits = analyzeCommits(scmProvider, commitAnalyzer, latestCommit); + var analyzedCommits = analyzeCommits(scmProvider, commitAnalyzer, releasePublisher, latestCommit); var majorCount = analyzedCommits.stream().filter(AnalyzedCommit.isBreaking).count(); var minorCount = analyzedCommits.stream().filter(AnalyzedCommit.isFeature).count(); @@ -146,26 +147,40 @@ public void execute() throws MojoExecutionException, MojoFailureException { } else { getLog().info("Continue with '%s' release".formatted(nextVersionType.name())); - latestVersion.increment(nextVersionType); - getLog().debug("Release version: '%s'".formatted(latestVersion.toString())); + var nextVersion = Version.of(latestVersion); + nextVersion.increment(nextVersionType); + + getLog().debug("Release version: '%s'".formatted(nextVersion.toString())); + + getLog().debug("Writing release notes to 'Changelog.md' for version '%s'".formatted(nextVersion.toString())); + final Remote remote; + try { + remote = scmProvider.getRemote(); + } catch (SCMException e) { + throw new MojoExecutionException(e); + } - getLog().debug("Writing release notes to 'Changelog.md' for version '%s'".formatted(latestVersion.toString())); var versionInfo = new VersionInfo( - latestVersion.toString(), + nextVersion.toString(), LocalDateTime.now().format(DateTimeFormatter.ISO_DATE), - "" // TODO read docs(changelog) commits and add as release description + "", // TODO read docs(changelog) commits and add as release description + releasePublisher.providerSpec().compareUrl( + remote, + latestVersion.toTag(), + nextVersion.toTag() + ) ); var notes = generateNotes(projectBaseDirectory, changelogRenderer, versionInfo, analyzedCommits); - getLog().debug("Setting project version in '%s' to '%s'".formatted(POM, latestVersion.toString())); + getLog().debug("Setting project version in '%s' to '%s'".formatted(POM, nextVersion.toString())); var pomXml = projectBaseDirectory.resolve(POM); - PomHelper.changeVersion(pomXml, latestVersion.toString()); + PomHelper.changeVersion(pomXml, nextVersion.toString()); var modules = isModule(project) ? project.getParent().getModules() : project.getModules(); var modulePomsXml = new ArrayList(); for (var module : modules) { var modulePomXml = projectBaseDirectory.resolve(module).resolve(POM); - PomHelper.changeParentVersion(modulePomXml, latestVersion.toString()); + PomHelper.changeParentVersion(modulePomXml, nextVersion.toString()); modulePomsXml.add(modulePomXml); } @@ -178,16 +193,16 @@ public void execute() throws MojoExecutionException, MojoFailureException { scmProvider.addFile(modulePomXml); } - scmProvider.commit(commitAnalyzer.generateReleaseCommitMessage(latestVersion.toString())); + scmProvider.commit(commitAnalyzer.generateReleaseCommitMessage(nextVersion.toString())); - createTag(scmProvider, latestVersion); + createTag(scmProvider, nextVersion); if (scm.isPush()) { publish(scmProvider); } if (release.isPublish()) { - notify(scmProvider, releasePublisher, latestVersion); + notify(scmProvider, releasePublisher, nextVersion); } } catch (SCMException e) { throw new MojoExecutionException(e); @@ -236,11 +251,11 @@ private LatestReleaseInfo getLatestRelease(SCMProvider scmProvider) throws MojoE var commits = scmProvider.readCommits(null); var latestTagOpt = tags.max(new TagVersionComparator(tagFormat)); - var latestCommitOpt = latestTagOpt.map(Tag::getCommitId) - .or(() -> commits.min(Comparator.comparing(Commit::getTimestamp)).map(Commit::getId)); + var latestCommitOpt = latestTagOpt.map(Tag::commitId) + .or(() -> commits.min(Comparator.comparing(Commit::timestamp)).map(Commit::id)); return new LatestReleaseInfo( - latestTagOpt.map(Tag::getName), + latestTagOpt.map(Tag::name), latestCommitOpt ); } catch (SCMException e) { @@ -251,11 +266,12 @@ private LatestReleaseInfo getLatestRelease(SCMProvider scmProvider) throws MojoE private List analyzeCommits( SCMProvider scmProvider, CommitAnalyzer commitAnalyzer, + ReleasePublisher releasePublisher, String latestCommit ) throws MojoExecutionException { try { var commits = scmProvider.readCommits(latestCommit); - return commitAnalyzer.analyzeCommits(commits.toList()); + return commitAnalyzer.analyzeCommits(commits.toList(), scmProvider.getRemote(), releasePublisher.providerSpec()); } catch (SCMException e) { throw new MojoExecutionException(e.getMessage(), e.getCause()); } @@ -320,16 +336,16 @@ private void notify( var remote = scmProvider.getRemote(); releasePublisher.publish( - remote.getScheme(), - remote.getHost(), - remote.getGroup(), - remote.getProject(), - ReleaseInfo.builder() - .time(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)) - .tagName(version.toTag()) - .name(version.toString()) - //.description("TODO") - .build() + remote.scheme(), + remote.host(), + remote.group(), + remote.project(), + new ReleaseInfo( + version.toTag(), + version.toString(), + null, // TODO + LocalDateTime.now().truncatedTo(ChronoUnit.DAYS) + ) ); } catch (SCMException | ReleaseException e) { throw new MojoExecutionException(e.getMessage(), e.getCause()); diff --git a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/model/Version.java b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/model/Version.java index 3e8e1f0..e158c6d 100644 --- a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/model/Version.java +++ b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/model/Version.java @@ -74,6 +74,10 @@ public static Version of(@NonNull int major, int minor, int patch, String tagFor return new Version(major, minor, patch, tagFormat); } + public static Version of(Version original) { + return Version.of(original.getMajor(), original.getMinor(), original.getPatch(), original.getTagFormat()); + } + public void increment(@NonNull Type type) { if (Type.MAJOR.equals(type)) { this.major++; diff --git a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/util/TagVersionComparator.java b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/util/TagVersionComparator.java index 3d9007c..a6ae38d 100644 --- a/semver-maven-plugin/src/main/java/io/github/sam42r/semver/util/TagVersionComparator.java +++ b/semver-maven-plugin/src/main/java/io/github/sam42r/semver/util/TagVersionComparator.java @@ -1,7 +1,7 @@ package io.github.sam42r.semver.util; import io.github.sam42r.semver.model.Version; -import io.github.sam42r.semver.scm.model.Tag; +import io.github.sam42r.semver.model.scm.Tag; import lombok.RequiredArgsConstructor; import java.util.Comparator; @@ -17,8 +17,8 @@ public int compare(Tag o1, Tag o2) { .thenComparingInt(Version::getMinor) .thenComparingInt(Version::getPatch) .compare( - Version.of(o1.getName(), tagFormat), - Version.of(o2.getName(), tagFormat) + Version.of(o1.name(), tagFormat), + Version.of(o2.name(), tagFormat) ); } } diff --git a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/SemanticReleaseMojoTest.java b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/SemanticReleaseMojoTest.java index 71f03fb..8e62369 100644 --- a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/SemanticReleaseMojoTest.java +++ b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/SemanticReleaseMojoTest.java @@ -12,12 +12,14 @@ import org.apache.maven.project.MavenProject; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.transport.URIish; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.IOException; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; @@ -72,7 +74,7 @@ void setup() throws IOException { @Test @SuppressWarnings("unchecked") - void shouldFindNoLatestRelease() throws GitAPIException, MojoExecutionException, MojoFailureException { + void shouldFindNoLatestRelease() throws GitAPIException, MojoExecutionException, MojoFailureException, URISyntaxException { try (var git = initializeGitRepository(tmp)) { git.add().addFilepattern("pom.xml").call(); git.commit().setMessage("Initial commit").call(); @@ -85,7 +87,7 @@ void shouldFindNoLatestRelease() throws GitAPIException, MojoExecutionException, @Test @SuppressWarnings("unchecked") - void shouldFindLatestRelease() throws IOException, GitAPIException, MojoExecutionException, MojoFailureException { + void shouldFindLatestRelease() throws IOException, GitAPIException, MojoExecutionException, MojoFailureException, URISyntaxException { try (var git = initializeGitRepository(tmp)) { git.add().addFilepattern("pom.xml").call(); git.commit().setMessage("Initial commit").call(); @@ -102,7 +104,7 @@ void shouldFindLatestRelease() throws IOException, GitAPIException, MojoExecutio } @Test - void shouldThrowWithEmptyGitRepository() throws GitAPIException { + void shouldThrowWithEmptyGitRepository() throws GitAPIException, URISyntaxException { try (var ignored = initializeGitRepository(tmp)) { assertThatThrownBy(() -> uut.execute()) .isInstanceOf(MojoExecutionException.class) @@ -117,8 +119,17 @@ void shouldThrowWithMissingGitRepository() { .hasMessageStartingWith("Could not find git configuration"); } - private Git initializeGitRepository(Path path) throws GitAPIException { - return Git.init().setDirectory(path.toFile()).call(); + private Git initializeGitRepository(Path path) throws GitAPIException, URISyntaxException { + var git = Git.init() + .setDirectory(path.toFile()) + .call(); + + git.remoteAdd() + .setName("origin") + .setUri(new URIish("https://junit.org/test/project")) + .call(); + + return git; } private Path createFile(Path path, String filename, String content) throws IOException { diff --git a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java index 9723ad0..c80b279 100644 --- a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java +++ b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java @@ -13,6 +13,7 @@ public class BundleHelper { private static final String[] MODULES = new String[]{ + "semver-maven-model", "semver-analyzer-api", "semver-analyzer-conventional", "semver-analyzer-gitmoji", diff --git a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/TagVersionComparatorTest.java b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/TagVersionComparatorTest.java index d8e800e..ab2f167 100644 --- a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/TagVersionComparatorTest.java +++ b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/TagVersionComparatorTest.java @@ -1,7 +1,7 @@ package io.github.sam42r.semver.util; import io.github.sam42r.semver.model.Version; -import io.github.sam42r.semver.scm.model.Tag; +import io.github.sam42r.semver.model.scm.Tag; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,18 +22,18 @@ void setUp() { @Test void shouldGetMax() { var tags = List.of( - Tag.builder().name("v0.10.0").build(), - Tag.builder().name("v0.9.0").build(), - Tag.builder().name("v0.1.0").build(), - Tag.builder().name("v0.0.10").build(), - Tag.builder().name("v0.0.9").build(), - Tag.builder().name("v0.0.1").build() + new Tag("v0.10.0", null), + new Tag("v0.9.0", null), + new Tag("v0.1.0", null), + new Tag("v0.0.10", null), + new Tag("v0.0.9", null), + new Tag("v0.0.1", null) ); - var alphabeticMax = tags.stream().max(Comparator.comparing(Tag::getName)); - assertThat(alphabeticMax).contains(Tag.builder().name("v0.9.0").build()); + var alphabeticMax = tags.stream().max(Comparator.comparing(Tag::name)); + assertThat(alphabeticMax).contains(new Tag("v0.9.0", null)); var semanticVersionMax = tags.stream().max(uut); - assertThat(semanticVersionMax).contains(Tag.builder().name("v0.10.0").build()); + assertThat(semanticVersionMax).contains(new Tag("v0.10.0", null)); } } diff --git a/semver-release-api/pom.xml b/semver-release-api/pom.xml index 8fd16fb..72a4292 100644 --- a/semver-release-api/pom.xml +++ b/semver-release-api/pom.xml @@ -20,6 +20,11 @@ + + io.github.sam42r + semver-maven-model + ${project.version} + org.projectlombok lombok diff --git a/semver-release-api/src/main/java/io/github/sam24r/semver/release/ReleasePublisher.java b/semver-release-api/src/main/java/io/github/sam24r/semver/release/ReleasePublisher.java index 42e4f34..df970c1 100644 --- a/semver-release-api/src/main/java/io/github/sam24r/semver/release/ReleasePublisher.java +++ b/semver-release-api/src/main/java/io/github/sam24r/semver/release/ReleasePublisher.java @@ -1,6 +1,7 @@ package io.github.sam24r.semver.release; -import io.github.sam24r.semver.release.model.ReleaseInfo; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.release.ReleaseInfo; import lombok.NonNull; public interface ReleasePublisher { @@ -12,4 +13,6 @@ void publish( @NonNull String project, @NonNull ReleaseInfo releaseInfo ) throws ReleaseException; + + ProviderSpec providerSpec(); } diff --git a/semver-release-api/src/main/java/io/github/sam24r/semver/release/model/ReleaseInfo.java b/semver-release-api/src/main/java/io/github/sam24r/semver/release/model/ReleaseInfo.java deleted file mode 100644 index 5ae8ace..0000000 --- a/semver-release-api/src/main/java/io/github/sam24r/semver/release/model/ReleaseInfo.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.sam24r.semver.release.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ReleaseInfo { - - private String tagName; - private String name; - private String description; - private LocalDateTime time; -} diff --git a/semver-release-github/src/main/java/io/github/sam42r/semver/release/DefaultGithubPublisher.java b/semver-release-github/src/main/java/io/github/sam42r/semver/release/DefaultGithubPublisher.java index a250696..186abe2 100644 --- a/semver-release-github/src/main/java/io/github/sam42r/semver/release/DefaultGithubPublisher.java +++ b/semver-release-github/src/main/java/io/github/sam42r/semver/release/DefaultGithubPublisher.java @@ -2,7 +2,8 @@ import io.github.sam24r.semver.release.ReleaseException; import io.github.sam24r.semver.release.ReleasePublisher; -import io.github.sam24r.semver.release.model.ReleaseInfo; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.release.ReleaseInfo; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -39,9 +40,9 @@ public void publish( var repository = gitHub.getRepository("%s/%s".formatted(group, project)); - var release = repository.createRelease(releaseInfo.getTagName()) - .name(releaseInfo.getName()) - .body(releaseInfo.getDescription()) + var release = repository.createRelease(releaseInfo.tagName()) + .name(releaseInfo.name()) + .body(releaseInfo.description()) .create(); log.debug("Released {}", release.getHtmlUrl()); @@ -49,4 +50,13 @@ public void publish( throw new ReleaseException(e); } } + + @Override + public ProviderSpec providerSpec() { + return new ProviderSpec( + "%s://%s/%s/%s/issues/%s", + "%s://%s/%s/%s/commit/%s", + "%s://%s/%s/%s/compare/%s...%s" + ); + } } diff --git a/semver-release-github/src/test/java/io/github/sam42r/semver/release/GithubPublisherTest.java b/semver-release-github/src/test/java/io/github/sam42r/semver/release/GithubPublisherTest.java index 33c2c62..10805e5 100644 --- a/semver-release-github/src/test/java/io/github/sam42r/semver/release/GithubPublisherTest.java +++ b/semver-release-github/src/test/java/io/github/sam42r/semver/release/GithubPublisherTest.java @@ -2,7 +2,7 @@ import io.github.sam24r.semver.release.ReleaseException; import io.github.sam24r.semver.release.ReleasePublisher; -import io.github.sam24r.semver.release.model.ReleaseInfo; +import io.github.sam42r.semver.model.release.ReleaseInfo; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -84,12 +84,12 @@ void shouldCreateRelease(MockServerClient client) throws ReleaseException { """) ); - var release = ReleaseInfo.builder() - .tagName("v1.0.0") - .name("v1.0.0") - .description("# Release v1.0.0") - .time(LocalDateTime.now()) - .build(); + var release = new ReleaseInfo( + "v1.0.0", + "v1.0.0", + "# Release v1.0.0", + LocalDateTime.now() + ); uut.publish( "http", @@ -150,12 +150,12 @@ void shouldThrowOnHttp422(MockServerClient client) { .withBody("endpoint has been spammed") ); - var release = ReleaseInfo.builder() - .tagName("v1.0.0") - .name("v1.0.0") - .description("# Release v1.0.0") - .time(LocalDateTime.now()) - .build(); + var release = new ReleaseInfo( + "v1.0.0", + "v1.0.0", + "# Release v1.0.0", + LocalDateTime.now() + ); assertThatThrownBy(() -> uut.publish( "http", @@ -181,7 +181,7 @@ void shouldThrowOnHttp422(MockServerClient client) { "http,localhost:42,JUnit,,project" }) void shouldThrowOnNullValue(String scheme, String instance, String group, String project, String field) { - var releaseInfo = ReleaseInfo.builder().build(); + var releaseInfo = new ReleaseInfo(null,null,null,null); assertThatThrownBy(() -> uut.publish(scheme, instance, group, project, releaseInfo)) .isInstanceOf(NullPointerException.class) .hasMessage("%s is marked non-null but is null".formatted(field)); diff --git a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java index 68ba331..c621e1f 100644 --- a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java +++ b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.github.sam24r.semver.release.ReleaseException; import io.github.sam24r.semver.release.ReleasePublisher; -import io.github.sam24r.semver.release.model.ReleaseInfo; +import io.github.sam42r.semver.model.release.ProviderSpec; +import io.github.sam42r.semver.model.release.ReleaseInfo; import io.github.sam42r.semver.release.model.GitlabRelease; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -69,9 +70,18 @@ public void publish( private GitlabRelease generatePayload(ReleaseInfo releaseInfo) { return GitlabRelease.builder() - .tagName(releaseInfo.getTagName()) - .name(releaseInfo.getName()) - .description(releaseInfo.getDescription()) + .tagName(releaseInfo.tagName()) + .name(releaseInfo.name()) + .description(releaseInfo.description()) .build(); } + + @Override + public ProviderSpec providerSpec() { + return new ProviderSpec( + "%s://%s/%s/%s/-/issues/%s", + "%s://%s/%s/%s@%s", + "%s://%s/%s/%s/-/compare/%s...%s" + ); + } } diff --git a/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java b/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java index 3822639..0161f2b 100644 --- a/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java +++ b/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java @@ -2,7 +2,7 @@ import io.github.sam24r.semver.release.ReleaseException; import io.github.sam24r.semver.release.ReleasePublisher; -import io.github.sam24r.semver.release.model.ReleaseInfo; +import io.github.sam42r.semver.model.release.ReleaseInfo; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -47,12 +47,12 @@ void shouldCreateRelease(MockServerClient client) throws ReleaseException { """) ); - var release = ReleaseInfo.builder() - .tagName("v1.0.0") - .name("v1.0.0") - .description("# Release v1.0.0") - .time(LocalDateTime.now()) - .build(); + var release = new ReleaseInfo( + "v1.0.0", + "v1.0.0", + "# Release v1.0.0", + LocalDateTime.now() + ); uut.publish( "http", @@ -76,12 +76,12 @@ void shouldThrowOnHttp500(MockServerClient client) { .withBody("Internal server error") ); - var release = ReleaseInfo.builder() - .tagName("v1.0.0") - .name("v1.0.0") - .description("# Release v1.0.0") - .time(LocalDateTime.now()) - .build(); + var release = new ReleaseInfo( + "v1.0.0", + "v1.0.0", + "# Release v1.0.0", + LocalDateTime.now() + ); assertThatThrownBy(() -> uut.publish( "http", diff --git a/semver-scm-api/pom.xml b/semver-scm-api/pom.xml index 5b75c5c..e4c841e 100644 --- a/semver-scm-api/pom.xml +++ b/semver-scm-api/pom.xml @@ -20,6 +20,12 @@ + + io.github.sam42r + semver-maven-model + ${project.version} + + org.apache.maven.scm maven-scm-api diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/AbstractScmProvider.java b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/AbstractScmProvider.java index 951d562..726429a 100644 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/AbstractScmProvider.java +++ b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/AbstractScmProvider.java @@ -1,9 +1,8 @@ package io.github.sam42r.semver.scm; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; -import io.github.sam42r.semver.scm.util.RemoteUtil; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; @@ -77,12 +76,11 @@ protected AbstractScmProvider( var changeLogScmResult = scmManager.changeLog(changeLogScmRequest); return changeLogScmResult.getChangeLog().getChangeSets().stream() - .map(v -> Commit.builder() - .id(v.getRevision()) - .timestamp(v.getDate().toInstant()) - .author(v.getAuthor()) - .message(v.getComment()) - .build()); + .map(v -> new Commit( + v.getRevision(), + v.getDate().toInstant(), + v.getAuthor(), + v.getComment())); } catch (ScmException e) { throw new SCMException(e); } @@ -98,10 +96,9 @@ protected AbstractScmProvider( return changeLogScmResult.getChangeLog().getChangeSets().stream() .filter(hasTag) - .map(v -> Tag.builder() - .name(v.getTags().get(0)) - .commitId(v.getRevision()) - .build()); + .map(v -> new Tag( + v.getTags().get(0), + v.getRevision())); } catch (ScmException e) { throw new SCMException(e); } @@ -141,7 +138,7 @@ public void addFile(@NonNull Path file) throws SCMException { var tagScmResult = scmManager.tag(repository, new ScmFileSet(path.toFile()), name); assert tagScmResult.isSuccess(); - return readTags().filter(v -> name.equals(v.getName())).findFirst().orElseThrow(); + return readTags().filter(v -> name.equals(v.name())).findFirst().orElseThrow(); } catch (ScmException e) { throw new SCMException(e); } @@ -149,7 +146,7 @@ public void addFile(@NonNull Path file) throws SCMException { @Override public @NonNull Remote getRemote() throws SCMException { - return RemoteUtil.parseUrl(getRemoteUrl().orElseThrow()); + return Remote.of(getRemoteUrl().orElseThrow()); } protected ScmRepository getScmRepository() throws SCMException { diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/SCMProvider.java b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/SCMProvider.java index b9b7112..140217d 100644 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/SCMProvider.java +++ b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/SCMProvider.java @@ -1,8 +1,8 @@ package io.github.sam42r.semver.scm; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; import lombok.NonNull; import java.nio.file.Path; diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Commit.java b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Commit.java deleted file mode 100644 index 735ba00..0000000 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Commit.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.sam42r.semver.scm.model; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -import java.time.Instant; - -@Data -@SuperBuilder -@NoArgsConstructor -@AllArgsConstructor -public class Commit { - - private String id; - private Instant timestamp; - private String author; - private String message; -} diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Remote.java b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Remote.java deleted file mode 100644 index f0498b9..0000000 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Remote.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.sam42r.semver.scm.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Remote { - - private String url; - private String scheme; - private String host; - private String group; - private String project; -} diff --git a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Tag.java b/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Tag.java deleted file mode 100644 index 15a592a..0000000 --- a/semver-scm-api/src/main/java/io/github/sam42r/semver/scm/model/Tag.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.sam42r.semver.scm.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Tag { - - private String name; - private String commitId; -} diff --git a/semver-scm-api/src/test/java/io/github/sam42r/semver/scm/util/RemoteUtilTest.java b/semver-scm-api/src/test/java/io/github/sam42r/semver/scm/util/RemoteUtilTest.java deleted file mode 100644 index de964ec..0000000 --- a/semver-scm-api/src/test/java/io/github/sam42r/semver/scm/util/RemoteUtilTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.sam42r.semver.scm.util; - -import io.github.sam42r.semver.scm.model.Remote; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import static org.assertj.core.api.Assertions.assertThat; - -class RemoteUtilTest { - - @ParameterizedTest - @ValueSource(strings = { - "git@github.com:JUnit/test.git", - "https://github.com/JUnit/test.git" - }) - void shouldReadRemoteGithub(String url) { - var actual = RemoteUtil.parseUrl(url); - - assertThat(actual).isEqualTo(Remote.builder() - .url(url) - .scheme("https") - .host("github.com") - .group("JUnit") - .project("test") - .build()); - } - - @ParameterizedTest - @ValueSource(strings = { - "git@gitlab.local:10022:JUnit/subgroup/test.git", - "https://gitlab.local:10022/JUnit/subgroup/test.git" - }) - void shouldReadRemoteGitlab(String url) { - var actual = RemoteUtil.parseUrl(url); - - assertThat(actual).isEqualTo(Remote.builder() - .url(url) - .scheme("https") - .host("gitlab.local:10022") - .group("JUnit/subgroup") - .project("test") - .build()); - } -} diff --git a/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProvider.java b/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProvider.java deleted file mode 100644 index d9fafda..0000000 --- a/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProvider.java +++ /dev/null @@ -1,281 +0,0 @@ -package io.github.sam42r.semver.scm; - - -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; -import io.github.sam42r.semver.scm.util.RemoteUtil; -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.SystemUtils; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; -import org.eclipse.jgit.transport.*; -import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory; -import org.eclipse.jgit.transport.ssh.jsch.OpenSshConfig; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Instant; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -/** - * {@link SCMProvider} for git. - * - * @author Sam42R - * @deprecated since switching to Maven-SCM {@link DefaultGitProvider} is used instead - */ -@Slf4j -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) -@Deprecated(since = "1.5.0", forRemoval = true) -public class GitProvider implements SCMProvider { - - private final Path repositoryPath; - private final String username; - private final String password; - - private Repository repository; - - @Override - public @NonNull Stream readCommits(String fromCommitId) throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - var logCommand = git.log(); - - if (fromCommitId != null) { - logCommand = logCommand.addRange( - ObjectId.fromString(fromCommitId), - git.getRepository().resolve("HEAD")); - } - - return StreamSupport.stream(logCommand.call().spliterator(), false) - .map(v -> Commit.builder() - .id(v.getId().getName()) - .timestamp(Instant.ofEpochSecond(v.getCommitTime())) - .author(v.getAuthorIdent().getName()) - .message(v.getFullMessage()) - .build()); - } catch (IOException | GitAPIException e) { - throw new SCMException(e.getMessage(), e); - } - } - - @Override - public @NonNull Stream readTags() throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - return git.tagList().call().stream() - .map(v -> Tag.builder() - .name(v.getName().replace("refs/tags/", "")) - .commitId(getObjectId(repository, v).getName()) - .build()); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - @Override - public void addFile(@NonNull Path file) throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - // note: jGit seems to accept slash as file separator only; therefore we have to replace the platform file - // separator to make it run on windows also - git.add().addFilepattern(repositoryPath.relativize(file).toString().replace(File.separatorChar, '/')).call(); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - @Override - public @NonNull Commit commit(@NonNull String message) throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - var commit = git.commit().setMessage(message).call(); - return Commit.builder() - .id(commit.getId().getName()) - .timestamp(Instant.ofEpochSecond(commit.getCommitTime())) - .author(commit.getAuthorIdent().getName()) - .message(commit.getFullMessage()) - .build(); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - @Override - public @NonNull Tag tag(@NonNull String name) throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - var tag = git.tag().setName(name).call(); - return Tag.builder() - .name(tag.getName().replace("refs/tags/", "")) - .commitId(getObjectId(repository, tag).getName()) - .build(); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - @Override - public String push(boolean force) throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - var pushBranchResults = git.push() - .setTransportConfigCallback(this::configureTransport) - .setRemote("origin") - .setForce(force) - .call(); - - var pushTagResults = git.push() - .setTransportConfigCallback(this::configureTransport) - .setRemote("origin") - .setForce(force) - .setPushTags() // pushes tags only - .call(); - - return Stream.concat( - StreamSupport.stream(pushBranchResults.spliterator(), false), - StreamSupport.stream(pushTagResults.spliterator(), false) - ) - .map(PushResult::getMessages) - .collect(Collectors.joining(System.lineSeparator())); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - @Override - public @NonNull Remote getRemote() throws SCMException { - var repository = getRepository(); - try (var git = new Git(repository)) { - var remoteConfigs = git.remoteList().call(); - var url = remoteConfigs.stream() - .filter(v -> "origin".equals(v.getName())) - .map(RemoteConfig::getURIs) - .flatMap(List::stream) - .map(URIish::toString) - .findAny() - .orElseThrow(); - return RemoteUtil.parseUrl(url); - } catch (GitAPIException e) { - throw new SCMException(e); - } - } - - private Repository getRepository() throws SCMException { - if (repository == null) { - try { - var gitDirectory = repositoryPath.resolve(".git"); - if (Files.notExists(gitDirectory) || !Files.isDirectory(gitDirectory) || !Files.isReadable(gitDirectory)) { - throw new SCMException("Could not find git repository"); - } - repository = FileRepositoryBuilder.create(gitDirectory.toFile()); - } catch (IOException e) { - throw new SCMException(e); - } - } - return repository; - } - - private ObjectId getObjectId(Repository repository, Ref ref) { - try { - var peel = repository.getRefDatabase().peel(ref); - return Optional.ofNullable(peel.getPeeledObjectId()).orElse(ref.getObjectId()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private void configureTransport(@NonNull Transport transport) { - if (transport instanceof SshTransport sshTransport) { - configureSshTransport(sshTransport); - } else if (transport instanceof HttpTransport httpTransport) { - configureHttpTransport(httpTransport); - } else { - log.warn("Could not configure transport '{}'", transport.getClass().getSimpleName()); - } - } - - private void configureSshTransport(@NonNull SshTransport sshTransport) { - log.debug("Configure SshTransport with SSH keys"); - - var jschConfigSessionFactory = new JschConfigSessionFactory() { - @Override - protected void configureJSch(JSch jsch) { - super.configureJSch(jsch); - - Predicate isKeyFile = path -> path.getFileName().toString().toLowerCase().startsWith("id_"); - Predicate isPublicKeyFile = path -> path.getFileName().toString().toLowerCase().endsWith(".pub"); - - var sshDirectory = SystemUtils.getUserHome().toPath().resolve(".ssh"); - try (var sshConfigFiles = Files.walk(sshDirectory)) { - var sshKeyFiles = sshConfigFiles.filter(isKeyFile).toList(); - - var publicKeyPath = sshKeyFiles.stream() - .filter(isPublicKeyFile) - .sorted() - .findFirst(); - var publicKeyBytes = publicKeyPath.map(this::readAllBytes) - .orElseThrow(() -> new IllegalStateException("Could not find public key file")); - - var privateKeyPath = sshKeyFiles.stream() - .filter(Predicate.not(isPublicKeyFile)) - .sorted() - .findFirst(); - var privateKeyBytes = privateKeyPath.map(this::readAllBytes) - .orElseThrow(() -> new IllegalStateException("Could not find private key file")); - - var privateKeyPassword = Optional.ofNullable(password).map(String::getBytes).orElse(null); - - jsch.addIdentity(null, privateKeyBytes, publicKeyBytes, privateKeyPassword); - } catch (IOException | JSchException e) { - throw new RuntimeException(e); - } - } - - @Override - protected void configure(OpenSshConfig.Host hc, Session session) { - // do nothing - // session.setPassword("***"); // NOT supported by GitHub - } - - private byte[] readAllBytes(@NonNull Path path) { - try { - return Files.readAllBytes(path); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - }; - sshTransport.setSshSessionFactory(jschConfigSessionFactory); - } - - - private void configureHttpTransport(@NonNull HttpTransport httpTransport) { - log.debug("Configure HttpTransport with username and password"); - - var usernameToSet = Optional.ofNullable(username).orElse(SystemUtils.getUserName()); - var passwordToSet = Optional.ofNullable(password).orElse(""); - - httpTransport.setCredentialsProvider( - new UsernamePasswordCredentialsProvider(usernameToSet, passwordToSet) - ); - } -} diff --git a/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProviderFactory.java b/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProviderFactory.java deleted file mode 100644 index 7fa6346..0000000 --- a/semver-scm-git/src/main/java/io/github/sam42r/semver/scm/GitProviderFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.sam42r.semver.scm; - -import lombok.NoArgsConstructor; -import lombok.NonNull; - -import javax.inject.Named; -import java.nio.file.Path; - - -/** - * @deprecated since switching to Maven-SCM {@link DefaultGitProviderFactory} is used instead - */ -@Named("Git(deprecated)") -@NoArgsConstructor -@Deprecated(since = "1.5.0", forRemoval = true) -public class GitProviderFactory implements SCMProviderFactory { - - @Override - public @NonNull GitProvider getInstance(@NonNull Path path, String username, String password) { - return new GitProvider(path, username, password); - } -} diff --git a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/AbstractGitProviderTest.java b/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/AbstractGitProviderTest.java deleted file mode 100644 index 255d198..0000000 --- a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/AbstractGitProviderTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package io.github.sam42r.semver.scm; - -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.lib.UserConfig; -import org.eclipse.jgit.transport.URIish; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -abstract class AbstractGitProviderTest { - - @TempDir - private Path tempDirectory; - private SCMProvider uut; - - @BeforeEach - void setup() { - uut = getUut(tempDirectory); - } - - abstract SCMProvider getUut(Path tempDirectory); - - @Test - void shouldReadCommits() throws GitAPIException, IOException, SCMException { - try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { - Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); - git.add().addFilepattern("README.md").call(); - var expected = git.commit().setMessage("Test commit").call(); - - var actual = uut.readCommits(null); - assertThat(actual).containsExactly( - Commit.builder() - .id(expected.getId().getName()) - .author(git.getRepository().getConfig().get(UserConfig.KEY).getAuthorName()) - .timestamp(Instant.ofEpochSecond(expected.getCommitTime())) - .message("Test commit") - .build() - ); - } - } - - @Test - void shouldReadTags() throws GitAPIException, IOException, SCMException { - try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { - Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); - git.add().addFilepattern("README.md").call(); - var expected = git.commit().setMessage("Test commit").call(); - git.tag().setName("v1.0.0").call(); - - var actual = uut.readTags(); - assertThat(actual).containsExactly( - Tag.builder() - .name("v1.0.0") - .commitId(expected.getId().getName()) - .build() - ); - } - } - - @Test - void shouldAddFiles() throws GitAPIException, IOException, SCMException { - try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { - var file = Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); - var directory = Files.createDirectory(tempDirectory.resolve("directory")); - var anotherFile = Files.writeString(directory.resolve("file.dat"), "JUnit"); - - uut.addFile(file); - uut.addFile(anotherFile); - - var actual = git.status().call(); - assertThat(actual.getAdded()).hasSize(2); - } - } - - @ParameterizedTest - @ValueSource(strings = { - "git@github.com:JUnit/test.git", - "https://github.com/JUnit/test.git" - }) - void shouldReadGithubRemote(String url) throws GitAPIException, URISyntaxException, SCMException { - try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { - git.remoteAdd().setName("origin").setUri(new URIish(url)).call(); - - var actual = uut.getRemote(); - - assertThat(actual).isEqualTo(Remote.builder() - .url(url) - .scheme("https") - .host("github.com") - .group("JUnit") - .project("test") - .build()); - } - } - - @ParameterizedTest - @ValueSource(strings = { - "git@gitlab.local:10022:JUnit/subgroup/test.git", - "https://gitlab.local:10022/JUnit/subgroup/test.git" - }) - void shouldReadGitlabRemote(String url) throws GitAPIException, URISyntaxException, SCMException { - try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { - git.remoteAdd().setName("origin").setUri(new URIish(url)).call(); - - var actual = uut.getRemote(); - - assertThat(actual).isEqualTo(Remote.builder() - .url(url) - .scheme("https") - .host("gitlab.local:10022") - .group("JUnit/subgroup") - .project("test") - .build()); - } - } -} diff --git a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/DefaultGitProviderTest.java b/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/DefaultGitProviderTest.java index b655e26..51b3921 100644 --- a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/DefaultGitProviderTest.java +++ b/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/DefaultGitProviderTest.java @@ -1,12 +1,128 @@ package io.github.sam42r.semver.scm; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.UserConfig; +import org.eclipse.jgit.transport.URIish; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; + +class DefaultGitProviderTest { + + @TempDir + private Path tempDirectory; + private SCMProvider uut; + + @BeforeEach + void setup() { + uut = new DefaultGitProviderFactory().getInstance(tempDirectory, null, null); + } + + @Test + void shouldReadCommits() throws GitAPIException, IOException, SCMException { + try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { + Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); + git.add().addFilepattern("README.md").call(); + var expected = git.commit().setMessage("Test commit").call(); + + var actual = uut.readCommits(null); + assertThat(actual).containsExactly( + new Commit( + expected.getId().getName(), + Instant.ofEpochSecond(expected.getCommitTime()), + git.getRepository().getConfig().get(UserConfig.KEY).getAuthorName(), + "Test commit") + ); + } + } + + @Test + void shouldReadTags() throws GitAPIException, IOException, SCMException { + try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { + Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); + git.add().addFilepattern("README.md").call(); + var expected = git.commit().setMessage("Test commit").call(); + git.tag().setName("v1.0.0").call(); + + var actual = uut.readTags(); + assertThat(actual).containsExactly( + new Tag( + "v1.0.0", + expected.getId().getName()) + ); + } + } + + @Test + void shouldAddFiles() throws GitAPIException, IOException, SCMException { + try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { + var file = Files.writeString(tempDirectory.resolve("README.md"), "#JUnit"); + var directory = Files.createDirectory(tempDirectory.resolve("directory")); + var anotherFile = Files.writeString(directory.resolve("file.dat"), "JUnit"); + + uut.addFile(file); + uut.addFile(anotherFile); + + var actual = git.status().call(); + assertThat(actual.getAdded()).hasSize(2); + } + } + + @ParameterizedTest + @ValueSource(strings = { + "git@github.com:JUnit/test.git", + "https://github.com/JUnit/test.git" + }) + void shouldReadGithubRemote(String url) throws GitAPIException, URISyntaxException, SCMException { + try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { + git.remoteAdd().setName("origin").setUri(new URIish(url)).call(); + + var actual = uut.getRemote(); + + assertThat(actual).isEqualTo( + new Remote( + url, + "https", + "github.com", + "JUnit", + "test") + ); + } + } + + @ParameterizedTest + @ValueSource(strings = { + "git@gitlab.local:10022:JUnit/subgroup/test.git", + "https://gitlab.local:10022/JUnit/subgroup/test.git" + }) + void shouldReadGitlabRemote(String url) throws GitAPIException, URISyntaxException, SCMException { + try (var git = Git.init().setDirectory(tempDirectory.toFile()).call()) { + git.remoteAdd().setName("origin").setUri(new URIish(url)).call(); -class DefaultGitProviderTest extends AbstractGitProviderTest { + var actual = uut.getRemote(); - @Override - SCMProvider getUut(Path tempDirectory) { - return new DefaultGitProviderFactory() - .getInstance(tempDirectory, null, null); + assertThat(actual).isEqualTo( + new Remote( + url, + "https", + "gitlab.local:10022", + "JUnit/subgroup", + "test") + ); + } } } diff --git a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/GitProviderTest.java b/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/GitProviderTest.java deleted file mode 100644 index ccfffde..0000000 --- a/semver-scm-git/src/test/java/io/github/sam42r/semver/scm/GitProviderTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.sam42r.semver.scm; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; - -class GitProviderTest extends AbstractGitProviderTest { - - @Override - SCMProvider getUut(Path tempDirectory) { - return new GitProviderFactory() - .getInstance(tempDirectory, null, null); - } - - @Test - @Disabled("local testing only") - void shouldPushToRemote() throws IOException, SCMException { - var path = Path.of("..").toRealPath(); - - var scmProvider = new GitProvider(path, null, "***"); - - var actual = scmProvider.push(false); - assertThat(actual).isNotNull(); - } -} diff --git a/semver-scm-mercurial/src/main/java/io/github/sam42r/semver/scm/HgProvider.java b/semver-scm-mercurial/src/main/java/io/github/sam42r/semver/scm/HgProvider.java index 5efc2fe..fd6d99a 100644 --- a/semver-scm-mercurial/src/main/java/io/github/sam42r/semver/scm/HgProvider.java +++ b/semver-scm-mercurial/src/main/java/io/github/sam42r/semver/scm/HgProvider.java @@ -1,8 +1,8 @@ package io.github.sam42r.semver.scm; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; import lombok.NonNull; import org.apache.commons.lang3.NotImplementedException; diff --git a/semver-scm-subversion/src/main/java/io/github/sam42r/semver/scm/SvnProvider.java b/semver-scm-subversion/src/main/java/io/github/sam42r/semver/scm/SvnProvider.java index 2705dd6..2f09205 100644 --- a/semver-scm-subversion/src/main/java/io/github/sam42r/semver/scm/SvnProvider.java +++ b/semver-scm-subversion/src/main/java/io/github/sam42r/semver/scm/SvnProvider.java @@ -1,8 +1,8 @@ package io.github.sam42r.semver.scm; -import io.github.sam42r.semver.scm.model.Commit; -import io.github.sam42r.semver.scm.model.Remote; -import io.github.sam42r.semver.scm.model.Tag; +import io.github.sam42r.semver.model.scm.Commit; +import io.github.sam42r.semver.model.scm.Remote; +import io.github.sam42r.semver.model.scm.Tag; import lombok.NonNull; import org.apache.commons.lang3.NotImplementedException;