Repository: vackosar/gitflow-incremental-builder
Pull request #252 (2020-09-08 21:48:14)

Introduce disableSelectedProjectsHandling
Opts out of the default "non-interference" policy regarding "explicitly selected projects".

@gastaldi This should be the solution for the problem mentioned here: https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/gitflow-incremental-builder.20%28GIB%29/near/208633072

/cc @mickroll (I think I'll merge this very soon, but you are welcome to have a look after it has been merged.)


Patch:
diff --git a/README.md b/README.md
index 7c0d173..db91252 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ This extension is **not limited to Git Flow setups!** The [extensive configurati
   - [gib.argsForUpstreamModules](#gibargsforupstreammodules)
   - [gib.forceBuildModules](#gibforcebuildmodules)
   - [gib.excludeDownstreamModulesPackagedAs](#gibexcludedownstreammodulespackagedas)
+  - [gib.disableSelectedProjectsHandling](#gibdisableselectedprojectshandling)
   - [gib.failOnMissingGitDir](#failonmissinggitdir)
   - [gib.failOnError](#gibfailonerror)
   - [gib.logImpactedTo](#giblogimpactedto)
@@ -334,6 +335,7 @@ Maven pom properties configuration with default values is below:
     <gib.argsForUpstreamModules></gib.argsForUpstreamModules>                          <!-- or -Dgib.afum=...  -->
     <gib.forceBuildModules></gib.forceBuildModules>                                    <!-- or -Dgib.fbm=...   -->
     <gib.excludeDownstreamModulesPackagedAs></gib.excludeDownstreamModulesPackagedAs>  <!-- or -Dgib.edmpa=... -->
+    <gib.disableSelectedProjectsHandling>false</gib.disableSelectedProjectsHandling>   <!-- or -Dgib.dsph=...  -->
     <gib.failOnMissingGitDir>true</gib.failOnMissingGitDir>                            <!-- or -Dgib.fomgd=... -->
     <gib.failOnError>true</gib.failOnError>                                            <!-- or -Dgib.foe=...   -->
     <gib.logImpactedTo></gib.logImpactedTo>                                            <!-- or -Dgib.lit=...   -->
@@ -578,6 +580,15 @@ deployment modules will be built.
 
 This property has no effect in case `buildAll` is enabled and an exclusion might be overriden by `gib.forceBuildModules`.
 
+### gib.disableSelectedProjectsHandling
+
+Disables special handling of [explicitly selected projects](#explicitly-selected-projects) (-pl, -f etc.).
+
+This can come in handy if you select just one module with `-pl` but you only want to have it built _fully_ (with tests etc.)
+if the selected module itself is changed or one of its (non-selected) upstream modules.
+
+Since: 3.12.0
+
 ### gib.failOnMissingGitDir
 
 Controls whether or not to fail on missing `.git` directory.
@@ -598,6 +609,10 @@ Since: 3.10.1
 
 ## Explicitly selected projects
 
+By default, GIB tries not to interfere with any projects/modules that have been selected explicitely by the user.
+
+The details are described in the following subsections. This special handling can be disabled via [gib.disableSelectedProjectsHandling](#gibdisableSelectedProjectsHandling).
+
 ### mvn -pl
 
 Since 3.10.0, special rules appy when `mvn -pl ...` (or `--projects ...`) is used:
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
index da82914..c7720b9 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
@@ -62,6 +62,7 @@ public class Configuration {
     public final Map<String, String> argsForUpstreamModules;
     public final List<Pattern> forceBuildModules;
     public final List<String> excludeDownstreamModulesPackagedAs;
+    public final boolean disableSelectedProjectsHandling;
 
     public final boolean failOnMissingGitDir;
     public final boolean failOnError;
@@ -111,6 +112,8 @@ public class Configuration {
 
             excludeDownstreamModulesPackagedAs = null;
 
+            disableSelectedProjectsHandling = false;
+
             // error handling config
 
             failOnMissingGitDir = false;
@@ -126,26 +129,26 @@ public class Configuration {
 
         // change detection config
 
-        disableBranchComparison = Boolean.valueOf(Property.disableBranchComparison.getValue(pluginProperties, projectProperties));
+        disableBranchComparison = Boolean.parseBoolean(Property.disableBranchComparison.getValue(pluginProperties, projectProperties));
         referenceBranch = Property.referenceBranch.getValue(pluginProperties, projectProperties);
-        fetchReferenceBranch = Boolean.valueOf(Property.fetchReferenceBranch.getValue(pluginProperties, projectProperties));
+        fetchReferenceBranch = Boolean.parseBoolean(Property.fetchReferenceBranch.getValue(pluginProperties, projectProperties));
         baseBranch = Property.baseBranch.getValue(pluginProperties, projectProperties);
-        fetchBaseBranch = Boolean.valueOf(Property.fetchBaseBranch.getValue(pluginProperties, projectProperties));
-        useJschAgentProxy = Boolean.valueOf(Property.useJschAgentProxy.getValue(pluginProperties, projectProperties));
-        compareToMergeBase = Boolean.valueOf(Property.compareToMergeBase.getValue(pluginProperties, projectProperties));
-        uncommitted = Boolean.valueOf(Property.uncommitted.getValue(pluginProperties, projectProperties));
-        untracked = Boolean.valueOf(Property.untracked.getValue(pluginProperties, projectProperties));
+        fetchBaseBranch = Boolean.parseBoolean(Property.fetchBaseBranch.getValue(pluginProperties, projectProperties));
+        useJschAgentProxy = Boolean.parseBoolean(Property.useJschAgentProxy.getValue(pluginProperties, projectProperties));
+        compareToMergeBase = Boolean.parseBoolean(Property.compareToMergeBase.getValue(pluginProperties, projectProperties));
+        uncommitted = Boolean.parseBoolean(Property.uncommitted.getValue(pluginProperties, projectProperties));
+        untracked = Boolean.parseBoolean(Property.untracked.getValue(pluginProperties, projectProperties));
         excludePathRegex = compileOptionalPatternPredicate(Property.excludePathRegex, pluginProperties, projectProperties);
         includePathRegex = compileOptionalPatternPredicate(Property.includePathRegex, pluginProperties, projectProperties);
 
         // build config
 
-        buildAll = Boolean.valueOf(Property.buildAll.getValue(pluginProperties, projectProperties));
-        buildAllIfNoChanges = Boolean.valueOf(Property.buildAllIfNoChanges.getValue(pluginProperties, projectProperties));
+        buildAll = Boolean.parseBoolean(Property.buildAll.getValue(pluginProperties, projectProperties));
+        buildAllIfNoChanges = Boolean.parseBoolean(Property.buildAllIfNoChanges.getValue(pluginProperties, projectProperties));
         buildDownstream = isBuildStreamActive(
                 Property.buildDownstream, pluginProperties, projectProperties, session, MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM);
         buildUpstreamMode = parseBuildUpstreamMode(session, pluginProperties, projectProperties);
-        skipTestsForUpstreamModules = Boolean.valueOf(Property.skipTestsForUpstreamModules.getValue(pluginProperties, projectProperties));
+        skipTestsForUpstreamModules = Boolean.parseBoolean(Property.skipTestsForUpstreamModules.getValue(pluginProperties, projectProperties));
 
         argsForUpstreamModules = parseDelimited(Property.argsForUpstreamModules.getValue(pluginProperties, projectProperties), " ")
                 .map(Configuration::keyValueStringToEntry)
@@ -158,10 +161,12 @@ public class Configuration {
         excludeDownstreamModulesPackagedAs = parseDelimited(Property.excludeDownstreamModulesPackagedAs.getValue(pluginProperties, projectProperties), ",")
                 .collect(collectingAndThen(toList(), Collections::unmodifiableList));
 
+        disableSelectedProjectsHandling = Boolean.parseBoolean(Property.disableSelectedProjectsHandling.getValue(pluginProperties, projectProperties));
+
         // error handling config
 
-        failOnMissingGitDir = Boolean.valueOf(Property.failOnMissingGitDir.getValue(pluginProperties, projectProperties));
-        failOnError = Boolean.valueOf(Property.failOnError.getValue(pluginProperties, projectProperties));
+        failOnMissingGitDir = Boolean.parseBoolean(Property.failOnMissingGitDir.getValue(pluginProperties, projectProperties));
+        failOnError = Boolean.parseBoolean(Property.failOnError.getValue(pluginProperties, projectProperties));
         logImpactedTo = Property.logImpactedTo.getValueOpt(pluginProperties, projectProperties).map(Paths::get);
     }
 
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
index 3092666..1f1311b 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java
@@ -62,23 +62,29 @@ class UnchangedProjectsRemover {
         // ensure to write logfile for impaced (even if just empty)
         config.logImpactedTo.ifPresent(logFilePath -> writeImpactedLogFile(Collections.emptySet(), logFilePath, config.mavenSession));
 
-        // before checking for any changes, check whether there are _only_ explicitly selected projects (-pl) which have the highest priority
-        final Set<MavenProject> selected = ProjectSelectionUtil.gatherSelectedProjects(config.mavenSession);
-        if (onlySelectedModulesPresent(selected, config.mavenSession)) {
-            printDelimiter();
-            logger.info("Building explicitly selected projects (without any adjustment): {}",
-                    config.mavenSession.getProjects().stream().map(MavenProject::getArtifactId).collect(Collectors.joining(", ")));
-            return;
-        }
+        final Set<MavenProject> selected;
+        if (config.disableSelectedProjectsHandling) {
+            selected = Collections.emptySet();
+        } else {
+            selected = ProjectSelectionUtil.gatherSelectedProjects(config.mavenSession);
+
+            // before checking for any changes, check whether there are _only_ explicitly selected projects (-pl) which have the highest priority
+            if (onlySelectedModulesPresent(selected, config.mavenSession)) {
+                printDelimiter();
+                logger.info("Building explicitly selected projects (without any adjustment): {}",
+                        config.mavenSession.getProjects().stream().map(MavenProject::getArtifactId).collect(Collectors.joining(", ")));
+                return;
+            }
 
-        // do nothing if:
-        // - building non-recursively (-N)
-        // - or only one "leaf" project/module is present (cases: mvn -f ... or cd ... or unusual case of non-multi-module project)
-        // the assumption here (similar to the -pl approach above): the user has decided to build a single module, so don't mess with that
-        if (!config.mavenSession.getRequest().isRecursive() || onlySingleLeafModulePresent(config)) {
-            printDelimiter();
-            logger.info("Building single project (without any adjustment): {}", config.currentProject.getArtifactId());
-            return;
+            // do nothing if:
+            // - building non-recursively (-N)
+            // - or only one "leaf" project/module is present (cases: mvn -f ... or cd ... or unusual case of non-multi-module project)
+            // the assumption here (similar to the -pl approach above): the user has decided to build a single module, so don't mess with that
+            if (!config.mavenSession.getRequest().isRecursive() || onlySingleLeafModulePresent(config)) {
+                printDelimiter();
+                logger.info("Building single project (without any adjustment): {}", config.currentProject.getArtifactId());
+                return;
+            }
         }
 
         final Set<MavenProject> changed = changedProjects.get(config);
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
index c66d12c..27e4828 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
@@ -135,6 +135,10 @@ public enum Property {
      * Defines the packaging (e.g. jar) of modules that depend on changed modules but shall not be built.
      */
     excludeDownstreamModulesPackagedAs("", "edmpa"),
+    /**
+     * Disables special handling of explicitly selected projects (-pl, -f etc.).
+     */
+    disableSelectedProjectsHandling("false", "dsph", true),
 
     /**
      * Controls whether or not to fail on missing .git directory.


Test patch:
diff --git a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverSelectedProjectsTest.java b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverSelectedProjectsTest.java
index c50ae58..efb4b78 100644
--- a/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverSelectedProjectsTest.java
+++ b/src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverSelectedProjectsTest.java
@@ -48,6 +48,25 @@ public class UnchangedProjectsRemoverSelectedProjectsTest extends BaseUnchangedP
         assertProjectPropertiesEqual(moduleB);
     }
 
+    // mvn -pl :module-B
+    @Test
+    public void nothingChanged_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, false);
+        setProjectSelections(moduleB);
+        overrideProjects(moduleB);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        assertEquals(Collections.singletonList("validate"), mavenSessionMock.getGoals(), "Unexpected goals");
+
+        verify(mavenSessionMock).setProjects(Collections.singletonList(moduleB));
+
+        assertProjectPropertiesEqual(moduleB);
+    }
+
+
     // mvn -pl :module-B,:module-C
     @Test
     public void nothingChanged_twoSelected() throws GitAPIException, IOException {
@@ -66,6 +85,26 @@ public class UnchangedProjectsRemoverSelectedProjectsTest extends BaseUnchangedP
         assertProjectPropertiesEqual(moduleC);
     }
 
+    // mvn -pl :module-B,:module-C
+    @Test
+    public void nothingChanged_twoSelected_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, false);
+        MavenProject moduleC = addModuleMock(AID_MODULE_C, false);
+        setProjectSelections(moduleB, moduleC);
+        overrideProjects(moduleB, moduleC);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        assertEquals(Collections.singletonList("validate"), mavenSessionMock.getGoals(), "Unexpected goals");
+
+        verify(mavenSessionMock).setProjects(Collections.singletonList(moduleB));   // only B (sic!), as there can only be one "current" project
+
+        assertProjectPropertiesEqual(moduleB);
+        assertProjectPropertiesEqual(moduleC);
+    }
+
     // mvn -pl :module-B -am
     @Test
     public void nothingChanged_makeUpstream() throws GitAPIException, IOException {
@@ -202,6 +241,26 @@ public class UnchangedProjectsRemoverSelectedProjectsTest extends BaseUnchangedP
         verify(mavenSessionMock, never()).setProjects(anyList());
     }
 
+    // mvn -pl :module-B
+    @Test
+    public void moduleAChanged_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, false);
+        setProjectSelections(moduleB);
+        overrideProjects(moduleB);
+
+        changedProjects.add(moduleA);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        assertEquals(Collections.emptyList(), mavenSessionMock.getGoals(), "Unexpected goals");
+
+        verify(mavenSessionMock).setProjects(Collections.singletonList(moduleB));
+
+        assertProjectPropertiesEqual(moduleB);
+    }
+
     // mvn -pl :module-B -am
     @Test
     public void moduleAChanged_makeUpstream() throws GitAPIException, IOException {
@@ -437,6 +496,37 @@ public class UnchangedProjectsRemoverSelectedProjectsTest extends BaseUnchangedP
         verify(mavenSessionMock, never()).setProjects(anyList());
     }
 
+    @Test
+    public void moduleBChanged_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, true);
+        setProjectSelections(moduleB);
+        overrideProjects(moduleB);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        verify(mavenSessionMock).setProjects(Collections.singletonList(moduleB));
+
+        assertProjectPropertiesEqual(moduleB);
+    }
+
+    @Test
+    public void moduleBChanged_twoSelected_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, true);
+        MavenProject moduleC = addModuleMock(AID_MODULE_C, false);
+        setProjectSelections(moduleB, moduleC);
+        overrideProjects(moduleB, moduleC);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        verify(mavenSessionMock).setProjects(Collections.singletonList(moduleB));
+
+        assertProjectPropertiesEqual(moduleB);
+    }
+
     // mvn -pl :module-B -am
     @Test
     public void moduleBChanged_makeUpstream() throws GitAPIException, IOException {
@@ -674,6 +764,23 @@ public class UnchangedProjectsRemoverSelectedProjectsTest extends BaseUnchangedP
         assertProjectPropertiesEqual(moduleE);
     }
 
+    @Test
+    public void twoSelected_bothChanged_disableSelectedProjectsHandling() throws GitAPIException, IOException {
+        MavenProject moduleB = addModuleMock(AID_MODULE_B, true);
+        MavenProject moduleC = addModuleMock(AID_MODULE_C, true);
+        setProjectSelections(moduleB, moduleC);
+        overrideProjects(moduleB, moduleC);
+
+        addGibProperty(Property.disableSelectedProjectsHandling, "true");
+
+        underTest.act(config());
+
+        verify(mavenSessionMock).setProjects(Arrays.asList(moduleB, moduleC));
+
+        assertProjectPropertiesEqual(moduleB);
+        assertProjectPropertiesEqual(moduleC);
+    }
+
     // See "-pl :...,:..." and don't forget to call overrideProjects() if any of the moduleMocks shall _not_ be in the projects list!
     private void setProjectSelections(MavenProject... projectsToSelect) {
         List<String> selection = Arrays.stream(projectsToSelect).map(p -> ":" + p.getArtifactId()).collect(Collectors.toList());