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

org.codehaus.mojo.versions.BumpMojo Maven / Gradle / Ivy

package org.codehaus.mojo.versions;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.Maven;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.RebaseCommand;
import org.eclipse.jgit.api.RebaseResult;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidMergeHeadsException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.NetRCCredentialsProvider;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;

import edu.emory.mathcs.backport.java.util.Collections;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/**
 * Sets the current projects version, updating the details of any child modules as necessary.
 * 
 * @author axet
 */
@Mojo( name = "bump", requiresProject = true, requiresDirectInvocation = true, aggregator = true )
public class BumpMojo extends AbstractMojo {

    /**
     * The new bump number to set. server-1.0.0 or server-test-1.0.0-1
     * 
     * @parameter expression="${bump}"
     * @since 1.0-beta-1
     */
    @Parameter( property = "bump", defaultValue = "" )
    private String bump;

    /**
     * The 'dev' branch name.
     * 
     * @parameter expression="${dev}" default-value="dev"
     * @since 1.0-beta-1
     */
    @Parameter( property = "dev", defaultValue = "dev" )
    private String dev;

    /**
     * The push changed back to repo?
     * 
     * @parameter expression="${push}" default-value="true"
     * @since 1.0-beta-1
     */
    @Parameter( property = "push", defaultValue = "true" )
    private Boolean push;

    /**
     * The master branch name.
     * 
     * @parameter expression="${master}" default-value="master"
     * @since 1.0-beta-1
     */
    @Parameter( property = "master", defaultValue = "master" )
    private String master;

    /**
     * @component allo
     * @since 1.0-alpha-1
     */
    @Component
    protected MavenProjectBuilder projectBuilder;

    /**
     * @parameter expression="${localRepository}"
     * @readonly
     * @since 1.0-alpha-1
     */
    @Parameter( property = "localRepository" )
    protected ArtifactRepository localRepository;

    /**
     * The Maven Project.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     * @since 1.0-alpha-1
     */
    @Parameter( property = "project" )
    private MavenProject project;

    /**
     * The Maven Session.
     * 
     * @component
     * @required
     * @readonly
     * @since 1.0-alpha-1
     */
    Maven maven;

    Git git;

    static Pattern VERSION_PATTERN = Pattern.compile("([a-zA-Z-_]+)-((?:\\d+)?(?:\\.?\\d+)?(?:\\.\\d+)?(?:-\\d+)?)"); 

    static class VersionSort implements Comparator {

        public int compare(String o1, String o2) {
            Matcher m1 = VERSION_PATTERN.matcher(o1);
            Matcher m2 = VERSION_PATTERN.matcher(o2);
            if(m1.find())
                o1 = m1.group(2);
            if(m2.find())
                o2 = m2.group(2);

            String[] oo1 = o1.split("[\\.-]");
            String[] oo2 = o2.split("[\\.-]");

            int len = Math.min(oo1.length, oo2.length);

            for (int i = 0; i < len; i++) {
                int r = Integer.valueOf(oo1[i]).compareTo(Integer.valueOf(oo2[i]));
                if (r != 0)
                    return r;
            }
            
            return Integer.valueOf(oo1.length).compareTo(Integer.valueOf(oo2.length));
        }
    }

