Skip to content

Commit

Permalink
Allow build numbers to be arbitrary strings or null
Browse files Browse the repository at this point in the history
This required changing the `buildNumber` data type from `long` to `String`.

Fixes #75
  • Loading branch information
marcelstoer committed Sep 19, 2019
1 parent 9ffa9fc commit 1c661aa
Show file tree
Hide file tree
Showing 26 changed files with 479 additions and 141 deletions.
11 changes: 10 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.danielflower.mavenplugins</groupId>
<artifactId>multi-module-maven-release-plugin</artifactId>
<version>3.2-SNAPSHOT</version> <!-- When changing also update scaffolding.TestProject.PLUGIN_VERSION_FOR_TESTS and add to src/site/markdown/changelog.md -->
<version>3.3-SNAPSHOT</version> <!-- When changing also update scaffolding.TestProject.PLUGIN_VERSION_FOR_TESTS and add to src/site/markdown/changelog.md -->

<name>The Multi Module Maven Release Plugin</name>
<description>A maven release plugin built for multi-maven-module git repositories allowing continuous deployment
Expand Down Expand Up @@ -123,6 +123,15 @@
<artifactId>maven-invoker</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<!--
The maven-dependency-plugin wants this to be listed explicitly as it comes as a transitive dependency
of maven-model.
-->
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ private AnnotatedTag(Ref ref, String name, JSONObject message) {
this.message = message;
}

public static AnnotatedTag create(String name, String version, long buildNumber) {
public static AnnotatedTag create(String name, String version, String buildNumber) {
JSONObject message = new JSONObject();
message.put(VERSION, version);
message.put(BUILD_NUMBER, String.valueOf(buildNumber));
message.put(BUILD_NUMBER, buildNumber);
return new AnnotatedTag(null, name, message);
}

Expand Down Expand Up @@ -67,8 +67,8 @@ public String version() {
return String.valueOf(message.get(VERSION));
}

public long buildNumber() {
return Long.parseLong(String.valueOf(message.get(BUILD_NUMBER)));
public String buildNumber() {
return String.valueOf(message.get(BUILD_NUMBER));
}

public Ref saveAtHEAD(Git git) throws GitAPIException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,12 @@ boolean isPotentiallySameVersionIgnoringBuildNumber(String versionWithoutBuildNu
return buildNumberOf(versionWithoutBuildNumber, refName) != null;
}

Long buildNumberOf(String versionWithoutBuildNumber, String refName) {
String buildNumberOf(String versionWithoutBuildNumber, String refName) {
String tagName = AnnotatedTag.stripRefPrefix(refName);
String prefix = versionWithoutBuildNumber + versionNamer.getDelimiter();
if (tagName.startsWith(prefix)) {
String end = tagName.substring(prefix.length());
try {
return Long.parseLong(end);
} catch (NumberFormatException e) {
return null;
}
return tagName.substring(prefix.length());
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.github.danielflower.mavenplugins.release;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.model.Scm;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
Expand Down Expand Up @@ -43,16 +45,37 @@ public abstract class BaseMojo extends AbstractMojo {
* </p>
* <p>
* By default, the plugin will automatically find a suitable build number.
* It will start at version 0 and increment this with each release.
* It will start at 0 and increment this with each release.
* </p>
* <p>
* This can be specified using a command line parameter ("-DbuildNumber=2")
* or in this plugin's configuration.
* This can be specified using a command line parameter (e.g.
* <code>-DbuildNumber=2</code>) or in this plugin's configuration.
* </p>
* <p>
* <strong>null vs. ""</strong>: if you do not define the build number it
* is treated as <code>null</code> and the plugin will start at 0. If you
* pass an empty string both the build number and the delimiter that
* precedes it will be omitted.<br>To define an empty string on the CLI use
* <code>-DbuildNumber=''</code>. For the same effect in the POM you can
* use <code>&lt;buildNumber&gt;""&lt;/buildNumber&gt;</code> as Maven does
* not support empty strings for configuration properties. The plugin will
* convert this accordingly.
* </p>
*/
// Maven has historically never supported empty strings in XML plugin configuration (see
// http://mail-archives.apache.org/mod_mbox/maven-users/200708.mbox/%[email protected]%3E
// from 2007). Hence, if a project defines <buildNumber></buildNumber> the property here will be null. However,
// Maven does support -DbuildNumber='' on the CLI.
@Parameter(property = "buildNumber")
protected Long buildNumber;
protected String buildNumber;

public void setBuildNumber(String buildNumber) {
if ("\"\"".equals(buildNumber)) {
this.buildNumber = StringUtils.EMPTY;
} else {
this.buildNumber = buildNumber;
}
}

/**
* <p>
Expand Down Expand Up @@ -252,5 +275,4 @@ protected static String getRemoteUrlOrNullIfNoneSet(Scm originalScm, Scm actualS
}
return GitHelper.scmUrlToRemote(remote);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.danielflower.mavenplugins.release;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
Expand Down Expand Up @@ -28,7 +29,7 @@ public List<ReleasableModule> getModulesInBuildOrder() {
return modulesInBuildOrder;
}

public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject rootProject, List<MavenProject> projects, Long buildNumber, List<String> modulesToForceRelease, NoChangesAction actionWhenNoChangesDetected, ResolverWrapper resolverWrapper, VersionNamer versionNamer) throws ValidationException, GitAPIException, MojoExecutionException {
public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject rootProject, List<MavenProject> projects, String buildNumber, List<String> modulesToForceRelease, NoChangesAction actionWhenNoChangesDetected, ResolverWrapper resolverWrapper, VersionNamer versionNamer) throws ValidationException, GitAPIException, MojoExecutionException {
DiffDetector detector = new TreeWalkingDiffDetector(gitRepo.git.getRepository());
List<ReleasableModule> modules = new ArrayList<ReleasableModule>();

Expand All @@ -42,14 +43,14 @@ public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject r
List<AnnotatedTag> previousTagsForThisModule = annotatedTagFinder.tagsForVersion(gitRepo.git, artifactId, versionWithoutBuildNumber);


Collection<Long> previousBuildNumbers = new ArrayList<Long>();
Collection<String> previousBuildNumbers = new ArrayList<>();
if (previousTagsForThisModule != null) {
for (AnnotatedTag previousTag : previousTagsForThisModule) {
previousBuildNumbers.add(previousTag.buildNumber());
}
}

Collection<Long> remoteBuildNumbers = getRemoteBuildNumbers(gitRepo, artifactId, versionWithoutBuildNumber, versionNamer);
Collection<String> remoteBuildNumbers = getRemoteBuildNumbers(gitRepo, artifactId, versionWithoutBuildNumber, versionNamer);
previousBuildNumbers.addAll(remoteBuildNumbers);

VersionName newVersion = versionNamer.name(project.getVersion(), buildNumber, previousBuildNumbers);
Expand Down Expand Up @@ -145,14 +146,14 @@ private static boolean hasSameMavenGAByDependencyLocation(ReleasableModule modul
return modelId[0].equals(module.getGroupId()) && modelId[1].equals(module.getArtifactId());
}

private static Collection<Long> getRemoteBuildNumbers(LocalGitRepo gitRepo, String artifactId, String versionWithoutBuildNumber, VersionNamer versionNamer) throws GitAPIException {
private static Collection<String> getRemoteBuildNumbers(LocalGitRepo gitRepo, String artifactId, String versionWithoutBuildNumber, VersionNamer versionNamer) throws GitAPIException {
Collection<Ref> remoteTagRefs = gitRepo.allTags();
Collection<Long> remoteBuildNumbers = new ArrayList<Long>();
Collection<String> remoteBuildNumbers = new ArrayList<>();
String tagWithoutBuildNumber = artifactId + "-" + versionWithoutBuildNumber;
AnnotatedTagFinder annotatedTagFinder = new AnnotatedTagFinder(versionNamer);
for (Ref remoteTagRef : remoteTagRefs) {
String remoteTagName = remoteTagRef.getName();
Long buildNumber = annotatedTagFinder.buildNumberOf(tagWithoutBuildNumber, remoteTagName);
String buildNumber = annotatedTagFinder.buildNumberOf(tagWithoutBuildNumber, remoteTagName);
if (buildNumber != null) {
remoteBuildNumbers.add(buildNumber);
}
Expand Down Expand Up @@ -190,20 +191,25 @@ static AnnotatedTag hasChangedSinceLastRelease(List<AnnotatedTag> previousTagsFo
try {
if (previousTagsForThisModule.size() == 0) return null;
boolean hasChanged = detector.hasChangedSince(relativePathToModule, project.getModel().getModules(), previousTagsForThisModule);
return hasChanged ? null : tagWithHighestBuildNumber(previousTagsForThisModule);
return hasChanged ? null : findTagWithHighestBuildNumber(previousTagsForThisModule);
} catch (Exception e) {
throw new MojoExecutionException("Error while detecting whether or not " + project.getArtifactId() + " has changed since the last release", e);
}
}

private static AnnotatedTag tagWithHighestBuildNumber(List<AnnotatedTag> tags) {
AnnotatedTag cur = null;
// Ignores any tags with non-numeric build numbers and finds the one with the largest build number among the others.
// The tags are guaranteed to be for the same /business version/ i.e. it's likely a small list.
private static AnnotatedTag findTagWithHighestBuildNumber(List<AnnotatedTag> tags) {
AnnotatedTag tagWithHighestBuildNumber = null;
for (AnnotatedTag tag : tags) {
if (cur == null || tag.buildNumber() > cur.buildNumber()) {
cur = tag;
if(StringUtils.isNumeric(tag.buildNumber())) {
if (tagWithHighestBuildNumber == null ||
Long.parseLong(tag.buildNumber()) > Long.parseLong(tagWithHighestBuildNumber.buildNumber())) {
tagWithHighestBuildNumber = tag;
}
}
}
return cur;
return tagWithHighestBuildNumber;
}

public ReleasableModule findByLabel(String label) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public String getVersion() {
return version.businessVersion();
}

public long getBuildNumber() {
public String getBuildNumber() {
return version.buildNumber();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
package com.github.danielflower.mavenplugins.release;

import org.apache.commons.lang3.StringUtils;

public class VersionName {
private final String version;
private final long buildNumber;
private final String buildNumber;
private final String developmentVersion;
private final String delimiter;
private final String releaseVersion;

public VersionName(String developmentVersion, String version, long buildNumber, String delimiter) {
public VersionName(String developmentVersion, String version, String buildNumber, String delimiter) {
Guard.notBlank("developmentVersion", developmentVersion);
Guard.notBlank("version", version);
Guard.notNull("buildNumber", buildNumber);
this.version = version;
this.buildNumber = buildNumber;
this.developmentVersion = developmentVersion;
this.delimiter = delimiter;
this.releaseVersion = buildNumber.length() > 0 ? version + delimiter + buildNumber : version;
}

public VersionName(String developmentVersion, String version, long buildNumber) {
public VersionName(String developmentVersion, String version, String buildNumber) {
this(developmentVersion, version, buildNumber, ".");
}

/**
* For example, "1.0" if the development version is "1.0-SNAPSHOT"
* For example, "1.0" if the development version is "1.0-SNAPSHOT".
*/
public String businessVersion() {
return version;
}

public long buildNumber() {
public String buildNumber() {
return buildNumber;
}

Expand All @@ -42,6 +46,6 @@ public String developmentVersion() {
* The business version with the build number appended, e.g. "1.0.1"
*/
public String releaseVersion() {
return version + delimiter + buildNumber;
return releaseVersion;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.danielflower.mavenplugins.release;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugins.annotations.Parameter;
import org.eclipse.jgit.lib.Repository;

Expand Down Expand Up @@ -33,35 +34,38 @@ public VersionNamer() {
this(".");
}

public VersionName name(String pomVersion, Long buildNumber, Collection<Long> previousBuildNumbers) throws ValidationException {

public VersionName name(String pomVersion, String buildNumber, Collection<String> previousBuildNumbers) throws ValidationException {
String effectiveBuildNumber = buildNumber;
if (buildNumber == null) {
if (previousBuildNumbers.size() == 0) {
buildNumber = 0L;
if (previousBuildNumbers == null || previousBuildNumbers.isEmpty()) {
effectiveBuildNumber = "0";
} else {
buildNumber = nextBuildNumber(previousBuildNumbers);
effectiveBuildNumber = nextBuildNumber(previousBuildNumbers);
}
}

VersionName versionName = new VersionName(pomVersion, pomVersion.replace("-SNAPSHOT", ""), buildNumber, this.delimiter);
VersionName versionName = new VersionName(pomVersion, pomVersion.replace("-SNAPSHOT", ""), effectiveBuildNumber, this.delimiter);

if (!Repository.isValidRefName("refs/tags/" + versionName.releaseVersion())) {
String summary = "Sorry, '" + versionName.releaseVersion() + "' is not a valid version.";
throw new ValidationException(summary, asList(
summary,
"Version numbers are used in the Git tag, and so can only contain characters that are valid in git tags.",
"Version numbers are used in the Git tag, and so can only contain characters that are valid in Git tags.",
"Please see https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html for tag naming rules."
));
}
return versionName;
}

private static long nextBuildNumber(Collection<Long> previousBuildNumbers) {
// Increments the largest previous build number. Skips over any non-numeric previous build numbers.
private static String nextBuildNumber(Collection<String> previousBuildNumbers) {
long max = 0;
for (Long buildNumber : previousBuildNumbers) {
max = Math.max(max, buildNumber);
for (String buildNumber : previousBuildNumbers) {
if (StringUtils.isNumeric(buildNumber)) {
max = Math.max(max, Long.parseLong(buildNumber));
}
}
return max + 1;
return String.valueOf(max + 1);
}

String getDelimiter() {
Expand Down
4 changes: 4 additions & 0 deletions src/site/markdown/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
---------

### 3.3.0

* Allow build numbers to be arbitrary strings or null, #75

### 3.2.0

* Added support for processing version properties e.g. `<version>${foo.version}</version>`
Expand Down
Loading

0 comments on commit 1c661aa

Please sign in to comment.