Skip to content

Commit

Permalink
Add basic rule check before scanning and extracting
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Apr 9, 2024
1 parent eeca811 commit 4c6499b
Show file tree
Hide file tree
Showing 17 changed files with 129 additions and 68 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Add json formatting [#118](https://github.com/nbbrd/heylogs/issues/118)
- Add versioning extension point [#235](https://github.com/nbbrd/heylogs/issues/235)
- Add forge extension point [#236](https://github.com/nbbrd/heylogs/issues/236)
- Add basic rule check before scanning and extracting [#243](https://github.com/nbbrd/heylogs/issues/243)

### Changed

Expand Down
30 changes: 16 additions & 14 deletions heylogs-api/src/main/java/internal/heylogs/StylishFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.util.Set;

import static java.lang.System.lineSeparator;
import static java.util.Arrays.asList;
import static java.util.Locale.ROOT;

// https://eslint.org/docs/latest/user-guide/formatters/#stylish
Expand Down Expand Up @@ -111,21 +110,24 @@ public void formatStatus(@NonNull Appendable appendable, @NonNull List<Scan> lis
}

private List<String> getStatusBody(Summary summary) {
if (summary.getReleaseCount() == 0) {
return asList(
"No release found",
summary.isHasUnreleasedSection() ? "Has an unreleased version" : "Has no unreleased version"
);
List<String> result = new ArrayList<>();
if (summary.isValid()) {
result.add("Valid changelog");
if (summary.getReleaseCount() == 0) {
result.add("No release found");
result.add(summary.isHasUnreleasedSection() ? "Has an unreleased version" : "Has no unreleased version");
} else {
result.add(String.format(ROOT, "Found %d releases", summary.getReleaseCount()));
result.add(String.format(ROOT, "Ranging from %s to %s", summary.getTimeRange().getFrom(), summary.getTimeRange().getTo()));
result.add(summary.getCompatibilities().isEmpty()
? "Not compatible with known versioning"
: "Compatible with " + String.join(", ", summary.getCompatibilities()));
result.add(summary.isHasUnreleasedSection() ? "Has an unreleased version" : "Has no unreleased version");
}
} else {
return asList(
String.format(ROOT, "Found %d releases", summary.getReleaseCount()),
String.format(ROOT, "Ranging from %s to %s", summary.getTimeRange().getFrom(), summary.getTimeRange().getTo()),
summary.getCompatibilities().isEmpty()
? "Not compatible with known versioning"
: "Compatible with " + String.join(", ", summary.getCompatibilities()),
summary.isHasUnreleasedSection() ? "Has an unreleased version" : "Has no unreleased version"
);
result.add("Invalid changelog");
}
return result;
}

@Override
Expand Down
9 changes: 8 additions & 1 deletion heylogs-api/src/main/java/nbbrd/heylogs/Extractor.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.vladsch.flexmark.ast.Reference;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import internal.heylogs.GuidingPrinciples;
import lombok.NonNull;

import java.time.LocalDate;
Expand All @@ -14,6 +15,8 @@
import java.util.List;
import java.util.regex.Pattern;

import static java.util.Arrays.asList;

@lombok.Value
@lombok.Builder(toBuilder = true)
public class Extractor {
Expand Down Expand Up @@ -54,7 +57,11 @@ public boolean contains(@NonNull Heading heading) {
return contains(Version.parse(heading));
}

public void extract(@NonNull Document root) {
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;

Expand Down
28 changes: 20 additions & 8 deletions heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import internal.heylogs.GuidingPrinciples;
import lombok.NonNull;
import nbbrd.design.MightBePromoted;
import nbbrd.design.StaticFactoryMethod;
import nbbrd.design.VisibleForTesting;
import nbbrd.heylogs.spi.*;

import java.io.IOException;
Expand All @@ -15,6 +17,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

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;
Expand Down Expand Up @@ -52,9 +55,7 @@ public class Heylogs {
List<Forge> forges;

public @NonNull List<Problem> validate(@NonNull Document doc) {
return concat(Stream.of(doc), Nodes.of(Node.class).descendants(doc))
.flatMap(node -> rules.stream().map(rule -> getProblemOrNull(node, rule)).filter(Objects::nonNull))
.collect(toList());
return getProblemStream(doc, rules).collect(toList());
}

public @NonNull List<Resource> getResources() {
Expand Down Expand Up @@ -108,11 +109,6 @@ private static Resource asResource(Forge forge) {
.build();
}

private static Problem getProblemOrNull(Node node, Rule rule) {
RuleIssue ruleIssueOrNull = rule.getRuleIssueOrNull(node);
return ruleIssueOrNull != null ? Problem.builder().rule(rule).issue(ruleIssueOrNull).build() : null;
}

public void formatProblems(@NonNull String formatId, @NonNull Appendable appendable, @NonNull List<Check> list) throws IOException {
getFormatById(formatId).formatProblems(appendable, list);
}
Expand All @@ -126,6 +122,10 @@ public void formatResources(@NonNull String formatId, @NonNull Appendable append
}

public @NonNull Summary scan(@NonNull Node document) {
boolean valid = !getProblemStream(document, asList(GuidingPrinciples.values())).findFirst().isPresent();

if (!valid) return Summary.builder().valid(false).build();

Map<Boolean, List<Version>> versionByType = Nodes.of(Heading.class)
.descendants(document)
.filter(Version::isVersionLevel)
Expand All @@ -137,6 +137,7 @@ public void formatResources(@NonNull String formatId, @NonNull Appendable append

return Summary
.builder()
.valid(true)
.releaseCount(versionByType.get(false).size())
.timeRange(versionByType.get(false).stream().map(Version::getDate).collect(toTimeRange()).orElse(TimeRange.ALL))
.compatibilities(compatibilities)
Expand All @@ -158,6 +159,17 @@ private Format getFormatById(String formatId) throws IOException {
.orElseThrow(() -> new IOException("Cannot find format '" + formatId + "'"));
}

@VisibleForTesting
static Stream<Problem> getProblemStream(Node root, List<Rule> 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;
}

public static final String FIRST_FORMAT_AVAILABLE = "";

@MightBePromoted
Expand Down
10 changes: 9 additions & 1 deletion heylogs-api/src/main/java/nbbrd/heylogs/Nodes.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
package nbbrd.heylogs;

import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import lombok.NonNull;
import lombok.Value;

import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static java.util.stream.Stream.concat;

@Value(staticConstructor = "of")
public class Nodes<T extends Node> {

@lombok.NonNull
Class<T> type;

public Stream<T> descendants(Node root) {
public @NonNull Stream<T> descendants(@NonNull Node root) {
return StreamSupport.stream(root.getDescendants().spliterator(), false)
.filter(type::isInstance)
.map(type::cast);
}

public static @NonNull Stream<Node> walk(@NonNull Node root) {
return concat(Stream.of(root), Nodes.of(Node.class).descendants(root));
}
}
2 changes: 2 additions & 0 deletions heylogs-api/src/main/java/nbbrd/heylogs/Summary.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
@lombok.Builder
public class Summary {

boolean valid;

@lombok.Builder.Default
int releaseCount = 0;

Expand Down
1 change: 1 addition & 0 deletions heylogs-api/src/test/java/_test/Sample.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static String asText(Heading heading) {
public static final Summary SUMMARY_1 = Summary.builder().build();
public static final Summary SUMMARY_2 = Summary
.builder()
.valid(true)
.compatibility("Strange Versioning")
.releaseCount(3)
.hasUnreleasedSection(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Index.atIndex;

public class GuidingPrinciplesTest {
public class
GuidingPrinciplesTest {

@Test
public void testIdPattern() {
Expand Down Expand Up @@ -61,9 +62,9 @@ public void testValidateEntryForEveryVersions() {
.map(GuidingPrinciples::validateAllH2ContainAVersion)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.contains(RuleIssue.builder().message("Invalid date format").line(2).column(1).build(), atIndex(0))
.contains(RuleIssue.builder().message("Missing date part").line(3).column(1).build(), atIndex(1))
.contains(RuleIssue.builder().message("Missing ref link").line(4).column(1).build(), atIndex(2))
.contains(RuleIssue.builder().message("Invalid date format").line(4).column(1).build(), atIndex(0))
.contains(RuleIssue.builder().message("Missing date part").line(5).column(1).build(), atIndex(1))
.contains(RuleIssue.builder().message("Missing ref link").line(6).column(1).build(), atIndex(2))
.hasSize(3);

}
Expand Down Expand Up @@ -115,6 +116,6 @@ public void testValidateLatestVersionFirst() {
.isEqualTo(RuleIssue.builder().message("Versions not sorted").line(3).column(1).build());

assertThat(validateLatestVersionFirst(using("/InvalidVersion.md")))
.isEqualTo(RuleIssue.builder().message("Versions not sorted").line(5).column(1).build());
.isEqualTo(RuleIssue.builder().message("Versions not sorted").line(7).column(1).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ public void testFormatStatus() {
assertThat(writing(appendable -> x.formatStatus(appendable, singletonList(SCAN1))))
.isEqualToNormalizingNewlines(
"source1\n"
+ " No release found \n"
+ " Has no unreleased version\n"
+ " Invalid changelog\n"
);

assertThat(writing(appendable -> x.formatStatus(appendable, singletonList(SCAN2))))
.isEqualToNormalizingNewlines(
"source2\n"
+ " Valid changelog \n"
+ " Found 3 releases \n"
+ " Ranging from 2010-01-01 to 2011-01-01\n"
+ " Compatible with Strange Versioning \n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ public void testValidateSemVer() {
.map(x::validateSemVer)
.filteredOn(Objects::nonNull)
.hasSize(1)
.contains(RuleIssue.builder().message("Invalid semver format: '.1.0'").line(2).column(1).build());
.contains(RuleIssue.builder().message("Invalid semver format: '.1.0'").line(4).column(1).build());
}
}
80 changes: 48 additions & 32 deletions heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@

import java.io.IOException;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

import static _test.Sample.using;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static nbbrd.heylogs.Heylogs.FIRST_FORMAT_AVAILABLE;
import static nbbrd.heylogs.spi.RuleSeverity.ERROR;
Expand Down Expand Up @@ -76,46 +74,63 @@ void testScan() {
Heylogs x = Heylogs.ofServiceLoader();

assertThat(x.scan(using("/Empty.md")))
.isEqualTo(new Summary(
0,
TimeRange.ALL,
Arrays.asList("Semantic Versioning"),
true
));
.isEqualTo(Summary
.builder()
.valid(false)
.releaseCount(0)
.timeRange(TimeRange.ALL)
.hasUnreleasedSection(false)
.build()
);

assertThat(x.scan(using("/Main.md")))
.isEqualTo(new Summary(
13,
TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15)),
Arrays.asList("Semantic Versioning"),
true
));
.isEqualTo(Summary
.builder()
.valid(true)
.releaseCount(13)
.timeRange(TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15)))
.compatibility("Semantic Versioning")
.hasUnreleasedSection(true)
.build()
);

assertThat(x.scan(using("/InvalidSemver.md")))
.isEqualTo(new Summary(
2,
TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
emptyList(),
true
));
.isEqualTo(Summary
.builder()
.valid(true)
.releaseCount(2)
.timeRange(TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)))
.hasUnreleasedSection(true)
.build()
);

assertThat(x.scan(using("/InvalidVersion.md")))
.isEqualTo(new Summary(
1,
TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
Arrays.asList("Semantic Versioning"),
true
));
.isEqualTo(Summary
.builder()
.valid(false)
.releaseCount(0)
.timeRange(TimeRange.ALL)
.hasUnreleasedSection(false)
.build()
);
}

@Test
public void testFormatStatus() throws IOException {
List<Scan> scans = singletonList(Scan.builder().source("file1").summary(new Summary(
1,
TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)),
Arrays.asList("Semantic Versioning"),
true
)).build());
List<Scan> scans = singletonList(
Scan
.builder()
.source("file1")
.summary(
Summary
.builder()
.valid(true)
.releaseCount(1)
.timeRange(TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)))
.compatibility("Semantic Versioning")
.hasUnreleasedSection(true)
.build())
.build());

assertThatIOException()
.isThrownBy(() -> Heylogs.builder().build().formatStatus(FIRST_FORMAT_AVAILABLE, new StringBuilder(), scans));
Expand All @@ -128,6 +143,7 @@ public void testFormatStatus() throws IOException {
assertThat(output.toString())
.isEqualToIgnoringNewLines(
"file1\n" +
" Valid changelog \n" +
" Found 1 releases \n" +
" Ranging from 2019-02-15 to 2019-02-15\n" +
" Compatible with Semantic Versioning \n" +
Expand Down
5 changes: 5 additions & 0 deletions heylogs-api/src/test/resources/InvalidSemver.md
Original file line number Diff line number Diff line change
@@ -1,2 +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
5 changes: 5 additions & 0 deletions heylogs-api/src/test/resources/InvalidVersion.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [1.1.0] - 2019-02-15
## [1.1.0] - 2019-02-
## [1.1.0]
## - 2019-02-15
## [unreleased ]

[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
Loading

0 comments on commit 4c6499b

Please sign in to comment.