/* * Copyright 2014-2020 Aleksandr Mashchenko. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.amashchenko.maven.plugin.gitflow; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuilder; import org.apache.maven.project.ProjectBuildingResult; import org.apache.maven.settings.Settings; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; /** * Abstract git flow mojo. * */ public abstract class AbstractGitFlowMojo extends AbstractMojo { /** A full name of the versions-maven-plugin set goal. */ private static final String VERSIONS_MAVEN_PLUGIN_SET_GOAL = "org.codehaus.mojo:versions-maven-plugin:set"; /** A full name of the versions-maven-plugin set-property goal. */ private static final String VERSIONS_MAVEN_PLUGIN_SET_PROPERTY_GOAL = "org.codehaus.mojo:versions-maven-plugin:set-property"; /** Name of the tycho-versions-plugin set-version goal. */ private static final String TYCHO_VERSIONS_PLUGIN_SET_GOAL = "org.eclipse.tycho:tycho-versions-plugin:set-version"; /** System line separator. */ protected static final String LS = System.getProperty("line.separator"); /** Success exit code. */ private static final int SUCCESS_EXIT_CODE = 0; /** Pattern of disallowed characters in Maven commands. */ private static final Pattern MAVEN_DISALLOWED_PATTERN = Pattern .compile("[&|;]"); /** Command line for Git executable. */ private final Commandline cmdGit = new Commandline(); /** Command line for Maven executable. */ private final Commandline cmdMvn = new Commandline(); /** Git flow configuration. */ @Parameter(defaultValue = "${gitFlowConfig}") protected GitFlowConfig gitFlowConfig; /** * Git commit messages. * * @since 1.2.1 */ @Parameter(defaultValue = "${commitMessages}") protected CommitMessages commitMessages; /** * Whether this is Tycho build. * * @since 1.1.0 */ @Parameter(defaultValue = "false") protected boolean tychoBuild; /** * Whether to call Maven install goal during the mojo execution. * * @since 1.0.5 */ @Parameter(property = "installProject", defaultValue = "false") protected boolean installProject = false; /** * Whether to fetch remote branch and compare it with the local one. * * @since 1.3.0 */ @Parameter(property = "fetchRemote", defaultValue = "true") protected boolean fetchRemote; /** * Whether to print commands output into the console. * * @since 1.0.7 */ @Parameter(property = "verbose", defaultValue = "false") private boolean verbose = false; /** * Command line arguments to pass to the underlying Maven commands. * * @since 1.8.0 */ @Parameter(property = "argLine") private String argLine; /** * Whether to make a GPG-signed commit. * * @since 1.9.0 */ @Parameter(property = "gpgSignCommit", defaultValue = "false") private boolean gpgSignCommit = false; /** * Whether to set -DgroupId='*' -DartifactId='*' when calling * versions-maven-plugin. * * @since 1.10.0 */ @Parameter(property = "versionsForceUpdate", defaultValue = "false") private boolean versionsForceUpdate = false; /** * Property to set version to. * * @since 1.13.0 */ @Parameter(property = "versionProperty") private String versionProperty; /** * Whether to skip updating version. Useful with {@link #versionProperty} to be * able to update <code>revision</code> property without modifying version tag. * * @since 1.13.0 */ @Parameter(property = "skipUpdateVersion") private boolean skipUpdateVersion = false; /** * Prefix that is applied to commit messages. * * @since 1.14.0 */ @Parameter(property = "commitMessagePrefix") private String commitMessagePrefix; /** * The path to the Maven executable. Defaults to "mvn". */ @Parameter(property = "mvnExecutable") private String mvnExecutable; /** * The path to the Git executable. Defaults to "git". */ @Parameter(property = "gitExecutable") private String gitExecutable; /** Maven session. */ @Parameter(defaultValue = "${session}", readonly = true) protected MavenSession mavenSession; @Component protected ProjectBuilder projectBuilder; /** Default prompter. */ @Component protected Prompter prompter; /** Maven settings. */ @Parameter(defaultValue = "${settings}", readonly = true) protected Settings settings; /** * Initializes command line executables. * */ private void initExecutables() { if (StringUtils.isBlank(cmdMvn.getExecutable())) { if (StringUtils.isBlank(mvnExecutable)) { mvnExecutable = "mvn"; } cmdMvn.setExecutable(mvnExecutable); } if (StringUtils.isBlank(cmdGit.getExecutable())) { if (StringUtils.isBlank(gitExecutable)) { gitExecutable = "git"; } cmdGit.setExecutable(gitExecutable); } } /** * Validates plugin configuration. Throws exception if configuration is not * valid. * * @param params * Configuration parameters to validate. * @throws MojoFailureException * If configuration is not valid. */ protected void validateConfiguration(String... params) throws MojoFailureException { if (StringUtils.isNotBlank(argLine) && MAVEN_DISALLOWED_PATTERN.matcher(argLine).find()) { throw new MojoFailureException( "The argLine doesn't match allowed pattern."); } if (params != null && params.length > 0) { for (String p : params) { if (StringUtils.isNotBlank(p) && MAVEN_DISALLOWED_PATTERN.matcher(p).find()) { throw new MojoFailureException("The '" + p + "' value doesn't match allowed pattern."); } } } } /** * Gets current project version from pom.xml file. * * @return Current project version. * @throws MojoFailureException */ protected String getCurrentProjectVersion() throws MojoFailureException { final MavenProject reloadedProject = reloadProject(mavenSession.getCurrentProject()); if (reloadedProject.getVersion() == null) { throw new MojoFailureException( "Cannot get current project version. This plugin should be executed from the parent project."); } return reloadedProject.getVersion(); } /** * Reloads project info from file * * @param project * @return * @throws MojoFailureException */ private MavenProject reloadProject(MavenProject project) throws MojoFailureException { try { ProjectBuildingResult result = projectBuilder.build(project.getFile(), mavenSession.getProjectBuildingRequest()); return result.getProject(); } catch (Exception e) { throw new MojoFailureException("Error re-loading project info", e); } } /** * Compares the production branch name with the development branch name. * * @return <code>true</code> if the production branch name is different from * the development branch name, <code>false</code> otherwise. */ protected boolean notSameProdDevName() { return !gitFlowConfig.getProductionBranch().equals( gitFlowConfig.getDevelopmentBranch()); } /** * Checks uncommitted changes. * * @throws MojoFailureException * @throws CommandLineException */ protected void checkUncommittedChanges() throws MojoFailureException, CommandLineException { getLog().info("Checking for uncommitted changes."); if (executeGitHasUncommitted()) { throw new MojoFailureException( "You have some uncommitted files. Commit or discard local changes in order to proceed."); } } protected void checkSnapshotDependencies() throws MojoFailureException { getLog().info("Checking for SNAPSHOT versions in dependencies."); List<String> snapshots = new ArrayList<String>(); List<String> builtArtifacts = new ArrayList<String>(); List<MavenProject> projects = mavenSession.getProjects(); for (MavenProject project : projects) { final MavenProject reloadedProject = reloadProject(project); builtArtifacts.add(reloadedProject.getGroupId() + ":" + reloadedProject.getArtifactId() + ":" + reloadedProject.getVersion()); List<Dependency> dependencies = reloadedProject.getDependencies(); for (Dependency d : dependencies) { String id = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion(); if (!builtArtifacts.contains(id) && ArtifactUtils.isSnapshot(d.getVersion())) { snapshots.add(reloadedProject + " -> " + d); } } } if (!snapshots.isEmpty()) { for (String s : snapshots) { getLog().warn(s); } throw new MojoFailureException( "There is some SNAPSHOT dependencies in the project, see warnings above. Change them or ignore with `allowSnapshots` property."); } } /** * Checks if branch name is acceptable. * * @param branchName * Branch name to check. * @return <code>true</code> when name is valid, <code>false</code> * otherwise. * @throws MojoFailureException * @throws CommandLineException */ protected boolean validBranchName(final String branchName) throws MojoFailureException, CommandLineException { CommandResult r = executeGitCommandExitCode("check-ref-format", "--allow-onelevel", branchName); return r.getExitCode() == SUCCESS_EXIT_CODE; } /** * Executes git commands to check for uncommitted changes. * * @return <code>true</code> when there are uncommitted changes, * <code>false</code> otherwise. * @throws CommandLineException * @throws MojoFailureException */ private boolean executeGitHasUncommitted() throws MojoFailureException, CommandLineException { boolean uncommited = false; // 1 if there were differences and 0 means no differences // git diff --no-ext-diff --ignore-submodules --quiet --exit-code final CommandResult diffCommandResult = executeGitCommandExitCode( "diff", "--no-ext-diff", "--ignore-submodules", "--quiet", "--exit-code"); String error = null; if (diffCommandResult.getExitCode() == SUCCESS_EXIT_CODE) { // git diff-index --cached --quiet --ignore-submodules HEAD -- final CommandResult diffIndexCommandResult = executeGitCommandExitCode( "diff-index", "--cached", "--quiet", "--ignore-submodules", "HEAD", "--"); if (diffIndexCommandResult.getExitCode() != SUCCESS_EXIT_CODE) { error = diffIndexCommandResult.getError(); uncommited = true; } } else { error = diffCommandResult.getError(); uncommited = true; } if (StringUtils.isNotBlank(error)) { throw new MojoFailureException(error); } return uncommited; } /** * Executes git config commands to set Git Flow configuration. * * @throws MojoFailureException * @throws CommandLineException */ protected void initGitFlowConfig() throws MojoFailureException, CommandLineException { gitSetConfig("gitflow.branch.master", gitFlowConfig.getProductionBranch()); gitSetConfig("gitflow.branch.develop", gitFlowConfig.getDevelopmentBranch()); gitSetConfig("gitflow.prefix.feature", gitFlowConfig.getFeatureBranchPrefix()); gitSetConfig("gitflow.prefix.release", gitFlowConfig.getReleaseBranchPrefix()); gitSetConfig("gitflow.prefix.hotfix", gitFlowConfig.getHotfixBranchPrefix()); gitSetConfig("gitflow.prefix.support", gitFlowConfig.getSupportBranchPrefix()); gitSetConfig("gitflow.prefix.versiontag", gitFlowConfig.getVersionTagPrefix()); gitSetConfig("gitflow.origin", gitFlowConfig.getOrigin()); } /** * Executes git config command. * * @param name * Option name. * @param value * Option value. * @throws MojoFailureException * @throws CommandLineException */ private void gitSetConfig(final String name, String value) throws MojoFailureException, CommandLineException { if (value == null || value.isEmpty()) { value = "\"\""; } // ignore error exit codes executeGitCommandExitCode("config", name, value); } /** * Executes git for-each-ref with <code>refname:short</code> format. * * @param branchName * Branch name to find. * @param firstMatch * Return first match. * @return Branch names which matches <code>refs/heads/{branchName}*</code>. * @throws MojoFailureException * @throws CommandLineException */ protected String gitFindBranches(final String branchName, final boolean firstMatch) throws MojoFailureException, CommandLineException { return gitFindBranches("refs/heads/", branchName, firstMatch); } /** * Executes git for-each-ref with <code>refname:short</code> format. * * @param refs * Refs to search. * @param branchName * Branch name to find. * @param firstMatch * Return first match. * @return Branch names which matches <code>{refs}{branchName}*</code>. * @throws MojoFailureException * @throws CommandLineException */ private String gitFindBranches(final String refs, final String branchName, final boolean firstMatch) throws MojoFailureException, CommandLineException { String wildcard = "*"; if (branchName.endsWith("/")) { wildcard = "**"; } String branches; if (firstMatch) { branches = executeGitCommandReturn("for-each-ref", "--count=1", "--format=\"%(refname:short)\"", refs + branchName + wildcard); } else { branches = executeGitCommandReturn("for-each-ref", "--format=\"%(refname:short)\"", refs + branchName + wildcard); } // on *nix systems return values from git for-each-ref are wrapped in // quotes // https://github.com/aleksandr-m/gitflow-maven-plugin/issues/3 branches = removeQuotes(branches); branches = StringUtils.strip(branches); return branches; } /** * Executes git for-each-ref to get all tags. * * @return Git tags. * @throws MojoFailureException * @throws CommandLineException */ protected String gitFindTags() throws MojoFailureException, CommandLineException { String tags = executeGitCommandReturn("for-each-ref", "--sort=*authordate", "--format=\"%(refname:short)\"", "refs/tags/"); // https://github.com/aleksandr-m/gitflow-maven-plugin/issues/3 tags = removeQuotes(tags); return tags; } /** * Executes git for-each-ref to get the last tag. * * @return Last tag. * @throws MojoFailureException * @throws CommandLineException */ protected String gitFindLastTag() throws MojoFailureException, CommandLineException { String tag = executeGitCommandReturn("for-each-ref", "--sort=-*authordate", "--count=1", "--format=\"%(refname:short)\"", "refs/tags/"); // https://github.com/aleksandr-m/gitflow-maven-plugin/issues/3 tag = removeQuotes(tag); tag = tag.replaceAll("\\r?\\n", ""); return tag; } /** * Removes double quotes from the string. * * @param str * String to remove quotes from. * @return String without quotes. */ private String removeQuotes(String str) { if (str != null && !str.isEmpty()) { str = str.replaceAll("\"", ""); } return str; } /** * Gets the current branch name. * * @return Current branch name. * @throws MojoFailureException * @throws CommandLineException */ protected String gitCurrentBranch() throws MojoFailureException, CommandLineException { String name = executeGitCommandReturn("symbolic-ref", "-q", "--short", "HEAD"); name = StringUtils.strip(name); return name; } /** * Checks if local branch with given name exists. * * @param branchName * Name of the branch to check. * @return <code>true</code> if local branch exists, <code>false</code> * otherwise. * @throws MojoFailureException * @throws CommandLineException */ protected boolean gitCheckBranchExists(final String branchName) throws MojoFailureException, CommandLineException { CommandResult commandResult = executeGitCommandExitCode("show-ref", "--verify", "--quiet", "refs/heads/" + branchName); return commandResult.getExitCode() == SUCCESS_EXIT_CODE; } /** * Checks if local tag with given name exists. * * @param tagName * Name of the tag to check. * @return <code>true</code> if local tag exists, <code>false</code> otherwise. * @throws MojoFailureException * @throws CommandLineException */ protected boolean gitCheckTagExists(final String tagName) throws MojoFailureException, CommandLineException { CommandResult commandResult = executeGitCommandExitCode("show-ref", "--verify", "--quiet", "refs/tags/" + tagName); return commandResult.getExitCode() == SUCCESS_EXIT_CODE; } /** * Executes git checkout. * * @param branchName * Branch name to checkout. * @throws MojoFailureException * @throws CommandLineException */ protected void gitCheckout(final String branchName) throws MojoFailureException, CommandLineException { getLog().info("Checking out '" + branchName + "' branch."); executeGitCommand("checkout", branchName); } /** * Executes git checkout -b. * * @param newBranchName * Create branch with this name. * @param fromBranchName * Create branch from this branch. * @throws MojoFailureException * @throws CommandLineException */ protected void gitCreateAndCheckout(final String newBranchName, final String fromBranchName) throws MojoFailureException, CommandLineException { getLog().info( "Creating a new branch '" + newBranchName + "' from '" + fromBranchName + "' and checking it out."); executeGitCommand("checkout", "-b", newBranchName, fromBranchName); } /** * Executes git branch. * * @param newBranchName * Create branch with this name. * @param fromBranchName * Create branch from this branch. * @throws MojoFailureException * @throws CommandLineException */ protected void gitCreateBranch(final String newBranchName, final String fromBranchName) throws MojoFailureException, CommandLineException { getLog().info( "Creating a new branch '" + newBranchName + "' from '" + fromBranchName + "'."); executeGitCommand("branch", newBranchName, fromBranchName); } /** * Replaces properties in message. * * @param message * @param map * Key is a string to replace wrapped in <code>@{...}</code>. Value * is a string to replace with. * @return */ private String replaceProperties(String message, Map<String, String> map) { if (map != null) { for (Entry<String, String> entr : map.entrySet()) { message = StringUtils.replace(message, "@{" + entr.getKey() + "}", entr.getValue()); } } return message; } /** * Executes git commit -a -m. * * @param message * Commit message. * @throws MojoFailureException * @throws CommandLineException */ protected void gitCommit(final String message) throws MojoFailureException, CommandLineException { gitCommit(message, null); } /** * Executes git commit -a -m, replacing <code>@{map.key}</code> with * <code>map.value</code>. * * @param message * Commit message. * @param messageProperties * Properties to replace in message. * @throws MojoFailureException * @throws CommandLineException */ protected void gitCommit(String message, Map<String, String> messageProperties) throws MojoFailureException, CommandLineException { if (StringUtils.isNotBlank(commitMessagePrefix)) { message = commitMessagePrefix + message; } message = replaceProperties(message, messageProperties); if (gpgSignCommit) { getLog().info("Committing changes. GPG-signed."); executeGitCommand("commit", "-a", "-S", "-m", message); } else { getLog().info("Committing changes."); executeGitCommand("commit", "-a", "-m", message); } } /** * Executes git rebase or git merge --ff-only or git merge --no-ff or git merge. * * @param branchName * Branch name to merge. * @param rebase * Do rebase. * @param noff * Merge with --no-ff. * @param ffonly * Merge with --ff-only. * @param message * Merge commit message. * @param messageProperties * Properties to replace in message. * @throws MojoFailureException * @throws CommandLineException */ protected void gitMerge(final String branchName, boolean rebase, boolean noff, boolean ffonly, String message, Map<String, String> messageProperties) throws MojoFailureException, CommandLineException { String sign = ""; if (gpgSignCommit) { sign = "-S"; } String msgParam = ""; String msg = ""; if (StringUtils.isNotBlank(message)) { if (StringUtils.isNotBlank(commitMessagePrefix)) { message = commitMessagePrefix + message; } msgParam = "-m"; msg = replaceProperties(message, messageProperties); } if (rebase) { getLog().info("Rebasing '" + branchName + "' branch."); executeGitCommand("rebase", sign, branchName); } else if (ffonly) { getLog().info("Merging (--ff-only) '" + branchName + "' branch."); executeGitCommand("merge", "--ff-only", sign, branchName); } else if (noff) { getLog().info("Merging (--no-ff) '" + branchName + "' branch."); executeGitCommand("merge", "--no-ff", sign, branchName, msgParam, msg); } else { getLog().info("Merging '" + branchName + "' branch."); executeGitCommand("merge", sign, branchName, msgParam, msg); } } /** * Executes git merge --no-ff. * * @param branchName * Branch name to merge. * @param message * Merge commit message. * @param messageProperties * Properties to replace in message. * @throws MojoFailureException * @throws CommandLineException */ protected void gitMergeNoff(final String branchName, final String message, final Map<String, String> messageProperties) throws MojoFailureException, CommandLineException { gitMerge(branchName, false, true, false, message, messageProperties); } /** * Executes git merge --squash. * * @param branchName * Branch name to merge. * @throws MojoFailureException * @throws CommandLineException */ protected void gitMergeSquash(final String branchName) throws MojoFailureException, CommandLineException { getLog().info("Squashing '" + branchName + "' branch."); executeGitCommand("merge", "--squash", branchName); } /** * Executes git tag -a [-s] -m. * * @param tagName * Name of the tag. * @param message * Tag message. * @param gpgSignTag * Make a GPG-signed tag. * @param messageProperties * Properties to replace in message. * @throws MojoFailureException * @throws CommandLineException */ protected void gitTag(final String tagName, String message, boolean gpgSignTag, Map<String, String> messageProperties) throws MojoFailureException, CommandLineException { message = replaceProperties(message, messageProperties); if (gpgSignTag) { getLog().info("Creating GPG-signed '" + tagName + "' tag."); executeGitCommand("tag", "-a", "-s", tagName, "-m", message); } else { getLog().info("Creating '" + tagName + "' tag."); executeGitCommand("tag", "-a", tagName, "-m", message); } } /** * Executes git branch -d. * * @param branchName * Branch name to delete. * @throws MojoFailureException * @throws CommandLineException */ protected void gitBranchDelete(final String branchName) throws MojoFailureException, CommandLineException { getLog().info("Deleting '" + branchName + "' branch."); executeGitCommand("branch", "-d", branchName); } /** * Executes git branch -D. * * @param branchName * Branch name to delete. * @throws MojoFailureException * @throws CommandLineException */ protected void gitBranchDeleteForce(final String branchName) throws MojoFailureException, CommandLineException { getLog().info("Deleting (-D) '" + branchName + "' branch."); executeGitCommand("branch", "-D", branchName); } /** * Fetches and checkouts from remote if local branch doesn't exist. * * @param branchName * Branch name to check. * @throws MojoFailureException * @throws CommandLineException */ protected void gitFetchRemoteAndCreate(final String branchName) throws MojoFailureException, CommandLineException { if (!gitCheckBranchExists(branchName)) { getLog().info( "Local branch '" + branchName + "' doesn't exist. Trying to fetch and check it out from '" + gitFlowConfig.getOrigin() + "'."); gitFetchRemote(branchName); gitCreateAndCheckout(branchName, gitFlowConfig.getOrigin() + "/" + branchName); } } /** * Executes git fetch and compares local branch with the remote. * * @param branchName * Branch name to fetch and compare. * @throws MojoFailureException * @throws CommandLineException */ protected void gitFetchRemoteAndCompare(final String branchName) throws MojoFailureException, CommandLineException { if (gitFetchRemote(branchName)) { getLog().info( "Comparing local branch '" + branchName + "' with remote '" + gitFlowConfig.getOrigin() + "/" + branchName + "'."); String revlistout = executeGitCommandReturn("rev-list", "--left-right", "--count", branchName + "..." + gitFlowConfig.getOrigin() + "/" + branchName); String[] counts = org.apache.commons.lang3.StringUtils.split( revlistout, '\t'); if (counts != null && counts.length > 1) { if (!"0".equals(org.apache.commons.lang3.StringUtils .deleteWhitespace(counts[1]))) { throw new MojoFailureException("Remote branch '" + gitFlowConfig.getOrigin() + "/" + branchName + "' is ahead of the local branch '" + branchName + "'. Execute git pull."); } } } } /** * Executes git fetch and git for-each-ref with <code>refname:short</code> * format. Searches <code>refs/remotes/{remoteName}/</code>. * * @param remoteName * Name of the remote. * @param branchName * Branch name to find. * @param firstMatch * Return first match. * @return Branch names which matches <code>refs/heads/{branchName}*</code>. * @throws MojoFailureException * @throws CommandLineException */ protected String gitFetchAndFindRemoteBranches(final String remoteName, final String branchName, final boolean firstMatch) throws MojoFailureException, CommandLineException { gitFetchRemote(); return gitFindBranches("refs/remotes/" + remoteName + "/", branchName, firstMatch); } /** * Executes git fetch. * * @return <code>true</code> if git fetch returned success exit code, * <code>false</code> otherwise. * @throws MojoFailureException * @throws CommandLineException */ private boolean gitFetchRemote() throws MojoFailureException, CommandLineException { return gitFetchRemote(""); } /** * Executes git fetch with specific branch. * * @param branchName * Branch name to fetch. * @return <code>true</code> if git fetch returned success exit code, * <code>false</code> otherwise. * @throws MojoFailureException * @throws CommandLineException */ private boolean gitFetchRemote(final String branchName) throws MojoFailureException, CommandLineException { getLog().info( "Fetching remote branch '" + gitFlowConfig.getOrigin() + " " + branchName + "'."); CommandResult result = executeGitCommandExitCode("fetch", "--quiet", gitFlowConfig.getOrigin(), branchName); boolean success = result.getExitCode() == SUCCESS_EXIT_CODE; if (!success) { getLog().warn( "There were some problems fetching remote branch '" + gitFlowConfig.getOrigin() + " " + branchName + "'. You can turn off remote branch fetching by setting the 'fetchRemote' parameter to false."); } return success; } /** * Executes git push, optionally with the <code>--follow-tags</code> * argument. * * @param branchName * Branch name to push. * @param pushTags * If <code>true</code> adds <code>--follow-tags</code> argument * to the git <code>push</code> command. * @throws MojoFailureException * @throws CommandLineException */ protected void gitPush(final String branchName, boolean pushTags) throws MojoFailureException, CommandLineException { getLog().info( "Pushing '" + branchName + "' branch" + " to '" + gitFlowConfig.getOrigin() + "'."); if (pushTags) { executeGitCommand("push", "--quiet", "-u", "--follow-tags", gitFlowConfig.getOrigin(), branchName); } else { executeGitCommand("push", "--quiet", "-u", gitFlowConfig.getOrigin(), branchName); } } protected void gitPushDelete(final String branchName) throws MojoFailureException, CommandLineException { getLog().info( "Deleting remote branch '" + branchName + "' from '" + gitFlowConfig.getOrigin() + "'."); CommandResult result = executeGitCommandExitCode("push", "--delete", gitFlowConfig.getOrigin(), branchName); if (result.getExitCode() != SUCCESS_EXIT_CODE) { getLog().warn( "There were some problems deleting remote branch '" + branchName + "' from '" + gitFlowConfig.getOrigin() + "'."); } } /** * Executes 'set' goal of versions-maven-plugin or 'set-version' of * tycho-versions-plugin in case it is tycho build. * * @param version * New version to set. * @throws MojoFailureException * @throws CommandLineException */ protected void mvnSetVersions(final String version) throws MojoFailureException, CommandLineException { getLog().info("Updating version(s) to '" + version + "'."); String newVersion = "-DnewVersion=" + version; String g = ""; String a = ""; if (versionsForceUpdate) { g = "-DgroupId="; a = "-DartifactId="; } if (tychoBuild) { String prop = ""; if (StringUtils.isNotBlank(versionProperty)) { prop = "-Dproperties=" + versionProperty; getLog().info("Updating property '" + versionProperty + "' to '" + version + "'."); } executeMvnCommand(TYCHO_VERSIONS_PLUGIN_SET_GOAL, prop, newVersion, "-Dtycho.mode=maven"); } else { if (!skipUpdateVersion) { executeMvnCommand(VERSIONS_MAVEN_PLUGIN_SET_GOAL, g, a, newVersion, "-DgenerateBackupPoms=false"); } if (StringUtils.isNotBlank(versionProperty)) { getLog().info("Updating property '" + versionProperty + "' to '" + version + "'."); executeMvnCommand(VERSIONS_MAVEN_PLUGIN_SET_PROPERTY_GOAL, newVersion, "-Dproperty=" + versionProperty, "-DgenerateBackupPoms=false"); } } } /** * Executes mvn clean test. * * @throws MojoFailureException * @throws CommandLineException */ protected void mvnCleanTest() throws MojoFailureException, CommandLineException { getLog().info("Cleaning and testing the project."); if (tychoBuild) { executeMvnCommand("clean", "verify"); } else { executeMvnCommand("clean", "test"); } } /** * Executes mvn clean install. * * @throws MojoFailureException * @throws CommandLineException */ protected void mvnCleanInstall() throws MojoFailureException, CommandLineException { getLog().info("Cleaning and installing the project."); executeMvnCommand("clean", "install"); } /** * Executes Maven goals. * * @param goals * The goals to execute. * @throws Exception */ protected void mvnRun(final String goals) throws Exception { getLog().info("Running Maven goals: " + goals); executeMvnCommand(CommandLineUtils.translateCommandline(goals)); } /** * Executes Git command and returns output. * * @param args * Git command line arguments. * @return Command output. * @throws CommandLineException * @throws MojoFailureException */ private String executeGitCommandReturn(final String... args) throws CommandLineException, MojoFailureException { return executeCommand(cmdGit, true, null, args).getOut(); } /** * Executes Git command without failing on non successful exit code. * * @param args * Git command line arguments. * @return Command result. * @throws CommandLineException * @throws MojoFailureException */ private CommandResult executeGitCommandExitCode(final String... args) throws CommandLineException, MojoFailureException { return executeCommand(cmdGit, false, null, args); } /** * Executes Git command. * * @param args * Git command line arguments. * @throws CommandLineException * @throws MojoFailureException */ private void executeGitCommand(final String... args) throws CommandLineException, MojoFailureException { executeCommand(cmdGit, true, null, args); } /** * Executes Maven command. * * @param args * Maven command line arguments. * @throws CommandLineException * @throws MojoFailureException */ private void executeMvnCommand(final String... args) throws CommandLineException, MojoFailureException { executeCommand(cmdMvn, true, argLine, args); } /** * Executes command line. * * @param cmd * Command line. * @param failOnError * Whether to throw exception on NOT success exit code. * @param argStr * Command line arguments as a string. * @param args * Command line arguments. * @return {@link CommandResult} instance holding command exit code, output * and error if any. * @throws CommandLineException * @throws MojoFailureException * If <code>failOnError</code> is <code>true</code> and command * exit code is NOT equals to 0. */ private CommandResult executeCommand(final Commandline cmd, final boolean failOnError, final String argStr, final String... args) throws CommandLineException, MojoFailureException { // initialize executables initExecutables(); if (getLog().isDebugEnabled()) { getLog().debug( cmd.getExecutable() + " " + StringUtils.join(args, " ") + (argStr == null ? "" : " " + argStr)); } cmd.clearArgs(); cmd.addArguments(args); if (StringUtils.isNotBlank(argStr)) { cmd.createArg().setLine(argStr); } final StringBufferStreamConsumer out = new StringBufferStreamConsumer( verbose); final CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); // execute final int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err); String errorStr = err.getOutput(); String outStr = out.getOutput(); if (failOnError && exitCode != SUCCESS_EXIT_CODE) { // not all commands print errors to error stream if (StringUtils.isBlank(errorStr) && StringUtils.isNotBlank(outStr)) { errorStr = outStr; } throw new MojoFailureException(errorStr); } return new CommandResult(exitCode, outStr, errorStr); } private static class CommandResult { private final int exitCode; private final String out; private final String error; private CommandResult(final int exitCode, final String out, final String error) { this.exitCode = exitCode; this.out = out; this.error = error; } /** * @return the exitCode */ public int getExitCode() { return exitCode; } /** * @return the out */ public String getOut() { return out; } /** * @return the error */ public String getError() { return error; } } public void setArgLine(String argLine) { this.argLine = argLine; } }