Implement VersionCollection in Java (#34050)

This commit is contained in:
Alpar Torok 2018-11-01 17:43:57 +02:00 committed by GitHub
parent 2fadec5c3d
commit f8378d91a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 988 additions and 930 deletions

View File

@ -103,10 +103,6 @@ subprojects {
* in a branch if there are only betas and rcs in the branch so we have
* *something* to test against. */
VersionCollection versions = new VersionCollection(file('server/src/main/java/org/elasticsearch/Version.java').readLines('UTF-8'))
if (versions.currentVersion != VersionProperties.elasticsearch) {
throw new GradleException("The last version in Versions.java [${versions.currentVersion}] does not match " +
"VersionProperties.elasticsearch [${VersionProperties.elasticsearch}]")
}
// build metadata from previous build, contains eg hashes for bwc builds
String buildMetadataValue = System.getenv('BUILD_METADATA')
@ -140,26 +136,16 @@ task verifyVersions {
if (gradle.startParameter.isOffline()) {
throw new GradleException("Must run in online mode to verify versions")
}
// Read the list from maven central
Node xml
// Read the list from maven central.
// Fetch the metadata an parse the xml into Version instances because it's more straight forward here
// rather than bwcVersion ( VersionCollection ).
new URL('https://repo1.maven.org/maven2/org/elasticsearch/elasticsearch/maven-metadata.xml').openStream().withStream { s ->
xml = new XmlParser().parse(s)
}
Set<Version> knownVersions = new TreeSet<>(xml.versioning.versions.version.collect { it.text() }.findAll { it ==~ /\d+\.\d+\.\d+/ }.collect { Version.fromString(it) })
// Limit the known versions to those that should be index compatible, and are not future versions
knownVersions = knownVersions.findAll { it.major >= bwcVersions.currentVersion.major - 1 && it.before(VersionProperties.elasticsearch) }
/* Limit the listed versions to those that have been marked as released.
* Versions not marked as released don't get the same testing and we want
* to make sure that we flip all unreleased versions to released as soon
* as possible after release. */
Set<Version> actualVersions = new TreeSet<>(bwcVersions.indexCompatible.findAll { false == it.snapshot })
// Finally, compare!
if (knownVersions.equals(actualVersions) == false) {
throw new GradleException("out-of-date released versions\nActual :" + actualVersions + "\nExpected:" + knownVersions +
"\nUpdate Version.java. Note that Version.CURRENT doesn't count because it is not released.")
bwcVersions.compareToAuthoritative(
new XmlParser().parse(s)
.versioning.versions.version
.collect { it.text() }.findAll { it ==~ /\d+\.\d+\.\d+/ }
.collect { Version.fromString(it) }
)
}
}
}
@ -251,20 +237,17 @@ subprojects {
"org.elasticsearch.plugin:percolator-client:${version}": ':modules:percolator',
"org.elasticsearch.plugin:rank-eval-client:${version}": ':modules:rank-eval',
]
bwcVersions.snapshotProjectNames.each { snapshotName ->
Version snapshot = bwcVersions.getSnapshotForProject(snapshotName)
if (snapshot != null ) {
String snapshotProject = ":distribution:bwc:${snapshotName}"
project(snapshotProject).ext.bwcVersion = snapshot
ext.projectSubstitutions["org.elasticsearch.distribution.deb:elasticsearch:${snapshot}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.rpm:elasticsearch:${snapshot}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.zip:elasticsearch:${snapshot}"] = snapshotProject
if (snapshot.onOrAfter('6.3.0')) {
ext.projectSubstitutions["org.elasticsearch.distribution.deb:elasticsearch-oss:${snapshot}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.rpm:elasticsearch-oss:${snapshot}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.zip:elasticsearch-oss:${snapshot}"] = snapshotProject
}
// substitute unreleased versions with projects that check out and build locally
bwcVersions.forPreviousUnreleased { VersionCollection.UnreleasedVersionInfo unreleasedVersion ->
Version unreleased = unreleasedVersion.version
String snapshotProject = ":distribution:bwc:${unreleasedVersion.gradleProjectName}"
ext.projectSubstitutions["org.elasticsearch.distribution.deb:elasticsearch:${unreleased}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.rpm:elasticsearch:${unreleased}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.zip:elasticsearch:${unreleased}"] = snapshotProject
if (unreleased.onOrAfter('6.3.0')) {
ext.projectSubstitutions["org.elasticsearch.distribution.deb:elasticsearch-oss:${unreleased}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.rpm:elasticsearch-oss:${unreleased}"] = snapshotProject
ext.projectSubstitutions["org.elasticsearch.distribution.zip:elasticsearch-oss:${unreleased}"] = snapshotProject
}
}

View File

@ -1,353 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.gradle
import org.gradle.api.GradleException
import org.gradle.api.InvalidUserDataException
import java.util.regex.Matcher
/**
* The collection of version constants declared in Version.java, for use in BWC testing.
*
* if major+1 released: released artifacts from $version down to major-1.highestMinor.highestPatch, none of these should be snapshots, period.
* if major+1 unreleased:
* - if released:
* -- caveat 0: snapshot for the major-1.highestMinor.highestPatch
* - if unreleased:
* -- caveat 0: snapshot for the major-1.highestMinor.highestPatch
* -- caveat 1: every same major lower minor branch should also be tested if its released, and if not, its a snapshot. There should only be max 2 of these.
* -- caveat 2: the largest released minor branch before the unreleased minor should also be a snapshot
* -- caveat 3: if the current version is a different major than the previous rules apply to major - 1 of the current version
*
* Please note that the caveat's also correspond with the 4 types of snapshots.
* - Caveat 0 - always maintenanceBugfixSnapshot.
* - Caveat 1 - This is tricky. If caveat 3 applies, the highest matching value is nextMinorSnapshot, if there is another it is the stagedMinorSnapshot.
* If caveat 3 does not apply then the only possible value is the stagedMinorSnapshot.
* - Caveat 2 - always nextBugfixSnapshot
* - Caveat 3 - this only changes the applicability of Caveat 1
*
* Notes on terminology:
* - The case for major+1 being released is accomplished through the isReleasableBranch value. If this is false, then the branch is no longer
* releasable, meaning not to test against any snapshots.
* - Released is defined as having > 1 suffix-free version in a major.minor series. For instance, only 6.2.0 means unreleased, but a
* 6.2.0 and 6.2.1 mean that 6.2.0 was released already.
*/
class VersionCollection {
private final List<Version> versions
Version nextMinorSnapshot
Version stagedMinorSnapshot
Version nextBugfixSnapshot
Version maintenanceBugfixSnapshot
final Version currentVersion
private final TreeSet<Version> versionSet = new TreeSet<>()
final List<String> snapshotProjectNames = ['next-minor-snapshot',
'staged-minor-snapshot',
'next-bugfix-snapshot',
'maintenance-bugfix-snapshot']
// When we roll 8.0 its very likely these will need to be extracted from this class
private final boolean isReleasableBranch = true
/**
* Construct a VersionCollection from the lines of the Version.java file. The basic logic for the following is pretty straight forward.
* @param versionLines The lines of the Version.java file.
*/
VersionCollection(List<String> versionLines) {
final boolean buildSnapshot = System.getProperty("build.snapshot", "true") == "true"
List<Version> versions = []
// This class should be converted wholesale to use the treeset
for (final String line : versionLines) {
final Matcher match = line =~ /\W+public static final Version V_(\d+)_(\d+)_(\d+)(_alpha\d+|_beta\d+|_rc\d+)? .*/
if (match.matches()) {
final Version foundVersion = new Version(
Integer.parseInt(match.group(1)), Integer.parseInt(match.group(2)),
Integer.parseInt(match.group(3)), (match.group(4) ?: '').replace('_', '-'), false)
safeAddToSet(foundVersion)
}
}
if (versionSet.empty) {
throw new GradleException("Unexpectedly found no version constants in Versions.java")
}
// If the major version has been released, then remove all of the alpha/beta/rc versions that exist in the set
versionSet.removeAll { it.suffix.isEmpty() == false && isMajorReleased(it, versionSet) }
// set currentVersion
Version lastVersion = versionSet.last()
currentVersion = new Version(lastVersion.major, lastVersion.minor, lastVersion.revision, lastVersion.suffix, buildSnapshot)
// remove all of the potential alpha/beta/rc from the currentVersion
versionSet.removeAll {
it.suffix.isEmpty() == false &&
it.major == currentVersion.major &&
it.minor == currentVersion.minor &&
it.revision == currentVersion.revision }
// re-add the currentVersion to the set
versionSet.add(currentVersion)
if (isReleasableBranch) {
if (isReleased(currentVersion)) {
// caveat 0 - if the minor has been released then it only has a maintenance version
// go back 1 version to get the last supported snapshot version of the line, which is a maint bugfix
Version highestMinor = getHighestPreviousMinor(currentVersion.major)
maintenanceBugfixSnapshot = replaceAsSnapshot(highestMinor)
} else {
// caveat 3 - if our currentVersion is a X.0.0, we need to check X-1 minors to see if they are released
if (currentVersion.minor == 0) {
for (Version version: getMinorTips(currentVersion.major - 1)) {
if (isReleased(version) == false) {
// caveat 1 - This should only ever contain 2 non released branches in flight. An example is 6.x is frozen,
// and 6.2 is cut but not yet released there is some simple logic to make sure that in the case of more than 2,
// it will bail. The order is that the minor snapshot is fulfilled first, and then the staged minor snapshot
if (nextMinorSnapshot == null) {
// it has not been set yet
nextMinorSnapshot = replaceAsSnapshot(version)
} else if (stagedMinorSnapshot == null) {
stagedMinorSnapshot = replaceAsSnapshot(version)
} else {
throw new GradleException("More than 2 snapshot version existed for the next minor and staged (frozen) minors.")
}
} else {
// caveat 2 - this is the last minor snap for this major, so replace the highest (last) one of these and break
nextBugfixSnapshot = replaceAsSnapshot(version)
// we only care about the largest minor here, so in the case of 6.1 and 6.0, it will only get 6.1
break
}
}
// caveat 0 - the last supported snapshot of the line is on a version that we don't support (N-2)
maintenanceBugfixSnapshot = null
} else {
// caveat 3 did not apply. version is not a X.0.0, so we are somewhere on a X.Y line
// only check till minor == 0 of the major
for (Version version: getMinorTips(currentVersion.major)) {
if (isReleased(version) == false) {
// caveat 1 - This should only ever contain 0 or 1 branch in flight. An example is 6.x is frozen, and 6.2 is cut
// but not yet released there is some simple logic to make sure that in the case of more than 1, it will bail
if (stagedMinorSnapshot == null) {
stagedMinorSnapshot = replaceAsSnapshot(version)
} else {
throw new GradleException("More than 1 snapshot version existed for the staged (frozen) minors.")
}
} else {
// caveat 2 - this is the last minor snap for this major, so replace the highest (last) one of these and break
nextBugfixSnapshot = replaceAsSnapshot(version)
// we only care about the largest minor here, so in the case of 6.1 and 6.0, it will only get 6.1
break
}
}
// caveat 0 - now dip back 1 version to get the last supported snapshot version of the line
Version highestMinor = getHighestPreviousMinor(currentVersion.major)
maintenanceBugfixSnapshot = replaceAsSnapshot(highestMinor)
}
}
}
this.versions = Collections.unmodifiableList(versionSet.toList())
}
/**
* @return The list of versions read from the Version.java file
*/
List<Version> getVersions() {
return versions
}
/**
* Index compat supports 1 previous entire major version. For instance, any 6.x test for this would test all of 5 up to that 6.x version
*
* @return All earlier versions that should be tested for index BWC with the current version.
*/
List<Version> getIndexCompatible() {
int actualMajor = (currentVersion.major == 5 ? 2 : currentVersion.major - 1)
return versionSet
.tailSet(Version.fromString("${actualMajor}.0.0"))
.headSet(currentVersion)
.asList()
}
/**
* Ensures the types of snapshot are not null and are also in the index compat list
*/
List<Version> getSnapshotsIndexCompatible() {
List<Version> compatSnapshots = []
List<Version> allCompatVersions = getIndexCompatible()
if (allCompatVersions.contains(nextMinorSnapshot)) {
compatSnapshots.add(nextMinorSnapshot)
}
if (allCompatVersions.contains(stagedMinorSnapshot)) {
compatSnapshots.add(stagedMinorSnapshot)
}
if (allCompatVersions.contains(nextBugfixSnapshot)) {
compatSnapshots.add(nextBugfixSnapshot)
}
if (allCompatVersions.contains(maintenanceBugfixSnapshot)) {
compatSnapshots.add(maintenanceBugfixSnapshot)
}
return compatSnapshots
}
/**
* Wire compat supports the last minor of the previous major. For instance, any 6.x test would test 5.6 up to that 6.x version
*
* @return All earlier versions that should be tested for wire BWC with the current version.
*/
List<Version> getWireCompatible() {
// Get the last minor of the previous major
Version lowerBound = getHighestPreviousMinor(currentVersion.major)
return versionSet
.tailSet(Version.fromString("${lowerBound.major}.${lowerBound.minor}.0"))
.headSet(currentVersion)
.toList()
}
/**
* Ensures the types of snapshot are not null and are also in the wire compat list
*/
List<Version> getSnapshotsWireCompatible() {
List<Version> compatSnapshots = []
List<Version> allCompatVersions = getWireCompatible()
if (allCompatVersions.contains(nextMinorSnapshot)) {
compatSnapshots.add(nextMinorSnapshot)
}
if (allCompatVersions.contains(stagedMinorSnapshot)) {
compatSnapshots.add(stagedMinorSnapshot)
}
if (allCompatVersions.contains(nextBugfixSnapshot)) {
compatSnapshots.add(nextBugfixSnapshot)
}
if (allCompatVersions.contains(maintenanceBugfixSnapshot)) {
compatSnapshots.add(maintenanceBugfixSnapshot)
}
// There was no wire compat for the 2.x line
compatSnapshots.removeAll {it.major == 2}
return compatSnapshots
}
/**
* Grabs the proper snapshot based on the name passed in. These names should correspond with gradle project names under bwc. If you
* are editing this if/else it is only because you added another project under :distribution:bwc. Do not modify this method or its
* reasoning for throwing the exception unless you are sure that it will not harm :distribution:bwc.
*/
Version getSnapshotForProject(String snapshotProjectName) {
if (snapshotProjectName == 'next-minor-snapshot') {
return nextMinorSnapshot
} else if (snapshotProjectName == 'staged-minor-snapshot') {
return stagedMinorSnapshot
} else if (snapshotProjectName == 'maintenance-bugfix-snapshot') {
return maintenanceBugfixSnapshot
} else if (snapshotProjectName == 'next-bugfix-snapshot') {
return nextBugfixSnapshot
} else {
throw new InvalidUserDataException("Unsupported project name ${snapshotProjectName}")
}
}
/**
* Uses basic logic about our releases to determine if this version has been previously released
*/
private boolean isReleased(Version version) {
return version.revision > 0
}
/**
* Validates that the count of non suffixed (alpha/beta/rc) versions in a given major to major+1 is greater than 1.
* This means that there is more than just a major.0.0 or major.0.0-alpha in a branch to signify it has been prevously released.
*/
private boolean isMajorReleased(Version version, TreeSet<Version> items) {
return items
.tailSet(Version.fromString("${version.major}.0.0"))
.headSet(Version.fromString("${version.major + 1}.0.0"))
.count { it.suffix.isEmpty() } // count only non suffix'd versions as actual versions that may be released
.intValue() > 1
}
/**
* Gets the largest version previous major version based on the nextMajorVersion passed in.
* If you have a list [5.0.2, 5.1.2, 6.0.1, 6.1.1] and pass in 6 for the nextMajorVersion, it will return you 5.1.2
*/
private Version getHighestPreviousMinor(Integer nextMajorVersion) {
SortedSet<Version> result = versionSet.headSet(Version.fromString("${nextMajorVersion}.0.0"))
return result.isEmpty() ? null : result.last()
}
/**
* Helper function for turning a version into a snapshot version, removing and readding it to the tree
*/
private Version replaceAsSnapshot(Version version) {
versionSet.remove(version)
Version snapshotVersion = new Version(version.major, version.minor, version.revision, version.suffix, true)
safeAddToSet(snapshotVersion)
return snapshotVersion
}
/**
* Safely adds a value to the treeset, or bails if the value already exists.
* @param version
*/
private void safeAddToSet(Version version) {
if (versionSet.add(version) == false) {
throw new GradleException("Versions.java contains duplicate entries for ${version}")
}
}
/**
* Gets the entire set of major.minor.* given those parameters.
*/
private SortedSet<Version> getMinorSetForMajor(Integer major, Integer minor) {
return versionSet
.tailSet(Version.fromString("${major}.${minor}.0"))
.headSet(Version.fromString("${major}.${minor + 1}.0"))
}
/**
* Gets the entire set of major.* to the currentVersion
*/
private SortedSet<Version> getMajorSet(Integer major) {
return versionSet
.tailSet(Version.fromString("${major}.0.0"))
.headSet(currentVersion)
}
/**
* Gets the tip of each minor set and puts it in a list.
*
* examples:
* [1.0.0, 1.1.0, 1.1.1, 1.2.0, 1.3.1] will return [1.0.0, 1.1.1, 1.2.0, 1.3.1]
* [1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4] will return [1.0.4]
*/
private List<Version> getMinorTips(Integer major) {
TreeSet<Version> majorSet = getMajorSet(major)
List<Version> minorList = new ArrayList<>()
for (int minor = majorSet.last().minor; minor >= 0; minor--) {
TreeSet<Version> minorSetInMajor = getMinorSetForMajor(major, minor)
minorList.add(minorSetInMajor.last())
}
return minorList
}
}

View File

@ -21,6 +21,10 @@ public final class Version implements Comparable<Version> {
private static final Pattern pattern =
Pattern.compile("(\\d)+\\.(\\d+)\\.(\\d+)(-alpha\\d+|-beta\\d+|-rc\\d+)?(-SNAPSHOT)?");
public Version(int major, int minor, int revision) {
this(major, minor, revision, "", false);
}
public Version(int major, int minor, int revision, String suffix, boolean snapshot) {
Objects.requireNonNull(major, "major version can't be null");
Objects.requireNonNull(minor, "minor version can't be null");
@ -31,25 +35,8 @@ public final class Version implements Comparable<Version> {
this.snapshot = snapshot;
this.suffix = suffix == null ? "" : suffix;
int suffixOffset = 0;
if (this.suffix.isEmpty()) {
// no suffix will be considered smaller, uncomment to change that
// suffixOffset = 100;
} else {
if (this.suffix.contains("alpha")) {
suffixOffset += parseSuffixNumber(this.suffix.substring(6));
} else if (this.suffix.contains("beta")) {
suffixOffset += 25 + parseSuffixNumber(this.suffix.substring(5));
} else if (this.suffix.contains("rc")) {
suffixOffset += 50 + parseSuffixNumber(this.suffix.substring(3));
}
else {
throw new IllegalArgumentException("Suffix must contain one of: alpha, beta or rc");
}
}
// currently snapshot is not taken into account
this.id = major * 10000000 + minor * 100000 + revision * 1000 + suffixOffset * 10 /*+ (snapshot ? 1 : 0)*/;
this.id = major * 10000000 + minor * 100000 + revision * 1000;
}
private static int parseSuffixNumber(String substring) {
@ -136,10 +123,7 @@ public final class Version implements Comparable<Version> {
Version version = (Version) o;
return major == version.major &&
minor == version.minor &&
revision == version.revision &&
id == version.id &&
snapshot == version.snapshot &&
Objects.equals(suffix, version.suffix);
revision == version.revision;
}
@Override
@ -176,4 +160,5 @@ public final class Version implements Comparable<Version> {
public int compareTo(Version other) {
return Integer.compare(getId(), other.getId());
}
}

View File

@ -0,0 +1,341 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.gradle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
/**
* A container for elasticsearch supported version information used in BWC testing.
*
* Parse the Java source file containing the versions declarations and use the known rules to figure out which are all
* the version the current one is wire and index compatible with.
* On top of this, figure out which of these are unreleased and provide the branch they can be built from.
*
* Note that in this context, currentVersion is the unreleased version this build operates on.
* At any point in time there will surely be four such unreleased versions being worked on,
* thus currentVersion will be one of these.
*
* Considering:
* <dl>
* <dt>M, M &gt; 0</dt>
* <dd>last released major</dd>
* <dt>N, N &gt; 0</dt>
* <dd>last released minor</dd>
* </dl>
*
* <ul>
* <li>the unreleased <b>major</b>, M+1.0.0 on the `master` branch</li>
* <li>the unreleased <b>minor</b>, M.N.0 on the `M.x` (x is literal) branch</li>
* <li>the unreleased <b>bugfix</b>, M.N.c (c &gt; 0) on the `M.b` branch</li>
* <li>the unreleased <b>maintenance</b>, M-1.d.e ( d &gt; 0, e &gt; 0) on the `(M-1).d` branch</li>
* </ul>
* In addition to these, there will be a fifth one when a minor reaches feature freeze, we call this the <i>staged</i>
* version:
* <ul>
* <li>the unreleased <b>staged</b>, M.N-2.0 (N &gt; 2) on the `M.(N-2)` branch</li>
* </ul>
*
* Each build is only concerned with versions before it, as those are the ones that need to be tested
* for backwards compatibility. We never look forward, and don't add forward facing version number to branches of previous
* version.
*
* Each branch has a current version, and expected compatible versions are parsed from the server code's Version` class.
* We can reliably figure out which the unreleased versions are due to the convention of always adding the next unreleased
* version number to server in all branches when a version is released.
* E.x when M.N.c is released M.N.c+1 is added to the Version class mentioned above in all the following branches:
* `M.b`, `M.x` and `master` so we can reliably assume that the leafs of the version tree are unreleased.
* This convention is enforced by checking the versions we consider to be unreleased against an
* authoritative source (maven central).
* We are then able to map the unreleased version to branches in git and Gradle projects that are capable of checking
* out and building them, so we can include these in the testing plan as well.
*/
public class VersionCollection {
private static final Pattern LINE_PATTERN = Pattern.compile(
"\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)? .*"
);
private final Version currentVersion;
private final Map<Integer, List<Version>> groupByMajor;
public class UnreleasedVersionInfo {
public final Version version;
public final String branch;
public final String gradleProjectName;
UnreleasedVersionInfo(Version version, String branch, String gradleProjectName) {
this.version = version;
this.branch = branch;
this.gradleProjectName = gradleProjectName;
}
}
public VersionCollection(List<String> versionLines) {
this(versionLines, VersionProperties.getElasticsearch());
}
protected VersionCollection(List<String> versionLines, Version currentVersionProperty) {
groupByMajor = versionLines.stream()
.map(LINE_PATTERN::matcher)
.filter(Matcher::matches)
.map(match -> new Version(
Integer.parseInt(match.group(1)),
Integer.parseInt(match.group(2)),
Integer.parseInt(match.group(3)),
(match.group(4) == null ? "" : match.group(4)).replace('_', '-'),
false
))
.sorted()
.filter(version -> version.getSuffix().isEmpty() || version.equals(currentVersionProperty))
.collect(Collectors.groupingBy(Version::getMajor, Collectors.toList()));
if (groupByMajor.isEmpty()) {
throw new IllegalArgumentException("Could not parse any versions");
}
currentVersion = getLatestVersionByKey(
groupByMajor,
groupByMajor.keySet().stream().max(Integer::compareTo)
.orElseThrow(() -> new IllegalStateException("Unexpected number of versions in collection"))
);
assertCurrentVersionMatchesParsed(currentVersionProperty);
assertNoOlderThanTwoMajors();
markUnreleasedAsSnapshot();
}
private void markUnreleasedAsSnapshot() {
getUnreleased().forEach(uv ->
groupByMajor.get(uv.getMajor()).set(
groupByMajor.get(uv.getMajor()).indexOf(uv),
new Version(uv.getMajor(), uv.getMinor(), uv.getRevision(),uv.getSuffix(), true)
)
);
}
private void assertNoOlderThanTwoMajors() {
Set<Integer> majors = groupByMajor.keySet();
if (majors.size() != 2 && currentVersion.getMinor() != 0 && currentVersion.getMajor() != 0) {
throw new IllegalStateException(
"Expected exactly 2 majors in parsed versions but found: " + majors
);
}
}
private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) {
if (currentVersionProperty.equals(currentVersion) == false) {
throw new IllegalStateException(
"Parsed versions latest version does not match the one configured in build properties. " +
"Parsed latest version is " + currentVersion + " but the build has " +
currentVersionProperty
);
}
}
public void forPreviousUnreleased(Consumer<UnreleasedVersionInfo> consumer) {
getUnreleased().stream()
.filter(version -> version.equals(currentVersion) == false)
.forEach(version -> consumer.accept(
new UnreleasedVersionInfo(
version,
getBranchFor(version),
getGradleProjectNameFor(version)
)
));
}
private String getGradleProjectNameFor(Version version) {
if (version.equals(currentVersion)) {
throw new IllegalArgumentException("The Gradle project to build " + version + " is the current build.");
}
Map<Integer, List<Version>> releasedMajorGroupedByMinor = getReleasedMajorGroupedByMinor();
if (version.getRevision() == 0) {
if (releasedMajorGroupedByMinor
.get(releasedMajorGroupedByMinor.keySet().stream().max(Integer::compareTo).orElse(0))
.contains(version)) {
return "minor";
} else {
return "staged";
}
} else {
if (releasedMajorGroupedByMinor
.getOrDefault(version.getMinor(), emptyList())
.contains(version)) {
return "bugfix";
} else {
return "maintenance";
}
}
}
private String getBranchFor(Version version) {
switch (getGradleProjectNameFor(version)) {
case "minor":
return version.getMajor() + ".x";
case "staged":
case "maintenance":
case "bugfix":
return version.getMajor() + "." + version.getMinor();
default:
throw new IllegalStateException("Unexpected Gradle project name");
}
}
public List<Version> getUnreleased() {
List<Version> unreleased = new ArrayList<>();
// The current version is being worked, is always unreleased
unreleased.add(currentVersion);
// the tip of the previous major is unreleased for sure, be it a minor or a bugfix
unreleased.add(getLatestVersionByKey(this.groupByMajor, currentVersion.getMajor() - 1));
final Map<Integer, List<Version>> groupByMinor = getReleasedMajorGroupedByMinor();
int greatestMinor = groupByMinor.keySet().stream().max(Integer::compareTo).orElse(0);
// the last bugfix for this minor series is always unreleased
unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor));
if (groupByMinor.get(greatestMinor).size() == 1) {
// we found an unreleased minor
unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor - 1));
if (groupByMinor.getOrDefault(greatestMinor - 1, emptyList()).size() == 1) {
// we found that the previous minor is staged but not yet released
// in this case, the minor before that has a bugfix
unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor - 2));
}
}
return unmodifiableList(
unreleased.stream()
.sorted()
.distinct()
.collect(Collectors.toList())
);
}
private Version getLatestVersionByKey(Map<Integer, List<Version>> groupByMajor, int key) {
return groupByMajor.getOrDefault(key, emptyList()).stream()
.max(Version::compareTo)
.orElseThrow(() -> new IllegalStateException("Unexpected number of versions in collection"));
}
private Map<Integer, List<Version>> getReleasedMajorGroupedByMinor() {
List<Version> currentMajorVersions = groupByMajor.get(currentVersion.getMajor());
List<Version> previousMajorVersions = groupByMajor.get(currentVersion.getMajor() - 1);
final Map<Integer, List<Version>> groupByMinor;
if (currentMajorVersions.size() == 1) {
// Current is an unreleased major: x.0.0 so we have to look for other unreleased versions in the previous major
groupByMinor = previousMajorVersions.stream()
.collect(Collectors.groupingBy(Version::getMinor, Collectors.toList()));
} else {
groupByMinor = currentMajorVersions.stream()
.collect(Collectors.groupingBy(Version::getMinor, Collectors.toList()));
}
return groupByMinor;
}
public void compareToAuthoritative(List<Version> authoritativeReleasedVersions) {
Set<Version> notReallyReleased = new HashSet<>(getReleased());
notReallyReleased.removeAll(authoritativeReleasedVersions);
if (notReallyReleased.isEmpty() == false) {
throw new IllegalStateException(
"out-of-date released versions" +
"\nFollowing versions are not really released, but the build thinks they are: " + notReallyReleased
);
}
Set<Version> incorrectlyConsideredUnreleased = new HashSet<>(authoritativeReleasedVersions);
incorrectlyConsideredUnreleased.retainAll(getUnreleased());
if (incorrectlyConsideredUnreleased.isEmpty() == false) {
throw new IllegalStateException(
"out-of-date released versions" +
"\nBuild considers versions unreleased, " +
"but they are released according to an authoritative source: " + incorrectlyConsideredUnreleased +
"\nThe next versions probably needs to be added to Version.java (CURRENT doesn't count)."
);
}
}
private List<Version> getReleased() {
List<Version> unreleased = getUnreleased();
return groupByMajor.values().stream()
.flatMap(Collection::stream)
.filter(each -> unreleased.contains(each) == false)
.collect(Collectors.toList());
}
public List<Version> getIndexCompatible() {
return unmodifiableList(
Stream.concat(
groupByMajor.get(currentVersion.getMajor() - 1).stream(),
groupByMajor.get(currentVersion.getMajor()).stream()
)
.filter(version -> version.equals(currentVersion) == false)
.collect(Collectors.toList())
);
}
public List<Version> getWireCompatible() {
List<Version> wireCompat = new ArrayList<>();
List<Version> prevMajors = groupByMajor.get(currentVersion.getMajor() - 1);
int minor = prevMajors.get(prevMajors.size() - 1).getMinor();
for (int i = prevMajors.size() - 1;
i > 0 && prevMajors.get(i).getMinor() == minor;
i--
) {
wireCompat.add(prevMajors.get(i));
}
wireCompat.addAll(groupByMajor.get(currentVersion.getMajor()));
wireCompat.remove(currentVersion);
wireCompat.sort(Version::compareTo);
return unmodifiableList(wireCompat);
}
public List<Version> getUnreleasedIndexCompatible() {
List<Version> unreleasedIndexCompatible = new ArrayList<>(getIndexCompatible());
unreleasedIndexCompatible.retainAll(getUnreleased());
return unmodifiableList(unreleasedIndexCompatible);
}
public List<Version> getUnreleasedWireCompatible() {
List<Version> unreleasedWireCompatible = new ArrayList<>(getWireCompatible());
unreleasedWireCompatible.retainAll(getUnreleased());
return unmodifiableList(unreleasedWireCompatible);
}
}

