Skip to content

Commit

Permalink
Merge pull request nishio-dens#9 from xob/cancel-outdated
Browse files Browse the repository at this point in the history
Add option to cancel outdated builds when a pull request is updated
  • Loading branch information
damovsky committed Feb 18, 2017
2 parents 4d68c41 + a6374a4 commit 82acdec
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,26 @@
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 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;

Expand All @@ -43,11 +49,12 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
private final String ciSkipPhrases;
private final boolean checkDestinationCommit;
private final boolean approveIfSuccess;
private final boolean cancelOutdatedJobs;

transient private BitbucketPullRequestsBuilder bitbucketPullRequestsBuilder;

@Extension
public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor();
public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor();

@DataBoundConstructor
public BitbucketBuildTrigger(
Expand All @@ -64,7 +71,8 @@ public BitbucketBuildTrigger(
String ciName,
String ciSkipPhrases,
boolean checkDestinationCommit,
boolean approveIfSuccess
boolean approveIfSuccess,
boolean cancelOutdatedJobs
) throws ANTLRException {
super(cron);
this.projectPath = projectPath;
Expand All @@ -81,6 +89,7 @@ public BitbucketBuildTrigger(
this.ciSkipPhrases = ciSkipPhrases;
this.checkDestinationCommit = checkDestinationCommit;
this.approveIfSuccess = approveIfSuccess;
this.cancelOutdatedJobs = cancelOutdatedJobs;
}

public String getProjectPath() {
Expand Down Expand Up @@ -114,7 +123,7 @@ public String getRepositoryName() {
public String getBranchesFilter() {
return branchesFilter;
}

public boolean getBranchesFilterBySCMIncludes() {
return branchesFilterBySCMIncludes;
}
Expand All @@ -139,6 +148,10 @@ public boolean getApproveIfSuccess() {
return approveIfSuccess;
}

public boolean getCancelOutdatedJobs() {
return cancelOutdatedJobs;
}

@Override
public void start(AbstractProject<?, ?> project, boolean newInstance) {
try {
Expand All @@ -164,9 +177,54 @@ public BitbucketPullRequestsBuilder getBuilder() {

public QueueTaskFuture<?> startJob(BitbucketCause cause) {
Map<String, ParameterValue> values = this.getDefaultParameters();

if (getCancelOutdatedJobs()) {
cancelPreviousJobsInQueueThatMatch(cause);
abortRunningJobsThatMatch(cause);
}

return this.job.scheduleBuild2(0, 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() {
Map<String, ParameterValue> values = new HashMap<String, ParameterValue>();
ParametersDefinitionProperty definitionProperty = this.job.getProperty(ParametersDefinitionProperty.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@
<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>
</j:jelly>
12 changes: 8 additions & 4 deletions src/test/java/BitbucketBuildRepositoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ public void repositorySimpleUserPasswordTest() throws Exception {
"", true,
"", "", "",
true,
true
true,
false
);

BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
Expand Down Expand Up @@ -148,7 +149,8 @@ public void repositoryCtorWithTriggerTest() throws Exception {
"", true,
"", "", "",
true,
true
true,
false
);

BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
Expand Down Expand Up @@ -202,7 +204,8 @@ public void repositoryProjectIdTest() throws ANTLRException, NoSuchAlgorithmExce
"", true,
"jenkins", "Jenkins", "",
true,
true
true,
false
);

BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
Expand Down Expand Up @@ -248,7 +251,8 @@ public void triggerLongCIKeyTest() throws ANTLRException, NoSuchAlgorithmExcepti
"", true,
"jenkins-too-long-ci-key", "Jenkins", "",
true,
true
true,
false
);

final MessageDigest MD5 = MessageDigest.getInstance("MD5");
Expand Down

0 comments on commit 82acdec

Please sign in to comment.