diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineConditions/PipelineSelfPromotionCondition.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineConditions/PipelineSelfPromotionCondition.java new file mode 100644 index 00000000..afa746f4 --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineConditions/PipelineSelfPromotionCondition.java @@ -0,0 +1,124 @@ +package hudson.plugins.promoted_builds.pipeline.PipelineConditions; + +import hudson.Extension; +import hudson.model.*; +import hudson.model.listeners.RunListener; +import hudson.plugins.promoted_builds.PromotionBadge; +import hudson.plugins.promoted_builds.conditions.SelfPromotionBadge; +import hudson.plugins.promoted_builds.pipeline.PipelinePromotionCondition; +import hudson.plugins.promoted_builds.pipeline.PipelinePromotionConditionDescriptor; +import hudson.plugins.promoted_builds.pipeline.PipelinePromotionProcess; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; + +public class PipelineSelfPromotionCondition extends PipelinePromotionCondition { + + private final List parameters; + private final boolean evenIfUnstable; + + public PipelineSelfPromotionCondition(@Nonnull List parameters, boolean evenIfUnstable){ + this.parameters = parameters; + this.evenIfUnstable = evenIfUnstable; + } + + + private static String localJobName; + + + + @Override + public PromotionBadge isMet(PipelinePromotionProcess promotionProcess, Run run){ + + localJobName = run.getParent().getFullName(); + + + if(!run.isBuilding()){ + Result r = run.getResult(); + if((r == Result.SUCCESS) || (evenIfUnstable && r == Result.UNSTABLE){ + return new SelfPromotionBadge(); + } + + } + return null; + } + + /** Singleton Design: private static List parameters; //[We pass the parameters from the DSL this way.] + * public static List getParameters(){ + * return parameters; + * } + * Note: parameters contain (Job name, Build Number) + */ + @Override + public List getParameters(){ + + return Collections.unmodifiableList(parameters); + } + + + + + //Confusion as to how do i call ".considerPromotion2" without the "conditions" from the UI and by instead using the ParameterValue + @Extension + public static final class RunListenerImpl extends RunListener> { + public RunListenerImpl() { + super((Class)Run.class); + } + + + // My Expected approach but don't know how to call ".considerPromotion2" from here on!! + /** + @Override + public void onCompleted(Run run, TaskListener listener) { + + for(ParameterValue p : parameters){ + if (p.equals(localJobName)) { + try { + p.considerPromotion2(build); + break; // move on to the next process + } catch (IOException e) { + e.printStackTrace(listener.error("Failed to promote a build")); + } + } + + */ + + //Original Implementation + /** + @Override + public void onCompleted(AbstractBuild build, TaskListener listener) { + JobPropertyImpl jp = build.getProject().getProperty(JobPropertyImpl.class); + if(jp!=null) { + for (PromotionProcess p : jp.getItems()) { + for (PromotionCondition cond : p.conditions) { + if (cond instanceof SelfPromotionCondition) { + try { + p.considerPromotion2(build); + break; // move on to the next process + } catch (IOException e) { + e.printStackTrace(listener.error("Failed to promote a build")); + } + } + } + } + } + } + */ + + } + + @Extension + public static final class DescriptorImpl extends PipelinePromotionConditionDescriptor { + public boolean isApplicable(Job item) { + return true; + } + //Cannot resolve the return method + + public String getDisplayName() { + + return null; //Messages.SelfPromotionCondition_DisplayName(); + } + + } +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotedBuildAction.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotedBuildAction.java new file mode 100644 index 00000000..f77f825c --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotedBuildAction.java @@ -0,0 +1,116 @@ +package hudson.plugins.promoted_builds.pipeline; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.model.BuildBadgeAction; +import hudson.model.Run; +import hudson.plugins.promoted_builds.Promotion; +import hudson.plugins.promoted_builds.PromotionProcess; +import hudson.plugins.promoted_builds.Status; +import hudson.util.CopyOnWriteList; +import org.kohsuke.stapler.export.Exported; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public final class PipelinePromotedBuildAction implements BuildBadgeAction { + + public final Run owner; + + private final CopyOnWriteList statuses = new CopyOnWriteList(); + + public PipelinePromotedBuildAction(Runowner){ + assert owner != null; + this.owner = owner; + } + + public PipelinePromotedBuildAction(Run owner, Status firstStatus){ + this(owner); + statuses.add(firstStatus); + } + + public Run getOwner(){ + + return owner; + } + // What do i do with this: getProject() ?? cannot convert it to getParent?? + public Run getProject(){ + + return owner.getProject(); + } + + // Gets resolved after Status is refactored + public boolean contains(PipelinePromotionProcess process){ + for(Status s : statuses) + if(s.isFor(process)) + return true; + return false; + } + public boolean contains(String name){ + for(Status s : statuses) + if(s.name.equals(name)); + return true; + return false; + } + + public synchronized boolean add(Status status) throws IOException { + for(Status s: statuses) + if(s.name.equals(status.name)) + return false; + + this.statuses.add(status); + status.parent = this; + owner.save(); + return true; + } + + @Exported + public List getPromotions(){ + return statuses.getView(); + } + + public List getPromotionBuilds(PromotionProcess promotionProcess) { + List filtered = new ArrayList(); + + for(Status s: getPromotions() ){ + if( s.isFor(promotionProcess)){ + filtered.addAll( s.getPromotionBuilds() ); + } + } + return filtered; + } + + + /** + * Finds the {@link Status} that has matching {@link Status#name} value. + * Or {@code null} if not found. + */ + @CheckForNull + public Status getPromotion(String name) { + for (Status s : statuses) + if(s.name.equals(name)) + return s; + return null; + } + + public boolean hasPromotion() { + return !statuses.isEmpty(); + } + + public String getIconFileName() { + return "star.png"; + } + + public String getDisplayName() { + return "Promotion Status"; + } + + public String getUrlName() { + return "promotion"; + } + + + + + +} diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionCondition.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionCondition.java new file mode 100644 index 00000000..daf4236b --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionCondition.java @@ -0,0 +1,52 @@ +package hudson.plugins.promoted_builds.pipeline; + +import hudson.DescriptorExtensionList; +import hudson.ExtensionPoint; +import hudson.model.Describable; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.plugins.promoted_builds.PromotionBadge; +import hudson.plugins.promoted_builds.util.JenkinsHelper; + +import javax.annotation.CheckForNull; +import java.util.ArrayList; +import java.util.List; + +public abstract class PipelinePromotionCondition implements ExtensionPoint, Describable { + + //for those promotions that don't satisfy the desired Promotion Conditions + @CheckForNull + public PromotionBadge isMet(Run run, TaskListener listener){ + return null; + } + + //The method is used by the Conditions to label those who satisfy the presented Promotion Conditions. + public PromotionBadge isMet(PipelinePromotionProcess promotionProcess, Run run, TaskListener listener){ + return isMet(run,listener); + } + + //Loads the descriptor with the URL + public PipelinePromotionConditionDescriptor getDescriptor() { + return (PipelinePromotionConditionDescriptor) JenkinsHelper.getInstance().getDescriptor(getClass()); + } + + //Holds a set of Descriptors + public static DescriptorExtensionList all() { + return JenkinsHelper.getInstance().getDescriptorList(PipelinePromotionCondition.class); + } + + /** + * Returns a subset of {@link PipelinePromotionConditionDescriptor}s that applies to the given project. + */ + //Required Promotion Conditions to be satisfied for Promotion is being defined here. + public static List getApplicableTriggers(Job p) { + List r = new ArrayList(); + for (PipelinePromotionConditionDescriptor t : all()) { + if(t.isApplicable(p)) + r.add(t); + } + return r; + } + +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionConditionDescriptor.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionConditionDescriptor.java new file mode 100644 index 00000000..56868a5a --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionConditionDescriptor.java @@ -0,0 +1,22 @@ +package hudson.plugins.promoted_builds.pipeline; + +import hudson.model.Descriptor; +import hudson.model.Job; + +public abstract class PipelinePromotionConditionDescriptor extends Descriptor { + + protected PipelinePromotionConditionDescriptor(Class clazz) { + super(clazz); + } + + protected PipelinePromotionConditionDescriptor() { + super(); + } + + /** + * Returns true if this condition is applicable to the given project. + * + * @return true to allow user to configure this promotion condition for the given project. + */ + public abstract boolean isApplicable(Job item); +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionProcess.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionProcess.java new file mode 100644 index 00000000..34d3578e --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelinePromotionProcess.java @@ -0,0 +1,164 @@ +package hudson.plugins.promoted_builds.pipeline; + +import hudson.model.*; +import hudson.plugins.promoted_builds.JobPropertyImpl; +import hudson.plugins.promoted_builds.Promotion; +import hudson.plugins.promoted_builds.PromotionBadge; +import hudson.plugins.promoted_builds.Status; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import static org.jenkinsci.plugins.tokenmacro.impl.XmlFileMacro.LOGGER; + +public class PipelinePromotionProcess { + + @Nonnull + private final List parameters; // [tried to consume the parameters from PromotionCondition][] + + PipelinePromotionProcess(List parameters) { + + this.parameters = parameters; + } + + public List justCheck = new ArrayList<>(parameters); //[copy the parameters to another List] + + + public String icon; + + public String getIcon() { + + return getIcon(icon); + } + + @Nonnull + private static String getIcon(@CheckForNull String sIcon) { + if ((sIcon == null) || sIcon.equals("")) + return "star-gold"; + else + return sIcon; + } + + /** + + + @Override // Origin + public Job getRootProject() { + + return getParent().getOwner().getRootProject(); + } + // QUES: What About this JobPropertyImpl + @Override + public JobPropertyImpl getParent() { + return (JobPropertyImpl)super.getParent(); + } + + */ + //(getParent) does not register + public Job getOwner() { + return getParent().getOwner(); + } + + + + @Override public boolean supportsMakeDisabled() { + return true; + } + + + + //My approach + /** + @CheckForNull + public Status isMet(Run run) { + List badges = new ArrayList(); + for (PipelinePromotionProcess cond : justCheck) { //checks the new parameter from "JustCheck" + PromotionBadge b = cond.isMet(this, run); + if(b == null) + return null; + badges.add(b); + } + return new Status(this,badges); + } + */ + + + + @CheckForNull + public Future considerPromotion2(Run run, List params, TaskListener listener) throws IOException { + + + PipelinePromotedBuildAction a = run.getAction(PipelinePromotedBuildAction.class); + + // if it's already promoted, no need to do anything. + if(a!=null && a.contains(this)) + return null; + // getName() --- is from AbstractItem so does not compile!! + LOGGER.fine("Considering the promotion of "+run+" via "+ getName() +" with parameters"); + Status qualification = isMet(run); + if(qualification==null) + return null; // not this time + + LOGGER.fine("Promotion condition of "+run+" is met: "+qualification); + Future f = promote2(run, new UserCause(), qualification, params); // TODO: define promotion cause + if (f==null) + LOGGER.warning(run+" qualifies for a promotion but the queueing failed."); + return f; + } + + + + + + public Future promote2(Run run, Cause cause, Status qualification, List params, TaskListener listener) throws IOException { + PipelinePromotedBuildAction a = run.getAction(PipelinePromotedBuildAction.class); + // build is qualified for a promotion. + if(a!=null) { + a.add(qualification); + } else { + run.addAction(new PipelinePromotedBuildAction(run,qualification)); + run.save(); + } + + // schedule promotion activity. + return scheduleBuild2(run,cause, params); + } + //QUES: Should I leave it till here only? + + /** + * @deprecated + * You need to be using {@link #scheduleBuild(AbstractBuild)} + */ + + + public boolean scheduleBuild(@Nonnull AbstractBuild build) { + return scheduleBuild(build,new UserCause()); + } + + + //QUES: Left as is!! + @CheckForNull + public Future scheduleBuild2(@Nonnull Run run, + Cause cause, @CheckForNull List params) { + List actions = new ArrayList(); + actions.add(Promotion.PromotionParametersAction.buildFor(run, params)); + actions.add(new PipelineTargetAction(run)); + + // remember what build we are promoting + return super.scheduleBuild2(0, cause, actions.toArray(new Action[actions.size()])); + } + + //QUES: After this it's all StaplerRequests + //Now go for (Status,PromotedBuildAction) + + + + + + + +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineTargetAction.java b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineTargetAction.java new file mode 100644 index 00000000..3352b615 --- /dev/null +++ b/src/main/java/hudson/plugins/promoted_builds/pipeline/PipelineTargetAction.java @@ -0,0 +1,164 @@ +package hudson.plugins.promoted_builds.pipeline; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.Extension; +import hudson.model.Job; +import hudson.model.ParameterValue; +import hudson.model.Result; +import hudson.model.Run; +import hudson.model.listeners.RunListener; +import hudson.plugins.promoted_builds.PromotionBadge; +import hudson.plugins.promoted_builds.conditions.SelfPromotionBadge; +import hudson.plugins.promoted_builds.util.JenkinsHelper; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; + +public class PipelineTargetAction { + + private final String jobName; + private final int number; + + public PipelineTargetAction(Run run) { + jobName = run.getParent().getFullName(); + number = run.getNumber(); + } + + @CheckForNull + public Run resolve() { + Job j = JenkinsHelper.getInstance().getItemByFullName(jobName, Job.class); + if (j==null) return null; + return j.getBuildByNumber(number); + } + + @CheckForNull + public Run resolve(PipelinePromotionProcess parent) { + Run run = this.resolve(); + if (run != null){ + return run; + } + //In case of project renamed. + Job j = parent.getOwner(); + if (j==null) return null; + return j.getBuildByNumber(number); + } + + // For this "Promotion" should also be refactored as the "return" does not compile correctly. + /* + public Run resolve(Promotion parent) { + return resolve(parent.getParent()); + } + */ + + + public static class PipelineSelfPromotionCondition extends PipelinePromotionCondition { + + private final List parameters; + private final boolean evenIfUnstable; + + public PipelineSelfPromotionCondition(@Nonnull List parameters, boolean evenIfUnstable){ + this.parameters = parameters; + this.evenIfUnstable = evenIfUnstable; + } + + + private static String localJobName; + + + + @Override + public PromotionBadge isMet(PipelinePromotionProcess promotionProcess, Run run){ + + localJobName = run.getParent().getFullName(); + + + if(!run.isBuilding()){ + Result r = run.getResult(); + if((r == Result.SUCCESS) || (evenIfUnstable && r == Result.UNSTABLE){ + return new SelfPromotionBadge(); + } + + } + return null; + } + + /** Singleton Design: private static List parameters; //[We pass the parameters from the DSL this way.] + * public static List getParameters(){ + * return parameters; + * } + * Note: parameters contain (Job name, Build Number) + */ + @Override + public List getParameters(){ + + return Collections.unmodifiableList(parameters); + } + + + + + //Confusion as to how do i call ".considerPromotion2" without the "conditions" from the UI and by instead using the ParameterValue + @Extension + public static final class RunListenerImpl extends RunListener> { + public RunListenerImpl() { + super((Class)Run.class); + } + + + // My Expected approach but don't know how to call ".considerPromotion2" from here on!! + /** + @Override + public void onCompleted(Run run, TaskListener listener) { + + for(ParameterValue p : parameters){ + if (p.equals(localJobName)) { + try { + p.considerPromotion2(build); + break; // move on to the next process + } catch (IOException e) { + e.printStackTrace(listener.error("Failed to promote a build")); + } + } + + */ + + //Original Implementation + /** + @Override + public void onCompleted(AbstractBuild build, TaskListener listener) { + JobPropertyImpl jp = build.getProject().getProperty(JobPropertyImpl.class); + if(jp!=null) { + for (PromotionProcess p : jp.getItems()) { + for (PromotionCondition cond : p.conditions) { + if (cond instanceof SelfPromotionCondition) { + try { + p.considerPromotion2(build); + break; // move on to the next process + } catch (IOException e) { + e.printStackTrace(listener.error("Failed to promote a build")); + } + } + } + } + } + } + */ + + } + + @Extension + public static final class DescriptorImpl extends PipelinePromotionConditionDescriptor { + public boolean isApplicable(Job item) { + return true; + } + //Cannot resolve the return method + + public String getDisplayName() { + + return null; //Messages.SelfPromotionCondition_DisplayName(); + } + + } + } +}