View File

@ -1,236 +0,0 @@
package org.elasticsearch.gradle
import org.elasticsearch.gradle.test.GradleUnitTestCase
import org.junit.Test
class VersionCollectionTests extends GradleUnitTestCase {
String formatVersion(String version) {
return " public static final Version V_${version.replaceAll("\\.", "_")} "
}
List<String> allVersions = [formatVersion('5.0.0'), formatVersion('5.0.0_alpha1'), formatVersion('5.0.0_alpha2'), formatVersion('5.0.0_beta1'),
formatVersion('5.0.0_rc1'),formatVersion('5.0.0_rc2'),formatVersion('5.0.1'), formatVersion('5.0.2'),
formatVersion('5.1.1'), formatVersion('5.1.2'), formatVersion('5.2.0'), formatVersion('5.2.1'), formatVersion('6.0.0'),
formatVersion('6.0.1'), formatVersion('6.1.0'), formatVersion('6.1.1'), formatVersion('6.2.0'), formatVersion('6.3.0'),
formatVersion('7.0.0_alpha1'), formatVersion('7.0.0_alpha2')]
/**
* This validates the logic of being on a unreleased major branch with a staged major-1.minor sibling. This case happens when a version is
* branched from Major-1.x At the time of this writing 6.2 is unreleased and 6.3 is the 6.x branch. This test simulates the behavior
* from 7.0 perspective, or master at the time of this writing.
*/
@Test
void testAgainstMajorUnreleasedWithExistingStagedMinorRelease() {
VersionCollection vc = new VersionCollection(allVersions)
assertNotNull(vc)
assertEquals(vc.nextMinorSnapshot, Version.fromString("6.3.0-SNAPSHOT"))
assertEquals(vc.stagedMinorSnapshot, Version.fromString("6.2.0-SNAPSHOT"))
assertEquals(vc.nextBugfixSnapshot, Version.fromString("6.1.1-SNAPSHOT"))
assertNull(vc.maintenanceBugfixSnapshot)
vc.indexCompatible.containsAll(vc.versions)
// This should contain the same list sans the current version
List indexCompatList = [Version.fromString("6.0.0"), Version.fromString("6.0.1"),
Version.fromString("6.1.0"), Version.fromString("6.1.1-SNAPSHOT"),
Version.fromString("6.2.0-SNAPSHOT"), Version.fromString("6.3.0-SNAPSHOT")]
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("6.3.0-SNAPSHOT")]
assertTrue(wireCompatList.containsAll(vc.wireCompatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 3)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.3.0-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.2.0-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.1.1-SNAPSHOT")))
assertEquals(vc.snapshotsWireCompatible.size(), 1)
assertEquals(vc.snapshotsWireCompatible.first(), Version.fromString("6.3.0-SNAPSHOT"))
}
/**
* This validates the logic of being on a unreleased major branch without a staged major-1.minor sibling. This case happens once a staged,
* unreleased minor is released. At the time of this writing 6.2 is unreleased, so adding a 6.2.1 simulates a 6.2 release. This test
* simulates the behavior from 7.0 perspective, or master at the time of this writing.
*/
@Test
void testAgainstMajorUnreleasedWithoutStagedMinorRelease() {
List localVersion = allVersions.clone()
localVersion.add(formatVersion('6.2.1')) // release 6.2
VersionCollection vc = new VersionCollection(localVersion)
assertNotNull(vc)
assertEquals(vc.nextMinorSnapshot, Version.fromString("6.3.0-SNAPSHOT"))
assertEquals(vc.stagedMinorSnapshot, null)
assertEquals(vc.nextBugfixSnapshot, Version.fromString("6.2.1-SNAPSHOT"))
assertNull(vc.maintenanceBugfixSnapshot)
vc.indexCompatible.containsAll(vc.versions)
// This should contain the same list sans the current version
List indexCompatList = [Version.fromString("6.0.0"), Version.fromString("6.0.1"),
Version.fromString("6.1.0"), Version.fromString("6.1.1"),
Version.fromString("6.2.0"), Version.fromString("6.2.1-SNAPSHOT"),
Version.fromString("6.3.0-SNAPSHOT")]
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("6.3.0-SNAPSHOT")]
assertTrue(wireCompatList.containsAll(vc.wireCompatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 2)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.3.0-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.2.1-SNAPSHOT")))
assertEquals(vc.snapshotsWireCompatible.size(), 1)
assertEquals(vc.snapshotsWireCompatible.first(), Version.fromString("6.3.0-SNAPSHOT"))
}
/**
* This validates the logic of being on a unreleased minor branch with a staged minor sibling. This case happens when a version is
* branched from Major.x At the time of this writing 6.2 is unreleased and 6.3 is the 6.x branch. This test simulates the behavior
* from 6.3 perspective.
*/
@Test
void testAgainstMinorReleasedBranch() {
List localVersion = allVersions.clone()
localVersion.removeAll { it.toString().contains('7_0_0')} // remove all the 7.x so that the actual version is 6.3 (6.x)
VersionCollection vc = new VersionCollection(localVersion)
assertNotNull(vc)
assertEquals(vc.nextMinorSnapshot, null)
assertEquals(vc.stagedMinorSnapshot, Version.fromString("6.2.0-SNAPSHOT"))
assertEquals(vc.nextBugfixSnapshot, Version.fromString("6.1.1-SNAPSHOT"))
assertEquals(vc.maintenanceBugfixSnapshot, Version.fromString("5.2.1-SNAPSHOT"))
// This should contain the same list sans the current version
List indexCompatList = vc.versions.subList(0, vc.versions.size() - 1)
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("5.2.0"), Version.fromString("5.2.1-SNAPSHOT"), Version.fromString("6.0.0"),
Version.fromString("6.0.1"), Version.fromString("6.1.0"), Version.fromString("6.1.1-SNAPSHOT"),
Version.fromString("6.2.0-SNAPSHOT")]
assertTrue(wireCompatList.containsAll(vc.wireCompatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 3)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.2.0-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.1.1-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
assertEquals(vc.snapshotsWireCompatible.size(), 3)
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("6.2.0-SNAPSHOT")))
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("6.1.1-SNAPSHOT")))
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
}
/**
* This validates the logic of being on a unreleased minor branch without a staged minor sibling. This case happens once a staged,
* unreleased minor is released. At the time of this writing 6.2 is unreleased, so adding a 6.2.1 simulates a 6.2 release. This test
* simulates the behavior from 6.3 perspective.
*/
@Test
void testAgainstMinorReleasedBranchNoStagedMinor() {
List localVersion = allVersions.clone()
// remove all the 7.x and add a 6.2.1 which means 6.2 was released
localVersion.removeAll { it.toString().contains('7_0_0')}
localVersion.add(formatVersion('6.2.1'))
VersionCollection vc = new VersionCollection(localVersion)
assertNotNull(vc)
assertEquals(vc.nextMinorSnapshot, null)
assertEquals(vc.stagedMinorSnapshot, null)
assertEquals(vc.nextBugfixSnapshot, Version.fromString("6.2.1-SNAPSHOT"))
assertEquals(vc.maintenanceBugfixSnapshot, Version.fromString("5.2.1-SNAPSHOT"))
// This should contain the same list sans the current version
List indexCompatList = vc.versions.subList(0, vc.versions.size() - 1)
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("5.2.0"), Version.fromString("5.2.1-SNAPSHOT"), Version.fromString("6.0.0"),
Version.fromString("6.0.1"), Version.fromString("6.1.0"), Version.fromString("6.1.1"),
Version.fromString("6.2.0"), Version.fromString("6.2.1-SNAPSHOT")]
assertTrue(wireCompatList.containsAll(vc.wireCompatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 2)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("6.2.1-SNAPSHOT")))
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
assertEquals(vc.snapshotsWireCompatible.size(), 2)
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("6.2.1-SNAPSHOT")))
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
}
/**
* This validates the logic of being on a released minor branch. At the time of writing, 6.2 is unreleased, so this is equivalent of being
* on 6.1.
*/
@Test
void testAgainstOldMinor() {
List localVersion = allVersions.clone()
// remove the 7 alphas and the ones greater than 6.1
localVersion.removeAll { it.toString().contains('7_0_0') || it.toString().contains('V_6_2') || it.toString().contains('V_6_3') }
VersionCollection vc = new VersionCollection(localVersion)
assertNotNull(vc)
assertEquals(vc.nextMinorSnapshot, null)
assertEquals(vc.stagedMinorSnapshot, null)
assertEquals(vc.nextBugfixSnapshot, null)
assertEquals(vc.maintenanceBugfixSnapshot, Version.fromString("5.2.1-SNAPSHOT"))
// This should contain the same list sans the current version
List indexCompatList = vc.versions.subList(0, vc.versions.size() - 1)
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("5.2.0"), Version.fromString("5.2.1-SNAPSHOT"), Version.fromString("6.0.0"),
Version.fromString("6.0.1"), Version.fromString("6.1.0")]
assertTrue(wireCompatList.containsAll(vc.wireCompatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 1)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
assertEquals(vc.snapshotsWireCompatible.size(), 1)
assertTrue(vc.snapshotsWireCompatible.contains(Version.fromString("5.2.1-SNAPSHOT")))
}
/**
* This validates the lower bound of wire compat, which is 5.0. It also validates that the span of 2.x to 5.x if it is decided to port
* this fix all the way to the maint 5.6 release.
*/
@Test
void testFloorOfWireCompatVersions() {
List localVersion = [formatVersion('2.0.0'), formatVersion('2.0.1'), formatVersion('2.1.0'), formatVersion('2.1.1'),
formatVersion('5.0.0'), formatVersion('5.0.1'), formatVersion('5.1.0'), formatVersion('5.1.1'),
formatVersion('5.2.0'),formatVersion('5.2.1'),formatVersion('5.3.0'),formatVersion('5.3.1'),
formatVersion('5.3.2')]
VersionCollection vc = new VersionCollection(localVersion)
assertNotNull(vc)
assertEquals(vc.maintenanceBugfixSnapshot, Version.fromString("2.1.1-SNAPSHOT"))
// This should contain the same list sans the current version
List indexCompatList = vc.versions.subList(0, vc.versions.size() - 1)
assertTrue(indexCompatList.containsAll(vc.indexCompatible))
assertTrue(vc.indexCompatible.containsAll(indexCompatList))
List wireCompatList = [Version.fromString("2.1.0"), Version.fromString("2.1.1-SNAPSHOT"), Version.fromString("5.0.0"),
Version.fromString("5.0.1"), Version.fromString("5.1.0"),
Version.fromString("5.1.1"), Version.fromString("5.2.0"), Version.fromString("5.2.1"),
Version.fromString("5.3.0"), Version.fromString("5.3.1")]
List<Version> compatible = vc.wireCompatible
assertTrue(wireCompatList.containsAll(compatible))
assertTrue(vc.wireCompatible.containsAll(wireCompatList))
assertEquals(vc.snapshotsIndexCompatible.size(), 1)
assertTrue(vc.snapshotsIndexCompatible.contains(Version.fromString("2.1.1-SNAPSHOT")))
// ensure none of the 2.x snapshots appear here, as this is the floor of bwc for wire compat
assertEquals(vc.snapshotsWireCompatible.size(), 0)
}
}

View File

@ -0,0 +1,406 @@
package org.elasticsearch.gradle;
import org.elasticsearch.gradle.test.GradleUnitTestCase;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
public class VersionCollectionTests extends GradleUnitTestCase {
private static final Map<String, List<String>> sampleVersions = new HashMap<>();
@Rule
public ExpectedException expectedEx = ExpectedException.none();
static {
// unreleased major and two unreleased minors ( minor in feature freeze )
sampleVersions.put("8.0.0", asList(
"7_0_0", "7_0_1", "7_1_0", "7_1_1", "7_2_0", "7_3_0", "8.0.0"
));
sampleVersions.put("7.0.0-alpha1", asList(
"6_0_0_alpha1", "6_0_0_alpha2", "6_0_0_beta1", "6_0_0_beta2", "6_0_0_rc1", "6_0_0_rc2",
"6_0_0", "6_0_1", "6_1_0", "6_1_1", "6_1_2", "6_1_3", "6_1_4",
"6_2_0", "6_2_1", "6_2_2", "6_2_3", "6_2_4",
"6_3_0", "6_3_1", "6_3_2",
"6_4_0", "6_4_1", "6_4_2",
"6_5_0", "7_0_0_alpha1"
));
sampleVersions.put("6.5.0", asList(
"5_0_0_alpha1", "5_0_0_alpha2", "5_0_0_alpha3", "5_0_0_alpha4", "5_0_0_alpha5", "5_0_0_beta1", "5_0_0_rc1",
"5_0_0", "5_0_1", "5_0_2", "5_1_1", "5_1_2", "5_2_0", "5_2_1", "5_2_2", "5_3_0", "5_3_1", "5_3_2", "5_3_3",
"5_4_0", "5_4_1", "5_4_2", "5_4_3", "5_5_0", "5_5_1", "5_5_2", "5_5_3", "5_6_0", "5_6_1", "5_6_2", "5_6_3",
"5_6_4", "5_6_5", "5_6_6", "5_6_7", "5_6_8", "5_6_9", "5_6_10", "5_6_11", "5_6_12", "5_6_13",
"6_0_0_alpha1", "6_0_0_alpha2", "6_0_0_beta1", "6_0_0_beta2", "6_0_0_rc1", "6_0_0_rc2", "6_0_0", "6_0_1",
"6_1_0", "6_1_1", "6_1_2", "6_1_3", "6_1_4", "6_2_0", "6_2_1", "6_2_2", "6_2_3", "6_2_4", "6_3_0", "6_3_1",
"6_3_2", "6_4_0", "6_4_1", "6_4_2", "6_5_0"
));
sampleVersions.put("6.6.0", asList(
"5_0_0_alpha1", "5_0_0_alpha2", "5_0_0_alpha3", "5_0_0_alpha4", "5_0_0_alpha5", "5_0_0_beta1", "5_0_0_rc1",
"5_0_0", "5_0_1", "5_0_2", "5_1_1", "5_1_2", "5_2_0", "5_2_1", "5_2_2", "5_3_0", "5_3_1", "5_3_2", "5_3_3",
"5_4_0", "5_4_1", "5_4_2", "5_4_3", "5_5_0", "5_5_1", "5_5_2", "5_5_3", "5_6_0", "5_6_1", "5_6_2", "5_6_3",
"5_6_4", "5_6_5", "5_6_6", "5_6_7", "5_6_8", "5_6_9", "5_6_10", "5_6_11", "5_6_12", "5_6_13",
"6_0_0_alpha1", "6_0_0_alpha2", "6_0_0_beta1", "6_0_0_beta2", "6_0_0_rc1", "6_0_0_rc2", "6_0_0", "6_0_1",
"6_1_0", "6_1_1", "6_1_2", "6_1_3", "6_1_4", "6_2_0", "6_2_1", "6_2_2", "6_2_3", "6_2_4", "6_3_0", "6_3_1",
"6_3_2", "6_4_0", "6_4_1", "6_4_2", "6_5_0", "6_6_0"
));
sampleVersions.put("6.4.2", asList(
"5_0_0_alpha1", "5_0_0_alpha2", "5_0_0_alpha3", "5_0_0_alpha4", "5_0_0_alpha5", "5_0_0_beta1", "5_0_0_rc1",
"5_0_0", "5_0_1", "5_0_2", "5_1_1", "5_1_2", "5_2_0", "5_2_1", "5_2_2", "5_3_0",
"5_3_1", "5_3_2", "5_3_3", "5_4_0", "5_4_1", "5_4_2", "5_4_3", "5_5_0", "5_5_1", "5_5_2", "5_5_3",
"5_6_0", "5_6_1", "5_6_2", "5_6_3", "5_6_4", "5_6_5", "5_6_6", "5_6_7", "5_6_8", "5_6_9", "5_6_10",
"5_6_11", "5_6_12", "5_6_13",
"6_0_0_alpha1", "6_0_0_alpha2", "6_0_0_beta1", "6_0_0_beta2", "6_0_0_rc1", "6_0_0_rc2",
"6_0_0", "6_0_1", "6_1_0", "6_1_1", "6_1_2", "6_1_3", "6_1_4", "6_2_0", "6_2_1", "6_2_2", "6_2_3",
"6_2_4", "6_3_0", "6_3_1", "6_3_2", "6_4_0", "6_4_1", "6_4_2"
));
}
@Test(expected = IllegalArgumentException.class)
public void testExceptionOnEmpty() {
new VersionCollection(asList("foo", "bar"), Version.fromString("7.0.0"));
}
@Test(expected = IllegalStateException.class)
public void testExceptionOnNonCurrent() {
new VersionCollection(singletonList(formatVersionToLine("6.5.0")), Version.fromString("7.0.0"));
}
@Test(expected = IllegalStateException.class)
public void testExceptionOnTooManyMajors() {
new VersionCollection(
asList(
formatVersionToLine("5.6.12"),
formatVersionToLine("6.5.0"),
formatVersionToLine("7.0.0")
),
Version.fromString("7.0.0")
);
}
public void testWireCompatible() {
assertVersionsEquals(
singletonList("6.5.0-SNAPSHOT"),
getVersionCollection("7.0.0-alpha1").getWireCompatible()
);
assertVersionsEquals(
asList(
"5.6.0", "5.6.1", "5.6.2", "5.6.3", "5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10",
"5.6.11", "5.6.12", "5.6.13-SNAPSHOT",
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4",
"6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4",
"6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1", "6.4.2-SNAPSHOT"
),
getVersionCollection("6.5.0").getWireCompatible()
);
assertVersionsEquals(
asList(
"5.6.0", "5.6.1", "5.6.2", "5.6.3", "5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10",
"5.6.11", "5.6.12", "5.6.13-SNAPSHOT", "6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4",
"6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4", "6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1"
),
getVersionCollection("6.4.2").getWireCompatible()
);
assertVersionsEquals(
asList(
"5.6.0", "5.6.1", "5.6.2", "5.6.3", "5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10",
"5.6.11", "5.6.12", "5.6.13-SNAPSHOT",
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4",
"6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4",
"6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1", "6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"
),
getVersionCollection("6.6.0").getWireCompatible()
);
assertVersionsEquals(
singletonList("7.3.0"),
getVersionCollection("8.0.0").getWireCompatible()
);
}
public void testWireCompatibleUnreleased() {
assertVersionsEquals(
singletonList("6.5.0-SNAPSHOT"),
getVersionCollection("7.0.0-alpha1").getUnreleasedWireCompatible()
);
assertVersionsEquals(
asList("5.6.13-SNAPSHOT", "6.4.2-SNAPSHOT"),
getVersionCollection("6.5.0").getUnreleasedWireCompatible()
);
assertVersionsEquals(
singletonList("5.6.13-SNAPSHOT"),
getVersionCollection("6.4.2").getUnreleasedWireCompatible()
);
assertVersionsEquals(
asList("5.6.13-SNAPSHOT", "6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"),
getVersionCollection("6.6.0").getUnreleasedWireCompatible()
);
assertVersionsEquals(
singletonList("7.3.0"),
getVersionCollection("8.0.0").getUnreleasedWireCompatible()
);
}
public void testIndexCompatible() {
assertVersionsEquals(
asList(
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4",
"6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4", "6.3.0", "6.3.1",
"6.3.2", "6.4.0", "6.4.1", "6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"
),
getVersionCollection("7.0.0-alpha1").getIndexCompatible()
);
assertVersionsEquals(
asList(
"5.0.0", "5.0.1", "5.0.2", "5.1.1", "5.1.2", "5.2.0", "5.2.1", "5.2.2", "5.3.0", "5.3.1", "5.3.2", "5.3.3",
"5.4.0", "5.4.1", "5.4.2", "5.4.3", "5.5.0", "5.5.1", "5.5.2", "5.5.3", "5.6.0", "5.6.1", "5.6.2", "5.6.3",
"5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10", "5.6.11", "5.6.12", "5.6.13-SNAPSHOT",
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4",
"6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1", "6.4.2-SNAPSHOT"
),
getVersionCollection("6.5.0").getIndexCompatible()
);
assertVersionsEquals(
asList(
"5.0.0", "5.0.1", "5.0.2", "5.1.1", "5.1.2", "5.2.0", "5.2.1", "5.2.2", "5.3.0", "5.3.1", "5.3.2", "5.3.3",
"5.4.0", "5.4.1", "5.4.2", "5.4.3", "5.5.0", "5.5.1", "5.5.2", "5.5.3", "5.6.0", "5.6.1", "5.6.2", "5.6.3",
"5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10", "5.6.11", "5.6.12", "5.6.13-SNAPSHOT",
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4",
"6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1"
),
getVersionCollection("6.4.2").getIndexCompatible()
);
assertVersionsEquals(
asList(
"5.0.0", "5.0.1", "5.0.2", "5.1.1", "5.1.2", "5.2.0", "5.2.1", "5.2.2", "5.3.0", "5.3.1", "5.3.2", "5.3.3",
"5.4.0", "5.4.1", "5.4.2", "5.4.3", "5.5.0", "5.5.1", "5.5.2", "5.5.3", "5.6.0", "5.6.1", "5.6.2", "5.6.3",
"5.6.4", "5.6.5", "5.6.6", "5.6.7", "5.6.8", "5.6.9", "5.6.10", "5.6.11", "5.6.12", "5.6.13-SNAPSHOT",
"6.0.0", "6.0.1", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4",
"6.3.0", "6.3.1", "6.3.2", "6.4.0", "6.4.1", "6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"
),
getVersionCollection("6.6.0").getIndexCompatible()
);
assertVersionsEquals(
asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0"),
getVersionCollection("8.0.0").getIndexCompatible()
);
}
public void testIndexCompatibleUnreleased() {
assertVersionsEquals(
asList("6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"),
getVersionCollection("7.0.0-alpha1").getUnreleasedIndexCompatible()
);
assertVersionsEquals(
asList("5.6.13-SNAPSHOT", "6.4.2-SNAPSHOT"),
getVersionCollection("6.5.0").getUnreleasedIndexCompatible()
);
assertVersionsEquals(
singletonList("5.6.13-SNAPSHOT"),
getVersionCollection("6.4.2").getUnreleasedIndexCompatible()
);
assertVersionsEquals(
asList("5.6.13-SNAPSHOT", "6.4.2-SNAPSHOT", "6.5.0-SNAPSHOT"),
getVersionCollection("6.6.0").getUnreleasedIndexCompatible()
);
assertVersionsEquals(
asList("7.1.1", "7.2.0", "7.3.0"),
getVersionCollection("8.0.0").getUnreleasedIndexCompatible()
);
}
public void testGetUnreleased() {
assertVersionsEquals(
asList("6.4.2", "6.5.0", "7.0.0-alpha1"),
getVersionCollection("7.0.0-alpha1").getUnreleased()
);
assertVersionsEquals(
asList("5.6.13", "6.4.2", "6.5.0"),
getVersionCollection("6.5.0").getUnreleased()
);
assertVersionsEquals(
asList("5.6.13", "6.4.2"),
getVersionCollection("6.4.2").getUnreleased()
);
assertVersionsEquals(
asList("5.6.13", "6.4.2", "6.5.0", "6.6.0"),
getVersionCollection("6.6.0").getUnreleased()
);
assertVersionsEquals(
asList("7.1.1", "7.2.0", "7.3.0", "8.0.0"),
getVersionCollection("8.0.0").getUnreleased()
);
}
public void testGetBranch() {
assertUnreleasedBranchNames(
asList("6.4", "6.x"),
getVersionCollection("7.0.0-alpha1")
);
assertUnreleasedBranchNames(
asList("5.6", "6.4"),
getVersionCollection("6.5.0")
);
assertUnreleasedBranchNames(
singletonList("5.6"),
getVersionCollection("6.4.2")
);
assertUnreleasedBranchNames(
asList("5.6", "6.4", "6.5"),
getVersionCollection("6.6.0")
);
assertUnreleasedBranchNames(
asList("7.1", "7.2", "7.x"),
getVersionCollection("8.0.0")
);
}
public void testGetGradleProjectName() {
assertUnreleasedGradleProjectNames(
asList("bugfix", "minor"),
getVersionCollection("7.0.0-alpha1")
);
assertUnreleasedGradleProjectNames(
asList("maintenance", "bugfix"),
getVersionCollection("6.5.0")
);
assertUnreleasedGradleProjectNames(
singletonList("maintenance"),
getVersionCollection("6.4.2")
);
assertUnreleasedGradleProjectNames(
asList("maintenance", "bugfix", "staged"),
getVersionCollection("6.6.0")
);
assertUnreleasedGradleProjectNames(
asList("bugfix", "staged", "minor"),
getVersionCollection("8.0.0")
);
}
public void testCompareToAuthoritative() {
List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1", "7.1.0")
.map(Version::fromString)
.collect(Collectors.toList());
VersionCollection vc = new VersionCollection(
listOfVersions.stream()
.map(this::formatVersionToLine)
.collect(Collectors.toList()),
Version.fromString("8.0.0")
);
vc.compareToAuthoritative(authoritativeReleasedVersions);
}
public void testCompareToAuthoritativeUnreleasedActuallyReleased() {
List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1", "7.1.0", "7.1.1", "8.0.0")
.map(Version::fromString)
.collect(Collectors.toList());
VersionCollection vc = new VersionCollection(
listOfVersions.stream()
.map(this::formatVersionToLine)
.collect(Collectors.toList()),
Version.fromString("8.0.0")
);
expectedEx.expect(IllegalStateException.class);
expectedEx.expectMessage("but they are released");
vc.compareToAuthoritative(authoritativeReleasedVersions);
}
public void testCompareToAuthoritativeNotReallyRelesed() {
List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1")
.map(Version::fromString)
.collect(Collectors.toList());
VersionCollection vc = new VersionCollection(
listOfVersions.stream()
.map(this::formatVersionToLine)
.collect(Collectors.toList()),
Version.fromString("8.0.0")
);
expectedEx.expect(IllegalStateException.class);
expectedEx.expectMessage("not really released");
vc.compareToAuthoritative(authoritativeReleasedVersions);
}
private void assertUnreleasedGradleProjectNames(List<String> expectedNAmes, VersionCollection versionCollection) {
List<String> actualNames = new ArrayList<>();
versionCollection.forPreviousUnreleased(unreleasedVersion ->
actualNames.add(unreleasedVersion.gradleProjectName)
);
assertEquals(expectedNAmes, actualNames);
}
private void assertUnreleasedBranchNames(List<String> expectedBranches, VersionCollection versionCollection) {
List<String> actualBranches = new ArrayList<>();
versionCollection.forPreviousUnreleased(unreleasedVersionInfo ->
actualBranches.add(unreleasedVersionInfo.branch)
);
assertEquals(expectedBranches, actualBranches);
}
private String formatVersionToLine(final String version) {
return " public static final Version V_" + version.replaceAll("\\.", "_") + " ";
}
private void assertVersionsEquals(List<String> expected, List<Version> actual) {
assertEquals(
expected.stream()
.map(Version::fromString)
.collect(Collectors.toList()),
actual
);
}
private VersionCollection getVersionCollection(String currentVersion) {
return new VersionCollection(
sampleVersions.get(currentVersion).stream()
.map(this::formatVersionToLine)
.collect(Collectors.toList()),
Version.fromString(currentVersion)
);
}
}

