Skip to content

Commit 303359d

Browse files
committed
Merge branch 'main' into load_many
2 parents c048c17 + c2ad34b commit 303359d

File tree

204 files changed

+7763
-3221
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

204 files changed

+7763
-3221
lines changed

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

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.gradle.internal.release;
11+
12+
import com.fasterxml.jackson.annotation.JsonInclude;
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
15+
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
16+
17+
import org.gradle.api.DefaultTask;
18+
import org.gradle.api.file.ConfigurableFileCollection;
19+
import org.gradle.api.file.Directory;
20+
import org.gradle.api.file.DirectoryProperty;
21+
import org.gradle.api.file.FileCollection;
22+
import org.gradle.api.file.RegularFile;
23+
import org.gradle.api.file.RegularFileProperty;
24+
import org.gradle.api.logging.Logger;
25+
import org.gradle.api.logging.Logging;
26+
import org.gradle.api.model.ObjectFactory;
27+
import org.gradle.api.tasks.InputDirectory;
28+
import org.gradle.api.tasks.InputFiles;
29+
import org.gradle.api.tasks.OutputFile;
30+
import org.gradle.api.tasks.TaskAction;
31+
import org.gradle.api.tasks.options.Option;
32+
import org.gradle.process.ExecOperations;
33+
34+
import java.io.File;
35+
import java.io.IOException;
36+
import java.io.StringReader;
37+
import java.time.Instant;
38+
import java.util.Comparator;
39+
import java.util.List;
40+
import java.util.Properties;
41+
import java.util.Set;
42+
import java.util.stream.Collectors;
43+
44+
import javax.annotation.Nullable;
45+
import javax.inject.Inject;
46+
47+
import static java.util.stream.Collectors.toList;
48+
49+
public class BundleChangelogsTask extends DefaultTask {
50+
private static final Logger LOGGER = Logging.getLogger(BundleChangelogsTask.class);
51+
52+
private final ConfigurableFileCollection changelogs;
53+
54+
private final RegularFileProperty bundleFile;
55+
private final DirectoryProperty changelogDirectory;
56+
private final DirectoryProperty changelogBundlesDirectory;
57+
58+
private final GitWrapper gitWrapper;
59+
60+
@Nullable
61+
private String branch;
62+
@Nullable
63+
private String bcRef;
64+
65+
private boolean finalize;
66+
67+
@Option(option = "branch", description = "Branch (or other ref) to use for generating the changelog bundle.")
68+
public void setBranch(String branch) {
69+
this.branch = branch;
70+
}
71+
72+
@Option(
73+
option = "bc-ref",
74+
description = "A source ref, typically the sha of a BC, that should be used to source PRs for changelog entries. "
75+
+ "The actual content of the changelogs will come from the 'branch' ref. "
76+
+ "You should generally always use bc-ref."
77+
)
78+
public void setBcRef(String ref) {
79+
this.bcRef = ref;
80+
}
81+
82+
@Option(option = "finalize", description = "Specify that the bundle is finalized, i.e. that the version has been released.")
83+
public void setFinalize(boolean finalize) {
84+
this.finalize = finalize;
85+
}
86+
87+
private static final ObjectMapper yamlMapper = new ObjectMapper(
88+
new YAMLFactory().enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
89+
.disable(YAMLGenerator.Feature.SPLIT_LINES)
90+
.enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR)
91+
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
92+
.enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
93+
).setSerializationInclusion(JsonInclude.Include.NON_NULL);
94+
95+
@Inject
96+
public BundleChangelogsTask(ObjectFactory objectFactory, ExecOperations execOperations) {
97+
changelogs = objectFactory.fileCollection();
98+
99+
bundleFile = objectFactory.fileProperty();
100+
changelogDirectory = objectFactory.directoryProperty();
101+
changelogBundlesDirectory = objectFactory.directoryProperty();
102+
103+
gitWrapper = new GitWrapper(execOperations);
104+
}
105+
106+
/*
107+
Given a branch, and possibly a build candidate commit sha
108+
Check out the changelog yaml files from the branch/BC sha
109+
Then, bundle them all up into one file and write it to disk, along with a timestamp and whether the release is considered released
110+
111+
When using a branch without a BC sha:
112+
- Check out the changelog yaml files from the HEAD of the branch
113+
114+
When using a BC sha:
115+
- Check out the changelog yaml files from the BC commit
116+
- Update those files with any updates from the HEAD of the branch (in case the changelogs get modified later)
117+
- Check for any changelog yaml files that were added AFTER the BC,
118+
but whose PR was merged before the BC (in case someone adds a forgotten changelog after the fact)
119+
*/
120+
@TaskAction
121+
public void executeTask() throws IOException {
122+
if (branch == null) {
123+
throw new IllegalArgumentException("'branch' not specified.");
124+
}
125+
126+
final String upstreamRemote = gitWrapper.getUpstream();
127+
Set<String> entriesFromBc = Set.of();
128+
129+
var didCheckoutChangelogs = false;
130+
try {
131+
var usingBcRef = bcRef != null && bcRef.isEmpty() == false;
132+
if (usingBcRef) {
133+
// Check out all the changelogs that existed at the time of the BC
134+
checkoutChangelogs(gitWrapper, upstreamRemote, bcRef);
135+
entriesFromBc = changelogDirectory.getAsFileTree().getFiles().stream().map(File::getName).collect(Collectors.toSet());
136+
137+
// Then add/update changelogs from the HEAD of the branch
138+
// We do an "add" here, rather than checking out the entire directory, in case changelogs have been removed for some reason
139+
addChangelogsFromRef(gitWrapper, upstreamRemote, branch);
140+
} else {
141+
checkoutChangelogs(gitWrapper, upstreamRemote, branch);
142+
}
143+
144+
didCheckoutChangelogs = true;
145+
Properties props = new Properties();
146+
props.load(
147+
new StringReader(
148+
gitWrapper.runCommand("git", "show", upstreamRemote + "/" + branch + ":build-tools-internal/version.properties")
149+
)
150+
);
151+
String version = props.getProperty("elasticsearch");
152+
153+
LOGGER.info("Finding changelog files for " + version + "...");
154+
155+
Set<String> finalEntriesFromBc = entriesFromBc;
156+
List<ChangelogEntry> entries = changelogDirectory.getAsFileTree().getFiles().stream().filter(f -> {
157+
// When not using a bc ref, we just take everything from the branch/sha passed in
158+
if (usingBcRef == false) {
159+
return true;
160+
}
161+
162+
// If the changelog was present in the BC sha, always use it
163+
if (finalEntriesFromBc.contains(f.getName())) {
164+
return true;
165+
}
166+
167+
// Otherwise, let's check to see if a reference to the PR exists in the commit log for the sha
168+
// This specifically covers the case of a PR being merged into the BC with a missing changelog file, and the file added
169+
// later.
170+
var prNumber = f.getName().replace(".yaml", "");
171+
var output = gitWrapper.runCommand("git", "log", bcRef, "--grep", "(#" + prNumber + ")");
172+
return output.trim().isEmpty() == false;
173+
}).map(ChangelogEntry::parse).sorted(Comparator.comparing(ChangelogEntry::getPr)).collect(toList());
174+
175+
ChangelogBundle bundle = new ChangelogBundle(version, finalize, Instant.now().toString(), entries);
176+
177+
yamlMapper.writeValue(new File("docs/release-notes/changelog-bundles/" + version + ".yml"), bundle);
178+
} finally {
179+
if (didCheckoutChangelogs) {
180+
gitWrapper.runCommand("git", "restore", "-s@", "-SW", "--", changelogDirectory.get().toString());
181+
}
182+
}
183+
}
184+
185+
private void checkoutChangelogs(GitWrapper gitWrapper, String upstream, String ref) {
186+
gitWrapper.updateRemote(upstream);
187+
188+
// If the changelog directory contains modified/new files, we should error out instead of wiping them out silently
189+
var output = gitWrapper.runCommand("git", "status", "--porcelain", changelogDirectory.get().toString()).trim();
190+
if (output.isEmpty() == false) {
191+
throw new IllegalStateException(
192+
"Changelog directory contains changes that will be wiped out by this task:\n" + changelogDirectory.get() + "\n" + output
193+
);
194+
}
195+
196+
gitWrapper.runCommand("rm", "-rf", changelogDirectory.get().toString());
197+
var refSpec = upstream + "/" + ref;
198+
if (ref.contains("upstream/")) {
199+
refSpec = ref.replace("upstream/", upstream + "/");
200+
} else if (ref.matches("^[0-9a-f]+$")) {
201+
refSpec = ref;
202+
}
203+
gitWrapper.runCommand("git", "checkout", refSpec, "--", changelogDirectory.get().toString());
204+
}
205+
206+
private void addChangelogsFromRef(GitWrapper gitWrapper, String upstream, String ref) {
207+
var refSpec = upstream + "/" + ref;
208+
if (ref.contains("upstream/")) {
209+
refSpec = ref.replace("upstream/", upstream + "/");
210+
} else if (ref.matches("^[0-9a-f]+$")) {
211+
refSpec = ref;
212+
}
213+
214+
gitWrapper.runCommand("git", "checkout", refSpec, "--", changelogDirectory.get() + "/*.yaml");
215+
}
216+
217+
@InputDirectory
218+
public DirectoryProperty getChangelogDirectory() {
219+
return changelogDirectory;
220+
}
221+
222+
public void setChangelogDirectory(Directory dir) {
223+
this.changelogDirectory.set(dir);
224+
}
225+
226+
@InputDirectory
227+
public DirectoryProperty getChangelogBundlesDirectory() {
228+
return changelogBundlesDirectory;
229+
}
230+
231+
public void setChangelogBundlesDirectory(Directory dir) {
232+
this.changelogBundlesDirectory.set(dir);
233+
}
234+
235+
@InputFiles
236+
public FileCollection getChangelogs() {
237+
return changelogs;
238+
}
239+
240+
public void setChangelogs(FileCollection files) {
241+
this.changelogs.setFrom(files);
242+
}
243+
244+
@OutputFile
245+
public RegularFileProperty getBundleFile() {
246+
return bundleFile;
247+
}
248+
249+
public void setBundleFile(RegularFile file) {
250+
this.bundleFile.set(file);
251+
}
252+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.gradle.internal.release;
11+
12+
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
14+
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
15+
16+
import org.gradle.api.logging.Logger;
17+
import org.gradle.api.logging.Logging;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.io.UncheckedIOException;
22+
import java.util.List;
23+
24+
public record ChangelogBundle(String version, boolean released, String generated, List<ChangelogEntry> changelogs) {
25+
26+
private static final Logger LOGGER = Logging.getLogger(GenerateReleaseNotesTask.class);
27+
private static final ObjectMapper yamlMapper = new ObjectMapper(
28+
new YAMLFactory().enable(YAMLGenerator.Feature.MINIMIZE_QUOTES).disable(YAMLGenerator.Feature.SPLIT_LINES)
29+
);
30+
31+
public ChangelogBundle(String version, String generated, List<ChangelogEntry> changelogs) {
32+
this(version, false, generated, changelogs);
33+
}
34+
35+
public static ChangelogBundle parse(File file) {
36+
try {
37+
return yamlMapper.readValue(file, ChangelogBundle.class);
38+
} catch (IOException e) {
39+
LOGGER.error("Failed to parse changelog bundle from " + file.getAbsolutePath(), e);
40+
throw new UncheckedIOException(e);
41+
}
42+
}
43+
44+
public static ChangelogBundle copy(ChangelogBundle bundle) {
45+
List<ChangelogEntry> changelogs = bundle.changelogs().stream().toList();
46+
return new ChangelogBundle(bundle.version(), bundle.released(), bundle.generated(), changelogs);
47+
}
48+
49+
public ChangelogBundle withChangelogs(List<ChangelogEntry> changelogs) {
50+
return new ChangelogBundle(version, released, generated, changelogs);
51+
}
52+
}

0 commit comments

Comments
 (0)