Skip to content

Commit

Permalink
Polish 'Allow issues appear in multiple sections'
Browse files Browse the repository at this point in the history
Replace the `Issues` property with the concept of a "section group".

See gh-38
  • Loading branch information
philwebb committed Sep 24, 2020
1 parent 063f1d3 commit 6fe2e6e
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 143 deletions.
32 changes: 32 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ $ java -jar github-changelog-generator.jar <milestone> <path_to_generate_file>
NOTE: By default `<milestone>` refers to the milestone title.
If you want to use milestone ID, you should set the `changelog.milestone-reference` property to `id`.



=== Customizing Sections
By default the changelog will contain the following sections:

Expand Down Expand Up @@ -62,6 +64,36 @@ changelog:
labels: ["fix"]
----



==== Showing issues in multiple sections
Unless otherwise configured, issues will only appear in the first matching section.
For example, if you have an issue labeled with `enhancement` and `documentation` then it will only appear in the "New Features" section.

If you want an issue to appear in multiple sections, you can use the `group` property.
Groups allow you to create logical groupings of related sections.
An issue may only appear once in any given group.

For example, you might define the following:

[source,yaml]
----
changelog:
sections:
- title: "Highlights"
labels: ["noteworthy"]
group: "highlights"
- title: "Enhancements"
labels: ["new"]
- title: "Bugs"
labels: ["fix"]
----

This will create two distinct groups, "highlights" and "default" (which is used if no `group` property is specified).
An issue labeled with `new` and `noteworthy` will appear in both the "Highlights" and "Enhancements" section.



