diff --git a/build.gradle b/build.gradle
index b11a3fc..7a608e5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,8 +30,8 @@ apply plugin: 'eclipse'
apply plugin: 'idea'
java {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
defaultTasks 'clean','build'
@@ -40,8 +40,8 @@ group = 'com.github.kdebisschop'
ext.rundeckPluginVersion = '1.2'
ext.pluginClassNames='com.bioraft.rundeck.nexus.Nexus3OptionProvider'
-ext.pluginName = 'Nexus3 Option Values Porvider'
-ext.pluginDescription = 'Fetches and filters assests from a Nexus3 repository'
+ext.pluginName = 'Nexus3 Option Values Provider'
+ext.pluginDescription = 'Fetches and filters assets from a Nexus3 repository'
scmVersion {
ignoreUncommittedChanges = true
@@ -65,7 +65,6 @@ scmVersion {
project.version = scmVersion.version
repositories {
- mavenLocal()
mavenCentral()
}
@@ -86,9 +85,10 @@ gradle.projectsEvaluated {
}
dependencies {
- implementation 'org.rundeck:rundeck-core:3.4.10-20220118'
+ implementation 'org.rundeck:rundeck-core:5.4.0-20240618'
+ implementation 'org.apache.maven:maven-artifact:3.9.8'
- testImplementation group: 'junit', name: 'junit', version:'4.12'
+ testImplementation group: 'junit', name: 'junit', version: '4.13.1'
testImplementation (
'org.mockito:mockito-all:1.10.19',
diff --git a/src/main/java/com/bioraft/rundeck/nexus/BranchOrVersion.java b/src/main/java/com/bioraft/rundeck/nexus/BranchOrVersion.java
index 0b90bc1..48ac3cf 100644
--- a/src/main/java/com/bioraft/rundeck/nexus/BranchOrVersion.java
+++ b/src/main/java/com/bioraft/rundeck/nexus/BranchOrVersion.java
@@ -15,38 +15,34 @@
*/
package com.bioraft.rundeck.nexus;
-import java.util.Arrays;
-import java.util.Iterator;
-
-import org.apache.commons.lang.builder.CompareToBuilder;
+import org.apache.maven.artifact.versioning.ComparableVersion;
/**
* Puts branches or versions followed by a build designator into a uniform structure.
- *
- * Expands three-part semantic versions to handle cases where there are more
- * than 3 parts and where build specifier may be attached by "_" or "+". (Although
- * it would really just be better if people only used semantic versions.)
- *
- * Also puts branches into a similar structure. In particular, branches like
+ *
+ *
Parses versions and branches where there is a build specifier attached by "-",
+ * "_" or "+".
+ *
+ *
Also puts branches into a similar structure. In particular, branches like
* ISSUE-1234-bug-description will be sorted numerically.
- *
- * In particular, the algorithm assumes each componentVersion or docker image tag
+ *
+ *
In particular, the algorithm assumes each componentVersion or docker image tag
* is composed of a version or branch followed by a build designator. The build
* designator is first extracted by looking for a part of the componentVersion
* after either "-", "_", or "+". If there is more than one [_+-] the match looks
* for the last one.
- *
- * The part before the build designator (and separator) is considered the version
+ *
+ *
The part before the build designator (and separator) is considered the version
* or branch. If there are two consecutive integers separated by a decimal, then it
- * is considered a version specifier. Otherwise it is considered a branch name. In
+ * is considered a version specifier. Otherwise, it is considered a branch name. In
* either case, it is split into parts at each period (i.e., ".").
- *
- * Each part of the versionOrBranch and the build specifier are considered in 3 parts:
+ *
+ *
Each part of the versionOrBranch and the build specifier are considered in 3 parts:
* - Zero or more non-numeric initial characters
* - Zero or more integers
* - Zero or more concluding characters that may be a mix of integers and non-integers
- *
- * These fields in order are used to sort the assets.
+ *
+ *
These fields in order are used to sort the assets.
*
* @author Karl DeBisschop
* @since 2019-12-26
@@ -55,11 +51,11 @@ public class BranchOrVersion {
public static final String BUILD_SEPARATOR_REGEX = "[_+-]";
- String artifactId;
- String versionOrBranch;
- String build;
- String sep;
- String[] parts;
+ private final String artifactId;
+ private final String versionOrBranch;
+ private String build;
+ private final String componentVersion;
+ private final String comparator;
public String getArtifactId() {
return artifactId;
@@ -75,21 +71,24 @@ public String getVersion() {
public BranchOrVersion(String path) {
artifactId = component(path);
- String tag = tag(path);
- if (tag.matches("^.+" + BUILD_SEPARATOR_REGEX + "[a-zA-Z0-9]+$")) {
- build = tag.replaceFirst("^.+" + BUILD_SEPARATOR_REGEX + "([a-zA-Z0-9]+)$", "$1");
- versionOrBranch = tag.replaceFirst("^(.+)" + BUILD_SEPARATOR_REGEX + build + "$", "$1");
- sep = tag.replaceFirst("^.+(" + BUILD_SEPARATOR_REGEX + ")" + build + "$", "$1");
+ componentVersion = tag(path);
+ String sep;
+ if (componentVersion.matches("^.+" + BUILD_SEPARATOR_REGEX + "[a-zA-Z0-9_-]+$")) {
+ build = componentVersion.replaceFirst("^.+" + BUILD_SEPARATOR_REGEX + "([a-zA-Z0-9_-]+)$", "$1");
+ versionOrBranch = componentVersion.replaceFirst("^(.+)" + BUILD_SEPARATOR_REGEX + build + "$", "$1");
+ sep = componentVersion.replaceFirst("^.+(" + BUILD_SEPARATOR_REGEX + ")" + build + "$", "$1");
} else {
build = "";
- versionOrBranch = tag;
+ versionOrBranch = componentVersion;
sep = "";
}
- this.parts = versionOrBranch.split("[.]");
- }
-
- public Iterator getIterator() {
- return Arrays.asList(parts).iterator();
+ if (componentVersion.matches("^rc(\\d+[.].*)")) {
+ build = build + "rc";
+ if (sep.isEmpty()) {
+ sep = "-";
+ }
+ }
+ comparator = componentVersion.replaceFirst("^(v|rc)(\\d+[.].*)", "$2") + sep + build;
}
/**
@@ -97,13 +96,12 @@ public Iterator getIterator() {
* Anything else is considered to be a branch.
*/
public boolean isVersion() {
- return versionOrBranch.matches(".*[0-9]+[.][0-9]+.*");
+ return versionOrBranch.matches(".*\\d+[.]\\d+.*");
}
/**
- * Use array of parts to do comparison so it can compare regardless of number of
- * parts in a version specifier.
- *
+ * Decorate maven ComparableVersion to handle a few edge cases.
+ *
* @param other The object to compare against.
* @return Return 0 if equal, 1 if this is greater, -1 if that is greater.
*/
@@ -112,50 +110,13 @@ public int compareTo(Object other) {
return 1;
}
BranchOrVersion that = (BranchOrVersion) other;
- CompareToBuilder comparator = new CompareToBuilder();
- Iterator thatIterator = that.getIterator();
- Iterator thisIterator = getIterator();
- while (thatIterator.hasNext()) {
- if (!thisIterator.hasNext()) {
- if (comparator.toComparison() == 0) {
- return -1;
- } else {
- break;
- }
- }
- String thisOne = thisIterator.next();
- String thatOne = thatIterator.next();
- comparator.append(partOne(thisOne), partOne(thatOne));
- comparator.append(partTwo(thisOne), partTwo(thatOne));
- comparator.append(partThree(thisOne), partThree(thatOne));
- }
- if (comparator.toComparison() == 0 && thisIterator.hasNext()) {
- return 1;
- }
- comparator.append(partOne(this.build), partOne(that.build));
- comparator.append(partTwo(this.build), partTwo(that.build));
- comparator.append(partThree(this.build), partThree(that.build));
- return comparator.toComparison();
- }
-
- private String partOne(String string) {
- return string.replaceFirst("^([^0-9]*).*$", "$1");
- }
-
- private Integer partTwo(String string) {
- String integerPart = string.replaceFirst("^[^0-9]*([0-9]*).*$", "$1");
- if (integerPart.length() == 0) {
- return 0;
- }
- return Integer.parseInt(integerPart);
- }
-
- private String partThree(String string) {
- return string.replaceFirst("^[^0-9]*[0-9]*", "");
+ ComparableVersion thisVersion = new ComparableVersion(comparator);
+ ComparableVersion thatVersion = new ComparableVersion(that.comparator);
+ return Integer.signum(thisVersion.compareTo(thatVersion));
}
public String toString() {
- return artifactId + ":" + String.join(".", parts) + sep + build;
+ return artifactId + ":" + componentVersion;
}
/**
@@ -168,7 +129,7 @@ private String component(String path) {
/**
* Extracts tag part of a path. The tag (or componentVersion in the Nexus query)
* can reflect either a branch name or a semantic version. Either branches or
- * versions may be suffixed by a build specified, which can be numeric or
+ * versions may be suffixed by a build specifier, which can be numeric or
* string-valued.
*/
private String tag(String path) {
diff --git a/src/main/java/com/bioraft/rundeck/nexus/Nexus3OptionProvider.java b/src/main/java/com/bioraft/rundeck/nexus/Nexus3OptionProvider.java
index 35439ac..42f22d4 100644
--- a/src/main/java/com/bioraft/rundeck/nexus/Nexus3OptionProvider.java
+++ b/src/main/java/com/bioraft/rundeck/nexus/Nexus3OptionProvider.java
@@ -32,7 +32,7 @@ public class Nexus3OptionProvider implements OptionValuesPlugin {
public static final String PLUGIN_NAME = "Nexus3OptionProvider";
- private OkHttpClient client;
+ private final OkHttpClient client;
@PluginProperty(title = "Endpoint scheme", description = "Nexus server scheme", required = true, defaultValue = "https", scope = PropertyScope.Project)
private String endpointScheme;
@@ -89,7 +89,7 @@ public List getOptionValues(Map configuration) {
private void setVariable(Map configuration, String variableName, String variable) {
if (configuration.containsKey(variableName)) {
config.put(variableName, configuration.get(variableName).toString());
- } else if (variable != null && variable.length() > 0) {
+ } else if (variable != null && !variable.isEmpty()) {
config.put(variableName, variable);
}
}
diff --git a/src/main/java/com/bioraft/rundeck/nexus/OptionProviderImpl.java b/src/main/java/com/bioraft/rundeck/nexus/OptionProviderImpl.java
index 8b4ac06..be47702 100644
--- a/src/main/java/com/bioraft/rundeck/nexus/OptionProviderImpl.java
+++ b/src/main/java/com/bioraft/rundeck/nexus/OptionProviderImpl.java
@@ -31,7 +31,7 @@
* Expands three-part semantic versions to handle cases where there are more
* than 3 parts and where build specifier may be attached by "_" or "+".
*
- * It would really just be better if people only used semantic versions.
+ * It would really just be better if people only used semantic versions.
*
* @author Karl DeBisschop
* @since 2019-12-26
@@ -40,7 +40,7 @@ public class OptionProviderImpl {
static final String CONTINUATION_TOKEN = "continuationToken";
- private OkHttpClient client;
+ private final OkHttpClient client;
private Map config;
@@ -105,12 +105,13 @@ public List getOptionValues(Map config) {
/**
* Update the Branches Map.
+ *
* @param current The branch or version object we are considering.
* @param versionOrBuild The version or build of the object we are considering.
*/
private void updateBranchOrVersionMap(BranchOrVersion current, String versionOrBuild, Map map) {
// If we are already tracking the branch, check to ensure this is newer before saving.
- // Otherwise, it is a new branc to us so start tracking it.
+ // Otherwise, it is a new branch to us so start tracking it.
if (map.containsKey(versionOrBuild)) {
if (current.compareTo(map.get(versionOrBuild)) > 0) {
map.put(versionOrBuild, current);
@@ -135,10 +136,7 @@ static SortedSet> entriesSortedByValues(
}
/**
- * Perform the Nexus API request.
- *
- * This is really just syntactic sugar for the initial request (with no
- * continuation token). But it adds some clarity.
+ * Perform the initial Nexus API request.
*
* @return An ArrayList of path strings.
*/
@@ -149,7 +147,7 @@ private ArrayList nexusSearch() {
/**
* Perform the Nexus API request, including continuation for larger result sets.
*
- * Since all we need to prepare the option list is path, extract that from the
+ * Since all we need to prepare the option list is path, extract that from the
* JsonNode and return just a list of strings in the interest of conserving
* system resources.
*
@@ -179,7 +177,7 @@ private ArrayList nexusSearch(String continuationToken) {
return new ArrayList<>();
}
json = body.string();
- if (json.length() == 0) {
+ if (json.isEmpty()) {
return new ArrayList<>();
}
} catch (NullPointerException | IOException e) {
@@ -198,7 +196,7 @@ private ArrayList nexusSearch(String continuationToken) {
}
if (tree.has(CONTINUATION_TOKEN)) {
String token = tree.path(CONTINUATION_TOKEN).asText();
- if (token.length() > 0) {
+ if (!token.isEmpty()) {
ArrayList nextItems = nexusSearch(token);
itemList.addAll(nextItems);
}
@@ -225,8 +223,8 @@ private HttpUrl.Builder buildUrl(String endpoint, String continuationToken) {
/**
* Provides a means of informing users that the Nexus host still needs to be
* configured.
- *
- * All methods are straightforward implementations of the interface.
+ *
+ * All methods are straightforward implementations of the interface.
*/
static class ErrorOptionValue implements OptionValue {
String name;
diff --git a/src/test/java/com/bioraft/rundeck/nexus/BranchOrVersionTest.java b/src/test/java/com/bioraft/rundeck/nexus/BranchOrVersionTest.java
index 3dd56fe..eb61e3f 100644
--- a/src/test/java/com/bioraft/rundeck/nexus/BranchOrVersionTest.java
+++ b/src/test/java/com/bioraft/rundeck/nexus/BranchOrVersionTest.java
@@ -45,40 +45,36 @@ public void testOne() {
@Test
public void testComparison() {
- BranchOrVersion testSubject;
- final String COMPONENT = "COMP";
+ BranchOrVersion version;
+ final String COMPONENT = "example.org/component";
final String EMPTY = "";
- final String DASH = "-";
- testSubject = subject(path(COMPONENT, "1.2.3", DASH, "3"));
-
- assertEquals(0, testSubject.compareTo(subject(path(COMPONENT, "1.2.3", DASH, "3"))));
-
- // 1.2.3-3 is greater than null
- assertEquals(1, testSubject.compareTo(null));
- // 1.2.3-3 is greater than 1.2.3-2
- assertEquals(1, testSubject.compareTo(subject(path(COMPONENT, "1.2.3", DASH, "2"))));
- // 1.2.3-3 is greater than 1.2.2-3
- assertEquals(1, testSubject.compareTo(subject(path(COMPONENT, "1.2.2", DASH, "3"))));
-
- // 1.2.3-3 is less than 1.2.3-4
- assertEquals(-1, testSubject.compareTo(subject(path(COMPONENT, "1.2.3", DASH, "4"))));
- // 1.2.3-3 is less than 1.2.3-14
- assertEquals(-1, testSubject.compareTo(subject(path(COMPONENT, "1.2.3", DASH, "14"))));
- // 1.2.3-3 is less than 2.2.3-1
- assertEquals(-1, testSubject.compareTo(subject(path(COMPONENT, "2.2.3", DASH, "1"))));
-
- // 1.2.3-3 is greater than 1.2-3
- assertEquals(1, testSubject.compareTo(subject(path(COMPONENT, "1.2", DASH, "3"))));
- // 1.2.3-3 is greater than 1.2.2.2-3
- assertEquals(1, testSubject.compareTo(subject(path(COMPONENT, "1.2.2.2", DASH, "3"))));
- // 1.2.3-3 is less than 1.2.3.1-3
- assertEquals(-1, testSubject.compareTo(subject(path(COMPONENT, "1.2.3.1", DASH, "3"))));
+ version = subject("1.2.3-3");
+ assertEquals(1, version.compareTo(null));
+ assertEquals(1, version.compareTo(subject("1.2.3")));
+ assertEquals(1, version.compareTo(subject("1.2.3-2")));
+ assertEquals(1, version.compareTo(subject("1.2.2-3")));
+ assertEquals(1, version.compareTo(subject("v1.2.2-3")));
+ assertEquals(1, version.compareTo(subject("1.2-3")));
+ assertEquals(1, version.compareTo(subject("1.2.2.2-3")));
+ assertEquals(1, version.compareTo(subject("rc1.2.3-3")));
+ assertEquals(0, version.compareTo(subject("v1.2.3-3")));
+ assertEquals(0, version.compareTo(subject("1.2.3-3")));
+ assertEquals(-1, version.compareTo(subject("1.2.3.1-3")));
+ assertEquals(-1, version.compareTo(subject("1.2.3-4")));
+ assertEquals(-1, version.compareTo(subject("1.2.3-14")));
+ assertEquals(-1, version.compareTo(subject("2.2.3-1")));
+ assertEquals(-1, version.compareTo(subject("v1.2.4-3")));
+ assertEquals(-1, version.compareTo(subject("rc1.2.4-3")));
+ assertEquals(-1, version.compareTo(subject("20240720.1-3")));
+ assertEquals(-1, version.compareTo(subject("20240720.1")));
+
+ version = subject("1.2.3-3");
+ assertEquals(1, version.compareTo(subject("rc1.2.2-4")));
+ assertEquals(-1, version.compareTo(subject("20240720.1")));
assertEquals(0, subject(COMPONENT).compareTo(subject(COMPONENT)));
assertEquals(0, subject(EMPTY).compareTo(subject(EMPTY)));
-
-
}
public void runTest(String component, String version, String separator, String build) {
@@ -89,11 +85,15 @@ public void runTest(String component, String version, String separator, String b
assertEquals(build, subject.getBuild());
}
- public String path(String component, String version, String separator, String build) {
- return "v2/" + component + "/manifests/" + version + separator + build;
+ public BranchOrVersion subject(String componentVersion) {
+ return new BranchOrVersion("v2/component/manifests/" + componentVersion);
+ }
+
+ public BranchOrVersion subject(String componentVersion, String build) {
+ return new BranchOrVersion("v2/component/manifests/" + componentVersion + "-" + build);
}
- public BranchOrVersion subject(String path) {
- return new BranchOrVersion(path);
+ public BranchOrVersion subject(String componentVersion, String separator, String build) {
+ return new BranchOrVersion("v2/component/manifests/" + componentVersion + separator + build);
}
}
\ No newline at end of file
diff --git a/src/test/java/com/bioraft/rundeck/nexus/Nexus3OptionProviderTest.java b/src/test/java/com/bioraft/rundeck/nexus/Nexus3OptionProviderTest.java
index 55c1781..5203fdf 100644
--- a/src/test/java/com/bioraft/rundeck/nexus/Nexus3OptionProviderTest.java
+++ b/src/test/java/com/bioraft/rundeck/nexus/Nexus3OptionProviderTest.java
@@ -269,17 +269,18 @@ public void returnsFourItemsForOneBranchAndTwoReleases() throws IOException {
public void returnsFourItemsForOneBranchAndTwoSemanticReleases() throws IOException {
when(client.newCall(any())).thenReturn(call);
String json = "{\"items\":[" + item("sprint-11_3") + "," + item("sprint-11_4") + "," + item("v1.2.3_4") + ","
- + item("v1.2.2_1") + "," + item("v1.2.3_3") + "," + item("v1.2.2_2") + "]}";
+ + item("v1.2.2_1") + "," + item("v1.2.3_3") + "," + item("v1.2.2_2") + "," + item("20240720.0") + "]}";
when(call.execute()).thenReturn(response(json));
Nexus3OptionProvider provider = new Nexus3OptionProvider(client);
List options = provider.getOptionValues(configuration);
- assertEquals(4, options.size());
- assertEquals("COMP_NAME:v1.2.3_4", options.get(0).getName());
+ assertEquals(5, options.size());
+ assertEquals("COMP_NAME:20240720.0", options.get(0).getName());
assertEquals("COMP_NAME:sprint-11_4", options.get(1).getName());
assertEquals("COMP_NAME:v1.2.2_2", options.get(2).getName());
assertEquals("COMP_NAME:v1.2.3_4", options.get(3).getName());
+ assertEquals("COMP_NAME:20240720.0", options.get(4).getName());
}
@Test
@@ -302,15 +303,16 @@ public void returnsFourItemsForOneBranchAndTwoBareSemanticReleases() throws IOEx
@Test
public void handlesTagsWithoutBuildSpecifier() throws IOException {
when(client.newCall(any())).thenReturn(call);
- String json = "{\"items\":[" + item("v141.1") + "," + item("rc141.1raft4711a") + "]}";
+ String json = "{\"items\":[" + item("v141.1") + "," + item("rc141.1") + "]}";
when(call.execute()).thenReturn(response(json));
Nexus3OptionProvider provider = new Nexus3OptionProvider(client);
List options = provider.getOptionValues(configuration);
assertEquals(3, options.size());
- assertEquals("COMP_NAME:rc141.1raft4711a", options.get(1).getName());
assertEquals("COMP_NAME:v141.1", options.get(0).getName());
+ assertEquals("COMP_NAME:rc141.1", options.get(1).getName());
+ assertEquals("COMP_NAME:v141.1", options.get(2).getName());
}
@Test
@@ -334,9 +336,9 @@ public void sortsBuildNumbersNumerically() throws IOException {
public void sortsIssuesNumerically() throws IOException {
when(client.newCall(any())).thenReturn(call);
String json = "{\"items\":[" + item("sprint_11-13") + "," + item("sprint_11-4") + "," + item("1.2.3-4") + ","
- + item("1.2.2-21") + "," + item("1.2.3-11") + "," + item("ISSUE-234-one-issue-2") + ","
- + item("ISSUE-234-one-issue-12") + "," + item("ISSUE-1000-another-issue-27") + ","
- + item("ISSUE-1000-another-issue-13") + "," + item("1.2.2-2") + "]}";
+ + item("1.2.2-21") + "," + item("1.2.3-11") + "," + item("ISSUE-1000-another-issue-27") + ","
+ + item("ISSUE-1000-another-issue-13") + "," + item("ISSUE-234-one-issue-2") + ","
+ + item("ISSUE-234-one-issue-12") + "," + item("1.2.2-2") + "]}";
when(call.execute()).thenReturn(response(json));
Nexus3OptionProvider provider = new Nexus3OptionProvider(client);