package org.paylogic.jenkins.gatekeeper; import hudson.EnvVars; import hudson.Extension; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; import hudson.util.FormValidation; import lombok.extern.java.Log; import org.apache.commons.lang.StringUtils; import org.jenkinsci.plugins.envinject.EnvInjectBuilderContributionAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.paylogic.jenkins.LogMessageSearcher; import org.paylogic.jenkins.advancedscm.AdvancedSCMManager; import org.paylogic.jenkins.advancedscm.SCMManagerFactory; import org.paylogic.jenkins.advancedscm.exceptions.AdvancedSCMException; import org.paylogic.jenkins.advancedscm.exceptions.MergeConflictException; import org.paylogic.jenkins.upmerge.releasebranch.ReleaseBranchImpl; import org.paylogic.jenkins.upmerge.releasebranch.ReleaseBranchInvalidException; import com.github.jknack.handlebars.Handlebars; import com.github.jknack.handlebars.Template; import com.github.jknack.handlebars.Context; import java.io.IOException; import java.io.PrintStream; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; /** * Main extension of the GatekeeperPlugin. Does Gatekeeper merge. */ @Log public class GatekeeperMerge extends Builder { public final String releaseFilePath; public final String releaseFileContentTemplate; public final String commitUsername; @DataBoundConstructor public GatekeeperMerge(String commitUsername, String releaseFilePath, String releaseFileContentTemplate) { this.commitUsername = commitUsername; this.releaseFilePath = releaseFilePath; this.releaseFileContentTemplate = releaseFileContentTemplate; } @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) { PrintStream l = listener.getLogger(); l.println("----------------------------------------------------------"); l.println("------------------- Gatekeeper merge ---------------------"); l.println("----------------------------------------------------------"); try { return this.doPerform(build, launcher, listener); } catch (MergeConflictException e) { log.log(Level.SEVERE, "Exception during Gatekeeeper merge.", e); l.append("Exception occured, build aborting...\n"); LogMessageSearcher.logMessage(listener, "Merge conflict occured when Gatekeeper merging, " + "please check the Jenkins buildlog for conflicting files, resolve them, " + "and reassign this case to Mergekeepers."); return false; } catch (Exception e) { log.log(Level.SEVERE, "Exception during Gatekeeeper merge.", e); l.append("Exception occured, build aborting...\n"); LogMessageSearcher.logMessage(listener, e.toString()); return false; } } private boolean doPerform(AbstractBuild build, Launcher launcher, BuildListener listener) throws Exception { /* Set up enviroment and resolve some variables. */ EnvVars envVars = build.getEnvironment(listener); String featureBranch = envVars.get("FEATURE_BRANCH", ""); String targetBranch = envVars.get("TARGET_BRANCH", ""); String featureRepoUrl = envVars.get("REPO_URL", ""); String okRevision = envVars.get("APPROVED_REVISION", ""); int usableCaseId = 0; String repo_path = envVars.get("REPO_PATH", ""); AdvancedSCMManager amm = SCMManagerFactory.getManager(build, launcher, listener); amm.stripLocal(); listener.getLogger().append("Ensuring target release branch " + targetBranch + ".\n"); ensureReleaseBranch(amm, targetBranch); /* Actual Gatekeepering logic. Seperated to work differently when Rietveld support is active. */ boolean runNormalMerge = okRevision.isEmpty(); if (!runNormalMerge) { // Use Rietveld support. listener.getLogger().append("Trying to merge with revision " + okRevision + ".\n"); listener.getLogger().append("Which should be in repo " + featureRepoUrl + ", which we will pull.\n"); /* Actual gatekeepering commands.*/ amm.pull(featureRepoUrl, featureBranch); amm.updateClean(targetBranch); amm.mergeWorkspaceWith(okRevision, null); LogMessageSearcher.logMessage(listener, "Gatekeeper merge merged " + okRevision + " from " + featureRepoUrl + " to " + targetBranch + "."); } else { amm.pull(featureRepoUrl, featureBranch); amm.updateClean(targetBranch); amm.mergeWorkspaceWith(featureBranch, null); LogMessageSearcher.logMessage(listener, "Gatekeeper merge merged " + featureBranch + " to " + targetBranch + "."); } return true; } private void ensureReleaseBranch(AdvancedSCMManager amm, String targetBranch) throws AdvancedSCMException, ReleaseBranchInvalidException{ String releaseFileContent = null; if (releaseFileContentTemplate != null && !releaseFileContentTemplate.isEmpty() && releaseFilePath != null && !releaseFilePath.isEmpty()) { Handlebars handlebars = new Handlebars(); Context templateContext = Context.newContext(null); templateContext.data("release", amm.getReleaseBranch(targetBranch).getReleaseName()); Template mustacheTemplate; try { mustacheTemplate = handlebars.compileInline(releaseFileContentTemplate); releaseFileContent = mustacheTemplate.apply(templateContext); } catch (IOException e) { throw new AdvancedSCMException("Error rendering release file content template"); } } amm.ensureReleaseBranch( targetBranch, releaseFilePath, releaseFileContent, "[Jenkins Integration Merge] " + targetBranch + " release", commitUsername); } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl)super.getDescriptor(); } @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Builder> { @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return true; } @Override public String getDisplayName() { return "Perform Gatekeeper merge."; } public FormValidation doCheckCommitUsername(@QueryParameter String value) { if (!value.isEmpty()) { return FormValidation.ok(); } else { return FormValidation.error("Required field"); } } } }