Extracts Git metadata and a freely composable build number in pure Java without Git command-line tool.
Homepage: https://github.com/elab/jgit-buildnumber
Maven Central: search.maven.org - mvnrepository.com - repo1.maven.org
Current version | Published | Compatibility |
---|---|---|
2.7.0 | 2022-06-12 | Java 8 .. 17+, Maven 3+, Ant 1+, Gradle 3 .. 7+ |
Based on the work of Alex Kasko with merged changes from other forks. Thank you guys! Additionally contains many new features, bug fixes, and performance improvements.
🚀 The project enjoys growing popularity. The number of monthly downloads from Maven Central exceeded 10.000 (180 unique IPs) at the end of 2021 according to statistics of Sonatype (which is the hoster of open source projects for Maven Central). Thank you for your trust! ❤️
We believe that JGit Build Number is the best plugin for its purpose, but you can also look at alternatives:
- maven-git-commit-id-plugin
- original Git buildnumber plugin for Maven and Ant based on JGit
- Build Number Maven Plugin
Say what you think, feedback is always welcome :)
Contact: Eugen Labun labun@gmx.net
Contents:
- Philosophy
- Extracted properties
- Configuration
- Performance
- Usage in Maven
- Usage in Ant
- Usage in Gradle
- GitLab tips
- Development notes
- License information
- Changelog
Build number should identify the code state of the project from which it has been created. Particularly, it should not depend on:
- where the build takes place (locally, build server);
- how many times the same project state has been build (i.e. no simple increment).
In our case, the default BuildNumber looks like v19.3351.ddda02b
and consists of:
-
human readable id: tag name or branch name
v19
git describe --exact-match --tags HEAD # tag name git symbolic-ref -q HEAD # branch name
-
build incremental id: commits count (closely resembles SVN revision number)
3351
git rev-list HEAD | wc -l
-
globally unique id: commit SHA-1
ddda02b
git rev-parse HEAD # revision git rev-parse --short HEAD # short revision
-
dirty flag (optional): inserted if there are differences between working-tree, index, and HEAD; the BuildNumber would look like
v19.3351.ddda02b-dirty
; you cannot trust the build in this case :)git status
Instead of the Git CLI commands above, the pure Java JGit API is used.
Note: The JGit output (intentionally) doesn't coincide exactly with the output of Git CLI.
E.g. branch and tag names are returned without the refs/...
prefix.
Git metadata, build number, and build date (added for convenience) are published as following project properties:
property | description |
---|---|
git.revision | HEAD SHA-1 |
git.shortRevision | HEAD SHA-1 (abbreviated, see shortRevisionLength) |
git.dirty | contains dirtyValue if differences exist between working-tree, index, and HEAD; empty string otherwise; in verbose mode, detailed info will be printed to log about the changes which caused the dirty status (very helpful if the problem occurs on a remote build server) |
git.branch | branch name; empty string for detached HEAD (see also GitLab tips) |
git.tag | HEAD tag name; empty string if no tags defined; multiple tags separated with ; |
git.nearestTag | nearest tag name; empty string if no tags found; multiple tags (belonging to the same commit) are separated with ; Only the "counted" commits are looked for tags, see countCommitsSince... parameter; see also commitsCountSinceNearestTag property |
git.parent | SHA-1 of the parent commit (HEAD^ ); multiple parents separated with ; |
git.shortParent | SHA-1 of the parent commit (HEAD^ ) (abbreviated, see shortRevisionLength); multiple parents separated with ; |
git.commitsCount | commits count; -1 for a Git shallow clone; see countCommitsSince... |
git.commitsCountSinceNearestTag | commits count since nearestTag; empty string if the nearest tag is not found; the counting is exclusive (i.e. commit with this tag is not counted, to match the logic of describe) |
git.authorDate | authored date of HEAD commit; see gitDateFormat, dateFormatTimeZone |
git.commitDate | committed date of HEAD commit; see gitDateFormat, dateFormatTimeZone |
git.describe | result of JGit describe command (long format, all tags will be considered: annotated and lightweight (not annotated)); abbreviated commit hash if no tags found (see setAlways(true)) |
git.buildDateMillis | start time of plugin execution in milliseconds, as returned by System.currentTimeMillis() |
git.buildDate | start time of plugin execution, created from buildDateMillis and formatted according to buildDateFormat, dateFormatTimeZone |
git.buildNumber | composed from other properties according to buildNumberFormat parameter |
The default BuildNumber can be easily redefined using extracted properties (see buildNumberFormat).
All properties, including BuildNumber, are available in Maven, Ant, or Gradle build for the entire application.
Note that you can also redefine the default namespace git
using namespace parameter.
You can see the extracted properties, the execution time, and other info in the build log. Set the verbose parameter to true
to achieve that.
Extracted properties can be accessed in the same way in all build tools: as git.buildNumber
or ${git.buildNumber}
.
See examples in sections for Maven, Ant, Gradle.
We follow a zero configuration approach. Therefore all parameters are optional. But just in the case you would like to tweak something, there is a lot of possibilities to do that:
Working with parameters is very similar in all build tools. See examples in sections for Maven, Ant, Gradle.
Execution time primarily depends on the complexity of Git repo (especially on the number of tags, followed by the number of commits)
and whether you use a custom JS buildNumberFormat or not. Without custom buildNumberFormat
, you should expect execution time of 0.5 - 1.5 s.
With custom buildNumberFormat
add ca. 0.5 s.
The plugin binds to the validate
phase (the first Maven life cycle phase) per default, so that the extracted properties are available in all other phases.
Typical usage with writing extracted properties to the MANIFEST.MF file:
<build>
<plugins>
<plugin>
<!-- https://github.com/elab/jgit-buildnumber -->
<groupId>com.labun.buildnumber</groupId>
<artifactId>jgit-buildnumber-maven-plugin</artifactId>
<version>2.7.0</version>
<executions>
<execution>
<id>jgit-buildnumber</id>
<goals>
<goal>extract-buildnumber</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Version>${git.buildNumber}</Version>
<Build-Time>${git.buildDate}</Build-Time>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
You can write the extracted properties into arbitrary files (.properties, .java, ...) using Maven resource filtering.
For example, to create git.properties
file:
- Create file
/src/main/resources/git.properties
with the required properties:
buildNumber = ${git.buildNumber}
buildDate = ${git.buildDate}
...
Note: In Spring Boot projects, use this notation to access Maven properties: @git.buildNumber@
.
- Add resource filtering configuration to the
<build>
section of yourpom.xml
:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>git.properties</include>
</includes>
</resource>
</resources>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!-- https://github.com/elab/jgit-buildnumber -->
<groupId>com.labun.buildnumber</groupId>
<artifactId>jgit-buildnumber-maven-plugin</artifactId>
<version>2.7.0</version>
<executions>
<execution>
<id>git-buildnumber</id>
<goals>
<goal>extract-buildnumber</goal>
</goals>
<configuration>
<countCommitsSinceInclusive>v18-start</countCommitsSinceInclusive>
<dirtyValue>DEV</dirtyValue>
<buildNumberFormat>
branch + "." + commitsCount + "/" + commitDate + "/" + shortRevision + (dirty.length > 0 ? "-" + dirty : "");
</buildNumberFormat>
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
If the plugin is defined in parent module of a multi-module project, it will access the Git repo only once. (If you want to change that, see runOnlyAtExecutionRoot.) The properties extracted in parent module are propagated to all child modules. This only applies to normal Maven builds though, not for Eclipse m2e incremental builds, since Eclipse / OSGI has a flat workspace and doesn't support nested Maven modules.
The plugin contains lifecycle-mapping-metadata for Eclipse m2e, and will be executed in m2e incremental builds (yet not on configuration). This is particularly important for local deployments to a JEE server from within Eclipse, if you want to see the proper build number in your web application. (Local deployment somehow depends on m2e incremental build).
If you observe performance problems, "Run on incremental" can be disabled by adding the following to Eclipse m2e workspace
lifecycle-mapping-metadata.xml
(Eclipse > Window > Preferences > Maven > Lifecycle Mappings):
<?xml version="1.0" encoding="UTF-8"?>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<!-- https://github.com/elab/jgit-buildnumber -->
<groupId>com.labun.buildnumber</groupId>
<artifactId>jgit-buildnumber-maven-plugin</artifactId>
<versionRange>[0.0,)</versionRange>
<goals>
<goal>extract-buildnumber</goal>
</goals>
</pluginExecutionFilter>
<action>
<!-- disables plugin for m2e incremental builds -->
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
Restart Eclipse thereafter ("apply" in Preferences is not enough).
If for some reason you'd like to change the JGit version used (e.g. a bug was encountered in a specific version), it is possible.
Since the dependency on JGit is transitive (via jgit-buildnumber-common
artifact), the JGit version can be replaced via Maven exclusion mechanisms.
(If it was a direct dependency, that would likely not be possible.)
Example: To change the JGit version to 5.11.1.202105131744-r
(just an example, there is no particular reason for it), add the following <dependencies>
section to the jgit-buildnumber-maven-plugin
:
<plugin>
<!-- https://github.com/elab/jgit-buildnumber -->
<groupId>com.labun.buildnumber</groupId>
<artifactId>jgit-buildnumber-maven-plugin</artifactId>
<version>2.7.0</version>
<dependencies>
<dependency>
<groupId>com.labun.buildnumber</groupId>
<artifactId>jgit-buildnumber-common</artifactId>
<version>2.7.0</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>5.11.1.202105131744-r</version>
</dependency>
</dependencies>
<executions>
...
</executions>
</plugin>
As you see, the existing dependency was excluded from the jgit-buildnumber-common
artifact, and the new dependency was defined on the jgit-buildnumber-maven-plugin
.
Taking advantage of the Maven dependency resolution logic, you can do the same even more concise. Just add the other JGit version as a direct dependency to the jgit-buildnumber-maven-plugin
.
That other version, as the "nearest definition", will take precedence over the version defined in the jgit-buildnumber-common
.
The whole <dependencies>
section of the jgit-buildnumber-maven-plugin
would look as follows:
...
<dependencies>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>5.11.1.202105131744-r</version>
</dependency>
</dependencies>
...
You can control the versions used by starting Maven build with the --debug
option, and then looking in the log for "org.eclipse.jgit" version.
Usage is very similar to Maven. As all parameters are optional you don't have to specify any. Excerpt from build.xml
:
<target name="jgit-buildnumber">
<taskdef name="extract-buildnumber" classname="com.labun.buildnumber.JGitBuildNumberAntTask" classpathref="dependencies" />
<extract-buildnumber />
</target>
See complete build.xml
example with task parameters.
Another example shows how to extract two different buildNumbers for two repositories (e.g. "application" and "documentation") in one build file.
Usage is very similar to Maven and Ant. Essentially, you only need to specify the dependency on jgit-buildnumber-gradle-plugin
.
Complete working example of build.gradle
:
buildscript {
repositories { mavenLocal(); mavenCentral() }
dependencies { classpath 'com.labun.buildnumber:jgit-buildnumber-gradle-plugin:2.7.0' }
}
import com.labun.buildnumber.JGitBuildNumberGradleTask
task 'extract-buildnumber' (type: JGitBuildNumberGradleTask)
See extended build.gradle
example with task parameters.
The only difference in setting task parameters with Gradle
(as compared to Maven
and Ant) is that Gradle doesn't implicitly convert strings to other types.
Therefore you have to do it explicitly. "JGit Build Number" has only one such parameter: repositoryDirectory
of type java.io.File
.
If you need to specify this parameter, simply call an appropriate constructor:
task 'extract-buildnumber' (type: JGitBuildNumberGradleTask) {
repositoryDirectory = new File('<absolute path>') // or: repositoryDirectory = file('<relative path>')
...
}
Normally, the Git repo is checked out using a branch name. However, it also can be checked out using the SHA-1 of a specific commit, which is how the standard GitLab runner behaves, even on the branch builds. In this case, the HEAD is "detached" and the branch name is not available (this plugin returns an empty string for the branch
property). Moreover, GitLab performs some specific art of repo cloning, where no local branch references are present in the Git metadata (refs/heads
). So that "checking out" the CI_COMMIT_BRANCH is creating a new branch instead of checking out an existing one.
What can we do in such a case if the branch name is still required for the Build Number (assuming the build is triggered by a branch, otherwise the value CI_COMMIT_BRANCH will not be available):
- Disable the default cloning and SHA-1 based checkout by setting the GIT_STRATEGY variable to
none
and perform a normal repo cloning and branch checkout using the CI_COMMIT_BRANCH value.
OR
- Use the CI_COMMIT_BRANCH value directly in the
buildNumberFormat
JavaScript, utilizing the feature of the "JGit Build Number", which grants readonly access to environment variables from the JS viaenv
map.
Example:
<buildNumberFormat>
function isEmpty(v) { return v == null || v === ''; }
if (isEmpty(branch) && !isEmpty(env.CI_COMMIT_BRANCH)) {
print('\"branch\" value for BuildNumber will be taken from env.CI_COMMIT_BRANCH: ' + env.CI_COMMIT_BRANCH);
branch = env.CI_COMMIT_BRANCH;
}
branch + '.' + commitsCount + '-' + commitDate + '-' + shortRevision + (isEmpty(dirty) ? '' : '-' + dirty);
</buildNumberFormat>
By the way, you can see in the example how to print diagnostic messages directly from the JavaScript.
This section is intended for developers of "JGit Build Number". It can be ignored if you only use the plugins in your projects.
The project uses Lombok, a great tool which helps to free the source code from ugly boilerplate like getters and setters.
Lombok is supported by all major IDEs and build tools. For Eclipse, simply add this to eclipse.ini
:
-vmargs
-javaagent:<path-to-lombok-jar>
For other IDEs and tools, see projectlombok.org/setup.
This project is released under the Apache License 2.0.
-
Gradle 7+ compatibility (thanks to Tony Galuhn)
-
dependency updates:
- nashorn-core 15.3 -> 15.4,
- maven-core 3.8.4 -> 3.8.5,
- maven-plugin-api 3.8.4 -> 3.8.5,
- maven-plugin-annotations 3.6.2 -> 3.6.4,
- lombok 1.18.22 -> 1.18.24,
- logback 1.2.7 -> 1.2.11,
- maven-jar-plugin 3.2.0 -> 3.2.2 (in Maven example),
- slf4j-api 1.7.32 -> 1.7.36 (in Ant example)
-
add readonly 'env' map (environment variables) to JS context
-
README: add GitLab tips section (working around detached HEAD and absence of local branch references by utilizing the 'env' map in JS)
-
dependency updates:
- nashorn-core 15.2 -> 15.3 (this update adds compatibility with Java 17, see https://bugs.openjdk.java.net/browse/JDK-8269602 and openjdk/nashorn#16 for details),
- jgit 5.12.0.202106070339-r -> 5.13.0.202109080827-r (note: jgit v6 requires Java 11, so we will stick with v5 for now, to ensure compatibility with Java 8),
- lombok 1.18.20 -> 1.18.22,
- junit 5.7.2 -> 5.8.2,
- logback 1.2.3 -> 1.2.7,
- maven-plugin-plugin 3.6.0 -> 3.6.2,
- maven-site-plugin 3.7.1 -> 3.9.1,
- maven-project-info-reports-plugin 3.0.0 -> 3.1.2,
- maven-source-plugin 3.1.0 -> 3.2.1,
- maven-javadoc-plugin 3.1.0 -> 3.3.1,
- maven-scm-api 1.11.2 -> 1.12.0,
- maven-scm-provider-jgit 1.11.2 -> 1.12.0,
- maven-plugin-api 3.8.1 -> 3.8.4,
- maven-core 3.8.1 -> 3.8.4,
- maven-plugin-annotations 3.6.1 -> 3.6.2,
- ant 1.10.10 -> 1.10.12,
- maven-jar-plugin 3.1.1 -> 3.2.0 (in Maven example),
- slf4j-api 1.7.2 -> 1.7.32 (in Ant example)
-
Maven pom.xml files: align developers/contributors sections with Maven guidelines (i.e. leave under "developers" sections only those developers who are immediately responsible for the code and should be contacted about the project, as suggested in https://maven.apache.org/pom.html#Developers; move all others to "contributors")
-
README: add some Maven Central download statistics
-
README: redesign "Current version" table
- dependency updates: jgit 5.12.0.202106070339-r, lombok 1.18.20, maven-plugin-api 3.8.1, maven-core 3.8.1, maven-plugin-annotations 3.6.1, ant 1.10.10, groovy 2.5.14
- if git status is dirty, log which changes caused that (verbose mode only)
- use standalone version of Nashorn JavaScript engine if running on Java 11+
(prevents deprecation warning on Java 11-14; enables working with Java 15+ where Nashorn is not a part of JDK anymore) - initialize JavaScript engine in parallel with reading Git repo (reduces overall execution time by ca. 0.5 s)
- log number of tags (verbose mode only)
- print some error resolution hints if countCommitsSince... in conjunction with countCommitsInPath was used and no such commit (from the
countCommitsSince
parameter) is found - homepage URL added to poms: https://github.com/elab/jgit-buildnumber
- new property:
git.buildDateMillis
- new property:
git.commitsCountSinceNearestTag
- README revised (slightly restructured, notes about usage with Spring Boot added, section "Changing JGit version" added
- new property:
git.nearestTag
git.describe
: assuring not null result to prevent plugin fail (thanks to RobertPaasche)git.describe
: also consider lightweight (not annotated) tags