diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 348d600..8627b21 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -34,7 +34,7 @@ jobs: - name: Check links id: lychee - uses: lycheeverse/lychee-action@v1.9.3 + uses: lycheeverse/lychee-action@v1.10.0 with: fail: true args: --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md' diff --git a/.github/workflows/heylogs.yml b/.github/workflows/heylogs.yml new file mode 100644 index 0000000..c0e0bf5 --- /dev/null +++ b/.github/workflows/heylogs.yml @@ -0,0 +1,33 @@ +name: Heylogs + +on: [ push ] + +jobs: + badge-job: + if: startsWith(github.repository, 'nbbrd/') && startsWith(github.ref, 'refs/heads/develop') + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 21 + cache: 'maven' + + - name: Scan changelog + run: mvn -B -ntp -U com.github.nbbrd.heylogs:heylogs-maven-plugin::scan -Dheylogs.output.file=scan.json -Dheylogs.format.id=json + + - name: Create badges endpoint json + run: | + mkdir heylogs + jq '{schemaVersion: 1, label: "unreleased changes", message: "#\(.[0].summary.unreleasedChanges)", color: "E05735", logoColor: "white", namedLogo: "keepachangelog"}' scan.json > heylogs/unreleased-changes.json + + - name: Deploy badges endpoint json + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: badges + publish_dir: ./heylogs diff --git a/.github/workflows/java-ea-maven.yml b/.github/workflows/java-ea-maven.yml index e331b7b..a690e85 100644 --- a/.github/workflows/java-ea-maven.yml +++ b/.github/workflows/java-ea-maven.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: java: [ 21 ] - os: [ ubuntu-latest ] + os: [ ubuntu-latest, macos-latest ] name: JDK${{ matrix.java }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/.github/workflows/java8-maven.yml b/.github/workflows/java8-maven.yml index 77f5183..d0683de 100644 --- a/.github/workflows/java8-maven.yml +++ b/.github/workflows/java8-maven.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: java: [ 8, 21 ] - os: [ ubuntu-latest, macOS-latest, windows-latest ] + os: [ ubuntu-latest, macos-13, windows-latest ] name: JDK${{ matrix.java }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 34efa8b..ba00ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.9.0] - 2024-08-28 + +This release adds a release command that greatly improves the use of heylogs in automations. +It also brings some code refactoring to split the code into more manageable parts. + +### Added + +- Add release command to promote the Unreleased section into a new release version section [#10](https://github.com/nbbrd/heylogs/issues/10) + +### Changed + +- Improve code modularization + ## [0.8.1] - 2024-04-18 ### Fixed @@ -154,7 +167,8 @@ This release improves extension points and also aligns features of Maven plugin - Initial release -[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.8.1...HEAD +[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.9.0...HEAD +[0.9.0]: https://github.com/nbbrd/heylogs/compare/v0.8.1...v0.9.0 [0.8.1]: https://github.com/nbbrd/heylogs/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/nbbrd/heylogs/compare/v0.7.2...v0.8.0 [0.7.2]: https://github.com/nbbrd/heylogs/compare/v0.7.1...v0.7.2 diff --git a/README.md b/README.md index 0f94ff6..6ba9d33 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # heylogs [![Download](https://img.shields.io/github/release/nbbrd/heylogs.svg)](https://github.com/nbbrd/heylogs/releases/latest) +[![Changes](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnbbrd%2Fheylogs%2Fbadges%2Funreleased-changes.json)](https://github.com/nbbrd/heylogs/blob/develop/CHANGELOG.md) `heylogs` is a set of tools to deal with the [keep-a-changelog format](https://keepachangelog.com), a changelog format designed to be human-readable. @@ -16,11 +17,40 @@ Features: - Checks format - Summarizes content - Extracts versions +- Modify content -[ [Library](#library) | [Command-line tool](#command-line-tool) | [Maven plugin](#maven-plugin) | [Developing](#developing) | [Contributing](#contributing) | [Licensing](#licensing) | [Related work](#related-work)] +[ [Library](#library) | [Command-line tool](#command-line-tool) | [Maven plugin](#maven-plugin) | [Badges](#badges) | [Developing](#developing) | [Contributing](#contributing) | [Licensing](#licensing) | [Related work](#related-work)] ## Library +Heylogs is available as a Java library. +_Note that the API is currently in beta and might change frequently._ + +```xml + + + com.github.nbbrd.heylogs + heylogs-api + _VERSION_ + + + com.github.nbbrd.heylogs + heylogs-ext-github + _VERSION_ + runtime + + ... + +``` + +The API is straightforward and has a single point of entry: +```java +Heylogs heylogs = Heylogs.ofServiceLoader(); +Document flexmarkDocument = parseFileWithFlexmark(file); +List problems = heylogs.checkFormat(flexmarkDocument); +... +``` + `WIP` ## Command-line tool @@ -31,12 +61,13 @@ It requires a Java SE Runtime Environment (JRE) version 8 or later to run on suc It provides the following commands: -| Name | Description | -|-----------|---------------------------------| -| `scan` | Summarize changelog content | -| `check` | Check changelog format | -| `extract` | Extract versions from changelog | -| `list` | List available resources | +| Name | Description | +|-----------|-------------------| +| `check` | Check format | +| `scan` | Summarize content | +| `extract` | Extract versions | +| `release` | Release changes | +| `list` | List resources | It follows the Unix philosophy of [“Do one thing and do it well“](https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well) by performing a single function and beeing composable. @@ -140,12 +171,13 @@ To use the CLI without installing it: It provides the following goals: -| Name | Description | -|-----------|---------------------------------| -| `scan` | Summarize changelog content | -| `check` | Check changelog format | -| `extract` | Extract versions from changelog | -| `list` | List available resources | +| Name | Description | +|-----------|-------------------| +| `check` | Check format | +| `scan` | Summarize content | +| `extract` | Extract versions | +| `release` | Release changes | +| `list` | List resources | ### Examples @@ -196,6 +228,16 @@ Extract the latest version from the changelog during a release: ``` +## Badges + +Heylogs make it possible to generate nice badges using a [GitHub workflow](https://github.com/nbbrd/heylogs/blob/develop/.github/workflows/heylogs.yml) and the [shields.io API](https://shields.io/badges/endpoint-badge). +Here are some examples: + +![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnbbrd%2Fheylogs%2Fbadges%2Funreleased-changes.json) +![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnbbrd%2Fheylogs%2Fbadges%2Funreleased-changes.json&label=changelog) +![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnbbrd%2Fheylogs%2Fbadges%2Funreleased-changes.json&label=changelog&logo=none) +![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnbbrd%2Fheylogs%2Fbadges%2Funreleased-changes.json&label=%20) + ## Developing This project is written in Java and uses [Apache Maven](https://maven.apache.org/) as a build tool. diff --git a/heylogs-api/pom.xml b/heylogs-api/pom.xml index d1adc01..abf74fc 100644 --- a/heylogs-api/pom.xml +++ b/heylogs-api/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.8.1 + 0.9.0 heylogs-api @@ -22,7 +22,7 @@ com.github.nbbrd.java-io-util java-io-bom - 0.0.28 + 0.0.30 pom import @@ -65,17 +65,6 @@ - - org.semver4j - semver4j - 5.2.3 - - - org.jetbrains - annotations - - - com.github.nbbrd.java-io-util java-io-base @@ -84,11 +73,6 @@ com.github.nbbrd.java-io-util java-io-http - - com.google.code.gson - gson - 2.10.1 - @@ -102,4 +86,25 @@ test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + tests/** + + + + + + + \ No newline at end of file diff --git a/heylogs-api/src/main/java/internal/heylogs/ChangelogNodes.java b/heylogs-api/src/main/java/internal/heylogs/ChangelogNodes.java index 0129a7d..c89ba0a 100644 --- a/heylogs-api/src/main/java/internal/heylogs/ChangelogNodes.java +++ b/heylogs-api/src/main/java/internal/heylogs/ChangelogNodes.java @@ -3,11 +3,14 @@ import com.vladsch.flexmark.ast.BulletList; import com.vladsch.flexmark.ast.BulletListItem; import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.Reference; import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.sequence.BasedSequence; import nbbrd.heylogs.Nodes; import nbbrd.heylogs.TypeOfChange; import nbbrd.heylogs.Version; +import java.net.URL; import java.util.List; import java.util.Map; import java.util.Optional; @@ -64,4 +67,8 @@ private static List collect(Heading typeOfChange) { .flatMap(Nodes.of(BulletListItem.class)::descendants) .collect(toList()); } + + public static Reference newReference(Version newVersion, URL newURL) { + return new Reference(BasedSequence.of("[" + newVersion.getRef() + "]: " + newURL), null, null); + } } diff --git a/heylogs-api/src/main/java/internal/heylogs/RuleSupport.java b/heylogs-api/src/main/java/internal/heylogs/RuleSupport.java index 044d932..7e59747 100644 --- a/heylogs-api/src/main/java/internal/heylogs/RuleSupport.java +++ b/heylogs-api/src/main/java/internal/heylogs/RuleSupport.java @@ -1,12 +1,21 @@ package internal.heylogs; import com.vladsch.flexmark.ast.LinkNodeBase; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; import lombok.NonNull; +import nbbrd.heylogs.Nodes; +import nbbrd.heylogs.Problem; +import nbbrd.heylogs.spi.Rule; +import nbbrd.heylogs.spi.RuleIssue; import nbbrd.io.text.Parser; import java.net.URL; +import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Stream; public final class RuleSupport { @@ -21,4 +30,14 @@ private RuleSupport() { public static @NonNull Optional linkToURL(@NonNull LinkNodeBase link) { return Parser.onURL().parseValue(link.getUrl()); } + + public static @NonNull Stream problemStreamOf(@NonNull Document root, @NonNull List rules) { + return Nodes.walk(root) + .flatMap(node -> rules.stream().map(rule -> getProblemOrNull(node, rule)).filter(Objects::nonNull)); + } + + private static Problem getProblemOrNull(Node node, Rule rule) { + RuleIssue ruleIssueOrNull = rule.getRuleIssueOrNull(node); + return ruleIssueOrNull != null ? Problem.builder().rule(rule).issue(ruleIssueOrNull).build() : null; + } } diff --git a/heylogs-api/src/main/java/internal/heylogs/VersionNode.java b/heylogs-api/src/main/java/internal/heylogs/VersionNode.java new file mode 100644 index 0000000..50b90f8 --- /dev/null +++ b/heylogs-api/src/main/java/internal/heylogs/VersionNode.java @@ -0,0 +1,61 @@ +package internal.heylogs; + +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.Reference; +import com.vladsch.flexmark.ast.util.ReferenceRepository; +import com.vladsch.flexmark.util.ast.Document; +import lombok.NonNull; +import nbbrd.heylogs.Nodes; +import nbbrd.heylogs.Version; + +import java.net.URL; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static java.util.stream.Collectors.toList; + +@lombok.Value +public class VersionNode { + + @NonNull + Heading heading; + + @NonNull + Version version; + + @NonNull + Reference reference; + + public static VersionNode of(Version version, URL url) { + return new VersionNode( + version.toHeading(), version, + ChangelogNodes.newReference(version, url)); + } + + public static List allOf(Document document, ReferenceRepository repository) { + return Nodes.of(Heading.class) + .descendants(document) + .filter(Version::isVersionLevel) + .map(heading -> { + try { + Version version = Version.parse(heading); + return new VersionNode(heading, version, Objects.requireNonNull(repository.getFromRaw(version.getRef()))); + } catch (RuntimeException ex) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(toList()); + } + + public static Optional findUnreleased(List list) { + return list.stream() + .filter(versionNode -> versionNode.getVersion().isUnreleased()) + .findFirst(); + } + + public URL getURL() { + return URLExtractor.urlOf(getReference().getUrl()); + } +} diff --git a/heylogs-api/src/main/java/internal/heylogs/VersioningSupport.java b/heylogs-api/src/main/java/internal/heylogs/VersioningSupport.java new file mode 100644 index 0000000..85277c4 --- /dev/null +++ b/heylogs-api/src/main/java/internal/heylogs/VersioningSupport.java @@ -0,0 +1,19 @@ +package internal.heylogs; + +import nbbrd.heylogs.Version; +import nbbrd.heylogs.spi.Versioning; + +import java.util.List; +import java.util.stream.Stream; + +public final class VersioningSupport { + + private VersioningSupport() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static Stream versioningStreamOf(List list, List releases) { + return list.stream() + .filter(versioning -> releases.stream().allMatch(release -> versioning.isValidVersion(release.getRef()))); + } +} diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHub.java b/heylogs-api/src/main/java/internal/heylogs/github/GitHub.java deleted file mode 100644 index f86d19d..0000000 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHub.java +++ /dev/null @@ -1,46 +0,0 @@ -package internal.heylogs.github; - -import lombok.NonNull; -import nbbrd.design.DirectImpl; -import nbbrd.heylogs.spi.Forge; -import nbbrd.io.http.URLQueryBuilder; -import nbbrd.io.text.Parser; -import nbbrd.service.ServiceProvider; - -import java.io.UncheckedIOException; -import java.net.MalformedURLException; -import java.net.URL; - -@DirectImpl -@ServiceProvider -public final class GitHub implements Forge { - - @Override - public @NonNull String getForgeId() { - return "github"; - } - - @Override - public @NonNull String getForgeName() { - return "GitHub"; - } - - @Override - public boolean isCompareLink(@NonNull CharSequence text) { - return Parser.of(GitHubCompareLink::parse).parseValue(text).isPresent(); - } - - @Override - public @NonNull URL getBaseURL(@NonNull CharSequence text) { - GitHubCompareLink compareLink = GitHubCompareLink.parse(text); - try { - return URLQueryBuilder - .of(compareLink.getBase()) - .path(compareLink.getOwner()) - .path(compareLink.getRepo()) - .build(); - } catch (MalformedURLException ex) { - throw new UncheckedIOException(ex); - } - } -} diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java b/heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java deleted file mode 100644 index 13fcd2b..0000000 --- a/heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java +++ /dev/null @@ -1,119 +0,0 @@ -package nbbrd.heylogs; - -import com.vladsch.flexmark.ast.Heading; -import com.vladsch.flexmark.ast.RefNode; -import com.vladsch.flexmark.ast.Reference; -import com.vladsch.flexmark.util.ast.Document; -import com.vladsch.flexmark.util.ast.Node; -import internal.heylogs.GuidingPrinciples; -import lombok.NonNull; - -import java.time.LocalDate; -import java.time.Year; -import java.time.YearMonth; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import static java.util.Arrays.asList; - -@lombok.Value -@lombok.Builder(toBuilder = true) -public class Extractor { - - public static final Extractor DEFAULT = Extractor.builder().build(); - - @lombok.NonNull - @lombok.Builder.Default - String ref = ""; - - @lombok.NonNull - @lombok.Builder.Default - Pattern unreleasedPattern = Pattern.compile("^.*-SNAPSHOT$"); - - @lombok.NonNull - @lombok.Builder.Default - TimeRange timeRange = TimeRange.ALL; - - @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(@NonNull Version version) { - return (isUnreleasedPattern() && version.isUnreleased()) || version.getRef().contains(ref); - } - - public boolean contains(@NonNull Version version) { - return containsRef(version) && timeRange.contains(version.getDate()); - } - - public boolean contains(@NonNull Heading heading) { - return contains(Version.parse(heading)); - } - - public void extract(@NonNull Document root) throws IllegalArgumentException { - if (Heylogs.getProblemStream(root, asList(GuidingPrinciples.values())).findFirst().isPresent()) { - throw new IllegalArgumentException("Invalid changelog"); - } - - int found = 0; - boolean keep = false; - - List refNodes = new ArrayList<>(); - List references = new ArrayList<>(); - - for (Node current : root.getChildren()) { - - boolean versionHeading = current instanceof Heading - && Version.isVersionLevel((Heading) current); - - if (versionHeading) { - if (found >= getLimit() || !contains((Heading) current)) { - keep = false; - } else { - found++; - keep = true; - } - } - - if (keep) { - Nodes.of(RefNode.class) - .descendants(current) - .map(node -> node.getReference().toString()) - .forEach(refNodes::add); - if (versionHeading && ignoreContent) { - keep = false; - } - } else { - if (current instanceof Reference) { - references.add((Reference) current); - } else { - current.unlink(); - } - } - } - - references - .stream() - .filter(reference -> !refNodes.contains(reference.getReference().toString())) - .forEach(Node::unlink); - } - - public static LocalDate parseLocalDate(CharSequence input) { - try { - return Year.parse(input).atDay(1); - } catch (Exception ex1) { - try { - return YearMonth.parse(input).atDay(1); - } catch (Exception ex2) { - return LocalDate.parse(input); - } - } - } -} diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Filter.java b/heylogs-api/src/main/java/nbbrd/heylogs/Filter.java new file mode 100644 index 0000000..e9c393a --- /dev/null +++ b/heylogs-api/src/main/java/nbbrd/heylogs/Filter.java @@ -0,0 +1,57 @@ +package nbbrd.heylogs; + +import lombok.NonNull; + +import java.time.LocalDate; +import java.time.Year; +import java.time.YearMonth; +import java.util.regex.Pattern; + +@lombok.Value +@lombok.Builder(toBuilder = true) +public class Filter { + + public static final Filter DEFAULT = Filter.builder().build(); + + @lombok.NonNull + @lombok.Builder.Default + String ref = ""; + + @lombok.NonNull + @lombok.Builder.Default + Pattern unreleasedPattern = Pattern.compile("^.*-SNAPSHOT$"); + + @lombok.NonNull + @lombok.Builder.Default + TimeRange timeRange = TimeRange.ALL; + + @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(@NonNull Version version) { + return (isUnreleasedPattern() && version.isUnreleased()) || version.getRef().contains(ref); + } + + public boolean contains(@NonNull Version version) { + return containsRef(version) && timeRange.contains(version.getDate()); + } + + public static @NonNull LocalDate parseLocalDate(@NonNull CharSequence input) { + try { + return Year.parse(input).atDay(1); + } catch (Exception ex1) { + try { + return YearMonth.parse(input).atDay(1); + } catch (Exception ex2) { + return LocalDate.parse(input); + } + } + } +} diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java b/heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java index ef794ae..9a01972 100644 --- a/heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java +++ b/heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java @@ -1,6 +1,8 @@ package nbbrd.heylogs; import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.RefNode; +import com.vladsch.flexmark.ast.Reference; import com.vladsch.flexmark.ast.util.ReferenceRepository; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; @@ -8,24 +10,24 @@ import internal.heylogs.ChangelogNodes; import internal.heylogs.GuidingPrinciples; import internal.heylogs.URLExtractor; +import internal.heylogs.VersionNode; import lombok.NonNull; import nbbrd.design.MightBePromoted; import nbbrd.design.StaticFactoryMethod; -import nbbrd.design.VisibleForTesting; import nbbrd.heylogs.spi.*; import java.io.IOException; import java.net.URL; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; +import static internal.heylogs.RuleSupport.problemStreamOf; +import static internal.heylogs.VersioningSupport.versioningStreamOf; import static java.util.Arrays.asList; -import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; import static nbbrd.heylogs.TimeRange.toTimeRange; -import static nbbrd.heylogs.Util.illegalArgumentToNull; @lombok.Value @lombok.Builder(toBuilder = true) @@ -58,130 +60,155 @@ public class Heylogs { @lombok.Singular List forges; - public @NonNull List validate(@NonNull Document doc) { - return getProblemStream(doc, rules).collect(toList()); + public @NonNull List checkFormat(@NonNull Document document) { + return problemStreamOf(document, rules).collect(toList()); } - public @NonNull List getResources() { - return concat( - rules.stream().map(Heylogs::asResource), - formats.stream().map(Heylogs::asResource), - versionings.stream().map(Heylogs::asResource), - forges.stream().map(Heylogs::asResource) - ) - .sorted(comparing(Resource::getType).thenComparing(Resource::getCategory).thenComparing(Resource::getId)) - .collect(toList()); - } + public void extractVersions(@NonNull Document document, @NonNull Filter filter) { + if (isNotValidAgainstGuidingPrinciples(document)) { + throw new IllegalArgumentException("Invalid changelog"); + } - private static Resource asResource(Rule rule) { - return Resource - .builder() - .type("rule") - .category(rule.getRuleCategory()) - .id(rule.getRuleId()) - .name(rule.getRuleName()) - .build(); - } + int found = 0; + boolean keep = false; + + List refNodes = new ArrayList<>(); + List references = new ArrayList<>(); + + for (Node current : document.getChildren()) { + + boolean versionHeading = current instanceof Heading + && Version.isVersionLevel((Heading) current); + + if (versionHeading) { + if (found >= filter.getLimit() || !filter.contains(Version.parse((Heading) current))) { + keep = false; + } else { + found++; + keep = true; + } + } + + if (keep) { + Nodes.of(RefNode.class) + .descendants(current) + .map(node -> node.getReference().toString()) + .forEach(refNodes::add); + if (versionHeading && filter.isIgnoreContent()) { + keep = false; + } + } else { + if (current instanceof Reference) { + references.add((Reference) current); + } else { + current.unlink(); + } + } + } - private static Resource asResource(Format format) { - return Resource - .builder() - .type("format") - .category(format.getFormatCategory()) - .id(format.getFormatId()) - .name(format.getFormatName()) - .build(); + references + .stream() + .filter(reference -> !refNodes.contains(reference.getReference().toString())) + .forEach(Node::unlink); } - private static Resource asResource(Versioning versioning) { - return Resource - .builder() - .type("versioning") - .category("main") - .id(versioning.getVersioningId()) - .name(versioning.getVersioningName()) - .build(); + public @NonNull List listResources() { + return concat( + rules.stream().map(Resource::of), + formats.stream().map(Resource::of), + versionings.stream().map(Resource::of), + forges.stream().map(Resource::of) + ).sorted(Resource.DEFAULT_COMPARATOR).collect(toList()); } - private static Resource asResource(Forge forge) { - return Resource - .builder() - .type("forge") - .category("main") - .id(forge.getForgeId()) - .name(forge.getForgeName()) - .build(); - } + public void releaseChanges(@NonNull Document document, @NonNull Version newVersion, @NonNull String versionTagPrefix) { + if (isNotValidAgainstGuidingPrinciples(document)) { + throw new IllegalArgumentException("Invalid changelog"); + } - public void formatProblems(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { - getFormatById(formatId).formatProblems(appendable, list); - } + ReferenceRepository repository = Parser.REFERENCES.get(document); + List versions = VersionNode.allOf(document, repository); - public void formatStatus(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { - getFormatById(formatId).formatStatus(appendable, list); - } + VersionNode unreleased = VersionNode.findUnreleased(versions) + .orElseThrow(() -> new IllegalArgumentException("Cannot locate unreleased header")); - public void formatResources(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { - getFormatById(formatId).formatResources(appendable, list); + Forge forge = findForge(unreleased) + .orElseThrow(() -> new IllegalArgumentException("Cannot determine forge")); + + URL releaseURL = forge.deriveCompareLink(unreleased.getURL(), versionTagPrefix + newVersion.getRef()); + VersionNode release = VersionNode.of(newVersion, releaseURL); + + URL updatedURL = forge.deriveCompareLink(releaseURL, "HEAD"); + VersionNode updated = VersionNode.of(unreleased.getVersion(), updatedURL); + + repository.putRawKey(release.getReference().getReference(), release.getReference()); + repository.putRawKey(updated.getReference().getReference(), updated.getReference()); + + unreleased.getHeading().appendChild(release.getHeading()); + unreleased.getReference().insertAfter(release.getReference()); + unreleased.getReference().insertBefore(updated.getReference()); + unreleased.getReference().unlink(); } - public @NonNull Summary scan(@NonNull Node document) { - if (getProblemStream(document, asList(GuidingPrinciples.values())).findFirst().isPresent()) - return Summary.builder().valid(false).build(); + public @NonNull Summary scanContent(@NonNull Document document) { + if (isNotValidAgainstGuidingPrinciples(document)) { + return Summary.INVALID; + } + + ReferenceRepository repository = Parser.REFERENCES.get(document); + List versions = VersionNode.allOf(document, repository); + + if (versions.isEmpty()) { + return Summary.EMPTY; + } - List releases = Nodes.of(Heading.class) - .descendants(document) - .filter(Version::isVersionLevel) - .map(illegalArgumentToNull(Version::parse)) - .filter(Objects::nonNull) + List releases = versions + .stream() + .map(VersionNode::getVersion) .filter(version -> !version.isUnreleased()) .collect(toList()); - long unreleasedChanges = ChangelogNodes.getUnreleasedHeading(document) + long unreleasedChanges = VersionNode.findUnreleased(versions) + .map(VersionNode::getHeading) .map(ChangelogNodes::getBulletListsByTypeOfChange) .map(o -> o.values().stream().mapToLong(List::size).sum()) .orElse(0L); - Optional forgeURL = getLatestVersionURL(document); - Forge forgeOrNull = forgeURL.flatMap(this::getForge).orElse(null); + VersionNode first = versions.get(0); + Forge forgeOrNull = findForge(first).orElse(null); return Summary .builder() .valid(true) .releaseCount(releases.size()) .timeRange(releases.stream().map(Version::getDate).collect(toTimeRange()).orElse(TimeRange.ALL)) - .compatibilities(getCompatibilities(releases)) + .compatibilities(versioningStreamOf(versionings, releases).map(Versioning::getVersioningName).collect(toList())) .unreleasedChanges((int) unreleasedChanges) .forgeName(forgeOrNull != null ? forgeOrNull.getForgeName() : null) - .forgeURL(forgeURL.map(x -> getBaseURL(forgeOrNull, x)).orElse(null)) + .forgeURL(getBaseURL(forgeOrNull, first.getURL())) .build(); } - private URL getBaseURL(Forge forgeOrNull, CharSequence url) { - return forgeOrNull != null ? forgeOrNull.getBaseURL(url) : URLExtractor.baseOf(URLExtractor.urlOf(url)); + public void formatProblems(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { + getFormatById(formatId).formatProblems(appendable, list); + } + + public void formatStatus(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { + getFormatById(formatId).formatStatus(appendable, list); } - private Optional getForge(String url) { - return forges.stream().filter(forge -> forge.isCompareLink(url)).findFirst(); + public void formatResources(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List list) throws IOException { + getFormatById(formatId).formatResources(appendable, list); } - private static Optional getLatestVersionURL(Node document) { - return Nodes.of(Heading.class) - .descendants(document) - .filter(Version::isVersionLevel) - .map(heading -> { - ReferenceRepository repository = Parser.REFERENCES.get(heading.getDocument()); - String normalizeRef = repository.normalizeKey(Version.parse(heading).getRef()); - return Objects.requireNonNull(repository.get(normalizeRef)).getUrl().toString(); - }) - .findFirst(); + private URL getBaseURL(Forge forgeOrNull, URL url) { + return forgeOrNull != null ? forgeOrNull.getProjectURL(url) : URLExtractor.baseOf(url); } - private List getCompatibilities(List releases) { - return versionings.stream() - .filter(versioning -> releases.stream().allMatch(release -> versioning.isValidVersion(release.getRef()))) - .map(Versioning::getVersioningName) - .collect(toList()); + private Optional findForge(VersionNode node) { + return forges.stream() + .filter(forge -> forge.isCompareLink(node.getURL())) + .findFirst(); } private Format getFormatById(String formatId) throws IOException { @@ -191,19 +218,12 @@ private Format getFormatById(String formatId) throws IOException { .orElseThrow(() -> new IOException("Cannot find format '" + formatId + "'")); } - @VisibleForTesting - static Stream getProblemStream(Node root, List rules) { - return Nodes.walk(root) - .flatMap(node -> rules.stream().map(rule -> getProblemOrNull(node, rule)).filter(Objects::nonNull)); - } + public static final String FIRST_FORMAT_AVAILABLE = ""; - private static Problem getProblemOrNull(Node node, Rule rule) { - RuleIssue ruleIssueOrNull = rule.getRuleIssueOrNull(node); - return ruleIssueOrNull != null ? Problem.builder().rule(rule).issue(ruleIssueOrNull).build() : null; + private static boolean isNotValidAgainstGuidingPrinciples(Document document) { + return problemStreamOf(document, asList(GuidingPrinciples.values())).findFirst().isPresent(); } - public static final String FIRST_FORMAT_AVAILABLE = ""; - @MightBePromoted @SafeVarargs private static Stream concat(Stream first, Stream... rest) { diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Resource.java b/heylogs-api/src/main/java/nbbrd/heylogs/Resource.java index fce76cc..6387478 100644 --- a/heylogs-api/src/main/java/nbbrd/heylogs/Resource.java +++ b/heylogs-api/src/main/java/nbbrd/heylogs/Resource.java @@ -1,13 +1,70 @@ package nbbrd.heylogs; import lombok.NonNull; +import nbbrd.heylogs.spi.Forge; +import nbbrd.heylogs.spi.Format; +import nbbrd.heylogs.spi.Rule; +import nbbrd.heylogs.spi.Versioning; + +import java.util.Comparator; + +import static java.util.Comparator.comparing; @lombok.Value @lombok.Builder public class Resource { - @NonNull String type; - @NonNull String category; - @NonNull String id; - @NonNull String name; + @NonNull + String type; + @NonNull + String category; + @NonNull + String id; + @NonNull + String name; + + static Resource of(Rule rule) { + return Resource + .builder() + .type("rule") + .category(rule.getRuleCategory()) + .id(rule.getRuleId()) + .name(rule.getRuleName()) + .build(); + } + + static Resource of(Format format) { + return Resource + .builder() + .type("format") + .category(format.getFormatCategory()) + .id(format.getFormatId()) + .name(format.getFormatName()) + .build(); + } + + static Resource of(Versioning versioning) { + return Resource + .builder() + .type("versioning") + .category("main") + .id(versioning.getVersioningId()) + .name(versioning.getVersioningName()) + .build(); + } + + static Resource of(Forge forge) { + return Resource + .builder() + .type("forge") + .category("main") + .id(forge.getForgeId()) + .name(forge.getForgeName()) + .build(); + } + + static final Comparator DEFAULT_COMPARATOR + = comparing(Resource::getType) + .thenComparing(Resource::getCategory) + .thenComparing(Resource::getId); } diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/Summary.java b/heylogs-api/src/main/java/nbbrd/heylogs/Summary.java index e36b5a2..e57308f 100644 --- a/heylogs-api/src/main/java/nbbrd/heylogs/Summary.java +++ b/heylogs-api/src/main/java/nbbrd/heylogs/Summary.java @@ -7,6 +7,9 @@ @lombok.Builder public class Summary { + public static final Summary INVALID = Summary.builder().valid(false).build(); + public static final Summary EMPTY = Summary.builder().valid(true).build(); + boolean valid; @lombok.Builder.Default diff --git a/heylogs-api/src/main/java/nbbrd/heylogs/spi/Forge.java b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Forge.java index cb912d1..ea57ec3 100644 --- a/heylogs-api/src/main/java/nbbrd/heylogs/spi/Forge.java +++ b/heylogs-api/src/main/java/nbbrd/heylogs/spi/Forge.java @@ -19,8 +19,11 @@ public interface Forge { @NonNull String getForgeName(); - boolean isCompareLink(@NonNull CharSequence text); + boolean isCompareLink(@NonNull URL url); @NonNull - URL getBaseURL(@NonNull CharSequence text); + URL getProjectURL(@NonNull URL url); + + @NonNull + URL deriveCompareLink(@NonNull URL latest, @NonNull String nextTag); } diff --git a/heylogs-api/src/test/java/internal/heylogs/ChangelogNodesTest.java b/heylogs-api/src/test/java/internal/heylogs/ChangelogNodesTest.java index 4c10aba..ea5e683 100644 --- a/heylogs-api/src/test/java/internal/heylogs/ChangelogNodesTest.java +++ b/heylogs-api/src/test/java/internal/heylogs/ChangelogNodesTest.java @@ -8,8 +8,8 @@ import java.util.List; import java.util.Map; -import static _test.Sample.asHeading; -import static _test.Sample.using; +import static tests.heylogs.api.Sample.asHeading; +import static tests.heylogs.api.Sample.using; import static internal.heylogs.ChangelogNodes.*; import static org.assertj.core.api.Assertions.assertThat; diff --git a/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java b/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java index e99c17d..9268d7b 100644 --- a/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java +++ b/heylogs-api/src/test/java/internal/heylogs/ExtendedRulesTest.java @@ -1,31 +1,28 @@ package internal.heylogs; -import _test.Sample; import com.vladsch.flexmark.ast.LinkNodeBase; import com.vladsch.flexmark.util.ast.Node; import nbbrd.heylogs.Nodes; -import nbbrd.heylogs.spi.Rule; import nbbrd.heylogs.spi.RuleIssue; -import nbbrd.heylogs.spi.RuleLoader; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import tests.heylogs.api.Sample; import java.util.Objects; -import static _test.Sample.using; import static internal.heylogs.ExtendedRules.NO_RULE_ISSUE; import static internal.heylogs.ExtendedRules.validateConsistentSeparator; import static nbbrd.heylogs.Nodes.of; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.Index.atIndex; +import static tests.heylogs.api.Sample.using; +import static tests.heylogs.spi.RuleAssert.assertRuleCompliance; public class ExtendedRulesTest { @Test - public void testIdPattern() { - assertThat(ExtendedRules.values()) - .extracting(Rule::getRuleId) - .allMatch(RuleLoader.ID_PATTERN.asPredicate()); + public void testCompliance() { + assertRuleCompliance(new ExtendedRules.Batch()); } @Test diff --git a/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java b/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java index 69e08c5..73a2967 100644 --- a/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java +++ b/heylogs-api/src/test/java/internal/heylogs/GuidingPrinciplesTest.java @@ -2,28 +2,25 @@ import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.util.ast.Node; -import nbbrd.heylogs.spi.Rule; import nbbrd.heylogs.spi.RuleIssue; -import nbbrd.heylogs.spi.RuleLoader; import org.junit.jupiter.api.Test; import java.util.Objects; -import static _test.Sample.using; import static internal.heylogs.GuidingPrinciples.validateForHumans; import static internal.heylogs.GuidingPrinciples.validateLatestVersionFirst; import static nbbrd.heylogs.Nodes.of; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.Index.atIndex; +import static tests.heylogs.api.Sample.using; +import static tests.heylogs.spi.RuleAssert.assertRuleCompliance; public class GuidingPrinciplesTest { @Test - public void testIdPattern() { - assertThat(GuidingPrinciples.values()) - .extracting(Rule::getRuleId) - .allMatch(RuleLoader.ID_PATTERN.asPredicate()); + public void testCompliance() { + assertRuleCompliance(new GuidingPrinciples.Batch()); } @Test diff --git a/heylogs-api/src/test/java/internal/heylogs/StylishFormatTest.java b/heylogs-api/src/test/java/internal/heylogs/StylishFormatTest.java index 2f6f252..5bd3c1d 100644 --- a/heylogs-api/src/test/java/internal/heylogs/StylishFormatTest.java +++ b/heylogs-api/src/test/java/internal/heylogs/StylishFormatTest.java @@ -5,14 +5,20 @@ import nbbrd.heylogs.spi.FormatType; import org.junit.jupiter.api.Test; -import static _test.Sample.*; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static tests.heylogs.api.Sample.*; +import static tests.heylogs.spi.FormatAssert.assertFormatCompliance; class StylishFormatTest { + @Test + public void testCompliance() { + assertFormatCompliance(new StylishFormat()); + } + @Test public void testIdPattern() { assertThat(new StylishFormat().getFormatId()) diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubTest.java b/heylogs-api/src/test/java/internal/heylogs/github/GitHubTest.java deleted file mode 100644 index 7c10290..0000000 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package internal.heylogs.github; - -import internal.heylogs.URLExtractor; -import nbbrd.heylogs.spi.Forge; -import nbbrd.heylogs.spi.ForgeLoader; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -class GitHubTest { - - @Test - void testGetForgeId() { - assertThat(new GitHub().getForgeId()) - .matches(ForgeLoader.ID_PATTERN); - } - - @Test - void testGetForgeName() { - assertThat(new GitHub().getForgeName()) - .isNotBlank(); - } - - @Test - void testIsCompareLink() { - Forge x = new GitHub(); - assertThat(x.isCompareLink("")).isFalse(); - assertThat(x.isCompareLink("https://github.com/nbbrd/heylogs/compare/v0.7.2...HEAD")).isTrue(); - } - - @Test - void testGetBaseURL() { - Forge x = new GitHub(); - - assertThatIllegalArgumentException() - .isThrownBy(() -> x.getBaseURL("")); - - assertThat(x.getBaseURL("https://github.com/nbbrd/heylogs/compare/v0.7.2...HEAD")) - .isEqualTo(URLExtractor.urlOf("https://github.com/nbbrd/heylogs")); - } -} \ No newline at end of file diff --git a/heylogs-api/src/test/java/internal/heylogs/semver/SemVerTest.java b/heylogs-api/src/test/java/internal/heylogs/semver/SemVerTest.java deleted file mode 100644 index af8239e..0000000 --- a/heylogs-api/src/test/java/internal/heylogs/semver/SemVerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package internal.heylogs.semver; - -import nbbrd.heylogs.spi.VersioningLoader; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class SemVerTest { - - @Test - void getVersioningId() { - assertThat(new SemVer().getVersioningId()) - .matches(VersioningLoader.ID_PATTERN); - } - - @Test - void getVersioningName() { - assertThat(new SemVer().getVersioningName()) - .isNotBlank(); - } - - @Test - void isValidVersion() { - SemVer x = new SemVer(); - assertThat(x.isValidVersion("1.1.0")).isTrue(); - assertThat(x.isValidVersion(".1.0")).isFalse(); - } -} \ No newline at end of file diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java index edd41f3..675f902 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/ChangelogTest.java @@ -1,12 +1,12 @@ package nbbrd.heylogs; -import _test.Sample; +import tests.heylogs.api.Sample; import com.vladsch.flexmark.ast.Heading; import org.junit.jupiter.api.Test; -import static _test.Sample.using; +import static tests.heylogs.api.Sample.using; import static nbbrd.heylogs.Changelog.parse; -import static _test.Sample.asHeading; +import static tests.heylogs.api.Sample.asHeading; import static org.assertj.core.api.Assertions.*; public class ChangelogTest { diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java similarity index 63% rename from heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java rename to heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java index 5755aae..95f1565 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/ExtractorTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java @@ -1,21 +1,17 @@ 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 nbbrd.heylogs.Filter.builder; +import static nbbrd.heylogs.Filter.parseLocalDate; import static nbbrd.heylogs.Version.HYPHEN; import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.InstanceOfAssertFactories.STRING; -public class ExtractorTest { +public class FilterTest { @Test public void testRef() { @@ -64,7 +60,7 @@ public void testRef() { @Test public void testTimeRange() { - Function onTimeRange = o -> builder().timeRange(o).build(); + Function onTimeRange = o -> builder().timeRange(o).build(); assertThat(TimeRange.ALL) .extracting(onTimeRange) @@ -103,47 +99,6 @@ public void testTimeRange() { .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() @@ -162,7 +117,7 @@ public void testParseLocalDate() { .isEqualTo("2010-02-03"); } - private static Condition containing(Version version) { + private static Condition containing(Version version) { return new Condition<>(parent -> parent.contains(version), "Must contain %s", version); } diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java index dcb1e19..95840f4 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java @@ -1,23 +1,32 @@ package nbbrd.heylogs; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; import internal.heylogs.StylishFormat; -import internal.heylogs.semver.SemVerRule; +import lombok.NonNull; +import nbbrd.design.MightBePromoted; +import nbbrd.heylogs.spi.Forge; import nbbrd.heylogs.spi.Rule; import nbbrd.heylogs.spi.RuleIssue; +import nbbrd.heylogs.spi.RuleSeverity; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; +import tests.heylogs.api.Sample; import java.io.IOException; +import java.net.URL; import java.time.LocalDate; import java.util.List; +import java.util.function.Function; -import static _test.Sample.using; import static internal.heylogs.URLExtractor.urlOf; import static java.util.Collections.singletonList; +import static nbbrd.heylogs.Filter.builder; import static nbbrd.heylogs.Heylogs.FIRST_FORMAT_AVAILABLE; import static nbbrd.heylogs.spi.RuleSeverity.ERROR; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIOException; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.InstanceOfAssertFactories.list; +import static tests.heylogs.api.Sample.using; public class HeylogsTest { @@ -33,48 +42,118 @@ public void testFactories() { .map(Rule::getRuleId) .doesNotContain("semver"); - assertThat(Heylogs.ofServiceLoader().toBuilder().rule(new SemVerRule()).build()) + assertThat(Heylogs.ofServiceLoader().toBuilder().rule(new MockedRule()).build()) .extracting(Heylogs::getRules, list(Rule.class)) .hasSizeGreaterThan(1) .map(Rule::getRuleId) - .contains("semver"); + .contains("mocked"); } @Test - public void testValidate() { - assertThat(Heylogs.builder().build().validate(using("/InvalidVersion.md"))) + public void testCheckFormat() { + assertThat(Heylogs.builder().build().checkFormat(using("/InvalidVersion.md"))) .isEmpty(); - assertThat(Heylogs.ofServiceLoader().validate(using("/InvalidVersion.md"))) + assertThat(Heylogs.ofServiceLoader().checkFormat(using("/InvalidVersion.md"))) .isNotEmpty(); } @Test - public void testFormatProblems() throws IOException { - List checks = singletonList(Check.builder().source("file1").problem(Problem.builder().id("rule1").severity(ERROR).issue(RuleIssue.builder().message("some message").line(10).column(20).build()).build()).build()); + public void testExtractVersions() { + Heylogs x = Heylogs.ofServiceLoader(); - assertThatIOException() - .isThrownBy(() -> Heylogs.builder().build().formatProblems(FIRST_FORMAT_AVAILABLE, new StringBuilder(), checks)); + Function usingMain = extractor -> { + Document doc = using("/Main.md"); + x.extractVersions(doc, extractor); + return Sample.FORMATTER.render(doc); + }; - assertThatIOException() - .isThrownBy(() -> Heylogs.ofServiceLoader().formatProblems("other", new StringBuilder(), checks)); + 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"); - StringBuilder output = new StringBuilder(); - Heylogs.ofServiceLoader().formatProblems(StylishFormat.ID, output, checks); - assertThat(output.toString()) - .isEqualToIgnoringNewLines( - "file1\n" + - " 10:20 error some message rule1\n" + - " \n" + - " 1 problem\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 - void testScan() { - Heylogs x = Heylogs.ofServiceLoader(); + public void testListResources() { + assertThat(Heylogs.builder().build().listResources()) + .isEmpty(); - assertThat(x.scan(using("/Empty.md"))) + assertThat(Heylogs.ofServiceLoader().listResources()) + .isNotEmpty(); + } + + @Test + public void testReleaseChanges() { + Heylogs x = Heylogs.ofServiceLoader() + .toBuilder() + .forge(new MockedForge()) + .build(); + + Version v123 = Version.of("1.2.3", Version.HYPHEN, LocalDate.of(2010, 1, 1)); + + assertThatIllegalArgumentException().isThrownBy(() -> releaseChangesToString(x, using("/Empty.md"), v123)) + .withMessageContaining("Invalid changelog"); + + assertThat(releaseChangesToString(x, using("/Main.md"), v123)) + .contains( + "## [Unreleased]", + "## [1.2.3] - 2010-01-01", + "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", + "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...v1.2.3") + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD"); + + assertThat(releaseChangesToString(x, using("/UnreleasedChanges.md"), v123)) + .contains( + "## [Unreleased]", + "## [1.2.3] - 2010-01-01", + "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", + "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...v1.2.3") + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD"); + + assertThat(releaseChangesToString(x, using("/FirstRelease.md"), v123)) + .contains( + "## [Unreleased]", + "## [1.2.3] - 2010-01-01", + "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", + "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...v1.2.3") + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/HEAD...HEAD"); + } + + @Test + public void testScanContent() { + Heylogs x = Heylogs.ofServiceLoader() + .toBuilder() + .forge(new MockedForge()) + .build(); + + assertThat(x.scanContent(using("/Empty.md"))) .isEqualTo(Summary .builder() .valid(false) @@ -84,20 +163,20 @@ void testScan() { .build() ); - assertThat(x.scan(using("/Main.md"))) + assertThat(x.scanContent(using("/Main.md"))) .isEqualTo(Summary - .builder() - .valid(true) - .releaseCount(13) - .timeRange(TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15))) - .compatibility("Semantic Versioning") - .unreleasedChanges(2) - .forgeName("GitHub") - .forgeURL(urlOf("https://github.com/olivierlacan/keep-a-changelog")) - .build() + .builder() + .valid(true) + .releaseCount(13) + .timeRange(TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15))) +// .compatibility("Semantic Versioning") + .unreleasedChanges(2) + .forgeName("GitHub") + .forgeURL(urlOf("https://github.com/olivierlacan/keep-a-changelog")) + .build() ); - assertThat(x.scan(using("/InvalidSemver.md"))) + assertThat(x.scanContent(using("/InvalidSemver.md"))) .isEqualTo(Summary .builder() .valid(true) @@ -109,7 +188,7 @@ void testScan() { .build() ); - assertThat(x.scan(using("/InvalidVersion.md"))) + assertThat(x.scanContent(using("/InvalidVersion.md"))) .isEqualTo(Summary .builder() .valid(false) @@ -120,6 +199,27 @@ void testScan() { ); } + @Test + public void testFormatProblems() throws IOException { + List checks = singletonList(Check.builder().source("file1").problem(Problem.builder().id("rule1").severity(ERROR).issue(RuleIssue.builder().message("some message").line(10).column(20).build()).build()).build()); + + assertThatIOException() + .isThrownBy(() -> Heylogs.builder().build().formatProblems(FIRST_FORMAT_AVAILABLE, new StringBuilder(), checks)); + + assertThatIOException() + .isThrownBy(() -> Heylogs.ofServiceLoader().formatProblems("other", new StringBuilder(), checks)); + + StringBuilder output = new StringBuilder(); + Heylogs.ofServiceLoader().formatProblems(StylishFormat.ID, output, checks); + assertThat(output.toString()) + .isEqualToIgnoringNewLines( + "file1\n" + + " 10:20 error some message rule1\n" + + " \n" + + " 1 problem\n" + ); + } + @Test public void testFormatStatus() throws IOException { List scans = singletonList( @@ -158,4 +258,79 @@ public void testFormatStatus() throws IOException { " Has 3 unreleased changes \n" ); } + + private static String releaseChangesToString(Heylogs heylogs, Document doc, Version version) { + heylogs.releaseChanges(doc, version, "v"); + return Sample.FORMATTER.render(doc); + } + + private static final class MockedRule implements Rule { + + @Override + public @NonNull String getRuleId() { + return "mocked"; + } + + @Override + public @NonNull String getRuleName() { + return ""; + } + + @Override + public @NonNull String getRuleCategory() { + return ""; + } + + @Override + public boolean isRuleAvailable() { + return false; + } + + @Override + public @NonNull RuleSeverity getRuleSeverity() { + return RuleSeverity.OFF; + } + + @Override + public @Nullable RuleIssue getRuleIssueOrNull(@NonNull Node node) { + return null; + } + } + + private static final class MockedForge implements Forge { + + @Override + public @NonNull String getForgeId() { + return "github"; + } + + @Override + public @NonNull String getForgeName() { + return "GitHub"; + } + + @Override + public boolean isCompareLink(@NonNull URL url) { + return true; + } + + @Override + public @NonNull URL getProjectURL(@NonNull URL url) { + return urlOf("https://github.com/olivierlacan/keep-a-changelog"); + } + + @Override + public @NonNull URL deriveCompareLink(@NonNull URL latest, @NonNull String nextTag) { + String urlAsString = latest.toString(); + int oidIndex = urlAsString.lastIndexOf("/") + 1; + return urlOf(urlAsString.substring(0, oidIndex) + deriveOID(nextTag, urlAsString.substring(oidIndex))); + } + + @MightBePromoted + private static String deriveOID(String nextTag, String oid) { + return oid.endsWith("...HEAD") + ? oid.startsWith("HEAD...") ? (nextTag + "..." + nextTag) : (oid.substring(0, oid.length() - 4) + nextTag) + : (oid.substring(oid.indexOf("...") + 3) + "..." + nextTag); + } + } } diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/NodesTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/NodesTest.java index 694b77b..0820c69 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/NodesTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/NodesTest.java @@ -4,7 +4,7 @@ import internal.heylogs.ChangelogNodes; import org.junit.jupiter.api.Test; -import static _test.Sample.using; +import static tests.heylogs.api.Sample.using; import static org.assertj.core.api.Assertions.assertThat; class NodesTest { diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java index ebd732c..0ff8f53 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/TypeOfChangeTest.java @@ -1,11 +1,11 @@ package nbbrd.heylogs; -import _test.Sample; +import tests.heylogs.api.Sample; import com.vladsch.flexmark.ast.Heading; import org.junit.jupiter.api.Test; -import static _test.Sample.asHeading; -import static _test.Sample.using; +import static tests.heylogs.api.Sample.asHeading; +import static tests.heylogs.api.Sample.using; import static nbbrd.heylogs.TypeOfChange.parse; import static org.assertj.core.api.Assertions.*; diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java index 8a4282e..d9f2a55 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/VersionTest.java @@ -1,13 +1,13 @@ package nbbrd.heylogs; -import _test.Sample; +import tests.heylogs.api.Sample; import com.vladsch.flexmark.ast.Heading; import org.junit.jupiter.api.Test; import java.time.LocalDate; -import static _test.Sample.asHeading; -import static _test.Sample.using; +import static tests.heylogs.api.Sample.asHeading; +import static tests.heylogs.api.Sample.using; import static nbbrd.heylogs.Version.*; import static org.assertj.core.api.Assertions.*; diff --git a/heylogs-api/src/test/java/_test/Sample.java b/heylogs-api/src/test/java/tests/heylogs/api/Sample.java similarity index 99% rename from heylogs-api/src/test/java/_test/Sample.java rename to heylogs-api/src/test/java/tests/heylogs/api/Sample.java index 8ef758d..e623310 100644 --- a/heylogs-api/src/test/java/_test/Sample.java +++ b/heylogs-api/src/test/java/tests/heylogs/api/Sample.java @@ -1,4 +1,4 @@ -package _test; +package tests.heylogs.api; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.formatter.Formatter; diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/ForgeAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeAssert.java new file mode 100644 index 0000000..cb8bcb5 --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeAssert.java @@ -0,0 +1,37 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.Forge; + +import static internal.heylogs.URLExtractor.urlOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public final class ForgeAssert { + + private ForgeAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + @SuppressWarnings("DataFlowIssue") + public static void assertForgeCompliance(@NonNull Forge x) { + assertThat(x.getForgeId()) + .matches(nbbrd.heylogs.spi.ForgeLoader.ID_PATTERN); + + assertThat(x.getForgeName()) + .isNotEmpty() + .isNotNull(); + + assertThatNullPointerException() + .isThrownBy(() -> x.isCompareLink(null)); + + assertThatNullPointerException() + .isThrownBy(() -> x.getProjectURL(null)); + + assertThatNullPointerException() + .isThrownBy(() -> x.deriveCompareLink(null, "")); + + assertThatNullPointerException() + .isThrownBy(() -> x.deriveCompareLink(urlOf("http://localhost"), null)); + } +} diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/ForgeLinkAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeLinkAssert.java new file mode 100644 index 0000000..f86b5b4 --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeLinkAssert.java @@ -0,0 +1,18 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.ForgeLink; + +import static org.assertj.core.api.Assertions.assertThat; + +public final class ForgeLinkAssert { + + private ForgeLinkAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static void assertForgeLinkCompliance(@NonNull ForgeLink x) { + assertThat(x.getBase()) + .isNotNull(); + } +} diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/ForgeRefAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeRefAssert.java new file mode 100644 index 0000000..3a1d86a --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/ForgeRefAssert.java @@ -0,0 +1,20 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.ForgeRef; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public final class ForgeRefAssert { + + private ForgeRefAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + @SuppressWarnings("DataFlowIssue") + public static void assertForgeRefCompliance(@NonNull ForgeRef x) { + assertThatNullPointerException() + .isThrownBy(() -> x.isCompatibleWith(null)); + } +} diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/FormatAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/FormatAssert.java new file mode 100644 index 0000000..c8ae999 --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/FormatAssert.java @@ -0,0 +1,50 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.Format; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public final class FormatAssert { + + private FormatAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + @SuppressWarnings("DataFlowIssue") + public static void assertFormatCompliance(@NonNull Format x) { + assertThat(x.getFormatId()) + .matches(nbbrd.heylogs.spi.FormatLoader.ID_PATTERN); + + assertThat(x.getFormatName()) + .isNotEmpty() + .isNotNull(); + + assertThat(x.getFormatCategory()) + .isNotEmpty() + .isNotNull(); + + assertThat(x.getSupportedFormatTypes()) + .isNotNull(); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatStatus(null, emptyList())); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatStatus(new StringBuilder(), null)); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatResources(null, emptyList())); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatResources(new StringBuilder(), null)); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatProblems(null, emptyList())); + + assertThatNullPointerException() + .isThrownBy(() -> x.formatProblems(new StringBuilder(), null)); + } +} diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/RuleAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/RuleAssert.java new file mode 100644 index 0000000..c40e901 --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/RuleAssert.java @@ -0,0 +1,39 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.Rule; +import nbbrd.heylogs.spi.RuleBatch; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public final class RuleAssert { + + private RuleAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static void assertRuleCompliance(@NonNull RuleBatch x) { + x.getProviders().forEach(RuleAssert::assertRuleCompliance); + } + + @SuppressWarnings("DataFlowIssue") + public static void assertRuleCompliance(@NonNull Rule x) { + assertThat(x.getRuleId()) + .matches(nbbrd.heylogs.spi.RuleLoader.ID_PATTERN); + + assertThat(x.getRuleName()) + .isNotEmpty() + .isNotNull(); + + assertThat(x.getRuleCategory()) + .isNotEmpty() + .isNotNull(); + + assertThat(x.getRuleSeverity()) + .isNotNull(); + + assertThatNullPointerException() + .isThrownBy(() -> x.getRuleIssueOrNull(null)); + } +} diff --git a/heylogs-api/src/test/java/tests/heylogs/spi/VersioningAssert.java b/heylogs-api/src/test/java/tests/heylogs/spi/VersioningAssert.java new file mode 100644 index 0000000..5835905 --- /dev/null +++ b/heylogs-api/src/test/java/tests/heylogs/spi/VersioningAssert.java @@ -0,0 +1,27 @@ +package tests.heylogs.spi; + +import lombok.NonNull; +import nbbrd.heylogs.spi.Versioning; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public final class VersioningAssert { + + private VersioningAssert() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + @SuppressWarnings("DataFlowIssue") + public static void assertVersioningCompliance(@NonNull Versioning x) { + assertThat(x.getVersioningId()) + .matches(nbbrd.heylogs.spi.VersioningLoader.ID_PATTERN); + + assertThat(x.getVersioningName()) + .isNotEmpty() + .isNotNull(); + + assertThatNullPointerException() + .isThrownBy(() -> x.isValidVersion(null)); + } +} diff --git a/heylogs-api/src/test/resources/FirstRelease.md b/heylogs-api/src/test/resources/FirstRelease.md new file mode 100644 index 0000000..05c03a5 --- /dev/null +++ b/heylogs-api/src/test/resources/FirstRelease.md @@ -0,0 +1,5 @@ +# Changelog + +## [Unreleased] + +[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/HEAD...HEAD diff --git a/heylogs-bom/pom.xml b/heylogs-bom/pom.xml index 1ab5efa..230cb7a 100644 --- a/heylogs-bom/pom.xml +++ b/heylogs-bom/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.8.1 + 0.9.0 heylogs-bom @@ -24,11 +24,33 @@ ${project.groupId} ${project.version} + + heylogs-api + ${project.groupId} + ${project.version} + tests + test-jar + heylogs-cli ${project.groupId} ${project.version} + + heylogs-ext-github + ${project.groupId} + ${project.version} + + + heylogs-ext-json + ${project.groupId} + ${project.version} + + + heylogs-ext-semver + ${project.groupId} + ${project.version} + heylogs-maven-plugin ${project.groupId} diff --git a/heylogs-cli/pom.xml b/heylogs-cli/pom.xml index c57bb9a..4f5f0dd 100644 --- a/heylogs-cli/pom.xml +++ b/heylogs-cli/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.8.1 + 0.9.0 heylogs-cli @@ -46,6 +46,21 @@ ${project.groupId} ${project.version} + + heylogs-ext-github + ${project.groupId} + ${project.version} + + + heylogs-ext-json + ${project.groupId} + ${project.version} + + + heylogs-ext-semver + ${project.groupId} + ${project.version} + info.picocli picocli @@ -79,7 +94,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.6.0 package diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/HeylogsOptions.java b/heylogs-cli/src/main/java/internal/heylogs/cli/HeylogsOptions.java index 019e4a4..78e12f5 100644 --- a/heylogs-cli/src/main/java/internal/heylogs/cli/HeylogsOptions.java +++ b/heylogs-cli/src/main/java/internal/heylogs/cli/HeylogsOptions.java @@ -1,7 +1,7 @@ package internal.heylogs.cli; -import internal.heylogs.semver.SemVerRule; import nbbrd.heylogs.Heylogs; +import nbbrd.heylogs.ext.semver.SemVerRule; import picocli.CommandLine; @lombok.Getter 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 b14a2d2..918c0b4 100644 --- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/CheckCommand.java @@ -16,7 +16,7 @@ import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport; import static nbbrd.console.picocli.text.TextOutputSupport.newTextOutputSupport; -@Command(name = "check", description = "Check changelog format.") +@Command(name = "check", description = "Check format.") public final class CheckCommand implements Callable { @CommandLine.Mixin @@ -50,7 +50,7 @@ public Integer call() throws Exception { list.add(Check .builder() .source(markdown.getName(file)) - .problems(heylogs.validate(markdown.readDocument(file))) + .problems(heylogs.checkFormat(markdown.readDocument(file))) .build()); } heylogs.formatProblems(formatOptions.getFormatId(), writer, list); 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 ae54dc1..5fce4ac 100644 --- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ExtractCommand.java @@ -2,10 +2,10 @@ import com.vladsch.flexmark.util.ast.Document; import internal.heylogs.cli.ChangelogInputParameters; +import internal.heylogs.cli.HeylogsOptions; import internal.heylogs.cli.SpecialProperties; -import nbbrd.console.picocli.FileInputParameters; import nbbrd.console.picocli.FileOutputOptions; -import nbbrd.heylogs.Extractor; +import nbbrd.heylogs.Filter; import nbbrd.heylogs.TimeRange; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -18,7 +18,7 @@ import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport; import static internal.heylogs.cli.MarkdownOutputSupport.newMarkdownOutputSupport; -@Command(name = "extract", description = "Extract versions from changelog.") +@Command(name = "extract", description = "Extract versions.") public final class ExtractCommand implements Callable { @CommandLine.Mixin @@ -32,14 +32,14 @@ public final class ExtractCommand implements Callable { paramLabel = "", description = "Filter versions by name." ) - private String ref = Extractor.DEFAULT.getRef(); + private String ref = Filter.DEFAULT.getRef(); @CommandLine.Option( names = {"-u", "--unreleased"}, paramLabel = "", description = "Assume that versions that match this pattern are unreleased." ) - private Pattern unreleasedPattern = Extractor.DEFAULT.getUnreleasedPattern(); + private Pattern unreleasedPattern = Filter.DEFAULT.getUnreleasedPattern(); @CommandLine.Option( names = {"-f", "--from"}, @@ -47,7 +47,7 @@ public final class ExtractCommand implements Callable { description = "Filter versions by min date (included).", converter = LenientDateConverter.class ) - private LocalDate from = Extractor.DEFAULT.getTimeRange().getFrom(); + private LocalDate from = Filter.DEFAULT.getTimeRange().getFrom(); @CommandLine.Option( names = {"-t", "--to"}, @@ -55,13 +55,13 @@ public final class ExtractCommand implements Callable { description = "Filter versions by max date (included).", converter = LenientDateConverter.class ) - private LocalDate to = Extractor.DEFAULT.getTimeRange().getTo(); + private LocalDate to = Filter.DEFAULT.getTimeRange().getTo(); @CommandLine.Option( names = {"-l", "--limit"}, description = "Limit the number of versions." ) - private int limit = Extractor.DEFAULT.getLimit(); + private int limit = Filter.DEFAULT.getLimit(); @CommandLine.Option( names = "--ignore-content", @@ -69,6 +69,9 @@ public final class ExtractCommand implements Callable { ) private boolean ignoreContent = false; + @CommandLine.Mixin + private HeylogsOptions heylogsOptions; + @CommandLine.Option( names = {SpecialProperties.DEBUG_OPTION}, defaultValue = "false", @@ -87,7 +90,7 @@ private Document load() throws IOException { } private Document extract(Document document) { - getExtractor().extract(document); + heylogsOptions.initHeylogs().extractVersions(document, getFilter()); return document; } @@ -95,8 +98,8 @@ private void store(Document document) throws IOException { newMarkdownOutputSupport().writeDocument(output.getFile(), document); } - public Extractor getExtractor() { - return Extractor + public Filter getFilter() { + return Filter .builder() .ref(ref) .unreleasedPattern(unreleasedPattern) @@ -110,7 +113,7 @@ private static final class LenientDateConverter implements CommandLine.ITypeConv @Override public LocalDate convert(String value) { - return Extractor.parseLocalDate(value); + return Filter.parseLocalDate(value); } } } 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 649ab16..2729762 100644 --- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ListCommand.java @@ -14,7 +14,7 @@ import static nbbrd.console.picocli.text.TextOutputSupport.newTextOutputSupport; -@Command(name = "list", description = "List available resources.") +@Command(name = "list", description = "List resources.") public final class ListCommand implements Callable { @CommandLine.Mixin @@ -37,7 +37,7 @@ public final class ListCommand implements Callable { public Void call() throws IOException { try (Writer writer = newTextOutputSupport().newBufferedWriter(output.getFile())) { Heylogs heylogs = heylogsOptions.initHeylogs(); - heylogs.formatResources(formatOptions.getFormatId(), writer, heylogs.getResources()); + heylogs.formatResources(formatOptions.getFormatId(), writer, heylogs.listResources()); } return null; } diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java index 40f3421..69aaa3f 100644 --- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/MainCommand.java @@ -24,9 +24,10 @@ commandListHeading = "%nCommands:%n", headerHeading = "%n", subcommands = { - ScanCommand.class, CheckCommand.class, + ScanCommand.class, ExtractCommand.class, + ReleaseCommand.class, ListCommand.class }, description = { diff --git a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ReleaseCommand.java b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ReleaseCommand.java new file mode 100644 index 0000000..f0f591d --- /dev/null +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ReleaseCommand.java @@ -0,0 +1,81 @@ +package nbbrd.heylogs.cli; + +import com.vladsch.flexmark.util.ast.Document; +import internal.heylogs.cli.ChangelogInputParameters; +import internal.heylogs.cli.HeylogsOptions; +import internal.heylogs.cli.SpecialProperties; +import nbbrd.console.picocli.FileOutputOptions; +import nbbrd.heylogs.Version; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.concurrent.Callable; + +import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport; +import static internal.heylogs.cli.MarkdownOutputSupport.newMarkdownOutputSupport; + +@Command(name = "release", description = "Release changes.") +public final class ReleaseCommand implements Callable { + + @CommandLine.Mixin + private ChangelogInputParameters input; + + @CommandLine.Mixin + private FileOutputOptions output; + + @CommandLine.Option( + names = {"-r", "--ref"}, + paramLabel = "", + description = "Version reference.", + required = true + ) + private String ref; + + @CommandLine.Option( + names = {"-t", "--tag-prefix"}, + paramLabel = "", + description = "Version tag prefix.", + required = false + ) + private String tagPrefix = ""; + + @CommandLine.Option( + names = {"-d", "--date"}, + paramLabel = "", + description = "Release date (default : today).", + required = false + ) + private LocalDate date = LocalDate.now(ZoneId.systemDefault()); + + @CommandLine.Mixin + private HeylogsOptions heylogsOptions; + + @CommandLine.Option( + names = {SpecialProperties.DEBUG_OPTION}, + defaultValue = "false", + hidden = true + ) + private boolean debug; + + @Override + public Void call() throws Exception { + store(release(load())); + return null; + } + + private Document load() throws IOException { + return newMarkdownInputSupport().readDocument(input.getFile()); + } + + private Document release(Document document) { + heylogsOptions.initHeylogs().releaseChanges(document, Version.of(ref, '-', date), tagPrefix); + return document; + } + + private void store(Document document) throws IOException { + newMarkdownOutputSupport().writeDocument(output.getFile(), document); + } +} 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 71e3647..45cf47b 100644 --- a/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java +++ b/heylogs-cli/src/main/java/nbbrd/heylogs/cli/ScanCommand.java @@ -16,7 +16,7 @@ import static internal.heylogs.cli.MarkdownInputSupport.newMarkdownInputSupport; import static nbbrd.console.picocli.text.TextOutputSupport.newTextOutputSupport; -@Command(name = "scan", description = "Summarize changelog content.") +@Command(name = "scan", description = "Summarize content.") public final class ScanCommand implements Callable { @CommandLine.Mixin @@ -50,7 +50,7 @@ public Void call() throws Exception { list.add(Scan .builder() .source(markdown.getName(file)) - .summary(heylogs.scan(markdown.readDocument(file))) + .summary(heylogs.scanContent(markdown.readDocument(file))) .build()); } heylogs.formatStatus(formatOptions.getFormatId(), writer, list); diff --git a/heylogs-ext-github/pom.xml b/heylogs-ext-github/pom.xml new file mode 100644 index 0000000..ffaaae1 --- /dev/null +++ b/heylogs-ext-github/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + com.github.nbbrd.heylogs + heylogs-parent + 0.9.0 + + + heylogs-ext-github + jar + + + + + org.checkerframework + checker-qual + provided + + + org.projectlombok + lombok + provided + + + com.github.nbbrd.java-design-util + java-design-processor + provided + + + com.github.nbbrd.java-service-util + java-service-processor + provided + + + + + heylogs-api + ${project.groupId} + ${project.version} + + + + + heylogs-api + ${project.groupId} + ${project.version} + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + \ No newline at end of file diff --git a/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHub.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHub.java new file mode 100644 index 0000000..7df12f7 --- /dev/null +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHub.java @@ -0,0 +1,43 @@ +package nbbrd.heylogs.ext.github; + +import lombok.NonNull; +import nbbrd.design.DirectImpl; +import nbbrd.heylogs.spi.Forge; +import nbbrd.service.ServiceProvider; + +import java.net.URL; + +@DirectImpl +@ServiceProvider +public final class GitHub implements Forge { + + @Override + public @NonNull String getForgeId() { + return "github"; + } + + @Override + public @NonNull String getForgeName() { + return "GitHub"; + } + + @Override + public boolean isCompareLink(@NonNull URL url) { + try { + GitHubCompareLink.parse(url); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + @Override + public @NonNull URL getProjectURL(@NonNull URL url) { + return GitHubCompareLink.parse(url).getProjectURL(); + } + + @Override + public @NonNull URL deriveCompareLink(@NonNull URL latest, @NonNull String nextTag) { + return GitHubCompareLink.parse(latest).derive(nextTag).toURL(); + } +} diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHALink.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHALink.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHALink.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHALink.java index 9c59b2c..36f9f12 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHALink.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHALink.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeLink; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHARef.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHARef.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHARef.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHARef.java index ac3f2a4..0e91128 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCommitSHARef.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCommitSHARef.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeRef; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCompareLink.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCompareLink.java similarity index 61% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubCompareLink.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCompareLink.java index a9211e9..25354e9 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubCompareLink.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubCompareLink.java @@ -1,7 +1,8 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import lombok.AccessLevel; import lombok.NonNull; +import nbbrd.design.RepresentableAs; import nbbrd.design.RepresentableAsString; import nbbrd.design.StaticFactoryMethod; import nbbrd.heylogs.spi.ForgeLink; @@ -14,16 +15,18 @@ // https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-comparing-branches-in-pull-requests#three-dot-and-two-dot-git-diff-comparisons @RepresentableAsString +@RepresentableAs(URL.class) @lombok.Value @lombok.AllArgsConstructor(access = AccessLevel.PRIVATE) class GitHubCompareLink implements ForgeLink { @StaticFactoryMethod public static @NonNull GitHubCompareLink parse(@NonNull CharSequence text) { - return parseURL(urlOf(text)); + return parse(urlOf(text)); } - private static @NonNull GitHubCompareLink parseURL(@NonNull URL url) { + @StaticFactoryMethod + public static @NonNull GitHubCompareLink parse(@NonNull URL url) { String[] pathArray = getPathArray(url); checkPathLength(pathArray, 4); @@ -35,18 +38,41 @@ class GitHubCompareLink implements ForgeLink { return new GitHubCompareLink(baseOf(url), pathArray[0], pathArray[1], pathArray[2], pathArray[3]); } - @NonNull URL base; - @NonNull String owner; - @NonNull String repo; - @NonNull String type; - @NonNull String oid; + @NonNull + URL base; + @NonNull + String owner; + @NonNull + String repo; + @NonNull + String type; + @NonNull + String oid; @Override public String toString() { return URLQueryBuilder.of(base).path(owner).path(repo).path(type).path(oid).toString(); } + public URL toURL() { + return urlOf(toString()); + } + private static final Pattern OWNER = Pattern.compile("[a-z\\d](?:[a-z\\d]|-(?=[a-z\\d])){0,38}"); private static final Pattern REPO = Pattern.compile("[a-z\\d._-]{1,100}"); private static final Pattern OID = Pattern.compile(".+\\.{3}.+"); + + public GitHubCompareLink derive(String tag) { + return new GitHubCompareLink(base, owner, repo, type, getOid(tag)); + } + + public URL getProjectURL() { + return urlOf(URLQueryBuilder.of(base).path(owner).path(repo).toString()); + } + + private String getOid(String tag) { + return oid.endsWith("...HEAD") + ? oid.startsWith("HEAD...") ? (tag + "..." + tag) : (oid.substring(0, oid.length() - 4) + tag) + : (oid.substring(oid.indexOf("...") + 3) + "..." + tag); + } } diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueLink.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueLink.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueLink.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueLink.java index c000a84..82abfab 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueLink.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueLink.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeLink; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueRef.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueRef.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueRef.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueRef.java index a737b95..3a27e92 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubIssueRef.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubIssueRef.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeRef; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionLink.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionLink.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionLink.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionLink.java index 9c0b2f5..28adf61 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionLink.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionLink.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeLink; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionRef.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionRef.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionRef.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionRef.java index 7568d14..722747b 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubMentionRef.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubMentionRef.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeRef; import lombok.AccessLevel; diff --git a/heylogs-api/src/main/java/internal/heylogs/github/GitHubRules.java b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubRules.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/github/GitHubRules.java rename to heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubRules.java index 9309e81..a3f2a61 100644 --- a/heylogs-api/src/main/java/internal/heylogs/github/GitHubRules.java +++ b/heylogs-ext-github/src/main/java/nbbrd/heylogs/ext/github/GitHubRules.java @@ -1,4 +1,4 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import nbbrd.heylogs.spi.ForgeLink; import nbbrd.heylogs.spi.ForgeRef; diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHALinkTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHALinkTest.java similarity index 91% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHALinkTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHALinkTest.java index af93d50..d0b28e2 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHALinkTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHALinkTest.java @@ -1,14 +1,20 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import org.junit.jupiter.api.Test; import static internal.heylogs.URLExtractor.urlOf; -import static internal.heylogs.github.GitHubCommitSHALink.parse; +import static nbbrd.heylogs.ext.github.GitHubCommitSHALink.parse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static tests.heylogs.spi.ForgeLinkAssert.assertForgeLinkCompliance; class GitHubCommitSHALinkTest { + @Test + public void testCompliance() { + assertForgeLinkCompliance(parse("https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHARefTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHARefTest.java similarity index 90% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHARefTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHARefTest.java index 52339fc..0b47b4b 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCommitSHARefTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCommitSHARefTest.java @@ -1,13 +1,19 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import org.junit.jupiter.api.Test; -import static internal.heylogs.github.GitHubCommitSHARef.of; -import static internal.heylogs.github.GitHubCommitSHARef.parse; +import static nbbrd.heylogs.ext.github.GitHubCommitSHARef.of; +import static nbbrd.heylogs.ext.github.GitHubCommitSHARef.parse; import static org.assertj.core.api.Assertions.*; +import static tests.heylogs.spi.ForgeRefAssert.assertForgeRefCompliance; class GitHubCommitSHARefTest { + @Test + public void testCompliance() { + assertForgeRefCompliance(parse("862157d")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCompareLinkTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCompareLinkTest.java similarity index 91% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubCompareLinkTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCompareLinkTest.java index 942438e..8a02507 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubCompareLinkTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubCompareLinkTest.java @@ -1,14 +1,20 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import org.junit.jupiter.api.Test; import static internal.heylogs.URLExtractor.urlOf; -import static internal.heylogs.github.GitHubCompareLink.parse; +import static nbbrd.heylogs.ext.github.GitHubCompareLink.parse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static tests.heylogs.spi.ForgeLinkAssert.assertForgeLinkCompliance; class GitHubCompareLinkTest { + @Test + public void testCompliance() { + assertForgeLinkCompliance(parse("https://github.com/nbbrd/heylogs/compare/v0.7.2...HEAD")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueLinkTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueLinkTest.java similarity index 92% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueLinkTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueLinkTest.java index 406f8b0..8f55472 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueLinkTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueLinkTest.java @@ -1,14 +1,20 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import internal.heylogs.URLExtractor; import org.junit.jupiter.api.Test; -import static internal.heylogs.github.GitHubIssueLink.parse; +import static nbbrd.heylogs.ext.github.GitHubIssueLink.parse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static tests.heylogs.spi.ForgeLinkAssert.assertForgeLinkCompliance; class GitHubIssueLinkTest { + @Test + public void testCompliance() { + assertForgeLinkCompliance(parse("https://github.com/nbbrd/heylogs/issues/173")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueRefTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueRefTest.java similarity index 91% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueRefTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueRefTest.java index 47af56e..1853af8 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubIssueRefTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubIssueRefTest.java @@ -1,12 +1,18 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import org.junit.jupiter.api.Test; -import static internal.heylogs.github.GitHubIssueRef.*; +import static nbbrd.heylogs.ext.github.GitHubIssueRef.*; import static org.assertj.core.api.Assertions.*; +import static tests.heylogs.spi.ForgeRefAssert.assertForgeRefCompliance; class GitHubIssueRefTest { + @Test + public void testCompliance() { + assertForgeRefCompliance(parse("#173")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionLinkTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionLinkTest.java similarity index 84% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionLinkTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionLinkTest.java index ca20447..c41e8a0 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionLinkTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionLinkTest.java @@ -1,14 +1,20 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import internal.heylogs.URLExtractor; import org.junit.jupiter.api.Test; -import static internal.heylogs.github.GitHubMentionLink.parse; +import static nbbrd.heylogs.ext.github.GitHubMentionLink.parse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static tests.heylogs.spi.ForgeLinkAssert.assertForgeLinkCompliance; class GitHubMentionLinkTest { + @Test + public void testCompliance() { + assertForgeLinkCompliance(parse("https://github.com/orgs/nbbrd/teams/devs")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionRefTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionRefTest.java similarity index 88% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionRefTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionRefTest.java index 6c5b7bf..e2fa9c8 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubMentionRefTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubMentionRefTest.java @@ -1,13 +1,19 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; import org.junit.jupiter.api.Test; -import static internal.heylogs.github.GitHubMentionRef.of; -import static internal.heylogs.github.GitHubMentionRef.parse; +import static nbbrd.heylogs.ext.github.GitHubMentionRef.of; +import static nbbrd.heylogs.ext.github.GitHubMentionRef.parse; import static org.assertj.core.api.Assertions.*; +import static tests.heylogs.spi.ForgeRefAssert.assertForgeRefCompliance; class GitHubMentionRefTest { + @Test + public void testCompliance() { + assertForgeRefCompliance(parse("@charphi")); + } + @Test public void testRepresentableAsString() { assertThatIllegalArgumentException() diff --git a/heylogs-api/src/test/java/internal/heylogs/github/GitHubRulesTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubRulesTest.java similarity index 92% rename from heylogs-api/src/test/java/internal/heylogs/github/GitHubRulesTest.java rename to heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubRulesTest.java index 7c5e256..39b8d83 100644 --- a/heylogs-api/src/test/java/internal/heylogs/github/GitHubRulesTest.java +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubRulesTest.java @@ -1,30 +1,28 @@ -package internal.heylogs.github; +package nbbrd.heylogs.ext.github; -import _test.Sample; import com.vladsch.flexmark.ast.Link; import com.vladsch.flexmark.util.ast.Node; import nbbrd.heylogs.Nodes; import nbbrd.heylogs.spi.Rule; import nbbrd.heylogs.spi.RuleIssue; -import nbbrd.heylogs.spi.RuleLoader; import org.junit.jupiter.api.Test; +import tests.heylogs.api.Sample; import java.util.Objects; -import static _test.Sample.using; -import static internal.heylogs.github.GitHubRules.*; import static java.util.stream.Collectors.toList; import static nbbrd.heylogs.Nodes.of; +import static nbbrd.heylogs.ext.github.GitHubRules.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.Index.atIndex; +import static tests.heylogs.api.Sample.using; +import static tests.heylogs.spi.RuleAssert.assertRuleCompliance; public class GitHubRulesTest { @Test - public void testIdPattern() { - assertThat(new GitHubRules().getProviders()) - .extracting(Rule::getRuleId) - .allMatch(RuleLoader.ID_PATTERN.asPredicate()); + public void testCompliance() { + assertRuleCompliance(new GitHubRules()); } @Test diff --git a/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubTest.java b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubTest.java new file mode 100644 index 0000000..42735c1 --- /dev/null +++ b/heylogs-ext-github/src/test/java/nbbrd/heylogs/ext/github/GitHubTest.java @@ -0,0 +1,35 @@ +package nbbrd.heylogs.ext.github; + +import nbbrd.heylogs.spi.Forge; +import org.junit.jupiter.api.Test; + +import static internal.heylogs.URLExtractor.urlOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static tests.heylogs.spi.ForgeAssert.assertForgeCompliance; + +class GitHubTest { + + @Test + void testCompliance() { + assertForgeCompliance(new GitHub()); + } + + @Test + void testIsCompareLink() { + Forge x = new GitHub(); + assertThat(x.isCompareLink(urlOf("https://nbb.be"))).isFalse(); + assertThat(x.isCompareLink(urlOf("https://github.com/nbbrd/heylogs/compare/v0.7.2...HEAD"))).isTrue(); + } + + @Test + void testGetProjectURL() { + Forge x = new GitHub(); + + assertThatIllegalArgumentException() + .isThrownBy(() -> x.getProjectURL(urlOf("https://nbb.be"))); + + assertThat(x.getProjectURL(urlOf("https://github.com/nbbrd/heylogs/compare/v0.7.2...HEAD"))) + .isEqualTo(urlOf("https://github.com/nbbrd/heylogs")); + } +} \ No newline at end of file diff --git a/heylogs-api/src/test/resources/InvalidGitHubCommitSHARef.md b/heylogs-ext-github/src/test/resources/InvalidGitHubCommitSHARef.md similarity index 100% rename from heylogs-api/src/test/resources/InvalidGitHubCommitSHARef.md rename to heylogs-ext-github/src/test/resources/InvalidGitHubCommitSHARef.md diff --git a/heylogs-api/src/test/resources/InvalidGitHubIssueRef.md b/heylogs-ext-github/src/test/resources/InvalidGitHubIssueRef.md similarity index 100% rename from heylogs-api/src/test/resources/InvalidGitHubIssueRef.md rename to heylogs-ext-github/src/test/resources/InvalidGitHubIssueRef.md diff --git a/heylogs-api/src/test/resources/InvalidGitHubMentionRef.md b/heylogs-ext-github/src/test/resources/InvalidGitHubMentionRef.md similarity index 100% rename from heylogs-api/src/test/resources/InvalidGitHubMentionRef.md rename to heylogs-ext-github/src/test/resources/InvalidGitHubMentionRef.md diff --git a/heylogs-api/src/test/resources/InvalidGitHubPullRequestRef.md b/heylogs-ext-github/src/test/resources/InvalidGitHubPullRequestRef.md similarity index 100% rename from heylogs-api/src/test/resources/InvalidGitHubPullRequestRef.md rename to heylogs-ext-github/src/test/resources/InvalidGitHubPullRequestRef.md diff --git a/heylogs-ext-github/src/test/resources/Main.md b/heylogs-ext-github/src/test/resources/Main.md new file mode 100644 index 0000000..145e79f --- /dev/null +++ b/heylogs-ext-github/src/test/resources/Main.md @@ -0,0 +1,206 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Added Dutch translation + +### Fixed + +- Fixed foldouts in Dutch translation + +## [1.1.0] - 2019-02-15 + +### Added + +- Danish translation from [@frederikspang](https://github.com/frederikspang). +- Georgian translation from [@tatocaster](https://github.com/tatocaster). +- Changelog inconsistency section in Bad Practices + +### Changed + +- Fixed typos in Italian translation from [@lorenzo-arena](https://github.com/lorenzo-arena). +- Fixed typos in Indonesian translation from [@ekojs](https://github.com/ekojs). + +## [1.0.0] - 2017-06-20 + +### Added + +- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). +- Version navigation. +- Links to latest released version in previous versions. +- "Why keep a changelog?" section. +- "Who needs a changelog?" section. +- "How do I make a changelog?" section. +- "Frequently Asked Questions" section. +- New "Guiding Principles" sub-section to "How do I make a changelog?". +- Simplified and Traditional Chinese translations from [@tianshuo](https://github.com/tianshuo). +- German translation from [@mpbzh](https://github.com/mpbzh) & [@Art4](https://github.com/Art4). +- Italian translation from [@azkidenz](https://github.com/azkidenz). +- Swedish translation from [@magol](https://github.com/magol). +- Turkish translation from [@emreerkan](https://github.com/emreerkan). +- French translation from [@zapashcanon](https://github.com/zapashcanon). +- Brazilian Portuguese translation from [@Webysther](https://github.com/Webysther). +- Polish translation from [@amielucha](https://github.com/amielucha) & [@m-aciek](https://github.com/m-aciek). +- Russian translation from [@aishek](https://github.com/aishek). +- Czech translation from [@h4vry](https://github.com/h4vry). +- Slovak translation from [@jkostolansky](https://github.com/jkostolansky). +- Korean translation from [@pierceh89](https://github.com/pierceh89). +- Croatian translation from [@porx](https://github.com/porx). +- Persian translation from [@Hameds](https://github.com/Hameds). +- Ukrainian translation from [@osadchyi-s](https://github.com/osadchyi-s). + +### Changed + +- Start using "changelog" over "change log" since it's the common usage. +- Start versioning based on the current English version at 0.3.0 to help + translation authors keep things up-to-date. +- Rewrite "What makes unicorns cry?" section. +- Rewrite "Ignoring Deprecations" sub-section to clarify the ideal + scenario. +- Improve "Commit log diffs" sub-section to further argument against + them. +- Merge "Why can’t people just use a git log diff?" with "Commit log + diffs" +- Fix typos in Simplified Chinese and Traditional Chinese translations. +- Fix typos in Brazilian Portuguese translation. +- Fix typos in Turkish translation. +- Fix typos in Czech translation. +- Fix typos in Swedish translation. +- Improve phrasing in French translation. +- Fix phrasing and spelling in German translation. + +### Removed + +- Section about "changelog" vs "CHANGELOG". + +## [0.3.0] - 2015-12-03 + +### Added + +- RU translation from [@aishek](https://github.com/aishek). +- pt-BR translation from [@tallesl](https://github.com/tallesl). +- es-ES translation from [@ZeliosAriex](https://github.com/ZeliosAriex). + +## [0.2.0] - 2015-10-06 + +### Changed + +- Remove exclusionary mentions of "open source" since this project can + benefit both "open" and "closed" source projects equally. + +## [0.1.0] - 2015-10-06 + +### Added + +- Answer "Should you ever rewrite a change log?". + +### Changed + +- Improve argument against commit logs. +- Start following [SemVer](https://semver.org) properly. + +## [0.0.8] - 2015-02-17 + +### Changed + +- Update year to match in every README example. +- Reluctantly stop making fun of Brits only, since most of the world + writes dates in a strange way. + +### Fixed + +- Fix typos in recent README changes. +- Update outdated unreleased diff link. + +## [0.0.7] - 2015-02-16 + +### Added + +- Link, and make it obvious that date format is ISO 8601. + +### Changed + +- Clarified the section on "Is there a standard change log format?". + +### Fixed + +- Fix Markdown links to tag comparison URL with footnote-style links. + +## [0.0.6] - 2014-12-12 + +### Added + +- README section on "yanked" releases. + +## [0.0.5] - 2014-08-09 + +### Added + +- Markdown links to version tags on release headings. +- Unreleased section to gather unreleased changes and encourage note + keeping prior to releases. + +## [0.0.4] - 2014-08-09 + +### Added + +- Better explanation of the difference between the file ("CHANGELOG") + and its function "the change log". + +### Changed + +- Refer to a "change log" instead of a "CHANGELOG" throughout the site + to differentiate between the file and the purpose of the file — the + logging of changes. + +### Removed + +- Remove empty sections from CHANGELOG, they occupy too much space and + create too much noise in the file. People will have to assume that the + missing sections were intentionally left out because they contained no + notable changes. + +## [0.0.3] - 2014-08-09 + +### Added + +- "Why should I care?" section mentioning The Changelog podcast. + +## [0.0.2] - 2014-07-10 + +### Added + +- Explanation of the recommended reverse chronological release ordering. + +## [0.0.1] - 2014-05-31 + +### Added + +- This CHANGELOG file to hopefully serve as an evolving example of a + standardized open source project CHANGELOG. +- CNAME file to enable GitHub Pages custom domain +- README now contains answers to common questions about CHANGELOGs +- Good examples and basic guidelines, including proper date formatting. +- Counter-examples: "What makes unicorns cry?" + +[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.3.0...v1.0.0 +[0.3.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.8...v0.1.0 +[0.0.8]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.7...v0.0.8 +[0.0.7]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.6...v0.0.7 +[0.0.6]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.5...v0.0.6 +[0.0.5]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.4...v0.0.5 +[0.0.4]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.3...v0.0.4 +[0.0.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.2...v0.0.3 +[0.0.2]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.1...v0.0.2 +[0.0.1]: https://github.com/olivierlacan/keep-a-changelog/releases/tag/v0.0.1 \ No newline at end of file diff --git a/heylogs-ext-json/pom.xml b/heylogs-ext-json/pom.xml new file mode 100644 index 0000000..530805a --- /dev/null +++ b/heylogs-ext-json/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + com.github.nbbrd.heylogs + heylogs-parent + 0.9.0 + + + heylogs-ext-json + jar + + + + + org.checkerframework + checker-qual + provided + + + org.projectlombok + lombok + provided + + + com.github.nbbrd.java-design-util + java-design-processor + provided + + + com.github.nbbrd.java-service-util + java-service-processor + provided + + + + + heylogs-api + ${project.groupId} + ${project.version} + + + com.google.code.gson + gson + 2.11.0 + + + + + heylogs-api + ${project.groupId} + ${project.version} + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + \ No newline at end of file diff --git a/heylogs-api/src/main/java/internal/heylogs/JsonFormat.java b/heylogs-ext-json/src/main/java/nbbrd/heylogs/ext/json/JsonFormat.java similarity index 99% rename from heylogs-api/src/main/java/internal/heylogs/JsonFormat.java rename to heylogs-ext-json/src/main/java/nbbrd/heylogs/ext/json/JsonFormat.java index e4c2448..b16130b 100644 --- a/heylogs-api/src/main/java/internal/heylogs/JsonFormat.java +++ b/heylogs-ext-json/src/main/java/nbbrd/heylogs/ext/json/JsonFormat.java @@ -1,4 +1,4 @@ -package internal.heylogs; +package nbbrd.heylogs.ext.json; import com.google.gson.*; import lombok.NonNull; diff --git a/heylogs-api/src/test/java/internal/heylogs/JsonFormatTest.java b/heylogs-ext-json/src/test/java/nbbrd/heylogs/ext/json/JsonFormatTest.java similarity index 79% rename from heylogs-api/src/test/java/internal/heylogs/JsonFormatTest.java rename to heylogs-ext-json/src/test/java/nbbrd/heylogs/ext/json/JsonFormatTest.java index e28d629..6e16a1d 100644 --- a/heylogs-api/src/test/java/internal/heylogs/JsonFormatTest.java +++ b/heylogs-ext-json/src/test/java/nbbrd/heylogs/ext/json/JsonFormatTest.java @@ -1,21 +1,20 @@ -package internal.heylogs; +package nbbrd.heylogs.ext.json; import nbbrd.heylogs.spi.Format; -import nbbrd.heylogs.spi.FormatLoader; import org.junit.jupiter.api.Test; -import static _test.Sample.*; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static tests.heylogs.api.Sample.*; +import static tests.heylogs.spi.FormatAssert.assertFormatCompliance; class JsonFormatTest { @Test - public void testIdPattern() { - assertThat(new JsonFormat().getFormatId()) - .matches(FormatLoader.ID_PATTERN); + public void testCompliance() { + assertFormatCompliance(new JsonFormat()); } @Test @@ -23,13 +22,13 @@ public void testFormatProblems() { Format x = new JsonFormat(); assertThat(writing(appendable -> x.formatProblems(appendable, singletonList(CHECK1)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "check1.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/check1.json")); assertThat(writing(appendable -> x.formatProblems(appendable, singletonList(CHECK2)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "check2.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/check2.json")); assertThat(writing(appendable -> x.formatProblems(appendable, singletonList(CHECK3)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "check3.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/check3.json")); } @Test @@ -37,10 +36,10 @@ public void testFormatStatus() { Format x = new JsonFormat(); assertThat(writing(appendable -> x.formatStatus(appendable, singletonList(SCAN1)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "scan1.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/scan1.json")); assertThat(writing(appendable -> x.formatStatus(appendable, singletonList(SCAN2)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "scan2.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/scan2.json")); } @Test @@ -48,13 +47,13 @@ public void testFormatResource() { Format x = new JsonFormat(); assertThat(writing(appendable -> x.formatResources(appendable, emptyList()))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "resource1.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/resource1.json")); assertThat(writing(appendable -> x.formatResources(appendable, singletonList(RESOURCE1)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "resource2.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/resource2.json")); assertThat(writing(appendable -> x.formatResources(appendable, asList(RESOURCE1, RESOURCE2)))) - .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "resource3.json")); + .isEqualToNormalizingNewlines(contentOf(JsonFormatTest.class, "/resource3.json")); } } \ No newline at end of file diff --git a/heylogs-api/src/test/resources/internal/heylogs/check1.json b/heylogs-ext-json/src/test/resources/check1.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/check1.json rename to heylogs-ext-json/src/test/resources/check1.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/check2.json b/heylogs-ext-json/src/test/resources/check2.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/check2.json rename to heylogs-ext-json/src/test/resources/check2.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/check3.json b/heylogs-ext-json/src/test/resources/check3.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/check3.json rename to heylogs-ext-json/src/test/resources/check3.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/resource1.json b/heylogs-ext-json/src/test/resources/resource1.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/resource1.json rename to heylogs-ext-json/src/test/resources/resource1.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/resource2.json b/heylogs-ext-json/src/test/resources/resource2.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/resource2.json rename to heylogs-ext-json/src/test/resources/resource2.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/resource3.json b/heylogs-ext-json/src/test/resources/resource3.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/resource3.json rename to heylogs-ext-json/src/test/resources/resource3.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/scan1.json b/heylogs-ext-json/src/test/resources/scan1.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/scan1.json rename to heylogs-ext-json/src/test/resources/scan1.json diff --git a/heylogs-api/src/test/resources/internal/heylogs/scan2.json b/heylogs-ext-json/src/test/resources/scan2.json similarity index 100% rename from heylogs-api/src/test/resources/internal/heylogs/scan2.json rename to heylogs-ext-json/src/test/resources/scan2.json diff --git a/heylogs-ext-semver/pom.xml b/heylogs-ext-semver/pom.xml new file mode 100644 index 0000000..40be88f --- /dev/null +++ b/heylogs-ext-semver/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + + com.github.nbbrd.heylogs + heylogs-parent + 0.9.0 + + + heylogs-ext-semver + jar + + + + + org.checkerframework + checker-qual + provided + + + org.projectlombok + lombok + provided + + + com.github.nbbrd.java-design-util + java-design-processor + provided + + + com.github.nbbrd.java-service-util + java-service-processor + provided + + + + + heylogs-api + ${project.groupId} + ${project.version} + + + org.semver4j + semver4j + 5.3.0 + + + org.jetbrains + annotations + + + + + + + heylogs-api + ${project.groupId} + ${project.version} + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + \ No newline at end of file diff --git a/heylogs-api/src/main/java/internal/heylogs/semver/SemVer.java b/heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVer.java similarity index 94% rename from heylogs-api/src/main/java/internal/heylogs/semver/SemVer.java rename to heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVer.java index 1eb2947..1a4d719 100644 --- a/heylogs-api/src/main/java/internal/heylogs/semver/SemVer.java +++ b/heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVer.java @@ -1,4 +1,4 @@ -package internal.heylogs.semver; +package nbbrd.heylogs.ext.semver; import lombok.NonNull; import nbbrd.design.DirectImpl; diff --git a/heylogs-api/src/main/java/internal/heylogs/semver/SemVerRule.java b/heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVerRule.java similarity index 98% rename from heylogs-api/src/main/java/internal/heylogs/semver/SemVerRule.java rename to heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVerRule.java index 6190ef4..3411678 100644 --- a/heylogs-api/src/main/java/internal/heylogs/semver/SemVerRule.java +++ b/heylogs-ext-semver/src/main/java/nbbrd/heylogs/ext/semver/SemVerRule.java @@ -1,4 +1,4 @@ -package internal.heylogs.semver; +package nbbrd.heylogs.ext.semver; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.util.ast.Node; diff --git a/heylogs-api/src/test/java/internal/heylogs/semver/SemVerRuleTest.java b/heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerRuleTest.java similarity index 85% rename from heylogs-api/src/test/java/internal/heylogs/semver/SemVerRuleTest.java rename to heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerRuleTest.java index 1809a66..5cff839 100644 --- a/heylogs-api/src/test/java/internal/heylogs/semver/SemVerRuleTest.java +++ b/heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerRuleTest.java @@ -1,23 +1,22 @@ -package internal.heylogs.semver; +package nbbrd.heylogs.ext.semver; import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.util.ast.Node; import nbbrd.heylogs.spi.RuleIssue; -import nbbrd.heylogs.spi.RuleLoader; import org.junit.jupiter.api.Test; import java.util.Objects; -import static _test.Sample.using; import static nbbrd.heylogs.Nodes.of; import static org.assertj.core.api.Assertions.assertThat; +import static tests.heylogs.api.Sample.using; +import static tests.heylogs.spi.RuleAssert.assertRuleCompliance; public class SemVerRuleTest { @Test - public void testIdPattern() { - assertThat(new SemVerRule().getRuleId()) - .matches(RuleLoader.ID_PATTERN); + public void testCompliance() { + assertRuleCompliance(new SemVerRule()); } @Test diff --git a/heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerTest.java b/heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerTest.java new file mode 100644 index 0000000..423fb5d --- /dev/null +++ b/heylogs-ext-semver/src/test/java/nbbrd/heylogs/ext/semver/SemVerTest.java @@ -0,0 +1,21 @@ +package nbbrd.heylogs.ext.semver; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static tests.heylogs.spi.VersioningAssert.assertVersioningCompliance; + +class SemVerTest { + + @Test + public void testCompliance() { + assertVersioningCompliance(new SemVer()); + } + + @Test + void isValidVersion() { + SemVer x = new SemVer(); + assertThat(x.isValidVersion("1.1.0")).isTrue(); + assertThat(x.isValidVersion(".1.0")).isFalse(); + } +} \ No newline at end of file diff --git a/heylogs-ext-semver/src/test/resources/Empty.md b/heylogs-ext-semver/src/test/resources/Empty.md new file mode 100644 index 0000000..e69de29 diff --git a/heylogs-ext-semver/src/test/resources/InvalidSemver.md b/heylogs-ext-semver/src/test/resources/InvalidSemver.md new file mode 100644 index 0000000..886858b --- /dev/null +++ b/heylogs-ext-semver/src/test/resources/InvalidSemver.md @@ -0,0 +1,7 @@ +# Changelog + +## [1.1.0] - 2019-02-15 +## [.1.0] - 2019-02-15 + +[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0 +[.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0 diff --git a/heylogs-ext-semver/src/test/resources/Main.md b/heylogs-ext-semver/src/test/resources/Main.md new file mode 100644 index 0000000..145e79f --- /dev/null +++ b/heylogs-ext-semver/src/test/resources/Main.md @@ -0,0 +1,206 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Added Dutch translation + +### Fixed + +- Fixed foldouts in Dutch translation + +## [1.1.0] - 2019-02-15 + +### Added + +- Danish translation from [@frederikspang](https://github.com/frederikspang). +- Georgian translation from [@tatocaster](https://github.com/tatocaster). +- Changelog inconsistency section in Bad Practices + +### Changed + +- Fixed typos in Italian translation from [@lorenzo-arena](https://github.com/lorenzo-arena). +- Fixed typos in Indonesian translation from [@ekojs](https://github.com/ekojs). + +## [1.0.0] - 2017-06-20 + +### Added + +- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). +- Version navigation. +- Links to latest released version in previous versions. +- "Why keep a changelog?" section. +- "Who needs a changelog?" section. +- "How do I make a changelog?" section. +- "Frequently Asked Questions" section. +- New "Guiding Principles" sub-section to "How do I make a changelog?". +- Simplified and Traditional Chinese translations from [@tianshuo](https://github.com/tianshuo). +- German translation from [@mpbzh](https://github.com/mpbzh) & [@Art4](https://github.com/Art4). +- Italian translation from [@azkidenz](https://github.com/azkidenz). +- Swedish translation from [@magol](https://github.com/magol). +- Turkish translation from [@emreerkan](https://github.com/emreerkan). +- French translation from [@zapashcanon](https://github.com/zapashcanon). +- Brazilian Portuguese translation from [@Webysther](https://github.com/Webysther). +- Polish translation from [@amielucha](https://github.com/amielucha) & [@m-aciek](https://github.com/m-aciek). +- Russian translation from [@aishek](https://github.com/aishek). +- Czech translation from [@h4vry](https://github.com/h4vry). +- Slovak translation from [@jkostolansky](https://github.com/jkostolansky). +- Korean translation from [@pierceh89](https://github.com/pierceh89). +- Croatian translation from [@porx](https://github.com/porx). +- Persian translation from [@Hameds](https://github.com/Hameds). +- Ukrainian translation from [@osadchyi-s](https://github.com/osadchyi-s). + +### Changed + +- Start using "changelog" over "change log" since it's the common usage. +- Start versioning based on the current English version at 0.3.0 to help + translation authors keep things up-to-date. +- Rewrite "What makes unicorns cry?" section. +- Rewrite "Ignoring Deprecations" sub-section to clarify the ideal + scenario. +- Improve "Commit log diffs" sub-section to further argument against + them. +- Merge "Why can’t people just use a git log diff?" with "Commit log + diffs" +- Fix typos in Simplified Chinese and Traditional Chinese translations. +- Fix typos in Brazilian Portuguese translation. +- Fix typos in Turkish translation. +- Fix typos in Czech translation. +- Fix typos in Swedish translation. +- Improve phrasing in French translation. +- Fix phrasing and spelling in German translation. + +### Removed + +- Section about "changelog" vs "CHANGELOG". + +## [0.3.0] - 2015-12-03 + +### Added + +- RU translation from [@aishek](https://github.com/aishek). +- pt-BR translation from [@tallesl](https://github.com/tallesl). +- es-ES translation from [@ZeliosAriex](https://github.com/ZeliosAriex). + +## [0.2.0] - 2015-10-06 + +### Changed + +- Remove exclusionary mentions of "open source" since this project can + benefit both "open" and "closed" source projects equally. + +## [0.1.0] - 2015-10-06 + +### Added + +- Answer "Should you ever rewrite a change log?". + +### Changed + +- Improve argument against commit logs. +- Start following [SemVer](https://semver.org) properly. + +## [0.0.8] - 2015-02-17 + +### Changed + +- Update year to match in every README example. +- Reluctantly stop making fun of Brits only, since most of the world + writes dates in a strange way. + +### Fixed + +- Fix typos in recent README changes. +- Update outdated unreleased diff link. + +## [0.0.7] - 2015-02-16 + +### Added + +- Link, and make it obvious that date format is ISO 8601. + +### Changed + +- Clarified the section on "Is there a standard change log format?". + +### Fixed + +- Fix Markdown links to tag comparison URL with footnote-style links. + +## [0.0.6] - 2014-12-12 + +### Added + +- README section on "yanked" releases. + +## [0.0.5] - 2014-08-09 + +### Added + +- Markdown links to version tags on release headings. +- Unreleased section to gather unreleased changes and encourage note + keeping prior to releases. + +## [0.0.4] - 2014-08-09 + +### Added + +- Better explanation of the difference between the file ("CHANGELOG") + and its function "the change log". + +### Changed + +- Refer to a "change log" instead of a "CHANGELOG" throughout the site + to differentiate between the file and the purpose of the file — the + logging of changes. + +### Removed + +- Remove empty sections from CHANGELOG, they occupy too much space and + create too much noise in the file. People will have to assume that the + missing sections were intentionally left out because they contained no + notable changes. + +## [0.0.3] - 2014-08-09 + +### Added + +- "Why should I care?" section mentioning The Changelog podcast. + +## [0.0.2] - 2014-07-10 + +### Added + +- Explanation of the recommended reverse chronological release ordering. + +## [0.0.1] - 2014-05-31 + +### Added + +- This CHANGELOG file to hopefully serve as an evolving example of a + standardized open source project CHANGELOG. +- CNAME file to enable GitHub Pages custom domain +- README now contains answers to common questions about CHANGELOGs +- Good examples and basic guidelines, including proper date formatting. +- Counter-examples: "What makes unicorns cry?" + +[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.3.0...v1.0.0 +[0.3.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.8...v0.1.0 +[0.0.8]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.7...v0.0.8 +[0.0.7]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.6...v0.0.7 +[0.0.6]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.5...v0.0.6 +[0.0.5]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.4...v0.0.5 +[0.0.4]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.3...v0.0.4 +[0.0.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.2...v0.0.3 +[0.0.2]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.1...v0.0.2 +[0.0.1]: https://github.com/olivierlacan/keep-a-changelog/releases/tag/v0.0.1 \ No newline at end of file diff --git a/heylogs-maven-plugin/pom.xml b/heylogs-maven-plugin/pom.xml index e2a2e32..7889503 100644 --- a/heylogs-maven-plugin/pom.xml +++ b/heylogs-maven-plugin/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.8.1 + 0.9.0 heylogs-maven-plugin @@ -49,7 +49,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.12.0 + 3.14.0 provided @@ -59,6 +59,21 @@ ${project.groupId} ${project.version} + + heylogs-ext-github + ${project.groupId} + ${project.version} + + + heylogs-ext-json + ${project.groupId} + ${project.version} + + + heylogs-ext-semver + ${project.groupId} + ${project.version} + com.github.nbbrd.java-console-properties java-console-properties @@ -71,7 +86,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.12.0 + 3.14.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 index 98cad4c..c50a76e 100644 --- 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 @@ -1,13 +1,9 @@ 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 { @@ -23,14 +19,4 @@ public interface MojoFunction { } }; } - - @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 e9d2b2f..7ce1f88 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,9 +1,9 @@ package nbbrd.heylogs.maven.plugin; import internal.heylogs.StylishFormat; -import internal.heylogs.semver.SemVer; import nbbrd.heylogs.Check; import nbbrd.heylogs.Heylogs; +import nbbrd.heylogs.ext.semver.SemVer; import nbbrd.heylogs.spi.Versioning; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -73,7 +73,7 @@ private void check() throws MojoExecutionException { Check check = Check .builder() .source(inputFile.toString()) - .problems(heylogs.validate(readChangelog(inputFile))) + .problems(heylogs.checkFormat(readChangelog(inputFile))) .build(); try (Writer writer = newWriter(outputFile, check.hasErrors() ? getLog()::error : getLog()::info)) { 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 b50dc05..70766f6 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,7 +1,9 @@ package nbbrd.heylogs.maven.plugin; import com.vladsch.flexmark.util.ast.Document; -import nbbrd.heylogs.Extractor; +import internal.heylogs.maven.plugin.MojoFunction; +import nbbrd.heylogs.Filter; +import nbbrd.heylogs.Heylogs; import nbbrd.heylogs.TimeRange; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -9,11 +11,12 @@ import org.apache.maven.plugins.annotations.Parameter; import java.io.File; +import java.time.LocalDate; import java.util.Objects; +import java.util.regex.Pattern; import static internal.heylogs.maven.plugin.HeylogsParameters.*; -import static internal.heylogs.maven.plugin.MojoFunction.onLocalDate; -import static internal.heylogs.maven.plugin.MojoFunction.onPattern; +import static internal.heylogs.maven.plugin.MojoFunction.of; @Mojo(name = "extract", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true, requiresProject = false) public final class ExtractMojo extends HeylogsMojo { @@ -54,29 +57,32 @@ public void execute() throws MojoExecutionException { throw new MojoExecutionException("Changelog not found"); } - extract(loadExtractor()); + extract(loadFilter()); } - private Extractor loadExtractor() throws MojoExecutionException { - return Extractor + private Filter loadFilter() throws MojoExecutionException { + return Filter .builder() .ref(Objects.toString(ref, "")) - .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)) - ) + .unreleasedPattern(UNRELEASED_PATTERN_PARSER.applyWithMojo(unreleasedPattern)) + .timeRange(TimeRange.of(FROM_PARSER.applyWithMojo(from), TO_PARSER.applyWithMojo(to))) .limit(limit) .ignoreContent(ignoreContent) .build(); } - private void extract(Extractor extractor) throws MojoExecutionException { + private void extract(Filter filter) throws MojoExecutionException { + Heylogs heylogs = initHeylogs(false); + Document changelog = readChangelog(inputFile); - getLog().info("Extracting with " + extractor); - extractor.extract(changelog); + getLog().info("Extracting with " + filter); + heylogs.extractVersions(changelog, filter); writeChangelog(changelog, outputFile); } + + private static final MojoFunction UNRELEASED_PATTERN_PARSER = of(Pattern::compile, "Invalid unreleased pattern"); + private static final MojoFunction FROM_PARSER = of(Filter::parseLocalDate, "Invalid format for 'from' parameter"); + private static final MojoFunction TO_PARSER = of(Filter::parseLocalDate, "Invalid format for 'to' parameter"); } 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 index 0cce37e..1ea666e 100644 --- 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 @@ -3,9 +3,9 @@ import com.vladsch.flexmark.formatter.Formatter; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; -import internal.heylogs.semver.SemVerRule; import nbbrd.design.MightBePromoted; import nbbrd.heylogs.Heylogs; +import nbbrd.heylogs.ext.semver.SemVerRule; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Parameter; 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 index 1a1d4d6..cf42473 100644 --- 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 @@ -39,7 +39,7 @@ private void list() throws MojoExecutionException { Heylogs heylogs = initHeylogs(semver); try (Writer writer = newWriter(outputFile, getLog()::info)) { - heylogs.formatResources(formatId, writer, heylogs.getResources()); + heylogs.formatResources(formatId, writer, heylogs.listResources()); } catch (IOException ex) { throw new MojoExecutionException("Error while writing", ex); } diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ReleaseMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ReleaseMojo.java new file mode 100644 index 0000000..d361b14 --- /dev/null +++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/ReleaseMojo.java @@ -0,0 +1,85 @@ +package nbbrd.heylogs.maven.plugin; + +import com.vladsch.flexmark.util.ast.Document; +import internal.heylogs.maven.plugin.MojoFunction; +import nbbrd.design.MightBePromoted; +import nbbrd.heylogs.Heylogs; +import nbbrd.heylogs.Version; +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.time.LocalDate; +import java.time.ZoneId; +import java.util.Objects; + +import static internal.heylogs.maven.plugin.HeylogsParameters.*; +import static internal.heylogs.maven.plugin.MojoFunction.of; + +@Mojo(name = "release", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true, requiresProject = false) +public final class ReleaseMojo extends HeylogsMojo { + + @Parameter(defaultValue = WORKING_DIR_CHANGELOG, property = INPUT_FILE_PROPERTY) + private File inputFile; + + @Parameter(defaultValue = "${project.build.directory}/CHANGELOG.md", property = OUTPUT_FILE_PROPERTY) + private File outputFile; + + @Parameter(defaultValue = "${project.version}", property = "heylogs.ref") + private String ref; + + @Parameter(defaultValue = "", property = "heylogs.tag.prefix") + private String tagPrefix; + + @Parameter(defaultValue = "", property = "heylogs.date") + private String date; + + @Override + public void execute() throws MojoExecutionException { + if (skip) { + getLog().info("Release has been skipped."); + return; + } + + if (!inputFile.exists()) { + getLog().error("Changelog not found"); + throw new MojoExecutionException("Changelog not found"); + } + + release(loadVersion(), loadTagPrefix()); + } + + private Version loadVersion() throws MojoExecutionException { + return Version.of(ref, '-', DATE_PARSER.applyWithMojo(date)); + } + + private String loadTagPrefix() throws MojoExecutionException { + return TAG_PREFIX_PARSER.applyWithMojo(tagPrefix); + } + + private void release(Version version, String tagPrefix) throws MojoExecutionException { + Heylogs heylogs = initHeylogs(false); + + Document changelog = readChangelog(inputFile); + + getLog().info("Releasing " + version + " with tag prefix '" + tagPrefix + "'"); + heylogs.releaseChanges(changelog, version, tagPrefix); + + writeChangelog(changelog, outputFile); + } + + @MightBePromoted + private static String parseTagPrefix(String tagPrefix) { + return Objects.toString(tagPrefix, ""); + } + + @MightBePromoted + private static LocalDate parseDate(String date) { + return date == null ? LocalDate.now(ZoneId.systemDefault()) : LocalDate.parse(date); + } + + private static final MojoFunction TAG_PREFIX_PARSER = of(ReleaseMojo::parseTagPrefix, "Invalid format for 'tagPrefix' parameter"); + private static final MojoFunction DATE_PARSER = of(ReleaseMojo::parseDate, "Invalid format for 'date' parameter"); +} 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 index acf4081..9d7881d 100644 --- 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 @@ -51,7 +51,7 @@ private void scan() throws MojoExecutionException { Scan scan = Scan .builder() .source(inputFile.toString()) - .summary(heylogs.scan(readChangelog(inputFile))) + .summary(heylogs.scanContent(readChangelog(inputFile))) .build(); try (Writer writer = newWriter(outputFile, getLog()::info)) { diff --git a/lychee.toml b/lychee.toml index 7690982..bcb4aea 100644 --- a/lychee.toml +++ b/lychee.toml @@ -1 +1 @@ -exclude_path = ["heylogs-api/src/test/resources/Main.md"] \ No newline at end of file +exclude_path = ["heylogs-api/src/test/resources/Main.md","heylogs-ext-github/src/test/resources/Main.md","heylogs-ext-semver/src/test/resources/Main.md"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c5907d..4615da8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.8.1 + 0.9.0 pom heylogs @@ -39,7 +39,7 @@ UTF-8 - 2024-04-18T14:11:44Z + 2024-08-28T13:40:35Z 1.8 1.8 @@ -54,21 +54,21 @@ org.checkerframework checker-qual - 3.42.0 + 3.46.0 org.junit junit-bom - 5.10.2 + 5.11.0 pom import org.assertj assertj-core - 3.25.3 + 3.26.3 @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.3.2 + 3.4.0 org.apache.maven.plugins @@ -103,17 +103,17 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.3 org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.3 org.apache.maven.plugins maven-jar-plugin - 3.4.0 + 3.4.2 org.apache.maven.plugins @@ -123,17 +123,17 @@ org.apache.maven.plugins maven-site-plugin - 3.12.1 + 3.20.0 org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.4.0 org.apache.maven.surefire surefire-junit-platform - 3.2.5 + 3.4.0 @@ -141,17 +141,17 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.1 + 3.8.0 org.apache.maven.plugins maven-enforcer-plugin - 3.4.1 + 3.5.0 org.gaul modernizer-maven-plugin - 2.7.0 + 2.9.0 de.thetaphi @@ -171,7 +171,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.8.0 org.simplify4u.plugins @@ -181,7 +181,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 org.jacoco @@ -191,7 +191,7 @@ org.jreleaser jreleaser-maven-plugin - 1.11.0 + 1.13.1 @@ -243,6 +243,9 @@ heylogs-cli heylogs-maven-plugin heylogs-bom + heylogs-ext-github + heylogs-ext-json + heylogs-ext-semver @@ -276,10 +279,10 @@ - 1.18.32 + 1.18.34 1.9.0 1.5.1 - 4.7.5 + 4.7.6 @@ -476,7 +479,7 @@ true - + true @@ -658,6 +661,7 @@ ossrh https://s01.oss.sonatype.org/ true + 10 @@ -683,6 +687,7 @@ ossrh https://s01.oss.sonatype.org/ true + 10