diff --git a/.github/workflows/java-ea-maven.yml b/.github/workflows/java-ea-maven.yml
index 3d86d3a..6ec751a 100644
--- a/.github/workflows/java-ea-maven.yml
+++ b/.github/workflows/java-ea-maven.yml
@@ -32,6 +32,6 @@ jobs:
cache: 'maven'
- name: Build and (headless) test with Maven
- uses: GabrielBB/xvfb-action@v1
+ uses: smithki/xvfb-action@v1.1.2
with:
run: mvn -U -B -ntp package
diff --git a/.github/workflows/java8-maven.yml b/.github/workflows/java8-maven.yml
index 9b53d72..5f26664 100644
--- a/.github/workflows/java8-maven.yml
+++ b/.github/workflows/java8-maven.yml
@@ -29,7 +29,7 @@ jobs:
cache: 'maven'
- name: Build and (headless) test with Maven
- uses: GabrielBB/xvfb-action@v1
+ uses: smithki/xvfb-action@v1.1.2
with:
run: mvn -U -B -ntp package
@@ -59,7 +59,7 @@ jobs:
cache: 'maven'
- name: Deploy snapshot with Maven if settings defined
- run: test ! -f ci.settings.xml || mvn -B -ntp deploy -DskipTests=true -s ci.settings.xml -P base-deploy,snapshot-deploy,!non-deployable-modules
+ run: test ! -f ci.settings.xml || mvn -B -ntp deploy -DskipTests -s ci.settings.xml -P base-deploy,snapshot-deploy,!non-deployable-modules
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
@@ -67,13 +67,22 @@ jobs:
SIGN_KEY_PASS: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
- name: Dryrun release assets with Maven
- run: mvn -B -ntp install -DskipTests=true -P full-release -Djreleaser.dry.run=true
+ run: mvn -B -ntp install -DskipTests -P full-release -Djreleaser.output.directory=$PWD/out/jreleaser -Djreleaser.dry.run
env:
JRELEASER_GITHUB_TOKEN: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.MAVEN_GPG_PUBLIC_KEY }}
JRELEASER_GPG_SECRET_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+ - name: Upload JReleaser output
+ if: always()
+ uses: actions/upload-artifact@v3
+ with:
+ name: assets-snapshot-log
+ path: |
+ out/jreleaser/trace.log
+ out/jreleaser/output.properties
+
release-job:
needs: build-and-test-job
if: startsWith(github.repository, 'nbbrd/') && startsWith(github.ref, 'refs/tags/v')
@@ -100,7 +109,7 @@ jobs:
cache: 'maven'
- name: Deploy with Maven if settings defined
- run: test ! -f ci.settings.xml || mvn -B -ntp deploy -DskipTests=true -s ci.settings.xml -P base-deploy,release-deploy,!non-deployable-modules
+ run: test ! -f ci.settings.xml || mvn -B -ntp deploy -DskipTests -s ci.settings.xml -P base-deploy,release-deploy,!non-deployable-modules
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
@@ -110,9 +119,18 @@ jobs:
MAVEN_OPTS: "--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED"
- name: Release assets with Maven
- run: mvn -B -ntp install -DskipTests=true -P full-release
+ run: mvn -B -ntp install -DskipTests -P full-release -Djreleaser.output.directory=$PWD/out/jreleaser
env:
JRELEASER_GITHUB_TOKEN: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.MAVEN_GPG_PUBLIC_KEY }}
JRELEASER_GPG_SECRET_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+
+ - name: Upload JReleaser output
+ if: always()
+ uses: actions/upload-artifact@v3
+ with:
+ name: assets-release-log
+ path: |
+ out/jreleaser/trace.log
+ out/jreleaser/output.properties
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19665ed..07aa8c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,22 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+## [0.6.0] - 2023-06-20
+
+This release improves extension points and also aligns features of Maven plugin and CLI.
+
+### Added
+
+- Add extension point for scan formatting [#119](https://github.com/nbbrd/heylogs/issues/119)
+- Add scan mojo [#120](https://github.com/nbbrd/heylogs/issues/120)
+- Add list command and mojo [#120](https://github.com/nbbrd/heylogs/issues/120)
+
+### Changed
+
+- Refactor extension points [#119](https://github.com/nbbrd/heylogs/issues/119)
+- Merge old list command into extract command [#120](https://github.com/nbbrd/heylogs/issues/120)
+- Improve output of errors in check mojo [#119](https://github.com/nbbrd/heylogs/issues/119)
+
## [0.5.0] - 2022-11-29
### Added
@@ -67,7 +83,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Initial release
-[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.5.0...HEAD
+[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.6.0...HEAD
+[0.6.0]: https://github.com/nbbrd/heylogs/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/nbbrd/heylogs/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/nbbrd/heylogs/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/nbbrd/heylogs/compare/v0.3.1...v0.3.2
diff --git a/heylogs-api/pom.xml b/heylogs-api/pom.xml
index 571e3fd..fe0d1d0 100644
--- a/heylogs-api/pom.xml
+++ b/heylogs-api/pom.xml
@@ -7,7 +7,7 @@
com.github.nbbrd.heylogs
heylogs-parent
- 0.5.0
+ 0.6.0
heylogs-api
@@ -50,7 +50,12 @@
org.semver4j
semver4j
- 3.0.0
+ 4.3.0
+
+
+ com.github.nbbrd.java-io-util
+ java-io-base
+ 0.0.23
diff --git a/heylogs-api/src/main/java/internal/heylogs/ExtendedRules.java b/heylogs-api/src/main/java/internal/heylogs/ExtendedRules.java
index 775c1b0..9ff546b 100644
--- a/heylogs-api/src/main/java/internal/heylogs/ExtendedRules.java
+++ b/heylogs-api/src/main/java/internal/heylogs/ExtendedRules.java
@@ -3,13 +3,15 @@
import com.vladsch.flexmark.ast.Link;
import com.vladsch.flexmark.ast.LinkNodeBase;
import com.vladsch.flexmark.util.ast.Node;
+import nbbrd.design.MightBeGenerated;
import nbbrd.design.VisibleForTesting;
import nbbrd.heylogs.Failure;
-import nbbrd.heylogs.Rule;
-import nbbrd.heylogs.RuleBatch;
+import nbbrd.heylogs.spi.Rule;
+import nbbrd.heylogs.spi.RuleBatch;
+import nbbrd.io.text.Parser;
import nbbrd.service.ServiceProvider;
+import org.jetbrains.annotations.NotNull;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.stream.Stream;
@@ -18,19 +20,19 @@ public enum ExtendedRules implements Rule {
HTTPS {
@Override
- public Failure validate(Node node) {
- return node instanceof LinkNodeBase ? validateHttps((LinkNodeBase) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof LinkNodeBase ? validateHttps((LinkNodeBase) node) : NO_PROBLEM;
}
},
GITHUB_ISSUE_REF {
@Override
- public Failure validate(Node node) {
- return node instanceof Link ? validateGitHubIssueRef((Link) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Link ? validateGitHubIssueRef((Link) node) : NO_PROBLEM;
}
};
@Override
- public String getName() {
+ public @NotNull String getId() {
return name().toLowerCase(Locale.ROOT).replace('_', '-');
}
@@ -41,49 +43,61 @@ public boolean isAvailable() {
@VisibleForTesting
static Failure validateHttps(LinkNodeBase link) {
- try {
- if (new URL(link.getUrl().toString()).getProtocol().equals("http")) {
- return Failure.of(HTTPS, "Expecting HTTPS protocol", link);
- }
- } catch (MalformedURLException e) {
- }
- return null;
+ return Parser
+ .onURL()
+ .parseValue(link.getUrl())
+ .filter(url -> !url.getProtocol().equals("https"))
+ .map(ignore -> Failure
+ .builder()
+ .rule(HTTPS)
+ .message("Expecting HTTPS protocol")
+ .location(link)
+ .build())
+ .orElse(NO_PROBLEM);
}
@VisibleForTesting
static Failure validateGitHubIssueRef(Link link) {
int expected = getGitHubIssueRefFromURL(link);
int found = getGitHubIssueRefFromText(link);
- return expected != -1 && found != -1 && expected != found
- ? Failure.of(GITHUB_ISSUE_REF, "Expecting GitHub issue ref " + expected + ", found " + found, link)
- : null;
+ return expected != NO_ISSUE_REF && found != NO_ISSUE_REF && expected != found
+ ? Failure
+ .builder()
+ .rule(GITHUB_ISSUE_REF)
+ .message("Expecting GitHub issue ref " + expected + ", found " + found)
+ .location(link)
+ .build()
+ : NO_PROBLEM;
}
private static int getGitHubIssueRefFromURL(Link link) {
- try {
- URL url = new URL(link.getUrl().toString());
- if (url.getHost().equals("github.com")) {
- int index = url.getPath().indexOf("/issues/");
- if (index != -1) {
- return Integer.parseInt(url.getPath().substring(index + 8));
- }
+ URL url = Parser.onURL().parse(link.getUrl());
+ if (url != null && url.getHost().equals("github.com")) {
+ int index = url.getPath().indexOf("/issues/");
+ if (index != -1) {
+ return Parser
+ .onInteger()
+ .parseValue(url.getPath().substring(index + 8))
+ .orElse(NO_ISSUE_REF);
}
- } catch (MalformedURLException | NumberFormatException ex) {
}
- return -1;
+ return NO_ISSUE_REF;
}
private static int getGitHubIssueRefFromText(Link link) {
- try {
- String text = link.getText().toString();
- if (text.startsWith("#")) {
- return Integer.parseInt(text.substring(1));
- }
- } catch (NumberFormatException ex) {
+ String text = link.getText().toString();
+ if (text.startsWith("#")) {
+ return Parser
+ .onInteger()
+ .parseValue(text.substring(1))
+ .orElse(NO_ISSUE_REF);
}
- return -1;
+ return NO_ISSUE_REF;
}
+ private static final int NO_ISSUE_REF = -1;
+
+ @MightBeGenerated
@ServiceProvider
public static final class Batch implements RuleBatch {
diff --git a/heylogs-api/src/main/java/internal/heylogs/GuidingPrinciples.java b/heylogs-api/src/main/java/internal/heylogs/GuidingPrinciples.java
index 5a275c2..c8c5b45 100644
--- a/heylogs-api/src/main/java/internal/heylogs/GuidingPrinciples.java
+++ b/heylogs-api/src/main/java/internal/heylogs/GuidingPrinciples.java
@@ -6,8 +6,11 @@
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
+import nbbrd.design.MightBeGenerated;
import nbbrd.design.VisibleForTesting;
import nbbrd.heylogs.*;
+import nbbrd.heylogs.spi.Rule;
+import nbbrd.heylogs.spi.RuleBatch;
import nbbrd.service.ServiceProvider;
import org.jetbrains.annotations.NotNull;
@@ -19,49 +22,43 @@ public enum GuidingPrinciples implements Rule {
FOR_HUMANS {
@Override
- public Failure validate(Node node) {
- return node instanceof Document ? validateForHumans((Document) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Document ? validateForHumans((Document) node) : NO_PROBLEM;
}
},
ENTRY_FOR_EVERY_VERSIONS {
@Override
- public Failure validate(Node node) {
- return node instanceof Heading ? validateEntryForEveryVersions((Heading) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Heading ? validateEntryForEveryVersions((Heading) node) : NO_PROBLEM;
}
},
TYPE_OF_CHANGES_GROUPED {
@Override
- public Failure validate(Node node) {
- return node instanceof Heading ? validateTypeOfChangesGrouped((Heading) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Heading ? validateTypeOfChangesGrouped((Heading) node) : NO_PROBLEM;
}
},
LINKABLE {
@Override
- public Failure validate(Node node) {
- return node instanceof Heading ? validateLinkable((Heading) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Heading ? validateLinkable((Heading) node) : NO_PROBLEM;
}
},
LATEST_VERSION_FIRST {
@Override
- public Failure validate(Node node) {
- return node instanceof Document ? validateLatestVersionFirst((Document) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Document ? validateLatestVersionFirst((Document) node) : NO_PROBLEM;
}
},
DATE_DISPLAYED {
@Override
- public Failure validate(Node node) {
- return null;
- }
- },
- SEMVER {
- @Override
- public Failure validate(Node node) {
- return null;
+ public Failure validate(@NotNull Node node) {
+ return NO_PROBLEM;
}
};
@Override
- public String getName() {
+ public @NotNull String getId() {
return name().toLowerCase(Locale.ROOT).replace('_', '-');
}
@@ -79,49 +76,74 @@ static Failure validateForHumans(@NotNull Document document) {
switch (headings.size()) {
case 0:
- return Failure.of(FOR_HUMANS, "Missing Changelog heading", document);
+ return Failure
+ .builder()
+ .rule(FOR_HUMANS)
+ .message("Missing Changelog heading")
+ .location(document)
+ .build();
case 1:
try {
Changelog.parse(headings.get(0));
- return null;
+ return NO_PROBLEM;
} catch (IllegalArgumentException ex) {
- return Failure.of(FOR_HUMANS, ex.getMessage(), document);
+ return Failure
+ .builder()
+ .rule(FOR_HUMANS)
+ .message(ex.getMessage())
+ .location(document)
+ .build();
}
default:
- return Failure.of(FOR_HUMANS, "Too many Changelog headings", document);
+ return Failure
+ .builder()
+ .rule(FOR_HUMANS)
+ .message("Too many Changelog headings")
+ .location(document)
+ .build();
}
}
@VisibleForTesting
static Failure validateEntryForEveryVersions(@NotNull Heading heading) {
if (!Version.isVersionLevel(heading)) {
- return null;
+ return NO_PROBLEM;
}
try {
Version.parse(heading);
} catch (IllegalArgumentException ex) {
- return Failure.of(ENTRY_FOR_EVERY_VERSIONS, ex.getMessage(), heading);
- }
- return null;
+ return Failure
+ .builder()
+ .rule(ENTRY_FOR_EVERY_VERSIONS)
+ .message(ex.getMessage())
+ .location(heading)
+ .build();
+ }
+ return NO_PROBLEM;
}
@VisibleForTesting
static Failure validateTypeOfChangesGrouped(@NotNull Heading heading) {
if (!TypeOfChange.isTypeOfChangeLevel(heading)) {
- return null;
+ return NO_PROBLEM;
}
try {
TypeOfChange.parse(heading);
} catch (IllegalArgumentException ex) {
- return Failure.of(TYPE_OF_CHANGES_GROUPED, ex.getMessage(), heading);
- }
- return null;
+ return Failure
+ .builder()
+ .rule(TYPE_OF_CHANGES_GROUPED)
+ .message(ex.getMessage())
+ .location(heading)
+ .build();
+ }
+ return NO_PROBLEM;
}
@VisibleForTesting
static Failure validateLinkable(@NotNull Heading heading) {
if (!Version.isVersionLevel(heading)) {
- return null;
+ return NO_PROBLEM;
}
try {
@@ -132,10 +154,15 @@ static Failure validateLinkable(@NotNull Heading heading) {
Reference reference = repository.get(normalizeRef);
return reference == null
- ? Failure.of(LINKABLE, "Missing reference '" + version.getRef() + "'", heading)
- : null;
+ ? Failure
+ .builder()
+ .rule(LINKABLE)
+ .message("Missing reference '" + version.getRef() + "'")
+ .location(heading)
+ .build()
+ : NO_PROBLEM;
} catch (IllegalArgumentException ex) {
- return null;
+ return NO_PROBLEM;
}
}
@@ -145,7 +172,14 @@ static Failure validateLatestVersionFirst(@NotNull Document doc) {
Comparator comparator = Comparator.comparing((VersionNode item) -> item.getVersion().getDate()).reversed();
VersionNode unsortedItem = getFirstUnsortedItem(versions, comparator);
- return unsortedItem != null ? Failure.of(LATEST_VERSION_FIRST, "Versions not sorted", unsortedItem.getNode()) : null;
+ return unsortedItem != null
+ ? Failure
+ .builder()
+ .rule(LATEST_VERSION_FIRST)
+ .message("Versions not sorted")
+ .location(unsortedItem.getNode())
+ .build()
+ : NO_PROBLEM;
}
@lombok.Value
@@ -187,6 +221,7 @@ private static T getFirstUnsortedItem(List list, Comparator comparator
return null;
}
+ @MightBeGenerated
@ServiceProvider
public static final class Batch implements RuleBatch {
diff --git a/heylogs-api/src/main/java/internal/heylogs/SemverRule.java b/heylogs-api/src/main/java/internal/heylogs/SemverRule.java
index 6a23e6a..ad45011 100644
--- a/heylogs-api/src/main/java/internal/heylogs/SemverRule.java
+++ b/heylogs-api/src/main/java/internal/heylogs/SemverRule.java
@@ -4,44 +4,52 @@
import com.vladsch.flexmark.util.ast.Node;
import nbbrd.design.VisibleForTesting;
import nbbrd.heylogs.Failure;
-import nbbrd.heylogs.Rule;
import nbbrd.heylogs.Version;
+import nbbrd.heylogs.spi.Rule;
import nbbrd.service.ServiceProvider;
+import org.jetbrains.annotations.NotNull;
import org.semver4j.Semver;
@ServiceProvider
public final class SemverRule implements Rule {
@Override
- public String getName() {
+ public @NotNull String getId() {
return "semver";
}
@Override
- public Failure validate(Node node) {
- return node instanceof Heading ? validateSemVer((Heading) node) : null;
+ public Failure validate(@NotNull Node node) {
+ return node instanceof Heading ? validateSemVer((Heading) node) : NO_PROBLEM;
}
@Override
public boolean isAvailable() {
- return Rule.isEnabled(System.getProperties(), getName());
+ return Rule.isEnabled(System.getProperties(), getId());
}
@VisibleForTesting
Failure validateSemVer(Heading heading) {
if (!Version.isVersionLevel(heading)) {
- return null;
+ return NO_PROBLEM;
}
try {
Version version = Version.parse(heading);
if (version.isUnreleased()) {
- return null;
+ return NO_PROBLEM;
}
String ref = version.getRef();
- return Semver.isValid(ref) ? null : Failure.of(this, "Invalid semver format: '" + ref + "'", heading);
+ return Semver.isValid(ref)
+ ? NO_PROBLEM
+ : Failure
+ .builder()
+ .rule(this)
+ .message("Invalid semver format: '" + ref + "'")
+ .location(heading)
+ .build();
} catch (IllegalArgumentException ex) {
- return null;
+ return NO_PROBLEM;
}
}
}
diff --git a/heylogs-api/src/main/java/internal/heylogs/StylishFormat.java b/heylogs-api/src/main/java/internal/heylogs/StylishFormat.java
new file mode 100644
index 0000000..6680c9b
--- /dev/null
+++ b/heylogs-api/src/main/java/internal/heylogs/StylishFormat.java
@@ -0,0 +1,87 @@
+package internal.heylogs;
+
+import lombok.NonNull;
+import nbbrd.design.MightBePromoted;
+import nbbrd.heylogs.Failure;
+import nbbrd.heylogs.Status;
+import nbbrd.heylogs.spi.Format;
+import nbbrd.service.ServiceProvider;
+
+import java.io.IOException;
+import java.util.List;
+
+import static java.lang.System.lineSeparator;
+import static java.util.Locale.ROOT;
+
+// https://eslint.org/docs/latest/user-guide/formatters/#stylish
+@ServiceProvider
+public final class StylishFormat implements Format {
+
+ public static final String ID = "stylish";
+
+ @Override
+ public @NonNull String getId() {
+ return ID;
+ }
+
+ @Override
+ public void formatFailures(@NonNull Appendable appendable, @NonNull String source, @NonNull List failures) throws IOException {
+ appendable
+ .append(source)
+ .append(lineSeparator());
+
+ int l = failures.stream().mapToInt(failure -> getNumberOfDigits(failure.getLine())).max().orElse(0);
+ int c = failures.stream().mapToInt(failure -> getNumberOfDigits(failure.getColumn())).max().orElse(0);
+ int m = failures.stream().mapToInt(failure -> failure.getMessage().length()).max().orElse(0);
+
+ for (Failure x : failures) {
+ appendable
+ .append(String.format(ROOT, " %" + l + "d:%-" + c + "d error %-" + m + "s %s", x.getLine(), x.getColumn(), x.getMessage(), x.getRuleId()))
+ .append(lineSeparator());
+ }
+
+ appendable.append(lineSeparator());
+ switch (failures.size()) {
+ case 0:
+ appendable.append(" No problem");
+ break;
+ case 1:
+ appendable.append(" 1 problem");
+ break;
+ default:
+ appendable.append(String.format(ROOT, " %d problems", failures.size()));
+ break;
+ }
+ appendable.append(lineSeparator());
+ }
+
+ @MightBePromoted
+ private static int getNumberOfDigits(int number) {
+ return (int) (Math.log10(number) + 1);
+ }
+
+ @Override
+ public void formatStatus(@NonNull Appendable appendable, @NonNull String source, @NonNull Status status) throws IOException {
+ appendable.append(source);
+ appendable.append(lineSeparator());
+ if (status.getReleaseCount() == 0) {
+ appendable.append(" No release found");
+ appendable.append(lineSeparator());
+ } else {
+ appendable.append(String.format(ROOT, " Found %d releases", status.getReleaseCount()));
+ appendable.append(lineSeparator());
+ appendable.append(String.format(ROOT, " Ranging from %s to %s", status.getTimeRange().getFrom(), status.getTimeRange().getTo()));
+ appendable.append(lineSeparator());
+
+ if (status.isCompatibleWithSemver()) {
+ appendable.append(" Compatible with Semantic Versioning").append(status.getSemverDetails());
+ appendable.append(lineSeparator());
+ } else {
+ appendable.append(" Not compatible with Semantic Versioning");
+ appendable.append(lineSeparator());
+ }
+ }
+ appendable.append(status.isHasUnreleasedSection() ? " Has an unreleased version" : " Has no unreleased version");
+ appendable.append(lineSeparator());
+ }
+}
diff --git a/heylogs-api/src/main/java/internal/heylogs/StylishFormatter.java b/heylogs-api/src/main/java/internal/heylogs/StylishFormatter.java
deleted file mode 100644
index 2654907..0000000
--- a/heylogs-api/src/main/java/internal/heylogs/StylishFormatter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package internal.heylogs;
-
-import nbbrd.heylogs.Failure;
-import nbbrd.heylogs.FailureFormatter;
-import nbbrd.service.ServiceProvider;
-
-import java.io.IOException;
-import java.util.List;
-
-import static java.lang.System.lineSeparator;
-
-// https://eslint.org/docs/latest/user-guide/formatters/#stylish
-@ServiceProvider
-public final class StylishFormatter implements FailureFormatter {
-
- @Override
- public String getName() {
- return "stylish";
- }
-
- @Override
- public void format(Appendable appendable, String source, List failures) throws IOException {
- appendable
- .append(source)
- .append(lineSeparator());
-
- int l = failures.stream().mapToInt(failure -> getNumberOfDigits(failure.getLine())).max().orElse(0);
- int c = failures.stream().mapToInt(failure -> getNumberOfDigits(failure.getColumn())).max().orElse(0);
- int m = failures.stream().mapToInt(failure -> failure.getMessage().length()).max().orElse(0);
-
- for (Failure failure : failures) {
- appendable
- .append(String.format(" %" + l + "d:%-" + c + "d error %-" + m + "s %s", failure.getLine(), failure.getColumn(), failure.getMessage(), failure.getRule()))
- .append(lineSeparator());
- }
-
- appendable.append(lineSeparator());
- switch (failures.size()) {
- case 0:
- appendable.append(" No problem");
- break;
- case 1:
- appendable.append(" 1 problem");
- break;
- default:
- appendable.append(String.format(" %d problems", failures.size()));
- break;
- }
- appendable.append(lineSeparator());
- }
-
- private static int getNumberOfDigits(int number) {
- return (int) (Math.log10(number) + 1);
- }
-}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/BaseSection.java b/heylogs-api/src/main/java/nbbrd/heylogs/BaseSection.java
index 9a3ba2f..0dac8c8 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/BaseSection.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/BaseSection.java
@@ -1,6 +1,7 @@
package nbbrd.heylogs;
import com.vladsch.flexmark.ast.Heading;
+import lombok.NonNull;
import nbbrd.design.SealedType;
@SealedType({
@@ -10,5 +11,5 @@
})
public interface BaseSection {
- Heading toHeading();
+ @NonNull Heading toHeading();
}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Changelog.java b/heylogs-api/src/main/java/nbbrd/heylogs/Changelog.java
index d9f1e3d..35ad679 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/Changelog.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Changelog.java
@@ -3,15 +3,19 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ast.Text;
import com.vladsch.flexmark.util.sequence.BasedSequence;
+import lombok.NonNull;
import nbbrd.design.RepresentableAs;
+import nbbrd.design.StaticFactoryMethod;
@RepresentableAs(Heading.class)
public enum Changelog implements BaseSection {
+
INSTANCE;
private static final int HEADING_LEVEL = 1;
- public static Changelog parse(Heading heading) {
+ @StaticFactoryMethod
+ public static @NonNull Changelog parse(@NonNull Heading heading) {
if (!isChangelogLevel(heading)) {
throw new IllegalArgumentException("Invalid heading level");
}
@@ -22,7 +26,7 @@ public static Changelog parse(Heading heading) {
}
@Override
- public Heading toHeading() {
+ public @NonNull Heading toHeading() {
Heading result = new Heading();
result.setOpeningMarker(BasedSequence.repeatOf("#", HEADING_LEVEL));
result.setLevel(HEADING_LEVEL);
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Checker.java b/heylogs-api/src/main/java/nbbrd/heylogs/Checker.java
new file mode 100644
index 0000000..ad6b84a
--- /dev/null
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Checker.java
@@ -0,0 +1,61 @@
+package nbbrd.heylogs;
+
+import com.vladsch.flexmark.util.ast.Document;
+import com.vladsch.flexmark.util.ast.Node;
+import lombok.NonNull;
+import nbbrd.design.StaticFactoryMethod;
+import nbbrd.heylogs.spi.Format;
+import nbbrd.heylogs.spi.FormatLoader;
+import nbbrd.heylogs.spi.Rule;
+import nbbrd.heylogs.spi.RuleLoader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@lombok.Value
+@lombok.Builder(toBuilder = true)
+public class Checker {
+
+ @StaticFactoryMethod
+ public static @NonNull Checker ofServiceLoader() {
+ return Checker
+ .builder()
+ .rules(RuleLoader.load())
+ .formats(FormatLoader.load())
+ .build();
+ }
+
+ @NonNull
+ @lombok.Singular
+ List rules;
+
+ @NonNull
+ @lombok.Singular
+ List formats;
+
+ @NonNull
+ @lombok.Builder.Default
+ String formatId = FIRST_FORMAT_AVAILABLE;
+
+ public @NonNull List validate(@NonNull Document doc) {
+ return Stream.concat(Stream.of(doc), Nodes.of(Node.class).descendants(doc))
+ .flatMap(node -> rules.stream().map(rule -> rule.validate(node)).filter(Objects::nonNull))
+ .collect(Collectors.toList());
+ }
+
+ public void formatFailures(@NonNull Appendable appendable, @NonNull String source, @NonNull List failures) throws IOException {
+ getFormatById().formatFailures(appendable, source, failures);
+ }
+
+ private Format getFormatById() throws IOException {
+ return formats.stream()
+ .filter(format -> formatId.equals(FIRST_FORMAT_AVAILABLE) || format.getId().equals(formatId))
+ .findFirst()
+ .orElseThrow(() -> new IOException("Cannot find format '" + formatId + "'"));
+ }
+
+ private static final String FIRST_FORMAT_AVAILABLE = "";
+}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/VersionFilter.java b/heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java
similarity index 78%
rename from heylogs-api/src/main/java/nbbrd/heylogs/VersionFilter.java
rename to heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java
index 94b0e8b..4632c1f 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/VersionFilter.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java
@@ -5,6 +5,7 @@
import com.vladsch.flexmark.ast.Reference;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
+import lombok.NonNull;
import java.time.LocalDate;
import java.time.Year;
@@ -14,10 +15,11 @@
import java.util.regex.Pattern;
@lombok.Value
-@lombok.Builder
-public class VersionFilter {
+@lombok.Builder(toBuilder = true)
+public class Extractor {
+
+ public static final Extractor DEFAULT = Extractor.builder().build();
- public static final VersionFilter DEFAULT = VersionFilter.builder().build();
@lombok.NonNull
@lombok.Builder.Default
String ref = "";
@@ -33,23 +35,26 @@ public class VersionFilter {
@lombok.Builder.Default
int limit = Integer.MAX_VALUE;
+ @lombok.Builder.Default
+ boolean ignoreContent = false;
+
private boolean isUnreleasedPattern() {
return unreleasedPattern.asPredicate().test(ref);
}
- private boolean containsRef(Version version) {
+ private boolean containsRef(@NonNull Version version) {
return (isUnreleasedPattern() && version.isUnreleased()) || version.getRef().contains(ref);
}
- public boolean contains(Heading heading) {
- return contains(Version.parse(heading));
+ public boolean contains(@NonNull Version version) {
+ return containsRef(version) && timeRange.contains(version.getDate());
}
- public boolean contains(Version version) {
- return containsRef(version) && timeRange.contains(version.getDate());
+ public boolean contains(@NonNull Heading heading) {
+ return contains(Version.parse(heading));
}
- public void apply(Document root) {
+ public void extract(@NonNull Document root) {
int found = 0;
boolean keep = false;
@@ -58,7 +63,10 @@ public void apply(Document root) {
for (Node current : root.getChildren()) {
- if (current instanceof Heading && Version.isVersionLevel((Heading) current)) {
+ boolean versionHeading = current instanceof Heading
+ && Version.isVersionLevel((Heading) current);
+
+ if (versionHeading) {
if (found >= getLimit() || !contains((Heading) current)) {
keep = false;
} else {
@@ -72,6 +80,9 @@ public void apply(Document root) {
.descendants(current)
.map(node -> node.getReference().toString())
.forEach(refNodes::add);
+ if (versionHeading && ignoreContent) {
+ keep = false;
+ }
} else {
if (current instanceof Reference) {
references.add((Reference) current);
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Failure.java b/heylogs-api/src/main/java/nbbrd/heylogs/Failure.java
index a625bd1..e1ea34c 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/Failure.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Failure.java
@@ -1,19 +1,15 @@
package nbbrd.heylogs;
-import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
-import lombok.AccessLevel;
+import lombok.NonNull;
+import nbbrd.heylogs.spi.Rule;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-@lombok.Value(staticConstructor = "of")
+@lombok.Value
+@lombok.Builder
public class Failure {
@lombok.NonNull
- String rule;
+ String ruleId;
@lombok.NonNull
String message;
@@ -22,17 +18,15 @@ public class Failure {
int column;
- public static Failure of(Rule rule, String message, Node node) {
- return new Failure(rule.getName(), message, node.getStartLineNumber() + 1, node.lineColumnAtStart().getSecond() + 1);
- }
+ public static final class Builder {
- public static Failure of(Rule rule, String message, int line, int column) {
- return new Failure(rule.getName(), message, line, column);
- }
+ public @NonNull Builder rule(@NonNull Rule rule) {
+ return ruleId(rule.getId());
+ }
- public static List allOf(Document doc, List rules) {
- return Stream.concat(Stream.of(doc), Nodes.of(Node.class).descendants(doc))
- .flatMap(node -> rules.stream().map(rule -> rule.validate(node)).filter(Objects::nonNull))
- .collect(Collectors.toList());
+ public @NonNull Builder location(@NonNull Node location) {
+ return line(location.getStartLineNumber() + 1)
+ .column(location.lineColumnAtStart().getSecond() + 1);
+ }
}
}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/FailureFormatter.java b/heylogs-api/src/main/java/nbbrd/heylogs/FailureFormatter.java
deleted file mode 100644
index e277412..0000000
--- a/heylogs-api/src/main/java/nbbrd/heylogs/FailureFormatter.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package nbbrd.heylogs;
-
-import nbbrd.service.Quantifier;
-import nbbrd.service.ServiceDefinition;
-
-import java.io.IOException;
-import java.util.List;
-
-@ServiceDefinition(
- quantifier = Quantifier.MULTIPLE,
- batch = true
-)
-public interface FailureFormatter {
-
- String getName();
-
- void format(Appendable appendable, String source, List failures) throws IOException;
-}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Scan.java b/heylogs-api/src/main/java/nbbrd/heylogs/Scan.java
deleted file mode 100644
index ea7eb1e..0000000
--- a/heylogs-api/src/main/java/nbbrd/heylogs/Scan.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package nbbrd.heylogs;
-
-import com.vladsch.flexmark.ast.Heading;
-import com.vladsch.flexmark.util.ast.Node;
-import org.semver4j.Semver;
-
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import static java.util.stream.Collectors.groupingBy;
-import static java.util.stream.Collectors.toList;
-import static nbbrd.heylogs.TimeRange.toTimeRange;
-
-@lombok.Value
-public class Scan {
-
- public static Scan of(Node document) {
- Map> versionByType = Nodes.of(Heading.class)
- .descendants(document)
- .filter(Version::isVersionLevel)
- .map(Scan::parseVersionOrNull)
- .filter(Objects::nonNull)
- .collect(Collectors.partitioningBy(Version::isUnreleased));
-
- boolean compatibleWithSemver = isCompatibleWithSemver(versionByType.get(false));
-
- return new Scan(
- versionByType.get(false).size(),
- versionByType.get(false).stream().map(Version::getDate).collect(toTimeRange()).orElse(TimeRange.ALL),
- compatibleWithSemver,
- compatibleWithSemver ? getDetails(versionByType.get(false)) : "",
- versionByType.containsKey(true)
- );
- }
-
- private static Version parseVersionOrNull(Heading heading) {
- try {
- return Version.parse(heading);
- } catch (IllegalArgumentException ex) {
- return null;
- }
- }
-
- int releaseCount;
- TimeRange timeRange;
- boolean compatibleWithSemver;
- String semverDetails;
- boolean hasUnreleasedSection;
-
- private static boolean isCompatibleWithSemver(List releases) {
- return releases.stream().map(Version::getRef).allMatch(Semver::isValid);
- }
-
- private static String getDetails(List releases) {
- List semvers = releases.stream().map(Version::getRef).map(Semver::parse).collect(toList());
-
- SortedMap> diffs = IntStream.range(1, semvers.size())
- .mapToObj(i -> semvers.get(i).diff(semvers.get(i - 1)))
- .collect(groupingBy((Semver.VersionDiff o) -> o, TreeMap::new, toList()));
-
- return diffs
- .entrySet()
- .stream()
- .map(entry -> entry.getValue().size() + " " + entry.getKey().toString())
- .collect(Collectors.joining(", ", " (", ")"));
- }
-}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Scanner.java b/heylogs-api/src/main/java/nbbrd/heylogs/Scanner.java
new file mode 100644
index 0000000..e68d9db
--- /dev/null
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Scanner.java
@@ -0,0 +1,101 @@
+package nbbrd.heylogs;
+
+import com.vladsch.flexmark.ast.Heading;
+import com.vladsch.flexmark.util.ast.Node;
+import lombok.NonNull;
+import nbbrd.design.StaticFactoryMethod;
+import nbbrd.heylogs.spi.Format;
+import nbbrd.heylogs.spi.FormatLoader;
+import org.semver4j.Semver;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+import static nbbrd.heylogs.TimeRange.toTimeRange;
+
+@lombok.Value
+@lombok.Builder(toBuilder = true)
+public class Scanner {
+
+ @StaticFactoryMethod
+ public static @NonNull Scanner ofServiceLoader() {
+ return builder()
+ .formats(FormatLoader.load())
+ .build();
+ }
+
+ @NonNull
+ @lombok.Singular
+ List formats;
+
+ @NonNull
+ @lombok.Builder.Default
+ String formatId = FIRST_FORMAT_AVAILABLE;
+
+ public @NonNull Status scan(@NonNull Node document) {
+ Map> versionByType = Nodes.of(Heading.class)
+ .descendants(document)
+ .filter(Version::isVersionLevel)
+ .map(Scanner::parseVersionOrNull)
+ .filter(Objects::nonNull)
+ .collect(Collectors.partitioningBy(Version::isUnreleased));
+
+ boolean compatibleWithSemver = isCompatibleWithSemver(versionByType.get(false));
+
+ return Status
+ .builder()
+ .releaseCount(versionByType.get(false).size())
+ .timeRange(versionByType.get(false).stream().map(Version::getDate).collect(toTimeRange()).orElse(TimeRange.ALL))
+ .compatibleWithSemver(compatibleWithSemver)
+ .semverDetails(compatibleWithSemver ? getDetails(versionByType.get(false)) : "")
+ .hasUnreleasedSection(versionByType.containsKey(true))
+ .build();
+ }
+
+ private static Version parseVersionOrNull(Heading heading) {
+ try {
+ return Version.parse(heading);
+ } catch (IllegalArgumentException ex) {
+ return null;
+ }
+ }
+
+ private static boolean isCompatibleWithSemver(List releases) {
+ return releases.stream().map(Version::getRef).allMatch(Semver::isValid);
+ }
+
+ private static String getDetails(List releases) {
+ List semvers = releases.stream().map(Version::getRef).map(Semver::parse).collect(toList());
+
+ TreeMap> diffs = IntStream.range(1, semvers.size())
+ .mapToObj(i -> semvers.get(i).diff(semvers.get(i - 1)))
+ .collect(groupingBy((Semver.VersionDiff o) -> o, TreeMap::new, toList()));
+
+ return diffs
+ .descendingMap()
+ .entrySet()
+ .stream()
+ .map(entry -> entry.getValue().size() + " " + entry.getKey().toString())
+ .collect(Collectors.joining(", ", " (", ")"));
+ }
+
+ public void formatStatus(@NonNull Appendable appendable, @NonNull String source, @NonNull Status status) throws IOException {
+ getFormatById().formatStatus(appendable, source, status);
+ }
+
+ private Format getFormatById() throws IOException {
+ return formats.stream()
+ .filter(format -> formatId.equals(FIRST_FORMAT_AVAILABLE) || format.getId().equals(formatId))
+ .findFirst()
+ .orElseThrow(() -> new IOException("Cannot find format '" + formatId + "'"));
+ }
+
+ private static final String FIRST_FORMAT_AVAILABLE = "";
+}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Status.java b/heylogs-api/src/main/java/nbbrd/heylogs/Status.java
new file mode 100644
index 0000000..2d9ed60
--- /dev/null
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Status.java
@@ -0,0 +1,21 @@
+package nbbrd.heylogs;
+
+@lombok.Value
+@lombok.Builder
+public class Status {
+
+ @lombok.Builder.Default
+ int releaseCount = 0;
+
+ @lombok.Builder.Default
+ TimeRange timeRange = TimeRange.ALL;
+
+ @lombok.Builder.Default
+ boolean compatibleWithSemver = false;
+
+ @lombok.Builder.Default
+ String semverDetails = "";
+
+ @lombok.Builder.Default
+ boolean hasUnreleasedSection = false;
+}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/TypeOfChange.java b/heylogs-api/src/main/java/nbbrd/heylogs/TypeOfChange.java
index fa3df35..527bc1b 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/TypeOfChange.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/TypeOfChange.java
@@ -3,10 +3,14 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ast.Text;
import com.vladsch.flexmark.util.sequence.BasedSequence;
+import lombok.NonNull;
+import nbbrd.design.RepresentableAs;
+import nbbrd.design.StaticFactoryMethod;
import java.util.stream.Stream;
@lombok.AllArgsConstructor
+@RepresentableAs(Heading.class)
public enum TypeOfChange implements BaseSection {
ADDED("Added"),
@@ -22,7 +26,7 @@ public enum TypeOfChange implements BaseSection {
final String label;
@Override
- public Heading toHeading() {
+ public @NonNull Heading toHeading() {
Heading result = new Heading();
result.setOpeningMarker(BasedSequence.repeatOf("#", HEADING_LEVEL));
result.setLevel(HEADING_LEVEL);
@@ -30,7 +34,8 @@ public Heading toHeading() {
return result;
}
- public static TypeOfChange parse(Heading heading) {
+ @StaticFactoryMethod
+ public static @NonNull TypeOfChange parse(@NonNull Heading heading) {
if (!isTypeOfChangeLevel(heading)) {
throw new IllegalArgumentException("Invalid heading level");
}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Version.java b/heylogs-api/src/main/java/nbbrd/heylogs/Version.java
index 5156675..fc62998 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/Version.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/Version.java
@@ -5,12 +5,16 @@
import com.vladsch.flexmark.ast.Text;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.sequence.BasedSequence;
+import lombok.NonNull;
+import nbbrd.design.RepresentableAs;
+import nbbrd.design.StaticFactoryMethod;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.Iterator;
-@lombok.Value
+@lombok.Value(staticConstructor = "of")
+@RepresentableAs(Heading.class)
public class Version implements BaseSection {
private static final String UNRELEASED_KEYWORD = "unreleased";
@@ -27,7 +31,7 @@ public boolean isUnreleased() {
}
@Override
- public Heading toHeading() {
+ public @NonNull Heading toHeading() {
Heading result = new Heading();
result.setOpeningMarker(BasedSequence.repeatOf("#", HEADING_LEVEL));
result.setLevel(HEADING_LEVEL);
@@ -47,7 +51,8 @@ public Heading toHeading() {
return result;
}
- public static Version parse(Heading heading) {
+ @StaticFactoryMethod
+ public static @NonNull Version parse(@NonNull Heading heading) {
if (!isVersionLevel(heading)) {
throw new IllegalArgumentException("Invalid heading level");
}
@@ -89,10 +94,6 @@ private static String parseRef(Node firstPart) throws IllegalArgumentException {
}
private static LocalDate parseDate(Node secondPart) throws IllegalArgumentException {
- if (!(secondPart instanceof Text)) {
- throw new IllegalArgumentException("Invalid date type");
- }
-
BasedSequence date = secondPart.getChars();
if (!date.trimStart().startsWith("-")) {
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/spi/Format.java b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Format.java
new file mode 100644
index 0000000..be997a4
--- /dev/null
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Format.java
@@ -0,0 +1,23 @@
+package nbbrd.heylogs.spi;
+
+import lombok.NonNull;
+import nbbrd.heylogs.Failure;
+import nbbrd.heylogs.Status;
+import nbbrd.service.Quantifier;
+import nbbrd.service.ServiceDefinition;
+
+import java.io.IOException;
+import java.util.List;
+
+@ServiceDefinition(
+ quantifier = Quantifier.MULTIPLE,
+ batch = true
+)
+public interface Format {
+
+ @NonNull String getId();
+
+ void formatFailures(@NonNull Appendable appendable, @NonNull String source, @NonNull List failures) throws IOException;
+
+ void formatStatus(@NonNull Appendable appendable, @NonNull String source, @NonNull Status status) throws IOException;
+}
diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Rule.java b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Rule.java
similarity index 69%
rename from heylogs-api/src/main/java/nbbrd/heylogs/Rule.java
rename to heylogs-api/src/main/java/nbbrd/heylogs/spi/Rule.java
index d82ee32..a6bbea0 100644
--- a/heylogs-api/src/main/java/nbbrd/heylogs/Rule.java
+++ b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Rule.java
@@ -1,14 +1,15 @@
-package nbbrd.heylogs;
+package nbbrd.heylogs.spi;
import com.vladsch.flexmark.util.ast.Node;
import lombok.NonNull;
+import nbbrd.heylogs.Failure;
import nbbrd.service.Quantifier;
import nbbrd.service.ServiceDefinition;
import nbbrd.service.ServiceFilter;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays;
import java.util.Properties;
-import java.util.stream.Stream;
@ServiceDefinition(
quantifier = Quantifier.MULTIPLE,
@@ -16,17 +17,19 @@
)
public interface Rule {
- String getName();
+ @NonNull String getId();
- Failure validate(Node node);
+ @Nullable Failure validate(@NonNull Node node);
@ServiceFilter
boolean isAvailable();
+ Failure NO_PROBLEM = null;
+
String ENABLE_KEY = "heylogs.rule.enable";
- static boolean isEnabled(@NonNull Properties properties, @NonNull String ruleName) {
+ static boolean isEnabled(@NonNull Properties properties, @NonNull String ruleId) {
String list = properties.getProperty(ENABLE_KEY);
- return list != null && Arrays.asList(list.split(",", -1)).contains(ruleName);
+ return list != null && Arrays.asList(list.split(",", -1)).contains(ruleId);
}
}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/Sample.java b/heylogs-api/src/test/java/_test/Sample.java
similarity index 91%
rename from heylogs-api/src/test/java/nbbrd/heylogs/Sample.java
rename to heylogs-api/src/test/java/_test/Sample.java
index a593aa1..1b28534 100644
--- a/heylogs-api/src/test/java/nbbrd/heylogs/Sample.java
+++ b/heylogs-api/src/test/java/_test/Sample.java
@@ -1,4 +1,4 @@
-package nbbrd.heylogs;
+package _test;
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.formatter.Formatter;
@@ -7,6 +7,7 @@
import com.vladsch.flexmark.util.sequence.BasedSequence;
import java.io.*;
+import java.nio.charset.StandardCharsets;
public class Sample {
@@ -18,7 +19,7 @@ public static Document using(String name) {
if (stream == null) {
throw new IllegalArgumentException("Missing resource '" + name + "'");
}
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
return PARSER.parseReader(reader);
}
} catch (IOException ex) {
diff --git a/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java b/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java
index f2de4f7..2a6583e 100644
--- a/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java
+++ b/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java
@@ -3,10 +3,9 @@
import com.vladsch.flexmark.ast.Link;
import com.vladsch.flexmark.ast.LinkNodeBase;
import com.vladsch.flexmark.util.ast.Node;
-import internal.heylogs.ExtendedRules;
import nbbrd.heylogs.Failure;
import nbbrd.heylogs.Nodes;
-import nbbrd.heylogs.Sample;
+import _test.Sample;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -15,7 +14,7 @@
import static internal.heylogs.ExtendedRules.GITHUB_ISSUE_REF;
import static internal.heylogs.ExtendedRules.HTTPS;
import static nbbrd.heylogs.Nodes.of;
-import static nbbrd.heylogs.Sample.using;
+import static _test.Sample.using;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Index.atIndex;
@@ -23,7 +22,7 @@ public class ExtendedRulesTest {
@Test
public void test() {
- Node sample = Sample.using("Main.md");
+ Node sample = Sample.using("/Main.md");
for (ExtendedRules rule : ExtendedRules.values()) {
Assertions.assertThat(Nodes.of(Node.class).descendants(sample).map(rule::validate).filter(Objects::nonNull))
.isEmpty();
@@ -32,34 +31,34 @@ public void test() {
@Test
public void testValidateHttps() {
- assertThat(of(LinkNodeBase.class).descendants(using("Main.md")))
+ assertThat(of(LinkNodeBase.class).descendants(using("/Main.md")))
.map(ExtendedRules::validateHttps)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(LinkNodeBase.class).descendants(using("NonHttps.md")))
+ assertThat(of(LinkNodeBase.class).descendants(using("/NonHttps.md")))
.map(ExtendedRules::validateHttps)
.isNotEmpty()
.filteredOn(Objects::nonNull)
- .contains(Failure.of(HTTPS, "Expecting HTTPS protocol", 1, 1), atIndex(0))
- .contains(Failure.of(HTTPS, "Expecting HTTPS protocol", 2, 7), atIndex(1))
+ .contains(Failure.builder().rule(HTTPS).message("Expecting HTTPS protocol").line(1).column(1).build(), atIndex(0))
+ .contains(Failure.builder().rule(HTTPS).message("Expecting HTTPS protocol").line(2).column(7).build(), atIndex(1))
.hasSize(2); // FIXME: should be 3
}
@Test
public void testValidateGitHubIssueRef() {
- assertThat(of(Link.class).descendants(using("Main.md")))
+ assertThat(of(Link.class).descendants(using("/Main.md")))
.map(ExtendedRules::validateGitHubIssueRef)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Link.class).descendants(using("InvalidGitHubIssueRef.md")))
+ assertThat(of(Link.class).descendants(using("/InvalidGitHubIssueRef.md")))
.map(ExtendedRules::validateGitHubIssueRef)
.isNotEmpty()
.filteredOn(Objects::nonNull)
- .contains(Failure.of(GITHUB_ISSUE_REF, "Expecting GitHub issue ref 172, found 173", 2, 1), atIndex(0))
+ .contains(Failure.builder().rule(GITHUB_ISSUE_REF).message("Expecting GitHub issue ref 172, found 173").line(2).column(1).build(), atIndex(0))
.hasSize(1);
}
}
diff --git a/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java b/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java
index b4d58d1..3c4dd09 100644
--- a/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java
+++ b/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java
@@ -2,7 +2,6 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.util.ast.Node;
-import internal.heylogs.GuidingPrinciples;
import nbbrd.heylogs.Failure;
import org.junit.jupiter.api.Test;
@@ -10,7 +9,7 @@
import static internal.heylogs.GuidingPrinciples.*;
import static nbbrd.heylogs.Nodes.of;
-import static nbbrd.heylogs.Sample.using;
+import static _test.Sample.using;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Index.atIndex;
@@ -18,7 +17,7 @@ public class GuidingPrinciplesTest {
@Test
public void testSample() {
- Node sample = using("Main.md");
+ Node sample = using("/Main.md");
for (GuidingPrinciples rule : GuidingPrinciples.values()) {
assertThat(of(Node.class).descendants(sample).map(rule::validate).filter(Objects::nonNull))
.isEmpty();
@@ -27,85 +26,85 @@ public void testSample() {
@Test
public void testValidateForHumans() {
- assertThat(validateForHumans(using("Main.md")))
+ assertThat(validateForHumans(using("/Main.md")))
.isNull();
- assertThat(validateForHumans(using("Empty.md")))
- .isEqualTo(Failure.of(FOR_HUMANS, "Missing Changelog heading", 1, 1));
+ assertThat(validateForHumans(using("/Empty.md")))
+ .isEqualTo(Failure.builder().rule(FOR_HUMANS).message("Missing Changelog heading").line(1).column(1).build());
- assertThat(validateForHumans(using("NoChangelog.md")))
- .isEqualTo(Failure.of(FOR_HUMANS, "Invalid text", 1, 1));
+ assertThat(validateForHumans(using("/NoChangelog.md")))
+ .isEqualTo(Failure.builder().rule(FOR_HUMANS).message("Invalid text").line(1).column(1).build());
- assertThat(validateForHumans(using("TooManyChangelog.md")))
- .isEqualTo(Failure.of(FOR_HUMANS, "Too many Changelog headings", 1, 1));
+ assertThat(validateForHumans(using("/TooManyChangelog.md")))
+ .isEqualTo(Failure.builder().rule(FOR_HUMANS).message("Too many Changelog headings").line(1).column(1).build());
}
@Test
public void testValidateEntryForEveryVersions() {
- assertThat(of(Heading.class).descendants(using("Main.md")))
+ assertThat(of(Heading.class).descendants(using("/Main.md")))
.map(GuidingPrinciples::validateEntryForEveryVersions)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Heading.class).descendants(using("InvalidVersion.md")))
+ assertThat(of(Heading.class).descendants(using("/InvalidVersion.md")))
.map(GuidingPrinciples::validateEntryForEveryVersions)
.isNotEmpty()
.filteredOn(Objects::nonNull)
- .contains(Failure.of(ENTRY_FOR_EVERY_VERSIONS, "Invalid date format", 2, 1), atIndex(0))
- .contains(Failure.of(ENTRY_FOR_EVERY_VERSIONS, "Missing date part", 3, 1), atIndex(1))
- .contains(Failure.of(ENTRY_FOR_EVERY_VERSIONS, "Missing ref link", 4, 1), atIndex(2))
+ .contains(Failure.builder().rule(ENTRY_FOR_EVERY_VERSIONS).message("Invalid date format").line(2).column(1).build(), atIndex(0))
+ .contains(Failure.builder().rule(ENTRY_FOR_EVERY_VERSIONS).message("Missing date part").line(3).column(1).build(), atIndex(1))
+ .contains(Failure.builder().rule(ENTRY_FOR_EVERY_VERSIONS).message("Missing ref link").line(4).column(1).build(), atIndex(2))
.hasSize(3);
}
@Test
public void testValidateTypeOfChangesGrouped() {
- assertThat(of(Heading.class).descendants(using("Main.md")))
+ assertThat(of(Heading.class).descendants(using("/Main.md")))
.map(GuidingPrinciples::validateTypeOfChangesGrouped)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Heading.class).descendants(using("InvalidTypeOfChange.md")))
+ assertThat(of(Heading.class).descendants(using("/InvalidTypeOfChange.md")))
.map(GuidingPrinciples::validateTypeOfChangesGrouped)
.isNotEmpty()
.filteredOn(Objects::nonNull)
- .contains(Failure.of(TYPE_OF_CHANGES_GROUPED, "Cannot parse 'Stuff'", 7, 1), atIndex(0))
+ .contains(Failure.builder().rule(TYPE_OF_CHANGES_GROUPED).message("Cannot parse 'Stuff'").line(7).column(1).build(), atIndex(0))
.hasSize(1);
}
@Test
public void testValidateLinkable() {
- assertThat(of(Heading.class).descendants(using("Main.md")))
+ assertThat(of(Heading.class).descendants(using("/Main.md")))
.map(GuidingPrinciples::validateLinkable)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Heading.class).descendants(using("MissingReference.md")))
+ assertThat(of(Heading.class).descendants(using("/MissingReference.md")))
.map(GuidingPrinciples::validateLinkable)
.isNotEmpty()
.filteredOn(Objects::nonNull)
- .contains(Failure.of(LINKABLE, "Missing reference '1.1.0'", 5, 1), atIndex(0))
+ .contains(Failure.builder().rule(LINKABLE).message("Missing reference '1.1.0'").line(5).column(1).build(), atIndex(0))
.hasSize(1);
}
@Test
public void testValidateLatestVersionFirst() {
- assertThat(validateLatestVersionFirst(using("Main.md")))
+ assertThat(validateLatestVersionFirst(using("/Main.md")))
.isNull();
- assertThat(validateLatestVersionFirst(using("Empty.md")))
+ assertThat(validateLatestVersionFirst(using("/Empty.md")))
.isNull();
- assertThat(validateLatestVersionFirst(using("NotLatestVersionFirst.md")))
- .isEqualTo(Failure.of(LATEST_VERSION_FIRST, "Versions not sorted", 3, 1));
+ assertThat(validateLatestVersionFirst(using("/NotLatestVersionFirst.md")))
+ .isEqualTo(Failure.builder().rule(LATEST_VERSION_FIRST).message("Versions not sorted").line(3).column(1).build());
- assertThat(validateLatestVersionFirst(using("UnsortedVersion.md")))
- .isEqualTo(Failure.of(LATEST_VERSION_FIRST, "Versions not sorted", 3, 1));
+ assertThat(validateLatestVersionFirst(using("/UnsortedVersion.md")))
+ .isEqualTo(Failure.builder().rule(LATEST_VERSION_FIRST).message("Versions not sorted").line(3).column(1).build());
- assertThat(validateLatestVersionFirst(using("InvalidVersion.md")))
- .isEqualTo(Failure.of(LATEST_VERSION_FIRST, "Versions not sorted", 5, 1));
+ assertThat(validateLatestVersionFirst(using("/InvalidVersion.md")))
+ .isEqualTo(Failure.builder().rule(LATEST_VERSION_FIRST).message("Versions not sorted").line(5).column(1).build());
}
}
diff --git a/heylogs-api/src/test/java/internal/heylogs/SemverRuleTest.java b/heylogs-api/src/test/java/internal/heylogs/SemverRuleTest.java
index 7946ab9..b92b2ec 100644
--- a/heylogs-api/src/test/java/internal/heylogs/SemverRuleTest.java
+++ b/heylogs-api/src/test/java/internal/heylogs/SemverRuleTest.java
@@ -2,14 +2,13 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.util.ast.Node;
-import internal.heylogs.SemverRule;
import nbbrd.heylogs.Failure;
import org.junit.jupiter.api.Test;
import java.util.Objects;
import static nbbrd.heylogs.Nodes.of;
-import static nbbrd.heylogs.Sample.using;
+import static _test.Sample.using;
import static org.assertj.core.api.Assertions.assertThat;
public class SemverRuleTest {
@@ -18,7 +17,7 @@ public class SemverRuleTest {
public void testSample() {
SemverRule x = new SemverRule();
- assertThat(of(Node.class).descendants(using("Main.md")))
+ assertThat(of(Node.class).descendants(using("/Main.md")))
.map(x::validate)
.filteredOn(Objects::nonNull)
.isEmpty();
@@ -28,20 +27,20 @@ public void testSample() {
public void testValidateSemVer() {
SemverRule x = new SemverRule();
- assertThat(of(Heading.class).descendants(using("Main.md")))
+ assertThat(of(Heading.class).descendants(using("/Main.md")))
.map(x::validateSemVer)
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Heading.class).descendants(using("Empty.md")))
+ assertThat(of(Heading.class).descendants(using("/Empty.md")))
.map(x::validateSemVer)
.filteredOn(Objects::nonNull)
.isEmpty();
- assertThat(of(Heading.class).descendants(using("InvalidSemver.md")))
+ assertThat(of(Heading.class).descendants(using("/InvalidSemver.md")))
.map(x::validateSemVer)
.filteredOn(Objects::nonNull)
.hasSize(1)
- .contains(Failure.of(x, "Invalid semver format: '.1.0'", 2, 1));
+ .contains(Failure.builder().rule(x).message("Invalid semver format: '.1.0'").line(2).column(1).build());
}
}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/AboutTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/AboutTest.java
new file mode 100644
index 0000000..a56dee7
--- /dev/null
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/AboutTest.java
@@ -0,0 +1,14 @@
+package nbbrd.heylogs;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AboutTest {
+
+ @Test
+ public void testVersion() {
+ assertThat(About.VERSION)
+ .isEqualTo("unknown");
+ }
+}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java
index b7ecadc..edd41f3 100644
--- a/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java
@@ -1,43 +1,42 @@
package nbbrd.heylogs;
+import _test.Sample;
import com.vladsch.flexmark.ast.Heading;
-import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
-import java.time.LocalDate;
-
+import static _test.Sample.using;
+import static nbbrd.heylogs.Changelog.parse;
+import static _test.Sample.asHeading;
import static org.assertj.core.api.Assertions.*;
public class ChangelogTest {
@Test
- public void testParseHeading() {
- assertThat(parsingHeading("# Changelog"))
+ public void testParse() {
+ //noinspection DataFlowIssue
+ assertThatNullPointerException()
+ .isThrownBy(() -> parse(null));
+
+ assertThat(parse(asHeading("# Changelog")))
.isEqualTo(Changelog.INSTANCE);
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## Changelog"))
+ .isThrownBy(() -> parse(asHeading("## Changelog")))
.withMessageContaining("Invalid heading level");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("# hello"))
+ .isThrownBy(() -> parse(asHeading("# hello")))
.withMessageContaining("Invalid text");
- assertThat(Nodes.of(Heading.class).descendants(Sample.using("Main.md")).filter(Changelog::isChangelogLevel).map(Changelog::parse))
+ assertThat(Nodes.of(Heading.class).descendants(using("/Main.md")).filter(Changelog::isChangelogLevel).map(Changelog::parse))
.hasSize(1)
.contains(Changelog.INSTANCE, atIndex(0));
}
@Test
- public void testFormatHeading() {
+ public void testToHeading() {
assertThat(Changelog.INSTANCE.toHeading())
- .extracting(Sample::asText)
- .asString()
+ .extracting(Sample::asText, STRING)
.isEqualTo("# Changelog");
}
-
- @NotNull
- private Changelog parsingHeading(String text) {
- return Changelog.parse(Sample.asHeading(text));
- }
}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/CheckerTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/CheckerTest.java
new file mode 100644
index 0000000..fd92946
--- /dev/null
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/CheckerTest.java
@@ -0,0 +1,64 @@
+package nbbrd.heylogs;
+
+import internal.heylogs.SemverRule;
+import nbbrd.heylogs.spi.Rule;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static _test.Sample.using;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIOException;
+import static org.assertj.core.api.InstanceOfAssertFactories.list;
+
+public class CheckerTest {
+
+ @Test
+ public void testFactories() {
+ assertThat(Checker.builder().build())
+ .returns(0, checker -> checker.getRules().size())
+ .returns(0, checker -> checker.getFormats().size());
+
+ assertThat(Checker.ofServiceLoader())
+ .extracting(Checker::getRules, list(Rule.class))
+ .hasSizeGreaterThan(1)
+ .map(Rule::getId)
+ .doesNotContain("semver");
+
+ assertThat(Checker.ofServiceLoader().toBuilder().rule(new SemverRule()).build())
+ .extracting(Checker::getRules, list(Rule.class))
+ .hasSizeGreaterThan(1)
+ .map(Rule::getId)
+ .contains("semver");
+ }
+
+ @Test
+ public void testValidate() {
+ assertThat(Checker.builder().build().validate(using("/InvalidVersion.md")))
+ .isEmpty();
+
+ assertThat(Checker.ofServiceLoader().validate(using("/InvalidVersion.md")))
+ .isNotEmpty();
+ }
+
+ @Test
+ public void testFormatFailures() throws IOException {
+ assertThatIOException()
+ .isThrownBy(() -> Checker.builder().build().formatFailures(new StringBuilder(), "", emptyList()));
+
+ assertThatIOException()
+ .isThrownBy(() -> Checker.ofServiceLoader().toBuilder().formatId("other").build().formatFailures(new StringBuilder(), "", emptyList()));
+
+ StringBuilder output = new StringBuilder();
+ Checker.ofServiceLoader().formatFailures(output, "file1", asList(Failure.builder().ruleId("rule1").message("some message").line(10).column(20).build()));
+ assertThat(output.toString())
+ .isEqualToIgnoringNewLines(
+ "file1\n" +
+ " 10:20 error some message rule1\n" +
+ "\n" +
+ " 1 problem\n"
+ );
+ }
+}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java
new file mode 100644
index 0000000..4ae25a4
--- /dev/null
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java
@@ -0,0 +1,171 @@
+package nbbrd.heylogs;
+
+import _test.Sample;
+import com.vladsch.flexmark.util.ast.Document;
+import org.assertj.core.api.Condition;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.function.Function;
+
+import static _test.Sample.using;
+import static nbbrd.heylogs.Extractor.builder;
+import static nbbrd.heylogs.Extractor.parseLocalDate;
+import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
+
+public class ExtractorTest {
+
+ @Test
+ public void testRef() {
+ assertThat(builder().build())
+ .describedAs("Empty reference")
+ .is(containing(unreleased))
+ .is(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(builder().ref("Unreleased").build())
+ .describedAs("Full reference")
+ .is(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+
+ assertThat(builder().ref("1.1.0").build())
+ .describedAs("Full reference")
+ .isNot(containing(unreleased))
+ .is(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+
+ assertThat(builder().ref("rel").build())
+ .describedAs("Partial reference")
+ .is(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+
+ assertThat(builder().ref("1.").build())
+ .describedAs("Partial reference")
+ .isNot(containing(unreleased))
+ .is(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(builder().ref("other").build())
+ .describedAs("Unknown reference")
+ .isNot(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+
+ assertThat(builder().ref("other-SNAPSHOT").build())
+ .describedAs("Matching unreleased pattern reference")
+ .is(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+ }
+
+ @Test
+ public void testTimeRange() {
+ Function onTimeRange = o -> builder().timeRange(o).build();
+
+ assertThat(TimeRange.ALL)
+ .extracting(onTimeRange)
+ .is(containing(unreleased))
+ .is(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(TimeRange.of(v1_0_0.getDate(), v1_1_0.getDate()))
+ .extracting(onTimeRange)
+ .isNot(containing(unreleased))
+ .is(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(TimeRange.of(v1_0_0.getDate(), v1_0_0.getDate()))
+ .extracting(onTimeRange)
+ .isNot(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(TimeRange.of(LocalDate.MIN, v1_0_0.getDate()))
+ .extracting(onTimeRange)
+ .isNot(containing(unreleased))
+ .isNot(containing(v1_1_0))
+ .is(containing(v1_0_0));
+
+ assertThat(TimeRange.of(v1_1_0.getDate(), v1_1_0.getDate()))
+ .extracting(onTimeRange)
+ .isNot(containing(unreleased))
+ .is(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+
+ assertThat(TimeRange.of(v1_1_0.getDate(), LocalDate.MAX))
+ .extracting(onTimeRange)
+ .is(containing(unreleased))
+ .is(containing(v1_1_0))
+ .isNot(containing(v1_0_0));
+ }
+
+
+ @Test
+ public void testExtract() {
+ Function usingMain = extractor -> {
+ Document doc = using("/Main.md");
+ extractor.extract(doc);
+ return Sample.FORMATTER.render(doc);
+ };
+
+ assertThat(builder().ref("1.1.0").build())
+ .extracting(usingMain, STRING)
+ .isEqualTo(
+ "## [1.1.0] - 2019-02-15\n" +
+ "\n" +
+ "### Added\n" +
+ "\n" +
+ "- Danish translation from [@frederikspang](https://github.com/frederikspang).\n" +
+ "- Georgian translation from [@tatocaster](https://github.com/tatocaster).\n" +
+ "- Changelog inconsistency section in Bad Practices\n" +
+ "\n" +
+ "### Changed\n" +
+ "\n" +
+ "- Fixed typos in Italian translation from [@lorenzo-arena](https://github.com/lorenzo-arena).\n" +
+ "- Fixed typos in Indonesian translation from [@ekojs](https://github.com/ekojs).\n" +
+ "\n" +
+ "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n" +
+ "\n");
+
+ assertThat(builder().ref("1.1.0").ignoreContent(true).build())
+ .extracting(usingMain, STRING)
+ .isEqualTo(
+ "## [1.1.0] - 2019-02-15\n" +
+ "\n" +
+ "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n" +
+ "\n");
+
+ assertThat(builder().ref("zzz").build())
+ .extracting(usingMain, STRING)
+ .isEmpty();
+ }
+
+ @Test
+ public void testParseLocalDate() {
+ assertThatNullPointerException()
+ .isThrownBy(() -> parseLocalDate(null));
+
+ assertThatExceptionOfType(RuntimeException.class)
+ .isThrownBy(() -> parseLocalDate(""));
+
+ assertThat(parseLocalDate("2010"))
+ .isEqualTo("2010-01-01");
+
+ assertThat(parseLocalDate("2010-02"))
+ .isEqualTo("2010-02-01");
+
+ assertThat(parseLocalDate("2010-02-03"))
+ .isEqualTo("2010-02-03");
+ }
+
+ private static Condition containing(Version version) {
+ return new Condition<>(parent -> parent.contains(version), "Must contain %s", version);
+ }
+
+ private final Version unreleased = Version.of("Unreleased", LocalDate.MAX);
+ private final Version v1_1_0 = Version.of("1.1.0", LocalDate.parse("2019-02-15"));
+ private final Version v1_0_0 = Version.of("1.0.0", LocalDate.parse("2017-06-20"));
+}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ScanTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/ScanTest.java
deleted file mode 100644
index d2a7dd5..0000000
--- a/heylogs-api/src/test/java/nbbrd/heylogs/ScanTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package nbbrd.heylogs;
-
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDate;
-
-import static nbbrd.heylogs.Sample.using;
-import static org.assertj.core.api.Assertions.assertThat;
-
-class ScanTest {
-
- @Test
- void of() {
- assertThat(Scan.of(using("Empty.md")))
- .isEqualTo(new Scan(
- 0,
- TimeRange.ALL,
- true, " ()",
- true
- ));
-
- assertThat(Scan.of(using("Main.md")))
- .isEqualTo(new Scan(
- 13,
- TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15)),
- true, " (1 MAJOR, 4 MINOR, 7 PATCH)",
- true
- ));
-
- assertThat(Scan.of(using("InvalidSemver.md")))
- .isEqualTo(new Scan(
- 2,
- TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
- false, "",
- true
- ));
- }
-}
\ No newline at end of file
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ScannerTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/ScannerTest.java
new file mode 100644
index 0000000..f96d252
--- /dev/null
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/ScannerTest.java
@@ -0,0 +1,75 @@
+package nbbrd.heylogs;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.time.LocalDate;
+
+import static _test.Sample.using;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIOException;
+
+class ScannerTest {
+
+ @Test
+ void testScan() {
+ Scanner x = Scanner.ofServiceLoader();
+
+ assertThat(x.scan(using("/Empty.md")))
+ .isEqualTo(new Status(
+ 0,
+ TimeRange.ALL,
+ true, " ()",
+ true
+ ));
+
+ assertThat(x.scan(using("/Main.md")))
+ .isEqualTo(new Status(
+ 13,
+ TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15)),
+ true, " (1 MAJOR, 4 MINOR, 7 PATCH)",
+ true
+ ));
+
+ assertThat(x.scan(using("/InvalidSemver.md")))
+ .isEqualTo(new Status(
+ 2,
+ TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
+ false, "",
+ true
+ ));
+
+ assertThat(x.scan(using("/InvalidVersion.md")))
+ .isEqualTo(new Status(
+ 1,
+ TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
+ true, " ()",
+ true
+ ));
+ }
+
+ @Test
+ public void testFormatStatus() throws IOException {
+ assertThatIOException()
+ .isThrownBy(() -> Scanner.builder().build().formatStatus(new StringBuilder(), "", Status.builder().build()));
+
+ assertThatIOException()
+ .isThrownBy(() -> Scanner.ofServiceLoader().toBuilder().formatId("other").build().formatStatus(new StringBuilder(), "", Status.builder().build()));
+
+ StringBuilder output = new StringBuilder();
+ Scanner.ofServiceLoader().formatStatus(output, "file1", new Status(
+ 1,
+ TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
+ true, " ()",
+ true
+ ));
+ assertThat(output.toString())
+ .isEqualToIgnoringNewLines(
+ "file1\n" +
+ " Found 1 releases\n" +
+ " Ranging from 2019-02-15 to 2019-02-15\n" +
+ " Compatible with Semantic Versioning ()\n" +
+ " Has an unreleased version\n"
+ );
+ }
+}
\ No newline at end of file
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java
index b2303ac..ebd732c 100644
--- a/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java
@@ -1,45 +1,46 @@
package nbbrd.heylogs;
+import _test.Sample;
import com.vladsch.flexmark.ast.Heading;
-import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
+import static _test.Sample.asHeading;
+import static _test.Sample.using;
+import static nbbrd.heylogs.TypeOfChange.parse;
import static org.assertj.core.api.Assertions.*;
public class TypeOfChangeTest {
@Test
- public void testParseHeading() {
- assertThat(parsingHeading("### Added"))
+ public void testParse() {
+ //noinspection DataFlowIssue
+ assertThatNullPointerException()
+ .isThrownBy(() -> parse(null));
+
+ assertThat(parse(asHeading("### Added")))
.isEqualTo(TypeOfChange.ADDED);
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## Added"))
+ .isThrownBy(() -> parse(asHeading("## Added")))
.withMessageContaining("Invalid heading level");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("#### Added"))
+ .isThrownBy(() -> parse(asHeading("#### Added")))
.withMessageContaining("Invalid heading level");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("### hello"))
+ .isThrownBy(() -> parse(asHeading("### hello")))
.withMessageContaining("Cannot parse");
- assertThat(Nodes.of(Heading.class).descendants(Sample.using("Main.md")).filter(TypeOfChange::isTypeOfChangeLevel).map(TypeOfChange::parse))
+ assertThat(Nodes.of(Heading.class).descendants(using("/Main.md")).filter(TypeOfChange::isTypeOfChangeLevel).map(TypeOfChange::parse))
.hasSize(24)
.contains(TypeOfChange.ADDED, atIndex(0));
}
@Test
- public void testFormatHeading() {
+ public void testToHeading() {
assertThat(TypeOfChange.ADDED.toHeading())
- .extracting(Sample::asText)
- .asString()
+ .extracting(Sample::asText, STRING)
.isEqualTo("### Added");
}
-
- @NotNull
- private TypeOfChange parsingHeading(String text) {
- return TypeOfChange.parse(Sample.asHeading(text));
- }
}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/VersionFilterTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/VersionFilterTest.java
deleted file mode 100644
index 6cb3502..0000000
--- a/heylogs-api/src/test/java/nbbrd/heylogs/VersionFilterTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package nbbrd.heylogs;
-
-import org.assertj.core.api.Condition;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDate;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class VersionFilterTest {
-
- @Test
- public void testRef() {
- assertThat(VersionFilter.builder().build())
- .describedAs("Empty reference")
- .is(containing(unreleased))
- .is(containing(v1_1_1));
-
- assertThat(VersionFilter.builder().ref("Unreleased").build())
- .describedAs("Full reference")
- .is(containing(unreleased))
- .isNot(containing(v1_1_1));
-
- assertThat(VersionFilter.builder().ref("1.1.0").build())
- .describedAs("Full reference")
- .isNot(containing(unreleased))
- .is(containing(v1_1_1));
-
- assertThat(VersionFilter.builder().ref("rel").build())
- .describedAs("Partial reference")
- .is(containing(unreleased))
- .isNot(containing(v1_1_1));
-
- assertThat(VersionFilter.builder().ref("other").build())
- .describedAs("Unknown reference")
- .isNot(containing(unreleased))
- .isNot(containing(v1_1_1));
-
- assertThat(VersionFilter.builder().ref("other-SNAPSHOT").build())
- .describedAs("Matching unreleased pattern reference")
- .is(containing(unreleased))
- .isNot(containing(v1_1_1));
- }
-
- private static Condition containing(Version version) {
- return new Condition<>(parent -> parent.contains(version), "Must contain %s", version);
- }
-
- private final Version unreleased = new Version("Unreleased", LocalDate.MAX);
- private final Version v1_1_1 = new Version("1.1.0", LocalDate.parse("2019-02-15"));
-}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java
index 8fcb5a6..c031f1a 100644
--- a/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java
@@ -1,89 +1,98 @@
package nbbrd.heylogs;
+import _test.Sample;
import com.vladsch.flexmark.ast.Heading;
-import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
+import static _test.Sample.asHeading;
+import static _test.Sample.using;
+import static nbbrd.heylogs.Version.parse;
import static org.assertj.core.api.Assertions.*;
public class VersionTest {
@Test
- public void testParseHeading() {
- assertThat(parsingHeading("## [Unreleased]"))
- .isEqualTo(new Version("Unreleased", LocalDate.MAX));
+ public void testParse() {
+ //noinspection DataFlowIssue
+ assertThatNullPointerException()
+ .isThrownBy(() -> parse(null));
- assertThat(parsingHeading("## [Unreleased ]"))
- .isEqualTo(new Version("Unreleased", LocalDate.MAX));
+ assertThat(parse(asHeading("## [Unreleased]")))
+ .isEqualTo(Version.of("Unreleased", LocalDate.MAX));
+
+ assertThat(parse(asHeading("## [Unreleased ]")))
+ .isEqualTo(Version.of("Unreleased", LocalDate.MAX));
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## [Unreleased] - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("## [Unreleased] - 2019-02-15")))
.withMessageContaining("Unexpected additional part");
- assertThat(parsingHeading("## [1.1.0] - 2019-02-15"))
- .isEqualTo(new Version("1.1.0", d20190215));
+ assertThat(parse(asHeading("## [1.1.0] - 2019-02-15")))
+ .isEqualTo(Version.of("1.1.0", d20190215));
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("# [1.1.0] - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("# [1.1.0] - 2019-02-15")))
.withMessageContaining("Invalid heading level");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("### [1.1.0] - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("### [1.1.0] - 2019-02-15")))
.withMessageContaining("Invalid heading level");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## Unreleased"))
+ .isThrownBy(() -> parse(asHeading("##")))
+ .withMessageContaining("Missing ref part");
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> parse(asHeading("## Unreleased")))
.withMessageContaining("Missing ref link");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## 1.1.0 - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("## 1.1.0 - 2019-02-15")))
.withMessageContaining("Missing ref link");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## [1.1.0](https://localhost) - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("## [1.1.0](https://localhost) - 2019-02-15")))
.withMessageContaining("Missing ref link");
assertThatIllegalArgumentException()
- .isThrownBy(() -> Version.parse(Sample.asHeading("## [1.1.0] - ")))
- .withMessageContaining("Invalid date");
+ .isThrownBy(() -> parse(asHeading("## [1.1.0] 2019-02-15")))
+ .withMessageContaining("Missing date prefix");
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> parse(asHeading("## [1.1.0] - ")))
+ .withMessageContaining("Invalid date format");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## [1.1.0] - 2019-02"))
- .withMessageContaining("Invalid date");
+ .isThrownBy(() -> parse(asHeading("## [1.1.0] - 2019-02")))
+ .withMessageContaining("Invalid date format");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## - 2019-02-15"))
+ .isThrownBy(() -> parse(asHeading("## - 2019-02-15")))
.withMessageContaining("Missing ref link");
assertThatIllegalArgumentException()
- .isThrownBy(() -> parsingHeading("## [1.1.0] - 2019-02-15 [hello]"))
+ .isThrownBy(() -> parse(asHeading("## [1.1.0] - 2019-02-15 [hello]")))
.withMessageContaining("Unexpected additional part");
- assertThat(Nodes.of(Heading.class).descendants(Sample.using("Main.md")).filter(Version::isVersionLevel).map(Version::parse))
+ assertThat(Nodes.of(Heading.class).descendants(using("/Main.md")).filter(Version::isVersionLevel).map(Version::parse))
.hasSize(14)
- .contains(new Version("Unreleased", LocalDate.MAX), atIndex(0))
- .contains(new Version("1.1.0", d20190215), atIndex(1));
+ .contains(Version.of("Unreleased", LocalDate.MAX), atIndex(0))
+ .contains(Version.of("1.1.0", d20190215), atIndex(1));
}
@Test
- public void testFormatHeading() {
- assertThat(new Version("Unreleased", LocalDate.MAX).toHeading())
+ public void testToHeading() {
+ assertThat(Version.of("Unreleased", LocalDate.MAX).toHeading())
.extracting(Sample::asText)
.asString()
.isEqualTo("## [Unreleased]");
- assertThat(new Version("1.1.0", d20190215).toHeading())
- .extracting(Sample::asText)
- .asString()
+ assertThat(Version.of("1.1.0", d20190215).toHeading())
+ .extracting(Sample::asText, STRING)
.isEqualTo("## [1.1.0] - 2019-02-15");
}
- @NotNull
- private Version parsingHeading(String text) {
- return Version.parse(Sample.asHeading(text));
- }
-
private final LocalDate d20190215 = LocalDate.parse("2019-02-15");
}
diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/RuleTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/spi/RuleTest.java
similarity index 90%
rename from heylogs-api/src/test/java/nbbrd/heylogs/RuleTest.java
rename to heylogs-api/src/test/java/nbbrd/heylogs/spi/RuleTest.java
index 874c0cc..0d881b4 100644
--- a/heylogs-api/src/test/java/nbbrd/heylogs/RuleTest.java
+++ b/heylogs-api/src/test/java/nbbrd/heylogs/spi/RuleTest.java
@@ -1,10 +1,11 @@
-package nbbrd.heylogs;
+package nbbrd.heylogs.spi;
+import nbbrd.heylogs.spi.Rule;
import org.junit.jupiter.api.Test;
import java.util.Properties;
-import static nbbrd.heylogs.Rule.isEnabled;
+import static nbbrd.heylogs.spi.Rule.isEnabled;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/Empty.md b/heylogs-api/src/test/resources/Empty.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/Empty.md
rename to heylogs-api/src/test/resources/Empty.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/InvalidGitHubIssueRef.md b/heylogs-api/src/test/resources/InvalidGitHubIssueRef.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/InvalidGitHubIssueRef.md
rename to heylogs-api/src/test/resources/InvalidGitHubIssueRef.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/InvalidHeadingLevel.md b/heylogs-api/src/test/resources/InvalidHeadingLevel.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/InvalidHeadingLevel.md
rename to heylogs-api/src/test/resources/InvalidHeadingLevel.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/InvalidSemver.md b/heylogs-api/src/test/resources/InvalidSemver.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/InvalidSemver.md
rename to heylogs-api/src/test/resources/InvalidSemver.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/InvalidTypeOfChange.md b/heylogs-api/src/test/resources/InvalidTypeOfChange.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/InvalidTypeOfChange.md
rename to heylogs-api/src/test/resources/InvalidTypeOfChange.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/InvalidVersion.md b/heylogs-api/src/test/resources/InvalidVersion.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/InvalidVersion.md
rename to heylogs-api/src/test/resources/InvalidVersion.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/Main.md b/heylogs-api/src/test/resources/Main.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/Main.md
rename to heylogs-api/src/test/resources/Main.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/MissingReference.md b/heylogs-api/src/test/resources/MissingReference.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/MissingReference.md
rename to heylogs-api/src/test/resources/MissingReference.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/NoChangelog.md b/heylogs-api/src/test/resources/NoChangelog.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/NoChangelog.md
rename to heylogs-api/src/test/resources/NoChangelog.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/NonHttps.md b/heylogs-api/src/test/resources/NonHttps.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/NonHttps.md
rename to heylogs-api/src/test/resources/NonHttps.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/NotLatestVersionFirst.md b/heylogs-api/src/test/resources/NotLatestVersionFirst.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/NotLatestVersionFirst.md
rename to heylogs-api/src/test/resources/NotLatestVersionFirst.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/TooManyChangelog.md b/heylogs-api/src/test/resources/TooManyChangelog.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/TooManyChangelog.md
rename to heylogs-api/src/test/resources/TooManyChangelog.md
diff --git a/heylogs-api/src/test/resources/nbbrd/heylogs/UnsortedVersion.md b/heylogs-api/src/test/resources/UnsortedVersion.md
similarity index 100%
rename from heylogs-api/src/test/resources/nbbrd/heylogs/UnsortedVersion.md
rename to heylogs-api/src/test/resources/UnsortedVersion.md
diff --git a/heylogs-bom/pom.xml b/heylogs-bom/pom.xml
new file mode 100644
index 0000000..c204fbe
--- /dev/null
+++ b/heylogs-bom/pom.xml
@@ -0,0 +1,161 @@
+
+
+ 4.0.0
+
+
+ com.github.nbbrd.heylogs
+ heylogs-parent
+ 0.6.0
+
+
+ heylogs-bom
+ pom
+
+ heylogs-bom
+ Keep-a-changelog tool - Bill of Materials
+ https://github.com/nbbrd/heylogs
+
+
+
+
+ heylogs-api
+ ${project.groupId}
+ ${project.version}
+
+
+ heylogs-cli
+ ${project.groupId}
+ ${project.version}
+
+
+ heylogs-maven-plugin
+ ${project.groupId}
+ ${project.version}
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ 1.5.0
+
+ bom
+ ${project.build.directory}
+
+
+
+ flatten
+ process-resources
+
+ flatten
+
+
+
+
+
+
+
+
+ com.github.nbbrd.heylogs
+ heylogs-maven-plugin
+ ${project.version}
+
+
+ changelog
+
+ check
+ extract
+ scan
+ list
+
+
+ ${project.parent.basedir}/CHANGELOG.md
+ true
+
+
+
+
+
+
+
+
+
+
+
+ full-release
+
+
+
+
+ org.jreleaser
+ jreleaser-maven-plugin
+ 1.5.1
+
+
+ release-assets
+ install
+
+ full-release
+
+
+
+
+
+ true
+
+ master
+
+ ${project.build.directory}/CHANGELOG.md
+
+
+
+
+ ALWAYS
+
+ true
+
+
+
+
+ SINGLE_JAR
+
+
+
+ ${project.parent.basedir}/heylogs-cli/target/heylogs-cli-${project.version}-bin.jar
+
+
+
+
+ nbbrd.heylogs.cli.HeylogsCommand
+ heylogs-cli
+ 8
+
+
+ RELEASE
+
+
+ RELEASE
+
+ true
+
+
+
+ RELEASE
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/heylogs-cli/pom.xml b/heylogs-cli/pom.xml
index 6b56e14..0a6de69 100644
--- a/heylogs-cli/pom.xml
+++ b/heylogs-cli/pom.xml
@@ -7,7 +7,7 @@
com.github.nbbrd.heylogs
heylogs-parent
- 0.5.0
+ 0.6.0
heylogs-cli
@@ -17,10 +17,6 @@
Keep-a-changelog tool - CLI
https://github.com/nbbrd/heylogs
-
- nbbrd.heylogs.cli.MainCommand
-
-
@@ -63,6 +59,12 @@
com.github.nbbrd.java-console-properties
java-console-properties
1.4.0
+
+
+ com.github.nbbrd.java-io-util
+ java-io-base
+
+
@@ -110,7 +112,7 @@
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
- ${project.x.mainClass}
+ nbbrd.heylogs.cli.HeylogsCommand
true
@@ -118,103 +120,6 @@
false
-
-
-
-
- com.github.nbbrd.heylogs
- heylogs-maven-plugin
- ${project.version}
-
-
- extract-changelog
-
- check
- extract
-
-
- ${project.parent.basedir}/CHANGELOG.md
- true
-
-
-
-
-
-
-
- full-release
-
-
-
- org.jreleaser
- jreleaser-maven-plugin
- 1.3.1
-
-
- install
-
- full-release
-
-
-
-
-
-
-
- true
- false
-
- master
-
- ${project.build.directory}/CHANGELOG.md
-
-
-
-
- true
-
-
- ALWAYS
-
- true
-
-
-
-
- SINGLE_JAR
-
-
-
- ${project.build.directory}/${project.artifactId}-${project.version}-bin.jar
-
-
-
-
- ${project.x.mainClass}
- ${project.artifactId}
- 8
-
-
- RELEASE
-
-
- RELEASE
-
- true
-
-
-
- RELEASE
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/FailureFormatOptions.java b/heylogs-cli/src/main/java/internal/heylogs/cli/FailureFormatOptions.java
deleted file mode 100644
index c27a7bc..0000000
--- a/heylogs-cli/src/main/java/internal/heylogs/cli/FailureFormatOptions.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package internal.heylogs.cli;
-
-import nbbrd.heylogs.FailureFormatter;
-import nbbrd.heylogs.FailureFormatterLoader;
-import picocli.CommandLine;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-@lombok.Getter
-@lombok.Setter
-public class FailureFormatOptions {
-
- @CommandLine.Option(
- names = {"-f", "--formatter"},
- paramLabel = "",
- defaultValue = "stylish",
- description = "Specify the formatter used to control the appearance of the result. Valid values: ${COMPLETION-CANDIDATES}.",
- completionCandidates = FailureFormatters.class,
- converter = FailureFormatters.class
- )
- private FailureFormatter formatter;
-
- public static final class FailureFormatters implements Iterable, CommandLine.ITypeConverter {
-
- private final List formatters = FailureFormatterLoader.load();
-
- @Override
- public Iterator iterator() {
- return formatters
- .stream()
- .map(FailureFormatter::getName)
- .iterator();
- }
-
- @Override
- public FailureFormatter convert(String value) throws Exception {
- return formatters
- .stream()
- .filter(formatter -> formatter.getName().equals(value))
- .findFirst()
- .orElseThrow(NoSuchElementException::new);
- }
- }
-}
diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/FormatCandidates.java b/heylogs-cli/src/main/java/internal/heylogs/cli/FormatCandidates.java
new file mode 100644
index 0000000..99e7dd8
--- /dev/null
+++ b/heylogs-cli/src/main/java/internal/heylogs/cli/FormatCandidates.java
@@ -0,0 +1,17 @@
+package internal.heylogs.cli;
+
+import nbbrd.heylogs.spi.Format;
+import nbbrd.heylogs.spi.FormatLoader;
+
+import java.util.Iterator;
+
+public final class FormatCandidates implements Iterable {
+
+ @Override
+ public Iterator iterator() {
+ return FormatLoader.load()
+ .stream()
+ .map(Format::getId)
+ .iterator();
+ }
+}
diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/RuleSetOptions.java b/heylogs-cli/src/main/java/internal/heylogs/cli/RuleSetOptions.java
deleted file mode 100644
index 76bf9a8..0000000
--- a/heylogs-cli/src/main/java/internal/heylogs/cli/RuleSetOptions.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package internal.heylogs.cli;
-
-import nbbrd.heylogs.Rule;
-import nbbrd.heylogs.RuleLoader;
-import picocli.CommandLine;
-
-import java.util.List;
-
-@lombok.Getter
-@lombok.Setter
-public class RuleSetOptions {
-
- @CommandLine.Option(
- names = {"-s", "--semver"},
- defaultValue = "false",
- description = "Mention if this changelog follows Semantic Versioning."
- )
- private boolean semver;
-
- public List getRules() {
- if (semver) {
- System.setProperty(Rule.ENABLE_KEY, "semver");
- }
- return RuleLoader.load();
- }
-}
diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/VersionFilterOptions.java b/heylogs-cli/src/main/java/internal/heylogs/cli/VersionFilterOptions.java
deleted file mode 100644
index 49ebc5b..0000000
--- a/heylogs-cli/src/main/java/internal/heylogs/cli/VersionFilterOptions.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package internal.heylogs.cli;
-
-import nbbrd.heylogs.TimeRange;
-import nbbrd.heylogs.VersionFilter;
-import picocli.CommandLine;
-
-import java.time.LocalDate;
-import java.util.regex.Pattern;
-
-@lombok.Getter
-@lombok.Setter
-public class VersionFilterOptions {
-
- @CommandLine.Option(
- names = {"-r", "--ref"},
- paramLabel = "[",
- description = "Filter versions by name."
- )
- private String ref = VersionFilter.DEFAULT.getRef();
-
- @CommandLine.Option(
- names = {"-u", "--unreleased"},
- paramLabel = "",
- description = "Assume that versions that match this pattern are unreleased."
- )
- private Pattern unreleasedPattern = VersionFilter.DEFAULT.getUnreleasedPattern();
-
- @CommandLine.Option(
- names = {"-f", "--from"},
- paramLabel = "",
- description = "Filter versions by min date (included).",
- converter = LenientDateConverter.class
- )
- private LocalDate from = VersionFilter.DEFAULT.getTimeRange().getFrom();
-
- @CommandLine.Option(
- names = {"-t", "--to"},
- paramLabel = "",
- description = "Filter versions by max date (included).",
- converter = LenientDateConverter.class
- )
- private LocalDate to = VersionFilter.DEFAULT.getTimeRange().getTo();
-
- @CommandLine.Option(
- names = {"-l", "--limit"},
- description = "Limit the number of versions."
- )
- private int limit = VersionFilter.DEFAULT.getLimit();
-
- public VersionFilter get() {
- return VersionFilter
- .builder()
- .ref(ref)
- .unreleasedPattern(unreleasedPattern)
- .timeRange(TimeRange.of(from, to))
- .limit(limit)
- .build();
- }
-
- private static final class LenientDateConverter implements CommandLine.ITypeConverter {
-
- @Override
- public LocalDate convert(String value) {
- return VersionFilter.parseLocalDate(value);
- }
- }
-}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java
index bbed2c7..1b80f55 100644
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java
+++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java
@@ -1,19 +1,17 @@
package nbbrd.heylogs.cli;
-import internal.heylogs.cli.FailureFormatOptions;
+import internal.heylogs.SemverRule;
+import internal.heylogs.StylishFormat;
+import internal.heylogs.cli.FormatCandidates;
import internal.heylogs.cli.MarkdownInputSupport;
-import internal.heylogs.cli.RuleSetOptions;
import nbbrd.console.picocli.FileOutputOptions;
import nbbrd.console.picocli.MultiFileInputOptions;
-import nbbrd.heylogs.Failure;
-import nbbrd.heylogs.FailureFormatter;
-import nbbrd.heylogs.Rule;
+import nbbrd.heylogs.Checker;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import java.io.Writer;
import java.nio.file.Path;
-import java.util.List;
import java.util.concurrent.Callable;
import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport;
@@ -28,29 +26,48 @@ public final class CheckCommand implements Callable {
@CommandLine.Mixin
private FileOutputOptions output;
- @CommandLine.Mixin
- private RuleSetOptions ruleSet;
+ @CommandLine.Option(
+ names = {"-s", "--semver"},
+ defaultValue = "false",
+ description = "Mention if this changelog follows Semantic Versioning."
+ )
+ private boolean semver;
- @CommandLine.Mixin
- private FailureFormatOptions format;
+ @CommandLine.Option(
+ names = {"-f", "--format"},
+ paramLabel = "",
+ defaultValue = StylishFormat.ID,
+ description = "Specify the format used to control the appearance of the result. Valid values: ${COMPLETION-CANDIDATES}.",
+ completionCandidates = FormatCandidates.class
+ )
+ private String formatId;
@Override
public Void call() throws Exception {
try (Writer writer = newTextOutputSupport().newBufferedWriter(output.getFile())) {
- List rules = ruleSet.getRules();
- FailureFormatter formatter = format.getFormatter();
+ Checker checker = getChecker();
MarkdownInputSupport markdown = newMarkdownInputSupport();
for (Path file : input.getAllFiles(markdown::accept)) {
- formatter.format(
+ checker.formatFailures(
writer,
markdown.getName(file),
- Failure.allOf(markdown.readDocument(file), rules)
+ checker.validate(markdown.readDocument(file))
);
}
}
return null;
}
+
+ private Checker getChecker() {
+ Checker.Builder result = Checker.ofServiceLoader()
+ .toBuilder()
+ .formatId(formatId);
+ if (semver) {
+ result.rule(new SemverRule());
+ }
+ return result.build();
+ }
}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/DebugCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/DebugCommand.java
deleted file mode 100644
index 2995f6b..0000000
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/DebugCommand.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nbbrd.heylogs.cli;
-
-import nbbrd.heylogs.FailureFormatter;
-import nbbrd.heylogs.FailureFormatterLoader;
-import nbbrd.heylogs.Rule;
-import nbbrd.heylogs.RuleLoader;
-import picocli.CommandLine.Command;
-
-import java.util.concurrent.Callable;
-import java.util.stream.Collectors;
-
-@Command(name = "debug", hidden = true)
-public final class DebugCommand implements Callable {
-
- @Override
- public Void call() {
- System.out.println("Rules: " + RuleLoader.load().stream().map(Rule::getName).collect(Collectors.joining(", ")));
- System.out.println("Formatters: " + FailureFormatterLoader.load().stream().map(FailureFormatter::getName).collect(Collectors.joining(", ")));
- return null;
- }
-}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java
index 901bf74..4b83793 100644
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java
+++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java
@@ -1,14 +1,17 @@
package nbbrd.heylogs.cli;
import com.vladsch.flexmark.util.ast.Document;
-import internal.heylogs.cli.VersionFilterOptions;
import nbbrd.console.picocli.FileInputParameters;
import nbbrd.console.picocli.FileOutputOptions;
+import nbbrd.heylogs.Extractor;
+import nbbrd.heylogs.TimeRange;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import java.io.IOException;
+import java.time.LocalDate;
import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport;
import static internal.heylogs.cli.MarkdownOutputSupport.newMarkdownOutputSupport;
@@ -22,8 +25,47 @@ public final class ExtractCommand implements Callable {
@CommandLine.Mixin
private FileOutputOptions output;
- @CommandLine.ArgGroup(heading = "%nFilters:%n", exclusive = false)
- private final VersionFilterOptions filter = new VersionFilterOptions();
+ @CommandLine.Option(
+ names = {"-r", "--ref"},
+ paramLabel = "][",
+ description = "Filter versions by name."
+ )
+ private String ref = Extractor.DEFAULT.getRef();
+
+ @CommandLine.Option(
+ names = {"-u", "--unreleased"},
+ paramLabel = "",
+ description = "Assume that versions that match this pattern are unreleased."
+ )
+ private Pattern unreleasedPattern = Extractor.DEFAULT.getUnreleasedPattern();
+
+ @CommandLine.Option(
+ names = {"-f", "--from"},
+ paramLabel = "",
+ description = "Filter versions by min date (included).",
+ converter = LenientDateConverter.class
+ )
+ private LocalDate from = Extractor.DEFAULT.getTimeRange().getFrom();
+
+ @CommandLine.Option(
+ names = {"-t", "--to"},
+ paramLabel = "",
+ description = "Filter versions by max date (included).",
+ converter = LenientDateConverter.class
+ )
+ private LocalDate to = Extractor.DEFAULT.getTimeRange().getTo();
+
+ @CommandLine.Option(
+ names = {"-l", "--limit"},
+ description = "Limit the number of versions."
+ )
+ private int limit = Extractor.DEFAULT.getLimit();
+
+ @CommandLine.Option(
+ names = "--ignore-content",
+ description = "Ignore versions content, keep headers only."
+ )
+ private boolean ignoreContent = false;
@Override
public Void call() throws Exception {
@@ -36,11 +78,30 @@ private Document load() throws IOException {
}
private Document extract(Document document) {
- filter.get().apply(document);
+ getExtractor().extract(document);
return document;
}
private void store(Document document) throws IOException {
newMarkdownOutputSupport().writeDocument(output.getFile(), document);
}
+
+ public Extractor getExtractor() {
+ return Extractor
+ .builder()
+ .ref(ref)
+ .unreleasedPattern(unreleasedPattern)
+ .timeRange(TimeRange.of(from, to))
+ .limit(limit)
+ .ignoreContent(ignoreContent)
+ .build();
+ }
+
+ private static final class LenientDateConverter implements CommandLine.ITypeConverter {
+
+ @Override
+ public LocalDate convert(String value) {
+ return Extractor.parseLocalDate(value);
+ }
+ }
}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/HeylogsCommand.java
similarity index 88%
rename from heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java
rename to heylogs-cli/src/main/java/nbbrd/heylogs/cli/HeylogsCommand.java
index 938e863..588e9d4 100644
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java
+++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/HeylogsCommand.java
@@ -14,7 +14,7 @@
@Command(
name = About.NAME,
- versionProvider = MainCommand.ManifestVersionProvider.class,
+ versionProvider = HeylogsCommand.ManifestVersionProvider.class,
scope = CommandLine.ScopeType.INHERIT,
sortOptions = false,
mixinStandardHelpOptions = true,
@@ -25,17 +25,16 @@
headerHeading = "%n",
subcommands = {
ScanCommand.class,
- ListCommand.class,
CheckCommand.class,
ExtractCommand.class,
- DebugCommand.class
+ ListCommand.class
},
description = {
"Set of tools to deal with the @|bold keep-a-changelog|@ format.",
"%nMore info at https://github.com/nbbrd/heylogs"
}
)
-public final class MainCommand implements Callable {
+public final class HeylogsCommand implements Callable {
public static void main(String[] args) {
SpecialProperties specialProperties = SpecialProperties.parse(args);
@@ -53,10 +52,10 @@ private static int execMain(SpecialProperties specialProperties, Properties prop
specialProperties.apply(System.getProperties());
try (AnsiConsole ignore = AnsiConsole.windowsInstall()) {
- CommandLine cmd = new CommandLine(new MainCommand());
+ CommandLine cmd = new CommandLine(new HeylogsCommand());
cmd.setCaseInsensitiveEnumValuesAllowed(true);
cmd.setDefaultValueProvider(new CommandLine.PropertiesDefaultProvider(properties));
- cmd.setExecutionExceptionHandler(new PrintAndLogExceptionHandler(MainCommand.class, specialProperties.isDebugRequired()));
+ cmd.setExecutionExceptionHandler(new PrintAndLogExceptionHandler(HeylogsCommand.class, specialProperties.isDebugRequired()));
return cmd.execute(args);
}
}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java
index d7c26b4..5c1d684 100644
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java
+++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java
@@ -1,62 +1,40 @@
package nbbrd.heylogs.cli;
-import com.vladsch.flexmark.ast.Heading;
-import com.vladsch.flexmark.util.ast.Document;
-import com.vladsch.flexmark.util.ast.Node;
-import internal.heylogs.cli.VersionFilterOptions;
-import nbbrd.console.picocli.FileInputParameters;
-import nbbrd.console.picocli.FileOutputOptions;
-import nbbrd.heylogs.Nodes;
-import nbbrd.heylogs.Version;
+import internal.heylogs.SemverRule;
+import nbbrd.heylogs.Checker;
+import nbbrd.heylogs.spi.Format;
+import nbbrd.heylogs.spi.Rule;
import picocli.CommandLine;
import picocli.CommandLine.Command;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.util.List;
import java.util.concurrent.Callable;
-import java.util.stream.Collectors;
-import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport;
-import static nbbrd.console.picocli.text.TextOutputSupport.newTextOutputSupport;
+import static java.util.stream.Collectors.joining;
-@Command(name = "list", description = "List versions from changelog.")
+@Command(name = "list", description = "List available resources.")
public final class ListCommand implements Callable {
- @CommandLine.Mixin
- private FileInputParameters input;
-
- @CommandLine.Mixin
- private FileOutputOptions output;
-
- @CommandLine.ArgGroup(heading = "%nFilters:%n", exclusive = false)
- private final VersionFilterOptions filter = new VersionFilterOptions();
+ @CommandLine.Option(
+ names = {"-s", "--semver"},
+ defaultValue = "false",
+ description = "Mention if this changelog follows Semantic Versioning."
+ )
+ private boolean semver;
@Override
- public Void call() throws Exception {
- store(list(load()));
+ public Void call() {
+ Checker checker = getChecker();
+ System.out.println("Rules: " + checker.getRules().stream().map(Rule::getId).collect(joining(", ")));
+ System.out.println("Formats: " + checker.getFormats().stream().map(Format::getId).collect(joining(", ")));
return null;
}
- private Document load() throws IOException {
- return newMarkdownInputSupport().readDocument(input.getFile());
- }
-
- private List list(Node document) {
- return Nodes.of(Heading.class)
- .descendants(document)
- .filter(Version::isVersionLevel)
- .filter(filter.get()::contains)
- .limit(filter.getLimit())
- .collect(Collectors.toList());
- }
-
- private void store(List list) throws IOException {
- try (BufferedWriter writer = newTextOutputSupport().newBufferedWriter(output.getFile())) {
- for (Heading item : list) {
- writer.append(item.getChars());
- writer.newLine();
- }
+ private Checker getChecker() {
+ Checker.Builder result = Checker.ofServiceLoader()
+ .toBuilder();
+ if (semver) {
+ result.rule(new SemverRule());
}
+ return result.build();
}
}
diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java
index 537772a..2ca7500 100644
--- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java
+++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java
@@ -1,14 +1,15 @@
package nbbrd.heylogs.cli;
+import internal.heylogs.StylishFormat;
+import internal.heylogs.cli.FormatCandidates;
import internal.heylogs.cli.MarkdownInputSupport;
import nbbrd.console.picocli.FileOutputOptions;
import nbbrd.console.picocli.MultiFileInputOptions;
-import nbbrd.heylogs.Scan;
+import nbbrd.heylogs.Scanner;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import java.io.BufferedWriter;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.Callable;
@@ -24,45 +25,37 @@ public final class ScanCommand implements Callable {
@CommandLine.Mixin
private FileOutputOptions output;
+ @CommandLine.Option(
+ names = {"-f", "--format"},
+ paramLabel = "",
+ defaultValue = StylishFormat.ID,
+ description = "Specify the format used to control the appearance of the result. Valid values: ${COMPLETION-CANDIDATES}.",
+ completionCandidates = FormatCandidates.class
+ )
+ private String formatId;
+
@Override
public Void call() throws Exception {
try (BufferedWriter writer = newTextOutputSupport().newBufferedWriter(output.getFile())) {
+ Scanner scanner = getScanner();
MarkdownInputSupport markdown = newMarkdownInputSupport();
for (Path file : input.getAllFiles(markdown::accept)) {
- write(
+ scanner.formatStatus(
writer,
markdown.getName(file),
- Scan.of(markdown.readDocument(file)));
+ scanner.scan(markdown.readDocument(file)));
}
}
return null;
}
- private static void write(BufferedWriter writer, String source, Scan scan) throws IOException {
- writer.write(source);
- writer.newLine();
- if (scan.getReleaseCount() == 0) {
- writer.append(" No release found");
- writer.newLine();
- } else {
- writer.append(String.format(" Found %d releases", scan.getReleaseCount()));
- writer.newLine();
- writer.append(String.format(" Ranging from %s to %s", scan.getTimeRange().getFrom(), scan.getTimeRange().getTo()));
- writer.newLine();
-
- if (scan.isCompatibleWithSemver()) {
- writer.append(" Compatible with Semantic Versioning" + scan.getSemverDetails());
- writer.newLine();
- } else {
- writer.append(" Not compatible with Semantic Versioning");
- writer.newLine();
- }
- }
- writer.append(scan.isHasUnreleasedSection() ? " Has an unreleased version" : " Has no unreleased version");
- writer.newLine();
- writer.newLine();
+ private Scanner getScanner() {
+ return Scanner.ofServiceLoader()
+ .toBuilder()
+ .formatId(formatId)
+ .build();
}
}
diff --git a/heylogs-maven-plugin/pom.xml b/heylogs-maven-plugin/pom.xml
index d4059fe..cb30d4d 100644
--- a/heylogs-maven-plugin/pom.xml
+++ b/heylogs-maven-plugin/pom.xml
@@ -7,7 +7,7 @@
com.github.nbbrd.heylogs
heylogs-parent
- 0.5.0
+ 0.6.0
heylogs-maven-plugin
@@ -18,10 +18,21 @@
https://github.com/nbbrd/heylogs
+
- heylogs-api
- ${project.groupId}
- ${project.version}
+ org.checkerframework
+ checker-qual
+ provided
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ com.github.nbbrd.java-design-util
+ java-design-processor
+ provided
org.apache.maven
@@ -38,9 +49,16 @@
org.apache.maven.plugin-tools
maven-plugin-annotations
- 3.7.0
+ 3.9.0
provided
+
+
+
+ heylogs-api
+ ${project.groupId}
+ ${project.version}
+
@@ -49,7 +67,7 @@
org.apache.maven.plugins
maven-plugin-plugin
- 3.7.0
+ 3.9.0
diff --git a/heylogs-maven-plugin/src/main/java/internal/heylogs/maven/plugin/MojoFunction.java b/heylogs-maven-plugin/src/main/java/internal/heylogs/maven/plugin/MojoFunction.java
new file mode 100644
index 0000000..98cad4c
--- /dev/null
+++ b/heylogs-maven-plugin/src/main/java/internal/heylogs/maven/plugin/MojoFunction.java
@@ -0,0 +1,36 @@
+package internal.heylogs.maven.plugin;
+
+import lombok.NonNull;
+import nbbrd.design.StaticFactoryMethod;
+import nbbrd.heylogs.Extractor;
+import org.apache.maven.plugin.MojoExecutionException;
+
+import java.time.LocalDate;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+
+@FunctionalInterface
+public interface MojoFunction {
+
+ Y applyWithMojo(X x) throws MojoExecutionException;
+
+ static @NonNull MojoFunction of(@NonNull Function function, @NonNull String errorMessage) {
+ return x -> {
+ try {
+ return function.apply(x);
+ } catch (IllegalArgumentException ex) {
+ throw new MojoExecutionException(errorMessage, ex);
+ }
+ };
+ }
+
+ @StaticFactoryMethod
+ static @NonNull MojoFunction onPattern(@NonNull String errorMessage) {
+ return of(Pattern::compile, errorMessage);
+ }
+
+ @StaticFactoryMethod
+ static @NonNull MojoFunction onLocalDate(@NonNull String errorMessage) {
+ return of(Extractor::parseLocalDate, errorMessage);
+ }
+}
diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/CheckMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/CheckMojo.java
index 6e9f440..c82f750 100644
--- a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/CheckMojo.java
+++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/CheckMojo.java
@@ -1,35 +1,36 @@
package nbbrd.heylogs.maven.plugin;
-import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
+import internal.heylogs.SemverRule;
+import internal.heylogs.StylishFormat;
+import nbbrd.heylogs.Checker;
import nbbrd.heylogs.Failure;
-import nbbrd.heylogs.Rule;
-import nbbrd.heylogs.RuleLoader;
-import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.semver4j.Semver;
+import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
-import java.io.Reader;
-import java.nio.file.Files;
+import java.io.StringReader;
import java.util.List;
+import static java.util.Locale.ROOT;
+
@Mojo(name = "check", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true)
-public final class CheckMojo extends AbstractMojo {
+public final class CheckMojo extends HeylogsMojo {
@Parameter(defaultValue = "${project.basedir}/CHANGELOG.md", property = "heylogs.input.file")
private File inputFile;
- @Parameter(defaultValue = "false", property = "heylogs.skip")
- private boolean skip;
-
@Parameter(defaultValue = "false", property = "heylogs.semver")
private boolean semver;
+ @Parameter(defaultValue = StylishFormat.ID, property = "heylogs.format.id")
+ private String formatId;
+
@Parameter(defaultValue = "${project.version}", readonly = true)
private String projectVersion;
@@ -44,70 +45,56 @@ public void execute() throws MojoExecutionException {
}
if (semver) {
- enableSemanticVersioning();
+ checkSemanticVersioning();
}
if (inputFile.exists()) {
- validateFile();
+ check(loadChecker());
} else {
- if (isRootProject()) {
- raiseErrorMissingFile();
+ if (isRootProject(projectBaseDir)) {
+ raiseErrorMissingChangelog();
} else {
- notifyMissingFile();
+ notifyMissingChangelog();
}
}
}
- private void enableSemanticVersioning() throws MojoExecutionException {
+ private void checkSemanticVersioning() throws MojoExecutionException {
getLog().info("Using Semantic Versioning specification");
if (Semver.isValid(projectVersion)) {
getLog().info("Valid project version");
- System.setProperty(Rule.ENABLE_KEY, "semver");
} else {
- getLog().error(String.format("Invalid project version: '%s' must follow Semantic Versioning specification (https://semver.org/)", projectVersion));
+ getLog().error(String.format(ROOT, "Invalid project version: '%s' must follow Semantic Versioning specification (https://semver.org/)", projectVersion));
throw new MojoExecutionException("Invalid project version. See above for details.");
}
}
- private void validateFile() throws MojoExecutionException {
- try {
- getLog().info("Reading " + inputFile);
- Document changelog = read();
-
- List failures = Failure.allOf(changelog, RuleLoader.load());
- if (!failures.isEmpty()) {
- getLog().error("Invalid changelog");
- failures.forEach(failure -> getLog().error(failure.toString()));
- throw new MojoExecutionException("Invalid changelog");
- }
- getLog().info("Valid changelog");
- } catch (IOException ex) {
- throw new MojoExecutionException("Error while checking changelog", ex);
+ private Checker loadChecker() {
+ Checker.Builder result = Checker.ofServiceLoader()
+ .toBuilder()
+ .formatId(formatId);
+ if (semver) {
+ result.rule(new SemverRule());
}
+ return result.build();
}
- private boolean isRootProject() {
- File parentDir = projectBaseDir.getParentFile();
- if (parentDir != null) {
- File parentPom = new File(parentDir, "pom.xml");
- return !parentPom.exists();
+ private void check(Checker checker) throws MojoExecutionException {
+ Document changelog = readChangelog(inputFile);
+ List failures = checker.validate(changelog);
+ writeFailures(checker, failures);
+ if (!failures.isEmpty()) {
+ throw new MojoExecutionException("Invalid changelog");
}
- return true;
}
- private void raiseErrorMissingFile() throws MojoExecutionException {
- getLog().error("Missing changelog");
- throw new MojoExecutionException("Missing changelog");
- }
-
- private void notifyMissingFile() {
- getLog().info("Changelog not found");
- }
-
- public Document read() throws IOException {
- Parser parser = Parser.builder().build();
- try (Reader reader = Files.newBufferedReader(inputFile.toPath())) {
- return parser.parseReader(reader);
+ private void writeFailures(Checker checker, List failures) throws MojoExecutionException {
+ try {
+ StringBuilder text = new StringBuilder();
+ checker.formatFailures(text, inputFile.toString(), failures);
+ new BufferedReader(new StringReader(text.toString())).lines().forEach(!failures.isEmpty() ? getLog()::error : getLog()::info);
+ } catch (IOException ex) {
+ throw new MojoExecutionException("Error while writing failures", ex);
}
}
}
diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ExtractMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ExtractMojo.java
index 057c253..8cd6e1d 100644
--- a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ExtractMojo.java
+++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ExtractMojo.java
@@ -1,29 +1,21 @@
package nbbrd.heylogs.maven.plugin;
-import com.vladsch.flexmark.formatter.Formatter;
-import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
+import nbbrd.heylogs.Extractor;
import nbbrd.heylogs.TimeRange;
-import nbbrd.heylogs.VersionFilter;
-import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.File;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.time.LocalDate;
-import java.time.format.DateTimeParseException;
import java.util.Objects;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
+
+import static internal.heylogs.maven.plugin.MojoFunction.onLocalDate;
+import static internal.heylogs.maven.plugin.MojoFunction.onPattern;
@Mojo(name = "extract", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true)
-public final class ExtractMojo extends AbstractMojo {
+public final class ExtractMojo extends HeylogsMojo {
@Parameter(defaultValue = "${project.basedir}/CHANGELOG.md", property = "heylogs.input.file")
private File inputFile;
@@ -43,12 +35,12 @@ public final class ExtractMojo extends AbstractMojo {
@Parameter(defaultValue = "0x7fffffff", property = "heylogs.limit")
private int limit;
- @Parameter(defaultValue = "false", property = "heylogs.skip")
- private boolean skip;
-
@Parameter(defaultValue = "^.*-SNAPSHOT$", property = "heylogs.unreleased.pattern")
private String unreleasedPattern;
+ @Parameter(defaultValue = "false", property = "heylogs.ignore.content")
+ private boolean ignoreContent;
+
@Override
public void execute() throws MojoExecutionException {
if (skip) {
@@ -61,70 +53,29 @@ public void execute() throws MojoExecutionException {
throw new MojoExecutionException("Changelog not found");
}
- getLog().info("Reading " + inputFile);
- Document changelog = read();
-
- VersionFilter filter = getFilter();
-
- getLog().info("Extracting with " + filter);
- filter.apply(changelog);
-
- getLog().info("Writing " + outputFile);
- write(changelog);
+ extract(loadExtractor());
}
- private VersionFilter getFilter() throws MojoExecutionException {
- return VersionFilter
+ private Extractor loadExtractor() throws MojoExecutionException {
+ return Extractor
.builder()
.ref(Objects.toString(ref, ""))
- .unreleasedPattern(fetchUnreleasedPattern())
- .timeRange(TimeRange.of(fetchFrom(), fetchTo()))
+ .unreleasedPattern(onPattern("Invalid unreleased pattern").applyWithMojo(unreleasedPattern))
+ .timeRange(TimeRange.of(
+ onLocalDate("Invalid format for 'from' parameter").applyWithMojo(from),
+ onLocalDate("Invalid format for 'to' parameter").applyWithMojo(to))
+ )
.limit(limit)
+ .ignoreContent(ignoreContent)
.build();
}
- private Pattern fetchUnreleasedPattern() throws MojoExecutionException {
- try {
- return Pattern.compile(unreleasedPattern);
- } catch (PatternSyntaxException ex) {
- throw new MojoExecutionException("Invalid unreleased pattern", ex);
- }
- }
+ private void extract(Extractor extractor) throws MojoExecutionException {
+ Document changelog = readChangelog(inputFile);
- private LocalDate fetchFrom() throws MojoExecutionException {
- try {
- return VersionFilter.parseLocalDate(from);
- } catch (DateTimeParseException ex) {
- throw new MojoExecutionException("Invalid format for 'from' parameter", ex);
- }
- }
-
- private LocalDate fetchTo() throws MojoExecutionException {
- try {
- return VersionFilter.parseLocalDate(to);
- } catch (DateTimeParseException ex) {
- throw new MojoExecutionException("Invalid format for 'to' parameter", ex);
- }
- }
+ getLog().info("Extracting with " + extractor);
+ extractor.extract(changelog);
- public Document read() throws MojoExecutionException {
- Parser parser = Parser.builder().build();
- try (Reader reader = Files.newBufferedReader(inputFile.toPath())) {
- return parser.parseReader(reader);
- } catch (IOException ex) {
- throw new MojoExecutionException("Failed to read file", ex);
- }
- }
-
- public void write(Document document) throws MojoExecutionException {
- try {
- Files.createDirectories(outputFile.getParentFile().toPath());
- Formatter formatter = Formatter.builder().build();
- try (Writer writer = Files.newBufferedWriter(outputFile.toPath())) {
- formatter.render(document, writer);
- }
- } catch (IOException ex) {
- throw new MojoExecutionException("Failed to write file", ex);
- }
+ writeChangelog(changelog, outputFile);
}
}
diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java
new file mode 100644
index 0000000..aa6b61f
--- /dev/null
+++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java
@@ -0,0 +1,61 @@
+package nbbrd.heylogs.maven.plugin;
+
+import com.vladsch.flexmark.formatter.Formatter;
+import com.vladsch.flexmark.parser.Parser;
+import com.vladsch.flexmark.util.ast.Document;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Parameter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Files;
+
+abstract class HeylogsMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "false", property = "heylogs.skip")
+ protected boolean skip;
+
+ protected void raiseErrorMissingChangelog() throws MojoExecutionException {
+ getLog().error("Missing changelog");
+ throw new MojoExecutionException("Missing changelog");
+ }
+
+ protected void notifyMissingChangelog() {
+ getLog().info("Changelog not found");
+ }
+
+ protected Document readChangelog(File inputFile) throws MojoExecutionException {
+ getLog().info("Reading changelog " + inputFile);
+ Parser parser = Parser.builder().build();
+ try (Reader reader = Files.newBufferedReader(inputFile.toPath())) {
+ return parser.parseReader(reader);
+ } catch (IOException ex) {
+ throw new MojoExecutionException("Failed to read changelog", ex);
+ }
+ }
+
+ protected void writeChangelog(Document document, File outputFile) throws MojoExecutionException {
+ getLog().info("Writing changelog " + outputFile);
+ Formatter formatter = Formatter.builder().build();
+ try {
+ Files.createDirectories(outputFile.getParentFile().toPath());
+ try (Writer writer = Files.newBufferedWriter(outputFile.toPath())) {
+ formatter.render(document, writer);
+ }
+ } catch (IOException ex) {
+ throw new MojoExecutionException("Failed to write changelog", ex);
+ }
+ }
+
+ protected static boolean isRootProject(File projectBaseDir) {
+ File parentDir = projectBaseDir.getParentFile();
+ if (parentDir != null) {
+ File parentPom = new File(parentDir, "pom.xml");
+ return !parentPom.exists();
+ }
+ return true;
+ }
+}
diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ListMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ListMojo.java
new file mode 100644
index 0000000..75fa44c
--- /dev/null
+++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ListMojo.java
@@ -0,0 +1,43 @@
+package nbbrd.heylogs.maven.plugin;
+
+import internal.heylogs.SemverRule;
+import nbbrd.heylogs.Checker;
+import nbbrd.heylogs.spi.Format;
+import nbbrd.heylogs.spi.Rule;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+import static java.util.stream.Collectors.joining;
+
+@Mojo(name = "list", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true)
+public final class ListMojo extends HeylogsMojo {
+
+ @Parameter(defaultValue = "false", property = "heylogs.semver")
+ private boolean semver;
+
+ @Override
+ public void execute() {
+ if (skip) {
+ getLog().info("Listing has been skipped.");
+ return;
+ }
+
+ list(loadChecker());
+ }
+
+ private Checker loadChecker() {
+ Checker.Builder result = Checker.ofServiceLoader()
+ .toBuilder();
+ if (semver) {
+ result.rule(new SemverRule());
+ }
+ return result.build();
+ }
+
+ private void list(Checker checker) {
+ getLog().info("Rules: " + checker.getRules().stream().map(Rule::getId).collect(joining(", ")));
+ getLog().info("Formats: " + checker.getFormats().stream().map(Format::getId).collect(joining(", ")));
+ }
+}
diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ScanMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ScanMojo.java
new file mode 100644
index 0000000..3eaa535
--- /dev/null
+++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ScanMojo.java
@@ -0,0 +1,69 @@
+package nbbrd.heylogs.maven.plugin;
+
+import com.vladsch.flexmark.util.ast.Document;
+import internal.heylogs.StylishFormat;
+import nbbrd.heylogs.Scanner;
+import nbbrd.heylogs.Status;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+
+@Mojo(name = "scan", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true)
+public final class ScanMojo extends HeylogsMojo {
+
+ @Parameter(defaultValue = "${project.basedir}/CHANGELOG.md", property = "heylogs.input.file")
+ private File inputFile;
+
+ @Parameter(defaultValue = StylishFormat.ID, property = "heylogs.format.id")
+ private String formatId;
+
+ @Parameter(defaultValue = "${project.basedir}", readonly = true)
+ private File projectBaseDir;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ if (skip) {
+ getLog().info("Scanning has been skipped.");
+ return;
+ }
+
+ if (inputFile.exists()) {
+ scan(loadScanner());
+ } else {
+ if (isRootProject(projectBaseDir)) {
+ raiseErrorMissingChangelog();
+ } else {
+ notifyMissingChangelog();
+ }
+ }
+ }
+
+ private Scanner loadScanner() {
+ return Scanner.ofServiceLoader()
+ .toBuilder()
+ .formatId(formatId)
+ .build();
+ }
+
+ private void scan(Scanner scanner) throws MojoExecutionException {
+ Document changelog = readChangelog(inputFile);
+ Status status = scanner.scan(changelog);
+ writeStatus(status, scanner);
+ }
+
+ private void writeStatus(Status status, Scanner scanner) throws MojoExecutionException {
+ try {
+ StringBuilder text = new StringBuilder();
+ scanner.formatStatus(text, inputFile.toString(), status);
+ new BufferedReader(new StringReader(text.toString())).lines().forEach(getLog()::info);
+ } catch (IOException ex) {
+ throw new MojoExecutionException("Error while writing status", ex);
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index acd6edb..bb04fc2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.nbbrd.heylogs
heylogs-parent
- 0.5.0
+ 0.6.0
pom
heylogs
@@ -42,21 +42,21 @@
org.checkerframework
checker-qual
- 3.27.0
+ 3.35.0
org.junit
junit-bom
- 5.9.1
+ 5.9.3
pom
import
org.assertj
assertj-core
- 3.23.1
+ 3.24.2
]
@@ -73,17 +73,17 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.10.1
+ 3.11.0
org.apache.maven.plugins
maven-deploy-plugin
- 3.0.0
+ 3.1.1
org.apache.maven.plugins
maven-install-plugin
- 3.1.0
+ 3.1.1
org.apache.maven.plugins
@@ -93,7 +93,7 @@
org.apache.maven.plugins
maven-resources-plugin
- 3.3.0
+ 3.3.1
org.apache.maven.plugins
@@ -103,7 +103,13 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.0.0-M7
+ 3.1.2
+
+
+
+ de.thetaphi
+ forbiddenapis
+ 3.5.1
@@ -121,6 +127,32 @@
+
+ de.thetaphi
+ forbiddenapis
+
+ false
+
+ jdk-unsafe
+ jdk-deprecated
+ jdk-internal
+ jdk-non-portable
+ jdk-reflection
+
+
+ javax.annotation.processing.Generated
+ lombok.Generated
+
+
+
+
+
+ check
+ testCheck
+
+
+
+
@@ -128,6 +160,7 @@
heylogs-api
heylogs-cli
heylogs-maven-plugin
+ heylogs-bom
@@ -166,10 +199,10 @@
- 1.18.24
- 1.5.2
- 1.3.1
- 4.7.0
+ 1.18.28
+ 1.6.1
+ 1.4.0
+ 4.7.4
@@ -336,17 +369,17 @@
org.apache.maven.plugins
maven-enforcer-plugin
- 3.1.0
+ 3.3.0
org.kordamp.maven
pomchecker-enforcer-rules
- 1.4.0
+ 1.9.0
org.codehaus.mojo
extra-enforcer-rules
- 1.6.1
+ 1.7.0
@@ -409,7 +442,7 @@
org.gaul
modernizer-maven-plugin
- 2.5.0
+ 2.6.0
1.8
@@ -441,7 +474,7 @@
org.jacoco
jacoco-maven-plugin
- 0.8.8
+ 0.8.10
@@ -474,7 +507,7 @@
com.amashchenko.maven.plugin
gitflow-maven-plugin
- 1.19.0
+ 1.20.0
v
@@ -493,7 +526,7 @@
org.apache.maven.plugins
maven-source-plugin
- 3.2.1
+ 3.3.0
attach-sources
@@ -506,7 +539,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 3.4.1
+ 3.5.0
attach-empty-javadocs