View File

@ -44,16 +44,12 @@ public class VersionTests extends GradleUnitTestCase {
assertTrue("1.10.20 is not interpreted as before 2.0.0",
Version.fromString("1.10.20").before("2.0.0")
);
assertTrue("7.0.0-alpha1 is not interpreted as before 7.0.0-alpha2",
Version.fromString("7.0.0-alpha1").before("7.0.0-alpha2")
);
assertTrue("7.0.0-alpha1 should be equal to 7.0.0-alpha1",
Version.fromString("7.0.0-alpha1").equals(Version.fromString("7.0.0-alpha1"))
);
assertTrue("7.0.0-SNAPSHOT should be equal to 7.0.0-SNAPSHOT",
Version.fromString("7.0.0-SNAPSHOT").equals(Version.fromString("7.0.0-SNAPSHOT"))
);
assertEquals(Version.fromString("5.2.1-SNAPSHOT"), Version.fromString("5.2.1-SNAPSHOT"));
}
public void testCollections() {
@ -89,51 +85,10 @@ public class VersionTests extends GradleUnitTestCase {
new Version(7, 0, 0, "", true)
));
// snapshot is not taken into account TODO inconsistent with equals
assertEquals(
0,
new Version(7, 0, 0, "", false).compareTo(
new Version(7, 0, 0, null, true))
);
// without sufix is smaller than with TODO
assertOrder(
new Version(7, 0, 0, null, false),
new Version(7, 0, 0, "-alpha1", false)
);
// numbered sufix
assertOrder(
new Version(7, 0, 0, "-alpha1", false),
new Version(7, 0, 0, "-alpha2", false)
);
// ranked sufix
assertOrder(
new Version(7, 0, 0, "-alpha8", false),
new Version(7, 0, 0, "-rc1", false)
);
// ranked sufix
assertOrder(
new Version(7, 0, 0, "-alpha8", false),
new Version(7, 0, 0, "-beta1", false)
);
// ranked sufix
assertOrder(
new Version(7, 0, 0, "-beta8", false),
new Version(7, 0, 0, "-rc1", false)
);
// major takes precedence
assertOrder(
new Version(6, 10, 10, "-alpha8", true),
new Version(7, 0, 0, "-alpha2", false)
);
// then minor
assertOrder(
new Version(7, 0, 10, "-alpha8", true),
new Version(7, 1, 0, "-alpha2", false)
);
// then revision
assertOrder(
new Version(7, 1, 0, "-alpha8", true),
new Version(7, 1, 10, "-alpha2", false)
new Version(7, 0, 0, "-alpha1", false).compareTo(
new Version(7, 0, 0, "", true))
);
}
@ -149,18 +104,6 @@ public class VersionTests extends GradleUnitTestCase {
Version.fromString("foo.bar.baz");
}
public void testExceptionSuffixNumber() {
expectedEx.expect(IllegalArgumentException.class);
expectedEx.expectMessage("Invalid suffix");
new Version(7, 1, 1, "-alpha", true);
}
public void testExceptionSuffix() {
expectedEx.expect(IllegalArgumentException.class);
expectedEx.expectMessage("Suffix must contain one of:");
new Version(7, 1, 1, "foo1", true);
}
private void assertOrder(Version smaller, Version bigger) {
assertEquals(smaller + " should be smaller than " + bigger, -1, smaller.compareTo(bigger));
}