== License
This project is Open Source software released under the
https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,12 @@ public class ApplicationProperties {
*/
private final List<Section> sections;

/**
* Issue structure within the changelog.
*/
private final Issues issues;

public ApplicationProperties(Repository repository, @DefaultValue("title") MilestoneReference milestoneReference,
List<Section> sections, Issues issues) {
List<Section> sections) {
Assert.notNull(repository, "Repository must not be null");
this.repository = repository;
this.milestoneReference = milestoneReference;
this.sections = sections;
this.issues = issues;
}

public Repository getRepository() {
Expand All @@ -77,56 +71,43 @@ public List<Section> getSections() {
return this.sections;
}

public Issues getIssues() {
return this.issues;
}

/**
* Properties for a single changelog section.
*/
public static class Section {

/**
* The title of the section.
* Title of the section.
*/
private final String title;

/**
* The labels used to identify if an issue is for the section.
* Group used to bound the contained issues. Issues appear in the first section of
* each group.
*/
private final String group;

/**
* Labels used to identify if an issue is for the section.
*/
private final List<String> labels;

public Section(String title, String... labels) {
public Section(String title, @DefaultValue("default") String group, String... labels) {
this.title = title;
this.group = (group != null) ? group : "default";
this.labels = Arrays.asList(labels);
}

public String getTitle() {
return this.title;
}

public List<String> getLabels() {
return this.labels;
}

}

/**
* Properties relating to issue structure within the release notes.
*/
public static class Issues {

/**
* Whether an issue can appear in multiple sections.
*/
private final Boolean allowInMultipleSections;

public Issues(@DefaultValue("false") Boolean allowInMultipleSections) {
this.allowInMultipleSections = allowInMultipleSections;
public String getGroup() {
return this.group;
}

public Boolean getAllowInMultipleSections() {
return this.allowInMultipleSections;
public List<String> getLabels() {
return this.labels;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,26 @@ class ChangelogSection {

private final String title;

private final String group;

private final List<String> labels;

ChangelogSection(String title, String... labels) {
this(title, Arrays.asList(labels));
ChangelogSection(String title, String group, String... labels) {
this(title, group, Arrays.asList(labels));
}

ChangelogSection(String title, List<String> labels) {
ChangelogSection(String title, String group, List<String> labels) {
Assert.hasText(title, "Title must not be empty");
Assert.isTrue(!CollectionUtils.isEmpty(labels), "Labels must not be empty");
this.title = title;
this.group = group;
this.labels = labels;
}

String getGroup() {
return this.group;
}

boolean isMatchFor(Issue issue) {
for (String candidate : this.labels) {
for (Label label : issue.getLabels()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
Expand All @@ -47,16 +49,13 @@ class ChangelogSections {
}

private static void add(List<ChangelogSection> sections, String title, String... labels) {
sections.add(new ChangelogSection(title, labels));
sections.add(new ChangelogSection(title, null, labels));
}

private final List<ChangelogSection> sections;

private final Boolean allowInMultipleSections;

ChangelogSections(ApplicationProperties properties) {
this.sections = adapt(properties.getSections());
this.allowInMultipleSections = properties.getIssues().getAllowInMultipleSections();
}

private List<ChangelogSection> adapt(List<ApplicationProperties.Section> propertySections) {
Expand All @@ -67,41 +66,31 @@ private List<ChangelogSection> adapt(List<ApplicationProperties.Section> propert
}

private ChangelogSection adapt(ApplicationProperties.Section propertySection) {
return new ChangelogSection(propertySection.getTitle(), propertySection.getLabels());
return new ChangelogSection(propertySection.getTitle(), propertySection.getGroup(),
propertySection.getLabels());
}

Map<ChangelogSection, List<Issue>> collate(List<Issue> issues) {
SortedMap<ChangelogSection, List<Issue>> collated = new TreeMap<>(Comparator.comparing(this.sections::indexOf));
for (Issue issue : issues) {
List<ChangelogSection> sections = (this.allowInMultipleSections) ? getAllMatchingSections(issue)
: Collections.singletonList(getSection(issue));
List<ChangelogSection> sections = getSections(issue);
for (ChangelogSection section : sections) {
if (section != null) {
collated.computeIfAbsent(section, (key) -> new ArrayList<>());
collated.get(section).add(issue);
}
collated.computeIfAbsent(section, (key) -> new ArrayList<>());
collated.get(section).add(issue);
}
}
return collated;
}

private ChangelogSection getSection(Issue issue) {
for (ChangelogSection section : this.sections) {
if (section.isMatchFor(issue)) {
return section;
}
}
return null;
}

private List<ChangelogSection> getAllMatchingSections(Issue issue) {
List<ChangelogSection> sections = new ArrayList<>();
private List<ChangelogSection> getSections(Issue issue) {
List<ChangelogSection> result = new ArrayList<>();
Set<String> groupClaimes = new HashSet<>();
for (ChangelogSection section : this.sections) {
if (section.isMatchFor(issue)) {
sections.add(section);
if (section.isMatchFor(issue) && groupClaimes.add(section.getGroup())) {
result.add(section);
}
}
return sections;
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public PullRequest getPullRequest() {
return this.pullRequest;
}

@Override
public String toString() {
return this.title;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public void loadYaml() throws Exception {
assertThat(sections).hasSize(2);
assertThat(sections.get(0).getTitle()).isEqualTo(":star: New Features");
assertThat(sections.get(0).getLabels()).containsExactly("enhancement");
assertThat(sections.get(0).getGroup()).isEqualTo("default");
assertThat(sections.get(1).getTitle()).isEqualTo("Bugs");
assertThat(sections.get(1).getLabels()).containsExactly("bug");
Boolean allowInMultipleSections = properties.getIssues().getAllowInMultipleSections();
assertThat(allowInMultipleSections).isTrue();
assertThat(sections.get(1).getGroup()).isEqualTo("test");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.Collections;
import java.util.List;

import io.spring.githubchangeloggenerator.ApplicationProperties.Issues;
import io.spring.githubchangeloggenerator.github.payload.Issue;
import io.spring.githubchangeloggenerator.github.payload.Label;
import io.spring.githubchangeloggenerator.github.payload.PullRequest;
Expand Down Expand Up @@ -160,7 +159,7 @@ public void whenEscapedUserMentionIsInIssueTitleItIsNotEscapedAgain() throws IOE

private void setupGenerator(MilestoneReference id) {
this.generator = new ChangelogGenerator(this.service,
new ApplicationProperties(REPO, id, Collections.emptyList(), new Issues(false)));
new ApplicationProperties(REPO, id, Collections.emptyList()));
}

private User createUser(String contributor12, String s) {
Expand Down
Loading

0 comments on commit 6fe2e6e

Please sign in to comment.