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

com.telenav.cactus.maven.DevelopmentPrepMojo Maven / Gradle / Ivy

The newest version!
package com.telenav.cactus.maven;

import com.mastfrog.util.preconditions.Exceptions;
import com.telenav.cactus.maven.log.BuildLog;
import com.telenav.cactus.maven.git.Branches;
import com.telenav.cactus.maven.git.Branches.Branch;
import com.telenav.cactus.maven.git.GitCheckout;
import com.telenav.cactus.maven.tree.ProjectTree;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;
import org.apache.maven.plugin.MojoExecutionException;
import static org.apache.maven.plugins.annotations.InstantiationStrategy.SINGLETON;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

/**
 * Gets the current checkout, or all checkouts containing projects with the
 * current project's groupId, or all checkouts in the entire tree - filtering
 * them to only checkouts that contain a pom.xml in the root, and does one of:
 * 
    *
  • If a branch is specified, ensures the checkout is on the head of that * branch, creating it if needed (and createBranchesIfNeeded is set)
  • *
  • If no branch is specified, attempts to find the branch the majority of * checkouts containing the same group id are on, and sets it to that, creating * it if needed
  • *
* * @author Tim Boudreau */ @org.apache.maven.plugins.annotations.Mojo( defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.NONE, instantiationStrategy = SINGLETON, name = "dev-prep", threadSafe = true) public class DevelopmentPrepMojo extends BaseMojo { @Parameter(property = "telenav.autoFixBranches", defaultValue = "false") boolean autoFixBranches = false; @Parameter(property = "telenav.createBranchesIfNeeded", defaultValue = "false") boolean createBranchesIfNeeded = false; @Parameter(property = "telenav.thisCheckoutOnly", defaultValue = "false") boolean thisCheckoutOnly = false; @Parameter(property = "telenav.thisGroupIdOnly", defaultValue = "true") boolean thisGroupIdOnly = true; @Parameter(property = "telenav.branch") String branchName; @Parameter(property = "telenav.baseBranch", defaultValue = "develop") String baseBranch = "develop"; GitCheckout myGitCheckout; @Override protected boolean isOncePerSession() { return true; } protected void validateParameters(BuildLog log, MavenProject project) throws Exception { System.out.println("autoFixBranches = " + autoFixBranches); System.out.println("createBranchesIfNeeded = " + createBranchesIfNeeded); System.out.println("thisCheckoutOnly = " + thisCheckoutOnly); System.out.println("thisGroupIdOnly = " + thisGroupIdOnly); System.out.println("branch = " + branchName); System.out.println("baseBranch = " + baseBranch); super.validateParameters(log, project); // Branches starting with - can get misinterpreted as flags to git branch if (!checkBranchName(branchName)) { throw new MojoExecutionException("feature '" + branchName + "' is not a valid feature branch name"); } if (!checkBranchName(baseBranch)) { throw new MojoExecutionException("feature '" + baseBranch + "' is not a valid branch name"); } Optional myRepo = GitCheckout.repository(project.getBasedir()); if (!myRepo.isPresent()) { throw new MojoExecutionException("Could not find a git checkout above " + project.getBasedir()); } else { myGitCheckout = myRepo.get(); } } private boolean checkBranchName(String branchName) { if (branchName == null) { return true; } return branchName.isBlank() || !branchName.startsWith("-") && !branchName.contains(" ") && !branchName.contains("\"") && !branchName.contains("'"); } @Override protected void performTasks(BuildLog log, MavenProject project) throws Exception { ProjectTree.from(project).ifPresent(tree -> { if (branchName != null) { ensureOnBranch(tree, log.child("branch-to:" + branchName), project); } else { ensureOnSomeConsistentBranch(tree, log.child("ensure-some-branch"), project); } }); } private void ensureOnBranch(ProjectTree tree, BuildLog log, MavenProject project) throws Exception { Map toMove; if (thisCheckoutOnly) { toMove = Collections.singletonMap(myGitCheckout, baseBranch); } else { toMove = new TreeMap<>(); tree.allCheckouts().forEach(checkout -> { if (checkout.hasPomInRoot()) { Optional currentBranch = tree.branchFor(checkout); if (thisGroupIdOnly) { if (!tree.groupIdsIn(checkout).contains(project.getGroupId())) { return; } } if (!currentBranch.isPresent() || !branchName.equals(currentBranch.get())) { System.out.println("ADD " + checkout.checkoutRoot().getFileName()); toMove.put(checkout, branchName); } } }); } if (!toMove.isEmpty()) { // Once without doing anything, to fail-fast without changing any branches moveCheckoutsToBranches(toMove, log, tree, true); // And again to really do it, now that we know it can work moveCheckoutsToBranches(toMove, log, tree, false); } else { log.info("All checkouts already on the branch '" + branchName + "'"); } } private void ensureOnSomeConsistentBranch(ProjectTree tree, BuildLog log, MavenProject project) throws Exception { Map changes = new HashMap<>(); if (thisCheckoutOnly) { if (!myGitCheckout.isDetachedHead()) { log.info("Single repo, already on a branch, nothing to do."); return; } log.info("Single repo, not on a branch - will move to '" + baseBranch + "'."); changes.put(myGitCheckout, baseBranch); } else { Predicate filter; if (thisGroupIdOnly) { filter = checkout -> { return tree.groupIdsIn(checkout).contains(project.getGroupId()); }; } else { filter = ignored -> true; } tree.allCheckouts().stream().filter(filter).filter(GitCheckout::hasPomInRoot).forEach(checkout -> { if (checkout.isSubmoduleRoot()) { return; } Set gids = tree.groupIdsIn(checkout); System.out.println(" gits " + gids + " in " + checkout); if (gids.size() > 1) { String msg = "More than one group id in non-root checkout " + checkout + ". Will not guess what branch to move this to. " + "Explicitly specify a branch instead with " + "-Dtelenav.branch=someBranch"; // Quietly throw: Exceptions.chuck(new MojoExecutionException(this, msg, msg)); } else if (gids.isEmpty()) { // This should have already been filtered out, but throwing a // NoSuchElementException would be non-intuitive log.warn("No group ids at all for checkout " + checkout); return; } Optional targetBranch = tree.mostCommonBranchForGroupId(gids.iterator().next()); String target = targetBranch.orElse(baseBranch); boolean needAdd = tree.isDetachedHead(checkout); System.out.println(" detached head " + needAdd + " for " + checkout); if (!needAdd) { Optional currentBranch = tree.branchFor(checkout); needAdd = !currentBranch.isPresent() || !target.equals(currentBranch.get()); } if (needAdd) { changes.put(checkout, target); } }); } if (!changes.isEmpty()) { // Once to fail fast without making changes moveCheckoutsToBranches(changes, log, tree, true); // And once to make changes moveCheckoutsToBranches(changes, log, tree, false); } else { log.info("Nothing to do."); } } private void moveCheckoutsToBranches(Map targetBranchToMoveToForCheckout, BuildLog childLog, ProjectTree tree, boolean pretend) throws MojoExecutionException { if (!targetBranchToMoveToForCheckout.isEmpty()) { childLog.info("Have " + targetBranchToMoveToForCheckout.size() + " checkouts to change branches for"); Map submoduleBranchesToUpdate = new HashMap<>(); for (Map.Entry e : targetBranchToMoveToForCheckout.entrySet()) { GitCheckout checkout = e.getKey(); String branchToChangeTo = e.getValue(); if (autoFixBranches) { childLog.info("Move to branch '" + branchToChangeTo + "' for " + checkout); Branches branches = e.getKey().branches(); Optional branch = branches.find(branchToChangeTo, true); if (!branch.isPresent()) { if (createBranchesIfNeeded) { if (checkout.hasUncommitedChanges() && !checkout.isSubmoduleRoot()) { if (tree.branchFor(checkout).isPresent() && !tree.branchFor(checkout).get().equals(baseBranch)) { throw new MojoExecutionException("Cannot create a new branch named '" + branchToChangeTo + " in " + checkout + " because it contain uncommited changes."); } } boolean success = checkout.createAndSwitchToBranch(branchToChangeTo, Optional.ofNullable(baseBranch), pretend); checkout.submoduleRelativePath().ifPresent(relativePath -> { submoduleBranchesToUpdate.put(relativePath, branchToChangeTo); }); if (!success) { throw new MojoExecutionException("Create and switch to branch " + branchToChangeTo + " failed for " + checkout); } } else { throw new MojoExecutionException("Local branch '" + e.getValue() + "' does not exist for " + e.getKey() + " and createBranchesIfNeeded is not set to true." + " Cannot switch to that branch."); } } else { boolean switched = pretend ? true : checkout.switchToBranch(branchToChangeTo); if (!switched) { throw new MojoExecutionException("Failed to switch to branch " + branchToChangeTo + " in " + checkout); } checkout.submoduleRelativePath().ifPresent(relativePath -> { submoduleBranchesToUpdate.put(relativePath, branchToChangeTo); }); } } else { childLog.info("autoFixBranches is false - branch should be " + branchToChangeTo + " for " + checkout); } } if (!autoFixBranches && !pretend) { throw new MojoExecutionException(this, "Some checkouts need their branches changed, " + "but telenav.autoFixBranches is false", "Some checkouts need their branches changed: " + targetBranchToMoveToForCheckout); } if (!pretend && targetBranchToMoveToForCheckout.containsKey(tree.root())) { log.info("Updating .gitmodules."); StringBuilder msg = new StringBuilder("Automated .gitmodules branch update moving"); for (Map.Entry e : submoduleBranchesToUpdate.entrySet()) { tree.root().setSubmoduleBranch(e.getKey().toString(), e.getValue()); msg.append("\n * ").append(e.getKey()).append(" to ").append(e.getValue()); } if (tree.root().hasUncommitedChanges()) { log.info("Committing updated .gitmodules"); tree.root().addAll(); tree.root().commit(msg.toString()); } tree.invalidateCache(); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy