Skip to content

Commit

Permalink
Fix scan of unreleased version
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Apr 9, 2024
1 parent 4c6499b commit da418f3
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Refactor API and SPI (breaking changes)
- Set CHANGELOG.md as default value for input file in command line [#237](https://github.com/nbbrd/heylogs/issues/237)

### Fixed

- Fix scan of unreleased version [#228](https://github.com/nbbrd/heylogs/issues/228)

## [0.7.2] - 2023-11-10

### Changed
Expand Down
67 changes: 67 additions & 0 deletions heylogs-api/src/main/java/internal/heylogs/ChangelogNodes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package internal.heylogs;

import com.vladsch.flexmark.ast.BulletList;
import com.vladsch.flexmark.ast.BulletListItem;
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.util.ast.Node;
import nbbrd.heylogs.Nodes;
import nbbrd.heylogs.TypeOfChange;
import nbbrd.heylogs.Version;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

import static java.util.stream.Collectors.toList;

public final class ChangelogNodes {

private ChangelogNodes() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

public static boolean isNotVersionHeading(Node node) {
return !(node instanceof Heading && Version.isVersionLevel((Heading) node));
}

public static boolean isTypeOfChangeNode(Node node) {
return node instanceof Heading && TypeOfChange.isTypeOfChangeLevel((Heading) node);
}

public static boolean isNotHeading(Node next) {
return !(next instanceof Heading);
}

public static boolean isUnreleasedHeading(Heading heading) {
try {
return Version.isVersionLevel(heading) && Version.parse(heading).isUnreleased();
} catch (IllegalArgumentException ex) {
return false;
}
}

public static Optional<Heading> getUnreleasedHeading(Node doc) {
return Nodes.of(Heading.class)
.descendants(doc)
.filter(ChangelogNodes::isUnreleasedHeading)
.findFirst();
}

public static Map<TypeOfChange, List<BulletListItem>> getBulletListsByTypeOfChange(Heading version) {
TreeMap<TypeOfChange, List<BulletListItem>> result = new TreeMap<>();
Nodes.next(version, ChangelogNodes::isNotVersionHeading)
.filter(ChangelogNodes::isTypeOfChangeNode)
.map(Heading.class::cast)
.forEach(typeOfChange -> result.put(TypeOfChange.parse(typeOfChange), collect(typeOfChange)));
return result;
}

private static List<BulletListItem> collect(Heading typeOfChange) {
return Nodes.next(typeOfChange, ChangelogNodes::isNotHeading)
.filter(BulletList.class::isInstance)
.map(BulletList.class::cast)
.flatMap(Nodes.of(BulletListItem.class)::descendants)
.collect(toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,14 @@ private List<String> getStatusBody(Summary summary) {
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");
}
result.add(summary.getUnreleasedChanges() > 0 ? ("Has " + summary.getUnreleasedChanges() + " unreleased changes") : "Has no unreleased changes");
} else {
result.add("Invalid changelog");
}
Expand Down
26 changes: 14 additions & 12 deletions heylogs-api/src/main/java/nbbrd/heylogs/Heylogs.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import internal.heylogs.ChangelogNodes;
import internal.heylogs.GuidingPrinciples;
import lombok.NonNull;
import nbbrd.design.MightBePromoted;
Expand All @@ -12,9 +13,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Arrays.asList;
Expand Down Expand Up @@ -122,26 +121,29 @@ 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 (getProblemStream(document, asList(GuidingPrinciples.values())).findFirst().isPresent())
return Summary.builder().valid(false).build();

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

Map<Boolean, List<Version>> versionByType = Nodes.of(Heading.class)
List<Version> releases = Nodes.of(Heading.class)
.descendants(document)
.filter(Version::isVersionLevel)
.map(illegalArgumentToNull(Version::parse))
.filter(Objects::nonNull)
.collect(Collectors.partitioningBy(Version::isUnreleased));
.filter(version -> !version.isUnreleased())
.collect(toList());

List<String> compatibilities = getCompatibilities(versionByType.get(false));
long unreleasedChanges = ChangelogNodes.getUnreleasedHeading(document)
.map(ChangelogNodes::getBulletListsByTypeOfChange)
.map(o -> o.values().stream().mapToLong(List::size).sum())
.orElse(0L);

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)
.hasUnreleasedSection(versionByType.containsKey(true))
.releaseCount(releases.size())
.timeRange(releases.stream().map(Version::getDate).collect(toTimeRange()).orElse(TimeRange.ALL))
.compatibilities(getCompatibilities(releases))
.unreleasedChanges((int) unreleasedChanges)
.build();
}

Expand Down
26 changes: 25 additions & 1 deletion heylogs-api/src/main/java/nbbrd/heylogs/Nodes.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
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.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

Expand All @@ -25,4 +28,25 @@ public class Nodes<T extends Node> {
public static @NonNull Stream<Node> walk(@NonNull Node root) {
return concat(Stream.of(root), Nodes.of(Node.class).descendants(root));
}

public static @NonNull Stream<Node> next(@NonNull Node seed, @NonNull Predicate<Node> hasNext) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(getNextNodeIterator(seed, hasNext), Spliterator.SIZED), false);
}

private static Iterator<Node> getNextNodeIterator(Node seed, Predicate<Node> hasNext) {
return new Iterator<Node>() {
Node current = seed;

@Override
public boolean hasNext() {
Node next = current.getNext();
return next != null && hasNext.test(next);
}

@Override
public Node next() {
return current = current.getNext();
}
};
}
}
2 changes: 1 addition & 1 deletion heylogs-api/src/main/java/nbbrd/heylogs/Summary.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ public class Summary {
List<String> compatibilities;

@lombok.Builder.Default
boolean hasUnreleasedSection = false;
int unreleasedChanges = 0;
}
2 changes: 1 addition & 1 deletion heylogs-api/src/test/java/_test/Sample.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static String asText(Heading heading) {
.valid(true)
.compatibility("Strange Versioning")
.releaseCount(3)
.hasUnreleasedSection(true)
.unreleasedChanges(3)
.timeRange(TimeRange.of(LocalDate.of(2010, 1, 1), LocalDate.of(2011, 1, 1)))
.build();

Expand Down
60 changes: 60 additions & 0 deletions heylogs-api/src/test/java/internal/heylogs/ChangelogNodesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package internal.heylogs;

import com.vladsch.flexmark.ast.BulletListItem;
import com.vladsch.flexmark.ast.Heading;
import nbbrd.heylogs.TypeOfChange;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

import static _test.Sample.asHeading;
import static _test.Sample.using;
import static internal.heylogs.ChangelogNodes.*;
import static org.assertj.core.api.Assertions.assertThat;

class ChangelogNodesTest {

@Test
public void testIsUnreleasedHeading() {
assertThat(isUnreleasedHeading(asHeading("## [Unreleased]"))).isTrue();
assertThat(isUnreleasedHeading(asHeading("## [unreleased]"))).isTrue();
assertThat(isUnreleasedHeading(asHeading("# [unreleased]"))).isFalse();
assertThat(isUnreleasedHeading(asHeading("## unreleased"))).isFalse();
assertThat(isUnreleasedHeading(asHeading("## [stuff]"))).isFalse();
}

@Test
public void testGetUnreleasedHeading() {
assertThat(getUnreleasedHeading(using("/Empty.md")))
.isEmpty();

assertThat(getUnreleasedHeading(using("/Main.md")))
.isNotEmpty()
.hasValueSatisfying(ChangelogNodes::isUnreleasedHeading);
}

@Test
public void testGetBulletListsByTypeOfChange() {
Heading unreleased = getUnreleasedHeading(using("/UnreleasedChanges.md")).orElseThrow(RuntimeException::new);

Map<TypeOfChange, List<BulletListItem>> x = getBulletListsByTypeOfChange(unreleased);

assertThat(x.keySet())
.hasSize(3)
.containsExactly(
TypeOfChange.ADDED,
TypeOfChange.CHANGED,
TypeOfChange.FIXED
);

assertThat(x.get(TypeOfChange.ADDED))
.hasSize(3)
.map(o -> o.getChildChars().toString().replaceAll("\\n", "").replaceAll("\\r", ""))
.containsExactly(
"Added Dutch translation",
"Added French translation",
"Added German translation"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void testFormatStatus() {
+ " Found 3 releases \n"
+ " Ranging from 2010-01-01 to 2011-01-01\n"
+ " Compatible with Strange Versioning \n"
+ " Has an unreleased version \n"
+ " Has 3 unreleased changes \n"
);
}

Expand Down
12 changes: 6 additions & 6 deletions heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void testScan() {
.valid(false)
.releaseCount(0)
.timeRange(TimeRange.ALL)
.hasUnreleasedSection(false)
.unreleasedChanges(0)
.build()
);

Expand All @@ -90,7 +90,7 @@ void testScan() {
.releaseCount(13)
.timeRange(TimeRange.of(LocalDate.of(2014, 5, 31), LocalDate.of(2019, 2, 15)))
.compatibility("Semantic Versioning")
.hasUnreleasedSection(true)
.unreleasedChanges(2)
.build()
);

Expand All @@ -100,7 +100,7 @@ void testScan() {
.valid(true)
.releaseCount(2)
.timeRange(TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)))
.hasUnreleasedSection(true)
.unreleasedChanges(0)
.build()
);

Expand All @@ -110,7 +110,7 @@ void testScan() {
.valid(false)
.releaseCount(0)
.timeRange(TimeRange.ALL)
.hasUnreleasedSection(false)
.unreleasedChanges(0)
.build()
);
}
Expand All @@ -128,7 +128,7 @@ public void testFormatStatus() throws IOException {
.releaseCount(1)
.timeRange(TimeRange.of(LocalDate.of(2019, 2, 15), LocalDate.of(2019, 2, 15)))
.compatibility("Semantic Versioning")
.hasUnreleasedSection(true)
.unreleasedChanges(3)
.build())
.build());

Expand All @@ -147,7 +147,7 @@ public void testFormatStatus() throws IOException {
" Found 1 releases \n" +
" Ranging from 2019-02-15 to 2019-02-15\n" +
" Compatible with Semantic Versioning \n" +
" Has an unreleased version \n"
" Has 3 unreleased changes \n"
);
}
}
25 changes: 25 additions & 0 deletions heylogs-api/src/test/java/nbbrd/heylogs/NodesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package nbbrd.heylogs;

import com.vladsch.flexmark.ast.Heading;
import internal.heylogs.ChangelogNodes;
import org.junit.jupiter.api.Test;

import static _test.Sample.using;
import static org.assertj.core.api.Assertions.assertThat;

class NodesTest {

@Test
public void testNext() {
Heading unreleased = ChangelogNodes.getUnreleasedHeading(using("/Main.md")).orElseThrow(RuntimeException::new);

assertThat(Nodes.next(unreleased, ignore -> false))
.isEmpty();

assertThat(Nodes.next(unreleased, ChangelogNodes::isNotVersionHeading))
.hasSize(4);

assertThat(Nodes.next(unreleased, ignore -> true))
.hasSize(75);
}
}
24 changes: 24 additions & 0 deletions heylogs-api/src/test/resources/UnreleasedChanges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Changelog

- Some bullet points

## [Unreleased]

- It's a trap !

### Added

- Added Dutch translation
- Added French translation
- Added German translation

### Fixed

- Fixed foldouts in Dutch translation

### Changed

## [1.1.0] - 2019-02-15

[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
2 changes: 1 addition & 1 deletion heylogs-api/src/test/resources/internal/heylogs/scan1.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"to": "+999999999-12-31"
},
"compatibilities": [],
"hasUnreleasedSection": false
"unreleasedChanges": 0
}
}
]
Loading

0 comments on commit da418f3

Please sign in to comment.