From 6f0fec874cd8e7c36d3697260853305c517aa919 Mon Sep 17 00:00:00 2001 From: Robert Hencke Date: Mon, 24 Oct 2016 20:00:39 -0400 Subject: [PATCH] Add support for Jenkins Pipeline. Before this commit, if you attempted to use the Stash pull-request builder plug in with Jenkins Pipeline, you would encounter an error. Ultimately, this error was due to the fact that this plug in require jobs to use the AbstractProject base class, but the Jenkins Pipeline job class (WorkflowJob) doesn't. The good news is that WorkflowJob and AbstractProject share a base class of Job, and that most functionality of this plug in can be made available to WorkflowJob refactoring uses of AbstractProject to uses of Job instead. For cases where this does not provide enough functionality, there are interfaces implemented in common by both (ParameterizedJobMixIn.ParameterizedJob, Queue.Task) that fill the gaps. The one remaining piece of functionality this commit does not extend to be available to Jenkins Pipeline is custom post-build comments, so a TODO to that effect was added. --- .../StashBuildListener.java | 16 +++---- .../StashBuildTrigger.java | 45 +++++++++++++------ .../stashpullrequestbuilder/StashBuilds.java | 32 ++++++------- .../StashPostBuildComment.java | 1 + .../StashPullRequestsBuilder.java | 16 +++---- .../StashRepository.java | 18 ++++---- 6 files changed, 73 insertions(+), 55 deletions(-) diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildListener.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildListener.java index afd5853b..06cd4a0d 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildListener.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildListener.java @@ -1,7 +1,7 @@ package stashpullrequestbuilder.stashpullrequestbuilder; import hudson.Extension; -import hudson.model.AbstractBuild; +import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; @@ -12,25 +12,25 @@ * Created by Nathan McCarthy */ @Extension -public class StashBuildListener extends RunListener { +public class StashBuildListener extends RunListener> { private static final Logger logger = Logger.getLogger(StashBuildTrigger.class.getName()); @Override - public void onStarted(AbstractBuild abstractBuild, TaskListener listener) { + public void onStarted(Run run, TaskListener listener) { logger.info("BuildListener onStarted called."); - StashBuildTrigger trigger = StashBuildTrigger.getTrigger(abstractBuild.getProject()); + StashBuildTrigger trigger = StashBuildTrigger.getTrigger(run.getParent()); if (trigger == null) { return; } - trigger.getBuilder().getBuilds().onStarted(abstractBuild); + trigger.getBuilder().getBuilds().onStarted(run); } @Override - public void onCompleted(AbstractBuild abstractBuild, @Nonnull TaskListener listener) { - StashBuildTrigger trigger = StashBuildTrigger.getTrigger(abstractBuild.getProject()); + public void onCompleted(Run run, @Nonnull TaskListener listener) { + StashBuildTrigger trigger = StashBuildTrigger.getTrigger(run.getParent()); if (trigger == null) { return; } - trigger.getBuilder().getBuilds().onCompleted(abstractBuild, listener); + trigger.getBuilder().getBuilds().onCompleted(run, listener); } } diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildTrigger.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildTrigger.java index ae8c7944..a91bf676 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildTrigger.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuildTrigger.java @@ -8,10 +8,11 @@ import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; -import hudson.model.AbstractProject; import hudson.model.Build; import hudson.model.Cause; +import hudson.model.CauseAction; import hudson.model.Item; +import hudson.model.Job; import hudson.model.ParameterDefinition; import hudson.model.ParameterValue; import hudson.model.ParametersAction; @@ -26,7 +27,9 @@ import hudson.triggers.TriggerDescriptor; import hudson.util.ListBoxModel; import jenkins.model.Jenkins; +import jenkins.model.ParameterizedJobMixIn; import net.sf.json.JSONObject; +import org.acegisecurity.Authentication; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; @@ -47,7 +50,7 @@ * Created by Nathan McCarthy */ @SuppressFBWarnings({"WMI_WRONG_MAP_ITERATOR", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"}) -public class StashBuildTrigger extends Trigger> { +public class StashBuildTrigger extends Trigger> { private static final Logger logger = Logger.getLogger(StashBuildTrigger.class.getName()); private final String projectPath; private final String cron; @@ -130,11 +133,15 @@ public String getcredentialsId() { } private StandardUsernamePasswordCredentials getCredentials() { + Authentication defaultAuth = null; + if (job instanceof Queue.Task) { + defaultAuth = Tasks.getDefaultAuthenticationOf((Queue.Task)this.job); + } return CredentialsMatchers.firstOrNull( CredentialsProvider.lookupCredentials( StandardUsernamePasswordCredentials.class, this.job, - Tasks.getDefaultAuthenticationOf(this.job), + defaultAuth, URIRequirementBuilder.fromUri(stashHost).build() ), CredentialsMatchers.allOf(CredentialsMatchers.withId(credentialsId))); @@ -189,21 +196,27 @@ public boolean isCancelOutdatedJobsEnabled() { } @Override - public void start(AbstractProject project, boolean newInstance) { + public void start(Job job, boolean newInstance) { try { this.stashPullRequestsBuilder = StashPullRequestsBuilder.getBuilder(); - this.stashPullRequestsBuilder.setProject(project); + this.stashPullRequestsBuilder.setJob(job); this.stashPullRequestsBuilder.setTrigger(this); this.stashPullRequestsBuilder.setupBuilder(); } catch(IllegalStateException e) { logger.log(Level.SEVERE, "Can't start trigger", e); return; } - super.start(project, newInstance); + super.start(job, newInstance); } - public static StashBuildTrigger getTrigger(AbstractProject project) { - Trigger trigger = project.getTrigger(StashBuildTrigger.class); + public static StashBuildTrigger getTrigger(Job job) { + if (!(job instanceof ParameterizedJobMixIn.ParameterizedJob)) { + return null; + } + + ParameterizedJobMixIn.ParameterizedJob pjob = (ParameterizedJobMixIn.ParameterizedJob) job; + + Trigger trigger = pjob.getTriggers().get(descriptor); return (StashBuildTrigger)trigger; } @@ -235,9 +248,13 @@ public QueueTaskFuture startJob(StashCause cause) { cancelPreviousJobsInQueueThatMatch(cause); abortRunningJobsThatMatch(cause); } - - return this.job.scheduleBuild2(0, cause, new ParametersAction(values)); - + + return new ParameterizedJobMixIn() { + @Override + protected Job asJob() { + return StashBuildTrigger.this.job; + } + }.scheduleBuild2(0, new ParametersAction(values), new CauseAction(cause)); } private void cancelPreviousJobsInQueueThatMatch(@Nonnull StashCause stashCause) { @@ -292,10 +309,10 @@ private List getDefaultParameters() { @Override public void run() { - if(this.getBuilder().getProject().isDisabled()) { - logger.info(format("Build Skip (%s).", getBuilder().getProject().getName())); + if(!this.getBuilder().getJob().isBuildable()) { + logger.info(format("Build Skip (%s).", getBuilder().getJob().getName())); } else { - logger.info(format("Build started (%s).", getBuilder().getProject().getName())); + logger.info(format("Build started (%s).", getBuilder().getJob().getName())); this.stashPullRequestsBuilder.run(); } this.getDescriptor().save(); diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuilds.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuilds.java index e7b5a0d6..ca015277 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuilds.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashBuilds.java @@ -1,8 +1,8 @@ package stashpullrequestbuilder.stashpullrequestbuilder; -import hudson.model.AbstractBuild; import hudson.model.Cause; import hudson.model.Result; +import hudson.model.Run; import hudson.model.TaskListener; import jenkins.model.JenkinsLocationConfiguration; @@ -23,46 +23,46 @@ public StashBuilds(StashBuildTrigger trigger, StashRepository repository) { this.repository = repository; } - public StashCause getCause(AbstractBuild build) { - Cause cause = build.getCause(StashCause.class); + public StashCause getCause(Run run) { + Cause cause = run.getCause(StashCause.class); if (cause == null || !(cause instanceof StashCause)) { return null; } return (StashCause) cause; } - public void onStarted(AbstractBuild build) { - StashCause cause = this.getCause(build); + public void onStarted(Run run) { + StashCause cause = this.getCause(run); if (cause == null) { return; } try { - build.setDescription(cause.getShortDescription()); + run.setDescription(cause.getShortDescription()); } catch (IOException e) { logger.log(Level.SEVERE, "Can't update build description", e); } } - public void onCompleted(AbstractBuild build, TaskListener listener) { - StashCause cause = this.getCause(build); + public void onCompleted(Run run, TaskListener listener) { + StashCause cause = this.getCause(run); if (cause == null) { return; } - Result result = build.getResult(); + Result result = run.getResult(); JenkinsLocationConfiguration globalConfig = new JenkinsLocationConfiguration(); String rootUrl = globalConfig.getUrl(); String buildUrl = ""; if (rootUrl == null) { - buildUrl = " PLEASE SET JENKINS ROOT URL FROM GLOBAL CONFIGURATION " + build.getUrl(); + buildUrl = " PLEASE SET JENKINS ROOT URL FROM GLOBAL CONFIGURATION " + run.getUrl(); } else { - buildUrl = rootUrl + build.getUrl(); + buildUrl = rootUrl + run.getUrl(); } repository.deletePullRequestComment(cause.getPullRequestId(), cause.getBuildStartCommentId()); String additionalComment = ""; - StashPostBuildCommentAction comments = build.getAction(StashPostBuildCommentAction.class); + StashPostBuildCommentAction comments = run.getAction(StashPostBuildCommentAction.class); if(comments != null) { String buildComment = result == Result.SUCCESS ? comments.getBuildSuccessfulComment() : comments.getBuildFailedComment(); @@ -70,14 +70,14 @@ public void onCompleted(AbstractBuild build, TaskListener listener) { additionalComment = "\n\n" + buildComment; } } - String duration = build.getDurationString(); + String duration = run.getDurationString(); repository.postFinishedComment(cause.getPullRequestId(), cause.getSourceCommitHash(), cause.getDestinationCommitHash(), result, buildUrl, - build.getNumber(), additionalComment, duration); + run.getNumber(), additionalComment, duration); //Merge PR - StashBuildTrigger trig = StashBuildTrigger.getTrigger(build.getProject()); - if(trig.getMergeOnSuccess() && build.getResult() == Result.SUCCESS) { + StashBuildTrigger trig = StashBuildTrigger.getTrigger(run.getParent()); + if(trig.getMergeOnSuccess() && run.getResult() == Result.SUCCESS) { boolean mergeStat = repository.mergePullRequest(cause.getPullRequestId(), cause.getPullRequestVersion()); if(mergeStat == true) { diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPostBuildComment.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPostBuildComment.java index 6182b4e6..a3f00e19 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPostBuildComment.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPostBuildComment.java @@ -18,6 +18,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +// TODO: Make available in Jenkins Pipeline public class StashPostBuildComment extends Notifier { private static final Logger logger = Logger.getLogger(StashBuildTrigger.class.getName()); private String buildSuccessfulComment; diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPullRequestsBuilder.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPullRequestsBuilder.java index 8d950b1d..fbe06870 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPullRequestsBuilder.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashPullRequestsBuilder.java @@ -2,8 +2,8 @@ import static java.lang.String.format; +import hudson.model.Job; import stashpullrequestbuilder.stashpullrequestbuilder.stash.StashPullRequestResponseValue; -import hudson.model.AbstractProject; import java.util.Collection; import java.util.logging.Logger; @@ -13,7 +13,7 @@ */ public class StashPullRequestsBuilder { private static final Logger logger = Logger.getLogger(StashBuildTrigger.class.getName()); - private AbstractProject project; + private Job job; private StashBuildTrigger trigger; private StashRepository repository; private StashBuilds builds; @@ -27,14 +27,14 @@ public void stop() { } public void run() { - logger.info(format("Build Start (%s).", project.getName())); + logger.info(format("Build Start (%s).", job.getName())); this.repository.init(); Collection targetPullRequests = this.repository.getTargetPullRequests(); this.repository.addFutureBuildTasks(targetPullRequests); } public StashPullRequestsBuilder setupBuilder() { - if (this.project == null || this.trigger == null) { + if (this.job == null || this.trigger == null) { throw new IllegalStateException(); } this.repository = new StashRepository(this.trigger.getProjectPath(), this); @@ -42,16 +42,16 @@ public StashPullRequestsBuilder setupBuilder() { return this; } - public void setProject(AbstractProject project) { - this.project = project; + public void setJob(Job job) { + this.job = job; } public void setTrigger(StashBuildTrigger trigger) { this.trigger = trigger; } - public AbstractProject getProject() { - return this.project; + public Job getJob() { + return this.job; } public StashBuildTrigger getTrigger() { diff --git a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashRepository.java b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashRepository.java index 50664064..ca9927df 100644 --- a/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashRepository.java +++ b/src/main/java/stashpullrequestbuilder/stashpullrequestbuilder/StashRepository.java @@ -68,7 +68,7 @@ public void init() { } public Collection getTargetPullRequests() { - logger.info(format("Fetch PullRequests (%s).", builder.getProject().getName())); + logger.info(format("Fetch PullRequests (%s).", builder.getJob().getName())); List pullRequests = client.getPullRequests(); List targetPullRequests = new ArrayList(); for(StashPullRequestResponseValue pullRequest : pullRequests) { @@ -80,11 +80,11 @@ public Collection getTargetPullRequests() { } public String postBuildStartCommentTo(StashPullRequestResponseValue pullRequest) { - String sourceCommit = pullRequest.getFromRef().getLatestCommit(); - String destinationCommit = pullRequest.getToRef().getLatestCommit(); - String comment = format(BUILD_START_MARKER, builder.getProject().getDisplayName(), sourceCommit, destinationCommit); - StashPullRequestComment commentResponse = this.client.postPullRequestComment(pullRequest.getId(), comment); - return commentResponse.getCommentId().toString(); + String sourceCommit = pullRequest.getFromRef().getLatestCommit(); + String destinationCommit = pullRequest.getToRef().getLatestCommit(); + String comment = format(BUILD_START_MARKER, builder.getJob().getDisplayName(), sourceCommit, destinationCommit); + StashPullRequestComment commentResponse = this.client.postPullRequestComment(pullRequest.getId(), comment); + return commentResponse.getCommentId().toString(); } public static AbstractMap.SimpleEntry getParameter(String content){ @@ -192,7 +192,7 @@ private String getMessageForBuildResult(Result result) { public void postFinishedComment(String pullRequestId, String sourceCommit, String destinationCommit, Result buildResult, String buildUrl, int buildNumber, String additionalComment, String duration) { String message = getMessageForBuildResult(buildResult); - String comment = format(BUILD_FINISH_SENTENCE, builder.getProject().getDisplayName(), sourceCommit, destinationCommit, message, buildUrl, buildNumber, duration); + String comment = format(BUILD_FINISH_SENTENCE, builder.getJob().getDisplayName(), sourceCommit, destinationCommit, message, buildUrl, buildNumber, duration); comment = comment.concat(additionalComment); @@ -233,7 +233,7 @@ private void deletePreviousBuildFinishedComments(StashPullRequestResponseValue p continue; } - String project_build_finished = format(BUILD_FINISH_REGEX, builder.getProject().getDisplayName()); + String project_build_finished = format(BUILD_FINISH_REGEX, builder.getJob().getDisplayName()); Matcher finishMatcher = Pattern.compile(project_build_finished, Pattern.CASE_INSENSITIVE).matcher(content); if (finishMatcher.find()) { @@ -289,7 +289,7 @@ private boolean isBuildTarget(StashPullRequestResponseValue pullRequest) { } //These will match any start or finish message -- need to check commits - String escapedBuildName = Pattern.quote(builder.getProject().getDisplayName()); + String escapedBuildName = Pattern.quote(builder.getJob().getDisplayName()); String project_build_start = String.format(BUILD_START_REGEX, escapedBuildName); String project_build_finished = String.format(BUILD_FINISH_REGEX, escapedBuildName); Matcher startMatcher = Pattern.compile(project_build_start, Pattern.CASE_INSENSITIVE).matcher(content);