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

Issue:
Introduce includePathRegex
The opposite to `excludePathRegex` but a combination should be possible (exclude winning over include).

Usecase: Only build certain modules that contain OpenAPI files in case they changed, to deploy those changes as artifacts for the frontend team to integrate.

Pull request description:
Issue #159: introduce includePathRegex
Resolves #159 

Patch:
diff --git a/README.md b/README.md
index a1354af..3115a26 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ This extension is **not limited to Git Flow setups!** The [extensive configurati
   - [gib.uncommited](#gibuncommited)
   - [gib.untracked](#gibuntracked)
   - [gib.excludePathRegex](#gibexcludePathRegex)
+  - [gib.includePathRegex](#gibincludepathregex)
   - [gib.buildAll](#gibbuildall)
   - [gib.buildAllIfNoChanges](#gibbuildallifnochanges)
   - [gib.buildDownstream](#gibbuilddownstream)
@@ -275,6 +276,7 @@ Maven pom properties configuration with default values is below:
     <gib.uncommited>true</gib.uncommited>                                                 <!-- or <gib.uc>... -->
     <gib.untracked>true</gib.untracked>                                                   <!-- or <gib.ut>... -->
     <gib.excludePathRegex>(?!x)x</gib.excludePathRegex>                                   <!-- or <gib.epr>... -->
+    <gib.includePathRegex>.*</gib.includePathRegex>                                       <!-- or <gib.ipr>... -->
     <gib.buildAll>false</gib.buildAll>                                                    <!-- or <gib.ba>... -->
     <gib.buildAllIfNoChanges>false</gib.buildAllIfNoChanges>                              <!-- or <gib.bainc>... -->
     <gib.buildDownstream>always</gib.buildDownstream>                                     <!-- or <gib.bd>... -->
@@ -365,6 +367,18 @@ The regular expression does _not_ need to describe the entire (absolute) path, b
 ```
 will be excluded when using `-Dgib.excludePathRegex=blacklisted` or `-Dgib.excludePathRegex=some-file\..*` etc., but is _not_ excluded when adding to the regular expression anything _outside_ of the git repository context like `/tmp/repo` or `repo`.
 
+This the opposite of [gib.includePathRegex](#gibincludepathregex) which can be combined with this property, but `gib.excludePathRegex` will take precedence.
+
+### gib.includePathRegex
+
+Can be used to include only certain changed files from being detected as changed, reducing the number of modules to build.
+
+This the opposite of [gib.excludePathRegex](#gibexcludepathregex) which can be combined with this property, but `gib.excludePathRegex` will take precedence.
+
+See [gib.excludePathRegex](#gibexcludepathregex) for general path matching rules.
+
+Since: 3.10.0
+
 ### gib.buildAll
 
 Builds all modules, including upstream modules (see also `gib.buildUpstream`). Can be used to (temporarily) override the reduction of modules to build.
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
index 75a030a..721b11d 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java
@@ -41,6 +41,7 @@ public class Configuration {
     public final boolean uncommited;
     public final boolean untracked;
     public final Predicate<String> excludePathRegex;
+    public final Predicate<String> includePathRegex;
 
     public final boolean buildAll;
     public final boolean buildAllIfNoChanges;
@@ -70,6 +71,7 @@ public class Configuration {
         uncommited = Boolean.valueOf(Property.uncommited.getValue(projectProperties));
         untracked = Boolean.valueOf(Property.untracked.getValue(projectProperties));
         excludePathRegex = compilePattern(Property.excludePathRegex, projectProperties).asPredicate();
+        includePathRegex = compilePattern(Property.includePathRegex, projectProperties).asPredicate();
 
         // build config
 
@@ -119,7 +121,7 @@ public class Configuration {
 
     /**
      * Returns whether or not the given make behaviour is active for the given session.
-     * 
+     *
      * @param expectedMakeBehavior one of {@link MavenExecutionRequest#REACTOR_MAKE_DOWNSTREAM} or {@link MavenExecutionRequest#REACTOR_MAKE_UPSTREAM}
      * @param session the session providing the request
      * @return whether the given behaviour (or {@link MavenExecutionRequest#REACTOR_MAKE_BOTH}) is active or not
@@ -228,7 +230,7 @@ public class Configuration {
 
         /**
          * Returns a {@link Configuration} instance which is constructed when first called. Subsequent calls will return the same instance.
-         * 
+         *
          * @return a {@link Configuration} instance
          */
         @Override
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/control/DifferentFiles.java b/src/main/java/com/vackosar/gitflowincrementalbuild/control/DifferentFiles.java
index 587300b..77ae561 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/control/DifferentFiles.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/control/DifferentFiles.java
@@ -208,7 +208,7 @@ public class DifferentFiles {
             final Set<Path> paths = new HashSet<>();
             while (treeWalk.next()) {
                 Path path = Paths.get(treeWalk.getPathString()).normalize();
-                if (pathNotExcluded(path)) {
+                if (pathIncluded(path)) {
                     paths.add(gitDir.resolve(path));
                 }
             }
@@ -240,7 +240,7 @@ public class DifferentFiles {
             return changes.stream()
                     .map(Paths::get)
                     .map(Path::normalize)
-                    .filter(this::pathNotExcluded)
+                    .filter(this::pathIncluded)
                     .map(workTree::resolve)
                     .collect(Collectors.toSet());
         }
@@ -254,10 +254,11 @@ public class DifferentFiles {
             }
         }
 
-        private boolean pathNotExcluded(Path path) {
+        private boolean pathIncluded(Path path) {
             boolean excluded = configuration.excludePathRegex.test(path.toString());
-            logger.debug("excluded {}: {}", excluded, path);
-            return !excluded;
+            boolean included = !excluded && configuration.includePathRegex.test(path.toString());
+            logger.debug("included {}: {}", included, path);
+            return included;
         }
     }
 }
diff --git a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
index 434f5f1..f8a1868 100644
--- a/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
+++ b/src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java
@@ -24,6 +24,7 @@ public enum Property {
     uncommited("true", "uc", true),
     untracked("true", "ut", true),
     excludePathRegex(Constants.NEVER_MATCH_REGEX, "epr"),
+    includePathRegex(Constants.ALWAYS_MATCH_REGEX, "ipr"),
 
     buildAll("false", "ba", true),
     buildAllIfNoChanges("false", "bainc", true),
@@ -132,6 +133,7 @@ public enum Property {
     }
 
     private static class Constants {
+        private static final String ALWAYS_MATCH_REGEX = ".*";
         private static final String NEVER_MATCH_REGEX = "(?!x)x";
     }
 }


Test patch:
diff --git a/src/test/java/com/vackosar/gitflowincrementalbuild/control/DifferentFilesTest.java b/src/test/java/com/vackosar/gitflowincrementalbuild/control/DifferentFilesTest.java
index 29f388a..c4d6dd4 100644
--- a/src/test/java/com/vackosar/gitflowincrementalbuild/control/DifferentFilesTest.java
+++ b/src/test/java/com/vackosar/gitflowincrementalbuild/control/DifferentFilesTest.java
@@ -39,7 +39,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUncommitted() throws Exception {
+    public void listWithUncommitted() throws Exception {
         Path modifiedFilePath = modifyTrackedFile(repoPath);
         projectProperties.setProperty(Property.uncommited.fullName(), "true");
 
@@ -47,7 +47,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUncommitted_disabled() throws Exception {
+    public void listWithUncommitted_disabled() throws Exception {
         Path modifiedFilePath = modifyTrackedFile(repoPath);
         projectProperties.setProperty(Property.uncommited.fullName(), "false");
 
@@ -55,7 +55,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUncommitted_excluded() throws Exception {
+    public void listWithUncommitted_excluded() throws Exception {
         Path modifiedFilePath = modifyTrackedFile(repoPath);
         projectProperties.setProperty(Property.uncommited.fullName(), "true");
         projectProperties.setProperty(Property.excludePathRegex.fullName(), Pattern.quote(repoPath.relativize(modifiedFilePath).toString()));
@@ -64,7 +64,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUntracked() throws Exception {
+    public void listWithUntracked() throws Exception {
         Path newFilePath = createNewUntrackedFile(repoPath);
         projectProperties.setProperty(Property.untracked.fullName(), "true");
 
@@ -72,7 +72,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUntracked_disabled() throws Exception {
+    public void listWithUntracked_disabled() throws Exception {
         Path newFilePath = createNewUntrackedFile(repoPath);
         projectProperties.setProperty(Property.untracked.fullName(), "false");
 
@@ -80,7 +80,7 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
     }
 
     @Test
-    public void listIncludingUntracked_excluded() throws Exception {
+    public void listWithUntracked_excluded() throws Exception {
         Path newFilePath = createNewUntrackedFile(repoPath);
         projectProperties.setProperty(Property.untracked.fullName(), "true");
         projectProperties.setProperty(Property.excludePathRegex.fullName(), Pattern.quote(repoPath.relativize(newFilePath).toString()));
@@ -124,6 +124,28 @@ public class DifferentFilesTest extends BaseDifferentFilesTest {
         Assert.assertEquals(expected, invokeUnderTest());
     }
 
+    @Test
+    public void listIncluding() throws Exception {
+        projectProperties.setProperty(Property.includePathRegex.fullName(), ".*file2.*");
+        final Set<Path> expected = new HashSet<>(Arrays.asList(
+                Paths.get(repoPath + "/parent/child2/subchild2/src/resources/file2"),
+                Paths.get(repoPath + "/parent/child2/subchild2/src/resources/file22")
+                ));
+
+        Assert.assertEquals(expected, invokeUnderTest());
+    }
+
+    @Test
+    public void listIncludingAndExcluding() throws Exception {
+        projectProperties.setProperty(Property.includePathRegex.fullName(), ".*file2.*");
+        projectProperties.setProperty(Property.excludePathRegex.fullName(), ".*file22.*");
+        final Set<Path> expected = new HashSet<>(Arrays.asList(
+                Paths.get(repoPath + "/parent/child2/subchild2/src/resources/file2")
+                ));
+
+        Assert.assertEquals(expected, invokeUnderTest());
+    }
+
     @Test
     public void listWithDisabledBranchComparison() throws Exception {
         projectProperties.setProperty(Property.disableBranchComparison.fullName(), "true");