package org.paylogic.jenkins.advancedscm.backends; import hudson.EnvVars; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.plugins.git.GitException; import hudson.plugins.git.GitSCM; import hudson.plugins.git.extensions.GitSCMExtension; import lombok.extern.java.Log; import org.apache.tools.ant.taskdefs.email.EmailAddress; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.transport.URIish; import org.jenkinsci.plugins.gitclient.GitClient; import org.paylogic.jenkins.advancedscm.Branch; import org.paylogic.jenkins.advancedscm.backends.helpers.AdvancedCliGit; import org.paylogic.jenkins.advancedscm.exceptions.AdvancedSCMException; import org.paylogic.jenkins.upmerge.releasebranch.ReleaseBranch; import org.paylogic.jenkins.upmerge.releasebranch.ReleaseBranchImpl; import org.paylogic.jenkins.upmerge.releasebranch.ReleaseBranchInvalidException; import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; /** * Mercurial Implementation of AdvancedSCMManager */ @Log public class GitBackend extends BaseBackend { private final AbstractBuild build; private final Launcher launcher; private final BuildListener listener; private final GitSCM scm; private final AdvancedCliGit git; public GitBackend(AbstractBuild build, Launcher launcher, BuildListener listener, GitSCM scm) throws Exception { this.build = build; this.launcher = launcher; this.listener = listener; this.scm = scm; FilePath path = build.getWorkspace(); EnvVars environment = build.getEnvironment(listener); for (GitSCMExtension ext : scm.getExtensions()) { FilePath r = ext.getWorkingDirectory(scm, build.getParent(), path, environment, listener); if (r!=null) { path = r; } } this.git = new AdvancedCliGit( scm, launcher, build.getBuiltOn(), new File(path.absolutize().getRemote()), listener, build.getEnvironment(listener)); this.repoPath = git.getWorkTree(); } /** * Get branches from command line output, * and put them in a List with Branches so it's nice to work with. * @param all : get all or only open branches * * @return List of Branches */ public List<Branch> getBranches(boolean all) throws AdvancedSCMException { List<Branch> result = new ArrayList<Branch>(); try { for (hudson.plugins.git.Branch branch : git.getRemoteBranches()) { String [] branchNameParts = branch.getName().split("/"); result.add(new Branch(branchNameParts[branchNameParts.length - 1], null, branch.getSHA1String())); } } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } return result; } /** * Get local branches from command line output, * and put them in a List with Branches so it's nice to work with. * * @return List of Branches */ public List<Branch> getLocalBranches() throws AdvancedSCMException { List<Branch> result = new ArrayList<Branch>(); try { for (hudson.plugins.git.Branch branch : git.getBranches()) { if (!branch.getName().contains("/")) { result.add(new Branch(branch.getName(), null, branch.getSHA1String())); } } } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } return result; } /** * Get branches from command line output, * and put them in a List so it's nice to work with. * @return List of String */ public List<String> getLocalBranchNames() throws AdvancedSCMException { List<String> list = new ArrayList<String>(); for (Branch branch: this.getLocalBranches()) { list.add(branch.getBranchName()); } return list; } /** * Get the current branch name in the workspace. * * @return String with branch name in it. */ public String getBranch() throws AdvancedSCMException { try { return git.launchCommand("rev-parse", "--abbrev-ref", "HEAD").trim(); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } /** * Updates workspace to given revision/branch. * * @param revision : String with revision, hash or branchname to update to. */ public void update(String revision) throws AdvancedSCMException { if (!revision.isEmpty() && !getLocalBranchNames().contains(revision)) { try { git.launchCommand("checkout", "-b", revision, "--track", "origin/" + revision); } catch (Exception exception) { throw new AdvancedSCMException(exception.toString()); } } else { try { git.checkout().ref(revision).execute(); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } } public void updateClean(String revision) throws AdvancedSCMException { update(revision); clean(); } public void stripLocal() throws AdvancedSCMException { clean(); List<String> repoBranchNames = getLocalBranchNames(); for (String branch: repoBranchNames) { git.checkout().branch(branch); clean(branch); try { git.launchCommand("checkout", "-f"); } catch (InterruptedException exception) { } } } public void clean() throws AdvancedSCMException { try { git.clean(); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } public void clean(String revision) throws AdvancedSCMException { update(revision); try { git.launchCommand("reset", "--hard", "origin/" + revision); } catch (GitException exception) { } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } clean(); } public void mergeWorkspaceWith( String revision, String updateTo) throws AdvancedSCMException { try { ObjectId rev; if (updateTo != null) { update(updateTo); rev = git.revParse(revision); } else { try { rev = git.revParse("feature/" + revision); } catch (GitException exception) { try { rev = git.revParse("origin/" + revision); } catch (GitException exc) { rev = git.revParse(revision); } } } EmailAddress address = new EmailAddress("dummy <dummy@foo.bar>"); git.setAuthor(address.getName(), address.getName()); git.setCommitter(address.getName(), address.getName()); git.launchCommand("merge", "--no-commit", "--no-ff", rev.getName()); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } public void commit(String message, String username) throws AdvancedSCMException { try { EmailAddress address = new EmailAddress(username); git.setAuthor(address.getName(), address.getAddress()); git.setCommitter(address.getName(), address.getAddress()); git.commit(message); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } /** * Merge possible current branch's heads. Not actual for git backend. * @param message : String commit message * @param username : String commit user name (with email) */ public void mergeHeads(String message, String username) throws AdvancedSCMException { } public void push(String... branchNames) throws AdvancedSCMException { List<String> repoBranchNames = getLocalBranchNames(); try { for (String branch: branchNames) { if (repoBranchNames.contains(branch)) { git.push().to(new URIish(git.getRemoteUrl("origin"))).ref(branch).execute(); } } } catch (URISyntaxException exception) { throw new AdvancedSCMException(exception.toString()); } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } public void pull() throws AdvancedSCMException { pull(null, "master"); } public void pull(String remote) throws AdvancedSCMException { pull(remote, "master"); } /** * Close given branch. Nothing has to be done in git backend. * @param branch: String branch name. * @param message : String with message to give this commit. * @param username : String commit user name (with email) */ public void closeBranch(String branch, String message, String username) { return; } public void pull(String remote, String branch) throws AdvancedSCMException { try { if (remote == null || remote.isEmpty()) { remote = git.getRemoteUrl("origin"); } try { git.launchCommand("remote", "rm", "feature"); } catch (GitException exception) { // when remote is new, can fail, but it's intentional } git.launchCommand("remote", "add", "feature", remote); try { git.launchCommand("fetch", "feature", branch); } catch (GitException exception) { // can be a new local branch, so can fail, but it's intentional } } catch (InterruptedException exception) { throw new AdvancedSCMException(exception.toString()); } } public ReleaseBranch getReleaseBranch(String branch) throws ReleaseBranchInvalidException { return new ReleaseBranchImpl(branch, "master"); } public ReleaseBranch createReleaseBranch( String branch, String releaseFilePath, String releaseFileContent, String message, String username) throws AdvancedSCMException, ReleaseBranchInvalidException { { try { this.update("master"); git.checkout("HEAD", branch); if (releaseFilePath != null && !releaseFilePath.isEmpty() && releaseFileContent != null && !releaseFileContent.isEmpty()) { this.createFile(releaseFilePath, releaseFileContent); git.add(releaseFilePath); EmailAddress address = new EmailAddress(username); git.setAuthor(address.getName(), address.getName()); git.setCommitter(address.getName(), address.getName()); git.commit(message); } return getReleaseBranch(branch); } catch (Exception e) { throw new AdvancedSCMException(e.getMessage()); } } } }