-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement VersionCollection in Java #34050
Changes from 4 commits
0e475b0
17b9734
8ab7df8
b08b3b6
bd03af5
f0ce80f
4f5ef6d
ca79ab9
fbf8f3e
c72cc95
ff69345
4d54ac8
d01c914
6962565
555ef78
a27505f
c2041d2
5d11cd4
dc328a7
179e54c
7d4a144
7252607
8032307
c576684
47a7b79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
/* | ||
* 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.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.SortedSet; | ||
import java.util.TreeSet; | ||
import java.util.function.Consumer; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.IntStream; | ||
|
||
/** | ||
* Parse the Java source file containing the versions declarations and use the known rules to figure out which are all | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This (and all javadocs) should start off with a simple one line description. In this case, something like:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first comment here seems like it is specific to the constructor? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add the summary, thanks! |
||
* 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. | ||
*/ | ||
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 SortedSet<Version> versions = new TreeSet<>(); | ||
private Version currentVersion; | ||
|
||
public class UnreleasedVersionDescription { | ||
alpar-t marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private final Version version; | ||
private final Integer index; | ||
private final String branch; | ||
|
||
public UnreleasedVersionDescription(Integer index, Version version, String branch) { | ||
this.version = version; | ||
this.index = index; | ||
this.branch = branch; | ||
} | ||
|
||
public Version getVersion() { | ||
alpar-t marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return version; | ||
} | ||
|
||
public Integer getIndex() { | ||
return index; | ||
} | ||
|
||
public String getBranch() { | ||
return branch; | ||
} | ||
} | ||
|
||
public VersionCollection(List<String> versionLines) { | ||
this(versionLines, VersionProperties.getElasticsearch()); | ||
} | ||
|
||
protected VersionCollection(List<String> versionLines, Version currentVersionProperty) { | ||
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 | ||
)) | ||
.filter(version -> version.getSuffix().isEmpty() || version.equals(currentVersionProperty)) | ||
.forEach(version -> { | ||
if (versions.add(version) == false) { | ||
throw new IllegalStateException("Found duplicate while parsing all versions: " + version); | ||
} | ||
}); | ||
if (versions.isEmpty()) { | ||
throw new IllegalArgumentException("Could not parse any versions"); | ||
} | ||
|
||
assertCurrentVersionMatchesParsed(currentVersionProperty); | ||
|
||
assertNoOlderThanTwoMajors(); | ||
} | ||
|
||
private void assertNoOlderThanTwoMajors() { | ||
SortedSet<Version> versionsMoreThanTowMajorsAgo = versions | ||
.headSet(new Version(currentVersion.getMajor() - 1, 0, 0, "", false)); | ||
if (versionsMoreThanTowMajorsAgo.isEmpty() == false) { | ||
throw new IllegalStateException( | ||
"Found unexpected parsed versions, more than two majors ago: " + versionsMoreThanTowMajorsAgo | ||
); | ||
} | ||
} | ||
|
||
private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) { | ||
currentVersion = versions.last(); | ||
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 forEachUnreleased(Consumer<UnreleasedVersionDescription> consumer) { | ||
List<Version> unreleasedVersionsList = new ArrayList<>(getUnreleased()); | ||
IntStream.range(0, unreleasedVersionsList.size()) | ||
.filter(index -> unreleasedVersionsList.get(index).equals(currentVersion) == false) | ||
.forEach(index -> | ||
consumer.accept(new UnreleasedVersionDescription( | ||
index, | ||
unreleasedVersionsList.get(index), | ||
getBranchFor(unreleasedVersionsList.get(index)) | ||
)) | ||
); | ||
} | ||
|
||
protected String getBranchFor(Version unreleasedVersion) { | ||
SortedSet<Version> unreleased = getUnreleased(); | ||
if (unreleased.contains(unreleasedVersion)) { | ||
if (unreleasedVersion.getMinor() == 0 && unreleasedVersion.getRevision() == 0) { | ||
return "master"; | ||
} | ||
if (unreleasedVersion.getRevision() != 0) { | ||
return unreleasedVersion.getMajor() + "." + unreleasedVersion.getMinor(); | ||
} | ||
Optional<Version> nextUnreleased = getVersionAfter(unreleased, unreleasedVersion); | ||
if (nextUnreleased.isPresent() && nextUnreleased.get().getMajor() == unreleasedVersion.getMajor()) { | ||
return unreleasedVersion.getMajor() + "." + unreleasedVersion.getMinor(); | ||
} | ||
return unreleasedVersion.getMajor() + ".x"; | ||
} else { | ||
throw new IllegalStateException( | ||
"Can't get branch of released version: " + unreleasedVersion + " unreleased versions are: " + unreleased | ||
); | ||
} | ||
} | ||
|
||
public SortedSet<Version> getUnreleased() { | ||
SortedSet<Version> unreleased = new TreeSet<>(); | ||
// The current version is being worked, is always unreleased | ||
unreleased.add(currentVersion); | ||
// The tip of the previous major will also be unreleased | ||
unreleased.add(getLatestBugfixBeforeMajor(currentVersion)); | ||
// Initial releases of minors and majors will have more unreleased versions before them | ||
if (currentVersion.getRevision() == 0) { | ||
Version greatestPreviousMinor = getLatestBugfixBeforeMinor(currentVersion); | ||
unreleased.add(greatestPreviousMinor); | ||
Version greatestMinorBehindByTwo = getLatestBugfixBeforeMinor(greatestPreviousMinor); | ||
if (greatestPreviousMinor.getRevision() == 0) { | ||
// So we have x.y.0 and x.(y-1).0 with nothing in-between. This happens when x.y.0 is feature freeze. | ||
// We'll add x.y.1 as soon as x.y.0 releases so this is correct. | ||
unreleased.add(greatestMinorBehindByTwo); | ||
} | ||
} | ||
return unreleased; | ||
} | ||
|
||
public SortedSet<Version> getIndexCompatible() { | ||
return Collections.unmodifiableSortedSet( | ||
versions | ||
.tailSet(new Version(currentVersion.getMajor() - 1, 0, 0)) | ||
.headSet(currentVersion) | ||
); | ||
} | ||
|
||
public SortedSet<Version> getWireCompatible() { | ||
final Version lastPrevMajor = getLatestBugfixBeforeMajor(currentVersion); | ||
return Collections.unmodifiableSortedSet( | ||
versions | ||
.tailSet(new Version(lastPrevMajor.getMajor(), lastPrevMajor.getMinor(), 0)) | ||
.headSet(currentVersion) | ||
); | ||
} | ||
|
||
public SortedSet<Version> getUnreleasedIndexCompatible() { | ||
SortedSet<Version> unreleasedIndexCompatible = new TreeSet<>(getIndexCompatible()); | ||
unreleasedIndexCompatible.retainAll(getUnreleased()); | ||
return Collections.unmodifiableSortedSet(unreleasedIndexCompatible); | ||
} | ||
|
||
public SortedSet<Version> getUnreleasedWireCompatible() { | ||
SortedSet<Version> unreleasedWireCompatible = new TreeSet<>(getWireCompatible()); | ||
unreleasedWireCompatible.retainAll(getUnreleased()); | ||
return Collections.unmodifiableSortedSet(unreleasedWireCompatible); | ||
} | ||
|
||
private Version getLatestBugfixBeforeMajor(Version version) { | ||
return getVersionBefore(new Version(version.getMajor(), 0, 0)); | ||
} | ||
|
||
private Version getLatestBugfixBeforeMinor(Version version) { | ||
return getVersionBefore(new Version(version.getMajor(), version.getMinor(), 0)); | ||
} | ||
|
||
private Version getVersionBefore(Version e) { | ||
return versions | ||
.headSet(e).last(); | ||
} | ||
|
||
private Optional<Version> getVersionAfter(SortedSet<Version> versions, Version version) { | ||
Iterator<Version> iterator = versions | ||
.tailSet(version).iterator(); | ||
if (iterator.hasNext()) { | ||
iterator.next(); | ||
if (iterator.hasNext()) { | ||
return Optional.of(iterator.next()); | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check is moved into version collections.
I tried to move checks and decisions there for better visibility.