mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 09:28:55 -04:00
[release-notes] Update automation to use new markdown format (#124161)
This commit is contained in:
parent
f275b71766
commit
c2ad34b97f
33 changed files with 3984 additions and 1489 deletions
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import org.elasticsearch.gradle.VersionProperties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* Generates the page that contains breaking changes deprecations for a minor release series.
|
||||
*/
|
||||
public class BreakingChangesGenerator {
|
||||
|
||||
static void update(File migrationTemplateFile, File migrationOutputFile, List<ChangelogEntry> entries) throws IOException {
|
||||
try (FileWriter output = new FileWriter(migrationOutputFile)) {
|
||||
output.write(
|
||||
generateMigrationFile(
|
||||
QualifiedVersion.of(VersionProperties.getElasticsearch()),
|
||||
Files.readString(migrationTemplateFile.toPath()),
|
||||
entries
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String generateMigrationFile(QualifiedVersion version, String template, List<ChangelogEntry> entries) throws IOException {
|
||||
final Map<Boolean, Map<String, List<ChangelogEntry.Deprecation>>> deprecationsByNotabilityByArea = entries.stream()
|
||||
.map(ChangelogEntry::getDeprecation)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(comparing(ChangelogEntry.Deprecation::getTitle))
|
||||
.collect(
|
||||
groupingBy(
|
||||
ChangelogEntry.Deprecation::isNotable,
|
||||
TreeMap::new,
|
||||
groupingBy(ChangelogEntry.Deprecation::getArea, TreeMap::new, toList())
|
||||
)
|
||||
);
|
||||
|
||||
final Map<Boolean, Map<String, List<ChangelogEntry.Breaking>>> breakingByNotabilityByArea = entries.stream()
|
||||
.map(ChangelogEntry::getBreaking)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(comparing(ChangelogEntry.Breaking::getTitle))
|
||||
.collect(
|
||||
groupingBy(
|
||||
ChangelogEntry.Breaking::isNotable,
|
||||
TreeMap::new,
|
||||
groupingBy(ChangelogEntry.Breaking::getArea, TreeMap::new, toList())
|
||||
)
|
||||
);
|
||||
|
||||
final Map<String, Object> bindings = new HashMap<>();
|
||||
bindings.put("breakingByNotabilityByArea", breakingByNotabilityByArea);
|
||||
bindings.put("deprecationsByNotabilityByArea", deprecationsByNotabilityByArea);
|
||||
bindings.put("isElasticsearchSnapshot", version.isSnapshot());
|
||||
bindings.put("majorDotMinor", version.major() + "." + version.minor());
|
||||
bindings.put("majorDotMinorDotRevision", version.major() + "." + version.minor() + "." + version.revision());
|
||||
bindings.put("majorMinor", String.valueOf(version.major()) + version.minor());
|
||||
bindings.put("nextMajor", (version.major() + 1) + ".0");
|
||||
bindings.put("version", version);
|
||||
|
||||
return TemplateUtils.render(template, bindings);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.Directory;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFile;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
import org.gradle.process.ExecOperations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class BundleChangelogsTask extends DefaultTask {
|
||||
private static final Logger LOGGER = Logging.getLogger(BundleChangelogsTask.class);
|
||||
|
||||
private final ConfigurableFileCollection changelogs;
|
||||
|
||||
private final RegularFileProperty bundleFile;
|
||||
private final DirectoryProperty changelogDirectory;
|
||||
private final DirectoryProperty changelogBundlesDirectory;
|
||||
|
||||
private final GitWrapper gitWrapper;
|
||||
|
||||
@Nullable
|
||||
private String branch;
|
||||
@Nullable
|
||||
private String bcRef;
|
||||
|
||||
private boolean finalize;
|
||||
|
||||
@Option(option = "branch", description = "Branch (or other ref) to use for generating the changelog bundle.")
|
||||
public void setBranch(String branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
@Option(
|
||||
option = "bc-ref",
|
||||
description = "A source ref, typically the sha of a BC, that should be used to source PRs for changelog entries. "
|
||||
+ "The actual content of the changelogs will come from the 'branch' ref. "
|
||||
+ "You should generally always use bc-ref."
|
||||
)
|
||||
public void setBcRef(String ref) {
|
||||
this.bcRef = ref;
|
||||
}
|
||||
|
||||
@Option(option = "finalize", description = "Specify that the bundle is finalized, i.e. that the version has been released.")
|
||||
public void setFinalize(boolean finalize) {
|
||||
this.finalize = finalize;
|
||||
}
|
||||
|
||||
private static final ObjectMapper yamlMapper = new ObjectMapper(
|
||||
new YAMLFactory().enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
|
||||
.disable(YAMLGenerator.Feature.SPLIT_LINES)
|
||||
.enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR)
|
||||
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
|
||||
.enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
|
||||
).setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
@Inject
|
||||
public BundleChangelogsTask(ObjectFactory objectFactory, ExecOperations execOperations) {
|
||||
changelogs = objectFactory.fileCollection();
|
||||
|
||||
bundleFile = objectFactory.fileProperty();
|
||||
changelogDirectory = objectFactory.directoryProperty();
|
||||
changelogBundlesDirectory = objectFactory.directoryProperty();
|
||||
|
||||
gitWrapper = new GitWrapper(execOperations);
|
||||
}
|
||||
|
||||
/*
|
||||
Given a branch, and possibly a build candidate commit sha
|
||||
Check out the changelog yaml files from the branch/BC sha
|
||||
Then, bundle them all up into one file and write it to disk, along with a timestamp and whether the release is considered released
|
||||
|
||||
When using a branch without a BC sha:
|
||||
- Check out the changelog yaml files from the HEAD of the branch
|
||||
|
||||
When using a BC sha:
|
||||
- Check out the changelog yaml files from the BC commit
|
||||
- Update those files with any updates from the HEAD of the branch (in case the changelogs get modified later)
|
||||
- Check for any changelog yaml files that were added AFTER the BC,
|
||||
but whose PR was merged before the BC (in case someone adds a forgotten changelog after the fact)
|
||||
*/
|
||||
@TaskAction
|
||||
public void executeTask() throws IOException {
|
||||
if (branch == null) {
|
||||
throw new IllegalArgumentException("'branch' not specified.");
|
||||
}
|
||||
|
||||
final String upstreamRemote = gitWrapper.getUpstream();
|
||||
Set<String> entriesFromBc = Set.of();
|
||||
|
||||
var didCheckoutChangelogs = false;
|
||||
try {
|
||||
var usingBcRef = bcRef != null && bcRef.isEmpty() == false;
|
||||
if (usingBcRef) {
|
||||
// Check out all the changelogs that existed at the time of the BC
|
||||
checkoutChangelogs(gitWrapper, upstreamRemote, bcRef);
|
||||
entriesFromBc = changelogDirectory.getAsFileTree().getFiles().stream().map(File::getName).collect(Collectors.toSet());
|
||||
|
||||
// Then add/update changelogs from the HEAD of the branch
|
||||
// We do an "add" here, rather than checking out the entire directory, in case changelogs have been removed for some reason
|
||||
addChangelogsFromRef(gitWrapper, upstreamRemote, branch);
|
||||
} else {
|
||||
checkoutChangelogs(gitWrapper, upstreamRemote, branch);
|
||||
}
|
||||
|
||||
didCheckoutChangelogs = true;
|
||||
Properties props = new Properties();
|
||||
props.load(
|
||||
new StringReader(
|
||||
gitWrapper.runCommand("git", "show", upstreamRemote + "/" + branch + ":build-tools-internal/version.properties")
|
||||
)
|
||||
);
|
||||
String version = props.getProperty("elasticsearch");
|
||||
|
||||
LOGGER.info("Finding changelog files for " + version + "...");
|
||||
|
||||
Set<String> finalEntriesFromBc = entriesFromBc;
|
||||
List<ChangelogEntry> entries = changelogDirectory.getAsFileTree().getFiles().stream().filter(f -> {
|
||||
// When not using a bc ref, we just take everything from the branch/sha passed in
|
||||
if (usingBcRef == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the changelog was present in the BC sha, always use it
|
||||
if (finalEntriesFromBc.contains(f.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, let's check to see if a reference to the PR exists in the commit log for the sha
|
||||
// This specifically covers the case of a PR being merged into the BC with a missing changelog file, and the file added
|
||||
// later.
|
||||
var prNumber = f.getName().replace(".yaml", "");
|
||||
var output = gitWrapper.runCommand("git", "log", bcRef, "--grep", "(#" + prNumber + ")");
|
||||
return output.trim().isEmpty() == false;
|
||||
}).map(ChangelogEntry::parse).sorted(Comparator.comparing(ChangelogEntry::getPr)).collect(toList());
|
||||
|
||||
ChangelogBundle bundle = new ChangelogBundle(version, finalize, Instant.now().toString(), entries);
|
||||
|
||||
yamlMapper.writeValue(new File("docs/release-notes/changelog-bundles/" + version + ".yml"), bundle);
|
||||
} finally {
|
||||
if (didCheckoutChangelogs) {
|
||||
gitWrapper.runCommand("git", "restore", "-s@", "-SW", "--", changelogDirectory.get().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkoutChangelogs(GitWrapper gitWrapper, String upstream, String ref) {
|
||||
gitWrapper.updateRemote(upstream);
|
||||
|
||||
// If the changelog directory contains modified/new files, we should error out instead of wiping them out silently
|
||||
var output = gitWrapper.runCommand("git", "status", "--porcelain", changelogDirectory.get().toString()).trim();
|
||||
if (output.isEmpty() == false) {
|
||||
throw new IllegalStateException(
|
||||
"Changelog directory contains changes that will be wiped out by this task:\n" + changelogDirectory.get() + "\n" + output
|
||||
);
|
||||
}
|
||||
|
||||
gitWrapper.runCommand("rm", "-rf", changelogDirectory.get().toString());
|
||||
var refSpec = upstream + "/" + ref;
|
||||
if (ref.contains("upstream/")) {
|
||||
refSpec = ref.replace("upstream/", upstream + "/");
|
||||
} else if (ref.matches("^[0-9a-f]+$")) {
|
||||
refSpec = ref;
|
||||
}
|
||||
gitWrapper.runCommand("git", "checkout", refSpec, "--", changelogDirectory.get().toString());
|
||||
}
|
||||
|
||||
private void addChangelogsFromRef(GitWrapper gitWrapper, String upstream, String ref) {
|
||||
var refSpec = upstream + "/" + ref;
|
||||
if (ref.contains("upstream/")) {
|
||||
refSpec = ref.replace("upstream/", upstream + "/");
|
||||
} else if (ref.matches("^[0-9a-f]+$")) {
|
||||
refSpec = ref;
|
||||
}
|
||||
|
||||
gitWrapper.runCommand("git", "checkout", refSpec, "--", changelogDirectory.get() + "/*.yaml");
|
||||
}
|
||||
|
||||
@InputDirectory
|
||||
public DirectoryProperty getChangelogDirectory() {
|
||||
return changelogDirectory;
|
||||
}
|
||||
|
||||
public void setChangelogDirectory(Directory dir) {
|
||||
this.changelogDirectory.set(dir);
|
||||
}
|
||||
|
||||
@InputDirectory
|
||||
public DirectoryProperty getChangelogBundlesDirectory() {
|
||||
return changelogBundlesDirectory;
|
||||
}
|
||||
|
||||
public void setChangelogBundlesDirectory(Directory dir) {
|
||||
this.changelogBundlesDirectory.set(dir);
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
public FileCollection getChangelogs() {
|
||||
return changelogs;
|
||||
}
|
||||
|
||||
public void setChangelogs(FileCollection files) {
|
||||
this.changelogs.setFrom(files);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getBundleFile() {
|
||||
return bundleFile;
|
||||
}
|
||||
|
||||
public void setBundleFile(RegularFile file) {
|
||||
this.bundleFile.set(file);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
|
||||
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
|
||||
public record ChangelogBundle(String version, boolean released, String generated, List<ChangelogEntry> changelogs) {
|
||||
|
||||
private static final Logger LOGGER = Logging.getLogger(GenerateReleaseNotesTask.class);
|
||||
private static final ObjectMapper yamlMapper = new ObjectMapper(
|
||||
new YAMLFactory().enable(YAMLGenerator.Feature.MINIMIZE_QUOTES).disable(YAMLGenerator.Feature.SPLIT_LINES)
|
||||
);
|
||||
|
||||
public ChangelogBundle(String version, String generated, List<ChangelogEntry> changelogs) {
|
||||
this(version, false, generated, changelogs);
|
||||
}
|
||||
|
||||
public static ChangelogBundle parse(File file) {
|
||||
try {
|
||||
return yamlMapper.readValue(file, ChangelogBundle.class);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to parse changelog bundle from " + file.getAbsolutePath(), e);
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ChangelogBundle copy(ChangelogBundle bundle) {
|
||||
List<ChangelogEntry> changelogs = bundle.changelogs().stream().toList();
|
||||
return new ChangelogBundle(bundle.version(), bundle.released(), bundle.generated(), changelogs);
|
||||
}
|
||||
|
||||
public ChangelogBundle withChangelogs(List<ChangelogEntry> changelogs) {
|
||||
return new ChangelogBundle(version, released, generated, changelogs);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
|
||||
|
@ -35,12 +36,12 @@ public class ChangelogEntry {
|
|||
private static final Logger LOGGER = Logging.getLogger(GenerateReleaseNotesTask.class);
|
||||
|
||||
private Integer pr;
|
||||
private List<Integer> issues;
|
||||
private String summary;
|
||||
private String area;
|
||||
private String type;
|
||||
private String summary;
|
||||
private Highlight highlight;
|
||||
private List<Integer> issues;
|
||||
private Breaking breaking;
|
||||
private Highlight highlight;
|
||||
private Deprecation deprecation;
|
||||
|
||||
private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
|
||||
|
@ -193,6 +194,7 @@ public class ChangelogEntry {
|
|||
this.body = body;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getAnchor() {
|
||||
return generatedAnchor(this.title);
|
||||
}
|
||||
|
@ -278,6 +280,7 @@ public class ChangelogEntry {
|
|||
this.notable = notable;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getAnchor() {
|
||||
return generatedAnchor(this.title);
|
||||
}
|
||||
|
|
|
@ -13,75 +13,60 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
|
||||
import org.elasticsearch.gradle.VersionProperties;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.Directory;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.RegularFile;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.process.ExecOperations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* Orchestrates the steps required to generate or update various release notes files.
|
||||
*/
|
||||
public class GenerateReleaseNotesTask extends DefaultTask {
|
||||
private static final Logger LOGGER = Logging.getLogger(GenerateReleaseNotesTask.class);
|
||||
|
||||
private final ConfigurableFileCollection changelogs;
|
||||
|
||||
private final RegularFileProperty releaseNotesIndexTemplate;
|
||||
private final RegularFileProperty releaseNotesTemplate;
|
||||
private final RegularFileProperty releaseHighlightsTemplate;
|
||||
private final RegularFileProperty breakingChangesTemplate;
|
||||
private final RegularFileProperty migrationIndexTemplate;
|
||||
private final RegularFileProperty deprecationsTemplate;
|
||||
|
||||
private final RegularFileProperty releaseNotesIndexFile;
|
||||
private final RegularFileProperty releaseNotesFile;
|
||||
private final RegularFileProperty releaseHighlightsFile;
|
||||
private final RegularFileProperty breakingChangesMigrationFile;
|
||||
private final RegularFileProperty migrationIndexFile;
|
||||
private final RegularFileProperty breakingChangesFile;
|
||||
private final RegularFileProperty deprecationsFile;
|
||||
|
||||
private final DirectoryProperty changelogBundleDirectory;
|
||||
|
||||
private final GitWrapper gitWrapper;
|
||||
|
||||
@Inject
|
||||
public GenerateReleaseNotesTask(ObjectFactory objectFactory, ExecOperations execOperations) {
|
||||
changelogs = objectFactory.fileCollection();
|
||||
|
||||
releaseNotesIndexTemplate = objectFactory.fileProperty();
|
||||
releaseNotesTemplate = objectFactory.fileProperty();
|
||||
releaseHighlightsTemplate = objectFactory.fileProperty();
|
||||
breakingChangesTemplate = objectFactory.fileProperty();
|
||||
migrationIndexTemplate = objectFactory.fileProperty();
|
||||
deprecationsTemplate = objectFactory.fileProperty();
|
||||
|
||||
releaseNotesIndexFile = objectFactory.fileProperty();
|
||||
releaseNotesFile = objectFactory.fileProperty();
|
||||
releaseHighlightsFile = objectFactory.fileProperty();
|
||||
breakingChangesMigrationFile = objectFactory.fileProperty();
|
||||
migrationIndexFile = objectFactory.fileProperty();
|
||||
breakingChangesFile = objectFactory.fileProperty();
|
||||
deprecationsFile = objectFactory.fileProperty();
|
||||
|
||||
changelogBundleDirectory = objectFactory.directoryProperty();
|
||||
|
||||
gitWrapper = new GitWrapper(execOperations);
|
||||
}
|
||||
|
@ -94,170 +79,42 @@ public class GenerateReleaseNotesTask extends DefaultTask {
|
|||
findAndUpdateUpstreamRemote(gitWrapper);
|
||||
}
|
||||
|
||||
LOGGER.info("Finding changelog files...");
|
||||
|
||||
final Map<QualifiedVersion, Set<File>> filesByVersion = partitionFilesByVersion(
|
||||
gitWrapper,
|
||||
currentVersion,
|
||||
this.changelogs.getFiles()
|
||||
);
|
||||
|
||||
final List<ChangelogEntry> entries = new ArrayList<>();
|
||||
final Map<QualifiedVersion, Set<ChangelogEntry>> changelogsByVersion = new HashMap<>();
|
||||
|
||||
filesByVersion.forEach((version, files) -> {
|
||||
Set<ChangelogEntry> entriesForVersion = files.stream().map(ChangelogEntry::parse).collect(toSet());
|
||||
entries.addAll(entriesForVersion);
|
||||
changelogsByVersion.put(version, entriesForVersion);
|
||||
});
|
||||
|
||||
final Set<QualifiedVersion> versions = getVersions(gitWrapper, currentVersion);
|
||||
|
||||
LOGGER.info("Updating release notes index...");
|
||||
ReleaseNotesIndexGenerator.update(
|
||||
versions,
|
||||
this.releaseNotesIndexTemplate.get().getAsFile(),
|
||||
this.releaseNotesIndexFile.get().getAsFile()
|
||||
);
|
||||
|
||||
LOGGER.info("Generating release notes...");
|
||||
final QualifiedVersion qualifiedVersion = QualifiedVersion.of(currentVersion);
|
||||
ReleaseNotesGenerator.update(
|
||||
this.releaseNotesTemplate.get().getAsFile(),
|
||||
this.releaseNotesFile.get().getAsFile(),
|
||||
qualifiedVersion,
|
||||
changelogsByVersion.getOrDefault(qualifiedVersion, Set.of())
|
||||
);
|
||||
|
||||
// Only update breaking changes and migration guide for new minors
|
||||
if (qualifiedVersion.revision() == 0) {
|
||||
LOGGER.info("Generating release highlights...");
|
||||
ReleaseHighlightsGenerator.update(
|
||||
this.releaseHighlightsTemplate.get().getAsFile(),
|
||||
this.releaseHighlightsFile.get().getAsFile(),
|
||||
entries
|
||||
);
|
||||
|
||||
LOGGER.info("Generating breaking changes / deprecations notes...");
|
||||
BreakingChangesGenerator.update(
|
||||
this.breakingChangesTemplate.get().getAsFile(),
|
||||
this.breakingChangesMigrationFile.get().getAsFile(),
|
||||
entries
|
||||
);
|
||||
|
||||
LOGGER.info("Updating migration/index...");
|
||||
MigrationIndexGenerator.update(
|
||||
getMinorVersions(versions),
|
||||
this.migrationIndexTemplate.get().getAsFile(),
|
||||
this.migrationIndexFile.get().getAsFile()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all tags in the major series for the supplied version
|
||||
* @param gitWrapper used to call `git`
|
||||
* @param currentVersion the version to base the query upon
|
||||
* @return all versions in the series
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static Set<QualifiedVersion> getVersions(GitWrapper gitWrapper, String currentVersion) {
|
||||
QualifiedVersion qualifiedVersion = QualifiedVersion.of(currentVersion);
|
||||
final String pattern = "v" + qualifiedVersion.major() + ".*";
|
||||
// We may be generating notes for a minor version prior to the latest minor, so we need to filter out versions that are too new.
|
||||
Set<QualifiedVersion> versions = Stream.concat(
|
||||
gitWrapper.listVersions(pattern).filter(v -> v.isBefore(qualifiedVersion)),
|
||||
Stream.of(qualifiedVersion)
|
||||
).collect(toSet());
|
||||
|
||||
// If this is a new minor ensure we include the previous minor, which may not have been released
|
||||
if (qualifiedVersion.minor() > 0 && qualifiedVersion.revision() == 0) {
|
||||
QualifiedVersion previousMinor = new QualifiedVersion(qualifiedVersion.major(), qualifiedVersion.minor() - 1, 0, null);
|
||||
versions.add(previousMinor);
|
||||
}
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert set of QualifiedVersion to MinorVersion by deleting all but the major and minor components.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static Set<MinorVersion> getMinorVersions(Set<QualifiedVersion> versions) {
|
||||
return versions.stream().map(MinorVersion::of).collect(toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Group a set of files by the version in which they first appeared, up until the supplied version. Any files not
|
||||
* present in an earlier version are assumed to have been introduced in the specified version.
|
||||
*
|
||||
* <p>This method works by finding all git tags prior to {@param versionString} in the same minor series, and
|
||||
* examining the git tree for that tag. By doing this over each tag, it is possible to see how the contents
|
||||
* of the changelog directory changed over time.
|
||||
*
|
||||
* @param gitWrapper used to call `git`
|
||||
* @param versionString the "current" version. Does not require a tag in git.
|
||||
* @param allFilesInCheckout the files to partition
|
||||
* @return a mapping from version to the files added in that version.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static Map<QualifiedVersion, Set<File>> partitionFilesByVersion(
|
||||
GitWrapper gitWrapper,
|
||||
String versionString,
|
||||
Set<File> allFilesInCheckout
|
||||
) {
|
||||
if (needsGitTags(versionString) == false) {
|
||||
return Map.of(QualifiedVersion.of(versionString), allFilesInCheckout);
|
||||
}
|
||||
|
||||
QualifiedVersion currentVersion = QualifiedVersion.of(versionString);
|
||||
|
||||
// Find all tags for this minor series, using a wildcard tag pattern.
|
||||
String tagWildcard = String.format(Locale.ROOT, "v%d.%d*", currentVersion.major(), currentVersion.minor());
|
||||
|
||||
final List<QualifiedVersion> earlierVersions = gitWrapper.listVersions(tagWildcard)
|
||||
// Only keep earlier versions, and if `currentVersion` is a prerelease, then only prereleases too.
|
||||
.filter(
|
||||
each -> each.isBefore(currentVersion)
|
||||
&& (currentVersion.isSnapshot() || (currentVersion.hasQualifier() == each.hasQualifier()))
|
||||
)
|
||||
.sorted(naturalOrder())
|
||||
LOGGER.info("Finding changelog bundles...");
|
||||
List<ChangelogBundle> allBundles = this.changelogBundleDirectory.getAsFileTree()
|
||||
.getFiles()
|
||||
.stream()
|
||||
.map(ChangelogBundle::parse)
|
||||
.toList();
|
||||
|
||||
if (earlierVersions.isEmpty()) {
|
||||
throw new GradleException("Failed to find git tags prior to [v" + currentVersion + "]");
|
||||
var bundles = getSortedBundlesWithUniqueChangelogs(allBundles);
|
||||
|
||||
LOGGER.info("Generating release notes...");
|
||||
ReleaseNotesGenerator.update(this.releaseNotesTemplate.get().getAsFile(), this.releaseNotesFile.get().getAsFile(), bundles);
|
||||
ReleaseNotesGenerator.update(this.breakingChangesTemplate.get().getAsFile(), this.breakingChangesFile.get().getAsFile(), bundles);
|
||||
ReleaseNotesGenerator.update(this.deprecationsTemplate.get().getAsFile(), this.deprecationsFile.get().getAsFile(), bundles);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static List<ChangelogBundle> getSortedBundlesWithUniqueChangelogs(List<ChangelogBundle> bundles) {
|
||||
List<ChangelogBundle> sorted = bundles.stream()
|
||||
.sorted(Comparator.comparing(ChangelogBundle::released).reversed().thenComparing(ChangelogBundle::generated))
|
||||
.toList();
|
||||
|
||||
// Ensure that each changelog/PR only shows up once, in its earliest release
|
||||
var uniquePrs = new HashSet<Integer>();
|
||||
List<ChangelogBundle> modifiedBundles = new ArrayList<>();
|
||||
for (int i = sorted.size() - 1; i >= 0; i--) {
|
||||
var bundle = sorted.get(i);
|
||||
if (bundle.released() == false) {
|
||||
List<ChangelogEntry> entries = bundle.changelogs().stream().filter(c -> false == uniquePrs.contains(c.getPr())).toList();
|
||||
modifiedBundles.add(bundle.withChangelogs(entries));
|
||||
} else {
|
||||
modifiedBundles.add(bundle);
|
||||
}
|
||||
uniquePrs.addAll(bundle.changelogs().stream().map(ChangelogEntry::getPr).toList());
|
||||
}
|
||||
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = new HashMap<>();
|
||||
|
||||
Set<File> mutableAllFilesInCheckout = new HashSet<>(allFilesInCheckout);
|
||||
|
||||
// 1. For each earlier version
|
||||
earlierVersions.forEach(earlierVersion -> {
|
||||
// 2. Find all the changelog files it contained
|
||||
Set<String> filesInTreeForVersion = gitWrapper.listFiles("v" + earlierVersion, "docs/changelog")
|
||||
.map(line -> Path.of(line).getFileName().toString())
|
||||
.collect(toSet());
|
||||
|
||||
Set<File> filesForVersion = new HashSet<>();
|
||||
partitionedFiles.put(earlierVersion, filesForVersion);
|
||||
|
||||
// 3. Find the `File` object for each one
|
||||
final Iterator<File> filesIterator = mutableAllFilesInCheckout.iterator();
|
||||
while (filesIterator.hasNext()) {
|
||||
File nextFile = filesIterator.next();
|
||||
if (filesInTreeForVersion.contains(nextFile.getName())) {
|
||||
// 4. And remove it so that it is associated with the earlier version
|
||||
filesForVersion.add(nextFile);
|
||||
filesIterator.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 5. Associate whatever is left with the current version.
|
||||
partitionedFiles.put(currentVersion, mutableAllFilesInCheckout);
|
||||
|
||||
return partitionedFiles;
|
||||
return modifiedBundles;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,18 +123,7 @@ public class GenerateReleaseNotesTask extends DefaultTask {
|
|||
*/
|
||||
private static void findAndUpdateUpstreamRemote(GitWrapper gitWrapper) {
|
||||
LOGGER.info("Finding upstream git remote");
|
||||
// We need to ensure the tags are up-to-date. Find the correct remote to use
|
||||
String upstream = gitWrapper.listRemotes()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().contains("elastic/elasticsearch"))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getKey)
|
||||
.orElseThrow(
|
||||
() -> new GradleException(
|
||||
"I need to ensure the git tags are up-to-date, but I couldn't find a git remote for [elastic/elasticsearch]"
|
||||
)
|
||||
);
|
||||
String upstream = gitWrapper.getUpstream();
|
||||
|
||||
LOGGER.info("Updating remote [{}]", upstream);
|
||||
// Now update the remote, and make sure we update the tags too
|
||||
|
@ -308,22 +154,13 @@ public class GenerateReleaseNotesTask extends DefaultTask {
|
|||
return true;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
public FileCollection getChangelogs() {
|
||||
return changelogs;
|
||||
@InputDirectory
|
||||
public DirectoryProperty getChangelogBundleDirectory() {
|
||||
return changelogBundleDirectory;
|
||||
}
|
||||
|
||||
public void setChangelogs(FileCollection files) {
|
||||
this.changelogs.setFrom(files);
|
||||
}
|
||||
|
||||
@InputFile
|
||||
public RegularFileProperty getReleaseNotesIndexTemplate() {
|
||||
return releaseNotesIndexTemplate;
|
||||
}
|
||||
|
||||
public void setReleaseNotesIndexTemplate(RegularFile file) {
|
||||
this.releaseNotesIndexTemplate.set(file);
|
||||
public void setChangelogBundleDirectory(Directory dir) {
|
||||
this.changelogBundleDirectory.set(dir);
|
||||
}
|
||||
|
||||
@InputFile
|
||||
|
@ -354,21 +191,12 @@ public class GenerateReleaseNotesTask extends DefaultTask {
|
|||
}
|
||||
|
||||
@InputFile
|
||||
public RegularFileProperty getMigrationIndexTemplate() {
|
||||
return migrationIndexTemplate;
|
||||
public RegularFileProperty getDeprecationsTemplate() {
|
||||
return deprecationsTemplate;
|
||||
}
|
||||
|
||||
public void setMigrationIndexTemplate(RegularFile file) {
|
||||
this.migrationIndexTemplate.set(file);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getReleaseNotesIndexFile() {
|
||||
return releaseNotesIndexFile;
|
||||
}
|
||||
|
||||
public void setReleaseNotesIndexFile(RegularFile file) {
|
||||
this.releaseNotesIndexFile.set(file);
|
||||
public void setDeprecationsTemplate(RegularFile file) {
|
||||
this.deprecationsTemplate.set(file);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
|
@ -390,20 +218,20 @@ public class GenerateReleaseNotesTask extends DefaultTask {
|
|||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getBreakingChangesMigrationFile() {
|
||||
return breakingChangesMigrationFile;
|
||||
public RegularFileProperty getBreakingChangesFile() {
|
||||
return breakingChangesFile;
|
||||
}
|
||||
|
||||
public void setBreakingChangesMigrationFile(RegularFile file) {
|
||||
this.breakingChangesMigrationFile.set(file);
|
||||
public void setBreakingChangesFile(RegularFile file) {
|
||||
this.breakingChangesFile.set(file);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getMigrationIndexFile() {
|
||||
return migrationIndexFile;
|
||||
public RegularFileProperty getDeprecationsFile() {
|
||||
return deprecationsFile;
|
||||
}
|
||||
|
||||
public void setMigrationIndexFile(RegularFile file) {
|
||||
this.migrationIndexFile.set(file);
|
||||
public void setDeprecationsFile(RegularFile file) {
|
||||
this.deprecationsFile.set(file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.process.ExecOperations;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
@ -87,4 +88,14 @@ public class GitWrapper {
|
|||
public Stream<String> listFiles(String ref, String path) {
|
||||
return runCommand("git", "ls-tree", "--name-only", "-r", ref, path).lines();
|
||||
}
|
||||
|
||||
public String getUpstream() {
|
||||
String upstream = listRemotes().entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().contains("elastic/elasticsearch"))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getKey)
|
||||
.orElseThrow(() -> new GradleException("Couldn't find a git remote for [elastic/elasticsearch]"));
|
||||
return upstream;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
|
||||
/**
|
||||
* This class ensures that the migrate/index page has the appropriate anchors and include directives
|
||||
* for the current repository version.
|
||||
*/
|
||||
public class MigrationIndexGenerator {
|
||||
|
||||
static void update(Set<MinorVersion> versions, File indexTemplate, File indexFile) throws IOException {
|
||||
try (FileWriter indexFileWriter = new FileWriter(indexFile)) {
|
||||
indexFileWriter.write(generateFile(versions, Files.readString(indexTemplate.toPath())));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String generateFile(Set<MinorVersion> versionsSet, String template) throws IOException {
|
||||
final Set<MinorVersion> versions = new TreeSet<>(reverseOrder());
|
||||
versions.addAll(versionsSet);
|
||||
final List<String> includeVersions = versions.stream().map(MinorVersion::underscore).collect(Collectors.toList());
|
||||
|
||||
final Map<String, Object> bindings = new HashMap<>();
|
||||
bindings.put("versions", versions);
|
||||
bindings.put("includeVersions", includeVersions);
|
||||
|
||||
return TemplateUtils.render(template, bindings);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import org.elasticsearch.gradle.VersionProperties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Generates the release highlights notes, for changelog files that contain the <code>highlight</code> field.
|
||||
*/
|
||||
public class ReleaseHighlightsGenerator {
|
||||
static void update(File templateFile, File outputFile, List<ChangelogEntry> entries) throws IOException {
|
||||
try (FileWriter output = new FileWriter(outputFile)) {
|
||||
output.write(
|
||||
generateFile(QualifiedVersion.of(VersionProperties.getElasticsearch()), Files.readString(templateFile.toPath()), entries)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String generateFile(QualifiedVersion version, String template, List<ChangelogEntry> entries) throws IOException {
|
||||
final List<String> priorVersions = new ArrayList<>();
|
||||
|
||||
if (version.minor() > 0) {
|
||||
final int major = version.major();
|
||||
for (int minor = version.minor() - 1; minor >= 0; minor--) {
|
||||
String majorMinor = major + "." + minor;
|
||||
priorVersions.add("{ref-bare}/" + majorMinor + "/release-highlights.html[" + majorMinor + "]");
|
||||
}
|
||||
}
|
||||
|
||||
final Map<Boolean, List<ChangelogEntry.Highlight>> groupedHighlights = entries.stream()
|
||||
.map(ChangelogEntry::getHighlight)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparingInt(ChangelogEntry.Highlight::getPr))
|
||||
.collect(Collectors.groupingBy(ChangelogEntry.Highlight::isNotable, Collectors.toList()));
|
||||
|
||||
final List<ChangelogEntry.Highlight> notableHighlights = groupedHighlights.getOrDefault(true, List.of());
|
||||
final List<ChangelogEntry.Highlight> nonNotableHighlights = groupedHighlights.getOrDefault(false, List.of());
|
||||
|
||||
final Map<String, Object> bindings = new HashMap<>();
|
||||
bindings.put("priorVersions", priorVersions);
|
||||
bindings.put("notableHighlights", notableHighlights);
|
||||
bindings.put("nonNotableHighlights", nonNotableHighlights);
|
||||
|
||||
return TemplateUtils.render(template, bindings);
|
||||
}
|
||||
}
|
|
@ -15,13 +15,16 @@ import java.io.File;
|
|||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
|
@ -30,6 +33,17 @@ import static java.util.stream.Collectors.toList;
|
|||
* type of change, then by team area.
|
||||
*/
|
||||
public class ReleaseNotesGenerator {
|
||||
|
||||
private record ChangelogsBundleWrapper(
|
||||
QualifiedVersion version,
|
||||
ChangelogBundle bundle,
|
||||
Map<String, Map<String, List<ChangelogEntry>>> changelogsByTypeByArea,
|
||||
QualifiedVersion unqualifiedVersion,
|
||||
String versionWithoutSeparator,
|
||||
List<ChangelogEntry.Highlight> notableHighlights,
|
||||
List<ChangelogEntry.Highlight> nonNotableHighlights
|
||||
) {}
|
||||
|
||||
/**
|
||||
* These mappings translate change types into the headings as they should appear in the release notes.
|
||||
*/
|
||||
|
@ -39,40 +53,95 @@ public class ReleaseNotesGenerator {
|
|||
TYPE_LABELS.put("breaking", "Breaking changes");
|
||||
TYPE_LABELS.put("breaking-java", "Breaking Java changes");
|
||||
TYPE_LABELS.put("bug", "Bug fixes");
|
||||
TYPE_LABELS.put("fixes", "Fixes");
|
||||
TYPE_LABELS.put("deprecation", "Deprecations");
|
||||
TYPE_LABELS.put("enhancement", "Enhancements");
|
||||
TYPE_LABELS.put("feature", "New features");
|
||||
TYPE_LABELS.put("features-enhancements", "Features and enhancements");
|
||||
TYPE_LABELS.put("new-aggregation", "New aggregation");
|
||||
TYPE_LABELS.put("regression", "Regressions");
|
||||
TYPE_LABELS.put("upgrade", "Upgrades");
|
||||
}
|
||||
|
||||
static void update(File templateFile, File outputFile, QualifiedVersion version, Set<ChangelogEntry> changelogs) throws IOException {
|
||||
/**
|
||||
* These are the types of changes that are considered "Features and Enhancements" in the release notes.
|
||||
*/
|
||||
private static final List<String> FEATURE_ENHANCEMENT_TYPES = List.of("feature", "new-aggregation", "enhancement", "upgrade");
|
||||
|
||||
static void update(File templateFile, File outputFile, List<ChangelogBundle> bundles) throws IOException {
|
||||
final String templateString = Files.readString(templateFile.toPath());
|
||||
|
||||
try (FileWriter output = new FileWriter(outputFile)) {
|
||||
output.write(generateFile(templateString, version, changelogs));
|
||||
output.write(generateFile(templateString, bundles));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String generateFile(String template, QualifiedVersion version, Set<ChangelogEntry> changelogs) throws IOException {
|
||||
final var changelogsByTypeByArea = buildChangelogBreakdown(changelogs);
|
||||
static String generateFile(String template, List<ChangelogBundle> bundles) throws IOException {
|
||||
var bundlesWrapped = new ArrayList<ChangelogsBundleWrapper>();
|
||||
|
||||
for (var bundle : bundles) {
|
||||
var changelogs = bundle.changelogs();
|
||||
final var changelogsByTypeByArea = buildChangelogBreakdown(changelogs);
|
||||
|
||||
final Map<Boolean, List<ChangelogEntry.Highlight>> groupedHighlights = changelogs.stream()
|
||||
.map(ChangelogEntry::getHighlight)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(comparingInt(ChangelogEntry.Highlight::getPr))
|
||||
.collect(groupingBy(ChangelogEntry.Highlight::isNotable, toList()));
|
||||
|
||||
final var notableHighlights = groupedHighlights.getOrDefault(true, List.of());
|
||||
final var nonNotableHighlights = groupedHighlights.getOrDefault(false, List.of());
|
||||
|
||||
final var version = QualifiedVersion.of(bundle.version());
|
||||
final var versionWithoutSeparator = version.withoutQualifier().toString().replaceAll("\\.", "");
|
||||
|
||||
final var wrapped = new ChangelogsBundleWrapper(
|
||||
version,
|
||||
bundle,
|
||||
changelogsByTypeByArea,
|
||||
version.withoutQualifier(),
|
||||
versionWithoutSeparator,
|
||||
notableHighlights,
|
||||
nonNotableHighlights
|
||||
);
|
||||
|
||||
bundlesWrapped.add(wrapped);
|
||||
}
|
||||
|
||||
final Map<String, Object> bindings = new HashMap<>();
|
||||
bindings.put("version", version);
|
||||
bindings.put("changelogsByTypeByArea", changelogsByTypeByArea);
|
||||
bindings.put("TYPE_LABELS", TYPE_LABELS);
|
||||
bindings.put("changelogBundles", bundlesWrapped);
|
||||
|
||||
return TemplateUtils.render(template, bindings);
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, List<ChangelogEntry>>> buildChangelogBreakdown(Set<ChangelogEntry> changelogs) {
|
||||
/**
|
||||
* The new markdown release notes are grouping several of the old change types together.
|
||||
* This method maps the change type that developers use in the changelogs to the new type that the release notes cares about.
|
||||
*/
|
||||
private static String getTypeFromEntry(ChangelogEntry entry) {
|
||||
if (entry.getBreaking() != null) {
|
||||
return "breaking";
|
||||
}
|
||||
|
||||
if (FEATURE_ENHANCEMENT_TYPES.contains(entry.getType())) {
|
||||
return "features-enhancements";
|
||||
}
|
||||
|
||||
if (entry.getType().equals("bug")) {
|
||||
return "fixes";
|
||||
}
|
||||
|
||||
return entry.getType();
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, List<ChangelogEntry>>> buildChangelogBreakdown(Collection<ChangelogEntry> changelogs) {
|
||||
Map<String, Map<String, List<ChangelogEntry>>> changelogsByTypeByArea = changelogs.stream()
|
||||
.collect(
|
||||
groupingBy(
|
||||
// Entries with breaking info are always put in the breaking section
|
||||
entry -> entry.getBreaking() == null ? entry.getType() : "breaking",
|
||||
entry -> getTypeFromEntry(entry),
|
||||
TreeMap::new,
|
||||
// Group changelogs for each type by their team area
|
||||
groupingBy(
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
|
||||
/**
|
||||
* This class ensures that the release notes index page has the appropriate anchors and include directives
|
||||
* for the current repository version.
|
||||
*/
|
||||
public class ReleaseNotesIndexGenerator {
|
||||
|
||||
static void update(Set<QualifiedVersion> versions, File indexTemplate, File indexFile) throws IOException {
|
||||
try (FileWriter indexFileWriter = new FileWriter(indexFile)) {
|
||||
indexFileWriter.write(generateFile(versions, Files.readString(indexTemplate.toPath())));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String generateFile(Set<QualifiedVersion> versionsSet, String template) throws IOException {
|
||||
final Set<QualifiedVersion> versions = new TreeSet<>(reverseOrder());
|
||||
|
||||
// For the purpose of generating the index, snapshot versions are the same as released versions. Prerelease versions are not.
|
||||
versionsSet.stream().map(v -> v.isSnapshot() ? v.withoutQualifier() : v).forEach(versions::add);
|
||||
|
||||
final List<String> includeVersions = versions.stream().map(QualifiedVersion::toString).collect(Collectors.toList());
|
||||
|
||||
final Map<String, Object> bindings = new HashMap<>();
|
||||
bindings.put("versions", versions);
|
||||
bindings.put("includeVersions", includeVersions);
|
||||
|
||||
return TemplateUtils.render(template, bindings);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ import org.gradle.api.provider.Provider;
|
|||
import org.gradle.api.tasks.util.PatternSet;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -55,9 +54,9 @@ public class ReleaseToolsPlugin implements Plugin<Project> {
|
|||
project.getTasks().register("tagVersions", TagVersionsTask.class);
|
||||
project.getTasks().register("setCompatibleVersions", SetCompatibleVersionsTask.class, t -> t.setThisVersion(version));
|
||||
|
||||
final FileTree yamlFiles = projectDirectory.dir("docs/changelog")
|
||||
.getAsFileTree()
|
||||
.matching(new PatternSet().include("**/*.yml", "**/*.yaml"));
|
||||
final Directory changeLogDirectory = projectDirectory.dir("docs/changelog");
|
||||
final Directory changeLogBundlesDirectory = projectDirectory.dir("docs/release-notes/changelog-bundles");
|
||||
final FileTree yamlFiles = changeLogDirectory.getAsFileTree().matching(new PatternSet().include("**/*.yml", "**/*.yaml"));
|
||||
|
||||
final Provider<ValidateYamlAgainstSchemaTask> validateChangelogsTask = project.getTasks()
|
||||
.register("validateChangelogs", ValidateYamlAgainstSchemaTask.class, task -> {
|
||||
|
@ -68,49 +67,41 @@ public class ReleaseToolsPlugin implements Plugin<Project> {
|
|||
task.setReport(new File(project.getBuildDir(), "reports/validateYaml.txt"));
|
||||
});
|
||||
|
||||
final Function<Boolean, Action<GenerateReleaseNotesTask>> configureGenerateTask = shouldConfigureYamlFiles -> task -> {
|
||||
final Action<BundleChangelogsTask> configureBundleTask = task -> {
|
||||
task.setGroup("Documentation");
|
||||
if (shouldConfigureYamlFiles) {
|
||||
task.setChangelogs(yamlFiles);
|
||||
task.setDescription("Generates release notes from changelog files held in this checkout");
|
||||
} else {
|
||||
task.setDescription("Generates stub release notes e.g. after feature freeze");
|
||||
}
|
||||
task.setDescription("Generates release notes from changelog files held in this checkout");
|
||||
task.setChangelogs(yamlFiles);
|
||||
task.setChangelogDirectory(changeLogDirectory);
|
||||
task.setChangelogBundlesDirectory(changeLogBundlesDirectory);
|
||||
task.setBundleFile(projectDirectory.file("docs/release-notes/changelogs-" + version.toString() + ".yml"));
|
||||
task.getOutputs().upToDateWhen(o -> false);
|
||||
};
|
||||
|
||||
task.setReleaseNotesIndexTemplate(projectDirectory.file(RESOURCES + "templates/release-notes-index.asciidoc"));
|
||||
task.setReleaseNotesIndexFile(projectDirectory.file("docs/reference/release-notes.asciidoc"));
|
||||
final Action<GenerateReleaseNotesTask> configureGenerateTask = task -> {
|
||||
task.setGroup("Documentation");
|
||||
task.setDescription("Generates release notes for all versions/branches using changelog bundles in this checkout");
|
||||
|
||||
task.setReleaseNotesTemplate(projectDirectory.file(RESOURCES + "templates/release-notes.asciidoc"));
|
||||
task.setReleaseNotesFile(
|
||||
projectDirectory.file(
|
||||
String.format(
|
||||
"docs/reference/release-notes/%d.%d.%d.asciidoc",
|
||||
version.getMajor(),
|
||||
version.getMinor(),
|
||||
version.getRevision()
|
||||
)
|
||||
)
|
||||
);
|
||||
task.setReleaseNotesTemplate(projectDirectory.file(RESOURCES + "templates/index.md"));
|
||||
task.setReleaseNotesFile(projectDirectory.file("docs/release-notes/index.md"));
|
||||
|
||||
task.setReleaseHighlightsTemplate(projectDirectory.file(RESOURCES + "templates/release-highlights.asciidoc"));
|
||||
task.setReleaseHighlightsFile(projectDirectory.file("docs/reference/release-notes/highlights.asciidoc"));
|
||||
|
||||
task.setBreakingChangesTemplate(projectDirectory.file(RESOURCES + "templates/breaking-changes.asciidoc"));
|
||||
task.setBreakingChangesMigrationFile(
|
||||
projectDirectory.file(
|
||||
String.format("docs/reference/migration/migrate_%d_%d.asciidoc", version.getMajor(), version.getMinor())
|
||||
)
|
||||
);
|
||||
task.setMigrationIndexTemplate(projectDirectory.file(RESOURCES + "templates/migration-index.asciidoc"));
|
||||
task.setMigrationIndexFile(projectDirectory.file("docs/reference/migration/index.asciidoc"));
|
||||
task.setBreakingChangesTemplate(projectDirectory.file(RESOURCES + "templates/breaking-changes.md"));
|
||||
task.setBreakingChangesFile(projectDirectory.file("docs/release-notes/breaking-changes.md"));
|
||||
|
||||
task.setDeprecationsTemplate(projectDirectory.file(RESOURCES + "templates/deprecations.md"));
|
||||
task.setDeprecationsFile(projectDirectory.file("docs/release-notes/deprecations.md"));
|
||||
|
||||
task.setChangelogBundleDirectory(changeLogBundlesDirectory);
|
||||
|
||||
task.getOutputs().upToDateWhen(o -> false);
|
||||
|
||||
task.dependsOn(validateChangelogsTask);
|
||||
};
|
||||
|
||||
project.getTasks().register("generateReleaseNotes", GenerateReleaseNotesTask.class).configure(configureGenerateTask.apply(true));
|
||||
project.getTasks()
|
||||
.register("generateStubReleaseNotes", GenerateReleaseNotesTask.class)
|
||||
.configure(configureGenerateTask.apply(false));
|
||||
project.getTasks().register("bundleChangelogs", BundleChangelogsTask.class).configure(configureBundleTask);
|
||||
project.getTasks().register("generateReleaseNotes", GenerateReleaseNotesTask.class).configure(configureGenerateTask);
|
||||
|
||||
project.getTasks().register("pruneChangelogs", PruneChangelogsTask.class).configure(task -> {
|
||||
task.setGroup("Documentation");
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
[[migrating-${majorDotMinor}]]
|
||||
== Migrating to ${majorDotMinor}
|
||||
++++
|
||||
<titleabbrev>${majorDotMinor}</titleabbrev>
|
||||
++++
|
||||
|
||||
This section discusses the changes that you need to be aware of when migrating
|
||||
your application to {es} ${majorDotMinor}.
|
||||
|
||||
See also <<release-highlights>> and <<es-release-notes>>.
|
||||
<% if (isElasticsearchSnapshot) { %>
|
||||
coming::[${majorDotMinorDotRevision}]
|
||||
<% } %>
|
||||
|
||||
[discrete]
|
||||
[[breaking-changes-${majorDotMinor}]]
|
||||
=== Breaking changes
|
||||
<% if (breakingByNotabilityByArea.isEmpty()) { %>
|
||||
There are no breaking changes in {es} ${majorDotMinor}.
|
||||
<% } else { %>
|
||||
The following changes in {es} ${majorDotMinor} might affect your applications
|
||||
and prevent them from operating normally.
|
||||
Before upgrading to ${majorDotMinor}, review these changes and take the described steps
|
||||
to mitigate the impact.
|
||||
<%
|
||||
if (breakingByNotabilityByArea.getOrDefault(true, []).isEmpty()) { %>
|
||||
|
||||
There are no notable breaking changes in {es} ${majorDotMinor}.
|
||||
But there are some less critical breaking changes.
|
||||
<% }
|
||||
[true, false].each { isNotable ->
|
||||
def breakingByArea = breakingByNotabilityByArea.getOrDefault(isNotable, [])
|
||||
if (breakingByArea.isEmpty() == false) {
|
||||
breakingByArea.eachWithIndex { area, breakingChanges, i ->
|
||||
print "\n[discrete]\n"
|
||||
print "[[breaking_${majorMinor}_${ area.toLowerCase().replaceAll("[^a-z0-9]+", "_") }_changes]]\n"
|
||||
print "==== ${area} changes\n"
|
||||
|
||||
for (breaking in breakingChanges) { %>
|
||||
[[${ breaking.anchor }]]
|
||||
.${breaking.title}
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
${breaking.details.trim()}
|
||||
|
||||
*Impact* +
|
||||
${breaking.impact.trim()}
|
||||
====
|
||||
<%
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deprecationsByNotabilityByArea.isEmpty() == false) { %>
|
||||
|
||||
[discrete]
|
||||
[[deprecated-${majorDotMinor}]]
|
||||
=== Deprecations
|
||||
|
||||
The following functionality has been deprecated in {es} ${majorDotMinor}
|
||||
and will be removed in a future version.
|
||||
While this won't have an immediate impact on your applications,
|
||||
we strongly encourage you to take the described steps to update your code
|
||||
after upgrading to ${majorDotMinor}.
|
||||
|
||||
To find out if you are using any deprecated functionality,
|
||||
enable <<deprecation-logging, deprecation logging>>.
|
||||
<%
|
||||
[true, false].each { isNotable ->
|
||||
def deprecationsByArea = deprecationsByNotabilityByArea.getOrDefault(isNotable, [])
|
||||
if (deprecationsByArea.isEmpty() == false) {
|
||||
deprecationsByArea.eachWithIndex { area, deprecations, i ->
|
||||
print "\n[discrete]\n"
|
||||
print "[[deprecations_${majorMinor}_${ area.toLowerCase().replaceAll("[^a-z0-9]+", "_") }]]\n"
|
||||
print "==== ${area} deprecations\n"
|
||||
|
||||
for (deprecation in deprecations) { %>
|
||||
[[${ deprecation.anchor }]]
|
||||
.${deprecation.title}
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
${deprecation.details.trim()}
|
||||
|
||||
*Impact* +
|
||||
${deprecation.impact.trim()}
|
||||
====
|
||||
<%
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} %>
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
navigation_title: "Breaking changes"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html
|
||||
---
|
||||
|
||||
# Elasticsearch breaking changes [elasticsearch-breaking-changes]
|
||||
|
||||
Breaking changes can impact your Elastic applications, potentially disrupting normal operations. Before you upgrade, carefully review the Elasticsearch breaking changes and take the necessary steps to mitigate any issues.
|
||||
|
||||
If you are migrating from a version prior to version 9.0, you must first upgrade to the last 8.x version available. To learn how to upgrade, check out [Upgrade](docs-content://deploy-manage/upgrade.md).
|
||||
|
||||
% ## Next version [elasticsearch-nextversion-breaking-changes]
|
||||
<%
|
||||
for(bundle in changelogBundles) {
|
||||
def version = bundle.version
|
||||
def versionForIds = bundle.version.toString().equals('9.0.0') ? bundle.versionWithoutSeparator : bundle.version
|
||||
def changelogsByTypeByArea = bundle.changelogsByTypeByArea
|
||||
def unqualifiedVersion = bundle.unqualifiedVersion
|
||||
def coming = !bundle.bundle.released
|
||||
|
||||
if (coming) {
|
||||
print "\n"
|
||||
print "```{applies_to}\n"
|
||||
print "stack: coming ${version}\n"
|
||||
print "```"
|
||||
}
|
||||
%>
|
||||
## ${unqualifiedVersion} [elasticsearch-${versionForIds}-breaking-changes]
|
||||
<%
|
||||
if (!changelogsByTypeByArea['breaking']) {
|
||||
print "\nNo breaking changes in this version.\n"
|
||||
} else {
|
||||
for (team in (changelogsByTypeByArea['breaking'] ?: [:]).keySet()) {
|
||||
print "\n${team}:\n";
|
||||
|
||||
for (change in changelogsByTypeByArea['breaking'][team]) {
|
||||
print "* ${change.summary} [#${change.pr}](https://github.com/elastic/elasticsearch/pull/${change.pr})"
|
||||
if (change.issues != null && change.issues.empty == false) {
|
||||
print change.issues.size() == 1 ? " (issue: " : " (issues: "
|
||||
print change.issues.collect { "[#${it}](https://github.com/elastic/elasticsearch/issues/${it})" }.join(", ")
|
||||
print ")"
|
||||
}
|
||||
print "\n"
|
||||
}
|
||||
}
|
||||
|
||||
print "\n\n"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
navigation_title: "Deprecations"
|
||||
---
|
||||
|
||||
# {{es}} deprecations [elasticsearch-deprecations]
|
||||
|
||||
Over time, certain Elastic functionality becomes outdated and is replaced or removed. To help with the transition, Elastic deprecates functionality for a period before removal, giving you time to update your applications.
|
||||
|
||||
Review the deprecated functionality for Elasticsearch. While deprecations have no immediate impact, we strongly encourage you update your implementation after you upgrade. To learn how to upgrade, check out [Upgrade](docs-content://deploy-manage/upgrade.md).
|
||||
|
||||
To give you insight into what deprecated features you’re using, {{es}}:
|
||||
|
||||
* Returns a `Warn` HTTP header whenever you submit a request that uses deprecated functionality.
|
||||
* [Logs deprecation warnings](docs-content://deploy-manage/monitor/logging-configuration/update-elasticsearch-logging-levels.md#deprecation-logging) when deprecated functionality is used.
|
||||
* [Provides a deprecation info API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-migration-deprecations) that scans a cluster’s configuration and mappings for deprecated functionality.
|
||||
|
||||
% ## Next version [elasticsearch-nextversion-deprecations]
|
||||
<%
|
||||
for(bundle in changelogBundles) {
|
||||
def version = bundle.version
|
||||
def versionForIds = bundle.version.toString().equals('9.0.0') ? bundle.versionWithoutSeparator : bundle.version
|
||||
def changelogsByTypeByArea = bundle.changelogsByTypeByArea
|
||||
def unqualifiedVersion = bundle.unqualifiedVersion
|
||||
def coming = !bundle.bundle.released
|
||||
|
||||
if (coming) {
|
||||
print "\n"
|
||||
print "```{applies_to}\n"
|
||||
print "stack: coming ${version}\n"
|
||||
print "```"
|
||||
}
|
||||
%>
|
||||
## ${unqualifiedVersion} [elasticsearch-${versionForIds}-deprecations]
|
||||
<%
|
||||
if (!changelogsByTypeByArea['deprecation']) {
|
||||
print "\nNo deprecations in this version.\n"
|
||||
} else {
|
||||
for (team in (changelogsByTypeByArea['deprecation'] ?: [:]).keySet()) {
|
||||
print "\n${team}:\n";
|
||||
|
||||
for (change in changelogsByTypeByArea['deprecation'][team]) {
|
||||
print "* ${change.summary} [#${change.pr}](https://github.com/elastic/elasticsearch/pull/${change.pr})"
|
||||
if (change.issues != null && change.issues.empty == false) {
|
||||
print change.issues.size() == 1 ? " (issue: " : " (issues: "
|
||||
print change.issues.collect { "[#${it}](https://github.com/elastic/elasticsearch/issues/${it})" }.join(", ")
|
||||
print ")"
|
||||
}
|
||||
print "\n"
|
||||
}
|
||||
}
|
||||
print "\n\n"
|
||||
}
|
||||
}
|
76
build-tools-internal/src/main/resources/templates/index.md
Normal file
76
build-tools-internal/src/main/resources/templates/index.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
navigation_title: "Elasticsearch"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/es-release-notes.html
|
||||
---
|
||||
|
||||
# Elasticsearch release notes [elasticsearch-release-notes]
|
||||
|
||||
Review the changes, fixes, and more in each version of Elasticsearch.
|
||||
|
||||
To check for security updates, go to [Security announcements for the Elastic stack](https://discuss.elastic.co/c/announcements/security-announcements/31).
|
||||
|
||||
% Release notes include only features, enhancements, and fixes. Add breaking changes, deprecations, and known issues to the applicable release notes sections.
|
||||
|
||||
% ## version.next [elasticsearch-next-release-notes]
|
||||
|
||||
% ### Features and enhancements [elasticsearch-next-features-enhancements]
|
||||
% *
|
||||
|
||||
% ### Fixes [elasticsearch-next-fixes]
|
||||
% *
|
||||
<%
|
||||
for(bundle in changelogBundles) {
|
||||
def version = bundle.version
|
||||
def versionForIds = bundle.version.toString().equals('9.0.0') ? bundle.versionWithoutSeparator : bundle.version
|
||||
def changelogsByTypeByArea = bundle.changelogsByTypeByArea
|
||||
def notableHighlights = bundle.notableHighlights
|
||||
def nonNotableHighlights = bundle.nonNotableHighlights
|
||||
def unqualifiedVersion = bundle.unqualifiedVersion
|
||||
def coming = !bundle.bundle.released
|
||||
|
||||
if (coming) {
|
||||
print "\n"
|
||||
print "```{applies_to}\n"
|
||||
print "stack: coming ${version}\n"
|
||||
print "```"
|
||||
}
|
||||
%>
|
||||
## ${unqualifiedVersion} [elasticsearch-${versionForIds}-release-notes]
|
||||
<%
|
||||
if (!notableHighlights.isEmpty() || !nonNotableHighlights.isEmpty()) {
|
||||
print "\n### Highlights [elasticsearch-${versionForIds}-highlights]\n"
|
||||
}
|
||||
|
||||
for (highlights in [notableHighlights, nonNotableHighlights]) {
|
||||
if (!highlights.isEmpty()) {
|
||||
for (highlight in highlights) { %>
|
||||
::::{dropdown} ${highlight.title}
|
||||
${highlight.body.trim()}
|
||||
::::
|
||||
<% }
|
||||
}
|
||||
}
|
||||
|
||||
for (changeType in ['features-enhancements', 'fixes', 'regression']) {
|
||||
if (changelogsByTypeByArea[changeType] == null || changelogsByTypeByArea[changeType].empty) {
|
||||
continue;
|
||||
}
|
||||
%>
|
||||
### ${ TYPE_LABELS.getOrDefault(changeType, 'No mapping for TYPE_LABELS[' + changeType + ']') } [elasticsearch-${versionForIds}-${changeType}]
|
||||
<% for (team in changelogsByTypeByArea[changeType].keySet()) {
|
||||
print "\n${team}:\n";
|
||||
|
||||
for (change in changelogsByTypeByArea[changeType][team]) {
|
||||
print "* ${change.summary} [#${change.pr}](https://github.com/elastic/elasticsearch/pull/${change.pr})"
|
||||
if (change.issues != null && change.issues.empty == false) {
|
||||
print change.issues.size() == 1 ? " (issue: " : " (issues: "
|
||||
print change.issues.collect { "[#${it}](https://github.com/elastic/elasticsearch/issues/${it})" }.join(", ")
|
||||
print ")"
|
||||
}
|
||||
print "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
[[es-release-notes]]
|
||||
= Release notes
|
||||
|
||||
[partintro]
|
||||
--
|
||||
|
||||
This section summarizes the changes in each release.
|
||||
|
||||
<% versions.each { print "* <<release-notes-${ it }>>\n" } %>
|
||||
--
|
||||
|
||||
<% includeVersions.each { print "include::release-notes/${ it }.asciidoc[]\n" } %>
|
|
@ -1,45 +0,0 @@
|
|||
<%
|
||||
def unqualifiedVersion = version.withoutQualifier()
|
||||
%>[[release-notes-$unqualifiedVersion]]
|
||||
== {es} version ${unqualifiedVersion}
|
||||
<% if (version.isSnapshot()) { %>
|
||||
coming[$unqualifiedVersion]
|
||||
<% } %>
|
||||
Also see <<breaking-changes-${ version.major }.${ version.minor },Breaking changes in ${ version.major }.${ version.minor }>>.
|
||||
<% if (changelogsByTypeByArea["security"] != null) { %>
|
||||
[discrete]
|
||||
[[security-updates-${unqualifiedVersion}]]
|
||||
=== Security updates
|
||||
|
||||
<% for (change in changelogsByTypeByArea.remove("security").remove("_all_")) {
|
||||
print "* ${change.summary}\n"
|
||||
}
|
||||
}
|
||||
if (changelogsByTypeByArea["known-issue"] != null) { %>
|
||||
[discrete]
|
||||
[[known-issues-${unqualifiedVersion}]]
|
||||
=== Known issues
|
||||
|
||||
<% for (change in changelogsByTypeByArea.remove("known-issue").remove("_all_")) {
|
||||
print "* ${change.summary}\n"
|
||||
}
|
||||
}
|
||||
for (changeType in changelogsByTypeByArea.keySet()) { %>
|
||||
[[${ changeType }-${ unqualifiedVersion }]]
|
||||
[float]
|
||||
=== ${ TYPE_LABELS.getOrDefault(changeType, 'No mapping for TYPE_LABELS[' + changeType + ']') }
|
||||
<% for (team in changelogsByTypeByArea[changeType].keySet()) {
|
||||
print "\n${team}::\n";
|
||||
|
||||
for (change in changelogsByTypeByArea[changeType][team]) {
|
||||
print "* ${change.summary} {es-pull}${change.pr}[#${change.pr}]"
|
||||
if (change.issues != null && change.issues.empty == false) {
|
||||
print change.issues.size() == 1 ? " (issue: " : " (issues: "
|
||||
print change.issues.collect { "{es-issue}${it}[#${it}]" }.join(", ")
|
||||
print ")"
|
||||
}
|
||||
print "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n\n"
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class BreakingChangesGeneratorTest {
|
||||
|
||||
/**
|
||||
* Check that the breaking changes can be correctly generated.
|
||||
*/
|
||||
@Test
|
||||
public void generateIndexFile_rendersCorrectMarkup() throws Exception {
|
||||
// given:
|
||||
final String template = getResource("/templates/breaking-changes.asciidoc");
|
||||
final String expectedOutput = getResource(
|
||||
"/org/elasticsearch/gradle/internal/release/BreakingChangesGeneratorTest.generateMigrationFile.asciidoc"
|
||||
);
|
||||
|
||||
final List<ChangelogEntry> entries = getEntries();
|
||||
|
||||
// when:
|
||||
final String actualOutput = BreakingChangesGenerator.generateMigrationFile(
|
||||
QualifiedVersion.of("8.4.0-SNAPSHOT"),
|
||||
template,
|
||||
entries
|
||||
);
|
||||
|
||||
// then:
|
||||
assertThat(actualOutput, equalTo(expectedOutput));
|
||||
}
|
||||
|
||||
private List<ChangelogEntry> getEntries() {
|
||||
ChangelogEntry entry1 = new ChangelogEntry();
|
||||
ChangelogEntry.Breaking breaking1 = new ChangelogEntry.Breaking();
|
||||
entry1.setBreaking(breaking1);
|
||||
|
||||
breaking1.setNotable(true);
|
||||
breaking1.setTitle("Breaking change number 1");
|
||||
breaking1.setArea("API");
|
||||
breaking1.setDetails("Breaking change details 1");
|
||||
breaking1.setImpact("Breaking change impact description 1");
|
||||
|
||||
ChangelogEntry entry2 = new ChangelogEntry();
|
||||
ChangelogEntry.Breaking breaking2 = new ChangelogEntry.Breaking();
|
||||
entry2.setBreaking(breaking2);
|
||||
|
||||
breaking2.setNotable(true);
|
||||
breaking2.setTitle("Breaking change number 2");
|
||||
breaking2.setArea("Cluster and node setting");
|
||||
breaking2.setDetails("Breaking change details 2");
|
||||
breaking2.setImpact("Breaking change impact description 2");
|
||||
|
||||
ChangelogEntry entry3 = new ChangelogEntry();
|
||||
ChangelogEntry.Breaking breaking3 = new ChangelogEntry.Breaking();
|
||||
entry3.setBreaking(breaking3);
|
||||
|
||||
breaking3.setNotable(false);
|
||||
breaking3.setTitle("Breaking change number 3");
|
||||
breaking3.setArea("Transform");
|
||||
breaking3.setDetails("Breaking change details 3");
|
||||
breaking3.setImpact("Breaking change impact description 3");
|
||||
|
||||
ChangelogEntry entry4 = new ChangelogEntry();
|
||||
ChangelogEntry.Breaking breaking4 = new ChangelogEntry.Breaking();
|
||||
entry4.setBreaking(breaking4);
|
||||
|
||||
breaking4.setNotable(true);
|
||||
breaking4.setTitle("Breaking change number 4");
|
||||
breaking4.setArea("Cluster and node setting");
|
||||
breaking4.setDetails("Breaking change details 4");
|
||||
breaking4.setImpact("Breaking change impact description 4");
|
||||
breaking4.setEssSettingChange(true);
|
||||
|
||||
ChangelogEntry entry5 = new ChangelogEntry();
|
||||
ChangelogEntry.Deprecation deprecation5 = new ChangelogEntry.Deprecation();
|
||||
entry5.setDeprecation(deprecation5);
|
||||
|
||||
deprecation5.setNotable(true);
|
||||
deprecation5.setTitle("Deprecation change number 5");
|
||||
deprecation5.setArea("Cluster and node setting");
|
||||
deprecation5.setDetails("Deprecation change details 5");
|
||||
deprecation5.setImpact("Deprecation change impact description 5");
|
||||
deprecation5.setEssSettingChange(false);
|
||||
|
||||
ChangelogEntry entry6 = new ChangelogEntry();
|
||||
ChangelogEntry.Deprecation deprecation6 = new ChangelogEntry.Deprecation();
|
||||
entry6.setDeprecation(deprecation6);
|
||||
|
||||
deprecation6.setNotable(true);
|
||||
deprecation6.setTitle("Deprecation change number 6");
|
||||
deprecation6.setArea("Cluster and node setting");
|
||||
deprecation6.setDetails("Deprecation change details 6");
|
||||
deprecation6.setImpact("Deprecation change impact description 6");
|
||||
deprecation6.setEssSettingChange(false);
|
||||
|
||||
ChangelogEntry entry7 = new ChangelogEntry();
|
||||
ChangelogEntry.Deprecation deprecation7 = new ChangelogEntry.Deprecation();
|
||||
entry7.setDeprecation(deprecation7);
|
||||
|
||||
deprecation7.setNotable(false);
|
||||
deprecation7.setTitle("Deprecation change number 7");
|
||||
deprecation7.setArea("Cluster and node setting");
|
||||
deprecation7.setDetails("Deprecation change details 7");
|
||||
deprecation7.setImpact("Deprecation change impact description 7");
|
||||
deprecation7.setEssSettingChange(false);
|
||||
|
||||
return List.of(entry1, entry2, entry3, entry4, entry5, entry6, entry7);
|
||||
}
|
||||
|
||||
private String getResource(String name) throws Exception {
|
||||
return Files.readString(Paths.get(Objects.requireNonNull(this.getClass().getResource(name)).toURI()), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
|
@ -9,38 +9,12 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.aMapWithSize;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class GenerateReleaseNotesTaskTest {
|
||||
private GitWrapper gitWrapper;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.gitWrapper = mock(GitWrapper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the task does not update git tags if the current version is a snapshot of the first patch release.
|
||||
*/
|
||||
|
@ -88,250 +62,4 @@ public class GenerateReleaseNotesTaskTest {
|
|||
public void needsGitTags_withLaterAlphaRelease_returnsFalse() {
|
||||
assertThat(GenerateReleaseNotesTask.needsGitTags("8.0.0-alpha2"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that partitioning changelog files when the current version is a snapshot returns a map with a single entry.
|
||||
*/
|
||||
@Test
|
||||
public void partitionFiles_withSnapshot_returnsSingleMapping() {
|
||||
// when:
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = GenerateReleaseNotesTask.partitionFilesByVersion(
|
||||
gitWrapper,
|
||||
"8.0.0-SNAPSHOT",
|
||||
Set.of(new File("docs/changelog/1234.yaml"))
|
||||
);
|
||||
|
||||
// then:
|
||||
assertThat(partitionedFiles, aMapWithSize(1));
|
||||
assertThat(
|
||||
partitionedFiles,
|
||||
hasEntry(equalTo(QualifiedVersion.of("8.0.0-SNAPSHOT")), hasItem(new File("docs/changelog/1234.yaml")))
|
||||
);
|
||||
verifyNoMoreInteractions(gitWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that partitioning changelog files when the current version is the first release
|
||||
* in a minor series returns a map with a single entry.
|
||||
*/
|
||||
@Test
|
||||
public void partitionFiles_withFirstRevision_returnsSingleMapping() {
|
||||
// when:
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = GenerateReleaseNotesTask.partitionFilesByVersion(
|
||||
gitWrapper,
|
||||
"8.5.0",
|
||||
Set.of(new File("docs/changelog/1234.yaml"))
|
||||
);
|
||||
|
||||
// then:
|
||||
assertThat(partitionedFiles, aMapWithSize(1));
|
||||
assertThat(partitionedFiles, hasEntry(equalTo(QualifiedVersion.of("8.5.0")), hasItem(new File("docs/changelog/1234.yaml"))));
|
||||
verifyNoMoreInteractions(gitWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that partitioning changelog files when the current version is the first alpha prerelease returns a map with a single entry.
|
||||
*/
|
||||
@Test
|
||||
public void partitionFiles_withFirstAlpha_returnsSingleMapping() {
|
||||
// when:
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = GenerateReleaseNotesTask.partitionFilesByVersion(
|
||||
gitWrapper,
|
||||
"8.0.0-alpha1",
|
||||
Set.of(new File("docs/changelog/1234.yaml"))
|
||||
);
|
||||
|
||||
// then:
|
||||
assertThat(partitionedFiles, aMapWithSize(1));
|
||||
assertThat(partitionedFiles, hasEntry(equalTo(QualifiedVersion.of("8.0.0-alpha1")), hasItem(new File("docs/changelog/1234.yaml"))));
|
||||
verifyNoMoreInteractions(gitWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that when deriving a lit of versions from git tags, the current unreleased version is included.
|
||||
*/
|
||||
@Test
|
||||
public void getVersions_includesCurrentAndPreviousVersion() {
|
||||
// given:
|
||||
when(gitWrapper.listVersions(anyString())).thenReturn(
|
||||
Stream.of("8.0.0-alpha1", "8.0.0-alpha2", "8.0.0-beta1", "8.0.0-beta2", "8.0.0-beta3", "8.0.0-rc1", "8.0.0", "8.0.1", "8.1.0")
|
||||
.map(QualifiedVersion::of)
|
||||
);
|
||||
|
||||
// when:
|
||||
Set<QualifiedVersion> versions = GenerateReleaseNotesTask.getVersions(gitWrapper, "8.3.0-SNAPSHOT");
|
||||
|
||||
// then:
|
||||
assertThat(
|
||||
versions,
|
||||
containsInAnyOrder(
|
||||
Stream.of(
|
||||
"8.0.0-alpha1",
|
||||
"8.0.0-alpha2",
|
||||
"8.0.0-beta1",
|
||||
"8.0.0-beta2",
|
||||
"8.0.0-beta3",
|
||||
"8.0.0-rc1",
|
||||
"8.0.0",
|
||||
"8.0.1",
|
||||
"8.1.0",
|
||||
"8.2.0",
|
||||
"8.3.0-SNAPSHOT"
|
||||
).map(QualifiedVersion::of).toArray(QualifiedVersion[]::new)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that when deriving a list of major.minor versions from git tags, the current unreleased version is included,
|
||||
* but any higher version numbers are not.
|
||||
*/
|
||||
@Test
|
||||
public void getMinorVersions_includesCurrentButNotFutureVersions() {
|
||||
// given:
|
||||
when(gitWrapper.listVersions(anyString())).thenReturn(
|
||||
Stream.of("8.0.0-alpha1", "8.0.0-alpha2", "8.0.0", "8.0.1", "8.1.0", "8.2.0", "8.2.1", "8.3.0", "8.3.1", "8.4.0")
|
||||
.map(QualifiedVersion::of)
|
||||
);
|
||||
|
||||
// when:
|
||||
Set<QualifiedVersion> versions = GenerateReleaseNotesTask.getVersions(gitWrapper, "8.3.0-SNAPSHOT");
|
||||
Set<MinorVersion> minorVersions = GenerateReleaseNotesTask.getMinorVersions(versions);
|
||||
|
||||
// then:
|
||||
assertThat(
|
||||
minorVersions,
|
||||
containsInAnyOrder(new MinorVersion(8, 0), new MinorVersion(8, 1), new MinorVersion(8, 2), new MinorVersion(8, 3))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the task partitions the list of files correctly by version for a prerelease.
|
||||
*/
|
||||
@Test
|
||||
public void partitionFiles_withPrerelease_correctlyGroupsByPrereleaseVersion() {
|
||||
// given:
|
||||
when(gitWrapper.listVersions(anyString())).thenReturn(
|
||||
Stream.of("8.0.0-alpha1", "8.0.0-alpha2", "8.0.0-beta1", "8.0.0-beta2", "8.0.0-beta3", "8.0.0-rc1", "8.0.0")
|
||||
.map(QualifiedVersion::of)
|
||||
);
|
||||
when(gitWrapper.listFiles(eq("v8.0.0-alpha1"), anyString())).thenReturn(
|
||||
Stream.of("docs/changelog/1_1234.yaml", "docs/changelog/1_5678.yaml")
|
||||
);
|
||||
when(gitWrapper.listFiles(eq("v8.0.0-alpha2"), anyString())).thenReturn(
|
||||
Stream.of("docs/changelog/2_1234.yaml", "docs/changelog/2_5678.yaml")
|
||||
);
|
||||
|
||||
Set<File> allFiles = Set.of(
|
||||
new File("docs/changelog/1_1234.yaml"),
|
||||
new File("docs/changelog/1_5678.yaml"),
|
||||
new File("docs/changelog/2_1234.yaml"),
|
||||
new File("docs/changelog/2_5678.yaml"),
|
||||
new File("docs/changelog/3_1234.yaml"),
|
||||
new File("docs/changelog/3_5678.yaml")
|
||||
);
|
||||
|
||||
// when:
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = GenerateReleaseNotesTask.partitionFilesByVersion(
|
||||
gitWrapper,
|
||||
"8.0.0-beta1",
|
||||
allFiles
|
||||
);
|
||||
|
||||
// then:
|
||||
verify(gitWrapper).listVersions("v8.0*");
|
||||
verify(gitWrapper).listFiles("v8.0.0-alpha1", "docs/changelog");
|
||||
verify(gitWrapper).listFiles("v8.0.0-alpha2", "docs/changelog");
|
||||
|
||||
assertThat(
|
||||
partitionedFiles,
|
||||
allOf(
|
||||
aMapWithSize(3),
|
||||
hasKey(QualifiedVersion.of("8.0.0-alpha1")),
|
||||
hasKey(QualifiedVersion.of("8.0.0-alpha2")),
|
||||
hasKey(QualifiedVersion.of("8.0.0-beta1"))
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(
|
||||
partitionedFiles,
|
||||
allOf(
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.0-alpha1")),
|
||||
containsInAnyOrder(new File("docs/changelog/1_1234.yaml"), new File("docs/changelog/1_5678.yaml"))
|
||||
),
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.0-alpha2")),
|
||||
containsInAnyOrder(new File("docs/changelog/2_1234.yaml"), new File("docs/changelog/2_5678.yaml"))
|
||||
),
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.0-beta1")),
|
||||
containsInAnyOrder(new File("docs/changelog/3_1234.yaml"), new File("docs/changelog/3_5678.yaml"))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the task partitions the list of files correctly by version for a patch release.
|
||||
*/
|
||||
@Test
|
||||
public void partitionFiles_withPatchRelease_correctlyGroupsByPatchVersion() {
|
||||
// given:
|
||||
when(gitWrapper.listVersions(anyString())).thenReturn(
|
||||
Stream.of("8.0.0-alpha1", "8.0.0-alpha2", "8.0.0-beta1", "8.0.0-rc1", "8.0.0", "8.0.1", "8.0.2", "8.1.0")
|
||||
.map(QualifiedVersion::of)
|
||||
);
|
||||
when(gitWrapper.listFiles(eq("v8.0.0"), anyString())).thenReturn(
|
||||
Stream.of("docs/changelog/1_1234.yaml", "docs/changelog/1_5678.yaml")
|
||||
);
|
||||
when(gitWrapper.listFiles(eq("v8.0.1"), anyString())).thenReturn(
|
||||
Stream.of("docs/changelog/2_1234.yaml", "docs/changelog/2_5678.yaml")
|
||||
);
|
||||
|
||||
Set<File> allFiles = Set.of(
|
||||
new File("docs/changelog/1_1234.yaml"),
|
||||
new File("docs/changelog/1_5678.yaml"),
|
||||
new File("docs/changelog/2_1234.yaml"),
|
||||
new File("docs/changelog/2_5678.yaml"),
|
||||
new File("docs/changelog/3_1234.yaml"),
|
||||
new File("docs/changelog/3_5678.yaml")
|
||||
);
|
||||
|
||||
// when:
|
||||
Map<QualifiedVersion, Set<File>> partitionedFiles = GenerateReleaseNotesTask.partitionFilesByVersion(gitWrapper, "8.0.2", allFiles);
|
||||
|
||||
// then:
|
||||
verify(gitWrapper).listVersions("v8.0*");
|
||||
verify(gitWrapper).listFiles("v8.0.0", "docs/changelog");
|
||||
verify(gitWrapper).listFiles("v8.0.1", "docs/changelog");
|
||||
|
||||
assertThat(
|
||||
partitionedFiles,
|
||||
allOf(
|
||||
aMapWithSize(3),
|
||||
hasKey(QualifiedVersion.of("8.0.0")),
|
||||
hasKey(QualifiedVersion.of("8.0.1")),
|
||||
hasKey(QualifiedVersion.of("8.0.2"))
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(
|
||||
partitionedFiles,
|
||||
allOf(
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.0")),
|
||||
containsInAnyOrder(new File("docs/changelog/1_1234.yaml"), new File("docs/changelog/1_5678.yaml"))
|
||||
),
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.1")),
|
||||
containsInAnyOrder(new File("docs/changelog/2_1234.yaml"), new File("docs/changelog/2_5678.yaml"))
|
||||
),
|
||||
hasEntry(
|
||||
equalTo(QualifiedVersion.of("8.0.2")),
|
||||
containsInAnyOrder(new File("docs/changelog/3_1234.yaml"), new File("docs/changelog/3_5678.yaml"))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class ReleaseHighlightsGeneratorTest {
|
||||
|
||||
/**
|
||||
* Check that the release highlights can be correctly generated when there are no highlights.
|
||||
*/
|
||||
@Test
|
||||
public void generateFile_withNoHighlights_rendersCorrectMarkup() throws Exception {
|
||||
// given:
|
||||
final String template = getResource("/templates/release-highlights.asciidoc");
|
||||
final String expectedOutput = getResource(
|
||||
"/org/elasticsearch/gradle/internal/release/ReleaseHighlightsGeneratorTest.noHighlights.generateFile.asciidoc"
|
||||
);
|
||||
|
||||
// when:
|
||||
final String actualOutput = ReleaseHighlightsGenerator.generateFile(QualifiedVersion.of("8.4.0-SNAPSHOT"), template, List.of());
|
||||
|
||||
// then:
|
||||
assertThat(actualOutput, equalTo(expectedOutput));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the release highlights can be correctly generated.
|
||||
*/
|
||||
@Test
|
||||
public void generateFile_rendersCorrectMarkup() throws Exception {
|
||||
// given:
|
||||
final String template = getResource("/templates/release-highlights.asciidoc");
|
||||
final String expectedOutput = getResource(
|
||||
"/org/elasticsearch/gradle/internal/release/ReleaseHighlightsGeneratorTest.generateFile.asciidoc"
|
||||
);
|
||||
|
||||
final List<ChangelogEntry> entries = getEntries();
|
||||
|
||||
// when:
|
||||
final String actualOutput = ReleaseHighlightsGenerator.generateFile(QualifiedVersion.of("8.4.0-SNAPSHOT"), template, entries);
|
||||
|
||||
// then:
|
||||
assertThat(actualOutput, equalTo(expectedOutput));
|
||||
}
|
||||
|
||||
private List<ChangelogEntry> getEntries() {
|
||||
ChangelogEntry entry123 = makeChangelogEntry(123, true);
|
||||
ChangelogEntry entry456 = makeChangelogEntry(456, true);
|
||||
ChangelogEntry entry789 = makeChangelogEntry(789, false);
|
||||
// Return unordered list, to test correct re-ordering
|
||||
return List.of(entry456, entry123, entry789);
|
||||
}
|
||||
|
||||
private ChangelogEntry makeChangelogEntry(int pr, boolean notable) {
|
||||
ChangelogEntry entry = new ChangelogEntry();
|
||||
entry.setPr(pr);
|
||||
ChangelogEntry.Highlight highlight = new ChangelogEntry.Highlight();
|
||||
entry.setHighlight(highlight);
|
||||
|
||||
highlight.setNotable(notable);
|
||||
highlight.setTitle("Notable release highlight number " + pr);
|
||||
highlight.setBody("Notable release body number " + pr);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private String getResource(String name) throws Exception {
|
||||
return Files.readString(Paths.get(Objects.requireNonNull(this.getClass().getResource(name)).toURI()), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
|
@ -15,67 +15,115 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.gradle.internal.release.GenerateReleaseNotesTask.getSortedBundlesWithUniqueChangelogs;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class ReleaseNotesGeneratorTest {
|
||||
|
||||
/**
|
||||
* Check that the release notes can be correctly generated.
|
||||
*/
|
||||
@Test
|
||||
public void generateFile_rendersCorrectMarkup() throws Exception {
|
||||
// given:
|
||||
final String template = getResource("/templates/release-notes.asciidoc");
|
||||
final String expectedOutput = getResource(
|
||||
"/org/elasticsearch/gradle/internal/release/ReleaseNotesGeneratorTest.generateFile.asciidoc"
|
||||
);
|
||||
private static final List<String> CHANGE_TYPES = List.of(
|
||||
"breaking",
|
||||
"breaking-java",
|
||||
"bug",
|
||||
"fixes",
|
||||
"deprecation",
|
||||
"enhancement",
|
||||
"feature",
|
||||
"features-enhancements",
|
||||
"new-aggregation",
|
||||
"regression",
|
||||
"upgrade"
|
||||
);
|
||||
|
||||
final Set<ChangelogEntry> entries = getEntries();
|
||||
@Test
|
||||
public void generateFile_index_rendersCorrectMarkup() throws Exception {
|
||||
testTemplate("index.md");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateFile_index_noHighlights_rendersCorrectMarkup() throws Exception {
|
||||
var bundles = getBundles();
|
||||
bundles = bundles.stream().filter(b -> false == b.version().equals("9.1.0")).toList();
|
||||
|
||||
testTemplate("index.md", "index.no-highlights.md", bundles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateFile_index_noChanges_rendersCorrectMarkup() throws Exception {
|
||||
var bundles = new ArrayList<ChangelogBundle>();
|
||||
|
||||
testTemplate("index.md", "index.no-changes.md", bundles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateFile_breakingChanges_rendersCorrectMarkup() throws Exception {
|
||||
testTemplate("breaking-changes.md");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateFile_deprecations_rendersCorrectMarkup() throws Exception {
|
||||
testTemplate("deprecations.md");
|
||||
}
|
||||
|
||||
public void testTemplate(String templateFilename) throws Exception {
|
||||
testTemplate(templateFilename, templateFilename, null);
|
||||
}
|
||||
|
||||
public void testTemplate(String templateFilename, String outputFilename) throws Exception {
|
||||
testTemplate(templateFilename, outputFilename, null);
|
||||
}
|
||||
|
||||
public void testTemplate(String templateFilename, String outputFilename, List<ChangelogBundle> bundles) throws Exception {
|
||||
// given:
|
||||
final String template = getResource("/templates/" + templateFilename);
|
||||
final String expectedOutput = getResource("/org/elasticsearch/gradle/internal/release/ReleaseNotesGeneratorTest." + outputFilename);
|
||||
|
||||
if (bundles == null) {
|
||||
bundles = getBundles();
|
||||
}
|
||||
|
||||
bundles = getSortedBundlesWithUniqueChangelogs(bundles);
|
||||
|
||||
// when:
|
||||
final String actualOutput = ReleaseNotesGenerator.generateFile(template, QualifiedVersion.of("8.2.0-SNAPSHOT"), entries);
|
||||
final String actualOutput = ReleaseNotesGenerator.generateFile(template, bundles);
|
||||
|
||||
// then:
|
||||
assertThat(actualOutput, equalTo(expectedOutput));
|
||||
}
|
||||
|
||||
private Set<ChangelogEntry> getEntries() {
|
||||
final Set<ChangelogEntry> entries = new HashSet<>();
|
||||
entries.addAll(buildEntries(1, 2));
|
||||
entries.addAll(buildEntries(2, 2));
|
||||
entries.addAll(buildEntries(3, 2));
|
||||
private List<ChangelogBundle> getBundles() {
|
||||
List<ChangelogBundle> bundles = new ArrayList<>();
|
||||
|
||||
// Security issues are presented first in the notes
|
||||
final ChangelogEntry securityEntry = new ChangelogEntry();
|
||||
securityEntry.setArea("Security");
|
||||
securityEntry.setType("security");
|
||||
securityEntry.setSummary("Test security issue");
|
||||
entries.add(securityEntry);
|
||||
for (int i = 0; i < CHANGE_TYPES.size(); i++) {
|
||||
bundles.add(
|
||||
new ChangelogBundle(
|
||||
"9.0." + i,
|
||||
i != CHANGE_TYPES.size() - 1,
|
||||
"2025-05-16T00:00:" + String.format("%d", 10 + i),
|
||||
buildEntries(i, 2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// known issues are presented after security issues
|
||||
final ChangelogEntry knownIssue = new ChangelogEntry();
|
||||
knownIssue.setArea("Search");
|
||||
knownIssue.setType("known-issue");
|
||||
knownIssue.setSummary("Test known issue");
|
||||
entries.add(knownIssue);
|
||||
final List<ChangelogEntry> entries = new ArrayList<>();
|
||||
entries.add(makeHighlightsEntry(51, false));
|
||||
entries.add(makeHighlightsEntry(50, true));
|
||||
entries.add(makeHighlightsEntry(52, true));
|
||||
|
||||
return entries;
|
||||
bundles.add(new ChangelogBundle("9.1.0", false, "2025-05-17T00:00:00", entries));
|
||||
|
||||
return bundles;
|
||||
}
|
||||
|
||||
private List<ChangelogEntry> buildEntries(int seed, int count) {
|
||||
// Sample of possible areas from `changelog-schema.json`
|
||||
final List<String> areas = List.of("Aggregation", "Cluster", "Indices", "Mappings", "Search", "Security");
|
||||
// Possible change types, with `breaking`, `breaking-java`, `known-issue` and `security` removed.
|
||||
final List<String> types = List.of("bug", "deprecation", "enhancement", "feature", "new-aggregation", "regression", "upgrade");
|
||||
|
||||
final String area = areas.get(seed % areas.size());
|
||||
final String type = types.get(seed % types.size());
|
||||
final String type = CHANGE_TYPES.get(seed % CHANGE_TYPES.size());
|
||||
|
||||
final List<ChangelogEntry> entries = new ArrayList<>(count);
|
||||
|
||||
|
@ -101,6 +149,30 @@ public class ReleaseNotesGeneratorTest {
|
|||
return entries;
|
||||
}
|
||||
|
||||
private List<ChangelogEntry> getHighlightsEntries() {
|
||||
ChangelogEntry entry123 = makeHighlightsEntry(123, true);
|
||||
ChangelogEntry entry456 = makeHighlightsEntry(456, true);
|
||||
ChangelogEntry entry789 = makeHighlightsEntry(789, false);
|
||||
// Return unordered list, to test correct re-ordering
|
||||
return List.of(entry456, entry123, entry789);
|
||||
}
|
||||
|
||||
private ChangelogEntry makeHighlightsEntry(int pr, boolean notable) {
|
||||
ChangelogEntry entry = new ChangelogEntry();
|
||||
entry.setPr(pr);
|
||||
ChangelogEntry.Highlight highlight = new ChangelogEntry.Highlight();
|
||||
entry.setHighlight(highlight);
|
||||
|
||||
highlight.setNotable(notable);
|
||||
highlight.setTitle((notable ? "[Notable] " : "") + "Release highlight number " + pr);
|
||||
highlight.setBody("Release highlight body number " + pr);
|
||||
entry.setType("feature");
|
||||
entry.setArea("Search");
|
||||
entry.setSummary("");
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private String getResource(String name) throws Exception {
|
||||
return Files.readString(Paths.get(Objects.requireNonNull(this.getClass().getResource(name)).toURI()), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.release;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class ReleaseNotesIndexGeneratorTest {
|
||||
|
||||
/**
|
||||
* Check that a release notes index can be generated.
|
||||
*/
|
||||
@Test
|
||||
public void generateFile_rendersCorrectMarkup() throws Exception {
|
||||
// given:
|
||||
final Set<QualifiedVersion> versions = Stream.of(
|
||||
"8.0.0-alpha1",
|
||||
"8.0.0-beta2",
|
||||
"8.0.0-rc3",
|
||||
"8.0.0",
|
||||
"8.0.1",
|
||||
"8.0.2",
|
||||
"8.1.0",
|
||||
"8.1.1",
|
||||
"8.2.0-SNAPSHOT"
|
||||
).map(QualifiedVersion::of).collect(Collectors.toSet());
|
||||
|
||||
final String template = getResource("/templates/release-notes-index.asciidoc");
|
||||
final String expectedOutput = getResource(
|
||||
"/org/elasticsearch/gradle/internal/release/ReleaseNotesIndexGeneratorTest.generateFile.asciidoc"
|
||||
);
|
||||
|
||||
// when:
|
||||
final String actualOutput = ReleaseNotesIndexGenerator.generateFile(versions, template);
|
||||
|
||||
// then:
|
||||
assertThat(actualOutput, equalTo(expectedOutput));
|
||||
}
|
||||
|
||||
private String getResource(String name) throws Exception {
|
||||
return Files.readString(Paths.get(Objects.requireNonNull(this.getClass().getResource(name)).toURI()), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
[[migrating-8.4]]
|
||||
== Migrating to 8.4
|
||||
++++
|
||||
<titleabbrev>8.4</titleabbrev>
|
||||
++++
|
||||
|
||||
This section discusses the changes that you need to be aware of when migrating
|
||||
your application to {es} 8.4.
|
||||
|
||||
See also <<release-highlights>> and <<es-release-notes>>.
|
||||
|
||||
coming::[8.4.0]
|
||||
|
||||
|
||||
[discrete]
|
||||
[[breaking-changes-8.4]]
|
||||
=== Breaking changes
|
||||
|
||||
The following changes in {es} 8.4 might affect your applications
|
||||
and prevent them from operating normally.
|
||||
Before upgrading to 8.4, review these changes and take the described steps
|
||||
to mitigate the impact.
|
||||
|
||||
[discrete]
|
||||
[[breaking_84_api_changes]]
|
||||
==== API changes
|
||||
|
||||
[[breaking_change_number_1]]
|
||||
.Breaking change number 1
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Breaking change details 1
|
||||
|
||||
*Impact* +
|
||||
Breaking change impact description 1
|
||||
====
|
||||
|
||||
[discrete]
|
||||
[[breaking_84_cluster_and_node_setting_changes]]
|
||||
==== Cluster and node setting changes
|
||||
|
||||
[[breaking_change_number_2]]
|
||||
.Breaking change number 2
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Breaking change details 2
|
||||
|
||||
*Impact* +
|
||||
Breaking change impact description 2
|
||||
====
|
||||
|
||||
[[breaking_change_number_4]]
|
||||
.Breaking change number 4
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Breaking change details 4
|
||||
|
||||
*Impact* +
|
||||
Breaking change impact description 4
|
||||
====
|
||||
|
||||
[discrete]
|
||||
[[breaking_84_transform_changes]]
|
||||
==== Transform changes
|
||||
|
||||
[[breaking_change_number_3]]
|
||||
.Breaking change number 3
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Breaking change details 3
|
||||
|
||||
*Impact* +
|
||||
Breaking change impact description 3
|
||||
====
|
||||
|
||||
|
||||
[discrete]
|
||||
[[deprecated-8.4]]
|
||||
=== Deprecations
|
||||
|
||||
The following functionality has been deprecated in {es} 8.4
|
||||
and will be removed in a future version.
|
||||
While this won't have an immediate impact on your applications,
|
||||
we strongly encourage you to take the described steps to update your code
|
||||
after upgrading to 8.4.
|
||||
|
||||
To find out if you are using any deprecated functionality,
|
||||
enable <<deprecation-logging, deprecation logging>>.
|
||||
|
||||
[discrete]
|
||||
[[deprecations_84_cluster_and_node_setting]]
|
||||
==== Cluster and node setting deprecations
|
||||
|
||||
[[deprecation_change_number_5]]
|
||||
.Deprecation change number 5
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Deprecation change details 5
|
||||
|
||||
*Impact* +
|
||||
Deprecation change impact description 5
|
||||
====
|
||||
|
||||
[[deprecation_change_number_6]]
|
||||
.Deprecation change number 6
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Deprecation change details 6
|
||||
|
||||
*Impact* +
|
||||
Deprecation change impact description 6
|
||||
====
|
||||
|
||||
[discrete]
|
||||
[[deprecations_84_cluster_and_node_setting]]
|
||||
==== Cluster and node setting deprecations
|
||||
|
||||
[[deprecation_change_number_7]]
|
||||
.Deprecation change number 7
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Deprecation change details 7
|
||||
|
||||
*Impact* +
|
||||
Deprecation change impact description 7
|
||||
====
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
navigation_title: "Breaking changes"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html
|
||||
---
|
||||
|
||||
# Elasticsearch breaking changes [elasticsearch-breaking-changes]
|
||||
|
||||
Breaking changes can impact your Elastic applications, potentially disrupting normal operations. Before you upgrade, carefully review the Elasticsearch breaking changes and take the necessary steps to mitigate any issues.
|
||||
|
||||
If you are migrating from a version prior to version 9.0, you must first upgrade to the last 8.x version available. To learn how to upgrade, check out [Upgrade](docs-content://deploy-manage/upgrade.md).
|
||||
|
||||
% ## Next version [elasticsearch-nextversion-breaking-changes]
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.1.0
|
||||
```
|
||||
## 9.1.0 [elasticsearch-9.1.0-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.0.10
|
||||
```
|
||||
## 9.0.10 [elasticsearch-9.0.10-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.9 [elasticsearch-9.0.9-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.8 [elasticsearch-9.0.8-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.7 [elasticsearch-9.0.7-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.6 [elasticsearch-9.0.6-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.5 [elasticsearch-9.0.5-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.4 [elasticsearch-9.0.4-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.3 [elasticsearch-9.0.3-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.2 [elasticsearch-9.0.2-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.1 [elasticsearch-9.0.1-breaking-changes]
|
||||
|
||||
No breaking changes in this version.
|
||||
|
||||
## 9.0.0 [elasticsearch-900-breaking-changes]
|
||||
|
||||
Aggregation:
|
||||
* Test changelog entry 0_0 [#0](https://github.com/elastic/elasticsearch/pull/0) (issue: [#1](https://github.com/elastic/elasticsearch/issues/1))
|
||||
* Test changelog entry 0_1 [#2](https://github.com/elastic/elasticsearch/pull/2) (issues: [#3](https://github.com/elastic/elasticsearch/issues/3), [#4](https://github.com/elastic/elasticsearch/issues/4))
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
navigation_title: "Deprecations"
|
||||
---
|
||||
|
||||
# {{es}} deprecations [elasticsearch-deprecations]
|
||||
|
||||
Over time, certain Elastic functionality becomes outdated and is replaced or removed. To help with the transition, Elastic deprecates functionality for a period before removal, giving you time to update your applications.
|
||||
|
||||
Review the deprecated functionality for Elasticsearch. While deprecations have no immediate impact, we strongly encourage you update your implementation after you upgrade. To learn how to upgrade, check out [Upgrade](docs-content://deploy-manage/upgrade.md).
|
||||
|
||||
To give you insight into what deprecated features you’re using, {{es}}:
|
||||
|
||||
* Returns a `Warn` HTTP header whenever you submit a request that uses deprecated functionality.
|
||||
* [Logs deprecation warnings](docs-content://deploy-manage/monitor/logging-configuration/update-elasticsearch-logging-levels.md#deprecation-logging) when deprecated functionality is used.
|
||||
* [Provides a deprecation info API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-migration-deprecations) that scans a cluster’s configuration and mappings for deprecated functionality.
|
||||
|
||||
% ## Next version [elasticsearch-nextversion-deprecations]
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.1.0
|
||||
```
|
||||
## 9.1.0 [elasticsearch-9.1.0-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.0.10
|
||||
```
|
||||
## 9.0.10 [elasticsearch-9.0.10-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.9 [elasticsearch-9.0.9-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.8 [elasticsearch-9.0.8-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.7 [elasticsearch-9.0.7-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.6 [elasticsearch-9.0.6-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.5 [elasticsearch-9.0.5-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.4 [elasticsearch-9.0.4-deprecations]
|
||||
|
||||
Search:
|
||||
* Test changelog entry 4_0 [#4000](https://github.com/elastic/elasticsearch/pull/4000) (issue: [#4001](https://github.com/elastic/elasticsearch/issues/4001))
|
||||
* Test changelog entry 4_1 [#4002](https://github.com/elastic/elasticsearch/pull/4002) (issues: [#4003](https://github.com/elastic/elasticsearch/issues/4003), [#4004](https://github.com/elastic/elasticsearch/issues/4004))
|
||||
|
||||
|
||||
|
||||
## 9.0.3 [elasticsearch-9.0.3-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.2 [elasticsearch-9.0.2-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.1 [elasticsearch-9.0.1-deprecations]
|
||||
|
||||
No deprecations in this version.
|
||||
|
||||
## 9.0.0 [elasticsearch-900-deprecations]
|
||||
|
||||
No deprecations in this version.
|
|
@ -1,44 +0,0 @@
|
|||
[[release-notes-8.2.0]]
|
||||
== {es} version 8.2.0
|
||||
|
||||
coming[8.2.0]
|
||||
|
||||
Also see <<breaking-changes-8.2,Breaking changes in 8.2>>.
|
||||
|
||||
[discrete]
|
||||
[[security-updates-8.2.0]]
|
||||
=== Security updates
|
||||
|
||||
* Test security issue
|
||||
|
||||
[discrete]
|
||||
[[known-issues-8.2.0]]
|
||||
=== Known issues
|
||||
|
||||
* Test known issue
|
||||
|
||||
[[deprecation-8.2.0]]
|
||||
[float]
|
||||
=== Deprecations
|
||||
|
||||
Cluster::
|
||||
* Test changelog entry 1_0 {es-pull}1000[#1000] (issue: {es-issue}1001[#1001])
|
||||
* Test changelog entry 1_1 {es-pull}1002[#1002] (issues: {es-issue}1003[#1003], {es-issue}1004[#1004])
|
||||
|
||||
[[enhancement-8.2.0]]
|
||||
[float]
|
||||
=== Enhancements
|
||||
|
||||
Indices::
|
||||
* Test changelog entry 2_0 {es-pull}2000[#2000] (issue: {es-issue}2001[#2001])
|
||||
* Test changelog entry 2_1 {es-pull}2002[#2002] (issues: {es-issue}2003[#2003], {es-issue}2004[#2004])
|
||||
|
||||
[[feature-8.2.0]]
|
||||
[float]
|
||||
=== New features
|
||||
|
||||
Mappings::
|
||||
* Test changelog entry 3_0 {es-pull}3000[#3000] (issue: {es-issue}3001[#3001])
|
||||
* Test changelog entry 3_1 {es-pull}3002[#3002] (issues: {es-issue}3003[#3003], {es-issue}3004[#3004])
|
||||
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
navigation_title: "Elasticsearch"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/es-release-notes.html
|
||||
---
|
||||
|
||||
# Elasticsearch release notes [elasticsearch-release-notes]
|
||||
|
||||
Review the changes, fixes, and more in each version of Elasticsearch.
|
||||
|
||||
To check for security updates, go to [Security announcements for the Elastic stack](https://discuss.elastic.co/c/announcements/security-announcements/31).
|
||||
|
||||
% Release notes include only features, enhancements, and fixes. Add breaking changes, deprecations, and known issues to the applicable release notes sections.
|
||||
|
||||
% ## version.next [elasticsearch-next-release-notes]
|
||||
|
||||
% ### Features and enhancements [elasticsearch-next-features-enhancements]
|
||||
% *
|
||||
|
||||
% ### Fixes [elasticsearch-next-fixes]
|
||||
% *
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.1.0
|
||||
```
|
||||
## 9.1.0 [elasticsearch-9.1.0-release-notes]
|
||||
|
||||
### Highlights [elasticsearch-9.1.0-highlights]
|
||||
|
||||
::::{dropdown} [Notable] Release highlight number 50
|
||||
Release highlight body number 50
|
||||
::::
|
||||
|
||||
::::{dropdown} [Notable] Release highlight number 52
|
||||
Release highlight body number 52
|
||||
::::
|
||||
|
||||
::::{dropdown} Release highlight number 51
|
||||
Release highlight body number 51
|
||||
::::
|
||||
|
||||
### Features and enhancements [elasticsearch-9.1.0-features-enhancements]
|
||||
|
||||
Search:
|
||||
* [#51](https://github.com/elastic/elasticsearch/pull/51)
|
||||
* [#50](https://github.com/elastic/elasticsearch/pull/50)
|
||||
* [#52](https://github.com/elastic/elasticsearch/pull/52)
|
||||
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.0.10
|
||||
```
|
||||
## 9.0.10 [elasticsearch-9.0.10-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.10-features-enhancements]
|
||||
|
||||
Search:
|
||||
* Test changelog entry 10_0 [#10000](https://github.com/elastic/elasticsearch/pull/10000) (issue: [#10001](https://github.com/elastic/elasticsearch/issues/10001))
|
||||
* Test changelog entry 10_1 [#10002](https://github.com/elastic/elasticsearch/pull/10002) (issues: [#10003](https://github.com/elastic/elasticsearch/issues/10003), [#10004](https://github.com/elastic/elasticsearch/issues/10004))
|
||||
|
||||
|
||||
## 9.0.9 [elasticsearch-9.0.9-release-notes]
|
||||
|
||||
### Regressions [elasticsearch-9.0.9-regression]
|
||||
|
||||
Mappings:
|
||||
* Test changelog entry 9_0 [#9000](https://github.com/elastic/elasticsearch/pull/9000) (issue: [#9001](https://github.com/elastic/elasticsearch/issues/9001))
|
||||
* Test changelog entry 9_1 [#9002](https://github.com/elastic/elasticsearch/pull/9002) (issues: [#9003](https://github.com/elastic/elasticsearch/issues/9003), [#9004](https://github.com/elastic/elasticsearch/issues/9004))
|
||||
|
||||
|
||||
## 9.0.8 [elasticsearch-9.0.8-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.8-features-enhancements]
|
||||
|
||||
Indices:
|
||||
* Test changelog entry 8_0 [#8000](https://github.com/elastic/elasticsearch/pull/8000) (issue: [#8001](https://github.com/elastic/elasticsearch/issues/8001))
|
||||
* Test changelog entry 8_1 [#8002](https://github.com/elastic/elasticsearch/pull/8002) (issues: [#8003](https://github.com/elastic/elasticsearch/issues/8003), [#8004](https://github.com/elastic/elasticsearch/issues/8004))
|
||||
|
||||
|
||||
## 9.0.7 [elasticsearch-9.0.7-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.7-features-enhancements]
|
||||
|
||||
Cluster:
|
||||
* Test changelog entry 7_0 [#7000](https://github.com/elastic/elasticsearch/pull/7000) (issue: [#7001](https://github.com/elastic/elasticsearch/issues/7001))
|
||||
* Test changelog entry 7_1 [#7002](https://github.com/elastic/elasticsearch/pull/7002) (issues: [#7003](https://github.com/elastic/elasticsearch/issues/7003), [#7004](https://github.com/elastic/elasticsearch/issues/7004))
|
||||
|
||||
|
||||
## 9.0.6 [elasticsearch-9.0.6-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.6-features-enhancements]
|
||||
|
||||
Aggregation:
|
||||
* Test changelog entry 6_0 [#6000](https://github.com/elastic/elasticsearch/pull/6000) (issue: [#6001](https://github.com/elastic/elasticsearch/issues/6001))
|
||||
* Test changelog entry 6_1 [#6002](https://github.com/elastic/elasticsearch/pull/6002) (issues: [#6003](https://github.com/elastic/elasticsearch/issues/6003), [#6004](https://github.com/elastic/elasticsearch/issues/6004))
|
||||
|
||||
|
||||
## 9.0.5 [elasticsearch-9.0.5-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.5-features-enhancements]
|
||||
|
||||
Security:
|
||||
* Test changelog entry 5_0 [#5000](https://github.com/elastic/elasticsearch/pull/5000) (issue: [#5001](https://github.com/elastic/elasticsearch/issues/5001))
|
||||
* Test changelog entry 5_1 [#5002](https://github.com/elastic/elasticsearch/pull/5002) (issues: [#5003](https://github.com/elastic/elasticsearch/issues/5003), [#5004](https://github.com/elastic/elasticsearch/issues/5004))
|
||||
|
||||
|
||||
## 9.0.4 [elasticsearch-9.0.4-release-notes]
|
||||
|
||||
|
||||
## 9.0.3 [elasticsearch-9.0.3-release-notes]
|
||||
|
||||
### Fixes [elasticsearch-9.0.3-fixes]
|
||||
|
||||
Mappings:
|
||||
* Test changelog entry 3_0 [#3000](https://github.com/elastic/elasticsearch/pull/3000) (issue: [#3001](https://github.com/elastic/elasticsearch/issues/3001))
|
||||
* Test changelog entry 3_1 [#3002](https://github.com/elastic/elasticsearch/pull/3002) (issues: [#3003](https://github.com/elastic/elasticsearch/issues/3003), [#3004](https://github.com/elastic/elasticsearch/issues/3004))
|
||||
|
||||
|
||||
## 9.0.2 [elasticsearch-9.0.2-release-notes]
|
||||
|
||||
### Fixes [elasticsearch-9.0.2-fixes]
|
||||
|
||||
Indices:
|
||||
* Test changelog entry 2_0 [#2000](https://github.com/elastic/elasticsearch/pull/2000) (issue: [#2001](https://github.com/elastic/elasticsearch/issues/2001))
|
||||
* Test changelog entry 2_1 [#2002](https://github.com/elastic/elasticsearch/pull/2002) (issues: [#2003](https://github.com/elastic/elasticsearch/issues/2003), [#2004](https://github.com/elastic/elasticsearch/issues/2004))
|
||||
|
||||
|
||||
## 9.0.1 [elasticsearch-9.0.1-release-notes]
|
||||
|
||||
|
||||
## 9.0.0 [elasticsearch-900-release-notes]
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
navigation_title: "Elasticsearch"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/es-release-notes.html
|
||||
---
|
||||
|
||||
# Elasticsearch release notes [elasticsearch-release-notes]
|
||||
|
||||
Review the changes, fixes, and more in each version of Elasticsearch.
|
||||
|
||||
To check for security updates, go to [Security announcements for the Elastic stack](https://discuss.elastic.co/c/announcements/security-announcements/31).
|
||||
|
||||
% Release notes include only features, enhancements, and fixes. Add breaking changes, deprecations, and known issues to the applicable release notes sections.
|
||||
|
||||
% ## version.next [elasticsearch-next-release-notes]
|
||||
|
||||
% ### Features and enhancements [elasticsearch-next-features-enhancements]
|
||||
% *
|
||||
|
||||
% ### Fixes [elasticsearch-next-fixes]
|
||||
% *
|
|
@ -0,0 +1,105 @@
|
|||
---
|
||||
navigation_title: "Elasticsearch"
|
||||
mapped_pages:
|
||||
- https://www.elastic.co/guide/en/elasticsearch/reference/current/es-release-notes.html
|
||||
---
|
||||
|
||||
# Elasticsearch release notes [elasticsearch-release-notes]
|
||||
|
||||
Review the changes, fixes, and more in each version of Elasticsearch.
|
||||
|
||||
To check for security updates, go to [Security announcements for the Elastic stack](https://discuss.elastic.co/c/announcements/security-announcements/31).
|
||||
|
||||
% Release notes include only features, enhancements, and fixes. Add breaking changes, deprecations, and known issues to the applicable release notes sections.
|
||||
|
||||
% ## version.next [elasticsearch-next-release-notes]
|
||||
|
||||
% ### Features and enhancements [elasticsearch-next-features-enhancements]
|
||||
% *
|
||||
|
||||
% ### Fixes [elasticsearch-next-fixes]
|
||||
% *
|
||||
|
||||
```{applies_to}
|
||||
stack: coming 9.0.10
|
||||
```
|
||||
## 9.0.10 [elasticsearch-9.0.10-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.10-features-enhancements]
|
||||
|
||||
Search:
|
||||
* Test changelog entry 10_0 [#10000](https://github.com/elastic/elasticsearch/pull/10000) (issue: [#10001](https://github.com/elastic/elasticsearch/issues/10001))
|
||||
* Test changelog entry 10_1 [#10002](https://github.com/elastic/elasticsearch/pull/10002) (issues: [#10003](https://github.com/elastic/elasticsearch/issues/10003), [#10004](https://github.com/elastic/elasticsearch/issues/10004))
|
||||
|
||||
|
||||
## 9.0.9 [elasticsearch-9.0.9-release-notes]
|
||||
|
||||
### Regressions [elasticsearch-9.0.9-regression]
|
||||
|
||||
Mappings:
|
||||
* Test changelog entry 9_0 [#9000](https://github.com/elastic/elasticsearch/pull/9000) (issue: [#9001](https://github.com/elastic/elasticsearch/issues/9001))
|
||||
* Test changelog entry 9_1 [#9002](https://github.com/elastic/elasticsearch/pull/9002) (issues: [#9003](https://github.com/elastic/elasticsearch/issues/9003), [#9004](https://github.com/elastic/elasticsearch/issues/9004))
|
||||
|
||||
|
||||
## 9.0.8 [elasticsearch-9.0.8-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.8-features-enhancements]
|
||||
|
||||
Indices:
|
||||
* Test changelog entry 8_0 [#8000](https://github.com/elastic/elasticsearch/pull/8000) (issue: [#8001](https://github.com/elastic/elasticsearch/issues/8001))
|
||||
* Test changelog entry 8_1 [#8002](https://github.com/elastic/elasticsearch/pull/8002) (issues: [#8003](https://github.com/elastic/elasticsearch/issues/8003), [#8004](https://github.com/elastic/elasticsearch/issues/8004))
|
||||
|
||||
|
||||
## 9.0.7 [elasticsearch-9.0.7-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.7-features-enhancements]
|
||||
|
||||
Cluster:
|
||||
* Test changelog entry 7_0 [#7000](https://github.com/elastic/elasticsearch/pull/7000) (issue: [#7001](https://github.com/elastic/elasticsearch/issues/7001))
|
||||
* Test changelog entry 7_1 [#7002](https://github.com/elastic/elasticsearch/pull/7002) (issues: [#7003](https://github.com/elastic/elasticsearch/issues/7003), [#7004](https://github.com/elastic/elasticsearch/issues/7004))
|
||||
|
||||
|
||||
## 9.0.6 [elasticsearch-9.0.6-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.6-features-enhancements]
|
||||
|
||||
Aggregation:
|
||||
* Test changelog entry 6_0 [#6000](https://github.com/elastic/elasticsearch/pull/6000) (issue: [#6001](https://github.com/elastic/elasticsearch/issues/6001))
|
||||
* Test changelog entry 6_1 [#6002](https://github.com/elastic/elasticsearch/pull/6002) (issues: [#6003](https://github.com/elastic/elasticsearch/issues/6003), [#6004](https://github.com/elastic/elasticsearch/issues/6004))
|
||||
|
||||
|
||||
## 9.0.5 [elasticsearch-9.0.5-release-notes]
|
||||
|
||||
### Features and enhancements [elasticsearch-9.0.5-features-enhancements]
|
||||
|
||||
Security:
|
||||
* Test changelog entry 5_0 [#5000](https://github.com/elastic/elasticsearch/pull/5000) (issue: [#5001](https://github.com/elastic/elasticsearch/issues/5001))
|
||||
* Test changelog entry 5_1 [#5002](https://github.com/elastic/elasticsearch/pull/5002) (issues: [#5003](https://github.com/elastic/elasticsearch/issues/5003), [#5004](https://github.com/elastic/elasticsearch/issues/5004))
|
||||
|
||||
|
||||
## 9.0.4 [elasticsearch-9.0.4-release-notes]
|
||||
|
||||
|
||||
## 9.0.3 [elasticsearch-9.0.3-release-notes]
|
||||
|
||||
### Fixes [elasticsearch-9.0.3-fixes]
|
||||
|
||||
Mappings:
|
||||
* Test changelog entry 3_0 [#3000](https://github.com/elastic/elasticsearch/pull/3000) (issue: [#3001](https://github.com/elastic/elasticsearch/issues/3001))
|
||||
* Test changelog entry 3_1 [#3002](https://github.com/elastic/elasticsearch/pull/3002) (issues: [#3003](https://github.com/elastic/elasticsearch/issues/3003), [#3004](https://github.com/elastic/elasticsearch/issues/3004))
|
||||
|
||||
|
||||
## 9.0.2 [elasticsearch-9.0.2-release-notes]
|
||||
|
||||
### Fixes [elasticsearch-9.0.2-fixes]
|
||||
|
||||
Indices:
|
||||
* Test changelog entry 2_0 [#2000](https://github.com/elastic/elasticsearch/pull/2000) (issue: [#2001](https://github.com/elastic/elasticsearch/issues/2001))
|
||||
* Test changelog entry 2_1 [#2002](https://github.com/elastic/elasticsearch/pull/2002) (issues: [#2003](https://github.com/elastic/elasticsearch/issues/2003), [#2004](https://github.com/elastic/elasticsearch/issues/2004))
|
||||
|
||||
|
||||
## 9.0.1 [elasticsearch-9.0.1-release-notes]
|
||||
|
||||
|
||||
## 9.0.0 [elasticsearch-900-release-notes]
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
[[es-release-notes]]
|
||||
= Release notes
|
||||
|
||||
[partintro]
|
||||
--
|
||||
|
||||
This section summarizes the changes in each release.
|
||||
|
||||
* <<release-notes-8.2.0>>
|
||||
* <<release-notes-8.1.1>>
|
||||
* <<release-notes-8.1.0>>
|
||||
* <<release-notes-8.0.2>>
|
||||
* <<release-notes-8.0.1>>
|
||||
* <<release-notes-8.0.0>>
|
||||
* <<release-notes-8.0.0-rc3>>
|
||||
* <<release-notes-8.0.0-beta2>>
|
||||
* <<release-notes-8.0.0-alpha1>>
|
||||
|
||||
--
|
||||
|
||||
include::release-notes/8.2.0.asciidoc[]
|
||||
include::release-notes/8.1.1.asciidoc[]
|
||||
include::release-notes/8.1.0.asciidoc[]
|
||||
include::release-notes/8.0.2.asciidoc[]
|
||||
include::release-notes/8.0.1.asciidoc[]
|
||||
include::release-notes/8.0.0.asciidoc[]
|
||||
include::release-notes/8.0.0-rc3.asciidoc[]
|
||||
include::release-notes/8.0.0-beta2.asciidoc[]
|
||||
include::release-notes/8.0.0-alpha1.asciidoc[]
|
||||
|
2521
docs/release-notes/changelog-bundles/9.0.0.yml
Normal file
2521
docs/release-notes/changelog-bundles/9.0.0.yml
Normal file
File diff suppressed because it is too large
Load diff
148
docs/release-notes/changelog-bundles/9.0.1.yml
Normal file
148
docs/release-notes/changelog-bundles/9.0.1.yml
Normal file
|
@ -0,0 +1,148 @@
|
|||
version: 9.0.1
|
||||
released: true
|
||||
generated: 2025-05-06T19:39:27.474912Z
|
||||
changelogs:
|
||||
- pr: 125694
|
||||
summary: LTR score bounding
|
||||
area: Ranking
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 125922
|
||||
summary: Fix text structure NPE when fields in list have null value
|
||||
area: Machine Learning
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126273
|
||||
summary: Fix LTR rescorer with model alias
|
||||
area: Ranking
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126310
|
||||
summary: Add Issuer to failed SAML Signature validation logs when available
|
||||
area: Security
|
||||
type: enhancement
|
||||
issues:
|
||||
- 111022
|
||||
- pr: 126342
|
||||
summary: Enable sort optimization on float and `half_float`
|
||||
area: Search
|
||||
type: enhancement
|
||||
issues: []
|
||||
- pr: 126583
|
||||
summary: Cancel expired async search task when a remote returns its results
|
||||
area: CCS
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126605
|
||||
summary: Fix equality bug in `WaitForIndexColorStep`
|
||||
area: ILM+SLM
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126614
|
||||
summary: Fix join masking eval
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126637
|
||||
summary: Improve resiliency of `UpdateTimeSeriesRangeService`
|
||||
area: TSDB
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126686
|
||||
summary: Fix race condition in `RestCancellableNodeClient`
|
||||
area: Task Management
|
||||
type: bug
|
||||
issues:
|
||||
- 88201
|
||||
- pr: 126729
|
||||
summary: Use terminal reader in keystore add command
|
||||
area: Infra/CLI
|
||||
type: bug
|
||||
issues:
|
||||
- 98115
|
||||
- pr: 126778
|
||||
summary: Fix bbq quantization algorithm but for differently distributed components
|
||||
area: Vector Search
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126783
|
||||
summary: Fix shard size of initializing restored shard
|
||||
area: Allocation
|
||||
type: bug
|
||||
issues:
|
||||
- 105331
|
||||
- pr: 126806
|
||||
summary: Workaround max name limit imposed by Jackson 2.17
|
||||
area: Infra/Core
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126850
|
||||
summary: "[otel-data] Bump plugin version to release _metric_names_hash changes"
|
||||
area: Data streams
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126852
|
||||
summary: "Validation checks on paths allowed for 'files' entitlements. Restrict the paths we allow access to, forbidding plugins to specify/request entitlements for reading or writing to specific protected directories."
|
||||
area: Infra/Core
|
||||
type: enhancement
|
||||
issues: []
|
||||
- pr: 126858
|
||||
summary: Leverage threadpool schedule for inference api to avoid long running thread
|
||||
area: Machine Learning
|
||||
type: bug
|
||||
issues:
|
||||
- 126853
|
||||
- pr: 126884
|
||||
summary: Rare terms aggregation false **positive** fix
|
||||
area: Aggregations
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126889
|
||||
summary: Rework uniquify to not use iterators
|
||||
area: Infra/Core
|
||||
type: bug
|
||||
issues:
|
||||
- 126883
|
||||
- pr: 126911
|
||||
summary: Fix `vec_caps` to test for OS support too (on x64)
|
||||
area: Vector Search
|
||||
type: bug
|
||||
issues:
|
||||
- 126809
|
||||
- pr: 126930
|
||||
summary: Adding missing `onFailure` call for Inference API start model request
|
||||
area: Machine Learning
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 126990
|
||||
summary: "Fix: consider case sensitiveness differences in Windows/Unix-like filesystems for files entitlements"
|
||||
area: Infra/Core
|
||||
type: bug
|
||||
issues:
|
||||
- 127047
|
||||
- pr: 127146
|
||||
summary: Fix sneaky bug in single value query
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127225
|
||||
summary: Fix count optimization with pushable union types
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues:
|
||||
- 127200
|
||||
- pr: 127353
|
||||
summary: Updating tika to 2.9.3
|
||||
area: Ingest Node
|
||||
type: upgrade
|
||||
issues: []
|
||||
- pr: 127414
|
||||
summary: Fix npe when using source confirmed text query against missing field
|
||||
area: Search
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127527
|
||||
summary: "No, line noise isn't a valid ip"
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
135
docs/release-notes/changelog-bundles/9.0.2.yml
Normal file
135
docs/release-notes/changelog-bundles/9.0.2.yml
Normal file
|
@ -0,0 +1,135 @@
|
|||
version: 9.0.2
|
||||
released: false
|
||||
generated: 2025-05-22T15:14:00.768080Z
|
||||
changelogs:
|
||||
- pr: 126992
|
||||
summary: Add missing `outbound_network` entitlement to x-pack-core
|
||||
area: Infra/Core
|
||||
type: bug
|
||||
issues:
|
||||
- 127003
|
||||
- pr: 127009
|
||||
summary: "ESQL: Keep `DROP` attributes when resolving field names"
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues:
|
||||
- 126418
|
||||
- pr: 127337
|
||||
summary: Http proxy support in JWT realm
|
||||
area: Authentication
|
||||
type: enhancement
|
||||
issues:
|
||||
- 114956
|
||||
- pr: 127383
|
||||
summary: Don't push down filters on the right hand side of an inlinejoin
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127475
|
||||
summary: Remove dangling spaces wherever found
|
||||
area: Security
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127563
|
||||
summary: "ESQL: Avoid unintended attribute removal"
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues:
|
||||
- 127468
|
||||
- pr: 127658
|
||||
summary: Append all data to Chat Completion buffer
|
||||
area: Machine Learning
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127687
|
||||
summary: "ESQL: Fix alias removal in regex extraction with JOIN"
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues:
|
||||
- 127467
|
||||
- pr: 127752
|
||||
summary: Downsampling does not consider passthrough fields as dimensions
|
||||
area: Downsampling
|
||||
type: bug
|
||||
issues:
|
||||
- 125156
|
||||
- pr: 127798
|
||||
summary: Handle streaming request body in audit log
|
||||
area: Audit
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127824
|
||||
summary: Skip the validation when retrieving the index mode during reindexing a time series data stream
|
||||
area: TSDB
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127856
|
||||
summary: Fix services API Google Vertex AI Rerank location field requirement
|
||||
area: Machine Learning
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127877
|
||||
summary: Check hidden frames in entitlements
|
||||
area: Infra/Core
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127921
|
||||
summary: "[9.x] Revert \"Enable madvise by default for all builds\""
|
||||
area: Vector Search
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127924
|
||||
summary: Limit Replace function memory usage
|
||||
area: ES|QL
|
||||
type: enhancement
|
||||
issues: []
|
||||
- pr: 127949
|
||||
summary: Ensure ordinal builder emit ordinal blocks
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127975
|
||||
summary: Fix a bug in `significant_terms`
|
||||
area: Aggregations
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 127991
|
||||
summary: Avoid nested docs in painless execute api
|
||||
area: Infra/Scripting
|
||||
type: bug
|
||||
issues:
|
||||
- 41004
|
||||
- pr: 128043
|
||||
summary: Make S3 custom query parameter optional
|
||||
area: Snapshot/Restore
|
||||
type: breaking
|
||||
issues: []
|
||||
breaking:
|
||||
area: Cluster and node setting
|
||||
title: Make S3 custom query parameter optional
|
||||
details: "Earlier versions of Elasticsearch would record the purpose of each S3 API call using the `?x-purpose=` custom query parameter. This isn't believed to be necessary outside of the ECH/ECE/ECK/... managed services, and it adds rather a lot to the request logs, so with this change we make the feature optional and disabled by default."
|
||||
impact: "If you wish to reinstate the old behaviour on a S3 repository, set `s3.client.${CLIENT_NAME}.add_purpose_custom_query_parameter` to `true` for the relevant client."
|
||||
notable: false
|
||||
essSettingChange: false
|
||||
- pr: 128047
|
||||
summary: Add missing entitlement to `repository-azure`
|
||||
area: Snapshot/Restore
|
||||
type: bug
|
||||
issues:
|
||||
- 128046
|
||||
- pr: 128111
|
||||
summary: Fix union types in CCS
|
||||
area: ES|QL
|
||||
type: bug
|
||||
issues: []
|
||||
- pr: 128153
|
||||
summary: "Fix: Add `NamedWriteable` for `RuleQueryRankDoc`"
|
||||
area: Relevance
|
||||
type: bug
|
||||
issues:
|
||||
- 126071
|
||||
- pr: 128161
|
||||
summary: Fix system data streams incorrectly showing up in the list of template validation problems
|
||||
area: Data streams
|
||||
type: bug
|
||||
issues: []
|
Loading…
Add table
Add a link
Reference in a new issue