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

org.kuali.maven.plugins.fusion.SplitMojo Maven / Gradle / Ivy

/**
 * 
 */
package org.kuali.maven.plugins.fusion;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kuali.maven.plugins.fusion.util.GitFusionUtils;
import org.kuali.student.git.model.GitRepositoryUtils;
import org.kuali.student.git.model.ExternalModuleUtils;
import org.kuali.student.git.model.ExternalModuleUtils.IBranchHeadProvider;
import org.kuali.student.git.model.branch.large.LargeBranchNameProviderMapImpl;
import org.kuali.student.git.model.ref.exception.BranchRefExistsException;
import org.kuali.student.git.model.ref.utils.GitRefUtils;
import org.kuali.student.git.model.tree.GitTreeNodeData;
import org.kuali.student.git.model.tree.utils.GitTreeProcessor;
import org.kuali.student.git.model.tree.utils.JGitTreeUtils;
import org.kuali.student.svn.model.ExternalModuleInfo;

/**
 * @author ocleirig
 * 
 */
@Mojo(name = FusionMavenPluginConstants.SPLIT_MOJO)
@Execute(goal = FusionMavenPluginConstants.SPLIT_MOJO)
public class SplitMojo extends AbstractFusionMojo {
	
	/*
	 * true or false and is used to determine if we should commit the current branch before performing the split operation.
	 */
	@Parameter(property = FusionMavenPluginConstants.COMMIT_BEFORE_SPLIT_PREFIX, defaultValue = FusionMavenPluginConstants.COMMIT_BEFORE_SPLIT_PREFIX_DEFAULT)
	protected String commitBeforeSplit;

	
	/*
	 * true of false and is used to determine if we should amend the previous commit vs creating a new commit.  Only used when commitBeforeSplit is set.
	 */
	@Parameter(property = FusionMavenPluginConstants.AMEND_SPLIT_COMMIT_PREFIX, defaultValue = FusionMavenPluginConstants.AMEND_SPLIT_COMMIT_PREFIX_DEFAULT)
	protected String amend;
	
	
	/*
	 * Merge mode just creates the split branches
	 * 
	 * tag mode creates tags based on the GAV of the top level modules.
	 */
	@Parameter(property = FusionMavenPluginConstants.SPLIT_STYLE_PREFIX)
	protected SplitStyle splitStyle = FusionMavenPluginConstants.SPLIT_STYLE_PREFIX_DEFAULT;  
	
	
	@Parameter(property = FusionMavenPluginConstants.SPLIT_AGGREGATE_BRANCH_NAME_PREFIX)
	protected String aggregateBranchName = FusionMavenPluginConstants.SPLIT_AGGREGATE_BRANCH_NAME_PREFIX_DEFAULT;  
	
	private GitTreeProcessor treeProcessor;


	private POMUtils pomUtils;


	private Repository repo;  
	
	/**
	 * 
	 */
	public SplitMojo() {
	}

