package org.jenkinsci.plugins.ghprb.extensions.build; import hudson.Extension; import hudson.model.Cause; import hudson.model.Job; import hudson.model.Queue; import hudson.model.Result; import hudson.model.Run; import hudson.util.RunList; import jenkins.model.Jenkins; import org.jenkinsci.plugins.ghprb.Ghprb; import org.jenkinsci.plugins.ghprb.GhprbCause; import org.jenkinsci.plugins.ghprb.extensions.GhprbBuildStep; import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalDefault; import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalExtension; import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; import org.kohsuke.stapler.DataBoundConstructor; import java.util.logging.Level; import java.util.logging.Logger; public class GhprbCancelBuildsOnUpdate extends GhprbExtension implements GhprbBuildStep, GhprbGlobalExtension, GhprbProjectExtension, GhprbGlobalDefault { @Extension public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); private static final Logger logger = Logger.getLogger(GhprbCancelBuildsOnUpdate.class.getName()); private final Boolean overrideGlobal; @DataBoundConstructor public GhprbCancelBuildsOnUpdate(Boolean overrideGlobal) { this.overrideGlobal = overrideGlobal == null ? Boolean.valueOf(false) : overrideGlobal; } public Boolean getOverrideGlobal() { return overrideGlobal == null ? Boolean.valueOf(false) : overrideGlobal; } private void cancelCurrentBuilds(Job<?, ?> project, Integer prId) { if (getOverrideGlobal()) { return; } logger.log(Level.FINER, "New build scheduled for " + project.getName() + " on PR # " + prId + ", checking for queued items to cancel."); Queue queue = Jenkins.getInstance().getQueue(); Queue.Item queueItem = project.getQueueItem(); while (queueItem != null) { GhprbCause qcause = null; for (Cause cause : queueItem.getCauses()){ if (cause instanceof GhprbCause) { qcause = (GhprbCause) cause; } } if (qcause != null && qcause.getPullID() == prId) { try { logger.log(Level.FINER, "Cancelling queued build of " + project.getName() + " for PR # " + qcause.getPullID() + ", checking for queued items to cancel."); queue.cancel(queueItem); } catch (Exception e) { logger.log(Level.SEVERE, "Unable to cancel queued build", e); } } queueItem = project.getQueueItem(); } logger.log(Level.FINER, "New build scheduled for " + project.getName() + " on PR # " + prId); RunList<?> runs = project.getBuilds(); for (Run<?, ?> run : runs) { if (!run.isBuilding() && !run.hasntStartedYet()) { break; } GhprbCause cause = Ghprb.getCause(run); if (cause == null) { continue; } if (cause.getPullID() == prId) { try { logger.log(Level.FINER, "Cancelling running build #" + run.getNumber() + " of " + project.getName() + " for PR # " + cause.getPullID()); run.addAction(this); run.getExecutor().interrupt(Result.ABORTED); } catch (Exception e) { logger.log(Level.SEVERE, "Error while trying to interrupt build!", e); } } } } public void onScheduleBuild(Job<?, ?> project, GhprbCause cause) { if (project == null || cause == null) { return; } if (project.isBuilding() || project.isInQueue()) { cancelCurrentBuilds(project, cause.getPullID()); } } public String getIconFileName() { return null; } public String getDisplayName() { return "Cancel Build on Pull Request Update"; } public String getUrlName() { return null; } @Override public DescriptorImpl getDescriptor() { return DESCRIPTOR; } public static final class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbGlobalExtension, GhprbProjectExtension { @Override public String getDisplayName() { return "Cancel build on update"; } } }