Repository: vackosar/gitflow-incremental-builder (java)

Issue:
Optionally buildAll if nothing has changed
Currently, `GIB` just invokes `validate` for the "current project" (usually the root module) in case nothing has changed.
It would come in handy in certain scenarios if there was a second mode (which can be turned on via a new property) that would instead behave as if `buildAll=true` was set to build everything but in an optimized way (`skipTestsForUpstreamModules` & `argsForUpstreamModules`).

Pull request description:
Issue #145: introduce buildAllIfNoChanges
Resolves #145 

Patch:
diff --git a/README.md b/README.md
index 1e2decd..a81030f 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ This extension is **not limited to Git Flow setups!** The [extensive configurati
   - [gib.untracked](#gibuntracked)
   - [gib.excludePathRegex](#gibexcludePathRegex)
   - [gib.buildAll](#gibbuildall)
+  - [gib.buildAllIfNoChanges](#gibbuildallifnochanges)
   - [gib.buildDownstream](#gibbuilddownstream)
   - [gib.buildUpstream](#gibbuildupstream)
   - [gib.buildUpstreamMode](#gibbuildupstreammode)
@@ -130,7 +131,7 @@ Branches are the same now:
 ```
 $ git diff develop
 ```
-Thus incremental does not build and test anything:
+Thus incremental does not build and test anything (see [gib.buildAllIfNoChanges](#gibbuildallifnochanges) for an alternate mode):
 ```
 $ mvn test
 [INFO] Scanning for projects...
@@ -271,6 +272,7 @@ Maven pom properties configuration with default values is below:
     <gib.untracked>true</gib.untracked>                                                   <!-- or <gib.ut>... -->
     <gib.excludePathRegex>(?!x)x</gib.excludePathRegex>                                   <!-- or <gib.epr>... -->
     <gib.buildAll>false</gib.buildAll>                                                    <!-- or <gib.ba>... -->
+    <gib.buildAllIfNoChanges>false</gib.buildAllIfNoChanges>                              <!-- or <gib.bainc>... -->
     <gib.buildDownstream>always</gib.buildDownstream>                                     <!-- or <gib.bd>... -->
     <gib.buildUpstream>derived</gib.buildUpstream>                                        <!-- or <gib.bu>... -->
     <gib.buildUpstreamMode>changed</gib.buildUpstreamMode>                                <!-- or <gib.bum>... -->
@@ -365,6 +367,14 @@ Builds all modules, including upstream modules (see also `gib.buildUpstream`). C
 
 Can be combined/useful with `gib.skipTestsForUpstreamModules` and/or `gib.argsForUpstreamModules`.
 
+### gib.buildAllIfNoChanges
+
+In case no changes are detected, GIB will (by default) just invoke goal `validate` in the "current project" (usually the root module), skipping any submodules.
+
+If this property is enabled, GIB will instead auto-activate [gib.buildAll](#gibbuildall) for all modules and will leave everthing else untouched.
+
+Since: 3.9.2
+
 ### gib.buildDownstream
 
 Controls whether or not to build downstream modules (= modules that depend on the modules GIB detected as changed):
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
index 172abe2..160941f 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
@@ -43,6 +43,7 @@ public class Configuration {
     public final Predicate<String> excludePathRegex;
 
     public final boolean buildAll;
+    public final boolean buildAllIfNoChanges;
     public final boolean buildDownstream;
     public final BuildUpstreamMode buildUpstreamMode;
     public final boolean skipTestsForUpstreamModules;
@@ -73,6 +74,7 @@ public class Configuration {
         // build config
 
         buildAll = Boolean.valueOf(Property.buildAll.getValue(projectProperties));
+        buildAllIfNoChanges = Boolean.valueOf(Property.buildAllIfNoChanges.getValue(projectProperties));
         buildDownstream = isBuildStreamActive(Property.buildDownstream, projectProperties, session, MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM);
         buildUpstreamMode = parseBuildUpstreamMode(session, projectProperties);
         skipTestsForUpstreamModules = Boolean.valueOf(Property.skipTestsForUpstreamModules.getValue(projectProperties));
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
index a74d7f3..b73f79a 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
@@ -35,9 +35,13 @@ class UnchangedProjectsRemover {
     void act() throws GitAPIException, IOException {
         Set<MavenProject> changed = changedProjects.get();
         printDelimiter();
+        if (changed.isEmpty()) {
+            handleNoChangesDetected();
+            return;
+        }
         logProjects(changed, "Changed Artifacts:");
 
-        // important: buildAll *always* need impacted incl. donwstream, otherwise applyNotImpactedModuleArgs() might disable tests etc. for downstream modules!
+        // note: buildAll *always* needs impacted incl. downstream, otherwise applyNotImpactedModuleArgs() might disable tests etc. for downstream modules!
         Configuration cfg = configProvider.get();
         Set<MavenProject> impacted = cfg.buildAll || cfg.buildDownstream
                 ? changed.stream()
@@ -54,14 +58,24 @@ class UnchangedProjectsRemover {
         }
     }
 
-    private void modifyProjectList(Set<MavenProject> changed, Set<MavenProject> impacted) {
-        Set<MavenProject> rebuild = getRebuildProjects(changed, impacted);
-        if (rebuild.isEmpty()) {
-            logger.info("No changed artifacts to build. Executing validate goal on current project only.");
+    private void handleNoChangesDetected() {
+        Configuration cfg = configProvider.get();
+        if (cfg.buildAllIfNoChanges) {
+            logger.info("No changed artifacts detected: Building all modules in buildAll mode.");
+            logger.info("- skip tests: {}", cfg.skipTestsForUpstreamModules);
+            logger.info("- additional args: {}", cfg.argsForUpstreamModules);
+            mavenSession.getProjects().stream().forEach(this::applyUpstreamModuleArgs);
+        } else {
+            logger.info("No changed artifacts detected: Executing validate goal on current project only, skipping all submodules.");
             mavenSession.setProjects(Collections.singletonList(mavenSession.getCurrentProject()));
             mavenSession.getGoals().clear();
             mavenSession.getGoals().add("validate");
-        } else if (!configProvider.get().forceBuildModules.isEmpty()) {
+        }
+    }
+
+    private void modifyProjectList(Set<MavenProject> changed, Set<MavenProject> impacted) {
+        Set<MavenProject> rebuild = getRebuildProjects(changed, impacted);
+        if (!configProvider.get().forceBuildModules.isEmpty()) {
             Set<MavenProject> forceBuildModules = mavenSession.getProjects().stream()
                     .filter(p -> matchesAny(p.getArtifactId(), configProvider.get().forceBuildModules))
                     .filter(p -> !rebuild.contains(p))
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
index d21b508..616b6bb 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
@@ -26,6 +26,7 @@ public enum Property {
     excludePathRegex(Constants.NEVER_MATCH_REGEX, "epr"),
 
     buildAll("false", "ba", true),
+    buildAllIfNoChanges("false", "bainc", true),
     buildDownstream("always", "bd", true),
     buildUpstream("derived", "bu", true),
     buildUpstreamMode("changed", "bum"),


Test patch:
diff --git a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/MavenIntegrationTest.java b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/MavenIntegrationTest.java
index 9a7b79b..6f30f78 100644
--- a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/MavenIntegrationTest.java
+++ b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/MavenIntegrationTest.java
@@ -150,7 +150,7 @@ public class MavenIntegrationTest extends BaseRepoTest {
 
         final String output = executeBuild(prop(Property.baseBranch, "refs/heads/develop"));
 
-        Assert.assertTrue(output.contains("Executing validate goal on current project only."));
+        Assert.assertTrue(output.contains("Executing validate goal on current project only"));
         Assert.assertTrue(output.contains(" child1"));
         Assert.assertFalse(output.contains(" child2"));
         Assert.assertFalse(output.contains(" subchild1"));
diff --git a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverTest.java b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverTest.java
index 89551de..18e30ea 100644
--- a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverTest.java
+++ b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverTest.java
@@ -105,6 +105,23 @@ public class UnchangedProjectsRemoverTest {
         verify(mavenSessionMock).setProjects(Collections.singletonList(mavenProjectMock));
     }
 
+    @Test
+    public void nothingChanged_buildAllIfNoChanges() throws GitAPIException, IOException {
+        MavenProject unchangedModuleMock = addModuleMock(ARTIFACT_ID_2, false);
+
+        projectProperties.put(Property.buildAllIfNoChanges.fullName(), "true");
+        projectProperties.put(Property.skipTestsForUpstreamModules.fullName(), "true");
+
+        underTest.act();
+
+        assertEquals("Unexpected goals", Collections.emptyList(), mavenSessionMock.getGoals());
+
+        verify(mavenSessionMock, never()).setProjects(anyList());
+
+        assertProjectPropertiesEqual(mavenProjectMock, ImmutableMap.of("maven.test.skip", "true"));
+        assertProjectPropertiesEqual(unchangedModuleMock, ImmutableMap.of("maven.test.skip", "true"));
+    }
+
     @Test
     public void singleChanged() throws GitAPIException, IOException {
         MavenProject changedModuleMock = addModuleMock(ARTIFACT_ID_2, true);