View File

@ -17,236 +17,225 @@
* under the License.
*/
import org.apache.tools.ant.taskdefs.condition.Os
import org.elasticsearch.gradle.LoggedExec
import org.elasticsearch.gradle.Version
import org.elasticsearch.gradle.VersionCollection
import java.nio.charset.StandardCharsets
import static org.elasticsearch.gradle.BuildPlugin.getJavaHome
/**
* This is a dummy project which does a local checkout of the previous
* wire compat version's branch, and builds a snapshot. This allows backcompat
* tests to test against the next unreleased version, closest to this version,
* without relying on snapshots.
* We want to be able to do BWC tests for unreleased versions without relying on and waiting for snapshots.
* For this we need to check out and build the unreleased versions.
* Since These depend on the current version, we can't name the Gradle projects statically, and don't know what the
* unreleased versions are when Gradle projects are set up, so we use "build-unreleased-version-*" as placeholders
* and configure them to build various versions here.
*/
subprojects {
bwcVersions.forPreviousUnreleased { VersionCollection.UnreleasedVersionInfo unreleasedVersion -> project("${project.path}:${unreleasedVersion.gradleProjectName}") {
Version bwcVersion = unreleasedVersion.version
String bwcBranch = unreleasedVersion.branch
apply plugin: 'distribution'
// Not published so no need to assemble
assemble.enabled = false
assemble.dependsOn.remove('buildBwcVersion')
Version bwcVersion = bwcVersions.getSnapshotForProject(project.name)
if (bwcVersion == null) {
// this project wont do anything
return
}
File checkoutDir = file("${buildDir}/bwc/checkout-${bwcBranch}")
String bwcBranch
if (project.name == 'next-minor-snapshot') {
// this is always a .x series
bwcBranch = "${bwcVersion.major}.x"
} else {
bwcBranch = "${bwcVersion.major}.${bwcVersion.minor}"
}
final String remote = System.getProperty("tests.bwc.remote", "elastic")
apply plugin: 'distribution'
// Not published so no need to assemble
assemble.enabled = false
assemble.dependsOn.remove('buildBwcVersion')
File checkoutDir = file("${buildDir}/bwc/checkout-${bwcBranch}")
final String remote = System.getProperty("tests.bwc.remote", "elastic")
final boolean gitFetchLatest
final String gitFetchLatestProperty = System.getProperty("tests.bwc.git_fetch_latest", "true")
if ("true".equals(gitFetchLatestProperty)) {
gitFetchLatest = true
} else if ("false".equals(gitFetchLatestProperty)) {
gitFetchLatest = false
} else {
throw new GradleException("tests.bwc.git_fetch_latest must be [true] or [false] but was [" + gitFetchLatestProperty + "]")
}
task createClone(type: LoggedExec) {
onlyIf { checkoutDir.exists() == false }
commandLine = ['git', 'clone', rootDir, checkoutDir]
}
task findRemote(type: LoggedExec) {
dependsOn createClone
workingDir = checkoutDir
commandLine = ['git', 'remote', '-v']
ByteArrayOutputStream output = new ByteArrayOutputStream()
standardOutput = output
doLast {
project.ext.remoteExists = false
output.toString('UTF-8').eachLine {
if (it.contains("${remote}\t")) {
project.ext.remoteExists = true
}
}
}
}
task addRemote(type: LoggedExec) {
dependsOn findRemote
onlyIf { project.ext.remoteExists == false }
workingDir = checkoutDir
commandLine = ['git', 'remote', 'add', "${remote}", "https://github.com/${remote}/elasticsearch.git"]
}
task fetchLatest(type: LoggedExec) {
onlyIf { project.gradle.startParameter.isOffline() == false && gitFetchLatest }
dependsOn addRemote
workingDir = checkoutDir
commandLine = ['git', 'fetch', '--all']
}
String buildMetadataKey = "bwc_refspec_${project.path.substring(1)}"
task checkoutBwcBranch(type: LoggedExec) {
String refspec = System.getProperty("tests.bwc.refspec.${bwcBranch}", buildMetadata.get(buildMetadataKey, "${remote}/${bwcBranch}"))
dependsOn fetchLatest
workingDir = checkoutDir
commandLine = ['git', 'checkout', refspec]
doFirst {
println "Checking out elasticsearch ${refspec} for branch ${bwcBranch}"
}
}
File buildMetadataFile = project.file("build/${project.name}/build_metadata")
task writeBuildMetadata(type: LoggedExec) {
dependsOn checkoutBwcBranch
workingDir = checkoutDir
commandLine = ['git', 'rev-parse', 'HEAD']
ignoreExitValue = true
ByteArrayOutputStream output = new ByteArrayOutputStream()
standardOutput = output
doLast {
if (execResult.exitValue != 0) {
output.toString('UTF-8').eachLine { line -> logger.error(line) }
execResult.assertNormalExitValue()
}
project.mkdir(buildMetadataFile.parent)
String commit = output.toString('UTF-8')
buildMetadataFile.setText("${buildMetadataKey}=${commit}", 'UTF-8')
println "Checked out elasticsearch commit ${commit}"
}
}
List<File> artifactFiles = []
List<String> projectDirs = []
for (String project : ['zip', 'deb', 'rpm']) {
String baseDir = "distribution"
if (bwcVersion.onOrAfter('6.3.0')) {
baseDir += project == 'zip' ? '/archives' : '/packages'
// add oss variant first
projectDirs.add("${baseDir}/oss-${project}")
artifactFiles.add(file("${checkoutDir}/${baseDir}/oss-${project}/build/distributions/elasticsearch-oss-${bwcVersion}.${project}"))
}
projectDirs.add("${baseDir}/${project}")
artifactFiles.add(file("${checkoutDir}/${baseDir}/${project}/build/distributions/elasticsearch-${bwcVersion}.${project}"))
}
task buildBwcVersion(type: Exec) {
dependsOn checkoutBwcBranch, writeBuildMetadata
workingDir = checkoutDir
doFirst {
// Execution time so that the checkouts are available
List<String> lines = file("$checkoutDir/.ci/java-versions.properties").readLines()
environment(
'JAVA_HOME',
getJavaHome(it, Integer.parseInt(
lines
.findAll({ it.startsWith("ES_BUILD_JAVA=java") })
.collect({ it.replace("ES_BUILD_JAVA=java", "").trim() })
.join("!!")
))
)
environment(
'RUNTIME_JAVA_HOME',
getJavaHome(it, Integer.parseInt(
lines
.findAll({ it.startsWith("ES_RUNTIME_JAVA=java") })
.collect({ it.replace("ES_RUNTIME_JAVA=java", "").trim() })
.join("!!")
))
)
}
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
executable 'cmd'
args '/C', 'call', new File(checkoutDir, 'gradlew').toString()
final boolean gitFetchLatest
final String gitFetchLatestProperty = System.getProperty("tests.bwc.git_fetch_latest", "true")
if ("true".equals(gitFetchLatestProperty)) {
gitFetchLatest = true
} else if ("false".equals(gitFetchLatestProperty)) {
gitFetchLatest = false
} else {
executable new File(checkoutDir, 'gradlew').toString()
throw new GradleException("tests.bwc.git_fetch_latest must be [true] or [false] but was [" + gitFetchLatestProperty + "]")
}
if (gradle.startParameter.isOffline()) {
args "--offline"
}
for (String dir : projectDirs) {
args ":${dir.replace('/', ':')}:assemble"
}
args "-Dbuild.snapshot=true"
final LogLevel logLevel = gradle.startParameter.logLevel
if ([LogLevel.QUIET, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG].contains(logLevel)) {
args "--${logLevel.name().toLowerCase(Locale.ENGLISH)}"
}
final String showStacktraceName = gradle.startParameter.showStacktrace.name()
assert ["INTERNAL_EXCEPTIONS", "ALWAYS", "ALWAYS_FULL"].contains(showStacktraceName)
if (showStacktraceName.equals("ALWAYS")) {
args "--stacktrace"
} else if (showStacktraceName.equals("ALWAYS_FULL")) {
args "--full-stacktrace"
}
standardOutput = new IndentingOutputStream(System.out)
errorOutput = new IndentingOutputStream(System.err)
doLast {
List missing = artifactFiles.grep { file ->
false == file.exists()
}
if (false == missing.empty) {
throw new InvalidUserDataException(
"Building bwc version didn't generate expected files ${missing}")
}
}
}
if (gradle.startParameter.taskNames == ["assemble"]) {
// Gradle needs the `artifacts` declaration, including `builtBy` bellow to make projects dependencies on this
// project work, but it will also trigger the build of these for the `assemble` task.
// Since these are only used for testing, we don't want to assemble them if `assemble` is the single command being
// ran.
logger.info("Skipping BWC builds since `assemble` is the only task name provided on the command line")
} else {
artifacts {
for (File artifactFile : artifactFiles) {
String artifactName = artifactFile.name.contains('oss') ? 'elasticsearch-oss' : 'elasticsearch'
String suffix = artifactFile.toString()[-3..-1]
'default' file: artifactFile, name: artifactName, type: suffix, builtBy: buildBwcVersion
}
task createClone(type: LoggedExec) {
onlyIf { checkoutDir.exists() == false }
commandLine = ['git', 'clone', rootDir, checkoutDir]
}
}
}
task findRemote(type: LoggedExec) {
dependsOn createClone
workingDir = checkoutDir
commandLine = ['git', 'remote', '-v']
ByteArrayOutputStream output = new ByteArrayOutputStream()
standardOutput = output
doLast {
project.ext.remoteExists = false
output.toString('UTF-8').eachLine {
if (it.contains("${remote}\t")) {
project.ext.remoteExists = true
}
}
}
}
task addRemote(type: LoggedExec) {
dependsOn findRemote
onlyIf { project.ext.remoteExists == false }
workingDir = checkoutDir
commandLine = ['git', 'remote', 'add', "${remote}", "https://github.com/${remote}/elasticsearch.git"]
}
task fetchLatest(type: LoggedExec) {
onlyIf { project.gradle.startParameter.isOffline() == false && gitFetchLatest }
dependsOn addRemote
workingDir = checkoutDir
commandLine = ['git', 'fetch', '--all']
}
String buildMetadataKey = "bwc_refspec_${project.path.substring(1)}"
task checkoutBwcBranch(type: LoggedExec) {
String refspec = System.getProperty("tests.bwc.refspec.${bwcBranch}", buildMetadata.get(buildMetadataKey, "${remote}/${bwcBranch}"))
dependsOn fetchLatest
workingDir = checkoutDir
commandLine = ['git', 'checkout', refspec]
doFirst {
println "Checking out elasticsearch ${refspec} for branch ${bwcBranch}"
}
}
File buildMetadataFile = project.file("build/${project.name}/build_metadata")
task writeBuildMetadata(type: LoggedExec) {
dependsOn checkoutBwcBranch
workingDir = checkoutDir
commandLine = ['git', 'rev-parse', 'HEAD']
ignoreExitValue = true
ByteArrayOutputStream output = new ByteArrayOutputStream()
standardOutput = output
doLast {
if (execResult.exitValue != 0) {
output.toString('UTF-8').eachLine { line -> logger.error(line) }
execResult.assertNormalExitValue()
}
project.mkdir(buildMetadataFile.parent)
String commit = output.toString('UTF-8')
buildMetadataFile.setText("${buildMetadataKey}=${commit}", 'UTF-8')
println "Checked out elasticsearch commit ${commit}"
}
}
List<File> artifactFiles = []
List<String> projectDirs = []
for (String project : ['zip', 'deb', 'rpm']) {
String baseDir = "distribution"
if (bwcVersion.onOrAfter('6.3.0')) {
baseDir += project == 'zip' ? '/archives' : '/packages'
// add oss variant first
projectDirs.add("${baseDir}/oss-${project}")
artifactFiles.add(file("${checkoutDir}/${baseDir}/oss-${project}/build/distributions/elasticsearch-oss-${bwcVersion}.${project}"))
}
projectDirs.add("${baseDir}/${project}")
artifactFiles.add(file("${checkoutDir}/${baseDir}/${project}/build/distributions/elasticsearch-${bwcVersion}.${project}"))
}
task buildBwcVersion(type: Exec) {
dependsOn checkoutBwcBranch, writeBuildMetadata
workingDir = checkoutDir
doFirst {
// Execution time so that the checkouts are available
List<String> lines = file("${checkoutDir}/.ci/java-versions.properties").readLines()
environment(
'JAVA_HOME',
getJavaHome(it, Integer.parseInt(
lines
.findAll({ it.startsWith("ES_BUILD_JAVA=java") })
.collect({ it.replace("ES_BUILD_JAVA=java", "").trim() })
.join("!!")
))
)
environment(
'RUNTIME_JAVA_HOME',
getJavaHome(it, Integer.parseInt(
lines
.findAll({ it.startsWith("ES_RUNTIME_JAVA=java") })
.collect({ it.replace("ES_RUNTIME_JAVA=java", "").trim() })
.join("!!")
))
)
}
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
executable 'cmd'
args '/C', 'call', new File(checkoutDir, 'gradlew').toString()
} else {
executable new File(checkoutDir, 'gradlew').toString()
}
if (gradle.startParameter.isOffline()) {
args "--offline"
}
for (String dir : projectDirs) {
args ":${dir.replace('/', ':')}:assemble"
}
args "-Dbuild.snapshot=true"
final LogLevel logLevel = gradle.startParameter.logLevel
if ([LogLevel.QUIET, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG].contains(logLevel)) {
args "--${logLevel.name().toLowerCase(Locale.ENGLISH)}"
}
final String showStacktraceName = gradle.startParameter.showStacktrace.name()
assert ["INTERNAL_EXCEPTIONS", "ALWAYS", "ALWAYS_FULL"].contains(showStacktraceName)
if (showStacktraceName.equals("ALWAYS")) {
args "--stacktrace"
} else if (showStacktraceName.equals("ALWAYS_FULL")) {
args "--full-stacktrace"
}
standardOutput = new IndentingOutputStream(System.out, bwcVersion)
errorOutput = new IndentingOutputStream(System.err, bwcVersion)
doLast {
List missing = artifactFiles.grep { file ->
false == file.exists()
}
if (false == missing.empty) {
throw new InvalidUserDataException(
"Building ${bwcVersion} didn't generate expected files ${missing}")
}
}
}
if (gradle.startParameter.taskNames == ["assemble"]) {
// Gradle needs the `artifacts` declaration, including `builtBy` bellow to make projects dependencies on this
// project work, but it will also trigger the build of these for the `assemble` task.
// Since these are only used for testing, we don't want to assemble them if `assemble` is the single command being
// ran.
logger.info("Skipping BWC builds since `assemble` is the only task name provided on the command line")
} else {
artifacts {
for (File artifactFile : artifactFiles) {
String artifactName = artifactFile.name.contains('oss') ? 'elasticsearch-oss' : 'elasticsearch'
String suffix = artifactFile.toString()[-3..-1]
'default' file: artifactFile, name: artifactName, type: suffix, builtBy: buildBwcVersion
}
}
}
}}
class IndentingOutputStream extends OutputStream {
public static final byte[] INDENT = " [bwc] ".getBytes(StandardCharsets.UTF_8)
private final OutputStream delegate
public final byte[] indent
private final OutputStream delegate
public IndentingOutputStream(OutputStream delegate) {
this.delegate = delegate
}
@Override
public void write(int b) {
write([b] as int[], 0, 1)
}
public void write(int[] bytes, int offset, int length) {
for (int i = 0; i < bytes.length; i++) {
delegate.write(bytes[i])
if (bytes[i] == '\n') {
delegate.write(INDENT)
}
public IndentingOutputStream(OutputStream delegate, Object version) {
this.delegate = delegate
indent = " [${version}] ".getBytes(StandardCharsets.UTF_8)
}
@Override
public void write(int b) {
write([b] as int[], 0, 1)
}
public void write(int[] bytes, int offset, int length) {
for (int i = 0; i < bytes.length; i++) {
delegate.write(bytes[i])
if (bytes[i] == '\n') {
delegate.write(indent)
}
}
}
}
}

View File

@ -98,7 +98,7 @@ test.enabled = false // no unit tests for rolling upgrades, only the rest integr
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsIndexCompatible) {
for (final def version : bwcVersions.unreleasedIndexCompatible) {
dependsOn "v${version}#bwcTest"
}
}

View File

@ -65,7 +65,7 @@ test.enabled = false // no unit tests for rolling upgrades, only the rest integr
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsWireCompatible) {
for (final def version : bwcVersions.unreleasedWireCompatible) {
dependsOn "v${version}#bwcTest"
}
}

View File

@ -145,7 +145,7 @@ test.enabled = false // no unit tests for rolling upgrades, only the rest integr
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsWireCompatible) {
for (final def version : bwcVersions.unreleasedWireCompatible) {
dependsOn "v${version}#bwcTest"
}
}

View File

@ -57,7 +57,7 @@ test.enabled = false
task integTest {
if (project.bwc_tests_enabled) {
final def version = bwcVersions.snapshotsIndexCompatible.first()
final def version = bwcVersions.unreleasedIndexCompatible.first()
dependsOn "v${version}#bwcTest"
}
}

View File

@ -24,10 +24,10 @@ List projects = [
'distribution:packages:deb',
'distribution:packages:oss-rpm',
'distribution:packages:rpm',
'distribution:bwc:next-minor-snapshot',
'distribution:bwc:staged-minor-snapshot',
'distribution:bwc:next-bugfix-snapshot',
'distribution:bwc:maintenance-bugfix-snapshot',
'distribution:bwc:bugfix',
'distribution:bwc:maintenance',
'distribution:bwc:minor',
'distribution:bwc:staged',
'distribution:tools:java-version-checker',
'distribution:tools:launchers',
'distribution:tools:plugin-cli',

View File

@ -237,7 +237,7 @@ subprojects {
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsIndexCompatible) {
for (final def version : bwcVersions.unreleasedIndexCompatible) {
dependsOn "v${version}#bwcTest"
}
}

View File

@ -125,7 +125,7 @@ test.enabled = false // no unit tests for rolling upgrades, only the rest integr
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsWireCompatible) {
for (final def version : bwcVersions.unreleasedWireCompatible) {
dependsOn "v${version}#bwcTest"
}
}

View File

@ -281,7 +281,7 @@ subprojects {
// basic integ tests includes testing bwc against the most recent version
task integTest {
if (project.bwc_tests_enabled) {
for (final def version : bwcVersions.snapshotsWireCompatible) {
for (final def version : bwcVersions.unreleasedWireCompatible) {
dependsOn "v${version}#bwcTest"
}
}