    public void init() throws MojoExecutionException {
        try {
            NetRCCredentialsProvider.install();

            FileRepositoryBuilder builder = new FileRepositoryBuilder();
            Repository repository = builder.findGitDir().readEnvironment().findGitDir().build();
            git = new Git(repository);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void out(String str) {
        System.out.println(str);
    }

    public void gitLastTags() throws MojoExecutionException {
        try {
            List list = git.tagList().call();

            Map> u = new TreeMap>();

            for (Ref r : list) {
                String tag = StringUtils.removeStart(r.getName(), "refs/tags/");
                tag = SwitchReleaseMojo.release(tag);
                String tt = tag.split("-")[0];
                List list2 = u.get(tt);
                if (list2 == null) {
                    list2 = new ArrayList();
                }
                list2.add(tag);
                u.put(tt, list2);
            }

            for (String tt : u.keySet()) {
                List list2 = u.get(tt);
                Collections.sort(list2, new VersionSort());

                for (int i = Math.max(0, list2.size() - 3); i < list2.size(); i++) {
                    out(list2.get(i));
                }
            }
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    public boolean gitTagExists(String tag) throws MojoExecutionException {
        try {
            List list = git.tagList().call();

            for (Ref r : list) {
                String tag2 = StringUtils.removeStart(r.getName(), "refs/tags/");
                if (tag2.equals(tag))
                    return true;
            }
            return false;
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    boolean notEmpty(Set ss) {
        return !ss.isEmpty();
    }

    boolean gitModified() throws MojoExecutionException {
        StatusCommand statusCommand = git.status();
        try {
            Status status = statusCommand.call();
            if (notEmpty(status.getModified()))
                return true;

            if (notEmpty(status.getAdded()))
                return true;

            if (notEmpty(status.getChanged()))
                return true;

            if (notEmpty(status.getConflicting()))
                return true;

            if (notEmpty(status.getRemoved()))
                return true;

            if (notEmpty(status.getUncommittedChanges()))
                return true;

            if (notEmpty(status.getUntracked()))
                return true;

            return false;
        } catch (NoWorkTreeException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void out(Set ss) {
        for (String s : ss) {
            out("?? " + s);
        }
    }

    void gitFetch() throws MojoExecutionException {
        try {
            FetchCommand ff = git.fetch();
            FetchResult f = ff.call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitCheckout(String m) throws MojoExecutionException {
        try {
            CheckoutCommand ff = git.checkout();
            ff.setName(m);
            Ref f = ff.call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitBranchD(String m) throws MojoExecutionException {
        try {
            List f = git.branchDelete().setBranchNames("refs/heads/" + m).call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitPush(String d) throws MojoExecutionException {
        String origin = git.getRepository().getConfig().getString("branch", d, "remote");
        String merge = git.getRepository().getConfig().getString("branch", d, "merge");

        merge = Repository.shortenRefName(merge);
        
        gitPush(origin, merge);
    }
    
    void gitPush(String o, String d) throws MojoExecutionException {
        try {
            RefSpec spec = new RefSpec(d + ":" + d);
            PushCommand ff = git.push();
            ff.setRemote(o);
            ff.setRefSpecs(spec);
            ff.setPushTags();
            Iterable f = ff.call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitCheckoutB(String tag) throws MojoExecutionException {
        try {
            CheckoutCommand ff = git.checkout();
            ff.setName(tag);
            ff.setCreateBranch(true);
            Ref f = ff.call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitTag(String tag) throws MojoExecutionException {
        try {
            git.tag().setName(tag).call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitRebase() throws MojoExecutionException {
        try {
            RebaseCommand ff = git.rebase();

            String masterOrigin = git.getRepository().getConfig().getString("branch", master, "remote");
            String masterMerge = git.getRepository().getConfig().getString("branch", master, "merge");

            masterMerge = Repository.shortenRefName(masterMerge);

            RevWalk walk = new RevWalk(git.getRepository());
            RevCommit commit = walk.parseCommit(git.getRepository().getRef(masterOrigin + "/" + masterMerge)
                    .getObjectId());

            ff.setUpstream(commit);
            RebaseResult f = ff.call();
            if (!f.getStatus().isSuccessful()) {
                throw new MojoExecutionException(f.getStatus().toString());
            }
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (MissingObjectException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IncorrectObjectTypeException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    void gitMerge(String c, String m) throws NoHeadException, ConcurrentRefUpdateException, CheckoutConflictException,
            InvalidMergeHeadsException, WrongRepositoryStateException, NoMessageException, GitAPIException, IOException {
        git.merge().setCommit(false).include(git.getRepository().getRef(m)).call();
        git.commit().setAll(true).setMessage(c).call();
    }

    void gitMergeNFF(String c, String m) throws MojoExecutionException {
        try {
            MergeCommand ff = git.merge();
            ff.setCommit(false);
            ff.setFastForward(FastForwardMode.NO_FF);
            ff.include(git.getRepository().getRef(m));
            MergeResult f = ff.call();
            if (!f.getMergeStatus().isSuccessful()) {
                throw new MojoExecutionException(f.getMergeStatus().toString());
            }
            git.commit().setAll(true).setMessage(c).call();
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    /**
     * Called when this mojo is executed.
     * 
     * @throws org.apache.maven.plugin.MojoExecutionException
     *             when things go wrong.
     * @throws org.apache.maven.plugin.MojoFailureException
     *             when things go wrong.
     */
    public void execute() throws MojoExecutionException, MojoFailureException {
        try {
            init();

            if (StringUtils.isEmpty(bump)) {
                out("Current tags:");
                gitLastTags();
                out("");
                out("type version like 'mvn -q com.github.axet:versions-maven-plugin:bump -Dbump=server-1.0.0'");
                System.exit(1);
            }

            if (gitTagExists(bump)) {
                out("Current tags:");
                gitLastTags();
                out("");
                out("tag '" + bump + "' already exist");
                System.exit(1);
            }

            String tag = bump;
            String branch;
            String version;

            Matcher m = VERSION_PATTERN.matcher(tag);
            if(!m.find()) {
                out("unable to parse tag: "+  tag);
                System.exit(1);
            }
            branch = m.group(1);
            version = m.group(2);

            if (gitModified()) {
                StatusCommand statusCommand = git.status();
                try {
                    out("Can't bump version on modified tree:");
                    Status status = statusCommand.call();
                    out(status.getModified());

                    out((status.getAdded()));

                    out((status.getChanged()));

                    out((status.getConflicting()));

                    out((status.getRemoved()));

                    out((status.getUncommittedChanges()));

                    out((status.getUntracked()));
                } catch (NoWorkTreeException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                } catch (GitAPIException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                }
                System.exit(1);
            }

            // merge master. we allowd to change master if it is a readme file
            // git fetch
            gitFetch();
            // git checkout master
            gitCheckout(master);
            // git rebase
            gitRebase();
            // git checkout dev
            gitCheckout(dev);
            // git merge -m "Merge master" master
            gitMerge("Merge master", master);

            // bump version in dev tree, otherwise it will produce conflict merge on master on second
            // tag for pom.xml
            // git checkout dev
            gitCheckout(dev);
            final List tags = new ArrayList();
            SwitchReleaseMojo release = new SwitchReleaseMojo() {
                @Override
                synchronized String incrementModuleVersion(String groupId, String artifactId, String version) {
                    String newVersion = super.incrementVersion(SwitchReleaseMojo.release(version));
                    tags.add(artifactId + "-" + newVersion);
                    return newVersion;
                }
            };
            release.projectBuilder = projectBuilder;
            release.localRepository = localRepository;
            release.setProject(project);
            release.setNewVersion(version);
            release.setLog(getLog());
            release.execute();
            // git commit -m "Bump version $BRANCH $VERSION" -a
            git.commit().setAll(true).setMessage("Bump version " + branch + " " + version).call();

            // create proper branch (server-1.0.0), helps produce nice git history
            // git checkout -b $TAG dev
            gitCheckoutB(tag);

            // prepare to merge
            // git checkout master
            gitCheckout(master);

            // do merge with a propertly named branch
            // git merge --no-ff -m "Merge branch '$TAG'" $TAG
            gitMergeNFF("Merge branch '" + tag + "'", tag);

            // do tag last master commit
            // git tag $TAG
            gitTag(tag);

            // tag all other changed (depended) modules
            for (String t : tags)
                gitTag(t);
            tags.clear();

            // drop perpared branch
            // git branch -d $TAG
            gitBranchD(tag);

            // switch back to the dev
            // git checkout dev
            gitCheckout(dev);

            // update version with -SNAPSHOT postfix
            // git commit -m "Bump version $BRANCH $VERSION" -a
            SwitchSnapshotMojo snapshot = new SwitchSnapshotMojo();
            snapshot.projectBuilder = projectBuilder;
            snapshot.localRepository = localRepository;
            snapshot.setProject(project);
            snapshot.setLog(getLog());
            snapshot.execute();
            git.commit().setAll(true).setMessage("Bump version " + branch + " " + version).call();

            if (push) {
                out("");
                out("All done, push repo");
                out("");
                // git push origin master || exit 1
                // git push --tags || exit 1
                // git push origin dev || exit 1
                gitPush(master);
                gitPush(dev);
            }
        } catch (GitAPIException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy