A maven extension for incremental building of multi-module projects when using feature branches (Git Flow). Builds or tests only changed maven modules compared to reference branch in Git (e.g. origin/develop) and all their dependents.
This extension is not limited to Git Flow setups! The extensive configuration options provide support many other branch setups and/or use cases.
- Add into maven pom file:
<build>
<extensions>
<extension>
<groupId>com.vackosar.gitflowincrementalbuilder</groupId>
<artifactId>gitflow-incremental-builder</artifactId>
<version>3.8</version>
</extension>
</extensions>
...
</build>
As IDEs like IntelliJ IDEA or Eclipse usually apply their own custom strategy to building changed modules, this extension should be disabled in such environments to avoid inconsistencies or errors (e.g. see issue 49).
See gib.enabled in the configuration section.
GIB 3.7 has to be used until an IntelliJ IDEA version is released that contains the fix for IDEA-200272. See issue 91 for more details.
Maven project parent
has two submodules child1
and child2
:
$ ls *
pom.xml
child1:
pom.xml src
child2:
pom.xml src
parent
has gitflow-incremental-builder configured:
$ cat pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>parent</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<gib.referenceBranch>refs/heads/develop</gib.referenceBranch>
</properties>
<modules>
<module>child1</module>
<module>child2</module>
</modules>
<build>
<extensions>
<extension>
<groupId>com.vackosar.gitflowincrementalbuilder</groupId>
<artifactId>gitflow-incremental-builder</artifactId>
<version>3.8</version>
</extension>
</extensions>
</build>
</project>
Currently checked out branch is feature
. There is also a branch develop
:
$ git branch
develop
* feature
Branches are the same now:
$ git diff develop
Thus incremental does not build and test anything:
$ mvn test
[INFO] Scanning for projects...
[INFO] gitflow-incremental-builder starting...
[INFO] Git dir is: C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\.git
[INFO] Head of branch HEAD is commit of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -----p
[INFO] Head of branch refs/heads/develop is commit of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -----p
[INFO] Using merge base of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -tr-sp
[INFO] ------------------------------------------------------------------------
[INFO] Changed Artifacts:
[INFO]
[INFO]
[INFO] No changed artifacts to build. Executing validate goal only.
[INFO] gitflow-incremental-builder exiting...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] child1
[INFO] child2
[INFO] parent
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building child1 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building child2 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building parent 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] child1 ............................................. SUCCESS [ 0.002 s]
[INFO] child2 ............................................. SUCCESS [ 0.001 s]
[INFO] parent ............................................. SUCCESS [ 0.000 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.953 s
[INFO] Finished at: 2016-12-30T16:56:35+01:00
[INFO] Final Memory: 9M/162M
[INFO] ------------------------------------------------------------------------
Now a file is written and added to git control making branches different:
$ echo "text" > child1/src/main/java/file1.txt
$ git add child1/src/main/java/file1.txt
$ git diff develop
diff --git a/child1/src/main/java/file1.txt b/child1/src/main/java/file1.txt
new file mode 100644
index 0000000..8e27be7
--- /dev/null
+++ b/child1/src/main/java/file1.txt
@@ -0,0 +1 @@
+text
warning: LF will be replaced by CRLF in child1/src/main/java/file1.txt.
The file will have its original line endings in your working directory.
Thus incremental build builds and tests only child1:
$ mvn test
[INFO] Scanning for projects...
[INFO] gitflow-incremental-builder starting...
[INFO] Git dir is: C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\.git
[INFO] Head of branch HEAD is commit of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -----p
[INFO] Head of branch refs/heads/develop is commit of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -----p
[INFO] Using merge base of id: commit 17b09f1f9ee9d2a56b7d7bf43d319db529cade9e 1483112738 -tr-sp
[INFO] ------------------------------------------------------------------------
[INFO] Changed Artifacts:
[INFO]
[INFO] child1
[INFO]
[INFO] gitflow-incremental-builder exiting...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building child1 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child1 ---
[WARNING] Using platform encoding (Cp1250 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\child1\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ child1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1250, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\child1\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child1 ---
[WARNING] Using platform encoding (Cp1250 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\child1\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ child1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1250, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\child1\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ child1 ---
[INFO] Surefire report directory: C:\Users\Vaclav\IdeaProjects\gitflow-incremental-builder\tmp\example\child1\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running ATest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.043 s
[INFO] Finished at: 2016-12-30T17:01:56+01:00
[INFO] Final Memory: 18M/167M
[INFO] ------------------------------------------------------------------------
Maven pom properties configuration with default values is below:
<properties>
<gib.enabled>true</gib.enabled>
<gib.disableBranchComparison>false</gib.disableBranchComparison>
<gib.referenceBranch>refs/remotes/origin/develop</gib.referenceBranch>
<gib.fetchReferenceBranch>false</gib.fetchReferenceBranch>
<gib.baseBranch>HEAD</gib.baseBranch>
<gib.fetchBaseBranch>false</gib.fetchBaseBranch>
<gib.compareToMergeBase>true</gib.compareToMergeBase>
<gib.uncommited>true</gib.uncommited>
<gib.untracked>true</gib.untracked>
<gib.excludePathRegex>(?!x)x</gib.excludePathRegex>
<gib.buildAll>false</gib.buildAll>
<gib.buildDownstream>always</gib.buildDownstream>
<gib.buildUpstream>derived</gib.buildUpstream>
<gib.buildUpstreamMode>changed</gib.buildUpstreamMode>
<gib.skipTestsForUpstreamModules>false</gib.skipTestsForUpstreamModules>
<gib.argsForUpstreamModules></gib.argsForUpstreamModules>
<gib.forceBuildModules></gib.forceBuildModules>
<gib.excludeTransitiveModulesPackagedAs></gib.excludeTransitiveModulesPackagedAs>
<gib.failOnMissingGitDir>true</gib.failOnMissingGitDir>
<gib.failOnError>true</gib.failOnError>
</properties>
Can be used to disable this extension temporarily or permanently (e.g. to avoid clashes with IDE building strategy).
Disables the comparison between baseBranch
and referenceBranch
. This property should be enabled if only uncommitted and/or untracked files shall be detected to only build projects that have been changed since the last commit in the current branch (see gib.uncommited
and gib.untracked
).
The following properties are not evaluated when gib.disableBranchComparison
is enabled:
gib.referenceBranch
gib.compareToMergeBase
gib.fetchReferenceBranch
gib.excludePathRegex
Detects changed files that have not yet been committed. This does not include untracked files (see git status
manual).
Detects files that are not yet tracked by git (see git status
manual). This does not include uncommitted files. A new file is not untracked anymore after it is added to the index.
Can be used to exclude certain changed files from being detected as changed, reducing the number of modules to build.
The regular expression does not need to describe the entire (absolute) path, but only the relevant part inside the git repository context. Example:
/tmp/repo/blacklisted/some-file.txt
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
.
Builds all modules, including upstream modules (see also gib.buildUpstream
). Can be used to (temporarily) override the reduction of modules to build.
Can be combined/useful with gib.skipTestsForUpstreamModules
and/or gib.argsForUpstreamModules
.
Controls whether or not to build downstream modules (= modules that depend on the modules GIB detected as changed):
always
ortrue
(default value): always build downstream modulesderived
: only build downstream modules ifmvn -amd
is callednever
orfalse
: never build downstream modules
Since: 3.8
Controls whether or not to build upstream modules (= dependencies and parents of the modules GIB has determined to build):
always
ortrue
: always build upstream modulesderived
(default value): only build upstream modules ifmvn -am
is callednever
orfalse
: never build upstream modules
See also gib.buildUpstreamMode
.
Since: 3.8
This property controls which upstream modules to build (if at all building upstream modules, see gib.buildUpstream
):
changed
(default value): selects only upstream modules of the directly changed modulesimpacted
: likechanged
but also selects upstream modules of modules that depend on the directly changed modules (in other words: upstream modules of the downstream modules of the changed modules)
changed
is a subset of impacted
.
impacted
may seem odd at first, but it does come in handy in certain scenarios, e.g. a Jenkins PR job that locally merges target branch into the PR branch before building.
Here it might be required to freshly compile upstream modules of not directly changed modules to avoid compile errors or test failures which originate from the target branch.
Both strategies can and usually should be combined with gib.skipTestsForUpstreamModules
and/or gib.argsForUpstreamModules
.
Note: Before 3.8, GIB did non have this property and was implicity applying the impacted
strategy, see also #44.
Since: 3.8
This property disables the compilation/execution of tests for upstream modules by adding maven.test.skip=true
. In case an upstream module produces a test jar just the test execution is disabled via skipTests=true
.
See gib.buildUpstream
or gib.buildAll
to learn when upstream modules are built.
Can be combined with gib.argsForUpstreamModules
.
This property allows adding arbitrary arguments/properties for upstream modules to futher reduce overhead, e.g. skip Checkstyle or Enforcer plugin. Arguments have to be sparated with a single space character and values are optional. Example:
mvn clean install -am -Dgib.argsForUpstreamModules='-Denforcer.skip -Dcheckstyle.skip=true'
See gib.buildUpstream
or gib.buildAll
to learn when upstream modules are built.
Can be combined with gib.skipTestsForUpstreamModules
.
Defines artifact ids of modules to build forcibly, even if these modules have not been changed and/or do not depend on changed modules. Example:
mvn clean install -Dgib.forceBuildModules=unchanged-module-1,unchanged-module-2
Regular expressions are also supported in each comma separated part, e.g.:
mvn clean install -Dgib.forceBuildModules=unchanged-module-.*,another-module
Each of these modules is subject to argsForUpstreamModules
and skipTestsForUpstreamModules
.
This property has no effect in case buildAll
is enabled.
Defines the packaging (e.g. jar
) of modules that depend on changed modules but shall not be built.
One possible use case for this is mainly working in an IDE, fixing all compile errors etc. and then just quickly building the least possible amount of modules
which are needed to (hot-)deploy the changes via mvn
on the command line.
In this scenario, by defining -Dgib.excludeTransitiveModulesPackagedAs=jar,pom
, only the directly changed jar
modules and the dependent war
and/or ear
deployment modules will be built.
This property has no effect in case buildAll
is enabled and an exclusion might be overriden by gib.forceBuildModules
.
- Maven version 3.3.9+ is recommended (however, GIB might work with Maven down to version 3.1.0)
- Project must use Git