All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amashchenko.maven.plugin.gitflow.AbstractGitFlowMojo Maven / Gradle / Ivy

/*
 * Copyright 2014-2018 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.io.FileReader;
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.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
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.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";
    /** 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;

    /**
     * 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)
    private MavenSession mavenSession;
    /** 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 {
        try {
            // read pom.xml
            final MavenXpp3Reader mavenReader = new MavenXpp3Reader();
            final FileReader fileReader = new FileReader(mavenSession
                    .getCurrentProject().getFile().getAbsoluteFile());
            try {
                final Model model = mavenReader.read(fileReader);

                if (model.getVersion() == null) {
                    throw new MojoFailureException(
                            "Cannot get current project version. This plugin should be executed from the parent project.");
                }

                return model.getVersion();
            } finally {
                if (fileReader != null) {
                    fileReader.close();
                }
            }
        } catch (Exception e) {
            throw new MojoFailureException("", e);
        }
    }

    /**
     * Compares the production branch name with the development branch name.
     * 
     * @return true if the production branch name is different from
     *         the development branch name, false 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 snapshots = new ArrayList();
        List builtArtifacts = new ArrayList();

        List projects = mavenSession.getProjects();
        for (MavenProject project : projects) {
            builtArtifacts.add(project.getGroupId() + ":"
                    + project.getArtifactId() + ":" + project.getVersion());

            List dependencies = project.getDependencies();
            for (Dependency d : dependencies) {
                String id = d.getGroupId() + ":" + d.getArtifactId() + ":"
                        + d.getVersion();
                if (!builtArtifacts.contains(id)
                        && ArtifactUtils.isSnapshot(d.getVersion())) {
                    snapshots.add(project + " -> " + 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 true when name is valid, false
     *         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 true when there are uncommitted changes,
     *         false 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 refname:short format.
     * 
     * @param branchName
     *            Branch name to find.
     * @param firstMatch
     *            Return first match.
     * @return Branch names which matches refs/heads/{branchName}*.
     * @throws MojoFailureException
     * @throws CommandLineException
     */
    protected String gitFindBranches(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/heads/" + branchName
                            + wildcard);
        } else {
            branches = executeGitCommandReturn("for-each-ref",
                    "--format=\"%(refname:short)\"", "refs/heads/" + 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);

        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;
    }

    /**
     * Checks if local branch with given name exists.
     *
     * @param branchName
     *            Name of the branch to check.
     * @return true if local branch exists, false
     *         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 true if local tag exists, false 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);
    }

    /**
     * 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 @{map.key} with
     * map.value.
     * 
     * @param message
     *            Commit message.
     * @param map
     *            Key is a string to replace wrapped in @{...}.
     *            Value is a string to replace with.
     * @throws MojoFailureException
     * @throws CommandLineException
     */
    protected void gitCommit(String message, Map map)
            throws MojoFailureException, CommandLineException {
        if (map != null) {
            for (Entry entr : map.entrySet()) {
                message = StringUtils.replace(message, "@{" + entr.getKey()
                        + "}", entr.getValue());
            }
        }

        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.
     * @throws MojoFailureException
     * @throws CommandLineException
     */
    protected void gitMerge(final String branchName, boolean rebase, boolean noff, boolean ffonly)
            throws MojoFailureException, CommandLineException {
        String sign = "";
        if (gpgSignCommit) {
            sign = "-S";
        }
        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);
        } else {
            getLog().info("Merging '" + branchName + "' branch.");
            executeGitCommand("merge", sign, branchName);
        }
    }

    /**
     * Executes git merge --no-ff.
     * 
     * @param branchName
     *            Branch name to merge.
     * @throws MojoFailureException
     * @throws CommandLineException
     */
    protected void gitMergeNoff(final String branchName)
            throws MojoFailureException, CommandLineException {
        gitMerge(branchName, false, true, false);
    }

    /**
     * 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 map
     *            Key is a string to replace wrapped in @{...}. Value
     *            is a string to replace with.
     * @throws MojoFailureException
     * @throws CommandLineException
     */
    protected void gitTag(final String tagName, String message, boolean gpgSignTag, Map map)
            throws MojoFailureException, CommandLineException {
        if (map != null) {
            for (Entry entr : map.entrySet()) {
                message = StringUtils.replace(message, "@{" + entr.getKey() + "}", entr.getValue());
            }
        }

        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.
     * 
     * @param branchName
     *            Branch name to fetch.
     * @return true if git fetch returned success exit code,
     *         false 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 --follow-tags
     * argument.
     * 
     * @param branchName
     *            Branch name to push.
     * @param pushTags
     *            If true adds --follow-tags argument
     *            to the git push 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 g = "";
        String a = "";
        if (versionsForceUpdate) {
            g = "-DgroupId='*'";
            a = "-DartifactId='*'";
        }

        if (tychoBuild) {
            executeMvnCommand(TYCHO_VERSIONS_PLUGIN_SET_GOAL, "-DnewVersion="
                    + version, "-Dtycho.mode=maven");
        } else {
            executeMvnCommand(VERSIONS_MAVEN_PLUGIN_SET_GOAL, g, a, "-DnewVersion="
                    + version, "-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 failOnError is true 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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy