package com.e_gineering.maven.gitflowhelper; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * If there is an ${env.GIT_BRANCH} property, assert that the current ${project.version} is semantically correct for the * git branch. Also, make sure there are no SNAPSHOT (plugin) dependencies if enforceNonSnapshots = true. */ @Mojo(requiresDependencyCollection = ResolutionScope.TEST, name = "enforce-versions", defaultPhase = LifecyclePhase.VALIDATE) public class EnforceVersionsMojo extends AbstractGitflowBranchMojo { @Parameter(defaultValue = "true", property = "enforceNonSnapshots", required = true) private boolean enforceNonSnapshots; @Parameter(defaultValue = "false", property = "allowGitflowPluginSnapshot", required = true) private boolean allowGitflowPluginSnapshot; @Override protected void execute(final GitBranchInfo branchInfo) throws MojoExecutionException, MojoFailureException { if (branchInfo.isVersioned()) { getLog().debug("Versioned Branch: " + branchInfo); Matcher gitMatcher = Pattern.compile(branchInfo.getPattern()).matcher(branchInfo.getName()); // We're in a versioned branch, we expect a non-SNAPSHOT version in the POM. if (gitMatcher.matches()) { // Always assert that pom versions match our expectations. if (hasSnapshotInModel(project)) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] is defined as a release branch. The maven project or one of its parents is currently a snapshot version."); } // Non-master version branches require a pom version match of some kind to the branch subgroups. if (gitMatcher.groupCount() > 0 && gitMatcher.group(gitMatcher.groupCount()) != null) { checkReleaseTypeBranchVersion(branchInfo, gitMatcher); } // Optionally (default true) reinforce that no dependencies may be snapshots. if (enforceNonSnapshots) { Set<String> snapshotDeps = getSnapshotDeps(); if (!snapshotDeps.isEmpty()) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] is defined as a release branch. The maven project has the following SNAPSHOT dependencies: " + snapshotDeps.toString()); } Set<String> snapshotPluginDeps = getSnapshotPluginDeps(); if (!snapshotPluginDeps.isEmpty()) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] is defined as a release branch. The maven project has the following SNAPSHOT plugin dependencies: " + snapshotPluginDeps.toString()); } } } } else if (branchInfo.isSnapshot() && !ArtifactUtils.isSnapshot(project.getVersion())) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] is detected as a SNAPSHOT-type branch, and expects a maven project version ending with -SNAPSHOT. The maven project version found was: [" + project.getVersion() + "]"); } } private void checkReleaseTypeBranchVersion(final GitBranchInfo branchInfo, final Matcher gitMatcher) throws MojoFailureException { // RELEASE, HOTFIX and SUPPORT branches require a match of the maven project version to the subgroup. // Depending on the value of the 'releaseBranchMatchType' param, it's either 'equals' or 'startsWith'. if ("equals".equals(releaseBranchMatchType)) { // HOTFIX and RELEASE branches require an exact match to the last subgroup. if ((GitBranchType.RELEASE.equals(branchInfo.getType()) || GitBranchType.HOTFIX.equals(branchInfo.getType())) && !gitMatcher.group(gitMatcher.groupCount()).trim().equals(project.getVersion().trim())) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] expected the maven project version to be: [" + gitMatcher.group(gitMatcher.groupCount()).trim() + "], but the maven project version is: [" + project.getVersion() + "]"); } // SUPPORT branches require a 'starts with' match of the maven project version to the subgroup. // ex: /origin/support/3.1 must have a maven version that starts with "3.1", ala: "3.1.2" if (GitBranchType.SUPPORT.equals(branchInfo.getType()) && !project.getVersion().startsWith(gitMatcher.group(gitMatcher.groupCount()).trim())) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] expected the maven project version to start with: [" + gitMatcher.group(gitMatcher.groupCount()).trim() + "], but the maven project version is: [" + project.getVersion() + "]"); } } else { // "startsWith" // ex: /origin/release/3.1 must have a maven version that starts with "3.1", ala: "3.1.2" if (gitMatcher.groupCount() > 0 && !GitBranchType.MASTER.equals(branchInfo.getType())) { String releaseBranchVersion = gitMatcher.group(gitMatcher.groupCount()).trim(); // Type check always returns true, as it's in VERSIONED_TYPES and not MASTER, but it's handy documentation if ((GitBranchType.RELEASE.equals(branchInfo.getType()) || GitBranchType.HOTFIX.equals(branchInfo.getType()) || GitBranchType.SUPPORT.equals(branchInfo.getType())) && !project.getVersion().startsWith(releaseBranchVersion)) { throw new MojoFailureException("The current git branch: [" + branchInfo.getName() + "] expected the maven project version to start with: [" + releaseBranchVersion + "], but the maven project version is: [" + project.getVersion() + "]"); } } } } private boolean hasSnapshotInModel(final MavenProject project) { MavenProject parent = project.getParent(); boolean projectIsSnapshot = ArtifactUtils.isSnapshot(project.getVersion()); boolean parentIsSnapshot = parent != null && hasSnapshotInModel(parent); return projectIsSnapshot || parentIsSnapshot; } private Set<String> getSnapshotDeps() { Set<String> snapshotDeps = new HashSet<>(); for (Artifact dep : project.getArtifacts()) { if (ArtifactUtils.isSnapshot(dep.getVersion())) { getLog().debug("SNAPSHOT dependency found: " + dep.toString()); snapshotDeps.add(dep.toString()); } } return snapshotDeps; } private Set<String> getSnapshotPluginDeps() { Set<String> snapshotPluginDeps = new HashSet<>(); for (Artifact plugin : project.getPluginArtifacts()) { if (plugin.isSnapshot()) { if (allowGitflowPluginSnapshot && plugin.getGroupId().equals("com.e-gineering") && plugin.getArtifactId().equals("gitflow-helper-maven-plugin")) { getLog().warn("SNAPSHOT com.e-gineering:gitflow-helper-maven-plugin detected. Allowing for this build."); continue; } getLog().debug("SNAPSHOT plugin dependency found: " + plugin.toString()); snapshotPluginDeps.add(plugin.toString()); } } return snapshotPluginDeps; } }