	private void createTag (String rootTagName, RevCommit commit, ObjectInserter objectInserter) throws IOException, MojoFailureException {
		
		ObjectId tagId = GitRefUtils.insertTag(rootTagName, commit,
				objectInserter);

		if (tagId != null) {

			// update the tag reference
			// copied from JGit's TagCommand
			Result updateResult = GitRefUtils.createTagReference(repo,
					rootTagName, tagId);

			if (updateResult != Result.NEW) {
				throw new MojoFailureException(
						"Problem creating tag reference for " + rootTagName
								+ " result = " + updateResult);
			}

		} else {
			throw new MojoFailureException("Failed to create tag : "
					+ rootTagName + " to commit : " + commit);
		}
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.maven.plugin.Mojo#execute()
	 */
	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {

		try {
			/*
			 * Assume that we are running on the aggregate and that the
			 * configuration will tell us how to fuse with our modules.
			 */

			File baseDir = project.getBasedir();

			/*
			 * Assume that the pom is located in the working copy of the git
			 * repository on the branch we want to fuse into
			 */

			repo = GitRepositoryUtils.buildFileRepository(baseDir,
					false, false);

			treeProcessor = new GitTreeProcessor(repo);
			
			pomUtils = new POMUtils();
			
			ObjectReader objectReader = repo.newObjectReader();

			ObjectInserter objectInserter = repo.newObjectInserter();

			RevWalk rw = new RevWalk(repo);

			String currentBranchName = repo.getBranch();

			Ref currentHead = repo.getRef(currentBranchName);
			
			RevCommit previousCommit = rw.parseCommit(currentHead.getObjectId());
			
			
			if (this.commitBeforeSplit != null && this.commitBeforeSplit.equals("true")) {
				
				CommitCommand commitCommand = GitFusionUtils.commit(repo, previousCommit, "[fusion-maven-plugin] Commit Before Split", true);
				
				RevCommit commit = commitCommand.call();
				
				if (commit == null) {
					
					objectReader.release();
					objectInserter.release();
					rw.release();
					
					repo.close();
					throw new MojoFailureException(
							"Unable commit on current branch : " + currentBranchName);
				}
				
				currentHead = repo.getRef(currentBranchName);
			}

			RevCommit fusedCommit = rw.parseCommit(currentHead.getObjectId());

			List externals = new ArrayList<>();
			
			MapmoduleNameToMapping = new HashMap();
			

			for (Mapping mapping : mappings) {

				Ref branchHead = repo.getRef(getBranchName(mapping));

				if (branchHead == null) {

					objectReader.release();
					objectInserter.release();
					rw.release();

					repo.close();

					throw new MojoFailureException(
							"Unable to find a branch head for : "
									+ mapping.getBranchName());
				}

				ObjectId headCommitId = branchHead.getObjectId();

				ExternalModuleInfo external = new ExternalModuleInfo(
						mapping.getModule(), mapping.getBranchName(), 
						headCommitId);

				externals.add(external);

				moduleNameToMapping.put(mapping.getModule(), mapping);

			}
				
				
			Map splitTreeData = ExternalModuleUtils.splitFusedTree(objectReader, objectInserter, rw, fusedCommit.getId(), externals);
			
			ObjectId aggregateTreeId = splitTreeData.remove("remainder");
			
			SetbranchesCreatedSet = new HashSet<>();
			
			Map branchNameToObjectIdMap = new HashMap<>();
			
			for (Map.Entry entry : splitTreeData.entrySet()) {
				
				String moduleName = entry.getKey();
				
				ObjectId splitTreeId = entry.getValue();
				
				Mapping mapping = moduleNameToMapping.get(moduleName);
				
				String splitBranchName = "split_" + mapping.getBranchName();
				
				branchesCreatedSet.add(splitBranchName);
				
				Ref moduleBranchHead = repo.getRef(getBranchName(mapping));
				
				RevCommit moduleBranchCommit = rw.parseCommit(moduleBranchHead.getObjectId());

				if (this.splitStyle.equals(SplitStyle.MERGE)) {
					
					Ref ref = createBranch(repo, objectInserter, moduleBranchCommit, moduleName, splitBranchName, splitTreeId);
					
					getLog().info(String.format ("created %s at %s", splitBranchName, ref.getObjectId().toString()));
				
					branchNameToObjectIdMap.put(mapping.getBranchName(), ref.getObjectId());
				
				}
				else if (this.splitStyle.equals(SplitStyle.TAG)) {
					
					String tagName = getTagName (splitTreeId);
					
					ObjectId commitId = createSplitCommit(moduleBranchCommit, moduleName, splitTreeId, objectInserter);
					
					createTag(tagName, rw.parseCommit(commitId), objectInserter);
					
					branchNameToObjectIdMap.put(mapping.getBranchName(), commitId);
					
				}
				else {
					getLog().error("invalid splitStyle: " + splitStyle);
				}
			}
			
			repo.getRefDatabase().refresh();
			
			branchesCreatedSet.add("split_" + aggregateBranchName);

			String fusionMavenPluginDataFileContent = createFusionMavenPluginDataFileString(repo, mappings, branchNameToObjectIdMap);
					
			aggregateTreeId = JGitTreeUtils.createTree(JGitTreeUtils.insertBlob(objectReader, objectInserter, aggregateTreeId, "fusion-maven-plugin.dat", fusionMavenPluginDataFileContent), objectInserter);
			
			Ref aggregateBranchHead = repo.getRef(aggregateBranchRefSpec + aggregateBranchName);
			
			RevCommit aggregateBranchCommit = rw.parseCommit(aggregateBranchHead.getObjectId());
			
			if (this.splitStyle.equals(SplitStyle.MERGE)) {
			
				createBranch(repo, objectInserter, aggregateBranchCommit, "aggregate", "split_"+aggregateBranchName, aggregateTreeId);
				
				getLog().info("Splitting Branch: " + currentBranchName);
				
				getLog().info("Split Branches: " + StringUtils.join(branchesCreatedSet.iterator(), ", "));
			
			}
			else if (this.splitStyle.equals(SplitStyle.TAG)) {
				
				String tagName = getTagName (aggregateTreeId);
				
				ObjectId commitId = createSplitCommit(aggregateBranchCommit, "aggregate", aggregateTreeId, objectInserter);
				
				createTag(tagName, rw.parseCommit(commitId), objectInserter);
			}
			else {
				getLog().error("invalid splitStyle: " + splitStyle);
			}
			
			objectReader.release();
			objectInserter.release();
			
			rw.release();
			
			repo.close();

		} catch (Exception e) {

			if (e instanceof MojoExecutionException)
				throw (MojoExecutionException) e;
			else if (e instanceof MojoFailureException)
				throw (MojoFailureException) e;
			else
				throw new MojoExecutionException(
						"SplitMojo failed unexpectantly: ", e);
		}

	}

	private String getTagName(ObjectId splitTreeId) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException, MojoFailureException {
		
		GitTreeNodeData splitTree = treeProcessor.extractExistingTreeData(splitTreeId, "");
		
		ObjectId pomBlobId = splitTree.find(repo, "pom.xml");
		
		String pomFileContents = new String (treeProcessor.getBlob(pomBlobId).getBytes());
		
		GAV moduleGav = pomUtils.getGAV(pomFileContents);

		if (moduleGav.getVersion().contains("-SNAPSHOT")) {
			throw new MojoFailureException("pom version of tree (" + splitTreeId.getName() + ") contains -SNAPSHOT ("+moduleGav.getVersion()+").  Tagging can only occur on non-snapshot versions.");
		}
		
		String pomVersionBasedTagName = MojoHelper.getInstance(this).getTagName(moduleGav);
		
		return pomVersionBasedTagName;
	}

	private String createFusionMavenPluginDataFileString(Repository repo,
			List mappings, final Map mappingModuleNameToCommitId) throws MojoFailureException, IOException {
		
		return ExternalModuleUtils.createFusionMavenPluginDataFileString(0L, new IBranchHeadProvider() {
			
			@Override
			public ObjectId getBranchHeadObjectId(String branchName) {
				/*
				 * Resolve through provided map.
				 */
				return mappingModuleNameToCommitId.get(branchName);
			}
		}, convertToExternalModules(repo, mappings, mappingModuleNameToCommitId), new LargeBranchNameProviderMapImpl());
	}

	private List convertToExternalModules(
			Repository repo, List mappings, MapmappingModuleNameToCommitId) throws IOException, MojoFailureException {
		
		
		List externals = new ArrayList<>(mappings.size());
		
		for (Mapping mapping : mappings) {
			
			ExternalModuleInfo external = new ExternalModuleInfo(mapping.getModule(), mapping.getBranchName());
			
			ObjectId mappingObjectId = mappingModuleNameToCommitId.get(mapping.getBranchName());
			
			if (mappingObjectId != null)
				external.setBranchHeadId(mappingObjectId);
			else {
				throw new MojoFailureException("Failed to determine the branch head for branch: " + mapping.getBranchName());
			}
			
			externals.add(external);
			
		}
		return externals;
	}

	private ObjectId createSplitCommit (RevCommit fusedCommit, String moduleName, ObjectId treeId, ObjectInserter objectInserter) throws IOException {
		
		Set parents = new HashSet<>();
		
		parents.add(fusedCommit.getId());
		
		ObjectId commitId = GitFusionUtils.commit(objectInserter, "jcaddel", "[email protected]", "Split out " + moduleName , treeId, parents);
		
		return commitId;
		
	}
	private Ref createBranch(Repository repo, ObjectInserter objectInserter, RevCommit parentCommit, String moduleName, String branchName, ObjectId treeId) throws IOException, BranchRefExistsException {
		
		ObjectId commitId = createSplitCommit(parentCommit, moduleName, treeId, objectInserter);
		
		Ref branchRef = GitRefUtils.createBranch(repo, Constants.R_HEADS + branchName, commitId, true);
		
		if (branchRef == null)
			getLog().warn("Unable to set branch : " + branchName + " to the split tree with id : " + commitId);
		
		return branchRef;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy