* 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.
*
*
- * 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.
+ * -DbuildNumber=2) or in this plugin's configuration.
*
+ *
+ * null vs. "": if you do not define the build number it
+ * is treated as null 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. To define an empty string on the CLI use
+ * -DbuildNumber=''. For the same effect in the POM you can
+ * use <buildNumber>""</buildNumber> as Maven does
+ * not support empty strings for configuration properties. The plugin will
+ * convert this accordingly.
+ *
*/
+ // Maven has historically never supported empty strings in XML plugin configuration (see
+ // http://mail-archives.apache.org/mod_mbox/maven-users/200708.mbox/%3C5a2cf1f60708090246l216f156esf46cc1e968b37ccd@mail.gmail.com%3E
+ // from 2007). Hence, if a project defines 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;
+ }
+ }
/**
*
@@ -162,6 +186,23 @@ public abstract class BaseMojo extends AbstractMojo {
@Parameter(property = "arguments")
public String arguments;
+ /**
+ *
List of relative file system paths to ignore when detecting changes in the project(s).
+ *
The primary purpose is to skip creating new releases if only "infrastructure" files such as
+ * .gitignore, .editorconfig and the like changed. Very basic wild cards are supported as
+ * follows:
+ *
+ *
foo.txt - matches foo.txt in the root of the top-level project
+ *
bar/foo.txt - matches foo.txt in the root of the bar directory
+ *
bar - matches the foo directory and ignores everything below
+ *
**.txt - matches all paths ending in .txt (suffix match)
+ *
**.editorconfig - matches all .editorconfig files in all (sub)directories; a special case of suffix matching
+ *
+ *
+ */
+ @Parameter(property = "ignoredPaths")
+ Set ignoredPaths;
+
final void setSettings(final Settings settings) {
this.settings = settings;
}
@@ -253,4 +294,53 @@ protected static String getRemoteUrlOrNullIfNoneSet(Scm originalScm, Scm actualS
return GitHelper.scmUrlToRemote(remote);
}
+ PluginConfiguration getPluginConfiguration() {
+ if (pluginConfiguration == null) {
+ pluginConfiguration = new PluginConfiguration(project, projects, buildNumber, versionNamer,
+ modulesToRelease, modulesToForceRelease, noChangesAction, factory, artifactResolver, remoteRepositories,
+ localRepository, settings, pullTags, arguments, ignoredPaths);
+ }
+ return pluginConfiguration;
+ }
+
+ static class PluginConfiguration {
+ final MavenProject rootProject;
+ final List projects;
+ final String buildNumber;
+ final VersionNamer versionNamer;
+ final List modulesToRelease;
+ final List modulesToForceRelease;
+ final NoChangesAction noChangesAction;
+ final ArtifactFactory factory;
+ final ArtifactResolver artifactResolver;
+ final List remoteRepositories;
+ final ArtifactRepository localRepository;
+ final Settings settings;
+ final boolean pullTags;
+ final String arguments;
+ final Set ignoredPaths;
+
+ PluginConfiguration(MavenProject rootProject, List projects, String buildNumber,
+ VersionNamer versionNamer, List modulesToRelease,
+ List modulesToForceRelease, NoChangesAction noChangesAction,
+ ArtifactFactory factory, ArtifactResolver artifactResolver, List remoteRepositories,
+ ArtifactRepository localRepository, Settings settings, boolean pullTags, String arguments,
+ Set ignoredPaths) {
+ this.rootProject = rootProject;
+ this.projects = projects;
+ this.buildNumber = buildNumber;
+ this.versionNamer = versionNamer;
+ this.modulesToRelease = modulesToRelease;
+ this.modulesToForceRelease = modulesToForceRelease;
+ this.noChangesAction = noChangesAction;
+ this.factory = factory;
+ this.artifactResolver = artifactResolver;
+ this.remoteRepositories = remoteRepositories;
+ this.localRepository = localRepository;
+ this.settings = settings;
+ this.pullTags = pullTags;
+ this.arguments = arguments;
+ this.ignoredPaths = ignoredPaths;
+ }
+ }
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/NextMojo.java b/src/main/java/com/github/danielflower/mavenplugins/release/NextMojo.java
index de41f73f..94de4c7a 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/NextMojo.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/NextMojo.java
@@ -45,8 +45,8 @@ public void execute() throws MojoExecutionException, MojoFailureException {
project.getModel().getScm()))
.credentialsProvider(getCredentialsProvider(log))
.buildFromCurrentDir();
- ResolverWrapper resolverWrapper = new ResolverWrapper(factory, artifactResolver, remoteRepositories, localRepository);
- Reactor reactor = Reactor.fromProjects(log, repo, project, projects, buildNumber, modulesToForceRelease, noChangesAction, resolverWrapper, versionNamer);
+ ResolverWrapper resolverWrapper = new ResolverWrapper(getPluginConfiguration());
+ Reactor reactor = Reactor.fromProjects(repo, getPluginConfiguration(), resolverWrapper, noChangesAction, log);
if (reactor == null) {
return;
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/Reactor.java b/src/main/java/com/github/danielflower/mavenplugins/release/Reactor.java
index b1422036..669e98af 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/Reactor.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/Reactor.java
@@ -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;
@@ -28,31 +29,32 @@ public List getModulesInBuildOrder() {
return modulesInBuildOrder;
}
- public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject rootProject, List projects, Long buildNumber, List modulesToForceRelease, NoChangesAction actionWhenNoChangesDetected, ResolverWrapper resolverWrapper, VersionNamer versionNamer) throws ValidationException, GitAPIException, MojoExecutionException {
- DiffDetector detector = new TreeWalkingDiffDetector(gitRepo.git.getRepository());
- List modules = new ArrayList();
+ static Reactor fromProjects(LocalGitRepo gitRepo, BaseMojo.PluginConfiguration pluginConfiguration, ResolverWrapper resolverWrapper, NoChangesAction noChangesAction, Log log) throws ValidationException, GitAPIException, MojoExecutionException {
+ DiffDetector detector = new TreeWalkingDiffDetector(gitRepo.git.getRepository(), pluginConfiguration.ignoredPaths);
+ List modules = new ArrayList<>();
- resolveVersionsDefinedThroughProperties(projects);
+ resolveVersionsDefinedThroughProperties(pluginConfiguration.projects);
+ VersionNamer versionNamer = pluginConfiguration.versionNamer;
AnnotatedTagFinder annotatedTagFinder = new AnnotatedTagFinder(versionNamer);
- for (MavenProject project : projects) {
- String relativePathToModule = calculateModulePath(rootProject, project);
+ for (MavenProject project : pluginConfiguration.projects) {
+ String relativePathToModule = calculateModulePath(pluginConfiguration.rootProject, project);
String artifactId = project.getArtifactId();
String versionWithoutBuildNumber = project.getVersion().replace("-SNAPSHOT", "");
List previousTagsForThisModule = annotatedTagFinder.tagsForVersion(gitRepo.git, artifactId, versionWithoutBuildNumber);
- Collection previousBuildNumbers = new ArrayList();
+ Collection previousBuildNumbers = new ArrayList<>();
if (previousTagsForThisModule != null) {
for (AnnotatedTag previousTag : previousTagsForThisModule) {
previousBuildNumbers.add(previousTag.buildNumber());
}
}
- Collection remoteBuildNumbers = getRemoteBuildNumbers(gitRepo, artifactId, versionWithoutBuildNumber, versionNamer);
+ Collection remoteBuildNumbers = getRemoteBuildNumbers(gitRepo, artifactId, versionWithoutBuildNumber, versionNamer);
previousBuildNumbers.addAll(remoteBuildNumbers);
- VersionName newVersion = versionNamer.name(project.getVersion(), buildNumber, previousBuildNumbers);
+ VersionName newVersion = versionNamer.name(project.getVersion(), pluginConfiguration.buildNumber, previousBuildNumbers);
boolean oneOfTheDependenciesHasChanged = false;
String changedDependency = null;
@@ -89,7 +91,7 @@ public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject r
String equivalentVersion = null;
- if(modulesToForceRelease != null && modulesToForceRelease.contains(artifactId)) {
+ if(pluginConfiguration.modulesToForceRelease != null && pluginConfiguration.modulesToForceRelease.contains(artifactId)) {
log.info("Releasing " + artifactId + " " + newVersion.releaseVersion() + " as we was asked to forced release.");
} else if (oneOfTheDependenciesHasChanged) {
log.info("Releasing " + artifactId + " " + newVersion.releaseVersion() + " as " + changedDependency + " has changed.");
@@ -113,7 +115,7 @@ public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject r
}
if (!atLeastOneBeingReleased(modules)) {
- switch (actionWhenNoChangesDetected) {
+ switch (noChangesAction) {
case ReleaseNone:
log.warn("No changes have been detected in any modules so will not perform release");
return null;
@@ -121,7 +123,7 @@ public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject r
throw new MojoExecutionException("No module changes have been detected");
default:
log.warn("No changes have been detected in any modules so will re-release them all");
- List newList = new ArrayList();
+ List newList = new ArrayList<>();
for (ReleasableModule module : modules) {
newList.add(module.createReleasableVersion());
}
@@ -145,14 +147,14 @@ private static boolean hasSameMavenGAByDependencyLocation(ReleasableModule modul
return modelId[0].equals(module.getGroupId()) && modelId[1].equals(module.getArtifactId());
}
- private static Collection getRemoteBuildNumbers(LocalGitRepo gitRepo, String artifactId, String versionWithoutBuildNumber, VersionNamer versionNamer) throws GitAPIException {
+ private static Collection getRemoteBuildNumbers(LocalGitRepo gitRepo, String artifactId, String versionWithoutBuildNumber, VersionNamer versionNamer) throws GitAPIException {
Collection remoteTagRefs = gitRepo.allTags();
- Collection remoteBuildNumbers = new ArrayList();
+ Collection 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);
}
@@ -190,20 +192,25 @@ static AnnotatedTag hasChangedSinceLastRelease(List 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 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 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) {
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/ReleasableModule.java b/src/main/java/com/github/danielflower/mavenplugins/release/ReleasableModule.java
index d76f0cd5..a70fbe2c 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/ReleasableModule.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/ReleasableModule.java
@@ -44,7 +44,7 @@ public String getVersion() {
return version.businessVersion();
}
- public long getBuildNumber() {
+ public String getBuildNumber() {
return version.buildNumber();
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/ReleaseMojo.java b/src/main/java/com/github/danielflower/mavenplugins/release/ReleaseMojo.java
index 5ded4f14..18d070e6 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/ReleaseMojo.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/ReleaseMojo.java
@@ -123,8 +123,8 @@ public void execute() throws MojoExecutionException, MojoFailureException {
.buildFromCurrentDir();
repo.errorIfNotClean();
- ResolverWrapper resolverWrapper = new ResolverWrapper(factory, artifactResolver, remoteRepositories, localRepository);
- Reactor reactor = Reactor.fromProjects(log, repo, project, projects, buildNumber, modulesToForceRelease, noChangesAction, resolverWrapper, versionNamer);
+ ResolverWrapper resolverWrapper = new ResolverWrapper(getPluginConfiguration());
+ Reactor reactor = Reactor.fromProjects(repo, getPluginConfiguration(), resolverWrapper, noChangesAction, log);
if (reactor == null) {
return;
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/ResolverWrapper.java b/src/main/java/com/github/danielflower/mavenplugins/release/ResolverWrapper.java
index 347e9d41..0656f7ea 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/ResolverWrapper.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/ResolverWrapper.java
@@ -10,43 +10,21 @@
import java.util.List;
-/**
- *
- */
-public class ResolverWrapper {
+class ResolverWrapper {
private final ArtifactFactory factory;
-
private final ArtifactResolver artifactResolver;
-
private final List remoteRepositories;
-
private final ArtifactRepository localRepository;
- public ResolverWrapper(ArtifactFactory factory, ArtifactResolver artifactResolver, List remoteRepositories, ArtifactRepository localRepository) {
- this.factory = factory;
- this.artifactResolver = artifactResolver;
- this.remoteRepositories = remoteRepositories;
- this.localRepository = localRepository;
- }
-
- public ArtifactFactory getFactory() {
- return factory;
- }
-
- public ArtifactResolver getArtifactResolver() {
- return artifactResolver;
- }
-
- public List getRemoteRepositories() {
- return remoteRepositories;
- }
-
- public ArtifactRepository getLocalRepository() {
- return localRepository;
+ ResolverWrapper(BaseMojo.PluginConfiguration pluginConfiguration) {
+ this.factory = pluginConfiguration.factory;
+ this.artifactResolver = pluginConfiguration.artifactResolver;
+ this.remoteRepositories = pluginConfiguration.remoteRepositories;
+ this.localRepository = pluginConfiguration.localRepository;
}
- public boolean isResolvable(String groupId, String artifactId, String version, String type, Log log) {
+ boolean isResolvable(String groupId, String artifactId, String version, String type, Log log) {
try {
Artifact pomArtifact = this.factory.createArtifact(groupId, artifactId, version, "", type);
artifactResolver.resolve(pomArtifact, remoteRepositories, localRepository);
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetector.java b/src/main/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetector.java
index 81fd260c..d16ac24d 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetector.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetector.java
@@ -6,34 +6,69 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.PathSuffixFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
public class TreeWalkingDiffDetector implements DiffDetector {
private final Repository repo;
+ private final Set ignoredPaths;
- public TreeWalkingDiffDetector(Repository repo) {
+ TreeWalkingDiffDetector(Repository repo, Set ignoredPaths) {
this.repo = repo;
+ this.ignoredPaths = ignoredPaths;
}
- public boolean hasChangedSince(String modulePath, java.util.List childModules, Collection tags) throws IOException {
+ TreeWalkingDiffDetector(Repository repository) {
+ this(repository, Collections.emptySet());
+ }
+
+ public boolean hasChangedSince(String modulePath, List childModules, Collection tags) throws IOException {
RevWalk walk = new RevWalk(repo);
try {
walk.setRetainBody(false);
walk.markStart(walk.parseCommit(repo.getRefDatabase().findRef("HEAD").getObjectId()));
- filterOutOtherModulesChanges(modulePath, childModules, walk);
+
+ List treeFilters = createTreeFiltersForOtherModulesChanges(modulePath, childModules);
+ treeFilters.addAll(createTreeFiltersForIgnoredPaths());
+ walk.setTreeFilter(treeFilters.size() == 1 ? treeFilters.get(0) : AndTreeFilter.create(treeFilters));
stopWalkingWhenTheTagsAreHit(tags, walk);
+
return walk.iterator().hasNext();
} finally {
walk.dispose();
}
}
+ private Collection createTreeFiltersForIgnoredPaths() {
+ List treeFilters = new ArrayList<>();
+ if (ignoredPaths != null) {
+ treeFilters.addAll(
+ ignoredPaths.stream()
+ // To differentiate path suffix filters from path filters in the configuration there is the special
+ // "**" prefix.
+ // foo.txt -> path filter that matches foo.txt in the root of the top-level project
+ // bar/foo.txt -> path filter that matches foo.txt in the root of the bar directory
+ // bar -> path filter that matches everything in/under the bar directory
+ // **.txt -> path suffix filter that matches all paths ending in .txt (suffix match)
+ // **.editorconfig -> path suffix filter that matches all .editorconfig files in all (sub)directories; a special case of suffix matching
+ .map(p -> p.startsWith("**") ? PathSuffixFilter.create(p.substring(2)): PathFilter.create(p))
+ // tree filters define what to include, yet the users define what to IGNORE -> negate the filter
+ .map(TreeFilter::negate)
+ .collect(Collectors.toList())
+ );
+ }
+ return treeFilters;
+ }
+
private static void stopWalkingWhenTheTagsAreHit(Collection tags, RevWalk walk) throws IOException {
for (AnnotatedTag tag : tags) {
ObjectId commitId = tag.ref().getTarget().getObjectId();
@@ -42,7 +77,7 @@ private static void stopWalkingWhenTheTagsAreHit(Collection tags,
}
}
- private void filterOutOtherModulesChanges(String modulePath, List childModules, RevWalk walk) {
+ private List createTreeFiltersForOtherModulesChanges(String modulePath, List childModules) {
boolean isRootModule = ".".equals(modulePath);
boolean isMultiModuleProject = !isRootModule || !childModules.isEmpty();
List treeFilters = new ArrayList<>();
@@ -60,7 +95,6 @@ private void filterOutOtherModulesChanges(String modulePath, List childM
}
}
- TreeFilter treeFilter = treeFilters.size() == 1 ? treeFilters.get(0) : AndTreeFilter.create(treeFilters);
- walk.setTreeFilter(treeFilter);
+ return treeFilters;
}
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/VersionName.java b/src/main/java/com/github/danielflower/mavenplugins/release/VersionName.java
index f6d13cad..01bdda80 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/VersionName.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/VersionName.java
@@ -1,12 +1,15 @@
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);
@@ -14,20 +17,21 @@ public VersionName(String developmentVersion, String version, long buildNumber,
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;
}
@@ -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;
}
}
diff --git a/src/main/java/com/github/danielflower/mavenplugins/release/VersionNamer.java b/src/main/java/com/github/danielflower/mavenplugins/release/VersionNamer.java
index 3c9d77ff..7763539a 100644
--- a/src/main/java/com/github/danielflower/mavenplugins/release/VersionNamer.java
+++ b/src/main/java/com/github/danielflower/mavenplugins/release/VersionNamer.java
@@ -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;
@@ -33,35 +34,38 @@ public VersionNamer() {
this(".");
}
- public VersionName name(String pomVersion, Long buildNumber, Collection previousBuildNumbers) throws ValidationException {
-
+ public VersionName name(String pomVersion, String buildNumber, Collection 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 previousBuildNumbers) {
+ // Increments the largest previous build number. Skips over any non-numeric previous build numbers.
+ private static String nextBuildNumber(Collection 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() {
diff --git a/src/site/markdown/changelog.md b/src/site/markdown/changelog.md
index 408c267a..4293e3e8 100644
--- a/src/site/markdown/changelog.md
+++ b/src/site/markdown/changelog.md
@@ -1,6 +1,14 @@
Changelog
---------
+### 3.4.0
+
+* Allow to define paths which should be ignored when detecting changes (`.gitignore` et.al.), #77
+
+### 3.3.0
+
+* Allow build numbers to be arbitrary strings or null, #75
+
### 3.2.0
* Added support for processing version properties e.g. `${foo.version}`
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagFinderTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagFinderTest.java
index ff93802a..dc1e1a1e 100644
--- a/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagFinderTest.java
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagFinderTest.java
@@ -22,22 +22,22 @@ public class AnnotatedTagFinderTest {
public void findsTheLatestCommitWhereThereHaveBeenNoBranches() throws Exception {
TestProject project = TestProject.independentVersionsProject();
- AnnotatedTag tag1 = saveFileInModule(project, "console-app", "1.2", 3);
- AnnotatedTag tag2 = saveFileInModule(project, "core-utils", "2", 0);
- AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", 4);
+ AnnotatedTag tag1 = saveFileInModule(project, "console-app", "1.2", "3");
+ AnnotatedTag tag2 = saveFileInModule(project, "core-utils", "2", "0");
+ AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", "4");
assertThat(annotatedTagFinder.tagsForVersion(project.local, "console-app", "1.3"), hasSize(0));
assertThat(annotatedTagFinder.tagsForVersion(project.local, "console-app", "1.2"), containsInAnyOrder(tag1, tag3));
assertThat(annotatedTagFinder.tagsForVersion(project.local, "core-utils", "2"), contains(tag2));
}
- static AnnotatedTag saveFileInModule(TestProject project, String moduleName, String version, long buildNumber) throws IOException, GitAPIException {
+ static AnnotatedTag saveFileInModule(TestProject project, String moduleName, String version, String buildNumber) throws IOException, GitAPIException {
project.commitRandomFile(moduleName);
String nameForTag = moduleName.equals(".") ? "root" : moduleName;
return tagLocalRepo(project, nameForTag + "-" + version + versionNamer.getDelimiter() + buildNumber, version, buildNumber);
}
- private static AnnotatedTag tagLocalRepo(TestProject project, String tagName, String version, long buildNumber) throws GitAPIException {
+ private static AnnotatedTag tagLocalRepo(TestProject project, String tagName, String version, String buildNumber) throws GitAPIException {
AnnotatedTag tag = AnnotatedTag.create(tagName, version, buildNumber);
tag.saveAtHEAD(project.local);
return tag;
@@ -54,10 +54,10 @@ public void canRecogniseTagsThatArePotentiallyOfTheSameVersion() {
@Test
public void returnsMultipleTagsOnASingleCommit() throws IOException, GitAPIException, MojoExecutionException {
TestProject project = TestProject.independentVersionsProject();
- saveFileInModule(project, "console-app", "1.2", 1);
- AnnotatedTag tag1 = tagLocalRepo(project, "console-app-1.1.1.1", "1.1.1", 1);
- AnnotatedTag tag3 = tagLocalRepo(project, "console-app-1.1.1.3", "1.1.1", 3);
- AnnotatedTag tag2 = tagLocalRepo(project, "console-app-1.1.1.2", "1.1.1", 2);
+ saveFileInModule(project, "console-app", "1.2", "1");
+ AnnotatedTag tag1 = tagLocalRepo(project, "console-app-1.1.1.1", "1.1.1", "1");
+ AnnotatedTag tag3 = tagLocalRepo(project, "console-app-1.1.1.3", "1.1.1", "3");
+ AnnotatedTag tag2 = tagLocalRepo(project, "console-app-1.1.1.2", "1.1.1", "2");
List annotatedTags = annotatedTagFinder.tagsForVersion(project.local, "console-app", "1.1.1");
assertThat(annotatedTags, containsInAnyOrder(tag1, tag2, tag3));
}
@@ -65,7 +65,7 @@ public void returnsMultipleTagsOnASingleCommit() throws IOException, GitAPIExcep
@Test
public void versionNamerCaresNotForOrderOfTags() throws ValidationException {
VersionNamer versionNamer = new VersionNamer();
- VersionName name = versionNamer.name("1.1.1", null, asList(1L, 3L, 2L));
+ VersionName name = versionNamer.name("1.1.1", null, asList("1", "3", "2"));
assertThat(name.releaseVersion(), equalTo("1.1.1.4"));
}
@@ -76,15 +76,15 @@ public void respectDelimiterWhenExtractingBuildNumber() {
// This should actually be implemented with a parameterized JUnit test but since this class isn't set up for
// that yet we'll use our own data-driven harness.
Arrays.asList(new Object[][]{
+ new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0-1", "1"}, // proper match with expected delimiter
+ new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0-A", "A"}, // non-numeric build number
new Object[]{"libs-1.0.0", refPrefix + "foo-1.0.0-1", null}, // artifact ID does not match -> expect null
- new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0-1", 1L}, // proper match with expected delimiter
new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0.1", null}, // no delimiter match
new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0@1", null}, // no delimiter match
- new Object[]{"libs-1.0.0", refPrefix + "libs-1.0.0-A", null} // format exception on build number
})
.forEach(dataSet -> {
final Object expectedBuildNumber = dataSet[2];
- final Long buildNumber = tagFinder.buildNumberOf(dataSet[0].toString(), dataSet[1].toString());
+ final String buildNumber = tagFinder.buildNumberOf(dataSet[0].toString(), dataSet[1].toString());
assertThat(buildNumber, is(expectedBuildNumber));
});
}
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagTest.java
index 1abce6fa..7b68b8df 100644
--- a/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagTest.java
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/AnnotatedTagTest.java
@@ -12,25 +12,25 @@
public class AnnotatedTagTest {
@Test
- public void gettersReturnValuesPassedIn() throws Exception {
+ public void gettersReturnValuesPassedIn() {
// yep, testing getters... but only because it isn't a simple POJO
- AnnotatedTag tag = AnnotatedTag.create("my-name", "the-version", 2134);
+ AnnotatedTag tag = AnnotatedTag.create("my-name", "the-version", "2134");
assertThat(tag.name(), equalTo("my-name"));
assertThat(tag.version(), equalTo("the-version"));
- assertThat(tag.buildNumber(), equalTo(2134L));
+ assertThat(tag.buildNumber(), equalTo("2134"));
}
@Test
public void aTagCanBeCreatedFromAGitTag() throws GitAPIException, IOException {
TestProject project = TestProject.singleModuleProject();
- AnnotatedTag tag = AnnotatedTag.create("my-name", "the-version", 2134);
+ AnnotatedTag tag = AnnotatedTag.create("my-name", "the-version", "2134");
tag.saveAtHEAD(project.local);
Ref ref = project.local.tagList().call().get(0);
AnnotatedTag inflatedTag = AnnotatedTag.fromRef(project.local.getRepository(), ref);
assertThat(inflatedTag.name(), equalTo("my-name"));
assertThat(inflatedTag.version(), equalTo("the-version"));
- assertThat(inflatedTag.buildNumber(), equalTo(2134L));
+ assertThat(inflatedTag.buildNumber(), equalTo("2134"));
}
@Test
@@ -42,7 +42,7 @@ public void ifATagIsSavedWithoutJsonThenTheVersionIsSetTo0Dot0() throws GitAPIEx
AnnotatedTag inflatedTag = AnnotatedTag.fromRef(project.local.getRepository(), ref);
assertThat(inflatedTag.name(), equalTo("my-name-1.0.2"));
assertThat(inflatedTag.version(), equalTo("0"));
- assertThat(inflatedTag.buildNumber(), equalTo(0L));
+ assertThat(inflatedTag.buildNumber(), equalTo("0"));
}
}
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/DiffDetectorTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/DiffDetectorTest.java
deleted file mode 100644
index d465d357..00000000
--- a/src/test/java/com/github/danielflower/mavenplugins/release/DiffDetectorTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.github.danielflower.mavenplugins.release;
-
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.junit.Test;
-import scaffolding.TestProject;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-import static com.github.danielflower.mavenplugins.release.AnnotatedTagFinderTest.saveFileInModule;
-import static java.util.Arrays.asList;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-public class DiffDetectorTest {
-
- @Test
- public void canDetectIfFilesHaveBeenChangedForAModuleSinceSomeSpecificTag() throws Exception {
- TestProject project = TestProject.independentVersionsProject();
-
- AnnotatedTag tag1 = saveFileInModule(project, "console-app", "1.2", 3);
- AnnotatedTag tag2 = saveFileInModule(project, "core-utils", "2", 0);
- AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", 4);
-
- DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
-
- assertThat(detector.hasChangedSince("core-utils", noChildModules(), asList(tag2)), is(false));
- assertThat(detector.hasChangedSince("console-app", noChildModules(), asList(tag2)), is(true));
- assertThat(detector.hasChangedSince("console-app", noChildModules(), asList(tag3)), is(false));
- }
-
- @Test
- public void canDetectThingsInTheRoot() throws IOException, GitAPIException {
- TestProject simple = TestProject.singleModuleProject();
- AnnotatedTag tag1 = saveFileInModule(simple, ".", "1.0", 1);
- simple.commitRandomFile(".");
- DiffDetector detector = new TreeWalkingDiffDetector(simple.local.getRepository());
- assertThat(detector.hasChangedSince(".", noChildModules(), asList(tag1)), is(true));
-
- AnnotatedTag tag2 = saveFileInModule(simple, ".", "1.0", 2);
- assertThat(detector.hasChangedSince(".", noChildModules(), asList(tag2)), is(false));
- }
-
- @Test
- public void canDetectChangesAfterTheLastTag() throws IOException, GitAPIException {
- TestProject project = TestProject.independentVersionsProject();
-
- saveFileInModule(project, "console-app", "1.2", 3);
- saveFileInModule(project, "core-utils", "2", 0);
- AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", 4);
- project.commitRandomFile("console-app");
-
- DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
- assertThat(detector.hasChangedSince("console-app", noChildModules(), asList(tag3)), is(true));
- }
-
- @Test
- public void canIgnoreChangesInModuleFolders() throws IOException, GitAPIException {
- TestProject project = TestProject.nestedProject();
-
- AnnotatedTag tag1 = saveFileInModule(project, "server-modules", "1.0.2.4", 0);
- project.commitRandomFile("server-modules/server-module-a");
-
- DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
- assertThat(detector.hasChangedSince("server-modules", asList("server-module-a", "server-module-b"),
- asList(tag1)), is(false));
- }
-
- @Test
- public void canDetectLocalChangesWithModuleFolders() throws IOException, GitAPIException {
- TestProject project = TestProject.nestedProject();
-
- AnnotatedTag tag1 = saveFileInModule(project, "server-modules", "1.0.2.4", 0);
- project.commitRandomFile("server-modules");
-
- DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
- assertThat(detector.hasChangedSince("server-modules", asList("server-module-a", "server-module-b"),
- asList(tag1)), is(true));
- }
-
-
- private static java.util.List noChildModules() {
- return new ArrayList();
- }
-}
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/LocalGitRepoTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/LocalGitRepoTest.java
index aa7ee251..e206fe4f 100644
--- a/src/test/java/com/github/danielflower/mavenplugins/release/LocalGitRepoTest.java
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/LocalGitRepoTest.java
@@ -70,9 +70,9 @@ public void canHaveManyTags() throws GitAPIException {
}
private static List tags(String... tagNames) {
- List tags = new ArrayList();
+ List tags = new ArrayList<>();
for (String tagName : tagNames) {
- tags.add(AnnotatedTag.create(tagName, "1", 0));
+ tags.add(AnnotatedTag.create(tagName, "1", "0"));
}
return tags;
}
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/ReactorTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/ReactorTest.java
index 0ba29111..bb2e83b0 100644
--- a/src/test/java/com/github/danielflower/mavenplugins/release/ReactorTest.java
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/ReactorTest.java
@@ -49,8 +49,8 @@ public void ifNotFoundThenAUnresolvedSnapshotDependencyExceptionIsThrown() throw
@Test
public void returnsTheLatestTagIfThereAreChanges() throws MojoExecutionException {
- AnnotatedTag onePointNine = AnnotatedTag.create("whatever-1.1.9", "1.1", 9);
- AnnotatedTag onePointTen = AnnotatedTag.create("whatever-1.1.10", "1.1", 10);
+ AnnotatedTag onePointNine = AnnotatedTag.create("whatever-1.1.9", "1.1", "9");
+ AnnotatedTag onePointTen = AnnotatedTag.create("whatever-1.1.10", "1.1", "10");
assertThat(Reactor.hasChangedSinceLastRelease(asList(onePointNine, onePointTen), new NeverChanged(), new MavenProject(), "whatever"), is(onePointTen));
assertThat(Reactor.hasChangedSinceLastRelease(asList(onePointTen, onePointNine), new NeverChanged(), new MavenProject(), "whatever"), is(onePointTen));
}
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/ReleasableModuleTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/ReleasableModuleTest.java
index 5f7b10b6..ccd44b2e 100644
--- a/src/test/java/com/github/danielflower/mavenplugins/release/ReleasableModuleTest.java
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/ReleasableModuleTest.java
@@ -20,18 +20,18 @@ public void getsTheTagFromTheArtifactAndVersion() throws Exception {
}
@Test
- public void aReleaseableModuleCanBeCreatedFromAnUnreleasableOne() {
+ public void aReleasableModuleCanBeCreatedFromAnUnreleasableOne() {
MavenProject project = new MavenProject();
project.setArtifactId("some-arty");
project.setGroupId("some-group");
ReleasableModule first = new ReleasableModule(
- project, new VersionName("1.2.3-SNAPSHOT", "1.2.3", 12), "1.2.3.11", "somewhere"
+ project, new VersionName("1.2.3-SNAPSHOT", "1.2.3", "12"), "1.2.3.11", "somewhere"
);
assertThat(first.willBeReleased(), is(false));
ReleasableModule changed = first.createReleasableVersion();
assertThat(changed.getArtifactId(), equalTo("some-arty"));
- assertThat(changed.getBuildNumber(), equalTo(12L));
+ assertThat(changed.getBuildNumber(), equalTo("12"));
assertThat(changed.getGroupId(), equalTo("some-group"));
assertThat(changed.getNewVersion(), equalTo("1.2.3.12"));
assertThat(changed.getProject(), is(project));
diff --git a/src/test/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetectorTest.java b/src/test/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetectorTest.java
new file mode 100644
index 00000000..141a4f9f
--- /dev/null
+++ b/src/test/java/com/github/danielflower/mavenplugins/release/TreeWalkingDiffDetectorTest.java
@@ -0,0 +1,148 @@
+package com.github.danielflower.mavenplugins.release;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import scaffolding.TestProject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static com.github.danielflower.mavenplugins.release.AnnotatedTagFinderTest.saveFileInModule;
+import static java.util.Arrays.asList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+@RunWith(Enclosed.class)
+public class TreeWalkingDiffDetectorTest {
+
+ @Test
+ public void canDetectIfFilesHaveBeenChangedForAModuleSinceSomeSpecificTag() throws Exception {
+ TestProject project = TestProject.independentVersionsProject();
+
+ AnnotatedTag tag1 = saveFileInModule(project, "console-app", "1.2", "3");
+ AnnotatedTag tag2 = saveFileInModule(project, "core-utils", "2", "0");
+ AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", "4");
+
+ DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
+
+ assertThat(detector.hasChangedSince("core-utils", noChildModules(), singletonListOf(tag1)), is(true));
+ assertThat(detector.hasChangedSince("core-utils", noChildModules(), singletonListOf(tag2)), is(false));
+ assertThat(detector.hasChangedSince("console-app", noChildModules(), singletonListOf(tag2)), is(true));
+ assertThat(detector.hasChangedSince("console-app", noChildModules(), singletonListOf(tag3)), is(false));
+ }
+
+ @Test
+ public void canDetectThingsInTheRoot() throws IOException, GitAPIException {
+ TestProject simple = TestProject.singleModuleProject();
+ AnnotatedTag tag1 = saveFileInModule(simple, ".", "1.0", "1");
+ simple.commitRandomFile(".");
+ DiffDetector detector = new TreeWalkingDiffDetector(simple.local.getRepository());
+ assertThat(detector.hasChangedSince(".", noChildModules(), singletonListOf(tag1)), is(true));
+
+ AnnotatedTag tag2 = saveFileInModule(simple, ".", "1.0", "2");
+ assertThat(detector.hasChangedSince(".", noChildModules(), singletonListOf(tag2)), is(false));
+ }
+
+ @Test
+ public void canDetectChangesAfterTheLastTag() throws IOException, GitAPIException {
+ TestProject project = TestProject.independentVersionsProject();
+
+ saveFileInModule(project, "console-app", "1.2", "3");
+ saveFileInModule(project, "core-utils", "2", "0");
+ AnnotatedTag tag3 = saveFileInModule(project, "console-app", "1.2", "4");
+ project.commitRandomFile("console-app");
+
+ DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
+ assertThat(detector.hasChangedSince("console-app", noChildModules(), singletonListOf(tag3)), is(true));
+ }
+
+ @Test
+ public void canIgnoreChangesInModuleFolders() throws IOException, GitAPIException {
+ TestProject project = TestProject.nestedProject();
+
+ AnnotatedTag tag1 = saveFileInModule(project, "server-modules", "1.0.2.4", "0");
+ project.commitRandomFile("server-modules/server-module-a");
+
+ DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
+ assertThat(detector.hasChangedSince("server-modules", asList("server-module-a", "server-module-b"),
+ singletonListOf(tag1)), is(false));
+ }
+
+ @Test
+ public void canDetectLocalChangesWithModuleFolders() throws IOException, GitAPIException {
+ TestProject project = TestProject.nestedProject();
+
+ AnnotatedTag tag1 = saveFileInModule(project, "server-modules", "1.0.2.4", "0");
+ project.commitRandomFile("server-modules");
+
+ DiffDetector detector = new TreeWalkingDiffDetector(project.local.getRepository());
+ assertThat(detector.hasChangedSince("server-modules", asList("server-module-a", "server-module-b"),
+ singletonListOf(tag1)), is(true));
+ }
+
+ @RunWith(Parameterized.class)
+ public static class IgnorePaths {
+ private static TestProject project;
+ private static AnnotatedTag tag;
+
+ @Parameter
+ public String ignoredPathsPattern;
+
+ @Parameter(1)
+ public boolean changeExpected;
+
+ @BeforeClass
+ public static void setup() throws IOException, GitAPIException {
+ project = TestProject.nestedProject();
+ // create & commit a random file, then create a tag at that revision
+ tag = saveFileInModule(project, "server-modules", "1.0", "0");
+ // create & commit three more files but do NOT create a tag at that revision
+ // -> changes in the project (-> signal for the plugin to release those)
+ project.commitFile("server-modules", "paul.txt");
+ project.commitFile("core-utils", "muller.txt");
+ project.commitFile(".", "4711.txt");
+ }
+
+ @Parameters(name = "{0} -> has changes: {1}")
+ public static Collection