diff options
Diffstat (limited to 'src')
7 files changed, 172 insertions, 94 deletions
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java index 95931ad..9e067e7 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java @@ -2,8 +2,12 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; import hudson.Extension; import hudson.model.AbstractBuild; +import hudson.model.Job; +import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; +import hudson.triggers.Trigger; +import jenkins.model.ParameterizedJobMixIn; import javax.annotation.Nonnull; import java.util.logging.Logger; @@ -12,25 +16,43 @@ import java.util.logging.Logger; * Created by nishio */ @Extension -public class BitbucketBuildListener extends RunListener<AbstractBuild> { - private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); +public class BitbucketBuildListener extends RunListener<Run<?, ?>> { + private static final Logger logger = Logger.getLogger(BitbucketBuildListener.class.getName()); @Override - public void onStarted(AbstractBuild abstractBuild, TaskListener listener) { - logger.info("BuildListener onStarted called."); - BitbucketBuildTrigger trigger = BitbucketBuildTrigger.getTrigger(abstractBuild.getProject()); - if (trigger == null) { - return; + public void onStarted(Run r, TaskListener listener) { + logger.info("BitbucketBuildListener onStarted called."); + BitbucketBuilds builds = builds(r); + if (builds != null) { + builds.onStarted((BitbucketCause) r.getCause(BitbucketCause.class), r); } - trigger.getBuilder().getBuilds().onStarted(abstractBuild); } @Override - public void onCompleted(AbstractBuild abstractBuild, @Nonnull TaskListener listener) { - BitbucketBuildTrigger trigger = BitbucketBuildTrigger.getTrigger(abstractBuild.getProject()); - if (trigger == null) { - return; + public void onCompleted(Run r, @Nonnull TaskListener listener) { + logger.info("BitbucketBuildListener onCompleted called."); + BitbucketBuilds builds = builds(r); + if (builds != null) { + builds.onCompleted((BitbucketCause) r.getCause(BitbucketCause.class), r.getResult(), r.getUrl()); } - trigger.getBuilder().getBuilds().onCompleted(abstractBuild); } + + private BitbucketBuilds builds(Run<?, ?> r) { + BitbucketBuildTrigger trigger = null; + if (r instanceof AbstractBuild) { + trigger = BitbucketBuildTrigger.getTrigger(((AbstractBuild) r).getProject()); + } else { + Job job = r.getParent(); + if (job instanceof ParameterizedJobMixIn.ParameterizedJob) { + + for (Trigger<?> t : ((ParameterizedJobMixIn.ParameterizedJob) job).getTriggers().values()) { + if (t instanceof BitbucketBuildTrigger) { + trigger = (BitbucketBuildTrigger) t; + } + } + } + } + return trigger == null ? null : trigger.getBuilder().getBuilds(); + } + } diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java index 81b5603..34e12b2 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java @@ -7,27 +7,34 @@ import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredenti import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials; import hudson.Extension; import hudson.model.*; +import hudson.model.Queue; import hudson.model.queue.QueueTaskFuture; import hudson.plugins.git.RevisionParameterAction; import hudson.triggers.Trigger; import hudson.triggers.TriggerDescriptor; import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; +import jenkins.model.ParameterizedJobMixIn; import net.sf.json.JSONObject; +import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf; /** * Created by nishio */ -public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { +public class BitbucketBuildTrigger extends Trigger<Job<?, ?>> { private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); private final String projectPath; private final String cron; @@ -43,6 +50,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { private final String ciSkipPhrases; private final boolean checkDestinationCommit; private final boolean approveIfSuccess; + private final boolean cancelOutdatedJobs; private final String commentTrigger; transient private BitbucketPullRequestsBuilder bitbucketPullRequestsBuilder; @@ -52,38 +60,40 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { @DataBoundConstructor public BitbucketBuildTrigger( - String projectPath, - String cron, - String credentialsId, - String username, - String password, - String repositoryOwner, - String repositoryName, - String branchesFilter, - boolean branchesFilterBySCMIncludes, - String ciKey, - String ciName, - String ciSkipPhrases, - boolean checkDestinationCommit, - boolean approveIfSuccess, - String commentTrigger - ) throws ANTLRException { - super(cron); - this.projectPath = projectPath; - this.cron = cron; - this.credentialsId = credentialsId; - this.username = username; - this.password = password; - this.repositoryOwner = repositoryOwner; - this.repositoryName = repositoryName; - this.branchesFilter = branchesFilter; - this.branchesFilterBySCMIncludes = branchesFilterBySCMIncludes; - this.ciKey = ciKey; - this.ciName = ciName; - this.ciSkipPhrases = ciSkipPhrases; - this.checkDestinationCommit = checkDestinationCommit; - this.approveIfSuccess = approveIfSuccess; - this.commentTrigger = commentTrigger; + String projectPath, + String cron, + String credentialsId, + String username, + String password, + String repositoryOwner, + String repositoryName, + String branchesFilter, + boolean branchesFilterBySCMIncludes, + String ciKey, + String ciName, + String ciSkipPhrases, + boolean checkDestinationCommit, + boolean approveIfSuccess, + boolean cancelOutdatedJobs, + String commentTrigger + ) throws ANTLRException { + super(cron); + this.projectPath = projectPath; + this.cron = cron; + this.credentialsId = credentialsId; + this.username = username; + this.password = password; + this.repositoryOwner = repositoryOwner; + this.repositoryName = repositoryName; + this.branchesFilter = branchesFilter; + this.branchesFilterBySCMIncludes = branchesFilterBySCMIncludes; + this.ciKey = ciKey; + this.ciName = ciName; + this.ciSkipPhrases = ciSkipPhrases; + this.checkDestinationCommit = checkDestinationCommit; + this.approveIfSuccess = approveIfSuccess; + this.cancelOutdatedJobs = cancelOutdatedJobs; + this.commentTrigger = commentTrigger; } public String getProjectPath() { @@ -139,17 +149,21 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { } public boolean getApproveIfSuccess() { - return approveIfSuccess; + return approveIfSuccess; + } + + public boolean getCancelOutdatedJobs() { + return cancelOutdatedJobs; } /** * @return a phrase that when entered in a comment will trigger a new build */ public String getCommentTrigger() { - return commentTrigger; + return commentTrigger; } @Override - public void start(AbstractProject<?, ?> project, boolean newInstance) { + public void start(Job<?, ?> project, boolean newInstance) { try { this.bitbucketPullRequestsBuilder = BitbucketPullRequestsBuilder.getBuilder(); this.bitbucketPullRequestsBuilder.setProject(project); @@ -171,9 +185,67 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { return this.bitbucketPullRequestsBuilder; } + private ParameterizedJobMixIn retrieveScheduleJob(final Job<?, ?> job) { + // TODO 1.621+ use standard method + return new ParameterizedJobMixIn() { + @Override + protected Job asJob() { + return job; + } + }; + } + public QueueTaskFuture<?> startJob(BitbucketCause cause) { Map<String, ParameterValue> values = this.getDefaultParameters(); - return this.job.scheduleBuild2(0, cause, new ParametersAction(new ArrayList(values.values())), new RevisionParameterAction(cause.getSourceCommitHash())); + + if (getCancelOutdatedJobs()) { + cancelPreviousJobsInQueueThatMatch(cause); + abortRunningJobsThatMatch(cause); + } + + return retrieveScheduleJob(this.job).scheduleBuild2(0, + new CauseAction(cause), + new ParametersAction(new ArrayList(values.values())), + new RevisionParameterAction(cause.getSourceCommitHash())); + } + + private void cancelPreviousJobsInQueueThatMatch(@Nonnull BitbucketCause bitbucketCause) { + logger.fine("Looking for queued jobs that match PR ID: " + bitbucketCause.getPullRequestId()); + Queue queue = Jenkins.getInstance().getQueue(); + for (Queue.Item item : queue.getItems()) { + if (hasCauseFromTheSamePullRequest(item.getCauses(), bitbucketCause)) { + logger.info("Canceling item in queue: " + item); + queue.cancel(item); + } + } + } + + private void abortRunningJobsThatMatch(@Nonnull BitbucketCause bitbucketCause) { + logger.fine("Looking for running jobs that match PR ID: " + bitbucketCause.getPullRequestId()); + for (Object o : job.getBuilds()) { + if (o instanceof Build) { + Build build = (Build) o; + if (build.isBuilding() && hasCauseFromTheSamePullRequest(build.getCauses(), bitbucketCause)) { + logger.info("Aborting build: " + build + " since PR is outdated"); + build.getExecutor().interrupt(Result.ABORTED); + } + } + } + } + + private boolean hasCauseFromTheSamePullRequest(@Nullable List<Cause> causes, @Nullable BitbucketCause pullRequestCause) { + if (causes != null && pullRequestCause != null) { + for (Cause cause : causes) { + if (cause instanceof BitbucketCause) { + BitbucketCause sc = (BitbucketCause) cause; + if (StringUtils.equals(sc.getPullRequestId(), pullRequestCause.getPullRequestId()) && + StringUtils.equals(sc.getRepositoryName(), pullRequestCause.getRepositoryName())) { + return true; + } + } + } + } + return false; } private Map<String, ParameterValue> getDefaultParameters() { @@ -190,12 +262,13 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { @Override public void run() { - if(this.getBuilder().getProject().isDisabled()) { - logger.info("Build Skip."); - } else { - this.bitbucketPullRequestsBuilder.run(); - } - this.getDescriptor().save(); + Job<?,?> project = this.getBuilder().getProject(); + if (project instanceof AbstractProject && ((AbstractProject)project).isDisabled()) { + logger.info("Build Skip."); + } else { + this.bitbucketPullRequestsBuilder.run(); + this.getDescriptor().save(); + } } @Override @@ -210,7 +283,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> { @Override public boolean isApplicable(Item item) { - return true; + return item instanceof Job && item instanceof ParameterizedJobMixIn.ParameterizedJob; } @Override diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java index ea8e892..216287d 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java @@ -1,10 +1,7 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState; -import hudson.model.AbstractBuild; -import hudson.model.Cause; -import hudson.model.Result; -import jenkins.model.Jenkins; +import hudson.model.*; import jenkins.model.JenkinsLocationConfiguration; import java.io.IOException; @@ -24,16 +21,7 @@ public class BitbucketBuilds { this.repository = repository; } - public BitbucketCause getCause(AbstractBuild build) { - Cause cause = build.getCause(BitbucketCause.class); - if (cause == null || !(cause instanceof BitbucketCause)) { - return null; - } - return (BitbucketCause) cause; - } - - public void onStarted(AbstractBuild build) { - BitbucketCause cause = this.getCause(build); + void onStarted(BitbucketCause cause, Run<?, ?> build) { if (cause == null) { return; } @@ -44,24 +32,21 @@ public class BitbucketBuilds { } } - public void onCompleted(AbstractBuild build) { - BitbucketCause cause = this.getCause(build); + void onCompleted(BitbucketCause cause, Result result, String buildUrl) { if (cause == null) { return; } - Result result = build.getResult(); JenkinsLocationConfiguration globalConfig = new JenkinsLocationConfiguration(); String rootUrl = globalConfig.getUrl(); - String buildUrl = ""; if (rootUrl == null) { logger.warning("PLEASE SET JENKINS ROOT URL IN GLOBAL CONFIGURATION FOR BUILD STATE REPORTING"); } else { - buildUrl = rootUrl + build.getUrl(); + buildUrl = rootUrl + buildUrl; BuildState state = result == Result.SUCCESS ? BuildState.SUCCESSFUL : BuildState.FAILED; repository.setBuildStatus(cause, state, buildUrl); } - if ( this.trigger.getApproveIfSuccess() && result == Result.SUCCESS ) { + if (this.trigger.getApproveIfSuccess() && result == Result.SUCCESS) { this.repository.postPullRequestApproval(cause.getPullRequestId()); } } diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java index d337796..42fe562 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java @@ -1,7 +1,6 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest; -import hudson.model.AbstractProject; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; @@ -11,6 +10,7 @@ import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; +import hudson.model.Job; import org.apache.commons.codec.binary.Hex; /** @@ -18,7 +18,7 @@ import org.apache.commons.codec.binary.Hex; */ public class BitbucketPullRequestsBuilder { private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); - private AbstractProject<?, ?> project; + private Job<?, ?> project; private BitbucketBuildTrigger trigger; private BitbucketRepository repository; private BitbucketBuilds builds; @@ -47,7 +47,7 @@ public class BitbucketPullRequestsBuilder { return this; } - public void setProject(AbstractProject<?, ?> project) { + public void setProject(Job<?, ?> project) { this.project = project; } @@ -55,7 +55,7 @@ public class BitbucketPullRequestsBuilder { this.trigger = trigger; } - public AbstractProject<?, ?> getProject() { + public Job<?, ?> getProject() { return this.project; } @@ -69,12 +69,11 @@ public class BitbucketPullRequestsBuilder { return new String(Hex.encodeHex(MD5.digest(this.project.getFullName().getBytes("UTF-8")))); } catch (NoSuchAlgorithmException exc) { logger.log(Level.WARNING, "Failed to produce hash", exc); - exc.printStackTrace(); } catch (UnsupportedEncodingException exc) { logger.log(Level.WARNING, "Failed to produce hash", exc); - exc.printStackTrace(); } return this.project.getFullName(); + } public BitbucketBuildTrigger getTrigger() { diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java index 759c696..0c87478 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java @@ -119,10 +119,8 @@ public class ApiClient { return new String(Hex.encodeHex(SHA1.digest(computedKey.getBytes("UTF-8")))); } catch(NoSuchAlgorithmException e) { logger.log(Level.WARNING, "Failed to create hash provider", e); - e.printStackTrace(); } catch (UnsupportedEncodingException e) { logger.log(Level.WARNING, "Failed to create hash provider", e); - e.printStackTrace(); } } return (computedKey.length() <= MAX_KEY_SIZE_BB_API) ? computedKey : computedKey.substring(0, MAX_KEY_SIZE_BB_API); @@ -173,7 +171,6 @@ public class ApiClient { new NameValuePair[]{}), Pullrequest.Participant.class); } catch (IOException e) { logger.log(Level.WARNING, "Invalid pull request approval response.", e); - e.printStackTrace(); } return null; } @@ -186,7 +183,6 @@ public class ApiClient { return parse(post(v1("/pullrequests/" + pullRequestId + "/comments"), data), new TypeReference<Pullrequest.Comment>() {}); } catch(Exception e) { logger.log(Level.WARNING, "Invalid pull request comment response.", e); - e.printStackTrace(); } return null; } @@ -203,7 +199,6 @@ public class ApiClient { } while (url != null); } catch (Exception e) { logger.log(Level.WARNING, "invalid response.", e); - e.printStackTrace(); } return values; } @@ -255,10 +250,8 @@ public class ApiClient { return req.getResponseBodyAsString(); } catch (HttpException e) { logger.log(Level.WARNING, "Failed to send request.", e); - e.printStackTrace(); } catch (IOException e) { logger.log(Level.WARNING, "Failed to send request.", e); - e.printStackTrace(); } finally { req.releaseConnection(); } diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly index 5f7c37c..97e5d5c 100644 --- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly @@ -38,6 +38,9 @@ <f:entry title="Approve if build success?" field="approveIfSuccess"> <f:checkbox /> </f:entry> + <f:entry title="Cancel outdated jobs?" field="cancelOutdatedJobs"> + <f:checkbox default="false"/> + </f:entry> <f:entry title="Comment phrase to trigger build" field="commentTrigger"> <f:textbox default="test this please"/> </f:entry> diff --git a/src/test/java/BitbucketBuildRepositoryTest.java b/src/test/java/BitbucketBuildRepositoryTest.java index 7a338a7..f076c4d 100644 --- a/src/test/java/BitbucketBuildRepositoryTest.java +++ b/src/test/java/BitbucketBuildRepositoryTest.java @@ -119,7 +119,7 @@ public class BitbucketBuildRepositoryTest { "", "", "", true, true, - BitbucketRepository.DEFAULT_COMMENT_TRIGGER + false, BitbucketRepository.DEFAULT_COMMENT_TRIGGER ); BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class); @@ -149,7 +149,8 @@ public class BitbucketBuildRepositoryTest { "", true, "", "", "", true, - true, BitbucketRepository.DEFAULT_COMMENT_TRIGGER + true, + false, BitbucketRepository.DEFAULT_COMMENT_TRIGGER ); BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class); @@ -203,7 +204,8 @@ public class BitbucketBuildRepositoryTest { "", true, "jenkins", "Jenkins", "", true, - true, BitbucketRepository.DEFAULT_COMMENT_TRIGGER + true, + false, BitbucketRepository.DEFAULT_COMMENT_TRIGGER ); BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class); @@ -249,7 +251,8 @@ public class BitbucketBuildRepositoryTest { "", true, "jenkins-too-long-ci-key", "Jenkins", "", true, - true, BitbucketRepository.DEFAULT_COMMENT_TRIGGER + true, + false, BitbucketRepository.DEFAULT_COMMENT_TRIGGER ); final MessageDigest MD5 = MessageDigest.getInstance("MD5"); |