Verifying Branches Using The Git Plugin
Verifying Branches Using The Git Plugin
8. November 2016 Keine Kommentare zu Verifying Branches Using The Git PluginjQAssistant provides a public API for creating scanner plugins to enable its users extending the tool for their own needs. A very valuable contribution is the Git plugin by Jens Nerche (Kontext E GmbH). It allows very interesting insight into the history of a project but also opens up possibilities to ensure correct tagging and branching. This post describes a common use case which came up some days ago in a customer project.
A Simple Release Strategy
The following (simplified) branching strategy has been chosen for working with the Git repository:
– All release versions use the format „major.minor.patch“ (e.g. 1.0.0)
– New features are developed on the master branch using a snapshot version (1.0.0-SNAPSHOT)
– For creating a major or minor release a new release branch (1.0) is created and a tag (1.0.0) is set
– The current development continues on the master branch with an increased major or minor version (e.g. 1.1.0-SNAPSHOT)
– The development version on the release branch is increased for the patch number only (1.0.1-SNAPSHOT)
If a problem is identified for the release:
– It is fixed on the release branch
– A bug fix release is created from there
– The tag is set (1.0.1)
– The development version is increased (1.0.2-SNAPSHOT)
Here comes the crucial part: It must be ensured that the problem is not only fixed within the bug fix release but for all future releases. Therefore the changes need to be merged back to the main development branch which in this case is the master. The following graph has been created using jQAssistant and the Git plugin and visualizes a sample repository following this strategy:
The repository structure mainly consists of commits represented by nodes with the label „Commit“. A commit may reference parents using HAS_PARENT relationships. On the very left side the initial commit can be seen, it is followed by a release commit. According to the defined strategy it is referenced by a tag (1.0.0) and it is the parent of two commits from two branches: the master branch (top) and the 1.0 release branch (bottom). The latter contains a release commit which is tagged with 1.0.1. This one has been merged back to the master branch, therefore it is also a parent for a merge commit there.
But there is also another release commit on the release branch with the tag 1.0.2. It can be seen that this one has not been merged back to the master, so future releases (like 1.1.0) will not contain required changes.
Putting The Git Plugin Into Action
Using jQAssistant and the Git plugin it is possible to detect this situation and break builds if necessary. This will be demonstrated using the jQAssistant Maven plugin but can also be implemented in a similar way using the command line interface (CLI) from the downloadable distribution (e.g. in Gradle builds). First of all the Git plugin and required configuration needs to be added to the pom.xml of the Maven project:
<?xml version="1.0" encoding="UTF-8"?> <project ...> ... <properties> <jqassistant.version>1.1.3</jqassistant.version> </properties> <build> <plugins> ... <!-- jQAssistant --> <plugin> <groupId>com.buschmais.jqassistant.scm</groupId> <artifactId>jqassistant-maven-plugin</artifactId> <version>${jqassistant.version}</version> <executions> <execution> <goals> <goal>scan</goal> <goal>analyze</goal> </goals> <configuration> <failOnViolations>true</failOnViolations> <scanIncludes> <scanInclude> <path>${project.basedir}/.git</path> </scanInclude> </scanIncludes> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>de.kontext-e.jqassistant.plugin</groupId> <artifactId>jqassistant.plugin.git</artifactId> <version>1.1.2</version> </dependency> </dependencies> </plugin> </plugins> </build> ... </project>
The shown setup declares the Git plugin as dependency for the jQAssistant Maven plugin and will therefore be activated automatically. Furthermore the „scanIncludes“ section points to the local Git repository which will be detected and scanned by the Git plugin.
Now it’s time to add some rules. Therefore a folder „jqassistant“ is created in the same folder as the pom.xml and two files are added to it:
default.adoc
= Coding Rules [[default]] [role=group,includesGroups="git:Default"] == Default Rules The following rule groups are executed by default: - <<git:Default>> include::git.adoc[]
The file default.adoc defines a group „default“ which is executed by default by the jQAssistant „analyze“ goal. It includes another group „git:Default“ which is defined in the second file. It defines two rules, a concept „git:ReleaseTag“ and a constraint „git:ReleasesMustBeMergedToMaster“:
git.adoc
[[git:Default]] [role=group,includesConstraints="git:ReleasesMustBeMergedToMaster"] == Git === Concepts [[git:ReleaseTag]] [source,cypher,role=concept] .Labels all tags matching to the pattern 'major.minor.patch' as "Release" and sets properties for majorVersion, minorVersion and patchVersion. ---- MATCH (tag:Git:Tag) WHERE tag.label =~ "\\d?\\.\\d?\\.\\d?" WITH tag, split(tag.label, ".") as version SET tag:Release, tag.majorVersion = toInt(version[0]), tag.minorVersion = toInt(version[1]), tag.patchVersion = toInt(version[2]) RETURN tag.label as Tag, tag.majorVersion as MajorVersion,tag.minorVersion as MinorVersion,tag.patchVersion as PatchVersion ORDER BY MajorVersion desc,MinorVersion desc,PatchVersion desc ---- === Constraints [[git:ReleasesMustBeMergedToMaster]] [source,cypher,role=constraint,requiresConcepts="git:ReleaseTag"] .All release tags must to be merged to the master branch. ---- MATCH (master:Git:Branch)-[:HAS_HEAD]->(head:Commit) WHERE master.name ends with "/master" WITH master,head MATCH (tag:Tag:Release)-[:ON_COMMIT]->(tagCommit:Git:Commit) OPTIONAL MATCH path=shortestPath((head)-[:HAS_PARENT*]->(tagCommit)) WITH master,tag,path WHERE path is null RETURN master.name as MasterBranch, collect(tag.label) as MissingReleases ----
The concept „git:ReleaseTag“ matches all tags that follow the defined version pattern „major.minor.patch“ by using a regular expression. The version information is parsed and stored as properties on the tag node which also receives a label „Release“.
The constraint „git:ReleasesMustBeMergedToMaster“ matches the current heads of the available master branches (e.g. „heads/master“ and „remotes/origin/master“) and tries to find a path from them to all release tags which have been identified by the concept. If no such path exists the release tag has not been integrated into the master (see graph above) and the constraint will return a result and therefore fail. For the described situation the Maven build will return the following information on the console and also break as „failOnViolations“ is set to true:
[INFO] --- jqassistant-maven-plugin:1.1.3:analyze (default) @ git --- [INFO] Executing analysis for 'git'. [INFO] Reading rules from directory C:\Development\projects\blog\git\jqassistant [INFO] Executing group 'default' [INFO] Executing group 'git:Default' [INFO] Applying concept 'git:ReleaseTag' with severity: 'MINOR'. [INFO] Validating constraint 'git:ReleasesMustBeMergedToMaster' with severity: 'INFO'. [ERROR] --[ Constraint Violation ]----------------------------------------- [ERROR] Constraint: git:ReleasesMustBeMergedToMaster [ERROR] Severity: INFO [ERROR] All release tags must to be merged to the master branch. [ERROR] MasterBranch=heads/master, MissingReleases=[1.0.2] [ERROR] -------------------------------------------------------------------
Leave a comment