Skip to content

Commit 9326c7f

Browse files
committed
Rework breaking changes for new structure (#80907)
The structure of the breaking changes / migration guide was updated in #79162 to change the categories and split the breaking changes into different files. This PR amends the changelog generator code in line with this rework.
1 parent 7fd074d commit 9326c7f

File tree

11 files changed

+293
-121
lines changed

11 files changed

+293
-121
lines changed

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/BreakingChangesGenerator.java

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,118 @@
1111
import com.google.common.annotations.VisibleForTesting;
1212

1313
import org.elasticsearch.gradle.VersionProperties;
14+
import org.gradle.api.GradleException;
1415

1516
import java.io.File;
1617
import java.io.FileWriter;
1718
import java.io.IOException;
1819
import java.nio.file.Files;
1920
import java.util.HashMap;
2021
import java.util.List;
22+
import java.util.Locale;
2123
import java.util.Map;
2224
import java.util.Objects;
25+
import java.util.Set;
2326
import java.util.TreeMap;
27+
import java.util.TreeSet;
2428
import java.util.stream.Collectors;
2529

2630
import static java.util.Comparator.comparing;
2731
import static java.util.stream.Collectors.groupingBy;
32+
import static java.util.stream.Collectors.toCollection;
2833

2934
/**
30-
* Generates the page that lists the breaking changes and deprecations for a minor version release.
35+
* Generates the page that contains an index into the breaking changes and lists deprecations for a minor version release,
36+
* and the individual pages for each breaking area.
3137
*/
3238
public class BreakingChangesGenerator {
3339

34-
static void update(File templateFile, File outputFile, List<ChangelogEntry> entries) throws IOException {
35-
try (FileWriter output = new FileWriter(outputFile)) {
40+
// Needs to match `changelog-schema.json`
41+
private static final List<String> BREAKING_AREAS = List.of(
42+
"Cluster and node setting",
43+
"Command line tool",
44+
"Index setting",
45+
"JVM option",
46+
"Java API",
47+
"Logging",
48+
"Mapping",
49+
"Packaging",
50+
"Painless",
51+
"REST API",
52+
"System requirement",
53+
"Transform"
54+
);
55+
56+
static void update(
57+
File indexTemplateFile,
58+
File indexOutputFile,
59+
File outputDirectory,
60+
File areaTemplateFile,
61+
List<ChangelogEntry> entries
62+
) throws IOException {
63+
if (outputDirectory.exists()) {
64+
if (outputDirectory.isDirectory() == false) {
65+
throw new GradleException("Path [" + outputDirectory + "] exists but isn't a directory!");
66+
}
67+
} else {
68+
Files.createDirectory(outputDirectory.toPath());
69+
}
70+
71+
try (FileWriter output = new FileWriter(indexOutputFile)) {
3672
output.write(
37-
generateFile(QualifiedVersion.of(VersionProperties.getElasticsearch()), Files.readString(templateFile.toPath()), entries)
73+
generateIndexFile(
74+
QualifiedVersion.of(VersionProperties.getElasticsearch()),
75+
Files.readString(indexTemplateFile.toPath()),
76+
entries
77+
)
3878
);
3979
}
40-
}
4180

42-
@VisibleForTesting
43-
static String generateFile(QualifiedVersion version, String template, List<ChangelogEntry> entries) throws IOException {
81+
String areaTemplate = Files.readString(areaTemplateFile.toPath());
4482

45-
final Map<Boolean, Map<String, List<ChangelogEntry.Breaking>>> breakingChangesByNotabilityByArea = entries.stream()
46-
.map(ChangelogEntry::getBreaking)
47-
.filter(Objects::nonNull)
48-
.sorted(comparing(ChangelogEntry.Breaking::getTitle))
49-
.collect(
50-
groupingBy(
51-
ChangelogEntry.Breaking::isNotable,
52-
groupingBy(ChangelogEntry.Breaking::getArea, TreeMap::new, Collectors.toList())
53-
)
54-
);
83+
for (String breakingArea : BREAKING_AREAS) {
84+
final List<ChangelogEntry.Breaking> entriesForArea = entries.stream()
85+
.map(ChangelogEntry::getBreaking)
86+
.filter(entry -> entry != null && breakingArea.equals(entry.getArea()))
87+
.collect(Collectors.toList());
88+
89+
if (entriesForArea.isEmpty()) {
90+
continue;
91+
}
92+
93+
final String outputFilename = breakingArea.toLowerCase(Locale.ROOT).replaceFirst(" and", "").replaceAll(" ", "-")
94+
+ "-changes.asciidoc";
95+
96+
try (FileWriter output = new FileWriter(outputDirectory.toPath().resolve(outputFilename).toFile())) {
97+
output.write(
98+
generateBreakingAreaFile(
99+
QualifiedVersion.of(VersionProperties.getElasticsearch()),
100+
areaTemplate,
101+
breakingArea,
102+
entriesForArea
103+
)
104+
);
105+
}
106+
}
107+
}
55108

109+
@VisibleForTesting
110+
static String generateIndexFile(QualifiedVersion version, String template, List<ChangelogEntry> entries) throws IOException {
56111
final Map<String, List<ChangelogEntry.Deprecation>> deprecationsByArea = entries.stream()
57112
.map(ChangelogEntry::getDeprecation)
58113
.filter(Objects::nonNull)
59114
.sorted(comparing(ChangelogEntry.Deprecation::getTitle))
60115
.collect(groupingBy(ChangelogEntry.Deprecation::getArea, TreeMap::new, Collectors.toList()));
61116

117+
final List<String> breakingIncludeList = entries.stream()
118+
.filter(each -> each.getBreaking() != null)
119+
.map(each -> each.getBreaking().getArea().toLowerCase(Locale.ROOT).replaceFirst(" and", "").replaceAll(" ", "-"))
120+
.distinct()
121+
.sorted()
122+
.toList();
123+
62124
final Map<String, Object> bindings = new HashMap<>();
63-
bindings.put("breakingChangesByNotabilityByArea", breakingChangesByNotabilityByArea);
125+
bindings.put("breakingIncludeList", breakingIncludeList);
64126
bindings.put("deprecationsByArea", deprecationsByArea);
65127
bindings.put("isElasticsearchSnapshot", version.isSnapshot());
66128
bindings.put("majorDotMinor", version.getMajor() + "." + version.getMinor());
@@ -70,4 +132,28 @@ static String generateFile(QualifiedVersion version, String template, List<Chang
70132

71133
return TemplateUtils.render(template, bindings);
72134
}
135+
136+
@VisibleForTesting
137+
static String generateBreakingAreaFile(
138+
QualifiedVersion version,
139+
String template,
140+
String breakingArea,
141+
List<ChangelogEntry.Breaking> entriesForArea
142+
) throws IOException {
143+
final Map<Boolean, Set<ChangelogEntry.Breaking>> breakingEntriesByNotability = entriesForArea.stream()
144+
.collect(
145+
groupingBy(
146+
ChangelogEntry.Breaking::isNotable,
147+
toCollection(() -> new TreeSet<>(comparing(ChangelogEntry.Breaking::getTitle)))
148+
)
149+
);
150+
151+
final Map<String, Object> bindings = new HashMap<>();
152+
bindings.put("breakingArea", breakingArea);
153+
bindings.put("breakingEntriesByNotability", breakingEntriesByNotability);
154+
bindings.put("breakingAreaAnchor", breakingArea.toLowerCase(Locale.ROOT).replaceFirst(" and", "").replaceAll(" ", "_"));
155+
bindings.put("majorMinor", String.valueOf(version.getMajor()) + version.getMinor());
156+
157+
return TemplateUtils.render(template, bindings);
158+
}
73159
}

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ChangelogEntry.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ public static class Breaking {
215215
private String details;
216216
private String impact;
217217
private boolean notable;
218+
private boolean essSettingChange;
218219

219220
public String getArea() {
220221
return area;
@@ -260,6 +261,14 @@ public String getAnchor() {
260261
return generatedAnchor(this.title);
261262
}
262263

264+
public boolean isEssSettingChange() {
265+
return essSettingChange;
266+
}
267+
268+
public void setEssSettingChange(boolean essSettingChange) {
269+
this.essSettingChange = essSettingChange;
270+
}
271+
263272
@Override
264273
public boolean equals(Object o) {
265274
if (this == o) {
@@ -273,23 +282,25 @@ public boolean equals(Object o) {
273282
&& Objects.equals(area, breaking.area)
274283
&& Objects.equals(title, breaking.title)
275284
&& Objects.equals(details, breaking.details)
276-
&& Objects.equals(impact, breaking.impact);
285+
&& Objects.equals(impact, breaking.impact)
286+
&& Objects.equals(essSettingChange, breaking.essSettingChange);
277287
}
278288

279289
@Override
280290
public int hashCode() {
281-
return Objects.hash(area, title, details, impact, notable);
291+
return Objects.hash(area, title, details, impact, notable, essSettingChange);
282292
}
283293

284294
@Override
285295
public String toString() {
286296
return String.format(
287-
"Breaking{area='%s', title='%s', details='%s', impact='%s', isNotable=%s}",
297+
"Breaking{area='%s', title='%s', details='%s', impact='%s', notable=%s, essSettingChange=%s}",
288298
area,
289299
title,
290300
details,
291301
impact,
292-
notable
302+
notable,
303+
essSettingChange
293304
);
294305
}
295306
}
@@ -351,7 +362,7 @@ public String toString() {
351362
}
352363

353364
private static String generatedAnchor(String input) {
354-
final List<String> excludes = List.of("the", "is", "a");
365+
final List<String> excludes = List.of("the", "is", "a", "and");
355366

356367
final String[] words = input.toLowerCase(Locale.ROOT)
357368
.replaceAll("[^\\w]+", "_")

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/GenerateReleaseNotesTask.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import org.gradle.api.DefaultTask;
1515
import org.gradle.api.GradleException;
1616
import org.gradle.api.file.ConfigurableFileCollection;
17+
import org.gradle.api.file.Directory;
18+
import org.gradle.api.file.DirectoryProperty;
1719
import org.gradle.api.file.FileCollection;
1820
import org.gradle.api.file.RegularFile;
1921
import org.gradle.api.file.RegularFileProperty;
@@ -22,6 +24,7 @@
2224
import org.gradle.api.model.ObjectFactory;
2325
import org.gradle.api.tasks.InputFile;
2426
import org.gradle.api.tasks.InputFiles;
27+
import org.gradle.api.tasks.OutputDirectory;
2528
import org.gradle.api.tasks.OutputFile;
2629
import org.gradle.api.tasks.TaskAction;
2730
import org.gradle.process.ExecOperations;
@@ -55,11 +58,13 @@ public class GenerateReleaseNotesTask extends DefaultTask {
5558
private final RegularFileProperty releaseNotesTemplate;
5659
private final RegularFileProperty releaseHighlightsTemplate;
5760
private final RegularFileProperty breakingChangesTemplate;
61+
private final RegularFileProperty breakingChangesAreaTemplate;
5862

5963
private final RegularFileProperty releaseNotesIndexFile;
6064
private final RegularFileProperty releaseNotesFile;
6165
private final RegularFileProperty releaseHighlightsFile;
62-
private final RegularFileProperty breakingChangesFile;
66+
private final RegularFileProperty breakingChangesIndexFile;
67+
private final DirectoryProperty breakingChangesDirectory;
6368

6469
private final GitWrapper gitWrapper;
6570

@@ -71,11 +76,13 @@ public GenerateReleaseNotesTask(ObjectFactory objectFactory, ExecOperations exec
7176
releaseNotesTemplate = objectFactory.fileProperty();
7277
releaseHighlightsTemplate = objectFactory.fileProperty();
7378
breakingChangesTemplate = objectFactory.fileProperty();
79+
breakingChangesAreaTemplate = objectFactory.fileProperty();
7480

7581
releaseNotesIndexFile = objectFactory.fileProperty();
7682
releaseNotesFile = objectFactory.fileProperty();
7783
releaseHighlightsFile = objectFactory.fileProperty();
78-
breakingChangesFile = objectFactory.fileProperty();
84+
breakingChangesIndexFile = objectFactory.fileProperty();
85+
breakingChangesDirectory = objectFactory.directoryProperty();
7986

8087
gitWrapper = new GitWrapper(execOperations);
8188
}
@@ -129,7 +136,9 @@ public void executeTask() throws IOException {
129136
LOGGER.info("Generating breaking changes / deprecations notes...");
130137
BreakingChangesGenerator.update(
131138
this.breakingChangesTemplate.get().getAsFile(),
132-
this.breakingChangesFile.get().getAsFile(),
139+
this.breakingChangesIndexFile.get().getAsFile(),
140+
this.breakingChangesDirectory.get().getAsFile(),
141+
this.breakingChangesAreaTemplate.get().getAsFile(),
133142
entries
134143
);
135144
}
@@ -339,11 +348,29 @@ public void setReleaseHighlightsFile(RegularFile file) {
339348
}
340349

341350
@OutputFile
342-
public RegularFileProperty getBreakingChangesFile() {
343-
return breakingChangesFile;
351+
public RegularFileProperty getBreakingChangesIndexFile() {
352+
return breakingChangesIndexFile;
344353
}
345354

346-
public void setBreakingChangesFile(RegularFile file) {
347-
this.breakingChangesFile.set(file);
355+
public void setBreakingChangesIndexFile(RegularFile file) {
356+
this.breakingChangesIndexFile.set(file);
357+
}
358+
359+
public void setBreakingChangesDirectory(Directory breakingChangesDirectory) {
360+
this.breakingChangesDirectory.set(breakingChangesDirectory);
361+
}
362+
363+
@OutputDirectory
364+
public DirectoryProperty getBreakingChangesDirectory() {
365+
return breakingChangesDirectory;
366+
}
367+
368+
@InputFile
369+
public RegularFileProperty getBreakingChangesAreaTemplate() {
370+
return breakingChangesAreaTemplate;
371+
}
372+
373+
public void setBreakingChangesAreaTemplate(RegularFile file) {
374+
this.breakingChangesAreaTemplate.set(file);
348375
}
349376
}

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,15 @@ public void apply(Project project) {
8484
task.setReleaseHighlightsFile(projectDirectory.file("docs/reference/release-notes/highlights.asciidoc"));
8585

8686
task.setBreakingChangesTemplate(projectDirectory.file(RESOURCES + "templates/breaking-changes.asciidoc"));
87-
task.setBreakingChangesFile(
87+
task.setBreakingChangesIndexFile(
8888
projectDirectory.file(
8989
String.format("docs/reference/migration/migrate_%d_%d.asciidoc", version.getMajor(), version.getMinor())
9090
)
9191
);
92+
task.setBreakingChangesAreaTemplate(projectDirectory.file(RESOURCES + "templates/breaking-changes-area.asciidoc"));
93+
task.setBreakingChangesDirectory(
94+
projectDirectory.dir(String.format("docs/reference/migration/migrate_%d_%d", version.getMajor(), version.getMinor()))
95+
);
9296

9397
task.dependsOn(validateChangelogsTask);
9498
});

build-tools-internal/src/main/resources/changelog-schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@
157157
},
158158
"notable": {
159159
"type": "boolean"
160+
},
161+
"ess_setting_change": {
162+
"type": "boolean"
160163
}
161164
},
162165
"required": [
@@ -179,6 +182,9 @@
179182
"body": {
180183
"type": "string",
181184
"minLength": 1
185+
},
186+
"ess_setting_change": {
187+
"type": "boolean"
182188
}
183189
},
184190
"required": [
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[discrete]
2+
[[breaking_${majorMinor}_${breakingAreaAnchor}]]
3+
==== ${breakingArea}
4+
5+
//NOTE: The notable-breaking-changes tagged regions are re-used in the
6+
//Installation and Upgrade Guide
7+
8+
TIP: {ess-setting-change}
9+
10+
<%
11+
[true, false].each { isNotable ->
12+
def breakingChanges = breakingEntriesByNotability.getOrDefault(isNotable, [])
13+
14+
if (breakingChanges.isEmpty() == false) {
15+
if (isNotable) {
16+
/* No newline here, one will be added below */
17+
print "// tag::notable-breaking-changes[]"
18+
}
19+
20+
for (breaking in breakingChanges) { %>
21+
[[${ breaking.anchor }]]
22+
. ${breaking.title}${ breaking.essSettingChange ? ' {ess-icon}' : '' }
23+
[%collapsible]
24+
====
25+
*Details* +
26+
${breaking.details.trim()}
27+
28+
*Impact* +
29+
${breaking.impact.trim()}
30+
====
31+
<%
32+
}
33+
34+
if (isNotable) {
35+
print "// end::notable-breaking-changes[]\n"
36+
}
37+
}
38+
}
39+
%>

0 commit comments

Comments
 (0)