From a1b25b0027e9534a5e8463fadfe387a19f85a840 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 9 May 2014 22:40:54 +0200 Subject: [PATCH 01/21] I-rh01 to I-rh07 (see separate notes) --- pom.xml | 52 ++-- .../buildpipeline/BuildJSONBuilder.groovy | 9 +- .../buildpipeline/ProjectJSONBuilder.groovy | 2 + .../plugin/buildpipeline/BuildForm.java | 63 +++- .../plugin/buildpipeline/BuildGrid.java | 9 + .../buildpipeline/BuildPipelineForm.java | 12 +- .../buildpipeline/BuildPipelineView.java | 247 +++++++++------- .../buildpipeline/DefaultBuildGridImpl.java | 35 ++- .../buildpipeline/DefaultProjectGridImpl.java | 17 +- .../DownstreamProjectGridBuilder.java | 92 +++--- .../hudson/plugin/buildpipeline/Grid.java | 31 ++ .../plugin/buildpipeline/PipelineBuild.java | 236 ++++++++++----- .../plugin/buildpipeline/ProjectForm.java | 195 ++++++++++--- .../plugin/buildpipeline/ProjectGrid.java | 24 +- .../buildpipeline/ProjectGridBuilder.java | 5 +- .../trigger/BuildPipelineTrigger.java | 21 +- .../hudson/plugin/util/HudsonResult.java | 2 +- .../hudson/plugin/util/ScheduleUtil.java | 244 ++++++++++++++++ .../model/IntervalParameterDefinition.java | 220 ++++++++++++++ .../model/IntervalParameterValue.java | 165 +++++++++++ .../buildpipeline/BuildPipelineView/bpp.jelly | 275 ++++++++++++------ .../ch/srsx/timetable/Messages.properties | 4 + .../IntervalParameterDefinition/config.jelly | 41 +++ .../config_de.properties | 4 + .../help-endTime.html | 3 + .../help-startTime.html | 3 + .../IntervalParameterDefinition/index.jelly | 35 +++ .../model/IntervalParameterValue/value.jelly | 33 +++ src/main/webapp/css/main.css | 141 ++++++++- src/main/webapp/help/parameter/interval.html | 4 + .../webapp/images/h-tile-cross-bottom.png | Bin 0 -> 789 bytes src/main/webapp/images/h-tile-cross.png | Bin 0 -> 464 bytes src/main/webapp/images/h-tile-down.png | Bin 0 -> 609 bytes src/main/webapp/images/h-tile-left.png | Bin 0 -> 476 bytes src/main/webapp/images/h-tile-right.png | Bin 0 -> 465 bytes src/main/webapp/images/h-tile-top-right.png | Bin 0 -> 623 bytes src/main/webapp/images/h-tile.png | Bin 0 -> 374 bytes src/main/webapp/images/manual.png | Bin 0 -> 608 bytes .../webapp/images/r-tile-cross-bottom.png | Bin 0 -> 553 bytes src/main/webapp/images/r-tile-cross.png | Bin 0 -> 497 bytes src/main/webapp/images/r-tile-down.png | Bin 0 -> 500 bytes src/main/webapp/images/r-tile-left.png | Bin 0 -> 487 bytes src/main/webapp/images/r-tile-right.png | Bin 0 -> 474 bytes src/main/webapp/images/r-tile-top-right.png | Bin 0 -> 569 bytes src/main/webapp/images/r-tile.png | Bin 0 -> 380 bytes src/main/webapp/images/tile-cross-bottom.png | Bin 0 -> 530 bytes src/main/webapp/images/tile-cross.png | Bin 0 -> 475 bytes src/main/webapp/images/tile-down.png | Bin 0 -> 493 bytes src/main/webapp/images/tile-left.png | Bin 0 -> 482 bytes src/main/webapp/images/tile-right.png | Bin 0 -> 464 bytes src/main/webapp/images/tile-top-right.png | Bin 0 -> 652 bytes src/main/webapp/images/tile.png | Bin 0 -> 380 bytes src/main/webapp/js/build-pipeline.js | 16 +- 53 files changed, 1843 insertions(+), 397 deletions(-) create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java create mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java create mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java create mode 100644 src/main/resources/ch/srsx/timetable/Messages.properties create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly create mode 100644 src/main/webapp/help/parameter/interval.html create mode 100644 src/main/webapp/images/h-tile-cross-bottom.png create mode 100644 src/main/webapp/images/h-tile-cross.png create mode 100644 src/main/webapp/images/h-tile-down.png create mode 100644 src/main/webapp/images/h-tile-left.png create mode 100644 src/main/webapp/images/h-tile-right.png create mode 100644 src/main/webapp/images/h-tile-top-right.png create mode 100644 src/main/webapp/images/h-tile.png create mode 100644 src/main/webapp/images/manual.png create mode 100644 src/main/webapp/images/r-tile-cross-bottom.png create mode 100644 src/main/webapp/images/r-tile-cross.png create mode 100644 src/main/webapp/images/r-tile-down.png create mode 100644 src/main/webapp/images/r-tile-left.png create mode 100644 src/main/webapp/images/r-tile-right.png create mode 100644 src/main/webapp/images/r-tile-top-right.png create mode 100644 src/main/webapp/images/r-tile.png create mode 100644 src/main/webapp/images/tile-cross-bottom.png create mode 100644 src/main/webapp/images/tile-cross.png create mode 100644 src/main/webapp/images/tile-down.png create mode 100644 src/main/webapp/images/tile-left.png create mode 100644 src/main/webapp/images/tile-right.png create mode 100644 src/main/webapp/images/tile-top-right.png create mode 100644 src/main/webapp/images/tile.png diff --git a/pom.xml b/pom.xml index 17943d09..15635f17 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 1.509.3 + 1.558 build-pipeline-plugin 1.4.3-SNAPSHOT @@ -95,7 +95,7 @@ org.hamcrest hamcrest-core - 1.2 + 1.3 test @@ -109,6 +109,11 @@ parameterized-trigger 2.17 + + org.jenkins-ci.plugins.m2release + m2release + 0.14.0 + org.seleniumhq.selenium @@ -143,6 +148,11 @@ 1.8.3 test + + joda-time + joda-time + 1.6 + @@ -237,25 +247,25 @@ - - org.codehaus.mojo - findbugs-maven-plugin - 2.3.2 - - - Max - Medium - true - findbugs-exclude.xml - - - - - check - - - - + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins maven-release-plugin diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy index af7e0d3a..cca3eb52 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy @@ -18,9 +18,10 @@ class BuildJSONBuilder { extId(pipelineBuild.currentBuild?.externalizableId) hasPermission(pipelineBuild.project?.hasPermission(Item.BUILD)); hasUpstreamBuild(null != pipelineBuild.upstreamBuild) + isQueued(buildStatus == 'QUEUED') isBuilding(buildStatus == 'BUILDING') - isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL') - isPending(buildStatus == 'PENDING') + isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL' && buildStatus != 'QUEUED') + isPending(buildStatus == 'PENDING') isSuccess(buildStatus == 'SUCCESS') isReadyToBeManuallyBuilt(pipelineBuild.isReadyToBeManuallyBuilt()) isManualTrigger(pipelineBuild.isManualTrigger()) @@ -37,6 +38,10 @@ class BuildJSONBuilder { url(pipelineBuild.buildResultURL ? pipelineBuild.buildResultURL : pipelineBuild.projectURL) userId(pipelineBuild.currentBuild?.getCause(Cause.UserIdCause.class)?.getUserId()) estimatedRemainingTime(pipelineBuild.currentBuild?.executor?.estimatedRemainingTime) + queuedForDate(pipelineBuild.formattedQueuedForDate) + queuedForTime(pipelineBuild.formattedQueuedForTime) + queueWaitDuration(pipelineBuild.queueWaitDuration) + queueId(pipelineBuild.queueId) } project { disabled(pipelineBuild.projectDisabled) diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy index a8a6fc2e..5b710643 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy @@ -16,6 +16,8 @@ class ProjectJSONBuilder { def builder = new JsonBuilder() def root = builder { id(projectForm.id) + row(projectForm.row) + col(projectForm.col) name(projectForm.name) health(projectForm.health) url(projectForm.url) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 163e7dd9..1e935fc8 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -3,14 +3,20 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.ParametersDefinitionProperty; +import hudson.model.Queue.WaitingItem; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + /** * @author Centrum Systems * @@ -49,12 +55,11 @@ public class BuildForm { */ private List dependencies = new ArrayList(); - /** * project stringfied list of parameters for the project * */ private final ArrayList parameters; - + /** * @param pipelineBuild * pipeline build domain used to see the form @@ -123,6 +128,28 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing + final WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(pipelineBuild.getProject().getDisplayName()); + if (wItem != null) { + updated = true; + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(pipelineBuild.getProject().getDisplayName()); + if (wItem == null) { + updated = true; } return updated; } @@ -135,6 +162,10 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -143,7 +174,11 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - + + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } + public ArrayList getParameterList() { return parameters; } @@ -152,4 +187,26 @@ public Integer getProjectId() { return projectId; } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java index 40c11deb..53d76a0c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java @@ -1,5 +1,7 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import hudson.model.AbstractBuild; + /** * {@link Grid} of {@link BuildForm}s, which represents one instance * of a pipeline execution. @@ -7,4 +9,11 @@ * @author Kohsuke Kawaguchi */ public abstract class BuildGrid extends Grid { + /** + * Search for a BuildForm by a given Build in the current BuildGrid + * + * @param build - the build to search for in the BuildGrid + * @return BuildForm the form containing the given build + */ + public abstract BuildForm findBuildForm(AbstractBuild build); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java index 4b4918b9..a7eff673 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java @@ -1,10 +1,9 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import com.google.common.collect.Iterables; - import java.util.Arrays; import java.util.List; -import java.util.logging.Logger; + +import com.google.common.collect.Iterables; /** * @author Centrum Systems @@ -13,11 +12,6 @@ * */ public class BuildPipelineForm { - /** - * logger - */ - private static final Logger LOGGER = Logger.getLogger(BuildPipelineForm.class.getName()); - /** * projects laid out in a grid using maps to ease accessing (or maybe I made it way too complicated by not using a 2-dimensional array) * Outside map holds rows and inner map has ProjectForm at a particular position (defined with key) @@ -38,6 +32,8 @@ public class BuildPipelineForm { public BuildPipelineForm(final ProjectGrid grid, final Iterable builds) { projectGrid = grid; buildGrids = Arrays.asList(Iterables.toArray(builds, BuildGrid.class)); + // Handle lastSuccessful Builds correctly, when some jobs are used in multiple Views + grid.correctProjectForms(builds); } public ProjectGrid getProjectGrid() { diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 3e9f56c1..e95b2ac0 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -24,8 +24,6 @@ */ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import com.google.common.collect.Iterables; - import hudson.Extension; import hudson.model.Action; import hudson.model.Item; @@ -48,7 +46,6 @@ import hudson.security.Permission; import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; -import jenkins.model.Jenkins; import java.io.IOException; import java.net.URISyntaxException; @@ -63,6 +60,9 @@ import javax.servlet.ServletException; +import jenkins.model.Jenkins; + +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -71,21 +71,24 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + +import com.google.common.collect.Iterables; /** - * This view displays the set of jobs that are related - * based on their upstream\downstream relationships as a pipeline. Each - * build pipeline becomes a row on the view. - * + * This view displays the set of jobs that are related based on their + * upstream\downstream relationships as a pipeline. Each build pipeline becomes + * a row on the view. + * * @author Centrum Systems - * + * */ public class BuildPipelineView extends View { /** - * @deprecated - * For backward compatibility. Back when we didn't have {@link #gridBuilder}, - * this field stored the first job to display. + * @deprecated For backward compatibility. Back when we didn't have + * {@link #gridBuilder}, this field stored the first job to + * display. */ @Deprecated private volatile String selectedJob; @@ -116,14 +119,15 @@ public class BuildPipelineView extends View { /** showPipelineParametersInHeaders */ private boolean showPipelineParametersInHeaders; - + /** * informs if the first job has parameters */ private boolean startsWithParameters; /** - * Frequency at which the Build Pipeline Plugin updates the build cards in seconds + * Frequency at which the Build Pipeline Plugin updates the build cards in + * seconds */ private int refreshFrequency = 3; @@ -131,7 +135,8 @@ public class BuildPipelineView extends View { private boolean showPipelineDefinitionHeader; /* - * Keep feature flag properties in one place so that it is easy to refactor them out later. + * Keep feature flag properties in one place so that it is easy to refactor + * them out later. */ /* Feature flags - START */ @@ -141,8 +146,8 @@ public class BuildPipelineView extends View { private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); /** - * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the - * serialization + * An instance of {@link Cause.UserIdCause} related to the current user. + * Must be transient, or xstream will include it in the serialization */ private class MyUserIdCause extends Cause.UserIdCause { /** @@ -155,7 +160,8 @@ private class MyUserIdCause extends Cause.UserIdCause { */ public MyUserIdCause() { try { - // this block can generate a CyclicGraphDetector.CycleDetectedException + // this block can generate a + // CyclicGraphDetector.CycleDetectedException // in cases that I haven't quite figured out yet // also an org.acegisecurity.AccessDeniedException when the user // is not logged in @@ -209,7 +215,7 @@ public void print(final TaskListener listener) { } /** - * + * * @param name * the name of the pipeline build view. * @param buildViewTitle @@ -226,10 +232,9 @@ public void print(final TaskListener listener) { * Indicates whether the first job of the pipeline takes * parameters */ - public BuildPipelineView(final String name, final String buildViewTitle, - final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { - super(name, Hudson.getInstance()); + public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, + final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { + super(name, Jenkins.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; this.noOfDisplayedBuilds = noOfDisplayedBuilds; @@ -239,7 +244,7 @@ public BuildPipelineView(final String name, final String buildViewTitle, } /** - * + * * @param name * the name of the pipeline build view. * @param buildViewTitle @@ -260,20 +265,23 @@ public BuildPipelineView(final String name, final String buildViewTitle, * @param showPipelineDefinitionHeader * Indicates whether the pipeline headers should be shown. * @param refreshFrequency - * Frequency at which the build pipeline plugin refreshes build cards + * Frequency at which the build pipeline plugin refreshes build + * cards * @param cssUrl * URL for the custom CSS file. * @param selectedJob - * the first job name in the pipeline. it can be set to null when gridBuilder is passed. + * the first job name in the pipeline. it can be set to null when + * gridBuilder is passed. * @param startsWithParameters - * indicates whether the first job of the pipeline takes parameters + * indicates whether the first job of the pipeline takes + * parameters */ @DataBoundConstructor public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, - final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, - final int refreshFrequency, final String cssUrl, final String selectedJob, final boolean startsWithParameters) { + final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, + final boolean showPipelineParameters, final boolean showPipelineParametersInHeaders, + final boolean showPipelineDefinitionHeader, final int refreshFrequency, final String cssUrl, final String selectedJob, + final boolean startsWithParameters) { this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl, startsWithParameters); this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; this.showPipelineParameters = showPipelineParameters; @@ -281,21 +289,23 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; this.startsWithParameters = startsWithParameters; this.selectedJob = selectedJob; - //not exactly understanding the lifecycle here, but I want a default of 3 - //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) + // not exactly understanding the lifecycle here, but I want a default of + // 3 + // (this is what the class variable is set to 3, if it's 0, set it to + // default, refresh of 0 does not make sense anyway) if (refreshFrequency < 1) { this.refreshFrequency = 3; } else { this.refreshFrequency = refreshFrequency; } - - //for remote api support + + // for remote api support if (this.gridBuilder == null) { if (this.selectedJob != null) { this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); } } - + if (this.selectedJob == null) { if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); @@ -304,8 +314,7 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P } /** - * @return - * must be always 'this' + * @return must be always 'this' */ protected Object readResolve() { if (gridBuilder == null) { @@ -318,7 +327,7 @@ protected Object readResolve() { /** * Handles the configuration submission - * + * * @param req * Stapler Request * @throws FormException @@ -334,9 +343,11 @@ protected void submit(final StaplerRequest req) throws IOException, ServletExcep } /** - * Checks whether the user has a permission to start a new instance of the pipeline. - * - * @return - true: Has Build permission; false: Does not have Build permission + * Checks whether the user has a permission to start a new instance of the + * pipeline. + * + * @return - true: Has Build permission; false: Does not have Build + * permission * @see hudson.model.Item */ public boolean hasBuildPermission() { @@ -345,8 +356,9 @@ public boolean hasBuildPermission() { /** * Checks whether the user has Configure permission for the current project. - * - * @return - true: Has Configure permission; false: Does not have Configure permission + * + * @return - true: Has Configure permission; false: Does not have Configure + * permission */ public boolean hasConfigurePermission() { return this.hasPermission(CONFIGURE); @@ -362,7 +374,7 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { /** * Get a List of downstream projects. - * + * * @param currentProject * - The project from which we want the downstream projects * @return - A List of downstream projects @@ -373,35 +385,35 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { /** * Determines if the current project has any downstream projects - * + * * @param currentProject * - The project from which we are testing. - * @return - true; has downstream projects; false: does not have downstream projects + * @return - true; has downstream projects; false: does not have downstream + * projects */ public boolean hasDownstreamProjects(final AbstractProject currentProject) { return (getDownstreamProjects(currentProject).size() > 0); } - + /** * Returns BuildPipelineForm containing the build pipeline to display. - * - * @return - Representation of the projects and their related builds making up the build pipeline view + * + * @return - Representation of the projects and their related builds making + * up the build pipeline view */ public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid project = gridBuilder.build(this); - if (project.isEmpty()) { + final ProjectGrid projectGrid = gridBuilder.build(this); + if (projectGrid.isEmpty()) { return null; } - return new BuildPipelineForm( - project, - Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); } /** * Retrieves the project URL - * + * * @param project * - The project * @return URL - of the project @@ -415,7 +427,7 @@ public String getProjectURL(final AbstractProject project) throws URISynta /** * Trigger a manual build - * + * * @param upstreamBuildNumber * upstream build number * @param triggerProjectName @@ -440,7 +452,8 @@ public int triggerManualBuild(final Integer upstreamBuildNumber, final String tr buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); } - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } /** @@ -483,13 +496,15 @@ public int rerunBuild(final String externalizableId) { } /** - * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. - * + * Given an AbstractProject and a build number the associated AbstractBuild + * will be retrieved. + * * @param buildNo * - Build number * @param project * - AbstractProject - * @return The AbstractBuild associated with the AbstractProject and build number. + * @return The AbstractBuild associated with the AbstractProject and build + * number. */ @SuppressWarnings("unchecked") private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { @@ -509,29 +524,34 @@ public int rerunBuild(final String externalizableId) { /** * Schedules a build to start. - * - * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. - * + * + * The build will take an upstream build as its Cause and a set of + * ParametersAction from the upstream build. + * * @param triggerProject * - Schedule a build to start on this AbstractProject * @param upstreamBuild - * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. + * - The upstream AbstractBuild that will be used as a Cause for + * the triggerProject's build. * @param buildParametersAction - * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * - The upstream ParametersAction that will be used as an Action + * for the triggerProject's build. + * @param delay + * - The delay for the build to be scheduled * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction) { + final Action buildParametersAction, final int delay) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = - buildParametersAction instanceof ParametersAction - ? (ParametersAction) buildParametersAction : new ParametersAction(); + ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction + : new ParametersAction(); if (upstreamBuild != null) { @@ -559,19 +579,19 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } /** - * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java - * + * From parameterized trigger plugin + * src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java + * * @param base - * One of the two parameters to merge. + * One of the two parameters to merge. * @param overlay - * The other parameters to merge - * @return - * Result of the merge. + * The other parameters to merge + * @return Result of the merge. */ private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { final LinkedHashMap params = new LinkedHashMap(); @@ -585,11 +605,13 @@ private static ParametersAction mergeParameters(final ParametersAction base, fin } /** - * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. - * + * Checks whether the given {@link Action} contains a reference to a + * {@link UserIdCause} object. + * * @param buildAction * the action to check. - * @return true if the action has a reference to a userId cause. + * @return true if the action has a reference to a userId + * cause. */ private boolean isUserIdCauseAction(final Action buildAction) { boolean retval = false; @@ -605,10 +627,12 @@ private boolean isUserIdCauseAction(final Action buildAction) { } /** - * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. - * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the - * downstream build needs to remove the old user action inherited from upstream, and add its own. - * + * Removes any UserId cause action from the given actions collection. This + * is used by downstream builds that inherit upstream actions. The + * downstream build can be initiated by another user that is different from + * the user who initiated the upstream build, so the downstream build needs + * to remove the old user action inherited from upstream, and add its own. + * * @param actions * a collection of build actions. * @return a collection of build actions with all UserId causes removed. @@ -625,13 +649,14 @@ private List removeUserIdCauseActions(final List actions) { /** * This descriptor class is required to configure the View Page - * + * */ @Extension public static final class DescriptorImpl extends ViewDescriptor { /** - * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of + * descriptor impl constructor This empty constructor is required for + * stapler. If you remove this constructor, text name of * "Build Pipeline View" will be not displayed in the "NewView" page */ public DescriptorImpl() { @@ -640,7 +665,7 @@ public DescriptorImpl() { /** * get the display name - * + * * @return display name */ @Override @@ -650,7 +675,7 @@ public String getDisplayName() { /** * Display No Of Builds Items in the Edit View Page - * + * * @return ListBoxModel */ public ListBoxModel doFillNoOfDisplayedBuildsItems() { @@ -675,7 +700,7 @@ public ListBoxModel doFillNoOfDisplayedBuildsItems() { /** * Display Console Output Link Style Items in the Edit View Page - * + * * @return ListBoxModel */ public ListBoxModel doFillConsoleOutputLinkStyleItems() { @@ -762,9 +787,9 @@ public String getShowPipelineParameters() { public void setShowPipelineParameters(final boolean showPipelineParameters) { this.showPipelineParameters = showPipelineParameters; } - - public boolean isStartsWithParameters() { - return startsWithParameters; + + public boolean isStartsWithParameters() { + return startsWithParameters && gridBuilder.startsWithParameters(this); } public String getStartsWithParameters() { @@ -773,7 +798,7 @@ public String getStartsWithParameters() { public void setStartsWithParameters(final boolean startsWithParameters) { this.startsWithParameters = startsWithParameters; - } + } public boolean isShowPipelineParametersInHeaders() { return showPipelineParametersInHeaders; @@ -838,15 +863,16 @@ public boolean contains(final TopLevelItem item) { } /** - * If a project name is changed we check if the selected job for this view also needs to be changed. - * + * If a project name is changed we check if the selected job for this view + * also needs to be changed. + * * @param item * - The Item that has been renamed * @param oldName * - The old name of the Item * @param newName * - The new name of the Item - * + * */ @Override public void onJobRenamed(final Item item, final String oldName, final String newName) { @@ -874,20 +900,43 @@ private static final class LinkStyle { /** this window link style option */ static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ } - + @Override public boolean hasPermission(final Permission p) { boolean display = true; - //tester la liste vide seulement en lecture + // tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - //Pas en lecture => permission standard + // Pas en lecture => permission standard display = super.hasPermission(p); } return display; } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), AbstractProject.class); + if (project != null) { + // If a M2ReleaseAction is found + for (final Action nextAction : project.getAllActions()) { + if (nextAction instanceof M2ReleaseAction) { + return true; + } + } + } + return false; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java index b66e9641..2a47ecde 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java @@ -1,18 +1,20 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import hudson.model.AbstractBuild; + import java.util.HashMap; import java.util.Map; /** * {@link BuildGrid} implementation backed by a sparse array. - * + * * @author Kohsuke Kawaguchi */ public class DefaultBuildGridImpl extends BuildGrid { /** * Actual data. */ - private final Map> data = new HashMap>(); + private final Map> data = new HashMap>(); /** * Dimension of the {@link #data} @@ -20,14 +22,15 @@ public class DefaultBuildGridImpl extends BuildGrid { private int rows, cols; /** - * Mutable, but only for the code that instantiates {@link DefaultBuildGridImpl}. - * + * Mutable, but only for the code that instantiates + * {@link DefaultBuildGridImpl}. + * * @param row - * position of the form + * position of the form * @param col - * position of the form + * position of the form * @param p - * The build to add. null to remove the value. + * The build to add. null to remove the value. */ public void set(int row, int col, BuildForm p) { Map c = data.get(row); @@ -59,4 +62,22 @@ public int getColumns() { public int getRows() { return rows; } + + @Override + public BuildForm findBuildForm(AbstractBuild build) { + if (build != null) { + final String fullBuildName = build.getFullDisplayName(); + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + final BuildForm form = get(r, c); + if (form != null) { + if (fullBuildName.equals(form.getFullBuildName())) { + return form; + } + } + } + } + } + return null; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java index 213035dc..3b885767 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java @@ -1,6 +1,8 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -37,7 +39,8 @@ public void set(int row, int col, ProjectForm p) { data.put(row, c); } c.put(col, p); - + p.setCoords(row, col); + rows = Math.max(rows, row + 1); cols = Math.max(cols, col + 1); } @@ -60,7 +63,7 @@ public ProjectForm get(int row, int col) { } return cols.get(col); } - + @Override public int getColumns() { return cols; @@ -71,4 +74,14 @@ public int getRows() { return rows; } + @Override + public void correctProjectForms(Iterable buildGrids) { + final Collection> maps = data.values(); + for (Map map : maps) { + final Collection forms = map.values(); + for (ProjectForm projectForm : forms) { + projectForm.correctLastSuccessfulBuilds(buildGrids); + } + } + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java index c7a21048..a82b0176 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java @@ -1,14 +1,21 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import hudson.Extension; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; import hudson.model.Item; import hudson.model.ItemGroup; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; import hudson.model.ParametersDefinitionProperty; import hudson.util.AdaptedIterator; import hudson.util.HttpResponses; import hudson.util.ListBoxModel; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + +import javax.servlet.ServletException; + import jenkins.model.Jenkins; import jenkins.util.TimeDuration; @@ -19,15 +26,9 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.servlet.ServletException; - -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; - /** * {@link ProjectGridBuilder} based on the upstream/downstream relationship. - * + * * @author Kohsuke Kawaguchi */ public class DownstreamProjectGridBuilder extends ProjectGridBuilder { @@ -37,7 +38,8 @@ public class DownstreamProjectGridBuilder extends ProjectGridBuilder { private String firstJob; /** - * @param firstJob Name of the job to lead the piepline. + * @param firstJob + * Name of the job to lead the piepline. */ @DataBoundConstructor public DownstreamProjectGridBuilder(String firstJob) { @@ -54,7 +56,8 @@ private static final class GridImpl extends DefaultProjectGridImpl { private final AbstractProject start; /** - * @param start The first project to lead the pipeline. + * @param start + * The first project to lead the pipeline. */ private GridImpl(AbstractProject start) { this.start = start; @@ -63,11 +66,16 @@ private GridImpl(AbstractProject start) { /** * Function called recursively to place a project form in a grid - * - * @param startingRow project will be placed in the starting row and 1st child as well. Each subsequent - * child will be placed in a row below the previous. - * @param startingColumn project will be placed in starting column. All children will be placed in next column. - * @param projectForm project to be placed + * + * @param startingRow + * project will be placed in the starting row and 1st child + * as well. Each subsequent child will be placed in a row + * below the previous. + * @param startingColumn + * project will be placed in starting column. All children + * will be placed in next column. + * @param projectForm + * project to be placed */ private void placeProjectInGrid(final int startingRow, final int startingColumn, final ProjectForm projectForm) { if (projectForm == null) { @@ -78,8 +86,8 @@ private void placeProjectInGrid(final int startingRow, final int startingColumn, set(row, startingColumn, projectForm); final int childrensColumn = startingColumn + 1; - for (final ProjectForm downstreamProject : projectForm.getDependencies()) { - placeProjectInGrid(row, childrensColumn, downstreamProject); + for (final ProjectForm downstreamForm : projectForm.getDependencies()) { + placeProjectInGrid(row, childrensColumn, downstreamForm); row++; } } @@ -91,7 +99,8 @@ private void placeProjectInGrid(final int startingRow, final int startingColumn, @Override public Iterator iterator() { if (start == null) { - return Collections.emptyList().iterator(); // no dat + return Collections.emptyList().iterator(); // no + // dat } final Iterator> base = start.getBuilds().iterator(); @@ -111,11 +120,13 @@ public Iterable builds() { } /** - * {@link BuildGrid} implementation that lays things out via its upstream/downstream relationship. + * {@link BuildGrid} implementation that lays things out via its + * upstream/downstream relationship. */ private static final class BuildGridImpl extends DefaultBuildGridImpl { /** - * @param start The first build to lead the pipeline instance. + * @param start + * The first build to lead the pipeline instance. */ private BuildGridImpl(final BuildForm start) { placeBuildInGrid(0, 0, start); @@ -123,11 +134,16 @@ private BuildGridImpl(final BuildForm start) { /** * Function called recursively to place a build form in a grid - * - * @param startingRow build will be placed in the starting row and 1st child as well. Each subsequent child - * will be placed in a row below the previous. - * @param startingColumn build will be placed in starting column. All children will be placed in next column. - * @param buildForm build to be placed + * + * @param startingRow + * build will be placed in the starting row and 1st child as + * well. Each subsequent child will be placed in a row below + * the previous. + * @param startingColumn + * build will be placed in starting column. All children will + * be placed in next column. + * @param buildForm + * build to be placed */ private void placeBuildInGrid(final int startingRow, final int startingColumn, final BuildForm buildForm) { int row = getNextAvailableRow(startingRow, startingColumn); @@ -147,8 +163,9 @@ public String getFirstJob() { /** * The job that's configured as the head of the pipeline. - * - * @param owner View that this builder is operating under. + * + * @param owner + * View that this builder is operating under. * @return possibly null */ public AbstractProject getFirstJob(BuildPipelineView owner) { @@ -160,7 +177,7 @@ public boolean hasBuildPermission(BuildPipelineView owner) { final AbstractProject job = getFirstJob(owner); return job != null && job.hasPermission(Item.BUILD); } - + @Override public boolean startsWithParameters(BuildPipelineView owner) { final AbstractProject firstJob = this.getFirstJob(owner); @@ -185,8 +202,10 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod p.doBuild(req, rsp, new TimeDuration(0)); } catch (IllegalStateException e) { ; - // Ignore because sendRedirect(String) gets called twice. We do not want to hit the top - // level of the project but instead we want to be redirected back 1 directory. + // Ignore because sendRedirect(String) gets called twice. We + // do not want to hit the top + // level of the project but instead we want to be redirected + // back 1 directory. } } }; @@ -210,7 +229,8 @@ public void onJobRenamed(BuildPipelineView owner, Item item, String oldName, Str /** * Descriptor. */ - @Extension(ordinal = 1000) // historical default behavior, so give it a higher priority + @Extension(ordinal = 1000) + // historical default behavior, so give it a higher priority public static class DescriptorImpl extends ProjectGridBuilderDescriptor { @Override public String getDisplayName() { @@ -219,15 +239,15 @@ public String getDisplayName() { /** * Display Job List Item in the Edit View Page - * - * @param context What to resolve relative job names against? + * + * @param context + * What to resolve relative job names against? * @return ListBoxModel */ public ListBoxModel doFillFirstJobItems(@AncestorInPath ItemGroup context) { final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); for (final AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class)) { - options.add(/* TODO 1.515: p.getRelativeDisplayNameFrom(context) */p.getFullDisplayName(), - p.getRelativeNameFrom(context)); + options.add(/* TODO 1.515: p.getRelativeDisplayNameFrom(context) */p.getFullDisplayName(), p.getRelativeNameFrom(context)); } return options; } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java index e750f4cf..ca9362b8 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java @@ -92,4 +92,35 @@ private boolean hasDataToRight(final int row, final int col) { } return false; } + + /** + * Search for dependencies of the starting project further down the rows. + * + * @param startRow - the row of the project to search from + * @param startCol - the col of the project to search from + * @return boolean - whether there is a project further down from the current position + */ + public boolean hasDependencyFurtherDown(int startRow, int startCol) { + // Search for a further down projectForm + final int nextRow = startRow + 1; + final int nextCol = startCol + 1; + int nextTRow = -1; + for (int locRow = nextRow; locRow < getRows(); locRow++) { + if (get(locRow, nextCol) != null) { + nextTRow = locRow; + break; + } + } + // Now test, that there is no other project between startRow and row of nextPf + // only if there is none, then the furtherDown project is correct for the current starting point + if (nextTRow != -1) { + for (int locRow = nextRow; locRow <= nextTRow; locRow++) { + if (get(locRow, startCol) != null) { + return false; + } + } + return true; + } + return false; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 53fe2510..a71d3cbc 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,9 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Hudson; +import hudson.model.Queue; +import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; +import hudson.plugins.parameterizedtrigger.SubProjectsAction; import java.text.DateFormat; import java.util.ArrayList; @@ -39,15 +41,15 @@ import java.util.Map; import java.util.logging.Logger; +import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; -import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; /** * @author Centrum Systems - * + * */ public class PipelineBuild { /** Represents the current build */ @@ -57,13 +59,14 @@ public class PipelineBuild { /** Represents the upstream build */ private AbstractBuild upstreamBuild; /** - * Contains the upstreamBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - - * PENDING - MANUAL + * Contains the upstreamBuild result. Can be one of the following: - + * BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - PENDING - + * MANUAL */ private String upstreamBuildResult; /** - * Contains the currentBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - - * PENDING - MANUAL + * Contains the currentBuild result. Can be one of the following: - BUILDING + * - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - PENDING - MANUAL */ private String currentBuildResult; @@ -77,8 +80,9 @@ public PipelineBuild() { } /** - * Creates a new PipelineBuild with currentBuild, project and upstreamBuild set. - * + * Creates a new PipelineBuild with currentBuild, project and upstreamBuild + * set. + * * @param build * - current build * @param project @@ -96,9 +100,9 @@ public PipelineBuild(final AbstractBuild build, final AbstractProject build) { this(build, build.getProject(), build.getPreviousBuild()); @@ -133,8 +137,9 @@ public void setProject(final AbstractProject currentProject) { } /** - * Returns the project name. If the current project is null the project name is determined using the current build. - * + * Returns the project name. If the current project is null the project name + * is determined using the current build. + * * @return - Project name */ public AbstractProject getProject() { @@ -149,8 +154,9 @@ public void setProject(final AbstractProject currentProject) { /** * Returns the current build number. - * - * @return - Current build number or empty String is the current build is null. + * + * @return - Current build number or empty String is the current build is + * null. */ public String getCurrentBuildNumber() { if (this.currentBuild != null) { @@ -161,9 +167,11 @@ public String getCurrentBuildNumber() { } /** - * Constructs a List of downstream PipelineBuild objects that make up the current pipeline. - * - * @return - List of downstream PipelineBuild objects that make up the current pipeline. + * Constructs a List of downstream PipelineBuild objects that make up the + * current pipeline. + * + * @return - List of downstream PipelineBuild objects that make up the + * current pipeline. */ public List getDownstreamPipeline() { final List pbList = new ArrayList(); @@ -180,7 +188,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -189,7 +197,8 @@ public List getDownstreamPipeline() { returnedBuild = BuildUtil.getDownstreamBuild(dependency, currentBuild); } final PipelineBuild candidate = new PipelineBuild(returnedBuild, dependency, this.currentBuild); - // if subprojects come back as downstreams someday, no duplicates wanted + // if subprojects come back as downstreams someday, no + // duplicates wanted if (!pbList.contains(candidate)) { pbList.add(candidate); } @@ -203,7 +212,7 @@ public List getDownstreamPipeline() { /** * Build a URL of the currentBuild - * + * * @return URL of the currentBuild */ public String getBuildResultURL() { @@ -212,7 +221,7 @@ public String getBuildResultURL() { /** * Builds a URL of the current project - * + * * @return URL - of the project */ public String getProjectURL() { @@ -221,7 +230,7 @@ public String getProjectURL() { /** * Determines the result of the current build. - * + * * @return - String representing the build result * @see PipelineBuild#getBuildResult(AbstractBuild) */ @@ -232,7 +241,7 @@ public String getCurrentBuildResult() { /** * Determines the result of the upstream build. - * + * * @return - String representing the build result * @see PipelineBuild#getBuildResult(AbstractBuild) */ @@ -244,9 +253,10 @@ public String getUpstreamBuildResult() { } /** - * Determines the result for a particular build. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - - * ABORT - PENDING - MANUAL - * + * Determines the result for a particular build. Can be one of the + * following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT + * - PENDING - MANUAL + * * @param build * - The build for which a result is requested. * @return - String representing the build result @@ -269,22 +279,30 @@ private String getBuildResult(final AbstractBuild build) { } /** - * Determines the pending currentBuild status of a currentBuild in the pipeline that has not been completed. (i.e. the currentBuild is - * null) - * - * @return - PENDING: Current currentBuild is pending the execution of upstream builds. MANUAL: Current currentBuild requires a manual - * trigger + * Determines the pending currentBuild status of a currentBuild in the + * pipeline that has not been completed. (i.e. the currentBuild is null) + * + * @return - PENDING: Current currentBuild is pending the execution of + * upstream builds. MANUAL: Current currentBuild requires a manual + * trigger. */ private String getPendingStatus() { String pendingStatus = HudsonResult.PENDING.toString(); - final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); - - if (upstreamPB != null) { - if (this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || - getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ScheduleUtil.getQueuedItem(project.getDisplayName()) != null) { + // Check for upstreamBuild being latest build (otherwise, do not set + // status on QUEUED) + if (upstreamBuild != null) { + final AbstractBuild lastUpstreamBuild = upstreamBuild.getProject().getLastBuild(); + if (lastUpstreamBuild != null && upstreamBuild.getNumber() == lastUpstreamBuild.getNumber()) { + pendingStatus = HudsonResult.QUEUED.toString(); + } + } + } else { + final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + if (upstreamPB != null && this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { pendingStatus = HudsonResult.MANUAL.toString(); } @@ -294,11 +312,12 @@ private String getPendingStatus() { return pendingStatus; } - /** - * Returns the upstream PipelineBuild object from the current PipelineBuild object. - * - * @return - Upstream PipelineBuild object from the current PipelineBuild object + * Returns the upstream PipelineBuild object from the current PipelineBuild + * object. + * + * @return - Upstream PipelineBuild object from the current PipelineBuild + * object */ public PipelineBuild getUpstreamPipelineBuild() { @SuppressWarnings("rawtypes") @@ -311,12 +330,12 @@ public PipelineBuild getUpstreamPipelineBuild() { } else { upstreamBuildName = ""; } - if (upstreamProjects.size() > 0) { + if (upstreamProjects.size() > 0) { if (isManualTrigger()) { - for (AbstractProject upstreamProject : upstreamProjects) { + for (AbstractProject upstreamProject : upstreamProjects) { if (upstreamProject.getName().equals(upstreamBuildName)) { previousProject = upstreamProject; - break; + break; } } } @@ -331,8 +350,9 @@ public PipelineBuild getUpstreamPipelineBuild() { /** * Returns the current build duration. - * - * @return - Current build duration or an empty String if the current build is null. + * + * @return - Current build duration or an empty String if the current build + * is null. */ public String getBuildDuration() { if (this.currentBuild != null) { @@ -342,6 +362,35 @@ public String getBuildDuration() { } } + /** + * Returns the current wait duration (when queued). + * + * @return - Current wait duration or an empty String if no queueing in + * progress is null. + */ + public String getQueueWaitDuration() { + final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); + if (wItem != null) { + return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); + } else { + return ""; //$NON-NLS-1$ + } + } + + /** + * Returns the current id of the queue item. + * + * @return - Current id of the queue item + */ + public Integer getQueueId() { + final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); + if (wItem != null) { + return wItem.id; + } else { + return 0; //$NON-NLS-1$ + } + } + /** * {@inheritDoc} */ @@ -352,8 +401,9 @@ public String toString() { /** * Returns the current build description. - * - * @return - Current build description or the project name if the current build is null. + * + * @return - Current build description or the project name if the current + * build is null. */ public String getBuildDescription() { if (this.currentBuild != null) { @@ -365,7 +415,7 @@ public String getBuildDescription() { /** * Returns the estimated percentage complete of the current build. - * + * * @return - Estimated percentage complete of the current build. */ public long getBuildProgress() { @@ -378,9 +428,10 @@ public long getBuildProgress() { } /** - * Calculates percentage of the current duration to the estimated duration. Caters for the possibility that current duration will be - * longer than estimated duration - * + * Calculates percentage of the current duration to the estimated duration. + * Caters for the possibility that current duration will be longer than + * estimated duration + * * @param duration * - Current running time in milliseconds * @param estimatedDuration @@ -399,7 +450,7 @@ protected long calculatePercentage(final long duration, final long estimatedDura /** * Return pipeline version which is simply the first build's number - * + * * @return pipeline verison */ public String getPipelineVersion() { @@ -420,14 +471,15 @@ public String getPipelineVersion() { /** * Checks whether the user has Build permission for the current project. - * - * @return - true: Has Build permission; false: Does not have Build permission + * + * @return - true: Has Build permission; false: Does not have Build + * permission * @see hudson.model.Item */ public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Hudson.getInstance().isUseSecurity()) { + if (!Jenkins.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -442,14 +494,13 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); + return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) + && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() - && !"PENDING".equals(getCurrentBuildResult()) - && !"BUILDING".equals(getCurrentBuildResult()) - && hasBuildPermission(); + return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) + && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } /** @@ -459,30 +510,37 @@ private boolean upstreamBuildSucceeded() { return this.getUpstreamBuild() != null && HudsonResult.SUCCESS.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * @return upstream build exists and unstable. - */ - private boolean upstreamBuildUnstable() { - return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); - } - + /** + * @return upstream build exists and unstable. + */ + private boolean upstreamBuildUnstable() { + return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); + } /** - * Determine if the project is triggered manually, regardless of the state of its upstream builds - * + * Determine if the project is triggered manually, regardless of the state + * of its upstream builds + * * @return true if it is manual */ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); + } else { + for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { + manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); + if (manualTrigger) { + break; + } + } } return manualTrigger; } /** * Start time of build - * + * * @return start time */ public Date getStartTime() { @@ -504,11 +562,35 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartTime = ""; //$NON-NLS-1$ + String formattedStartDate = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); + formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedStartTime; + return formattedStartDate; + } + + /** + * @return Formatted queued for time + */ + public String getFormattedQueuedForTime() { + String formattedQueuedForTime = ""; //$NON-NLS-1$ + final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); + if (wItem != null) { + formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForTime; + } + + /** + * @return Formatted queued for date + */ + public String getFormattedQueuedForDate() { + String formattedQueuedForDate = ""; //$NON-NLS-1$ + final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); + if (wItem != null) { + formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForDate; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 4dbde52a..63ec3c23 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -3,21 +3,24 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Hudson; +import hudson.plugins.parameterizedtrigger.SubProjectsAction; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import jenkins.model.Jenkins; -import hudson.plugins.parameterizedtrigger.SubProjectsAction; import org.kohsuke.stapler.bind.JavaScriptMethod; /** * @author Centrum Systems - * + * * Representation of a set of projects - * + * */ public class ProjectForm { /** @@ -48,18 +51,33 @@ public class ProjectForm { /** * the latest successful build number */ - private final String lastSuccessfulBuildNumber; + private String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private final Map lastSuccessfulBuildParams; + private Map lastSuccessfulBuildParams; /** * keep reference to the project so that we can update it */ private final AbstractProject project; + /** + * keep reference to the first project in the current pipeline view + */ + private final AbstractProject firstProject; + + /** + * Hold the row this project is placed in (handle duplicate project-ids) + */ + private int row; + + /** + * Hold the column this project is placed in (handle duplicate project-ids) + */ + private int col; + /** * @param name * project name @@ -74,13 +92,25 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; + firstProject = null; } /** * @param project - * project + * - the project to wrap */ public ProjectForm(final AbstractProject project) { + this(project, null); + } + + /** + * @param project + * - the project to wrap + * @param firstProject + * - the first project associated with the grid for this + * projectform + */ + public ProjectForm(final AbstractProject project, final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); @@ -90,14 +120,15 @@ public ProjectForm(final AbstractProject project) { url = pipelineBuild.getProjectURL(); dependencies = new ArrayList(); for (final AbstractProject dependency : project.getDownstreamProjects()) { - dependencies.add(new ProjectForm(dependency)); + dependencies.add(new ProjectForm(dependency, firstProject)); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { - final ProjectForm candidate = new ProjectForm(dependency); - // if subprojects come back as downstreams someday, no duplicates wanted + final ProjectForm candidate = new ProjectForm(dependency, firstProject); + // if subprojects come back as downstreams someday, no + // duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -107,28 +138,53 @@ public ProjectForm(final AbstractProject project) { } this.displayTrigger = true; - final AbstractBuild lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() : lastSuccessfulBuild.getBuildVariables(); + // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are + // used in different pipelines, to avoid wrong parameters in the + // headers) + handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); + this.project = project; + this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * + * This method is only called for a starting project in a pipeline view, + * therefore save it as such. + * * @param p - * project to be wrapped. - * @return - * possibly null. + * project to be wrapped. + * @return possibly null. */ public static ProjectForm as(AbstractProject p) { - return p != null ? new ProjectForm(p) : null; + return p != null ? new ProjectForm(p, p) : null; } public String getName() { return name; } + public int getRow() { + return row; + } + + public int getColumn() { + return col; + } + + /** + * Set the coordinates for this project form. + * + * @param row + * - the row this form is set into + * @param col + * - the column this form is set into + */ + public void setCoords(int row, int col) { + this.row = row; + this.col = col; + } + public String getHealth() { return health; } @@ -154,13 +210,16 @@ public List getDependencies() { } /** - * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both - * so all future jobs will not display the trigger. see main.jelly - * + * Gets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with isTriggerOnlyLatestJob property + * allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first job which + * should show the trigger button will render and then a call will be made + * to 'setDisplayTrigger' to change the value to both so all future jobs + * will not display the trigger. see main.jelly + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -168,15 +227,19 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both - * so all future jobs will not display the trigger. see main.jelly - * + * Sets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with isTriggerOnlyLatestJob property + * allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first job which + * should show the trigger button will render and then a call will be made + * to 'setDisplayTrigger' to change the value to both so all future jobs + * will not display the trigger. see main.jelly + * * @param display - * - boolean to indicate whether the trigger button should be shown + * - boolean to indicate whether the trigger button should be + * shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -218,12 +281,72 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod public String asJSON() { - return ProjectJSONBuilder.asJSON(new ProjectForm(project)); + return ProjectJSONBuilder.asJSON(this); // new ProjectForm(project, + // firstProject)); + } + + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + public Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } + + /** + * Correct the last successful build settings of this form, if that build is + * not on the current BuildPipelineView (when Jobs are used on multiple + * Pipelines) + * + * @param buildGrids + * - the build-grids present on this view + */ + public void correctLastSuccessfulBuilds(Iterable buildGrids) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } + } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + } + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); } + /** + * Set build-number and build-params for last successful build. + * + * @param lastSuccessfulBuild + * - the last successful build (can be null) + */ + private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() + : filterSensitiveBuildVariables(lastSuccessfulBuild); + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java index 88cb906c..d8d7aecb 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java @@ -2,21 +2,31 @@ /** * Two-dimensional placement of {@link ProjectForm}s into a grid/matrix layout. - * + * * This class is also responsible for producing a sequence of {@link BuildGrid}s * that are the instances of the pipelines. - * + * * @author Kohsuke Kawaguchi */ public abstract class ProjectGrid extends Grid { /** * Iterates instances of the pipeline grid view from this project layout. - * - * The caller is only going to iterate {@link BuildGrid}s up to a certain number - * that the user has configured. - * - * + * + * The caller is only going to iterate {@link BuildGrid}s up to a certain + * number that the user has configured. + * + * * @return never null. */ public abstract Iterable builds(); + + /** + * Correct things in project forms due to usage of jobs in multiple + * pipelines + * + * @param buildGrids + * - the buildGrids available to search for each + * lastSuccessfulBuild of a projectform + */ + public abstract void correctProjectForms(Iterable buildGrids); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java index 0bdbddfd..66bc891c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java @@ -2,12 +2,13 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Item; + +import java.io.IOException; + import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; -import java.io.IOException; - /** * Encapsulates the definition of how to layout projects into a {@link ProjectGrid}. * diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java index 9e342e08..0ece8271 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java @@ -26,17 +26,16 @@ import hudson.Extension; import hudson.Launcher; -import hudson.PluginWrapper; import hudson.Util; +import hudson.PluginWrapper; import hudson.model.BuildListener; -import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; import hudson.model.Item; +import hudson.model.ItemGroup; import hudson.model.Items; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Descriptor; -import hudson.model.Hudson; import hudson.model.Project; import hudson.model.listeners.ItemListener; import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; @@ -54,13 +53,14 @@ import java.util.logging.Level; import java.util.logging.Logger; +import jenkins.model.DependencyDeclarer; +import jenkins.model.Jenkins; + +import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import au.com.centrumsystems.hudson.plugin.buildpipeline.Strings; -import hudson.model.ItemGroup; -import jenkins.model.Jenkins; -import org.kohsuke.stapler.AncestorInPath; /** * The build pipeline trigger allows the creation of downstream jobs which aren't triggered automatically. This allows us to have manual @@ -68,7 +68,8 @@ * * @author Centrum Systems */ -public class BuildPipelineTrigger extends Notifier implements DependecyDeclarer { +@SuppressWarnings("unchecked") +public class BuildPipelineTrigger extends Notifier implements DependencyDeclarer { /** * logger */ @@ -258,7 +259,7 @@ public static class DescriptorImpl extends BuildStepDescriptor { * @return true if it is possible to add parameters to the trigger */ public boolean canAddParameters() { - final PluginWrapper plugin = Hudson.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ + final PluginWrapper plugin = Jenkins.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ return plugin != null && plugin.isActive(); } @@ -296,7 +297,7 @@ public String getHelpFile() { * @param context - the context * @return hudson.util.FormValidation */ - public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, + public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, @QueryParameter("downstreamProjectNames") final String value) { final StringTokenizer tokens = new StringTokenizer(Util.fixNull(value), ","); //$NON-NLS-1$ boolean some = false; @@ -322,7 +323,7 @@ public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup co } public List> getBuilderConfigDescriptors() { - return Hudson.getInstance().>getDescriptorList(AbstractBuildParameters.class); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java index 95d267c6..e05da161 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java @@ -33,5 +33,5 @@ public enum HudsonResult { /** * Hudson Result */ - SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, BUILDING, PENDING, MANUAL + SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, QUEUED, BUILDING, PENDING, MANUAL } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java new file mode 100644 index 00000000..3d6eb19f --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java @@ -0,0 +1,244 @@ +package au.com.centrumsystems.hudson.plugin.util; + +import hudson.model.ParameterValue; +import hudson.model.ParametersAction; +import hudson.model.Queue; +import hudson.model.Queue.Item; +import hudson.model.Queue.WaitingItem; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Logger; + +import jenkins.model.Jenkins; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import ch.srsx.timetable.model.IntervalParameterValue; + +/** + * Utitily class to help scheduling Jobs in the context of the build-pipeline + * plugin. + * + * @author rhirt + * + */ +public final class ScheduleUtil { + + /** A Logger object is used to log messages */ + private static final Logger LOGGER = Logger.getLogger(ScheduleUtil.class.getName()); + + /** The format used for specifying time */ + private static DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm"); + + /** Delimiter for time-range on one line */ + public static final String TIMES_DELIMITER = "-"; + + /** Define a comparator for intervals */ + public static class IntervalComparator implements Comparator { + + @Override + public int compare(Interval o1, Interval o2) { + return o1.getStart().compareTo(o2.getStart()); + } + } + + /** + * Calculate a delay, depending on the windows defined for the job. + * + * @param action + * - the action + * @return int - the calculated delay for the job-execution + */ + public static int calcDelay(ParametersAction action) { + + // Check for instances of IntervalParameterValue on the job-definition + if (action == null || (!hasIntervalParameterValues(action))) { + LOGGER.info("No action or intervals present, delay 0"); + return 0; + } + + // Now, determine the deployment-windows on the job + // Derive it from the IntervalParameterValues: + final List intervals = getIntervals(action); + + // Check the intervals for a match + final DateTime now = new DateTime(); + for (Interval interval : intervals) { + if (interval.isAfterNow()) { + // the interval is after now, take the delay from there and + // return + final int delay = (interval.getStart().getMillisOfDay() - now.getMillisOfDay()) / 1000; + LOGGER.info("Now before the next interval, delay is " + delay); + return delay; + } else if (interval.containsNow()) { + // now is inside an interval, no delay necessary + LOGGER.info("Now inside an interval, delay is 0"); + return 0; + } + } + + // We are after the last interval, but have at least one interval + // now take the start of the first interval and add a day to it, + // than take the time from then - now + final DateTime firstOfNextDay = intervals.get(0).getStart().plusDays(1); + final int delay = (int) (firstOfNextDay.getMillis() - now.getMillis()) / 1000; + LOGGER.info("Now before the first interval next day, delay is " + delay); + return delay; + } + + /** + * Retrieve a queued item from the build history + * + * @param name + * the name + * @return Item - the item from the queue for the given name + */ + public static Item getQueuedItem(String name) { + final Queue q = Jenkins.getInstance().getQueue(); + final List qitems = q.getApproximateItemsQuickly(); + + // Now search queued items for given project name + for (Item qitem : qitems) { + if (qitem.task.getDisplayName().equals(name)) { + LOGGER.fine("Found queued item for Task " + name); + return qitem; + } + } + return null; + } + + /** + * Retrieve a queued item of type WaitingItem + * + * @param name + * the name + * @return WaitingItem - the waiting item from the queue for the given name + */ + public static WaitingItem getQueuedWaitingItem(String name) { + final Item qItem = ScheduleUtil.getQueuedItem(name); + if (qItem instanceof WaitingItem) { + return (WaitingItem) qItem; + } + return null; + } + + /** + * Read the provided intervals from the action of the job + * + * @param action + * the action + * @return List - the list of the intervals read from the action, + * sorted ascending + */ + private static List getIntervals(ParametersAction action) { + final List intervals = new ArrayList(); + final List pvs = action.getParameters(); + for (ParameterValue pv : pvs) { + if (pv instanceof IntervalParameterValue) { + intervals.add(((IntervalParameterValue) pv).getValue()); + } + } + Collections.sort(intervals, new ScheduleUtil.IntervalComparator()); + return intervals; + } + + /** + * Parse a given time of the format HH:mm and convert it into a DateTime + * today + * + * @param value + * - the value to parse + * @return DateTime - the parsed time, moved into today + * */ + public static DateTime parseTime(String value) { + final DateTime today = new DateTime(); + try { + return fmt.parseDateTime(value.trim()).withDate(today.getYear(), today.getMonthOfYear(), today.getDayOfMonth()); + } catch (IllegalArgumentException iaex) { + LOGGER.warning("Time could not be parsed: " + value); + } + return null; + } + + /** + * Parse the given String to create an interval from a compact + * representation in the form (HH:mm - HH:mm) + * + * @param value + * - the value in the format HH:mm - HH:mm + * @return Interval - the interval parsed + */ + public static Interval parseInterval(String value) { + final String[] timeValues = value.split(TIMES_DELIMITER); + if (timeValues.length == 2) { + return new Interval(parseTime(timeValues[0]), parseTime(timeValues[1])); + } + return null; + } + + /** + * Format the start time of the given interval in the form (HH:mm) + * + * @param value + * - the interval from which the start-time will be formatted + * @return String - the formatted String + */ + public static String formatStartTime(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getStart()); + } + + /** + * Format the end time of the given interval in the form (HH:mm) + * + * @param value + * - the interval from which the end-time will be formatted + * @return String - the formatted String + */ + public static String formatEndTime(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getEnd()); + } + + /** + * Format the given interval for a compact representation in the form (HH:mm + * - HH:mm) + * + * @param value + * - the interval to be formatted + * @return String - the formatted String + */ + public static String formatInterval(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getStart()) + TIMES_DELIMITER + fmt.print(value.getEnd()); + } + + /** + * Check whether an IntervalParameterValue is present on the job + * + * @param action + * - the action + * @return boolean - whether IntervalParameterValue(s) are present + */ + private static boolean hasIntervalParameterValues(ParametersAction action) { + final List pvs = action.getParameters(); + for (ParameterValue pv : pvs) { + if (pv instanceof IntervalParameterValue) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java new file mode 100644 index 00000000..ccfa129c --- /dev/null +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java @@ -0,0 +1,220 @@ +package ch.srsx.timetable.model; + +import hudson.Extension; +import hudson.model.ParameterValue; +import hudson.model.ParameterDefinition; +import hudson.util.FormValidation; + +import java.util.Date; + +import net.sf.json.JSONObject; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.export.Exported; + +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; +import ch.srsx.timetable.Messages; + +/** + * @author rhirt + */ +public class IntervalParameterDefinition extends ParameterDefinition { + /** Serial ID */ + private static final long serialVersionUID = -8142303813745641667L; + + /** Default value */ + private Interval defaultValue; + + /** + * Constructor requesting name, startTime, endTime and description + * + * @param name + * the name + * @param startTime + * - string in the format HH:mm for the start of the interval + * @param endTime + * - string in the format HH:mm for the end of the interval + * @param description + * the description + */ + @DataBoundConstructor + public IntervalParameterDefinition(String name, String startTime, String endTime, String description) { + super(name, description); + this.defaultValue = new Interval(ScheduleUtil.parseTime(startTime), ScheduleUtil.parseTime(endTime)); + } + + /** + * Constructor requesting name, interval and description + * + * @param name + * the name + * @param value + * - interval to derive this interval from + * @param description + * the description + */ + public IntervalParameterDefinition(String name, Interval value, String description) { + super(name, description); + this.defaultValue = value; + } + + @Override + public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { + if (defaultValue instanceof IntervalParameterValue) { + final IntervalParameterValue value = (IntervalParameterValue) defaultValue; + return new IntervalParameterDefinition(getName(), value.getValue(), getDescription()); + } else { + return this; + } + } + + /** + * @return String - text representation of the time value at the start of + * the interval + */ + public String getFormattedInterval() { + return ScheduleUtil.formatInterval(defaultValue); + } + + /** + * @return Date - the start of the interval as a date + */ + @Exported + public Date getIntervalFrom() { + if (defaultValue == null) { + return null; + } + return new Date(defaultValue.getStartMillis()); + } + + /** + * @return String - text representation of the time value at the start of + * the interval + */ + public String getIntervalFromText() { + return ScheduleUtil.formatStartTime(defaultValue); + } + + /** + * @return Date - the end of the interval as a date + */ + @Exported + public Date getIntervalTo() { + if (defaultValue == null) { + return null; + } + return new Date(defaultValue.getEndMillis()); + } + + /** + * @return String - text representation of the time value at the end of the + * interval + */ + public String getIntervalToText() { + return ScheduleUtil.formatEndTime(defaultValue); + } + + @Override + public IntervalParameterValue getDefaultParameterValue() { + return new IntervalParameterValue(getName(), defaultValue, getDescription()); + } + + @Override + public ParameterValue createValue(StaplerRequest req) { + // TODO Auto-generated method stub + // FileItem src; + // try { + // src = req.getFileItem( getName() ); + // } catch (ServletException e) { + // // Not sure what to do here. We might want to raise this + // // but that would involve changing the throws for this call + // return null; + // } catch (IOException e) { + // // ditto above + // return null; + // } + // if ( src == null ) { + // // the requested file parameter wasn't uploaded + // return null; + // } + // FileParameterValue p = new FileParameterValue(getName(), src, + // getFileName(src.getName())); + // p.setDescription(getDescription()); + // p.setLocation(getName()); + // return p; + return null; + } + + @Override + public ParameterValue createValue(StaplerRequest req, JSONObject jo) { + final IntervalParameterValue value = req.bindJSON(IntervalParameterValue.class, jo); + value.setDescription(getDescription()); + return value; + } + + /** + * @param value + * the value + * @return the interval parameter value object created + */ + public IntervalParameterValue createValue(Interval value) { + return new IntervalParameterValue(getName(), value, getDescription()); + } + + /** + * DescriptorImpl for this parameter definition + * + * @author rhirt + * + */ + @Extension + public static class DescriptorImpl extends ParameterDescriptor { + @Override + public String getDisplayName() { + return Messages.IntervalParameterDefinition_DisplayName(); + } + + @Override + public String getHelpFile() { + return "/plugin/build-pipeline-plugin/help/parameter/interval.html"; + } + + /** + * Checks if parameterised build intervals are valid. + * + * @param value + * - the value to check + * @return FormValidation - status whether validation succeeded + */ + public FormValidation doCheckTime(@QueryParameter String value) { + if (ScheduleUtil.parseTime(value) == null) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); + } + return FormValidation.ok(); + } + + /** + * Checks if parameterised build intervals are valid. + * + * @param fromValue + * - the value (from) to check + * @param toValue + * - the value (to) to check + * @return FormValidation - status whether validation succeeded + */ + public FormValidation doCheckInterval(@QueryParameter String fromValue, @QueryParameter String toValue) { + final DateTime from = ScheduleUtil.parseTime(fromValue); + final DateTime to = ScheduleUtil.parseTime(toValue); + if (from == null || to == null) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); + } else if (!from.isBefore(to)) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalInterval()); + } + return FormValidation.ok(); + } + } +} \ No newline at end of file diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java new file mode 100644 index 00000000..9094d796 --- /dev/null +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java @@ -0,0 +1,165 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Luca Domenico Milanesio, Tom Huybrechts + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package ch.srsx.timetable.model; + +import hudson.EnvVars; +import hudson.model.ParameterValue; +import hudson.model.AbstractBuild; +import hudson.model.Run; +import hudson.util.VariableResolver; + +import java.util.Locale; + +import org.joda.time.Interval; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.export.Exported; + +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + +/** + * {@link ParameterValue} created from {@link IntervalParameterDefinition}. + * + * @author rhirt + */ +public class IntervalParameterValue extends ParameterValue { + /** The serial id */ + private static final long serialVersionUID = 1L; + + /** the interval in question */ + @Exported(visibility = 4) + private final Interval value; + + /** + * Constructor taking name and value. + * + * @param name + * the name + * @param value + * the value, given as a formatted String (HH:mm - HH:mm) + */ + @DataBoundConstructor + public IntervalParameterValue(String name, String value) { + this(name, value, null); + } + + /** + * Constructor taking name, value and description. + * + * @param name + * the name + * @param value + * the value, given as a formatted String (HH:mm - HH:mm) + * @param description + * the description + */ + public IntervalParameterValue(String name, String value, String description) { + super(name, description); + this.value = ScheduleUtil.parseInterval(value); + } + + /** + * Constructor taking name, interval and description. + * + * @param name + * the name + * @param interval + * the value, given as an interval object + * @param description + * the description + */ + public IntervalParameterValue(String name, Interval interval, String description) { + super(name, description); + this.value = interval; + } + + /** + * Exposes the name/value as an environment variable. + * + * @param build + * - the run + * @param env + * - the environment + */ + @Override + public void buildEnvironment(Run build, EnvVars env) { + env.put(name, value.toString()); + env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); // backward + // compatibility + // pre + // 1.345 + } + + @Override + public VariableResolver createVariableResolver(AbstractBuild build) { + return new VariableResolver() { + public String resolve(String name) { + return IntervalParameterValue.this.name.equals(name) ? ScheduleUtil.formatInterval(value) : null; + } + }; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final IntervalParameterValue other = (IntervalParameterValue) obj; + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "(IntervalParameterValue) " + getName() + "='" + value.toString() + "'"; + } + + @Override + public String getShortDescription() { + return name + '=' + ScheduleUtil.formatInterval(value); + } + + public Interval getValue() { + return value; + } +} diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 4b13d6f5..382cbc63 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -38,17 +38,28 @@ -
- {{build.startDate}} {{build.startTime}} -
+ {{#if build.isQueued}} +
+ {{build.queuedForDate}} {{build.queuedForTime}} +
+ {{else}} +
+ {{build.startDate}} {{build.startTime}} +
+ {{/if}}
+ {{#if build.isQueued}} + + {{build.queueWaitDuration}} + + {{/if}} {{#if build.isComplete}} - + {{build.duration}} {{/if}} {{#if build.isBuilding}} - + {{build.duration}} {{/if}} @@ -77,34 +88,37 @@
+ {{#if build.isManualTrigger}} + This job is manually triggered + {{/if}} {{#if project.parameters.length}} This job has parameters {{/if}} {{#unless build.isPending}} {{#unless build.isReadyToBeManuallyBuilt}} - - - - - console - - - - - console - - - - - console - - - + {{#unless build.isQueued}} + + + + + console + + + + + console + + + + + console + + + + {{/unless}} {{/unless}} {{/unless}} - - {{#if build.isRerunnable}} {{#if build.isSuccess}} {{#if ${!from.triggerOnlyLatestJob}}} @@ -143,6 +157,11 @@ {{/if}} {{/if}} + {{#if build.isQueued}} + + cancel this build + + {{/if}}
{{/unless}} {{#if project.disabled}} @@ -228,20 +247,28 @@
- -
+ +
Trigger a Pipeline
-
- + + - +
Run
+ +
+
+ M2Release in a Pipeline +
+
Release
+
+
@@ -300,66 +327,103 @@ - - + + - + + + + - - - - + + + + + + + + + + + - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -386,7 +450,7 @@
- + @@ -415,12 +479,31 @@
${buildParam.getKey()}: ${buildParam.getValue()}
-
- + + +
+ + + + + + + + - + + + + + + + + + + + - - + - - - - + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/srsx/timetable/Messages.properties b/src/main/resources/ch/srsx/timetable/Messages.properties new file mode 100644 index 00000000..7073c54d --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/Messages.properties @@ -0,0 +1,4 @@ +IntervalParameterDefinition.DisplayName=Interval Parameter +IntervalParameterDefinition.IllegalInterval=Illegal Interval. +IntervalParameterDefinition.IllegalTime=Illegal Time-Format (should be HH:mm). + diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly new file mode 100644 index 00000000..2badb0d2 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties new file mode 100644 index 00000000..b218a253 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties @@ -0,0 +1,4 @@ +Name=Name +Interval\ From=Interval Startzeit +Interval\ To=Interval Endzeit +Description=Beschreibung diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html new file mode 100644 index 00000000..d4f5f8b6 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html @@ -0,0 +1,3 @@ +
+ The start-time of the interval to be used for allowed job-execution. +
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html new file mode 100644 index 00000000..d4f5f8b6 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html @@ -0,0 +1,3 @@ +
+ The start-time of the interval to be used for allowed job-execution. +
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly new file mode 100644 index 00000000..daddbb86 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly @@ -0,0 +1,35 @@ + + + + + +
+ + +
+
+
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly new file mode 100644 index 00000000..1a8e24f0 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 9e27519e..a7e3982a 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -61,7 +61,7 @@ height: 48px; margin-left: auto; margin-right: auto; - width: 340px; + width: 392px; } .icon-container { @@ -132,6 +132,11 @@ } .build-duration-container { + text-align: left; + text-overflow: clip; + overflow: hidden; + white-space: nowrap; + font-size: 0.9em; width: 135px; } @@ -285,6 +290,11 @@ tr.spacerRow td { color: #222; } +.QUEUED { + background-color: #eb642d; + color: #222; +} + .BUILDING { background-color: #f7f94b; color: #222; @@ -372,3 +382,132 @@ a:hover { .hidden { display: none; } + +.tile { + background: url(../images/tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileCrossBottom { + background: url(../images/tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileCross { + background: url(../images/tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileDown { + background: url(../images/tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileLeft { + background: url(../images/tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileRight { + background: url(../images/tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileTopRight { + background: url(../images/tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} + +.hTile { + background: url(../images/h-tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileCrossBottom { + background: url(../images/h-tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileCross { + background: url(../images/h-tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileDown { + background: url(../images/h-tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileLeft { + background: url(../images/h-tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileRight { + background: url(../images/h-tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileTopRight { + background: url(../images/h-tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} + +.rTile { + background: url(../images/r-tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileCrossBottom { + background: url(../images/r-tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileCross { + background: url(../images/r-tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileDown { + background: url(../images/r-tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileLeft { + background: url(../images/r-tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileRight { + background: url(../images/r-tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileTopRight { + background: url(../images/r-tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} diff --git a/src/main/webapp/help/parameter/interval.html b/src/main/webapp/help/parameter/interval.html new file mode 100644 index 00000000..7f4c7b9f --- /dev/null +++ b/src/main/webapp/help/parameter/interval.html @@ -0,0 +1,4 @@ +
+ Defines an interval parameter, where users can enter a start time and an end time, + which you can use to identify times when job execution is allowed. +
\ No newline at end of file diff --git a/src/main/webapp/images/h-tile-cross-bottom.png b/src/main/webapp/images/h-tile-cross-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..c09666e727ed14d85ac1d79947955da0238bc8c9 GIT binary patch literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH*D1Jf!`7srr_xVLu$^Hw=XI3ylC!n^bT4yNl# zj}~_ZxzCs)wZLN0w*Qv#MT@s@ZLUgTtJrw!ZP{6`hga{eZ8A1#S*Xavo0!4j>?|R5 z=z>67n*sfVLfQ{6j9h>HZvE+4z3I1eW9`4+jLe!7^#0zzpEdjLPBY1vl@=O$HMi$W z{^pw{`+sWMF0+#BuX``*FxO7>yvO`$YwGLQeKG%jeqqX-lowlhdgu4%E-(3AfAqrV z^~>*d?Av(!YyGr$_P^A6dv{)$ZEJSz%InCg->UDn+kgMrvv=KXCzJFEJN5THY31$R zeZ?aA*s=4;Q*!E0D4$>XzV3JN{PQ;++q{AX$6v>#ufHpQnw9&4Kk8LOzSlnk$zQKz ztFJBJtMcV#$*Xs|$tORyEm~&#`fBy{D?irXD3de3-}dj-|F>ng@6Jx&f4}_uqrz8h z9P+a^{5vOT_ht36D;$%-d)jUDS7elikz%fNqttoUQNa?#|SYylfMc&pA&e%?%|Oep!4(ma{n8zzhr%7eYx$y1Aphm RtAR!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?cI&M%?2V4j%WT^Zxm*Bn8I<& zLo-OFuDntU0%LPFvcMiHT48PTWmkDYRmfbjSIdklW zKErkXX=x=0N9^H!d&_kC_Jy&#>yDd8N!+b}FmK)Q!adh+FSkEXJb(Fr0)wZkpUXO@geCy0SD-fl literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-down.png b/src/main/webapp/images/h-tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..88fe3a2fd0d541e05a31c226111925e5037ed91a GIT binary patch literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hU$6V@O5Z+dI~IEd~s1i5$}`_`a{VV0`Yl z$bhpe+;1YswlnshO+Q{!wohVtyldTafvm`B3#VMFyL)@v+TKq!TlP=9*EvVeYRj${ z7oJP**50dYRkC(#uZV2btYv1Cs!dqbzF73-9%&;^0EHUpEEg^E19 zi5VQu#0Yh~xUkRnrfd7R+*3fC@`KCXT?8A#qPFF|yYr8_Edp?}l&rS!n*!bPd*Akn zi=5AYzT?)#_xAE?zG8{bTq1|G0~B$%AfDsN#zfzX=C`oLtt N;OXk;vd$@?2>@=z_&@*v literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-left.png b/src/main/webapp/images/h-tile-left.png new file mode 100644 index 0000000000000000000000000000000000000000..08a0b263316f9001a5ed1a69a17a496819558a56 GIT binary patch literal 476 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1|(&F^?3p*#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0rOCF{C2y?Onxn%?=E%fh!y~{(rlH@!CZx znTong8yc3anqS>F)28;QN^)Avw5KvVwjM7nN>*a;kWnaVIKfC?M$hYA^FG_ePxhBR zZusVR-P-$RdAG~_E|>V)eXX%;zy7*(?fq{8KmOf0iR!KoeKviMs|)<+neP9xex7M< sd42u6CkzvrUHAkn9q7uqE!}3nZoaapp*V2m($y85}Sb4q9e02OPUkpKVy literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-right.png b/src/main/webapp/images/h-tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c03f7b42e9dd82f5dc0a89728ca16da023390482 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA}&+iyz!UFFtubAIjp^#@=7y{E6QUeasT^SQ>q z=9NWXdTZSE(wy66K#q<7>fH&m)D~*D^a)rwz2I1=3}H;)z3zYY4B?CekNp!lvI6;>1s;*b3=DjSK$uZf!>fUTfr-V_#WAEJ?(N;|ye0<`hr~GTLx0;vH>fP= zo4c|l+|{H#~pz@{>%D^DcdQB^h@m zr4C&XXxpbW=QMBaR-MFrn>q=wNSlF4%RukTv)Qu$=#_vj$ibXphio- zLu&$x0-z;4yr_E6gckmnz4Wx`WbyMepJPnVW^enOIVbP0l+XkKCiwmg literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile.png b/src/main/webapp/images/h-tile.png new file mode 100644 index 0000000000000000000000000000000000000000..64a3d1d384fbb9f0189ad24095daad968cc40abb GIT binary patch literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1SBWTkdy;bjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4cwkCjv*CsZ!Z`!GBEHQUhvaAG`skqs7gWi frLC7o6x?KMmB*zw+b=N&7*-6Pu6{1-oD!MPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rg2lK~y+Tos-{F z5>XV#`OEq%>JO-%ee9VT2p>Y-vCYsWHAT&78x{r#h?uojhDrG)phJKyl(aSptMR}C z58w9O3mc?OO%Irhn{vMjUMXf#0Yf3KZ-AgFLrT!L@q z8I))e>C6F((&{?AbMw$N%__S=FdPn{C<<1r!s;gjr{_PeQSbC1hhp&i{TK`eR@;Pt zEjJWJ^ZW`@qRgN?cBo@9oX4w7ojnt3Hjh9c@JnDA2E?b!sGW6jUr6BAgB`}5*|41x z@_ro+8zQU3m&pK zvz)ebij~hjMoIq;x7*DHl}ZKvjct_My?;iIyg@3J;sS#1d{oV%)b9Qjt=xpq=L6kd zTR?+CFT-#5QEYYpNHSA^D2kxNr#@RijOSrVT0=0ghx9=e1^ohQzJ^drgY8z$};rI!!lvI6;>1s;*b3=DjSK$uZf!>a+PVV|dqV@O5Z+q<^ew*w>^0yPV7AKBu(@c;kg zDd%P>Mla`E_+a7D(%Jmi{X|YYdGp+|fJ9sZ#zWiJAx$~>X edmqnQ_P->>BsND}{68>y89ZJ6T-G@yGywpY8rk&# literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-cross.png b/src/main/webapp/images/r-tile-cross.png new file mode 100644 index 0000000000000000000000000000000000000000..6e268563430bf92b98b8d6504818b902670302eb GIT binary patch literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0Q69F{C2y?cKG0M;s&^F7k15KKwhMW3O1F zv$6n(QF57GR>A*#^23%s0V}5$91E4{%ZOM%{rlgS zKR?!_E}H-Ter|1D&D-nU>#u$6U6>cV{lw?U#meU{TsM6C?9j}$3Fl7 literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-down.png b/src/main/webapp/images/r-tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb5c59052e312c6b5c711603a158302cebd5987 GIT binary patch literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0`gFF{C2y?G;5oXGal-i!c2-0@@F4_}{wV zCWBaTr?$YPX<~H+0ruZzJ{wJbo|k6k-+%tN;mX@@zwW5tA$@vNWd6S*ncdT`f86#z z;{4~xYnDrAzrCvYsp`k|PkXI5uXO&u!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~ln2F{C2y?cI%D%#I=s7w@Vx{rDfBGWXh{ zbElkIM7%f%=EMNzrxSYsw=m*Pp7NfVFC8(%e1HLyRIt#)atDl1x5~or>mdK II;Vst0L=5LI{*Lx literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-right.png b/src/main/webapp/images/r-tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..495819724f8414097fdccd0c16a004b7eff5956e GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA>Y%*F{C2y?OjJNW=8>si+z%YoP~-N{}1mt z$66#PDC5cUoZ;ZzGxhwlmR*yt3Y>i})^6SQgU@Vr&85{#S}q7WxpT-W(v@*!)jnsd zd2z=ce%a$=^Xm2I>ez+%@15WOymV&86`TJRySSHS>X)>lx@>P{{ci*93k_G-I{dWv jFCP$hw_M;%zQed+n@r2UAL(y_@xkEf>gTe~DWM4f^j)5T literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-top-right.png b/src/main/webapp/images/r-tile-top-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e5a7c24f4c0eed81713b2349514c71abb563b5 GIT binary patch literal 569 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hd+7V@O5Z+q)b6L;^(`9-eMGCDh~L#KZaU z@BG8J#5Zi+xFz~xtV63KXXkH*@4xq_6{%hRJ2UvsGr5{yKYrHvWYvC3tJzUyAGW&r z>Dm8x`q#(r>wQ04{fdz#-B!lvI6;>1s;*b3=DjSK$uZf!>a+PVY#P^V@O5Z+q-+cjyOm-TugBcu)e7NVQnqz z{KL1-UAyFU^IZI$9q*Qjz25m;gmZG{O z*vilA$&h8X|04S0{k~_L|G&+xye+qm%e`a=lWgUe|Ic1k-HM4@zk2`MIJxC_KR@0j z)o)CHkAXrn>BWWp$qm!)Uf=cWw9y-lC5tnEF}5x@!lvI6;>1s;*b3=DjSK$uZf!>a+Pp}^C{F{C2y?Oj7JXG4aDhf5SZw#(huuHZ{I zUw>h1bf#$Kv|oM`|Nm}lN%^bTs5A3>|MuJsdk))ewpgg#(kEc$^n!!FjA!4U|2FGi zUVE}@+w0Ub_dj1Zi;MfNJMBwxwai_m_3tmFVdQ&MBb@0AzKl6aWAK literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-down.png b/src/main/webapp/images/tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd1c8e3e66dc9f53026c5479e05b77d4e773b3d GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~=(5F{C2y?G-~W7DoYxi|<&4jg#L$sNLJY zary^_*}5kdE#_l(TUYu2`=mv)PT75_sjs|uY|*p7hb?UFdMkHX$Nk&X+rRz##@KVm z_szR(IQ@%o)w%yl^|Ooj$KJfOu>A9k?K;1g|BbRYv0kX$(kEc$^nzn#Gp6zFjXjtA ldym|0-P?7$*BS0(Fx@TYxi~g!lvI6;>1s;*b3=DjSK$uZf!>a+Pq1@BOF{C2y?cI%D&V~{V7iT*;-l?gHUwHoE zn`gH!o_wUHaYOscZ-%2MpINg{`f&cfz==s8e}37a=aajAu8+?XhKbBBd;*pZbY(Pr z{8|%n{O@72qP1Ti|J{^#@JC&330~jw1p00i_>zopr0P*>zC;$Ke literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-right.png b/src/main/webapp/images/tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..59e18e64648106b66693ba866eef23d7de9daff8 GIT binary patch literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?Oj8zLk{vXk}v(^6ma2m5TE{rsyD@0xoS_t(t1An4@IA*)!@LSIJrFTTvp_BDL_ zXTIB>+A8P&Jke&}bMg3U+n#qVZ~b0yEmUsl6R>i6!2xDey?s_XLp0;Sqp$^k&c8sC ffUBSfBT?eso(^gIj9W#ZSYYsU^>bP0l+XkKhG(GI literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-top-right.png b/src/main/webapp/images/tile-top-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e7580c793f9a26808f3b699da60e1ea411f55e05 GIT binary patch literal 652 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>fUTfl1NR#WAEJ?(JP)ze5f(4j*SKbrzcShZY47uD}PF{6;_iJmes41MeYxeCayHjTu z`*_Yi-X1dVTCUZ+b$i>cw4eJK@j4ef|P{Rp$h_SZ3ZSS z3l({I6EirRooOc|XjYHdk$()U+7Q{c3`x`EMb8`94R|)UzZmk#BE`G52UG9q5JI$w-FFOA> u;E#Bsvpe_K=tSr2`E&>|)Ch6%ntw;gzy4>B6)*uYFnGH9xvX Date: Mon, 12 May 2014 07:33:40 +0200 Subject: [PATCH 02/21] Revert "I-rh01 to I-rh07 (see separate notes)" This reverts commit a1b25b0027e9534a5e8463fadfe387a19f85a840. --- pom.xml | 52 ++-- .../buildpipeline/BuildJSONBuilder.groovy | 9 +- .../buildpipeline/ProjectJSONBuilder.groovy | 2 - .../plugin/buildpipeline/BuildForm.java | 63 +--- .../plugin/buildpipeline/BuildGrid.java | 9 - .../buildpipeline/BuildPipelineForm.java | 12 +- .../buildpipeline/BuildPipelineView.java | 247 +++++++--------- .../buildpipeline/DefaultBuildGridImpl.java | 35 +-- .../buildpipeline/DefaultProjectGridImpl.java | 17 +- .../DownstreamProjectGridBuilder.java | 92 +++--- .../hudson/plugin/buildpipeline/Grid.java | 31 -- .../plugin/buildpipeline/PipelineBuild.java | 236 +++++---------- .../plugin/buildpipeline/ProjectForm.java | 195 +++---------- .../plugin/buildpipeline/ProjectGrid.java | 24 +- .../buildpipeline/ProjectGridBuilder.java | 5 +- .../trigger/BuildPipelineTrigger.java | 21 +- .../hudson/plugin/util/HudsonResult.java | 2 +- .../hudson/plugin/util/ScheduleUtil.java | 244 ---------------- .../model/IntervalParameterDefinition.java | 220 -------------- .../model/IntervalParameterValue.java | 165 ----------- .../buildpipeline/BuildPipelineView/bpp.jelly | 275 ++++++------------ .../ch/srsx/timetable/Messages.properties | 4 - .../IntervalParameterDefinition/config.jelly | 41 --- .../config_de.properties | 4 - .../help-endTime.html | 3 - .../help-startTime.html | 3 - .../IntervalParameterDefinition/index.jelly | 35 --- .../model/IntervalParameterValue/value.jelly | 33 --- src/main/webapp/css/main.css | 141 +-------- src/main/webapp/help/parameter/interval.html | 4 - .../webapp/images/h-tile-cross-bottom.png | Bin 789 -> 0 bytes src/main/webapp/images/h-tile-cross.png | Bin 464 -> 0 bytes src/main/webapp/images/h-tile-down.png | Bin 609 -> 0 bytes src/main/webapp/images/h-tile-left.png | Bin 476 -> 0 bytes src/main/webapp/images/h-tile-right.png | Bin 465 -> 0 bytes src/main/webapp/images/h-tile-top-right.png | Bin 623 -> 0 bytes src/main/webapp/images/h-tile.png | Bin 374 -> 0 bytes src/main/webapp/images/manual.png | Bin 608 -> 0 bytes .../webapp/images/r-tile-cross-bottom.png | Bin 553 -> 0 bytes src/main/webapp/images/r-tile-cross.png | Bin 497 -> 0 bytes src/main/webapp/images/r-tile-down.png | Bin 500 -> 0 bytes src/main/webapp/images/r-tile-left.png | Bin 487 -> 0 bytes src/main/webapp/images/r-tile-right.png | Bin 474 -> 0 bytes src/main/webapp/images/r-tile-top-right.png | Bin 569 -> 0 bytes src/main/webapp/images/r-tile.png | Bin 380 -> 0 bytes src/main/webapp/images/tile-cross-bottom.png | Bin 530 -> 0 bytes src/main/webapp/images/tile-cross.png | Bin 475 -> 0 bytes src/main/webapp/images/tile-down.png | Bin 493 -> 0 bytes src/main/webapp/images/tile-left.png | Bin 482 -> 0 bytes src/main/webapp/images/tile-right.png | Bin 464 -> 0 bytes src/main/webapp/images/tile-top-right.png | Bin 652 -> 0 bytes src/main/webapp/images/tile.png | Bin 380 -> 0 bytes src/main/webapp/js/build-pipeline.js | 16 +- 53 files changed, 397 insertions(+), 1843 deletions(-) delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java delete mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java delete mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java delete mode 100644 src/main/resources/ch/srsx/timetable/Messages.properties delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly delete mode 100644 src/main/webapp/help/parameter/interval.html delete mode 100644 src/main/webapp/images/h-tile-cross-bottom.png delete mode 100644 src/main/webapp/images/h-tile-cross.png delete mode 100644 src/main/webapp/images/h-tile-down.png delete mode 100644 src/main/webapp/images/h-tile-left.png delete mode 100644 src/main/webapp/images/h-tile-right.png delete mode 100644 src/main/webapp/images/h-tile-top-right.png delete mode 100644 src/main/webapp/images/h-tile.png delete mode 100644 src/main/webapp/images/manual.png delete mode 100644 src/main/webapp/images/r-tile-cross-bottom.png delete mode 100644 src/main/webapp/images/r-tile-cross.png delete mode 100644 src/main/webapp/images/r-tile-down.png delete mode 100644 src/main/webapp/images/r-tile-left.png delete mode 100644 src/main/webapp/images/r-tile-right.png delete mode 100644 src/main/webapp/images/r-tile-top-right.png delete mode 100644 src/main/webapp/images/r-tile.png delete mode 100644 src/main/webapp/images/tile-cross-bottom.png delete mode 100644 src/main/webapp/images/tile-cross.png delete mode 100644 src/main/webapp/images/tile-down.png delete mode 100644 src/main/webapp/images/tile-left.png delete mode 100644 src/main/webapp/images/tile-right.png delete mode 100644 src/main/webapp/images/tile-top-right.png delete mode 100644 src/main/webapp/images/tile.png diff --git a/pom.xml b/pom.xml index 15635f17..17943d09 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 1.558 + 1.509.3 build-pipeline-plugin 1.4.3-SNAPSHOT @@ -95,7 +95,7 @@ org.hamcrest hamcrest-core - 1.3 + 1.2 test @@ -109,11 +109,6 @@ parameterized-trigger 2.17 - - org.jenkins-ci.plugins.m2release - m2release - 0.14.0 - org.seleniumhq.selenium @@ -148,11 +143,6 @@ 1.8.3 test - - joda-time - joda-time - 1.6 - @@ -247,25 +237,25 @@ - - - - - - - - - - - - - - - - - - - + + org.codehaus.mojo + findbugs-maven-plugin + 2.3.2 + + + Max + Medium + true + findbugs-exclude.xml + + + + + check + + + + org.apache.maven.plugins maven-release-plugin diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy index cca3eb52..af7e0d3a 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy @@ -18,10 +18,9 @@ class BuildJSONBuilder { extId(pipelineBuild.currentBuild?.externalizableId) hasPermission(pipelineBuild.project?.hasPermission(Item.BUILD)); hasUpstreamBuild(null != pipelineBuild.upstreamBuild) - isQueued(buildStatus == 'QUEUED') isBuilding(buildStatus == 'BUILDING') - isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL' && buildStatus != 'QUEUED') - isPending(buildStatus == 'PENDING') + isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL') + isPending(buildStatus == 'PENDING') isSuccess(buildStatus == 'SUCCESS') isReadyToBeManuallyBuilt(pipelineBuild.isReadyToBeManuallyBuilt()) isManualTrigger(pipelineBuild.isManualTrigger()) @@ -38,10 +37,6 @@ class BuildJSONBuilder { url(pipelineBuild.buildResultURL ? pipelineBuild.buildResultURL : pipelineBuild.projectURL) userId(pipelineBuild.currentBuild?.getCause(Cause.UserIdCause.class)?.getUserId()) estimatedRemainingTime(pipelineBuild.currentBuild?.executor?.estimatedRemainingTime) - queuedForDate(pipelineBuild.formattedQueuedForDate) - queuedForTime(pipelineBuild.formattedQueuedForTime) - queueWaitDuration(pipelineBuild.queueWaitDuration) - queueId(pipelineBuild.queueId) } project { disabled(pipelineBuild.projectDisabled) diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy index 5b710643..a8a6fc2e 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy @@ -16,8 +16,6 @@ class ProjectJSONBuilder { def builder = new JsonBuilder() def root = builder { id(projectForm.id) - row(projectForm.row) - col(projectForm.col) name(projectForm.name) health(projectForm.health) url(projectForm.url) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 1e935fc8..163e7dd9 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -3,20 +3,14 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.ParametersDefinitionProperty; -import hudson.model.Queue.WaitingItem; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; - /** * @author Centrum Systems * @@ -55,11 +49,12 @@ public class BuildForm { */ private List dependencies = new ArrayList(); + /** * project stringfied list of parameters for the project * */ private final ArrayList parameters; - + /** * @param pipelineBuild * pipeline build domain used to see the form @@ -128,28 +123,6 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); - } else { - // try to see if the build went into queueing - final WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(pipelineBuild.getProject().getDisplayName()); - if (wItem != null) { - updated = true; - } - } - return updated; - } - - /** - * - * @param queueId the id of the queue-item that was cancelled - * - * @return is the queued item cancelled. - */ - @JavaScriptMethod - public boolean cancelQueued(final int queueId) { - boolean updated = false; - final WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(pipelineBuild.getProject().getDisplayName()); - if (wItem == null) { - updated = true; } return updated; } @@ -162,10 +135,6 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } - public String getFullBuildName() { - return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); - } - @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -174,11 +143,7 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - - public Map getFilteredParameters() { - return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); - } - + public ArrayList getParameterList() { return parameters; } @@ -187,26 +152,4 @@ public Integer getProjectId() { return projectId; } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - private Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } - } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java index 53d76a0c..40c11deb 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java @@ -1,7 +1,5 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import hudson.model.AbstractBuild; - /** * {@link Grid} of {@link BuildForm}s, which represents one instance * of a pipeline execution. @@ -9,11 +7,4 @@ * @author Kohsuke Kawaguchi */ public abstract class BuildGrid extends Grid { - /** - * Search for a BuildForm by a given Build in the current BuildGrid - * - * @param build - the build to search for in the BuildGrid - * @return BuildForm the form containing the given build - */ - public abstract BuildForm findBuildForm(AbstractBuild build); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java index a7eff673..4b4918b9 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java @@ -1,9 +1,10 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import com.google.common.collect.Iterables; + import java.util.Arrays; import java.util.List; - -import com.google.common.collect.Iterables; +import java.util.logging.Logger; /** * @author Centrum Systems @@ -12,6 +13,11 @@ * */ public class BuildPipelineForm { + /** + * logger + */ + private static final Logger LOGGER = Logger.getLogger(BuildPipelineForm.class.getName()); + /** * projects laid out in a grid using maps to ease accessing (or maybe I made it way too complicated by not using a 2-dimensional array) * Outside map holds rows and inner map has ProjectForm at a particular position (defined with key) @@ -32,8 +38,6 @@ public class BuildPipelineForm { public BuildPipelineForm(final ProjectGrid grid, final Iterable builds) { projectGrid = grid; buildGrids = Arrays.asList(Iterables.toArray(builds, BuildGrid.class)); - // Handle lastSuccessful Builds correctly, when some jobs are used in multiple Views - grid.correctProjectForms(builds); } public ProjectGrid getProjectGrid() { diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index e95b2ac0..3e9f56c1 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -24,6 +24,8 @@ */ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import com.google.common.collect.Iterables; + import hudson.Extension; import hudson.model.Action; import hudson.model.Item; @@ -46,6 +48,7 @@ import hudson.security.Permission; import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; import java.io.IOException; import java.net.URISyntaxException; @@ -60,9 +63,6 @@ import javax.servlet.ServletException; -import jenkins.model.Jenkins; - -import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -71,24 +71,21 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; - -import com.google.common.collect.Iterables; /** - * This view displays the set of jobs that are related based on their - * upstream\downstream relationships as a pipeline. Each build pipeline becomes - * a row on the view. - * + * This view displays the set of jobs that are related + * based on their upstream\downstream relationships as a pipeline. Each + * build pipeline becomes a row on the view. + * * @author Centrum Systems - * + * */ public class BuildPipelineView extends View { /** - * @deprecated For backward compatibility. Back when we didn't have - * {@link #gridBuilder}, this field stored the first job to - * display. + * @deprecated + * For backward compatibility. Back when we didn't have {@link #gridBuilder}, + * this field stored the first job to display. */ @Deprecated private volatile String selectedJob; @@ -119,15 +116,14 @@ public class BuildPipelineView extends View { /** showPipelineParametersInHeaders */ private boolean showPipelineParametersInHeaders; - + /** * informs if the first job has parameters */ private boolean startsWithParameters; /** - * Frequency at which the Build Pipeline Plugin updates the build cards in - * seconds + * Frequency at which the Build Pipeline Plugin updates the build cards in seconds */ private int refreshFrequency = 3; @@ -135,8 +131,7 @@ public class BuildPipelineView extends View { private boolean showPipelineDefinitionHeader; /* - * Keep feature flag properties in one place so that it is easy to refactor - * them out later. + * Keep feature flag properties in one place so that it is easy to refactor them out later. */ /* Feature flags - START */ @@ -146,8 +141,8 @@ public class BuildPipelineView extends View { private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); /** - * An instance of {@link Cause.UserIdCause} related to the current user. - * Must be transient, or xstream will include it in the serialization + * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the + * serialization */ private class MyUserIdCause extends Cause.UserIdCause { /** @@ -160,8 +155,7 @@ private class MyUserIdCause extends Cause.UserIdCause { */ public MyUserIdCause() { try { - // this block can generate a - // CyclicGraphDetector.CycleDetectedException + // this block can generate a CyclicGraphDetector.CycleDetectedException // in cases that I haven't quite figured out yet // also an org.acegisecurity.AccessDeniedException when the user // is not logged in @@ -215,7 +209,7 @@ public void print(final TaskListener listener) { } /** - * + * * @param name * the name of the pipeline build view. * @param buildViewTitle @@ -232,9 +226,10 @@ public void print(final TaskListener listener) { * Indicates whether the first job of the pipeline takes * parameters */ - public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { - super(name, Jenkins.getInstance()); + public BuildPipelineView(final String name, final String buildViewTitle, + final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { + super(name, Hudson.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; this.noOfDisplayedBuilds = noOfDisplayedBuilds; @@ -244,7 +239,7 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P } /** - * + * * @param name * the name of the pipeline build view. * @param buildViewTitle @@ -265,23 +260,20 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P * @param showPipelineDefinitionHeader * Indicates whether the pipeline headers should be shown. * @param refreshFrequency - * Frequency at which the build pipeline plugin refreshes build - * cards + * Frequency at which the build pipeline plugin refreshes build cards * @param cssUrl * URL for the custom CSS file. * @param selectedJob - * the first job name in the pipeline. it can be set to null when - * gridBuilder is passed. + * the first job name in the pipeline. it can be set to null when gridBuilder is passed. * @param startsWithParameters - * indicates whether the first job of the pipeline takes - * parameters + * indicates whether the first job of the pipeline takes parameters */ @DataBoundConstructor public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, - final boolean showPipelineParameters, final boolean showPipelineParametersInHeaders, - final boolean showPipelineDefinitionHeader, final int refreshFrequency, final String cssUrl, final String selectedJob, - final boolean startsWithParameters) { + final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, + final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, + final int refreshFrequency, final String cssUrl, final String selectedJob, final boolean startsWithParameters) { this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl, startsWithParameters); this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; this.showPipelineParameters = showPipelineParameters; @@ -289,23 +281,21 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; this.startsWithParameters = startsWithParameters; this.selectedJob = selectedJob; - // not exactly understanding the lifecycle here, but I want a default of - // 3 - // (this is what the class variable is set to 3, if it's 0, set it to - // default, refresh of 0 does not make sense anyway) + //not exactly understanding the lifecycle here, but I want a default of 3 + //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) if (refreshFrequency < 1) { this.refreshFrequency = 3; } else { this.refreshFrequency = refreshFrequency; } - - // for remote api support + + //for remote api support if (this.gridBuilder == null) { if (this.selectedJob != null) { this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); } } - + if (this.selectedJob == null) { if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); @@ -314,7 +304,8 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P } /** - * @return must be always 'this' + * @return + * must be always 'this' */ protected Object readResolve() { if (gridBuilder == null) { @@ -327,7 +318,7 @@ protected Object readResolve() { /** * Handles the configuration submission - * + * * @param req * Stapler Request * @throws FormException @@ -343,11 +334,9 @@ protected void submit(final StaplerRequest req) throws IOException, ServletExcep } /** - * Checks whether the user has a permission to start a new instance of the - * pipeline. - * - * @return - true: Has Build permission; false: Does not have Build - * permission + * Checks whether the user has a permission to start a new instance of the pipeline. + * + * @return - true: Has Build permission; false: Does not have Build permission * @see hudson.model.Item */ public boolean hasBuildPermission() { @@ -356,9 +345,8 @@ public boolean hasBuildPermission() { /** * Checks whether the user has Configure permission for the current project. - * - * @return - true: Has Configure permission; false: Does not have Configure - * permission + * + * @return - true: Has Configure permission; false: Does not have Configure permission */ public boolean hasConfigurePermission() { return this.hasPermission(CONFIGURE); @@ -374,7 +362,7 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { /** * Get a List of downstream projects. - * + * * @param currentProject * - The project from which we want the downstream projects * @return - A List of downstream projects @@ -385,35 +373,35 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { /** * Determines if the current project has any downstream projects - * + * * @param currentProject * - The project from which we are testing. - * @return - true; has downstream projects; false: does not have downstream - * projects + * @return - true; has downstream projects; false: does not have downstream projects */ public boolean hasDownstreamProjects(final AbstractProject currentProject) { return (getDownstreamProjects(currentProject).size() > 0); } - + /** * Returns BuildPipelineForm containing the build pipeline to display. - * - * @return - Representation of the projects and their related builds making - * up the build pipeline view + * + * @return - Representation of the projects and their related builds making up the build pipeline view */ public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid projectGrid = gridBuilder.build(this); - if (projectGrid.isEmpty()) { + final ProjectGrid project = gridBuilder.build(this); + if (project.isEmpty()) { return null; } - return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm( + project, + Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); } /** * Retrieves the project URL - * + * * @param project * - The project * @return URL - of the project @@ -427,7 +415,7 @@ public String getProjectURL(final AbstractProject project) throws URISynta /** * Trigger a manual build - * + * * @param upstreamBuildNumber * upstream build number * @param triggerProjectName @@ -452,8 +440,7 @@ public int triggerManualBuild(final Integer upstreamBuildNumber, final String tr buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); } - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -496,15 +483,13 @@ public int rerunBuild(final String externalizableId) { } /** - * Given an AbstractProject and a build number the associated AbstractBuild - * will be retrieved. - * + * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. + * * @param buildNo * - Build number * @param project * - AbstractProject - * @return The AbstractBuild associated with the AbstractProject and build - * number. + * @return The AbstractBuild associated with the AbstractProject and build number. */ @SuppressWarnings("unchecked") private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { @@ -524,34 +509,29 @@ public int rerunBuild(final String externalizableId) { /** * Schedules a build to start. - * - * The build will take an upstream build as its Cause and a set of - * ParametersAction from the upstream build. - * + * + * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. + * * @param triggerProject * - Schedule a build to start on this AbstractProject * @param upstreamBuild - * - The upstream AbstractBuild that will be used as a Cause for - * the triggerProject's build. + * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction - * - The upstream ParametersAction that will be used as an Action - * for the triggerProject's build. - * @param delay - * - The delay for the build to be scheduled + * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction, final int delay) { + final Action buildParametersAction) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) - // available: + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction - : new ParametersAction(); + ParametersAction parametersAction = + buildParametersAction instanceof ParametersAction + ? (ParametersAction) buildParametersAction : new ParametersAction(); if (upstreamBuild != null) { @@ -579,19 +559,19 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } /** - * From parameterized trigger plugin - * src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java - * + * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java + * * @param base - * One of the two parameters to merge. + * One of the two parameters to merge. * @param overlay - * The other parameters to merge - * @return Result of the merge. + * The other parameters to merge + * @return + * Result of the merge. */ private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { final LinkedHashMap params = new LinkedHashMap(); @@ -605,13 +585,11 @@ private static ParametersAction mergeParameters(final ParametersAction base, fin } /** - * Checks whether the given {@link Action} contains a reference to a - * {@link UserIdCause} object. - * + * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. + * * @param buildAction * the action to check. - * @return true if the action has a reference to a userId - * cause. + * @return true if the action has a reference to a userId cause. */ private boolean isUserIdCauseAction(final Action buildAction) { boolean retval = false; @@ -627,12 +605,10 @@ private boolean isUserIdCauseAction(final Action buildAction) { } /** - * Removes any UserId cause action from the given actions collection. This - * is used by downstream builds that inherit upstream actions. The - * downstream build can be initiated by another user that is different from - * the user who initiated the upstream build, so the downstream build needs - * to remove the old user action inherited from upstream, and add its own. - * + * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. + * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the + * downstream build needs to remove the old user action inherited from upstream, and add its own. + * * @param actions * a collection of build actions. * @return a collection of build actions with all UserId causes removed. @@ -649,14 +625,13 @@ private List removeUserIdCauseActions(final List actions) { /** * This descriptor class is required to configure the View Page - * + * */ @Extension public static final class DescriptorImpl extends ViewDescriptor { /** - * descriptor impl constructor This empty constructor is required for - * stapler. If you remove this constructor, text name of + * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of * "Build Pipeline View" will be not displayed in the "NewView" page */ public DescriptorImpl() { @@ -665,7 +640,7 @@ public DescriptorImpl() { /** * get the display name - * + * * @return display name */ @Override @@ -675,7 +650,7 @@ public String getDisplayName() { /** * Display No Of Builds Items in the Edit View Page - * + * * @return ListBoxModel */ public ListBoxModel doFillNoOfDisplayedBuildsItems() { @@ -700,7 +675,7 @@ public ListBoxModel doFillNoOfDisplayedBuildsItems() { /** * Display Console Output Link Style Items in the Edit View Page - * + * * @return ListBoxModel */ public ListBoxModel doFillConsoleOutputLinkStyleItems() { @@ -787,9 +762,9 @@ public String getShowPipelineParameters() { public void setShowPipelineParameters(final boolean showPipelineParameters) { this.showPipelineParameters = showPipelineParameters; } - - public boolean isStartsWithParameters() { - return startsWithParameters && gridBuilder.startsWithParameters(this); + + public boolean isStartsWithParameters() { + return startsWithParameters; } public String getStartsWithParameters() { @@ -798,7 +773,7 @@ public String getStartsWithParameters() { public void setStartsWithParameters(final boolean startsWithParameters) { this.startsWithParameters = startsWithParameters; - } + } public boolean isShowPipelineParametersInHeaders() { return showPipelineParametersInHeaders; @@ -863,16 +838,15 @@ public boolean contains(final TopLevelItem item) { } /** - * If a project name is changed we check if the selected job for this view - * also needs to be changed. - * + * If a project name is changed we check if the selected job for this view also needs to be changed. + * * @param item * - The Item that has been renamed * @param oldName * - The old name of the Item * @param newName * - The new name of the Item - * + * */ @Override public void onJobRenamed(final Item item, final String oldName, final String newName) { @@ -900,43 +874,20 @@ private static final class LinkStyle { /** this window link style option */ static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ } - + @Override public boolean hasPermission(final Permission p) { boolean display = true; - // tester la liste vide seulement en lecture + //tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - // Pas en lecture => permission standard + //Pas en lecture => permission standard display = super.hasPermission(p); } return display; } - - /** - * Check for M2ReleaseAction, if present, then provide an icon on top - * - * @return boolean - whether the first job is a m2release job - */ - public boolean isM2Release() { - if (Jenkins.getInstance().getPlugin("m2release") == null) { - return false; - } - final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); - final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), AbstractProject.class); - if (project != null) { - // If a M2ReleaseAction is found - for (final Action nextAction : project.getAllActions()) { - if (nextAction instanceof M2ReleaseAction) { - return true; - } - } - } - return false; - } - } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java index 2a47ecde..b66e9641 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java @@ -1,20 +1,18 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import hudson.model.AbstractBuild; - import java.util.HashMap; import java.util.Map; /** * {@link BuildGrid} implementation backed by a sparse array. - * + * * @author Kohsuke Kawaguchi */ public class DefaultBuildGridImpl extends BuildGrid { /** * Actual data. */ - private final Map> data = new HashMap>(); + private final Map> data = new HashMap>(); /** * Dimension of the {@link #data} @@ -22,15 +20,14 @@ public class DefaultBuildGridImpl extends BuildGrid { private int rows, cols; /** - * Mutable, but only for the code that instantiates - * {@link DefaultBuildGridImpl}. - * + * Mutable, but only for the code that instantiates {@link DefaultBuildGridImpl}. + * * @param row - * position of the form + * position of the form * @param col - * position of the form + * position of the form * @param p - * The build to add. null to remove the value. + * The build to add. null to remove the value. */ public void set(int row, int col, BuildForm p) { Map c = data.get(row); @@ -62,22 +59,4 @@ public int getColumns() { public int getRows() { return rows; } - - @Override - public BuildForm findBuildForm(AbstractBuild build) { - if (build != null) { - final String fullBuildName = build.getFullDisplayName(); - for (int r = 0; r < rows; r++) { - for (int c = 0; c < cols; c++) { - final BuildForm form = get(r, c); - if (form != null) { - if (fullBuildName.equals(form.getFullBuildName())) { - return form; - } - } - } - } - } - return null; - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java index 3b885767..213035dc 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java @@ -1,8 +1,6 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -39,8 +37,7 @@ public void set(int row, int col, ProjectForm p) { data.put(row, c); } c.put(col, p); - p.setCoords(row, col); - + rows = Math.max(rows, row + 1); cols = Math.max(cols, col + 1); } @@ -63,7 +60,7 @@ public ProjectForm get(int row, int col) { } return cols.get(col); } - + @Override public int getColumns() { return cols; @@ -74,14 +71,4 @@ public int getRows() { return rows; } - @Override - public void correctProjectForms(Iterable buildGrids) { - final Collection> maps = data.values(); - for (Map map : maps) { - final Collection forms = map.values(); - for (ProjectForm projectForm : forms) { - projectForm.correctLastSuccessfulBuilds(buildGrids); - } - } - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java index a82b0176..c7a21048 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java @@ -1,21 +1,14 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import hudson.Extension; -import hudson.model.Item; -import hudson.model.ItemGroup; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import hudson.model.Item; +import hudson.model.ItemGroup; import hudson.model.ParametersDefinitionProperty; import hudson.util.AdaptedIterator; import hudson.util.HttpResponses; import hudson.util.ListBoxModel; - -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; - -import javax.servlet.ServletException; - import jenkins.model.Jenkins; import jenkins.util.TimeDuration; @@ -26,9 +19,15 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; +import javax.servlet.ServletException; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + /** * {@link ProjectGridBuilder} based on the upstream/downstream relationship. - * + * * @author Kohsuke Kawaguchi */ public class DownstreamProjectGridBuilder extends ProjectGridBuilder { @@ -38,8 +37,7 @@ public class DownstreamProjectGridBuilder extends ProjectGridBuilder { private String firstJob; /** - * @param firstJob - * Name of the job to lead the piepline. + * @param firstJob Name of the job to lead the piepline. */ @DataBoundConstructor public DownstreamProjectGridBuilder(String firstJob) { @@ -56,8 +54,7 @@ private static final class GridImpl extends DefaultProjectGridImpl { private final AbstractProject start; /** - * @param start - * The first project to lead the pipeline. + * @param start The first project to lead the pipeline. */ private GridImpl(AbstractProject start) { this.start = start; @@ -66,16 +63,11 @@ private GridImpl(AbstractProject start) { /** * Function called recursively to place a project form in a grid - * - * @param startingRow - * project will be placed in the starting row and 1st child - * as well. Each subsequent child will be placed in a row - * below the previous. - * @param startingColumn - * project will be placed in starting column. All children - * will be placed in next column. - * @param projectForm - * project to be placed + * + * @param startingRow project will be placed in the starting row and 1st child as well. Each subsequent + * child will be placed in a row below the previous. + * @param startingColumn project will be placed in starting column. All children will be placed in next column. + * @param projectForm project to be placed */ private void placeProjectInGrid(final int startingRow, final int startingColumn, final ProjectForm projectForm) { if (projectForm == null) { @@ -86,8 +78,8 @@ private void placeProjectInGrid(final int startingRow, final int startingColumn, set(row, startingColumn, projectForm); final int childrensColumn = startingColumn + 1; - for (final ProjectForm downstreamForm : projectForm.getDependencies()) { - placeProjectInGrid(row, childrensColumn, downstreamForm); + for (final ProjectForm downstreamProject : projectForm.getDependencies()) { + placeProjectInGrid(row, childrensColumn, downstreamProject); row++; } } @@ -99,8 +91,7 @@ private void placeProjectInGrid(final int startingRow, final int startingColumn, @Override public Iterator iterator() { if (start == null) { - return Collections.emptyList().iterator(); // no - // dat + return Collections.emptyList().iterator(); // no dat } final Iterator> base = start.getBuilds().iterator(); @@ -120,13 +111,11 @@ public Iterable builds() { } /** - * {@link BuildGrid} implementation that lays things out via its - * upstream/downstream relationship. + * {@link BuildGrid} implementation that lays things out via its upstream/downstream relationship. */ private static final class BuildGridImpl extends DefaultBuildGridImpl { /** - * @param start - * The first build to lead the pipeline instance. + * @param start The first build to lead the pipeline instance. */ private BuildGridImpl(final BuildForm start) { placeBuildInGrid(0, 0, start); @@ -134,16 +123,11 @@ private BuildGridImpl(final BuildForm start) { /** * Function called recursively to place a build form in a grid - * - * @param startingRow - * build will be placed in the starting row and 1st child as - * well. Each subsequent child will be placed in a row below - * the previous. - * @param startingColumn - * build will be placed in starting column. All children will - * be placed in next column. - * @param buildForm - * build to be placed + * + * @param startingRow build will be placed in the starting row and 1st child as well. Each subsequent child + * will be placed in a row below the previous. + * @param startingColumn build will be placed in starting column. All children will be placed in next column. + * @param buildForm build to be placed */ private void placeBuildInGrid(final int startingRow, final int startingColumn, final BuildForm buildForm) { int row = getNextAvailableRow(startingRow, startingColumn); @@ -163,9 +147,8 @@ public String getFirstJob() { /** * The job that's configured as the head of the pipeline. - * - * @param owner - * View that this builder is operating under. + * + * @param owner View that this builder is operating under. * @return possibly null */ public AbstractProject getFirstJob(BuildPipelineView owner) { @@ -177,7 +160,7 @@ public boolean hasBuildPermission(BuildPipelineView owner) { final AbstractProject job = getFirstJob(owner); return job != null && job.hasPermission(Item.BUILD); } - + @Override public boolean startsWithParameters(BuildPipelineView owner) { final AbstractProject firstJob = this.getFirstJob(owner); @@ -202,10 +185,8 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod p.doBuild(req, rsp, new TimeDuration(0)); } catch (IllegalStateException e) { ; - // Ignore because sendRedirect(String) gets called twice. We - // do not want to hit the top - // level of the project but instead we want to be redirected - // back 1 directory. + // Ignore because sendRedirect(String) gets called twice. We do not want to hit the top + // level of the project but instead we want to be redirected back 1 directory. } } }; @@ -229,8 +210,7 @@ public void onJobRenamed(BuildPipelineView owner, Item item, String oldName, Str /** * Descriptor. */ - @Extension(ordinal = 1000) - // historical default behavior, so give it a higher priority + @Extension(ordinal = 1000) // historical default behavior, so give it a higher priority public static class DescriptorImpl extends ProjectGridBuilderDescriptor { @Override public String getDisplayName() { @@ -239,15 +219,15 @@ public String getDisplayName() { /** * Display Job List Item in the Edit View Page - * - * @param context - * What to resolve relative job names against? + * + * @param context What to resolve relative job names against? * @return ListBoxModel */ public ListBoxModel doFillFirstJobItems(@AncestorInPath ItemGroup context) { final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); for (final AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class)) { - options.add(/* TODO 1.515: p.getRelativeDisplayNameFrom(context) */p.getFullDisplayName(), p.getRelativeNameFrom(context)); + options.add(/* TODO 1.515: p.getRelativeDisplayNameFrom(context) */p.getFullDisplayName(), + p.getRelativeNameFrom(context)); } return options; } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java index ca9362b8..e750f4cf 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java @@ -92,35 +92,4 @@ private boolean hasDataToRight(final int row, final int col) { } return false; } - - /** - * Search for dependencies of the starting project further down the rows. - * - * @param startRow - the row of the project to search from - * @param startCol - the col of the project to search from - * @return boolean - whether there is a project further down from the current position - */ - public boolean hasDependencyFurtherDown(int startRow, int startCol) { - // Search for a further down projectForm - final int nextRow = startRow + 1; - final int nextCol = startCol + 1; - int nextTRow = -1; - for (int locRow = nextRow; locRow < getRows(); locRow++) { - if (get(locRow, nextCol) != null) { - nextTRow = locRow; - break; - } - } - // Now test, that there is no other project between startRow and row of nextPf - // only if there is none, then the furtherDown project is correct for the current starting point - if (nextTRow != -1) { - for (int locRow = nextRow; locRow <= nextTRow; locRow++) { - if (get(locRow, startCol) != null) { - return false; - } - } - return true; - } - return false; - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index a71d3cbc..53fe2510 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,9 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Queue; -import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; -import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import hudson.model.Hudson; import java.text.DateFormat; import java.util.ArrayList; @@ -41,15 +39,15 @@ import java.util.Map; import java.util.logging.Logger; -import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; +import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; +import hudson.plugins.parameterizedtrigger.SubProjectsAction; /** * @author Centrum Systems - * + * */ public class PipelineBuild { /** Represents the current build */ @@ -59,14 +57,13 @@ public class PipelineBuild { /** Represents the upstream build */ private AbstractBuild upstreamBuild; /** - * Contains the upstreamBuild result. Can be one of the following: - - * BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - PENDING - - * MANUAL + * Contains the upstreamBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - + * PENDING - MANUAL */ private String upstreamBuildResult; /** - * Contains the currentBuild result. Can be one of the following: - BUILDING - * - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - PENDING - MANUAL + * Contains the currentBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - + * PENDING - MANUAL */ private String currentBuildResult; @@ -80,9 +77,8 @@ public PipelineBuild() { } /** - * Creates a new PipelineBuild with currentBuild, project and upstreamBuild - * set. - * + * Creates a new PipelineBuild with currentBuild, project and upstreamBuild set. + * * @param build * - current build * @param project @@ -100,9 +96,9 @@ public PipelineBuild(final AbstractBuild build, final AbstractProject build) { this(build, build.getProject(), build.getPreviousBuild()); @@ -137,9 +133,8 @@ public void setProject(final AbstractProject currentProject) { } /** - * Returns the project name. If the current project is null the project name - * is determined using the current build. - * + * Returns the project name. If the current project is null the project name is determined using the current build. + * * @return - Project name */ public AbstractProject getProject() { @@ -154,9 +149,8 @@ public void setProject(final AbstractProject currentProject) { /** * Returns the current build number. - * - * @return - Current build number or empty String is the current build is - * null. + * + * @return - Current build number or empty String is the current build is null. */ public String getCurrentBuildNumber() { if (this.currentBuild != null) { @@ -167,11 +161,9 @@ public String getCurrentBuildNumber() { } /** - * Constructs a List of downstream PipelineBuild objects that make up the - * current pipeline. - * - * @return - List of downstream PipelineBuild objects that make up the - * current pipeline. + * Constructs a List of downstream PipelineBuild objects that make up the current pipeline. + * + * @return - List of downstream PipelineBuild objects that make up the current pipeline. */ public List getDownstreamPipeline() { final List pbList = new ArrayList(); @@ -188,7 +180,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -197,8 +189,7 @@ public List getDownstreamPipeline() { returnedBuild = BuildUtil.getDownstreamBuild(dependency, currentBuild); } final PipelineBuild candidate = new PipelineBuild(returnedBuild, dependency, this.currentBuild); - // if subprojects come back as downstreams someday, no - // duplicates wanted + // if subprojects come back as downstreams someday, no duplicates wanted if (!pbList.contains(candidate)) { pbList.add(candidate); } @@ -212,7 +203,7 @@ public List getDownstreamPipeline() { /** * Build a URL of the currentBuild - * + * * @return URL of the currentBuild */ public String getBuildResultURL() { @@ -221,7 +212,7 @@ public String getBuildResultURL() { /** * Builds a URL of the current project - * + * * @return URL - of the project */ public String getProjectURL() { @@ -230,7 +221,7 @@ public String getProjectURL() { /** * Determines the result of the current build. - * + * * @return - String representing the build result * @see PipelineBuild#getBuildResult(AbstractBuild) */ @@ -241,7 +232,7 @@ public String getCurrentBuildResult() { /** * Determines the result of the upstream build. - * + * * @return - String representing the build result * @see PipelineBuild#getBuildResult(AbstractBuild) */ @@ -253,10 +244,9 @@ public String getUpstreamBuildResult() { } /** - * Determines the result for a particular build. Can be one of the - * following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT - * - PENDING - MANUAL - * + * Determines the result for a particular build. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - + * ABORT - PENDING - MANUAL + * * @param build * - The build for which a result is requested. * @return - String representing the build result @@ -279,30 +269,22 @@ private String getBuildResult(final AbstractBuild build) { } /** - * Determines the pending currentBuild status of a currentBuild in the - * pipeline that has not been completed. (i.e. the currentBuild is null) - * - * @return - PENDING: Current currentBuild is pending the execution of - * upstream builds. MANUAL: Current currentBuild requires a manual - * trigger. + * Determines the pending currentBuild status of a currentBuild in the pipeline that has not been completed. (i.e. the currentBuild is + * null) + * + * @return - PENDING: Current currentBuild is pending the execution of upstream builds. MANUAL: Current currentBuild requires a manual + * trigger */ private String getPendingStatus() { String pendingStatus = HudsonResult.PENDING.toString(); - if (ScheduleUtil.getQueuedItem(project.getDisplayName()) != null) { - // Check for upstreamBuild being latest build (otherwise, do not set - // status on QUEUED) - if (upstreamBuild != null) { - final AbstractBuild lastUpstreamBuild = upstreamBuild.getProject().getLastBuild(); - if (lastUpstreamBuild != null && upstreamBuild.getNumber() == lastUpstreamBuild.getNumber()) { - pendingStatus = HudsonResult.QUEUED.toString(); - } - } - } else { - final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); - if (upstreamPB != null && this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + + if (upstreamPB != null) { + if (this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || + getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { pendingStatus = HudsonResult.MANUAL.toString(); } @@ -312,12 +294,11 @@ private String getPendingStatus() { return pendingStatus; } + /** - * Returns the upstream PipelineBuild object from the current PipelineBuild - * object. - * - * @return - Upstream PipelineBuild object from the current PipelineBuild - * object + * Returns the upstream PipelineBuild object from the current PipelineBuild object. + * + * @return - Upstream PipelineBuild object from the current PipelineBuild object */ public PipelineBuild getUpstreamPipelineBuild() { @SuppressWarnings("rawtypes") @@ -330,12 +311,12 @@ public PipelineBuild getUpstreamPipelineBuild() { } else { upstreamBuildName = ""; } - if (upstreamProjects.size() > 0) { + if (upstreamProjects.size() > 0) { if (isManualTrigger()) { - for (AbstractProject upstreamProject : upstreamProjects) { + for (AbstractProject upstreamProject : upstreamProjects) { if (upstreamProject.getName().equals(upstreamBuildName)) { previousProject = upstreamProject; - break; + break; } } } @@ -350,9 +331,8 @@ public PipelineBuild getUpstreamPipelineBuild() { /** * Returns the current build duration. - * - * @return - Current build duration or an empty String if the current build - * is null. + * + * @return - Current build duration or an empty String if the current build is null. */ public String getBuildDuration() { if (this.currentBuild != null) { @@ -362,35 +342,6 @@ public String getBuildDuration() { } } - /** - * Returns the current wait duration (when queued). - * - * @return - Current wait duration or an empty String if no queueing in - * progress is null. - */ - public String getQueueWaitDuration() { - final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); - if (wItem != null) { - return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); - } else { - return ""; //$NON-NLS-1$ - } - } - - /** - * Returns the current id of the queue item. - * - * @return - Current id of the queue item - */ - public Integer getQueueId() { - final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); - if (wItem != null) { - return wItem.id; - } else { - return 0; //$NON-NLS-1$ - } - } - /** * {@inheritDoc} */ @@ -401,9 +352,8 @@ public String toString() { /** * Returns the current build description. - * - * @return - Current build description or the project name if the current - * build is null. + * + * @return - Current build description or the project name if the current build is null. */ public String getBuildDescription() { if (this.currentBuild != null) { @@ -415,7 +365,7 @@ public String getBuildDescription() { /** * Returns the estimated percentage complete of the current build. - * + * * @return - Estimated percentage complete of the current build. */ public long getBuildProgress() { @@ -428,10 +378,9 @@ public long getBuildProgress() { } /** - * Calculates percentage of the current duration to the estimated duration. - * Caters for the possibility that current duration will be longer than - * estimated duration - * + * Calculates percentage of the current duration to the estimated duration. Caters for the possibility that current duration will be + * longer than estimated duration + * * @param duration * - Current running time in milliseconds * @param estimatedDuration @@ -450,7 +399,7 @@ protected long calculatePercentage(final long duration, final long estimatedDura /** * Return pipeline version which is simply the first build's number - * + * * @return pipeline verison */ public String getPipelineVersion() { @@ -471,15 +420,14 @@ public String getPipelineVersion() { /** * Checks whether the user has Build permission for the current project. - * - * @return - true: Has Build permission; false: Does not have Build - * permission + * + * @return - true: Has Build permission; false: Does not have Build permission * @see hudson.model.Item */ public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Jenkins.getInstance().isUseSecurity()) { + if (!Hudson.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -494,13 +442,14 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) - && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); + return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) - && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); + return !isReadyToBeManuallyBuilt() + && !"PENDING".equals(getCurrentBuildResult()) + && !"BUILDING".equals(getCurrentBuildResult()) + && hasBuildPermission(); } /** @@ -510,37 +459,30 @@ private boolean upstreamBuildSucceeded() { return this.getUpstreamBuild() != null && HudsonResult.SUCCESS.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * @return upstream build exists and unstable. - */ - private boolean upstreamBuildUnstable() { - return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); - } + /** + * @return upstream build exists and unstable. + */ + private boolean upstreamBuildUnstable() { + return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); + } + /** - * Determine if the project is triggered manually, regardless of the state - * of its upstream builds - * + * Determine if the project is triggered manually, regardless of the state of its upstream builds + * * @return true if it is manual */ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); - } else { - for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { - manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); - if (manualTrigger) { - break; - } - } } return manualTrigger; } /** * Start time of build - * + * * @return start time */ public Date getStartTime() { @@ -562,35 +504,11 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartDate = ""; //$NON-NLS-1$ + String formattedStartTime = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); + formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedStartDate; - } - - /** - * @return Formatted queued for time - */ - public String getFormattedQueuedForTime() { - String formattedQueuedForTime = ""; //$NON-NLS-1$ - final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); - if (wItem != null) { - formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); - } - return formattedQueuedForTime; - } - - /** - * @return Formatted queued for date - */ - public String getFormattedQueuedForDate() { - String formattedQueuedForDate = ""; //$NON-NLS-1$ - final Queue.WaitingItem wItem = ScheduleUtil.getQueuedWaitingItem(project.getDisplayName()); - if (wItem != null) { - formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); - } - return formattedQueuedForDate; + return formattedStartTime; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 63ec3c23..4dbde52a 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -3,24 +3,21 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import hudson.model.Hudson; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import jenkins.model.Jenkins; +import hudson.plugins.parameterizedtrigger.SubProjectsAction; import org.kohsuke.stapler.bind.JavaScriptMethod; /** * @author Centrum Systems - * + * * Representation of a set of projects - * + * */ public class ProjectForm { /** @@ -51,33 +48,18 @@ public class ProjectForm { /** * the latest successful build number */ - private String lastSuccessfulBuildNumber; + private final String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private Map lastSuccessfulBuildParams; + private final Map lastSuccessfulBuildParams; /** * keep reference to the project so that we can update it */ private final AbstractProject project; - /** - * keep reference to the first project in the current pipeline view - */ - private final AbstractProject firstProject; - - /** - * Hold the row this project is placed in (handle duplicate project-ids) - */ - private int row; - - /** - * Hold the column this project is placed in (handle duplicate project-ids) - */ - private int col; - /** * @param name * project name @@ -92,25 +74,13 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; - firstProject = null; } /** * @param project - * - the project to wrap + * project */ public ProjectForm(final AbstractProject project) { - this(project, null); - } - - /** - * @param project - * - the project to wrap - * @param firstProject - * - the first project associated with the grid for this - * projectform - */ - public ProjectForm(final AbstractProject project, final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); @@ -120,15 +90,14 @@ public ProjectForm(final AbstractProject project, final AbstractProject(); for (final AbstractProject dependency : project.getDownstreamProjects()) { - dependencies.add(new ProjectForm(dependency, firstProject)); + dependencies.add(new ProjectForm(dependency)); } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { - final ProjectForm candidate = new ProjectForm(dependency, firstProject); - // if subprojects come back as downstreams someday, no - // duplicates wanted + final ProjectForm candidate = new ProjectForm(dependency); + // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -138,53 +107,28 @@ public ProjectForm(final AbstractProject project, final AbstractProject lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() : lastSuccessfulBuild.getBuildVariables(); this.project = project; - this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * This method is only called for a starting project in a pipeline view, - * therefore save it as such. - * + * * @param p - * project to be wrapped. - * @return possibly null. + * project to be wrapped. + * @return + * possibly null. */ public static ProjectForm as(AbstractProject p) { - return p != null ? new ProjectForm(p, p) : null; + return p != null ? new ProjectForm(p) : null; } public String getName() { return name; } - public int getRow() { - return row; - } - - public int getColumn() { - return col; - } - - /** - * Set the coordinates for this project form. - * - * @param row - * - the row this form is set into - * @param col - * - the column this form is set into - */ - public void setCoords(int row, int col) { - this.row = row; - this.col = col; - } - public String getHealth() { return health; } @@ -210,16 +154,13 @@ public List getDependencies() { } /** - * Gets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with isTriggerOnlyLatestJob property - * allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first job which - * should show the trigger button will render and then a call will be made - * to 'setDisplayTrigger' to change the value to both so all future jobs - * will not display the trigger. see main.jelly - * + * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * so all future jobs will not display the trigger. see main.jelly + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -227,19 +168,15 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with isTriggerOnlyLatestJob property - * allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first job which - * should show the trigger button will render and then a call will be made - * to 'setDisplayTrigger' to change the value to both so all future jobs - * will not display the trigger. see main.jelly - * + * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * so all future jobs will not display the trigger. see main.jelly + * * @param display - * - boolean to indicate whether the trigger button should be - * shown + * - boolean to indicate whether the trigger button should be shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -281,72 +218,12 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod public String asJSON() { - return ProjectJSONBuilder.asJSON(this); // new ProjectForm(project, - // firstProject)); - } - - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - public Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } - - /** - * Correct the last successful build settings of this form, if that build is - * not on the current BuildPipelineView (when Jobs are used on multiple - * Pipelines) - * - * @param buildGrids - * - the build-grids present on this view - */ - public void correctLastSuccessfulBuilds(Iterable buildGrids) { - AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); - - outer: while (lastSuccessfulBuild != null) { - // Search the buildGrid for the lastSuccessfulBuild given above - BuildForm buildForm = null; - for (BuildGrid buildGrid : buildGrids) { - buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); - if (buildForm != null) { - break outer; - } - } - lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); - } - // Set the (may be) newly found last successful build - handleLastSuccessfulBuild(lastSuccessfulBuild); + return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } - /** - * Set build-number and build-params for last successful build. - * - * @param lastSuccessfulBuild - * - the last successful build (can be null) - */ - private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() - : filterSensitiveBuildVariables(lastSuccessfulBuild); - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java index d8d7aecb..88cb906c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java @@ -2,31 +2,21 @@ /** * Two-dimensional placement of {@link ProjectForm}s into a grid/matrix layout. - * + * * This class is also responsible for producing a sequence of {@link BuildGrid}s * that are the instances of the pipelines. - * + * * @author Kohsuke Kawaguchi */ public abstract class ProjectGrid extends Grid { /** * Iterates instances of the pipeline grid view from this project layout. - * - * The caller is only going to iterate {@link BuildGrid}s up to a certain - * number that the user has configured. - * - * + * + * The caller is only going to iterate {@link BuildGrid}s up to a certain number + * that the user has configured. + * + * * @return never null. */ public abstract Iterable builds(); - - /** - * Correct things in project forms due to usage of jobs in multiple - * pipelines - * - * @param buildGrids - * - the buildGrids available to search for each - * lastSuccessfulBuild of a projectform - */ - public abstract void correctProjectForms(Iterable buildGrids); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java index 66bc891c..0bdbddfd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGridBuilder.java @@ -2,13 +2,12 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Item; - -import java.io.IOException; - import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; +import java.io.IOException; + /** * Encapsulates the definition of how to layout projects into a {@link ProjectGrid}. * diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java index 0ece8271..9e342e08 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java @@ -26,16 +26,17 @@ import hudson.Extension; import hudson.Launcher; -import hudson.Util; import hudson.PluginWrapper; +import hudson.Util; import hudson.model.BuildListener; +import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; import hudson.model.Item; -import hudson.model.ItemGroup; import hudson.model.Items; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Descriptor; +import hudson.model.Hudson; import hudson.model.Project; import hudson.model.listeners.ItemListener; import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; @@ -53,14 +54,13 @@ import java.util.logging.Level; import java.util.logging.Logger; -import jenkins.model.DependencyDeclarer; -import jenkins.model.Jenkins; - -import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import au.com.centrumsystems.hudson.plugin.buildpipeline.Strings; +import hudson.model.ItemGroup; +import jenkins.model.Jenkins; +import org.kohsuke.stapler.AncestorInPath; /** * The build pipeline trigger allows the creation of downstream jobs which aren't triggered automatically. This allows us to have manual @@ -68,8 +68,7 @@ * * @author Centrum Systems */ -@SuppressWarnings("unchecked") -public class BuildPipelineTrigger extends Notifier implements DependencyDeclarer { +public class BuildPipelineTrigger extends Notifier implements DependecyDeclarer { /** * logger */ @@ -259,7 +258,7 @@ public static class DescriptorImpl extends BuildStepDescriptor { * @return true if it is possible to add parameters to the trigger */ public boolean canAddParameters() { - final PluginWrapper plugin = Jenkins.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ + final PluginWrapper plugin = Hudson.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ return plugin != null && plugin.isActive(); } @@ -297,7 +296,7 @@ public String getHelpFile() { * @param context - the context * @return hudson.util.FormValidation */ - public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, + public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, @QueryParameter("downstreamProjectNames") final String value) { final StringTokenizer tokens = new StringTokenizer(Util.fixNull(value), ","); //$NON-NLS-1$ boolean some = false; @@ -323,7 +322,7 @@ public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup } public List> getBuilderConfigDescriptors() { - return Jenkins.getInstance().>getDescriptorList(AbstractBuildParameters.class); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java index e05da161..95d267c6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java @@ -33,5 +33,5 @@ public enum HudsonResult { /** * Hudson Result */ - SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, QUEUED, BUILDING, PENDING, MANUAL + SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, BUILDING, PENDING, MANUAL } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java deleted file mode 100644 index 3d6eb19f..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java +++ /dev/null @@ -1,244 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.util; - -import hudson.model.ParameterValue; -import hudson.model.ParametersAction; -import hudson.model.Queue; -import hudson.model.Queue.Item; -import hudson.model.Queue.WaitingItem; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.logging.Logger; - -import jenkins.model.Jenkins; - -import org.joda.time.DateTime; -import org.joda.time.Interval; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; - -import ch.srsx.timetable.model.IntervalParameterValue; - -/** - * Utitily class to help scheduling Jobs in the context of the build-pipeline - * plugin. - * - * @author rhirt - * - */ -public final class ScheduleUtil { - - /** A Logger object is used to log messages */ - private static final Logger LOGGER = Logger.getLogger(ScheduleUtil.class.getName()); - - /** The format used for specifying time */ - private static DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm"); - - /** Delimiter for time-range on one line */ - public static final String TIMES_DELIMITER = "-"; - - /** Define a comparator for intervals */ - public static class IntervalComparator implements Comparator { - - @Override - public int compare(Interval o1, Interval o2) { - return o1.getStart().compareTo(o2.getStart()); - } - } - - /** - * Calculate a delay, depending on the windows defined for the job. - * - * @param action - * - the action - * @return int - the calculated delay for the job-execution - */ - public static int calcDelay(ParametersAction action) { - - // Check for instances of IntervalParameterValue on the job-definition - if (action == null || (!hasIntervalParameterValues(action))) { - LOGGER.info("No action or intervals present, delay 0"); - return 0; - } - - // Now, determine the deployment-windows on the job - // Derive it from the IntervalParameterValues: - final List intervals = getIntervals(action); - - // Check the intervals for a match - final DateTime now = new DateTime(); - for (Interval interval : intervals) { - if (interval.isAfterNow()) { - // the interval is after now, take the delay from there and - // return - final int delay = (interval.getStart().getMillisOfDay() - now.getMillisOfDay()) / 1000; - LOGGER.info("Now before the next interval, delay is " + delay); - return delay; - } else if (interval.containsNow()) { - // now is inside an interval, no delay necessary - LOGGER.info("Now inside an interval, delay is 0"); - return 0; - } - } - - // We are after the last interval, but have at least one interval - // now take the start of the first interval and add a day to it, - // than take the time from then - now - final DateTime firstOfNextDay = intervals.get(0).getStart().plusDays(1); - final int delay = (int) (firstOfNextDay.getMillis() - now.getMillis()) / 1000; - LOGGER.info("Now before the first interval next day, delay is " + delay); - return delay; - } - - /** - * Retrieve a queued item from the build history - * - * @param name - * the name - * @return Item - the item from the queue for the given name - */ - public static Item getQueuedItem(String name) { - final Queue q = Jenkins.getInstance().getQueue(); - final List qitems = q.getApproximateItemsQuickly(); - - // Now search queued items for given project name - for (Item qitem : qitems) { - if (qitem.task.getDisplayName().equals(name)) { - LOGGER.fine("Found queued item for Task " + name); - return qitem; - } - } - return null; - } - - /** - * Retrieve a queued item of type WaitingItem - * - * @param name - * the name - * @return WaitingItem - the waiting item from the queue for the given name - */ - public static WaitingItem getQueuedWaitingItem(String name) { - final Item qItem = ScheduleUtil.getQueuedItem(name); - if (qItem instanceof WaitingItem) { - return (WaitingItem) qItem; - } - return null; - } - - /** - * Read the provided intervals from the action of the job - * - * @param action - * the action - * @return List - the list of the intervals read from the action, - * sorted ascending - */ - private static List getIntervals(ParametersAction action) { - final List intervals = new ArrayList(); - final List pvs = action.getParameters(); - for (ParameterValue pv : pvs) { - if (pv instanceof IntervalParameterValue) { - intervals.add(((IntervalParameterValue) pv).getValue()); - } - } - Collections.sort(intervals, new ScheduleUtil.IntervalComparator()); - return intervals; - } - - /** - * Parse a given time of the format HH:mm and convert it into a DateTime - * today - * - * @param value - * - the value to parse - * @return DateTime - the parsed time, moved into today - * */ - public static DateTime parseTime(String value) { - final DateTime today = new DateTime(); - try { - return fmt.parseDateTime(value.trim()).withDate(today.getYear(), today.getMonthOfYear(), today.getDayOfMonth()); - } catch (IllegalArgumentException iaex) { - LOGGER.warning("Time could not be parsed: " + value); - } - return null; - } - - /** - * Parse the given String to create an interval from a compact - * representation in the form (HH:mm - HH:mm) - * - * @param value - * - the value in the format HH:mm - HH:mm - * @return Interval - the interval parsed - */ - public static Interval parseInterval(String value) { - final String[] timeValues = value.split(TIMES_DELIMITER); - if (timeValues.length == 2) { - return new Interval(parseTime(timeValues[0]), parseTime(timeValues[1])); - } - return null; - } - - /** - * Format the start time of the given interval in the form (HH:mm) - * - * @param value - * - the interval from which the start-time will be formatted - * @return String - the formatted String - */ - public static String formatStartTime(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getStart()); - } - - /** - * Format the end time of the given interval in the form (HH:mm) - * - * @param value - * - the interval from which the end-time will be formatted - * @return String - the formatted String - */ - public static String formatEndTime(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getEnd()); - } - - /** - * Format the given interval for a compact representation in the form (HH:mm - * - HH:mm) - * - * @param value - * - the interval to be formatted - * @return String - the formatted String - */ - public static String formatInterval(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getStart()) + TIMES_DELIMITER + fmt.print(value.getEnd()); - } - - /** - * Check whether an IntervalParameterValue is present on the job - * - * @param action - * - the action - * @return boolean - whether IntervalParameterValue(s) are present - */ - private static boolean hasIntervalParameterValues(ParametersAction action) { - final List pvs = action.getParameters(); - for (ParameterValue pv : pvs) { - if (pv instanceof IntervalParameterValue) { - return true; - } - } - return false; - } -} diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java deleted file mode 100644 index ccfa129c..00000000 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java +++ /dev/null @@ -1,220 +0,0 @@ -package ch.srsx.timetable.model; - -import hudson.Extension; -import hudson.model.ParameterValue; -import hudson.model.ParameterDefinition; -import hudson.util.FormValidation; - -import java.util.Date; - -import net.sf.json.JSONObject; - -import org.joda.time.DateTime; -import org.joda.time.Interval; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.export.Exported; - -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; -import ch.srsx.timetable.Messages; - -/** - * @author rhirt - */ -public class IntervalParameterDefinition extends ParameterDefinition { - /** Serial ID */ - private static final long serialVersionUID = -8142303813745641667L; - - /** Default value */ - private Interval defaultValue; - - /** - * Constructor requesting name, startTime, endTime and description - * - * @param name - * the name - * @param startTime - * - string in the format HH:mm for the start of the interval - * @param endTime - * - string in the format HH:mm for the end of the interval - * @param description - * the description - */ - @DataBoundConstructor - public IntervalParameterDefinition(String name, String startTime, String endTime, String description) { - super(name, description); - this.defaultValue = new Interval(ScheduleUtil.parseTime(startTime), ScheduleUtil.parseTime(endTime)); - } - - /** - * Constructor requesting name, interval and description - * - * @param name - * the name - * @param value - * - interval to derive this interval from - * @param description - * the description - */ - public IntervalParameterDefinition(String name, Interval value, String description) { - super(name, description); - this.defaultValue = value; - } - - @Override - public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { - if (defaultValue instanceof IntervalParameterValue) { - final IntervalParameterValue value = (IntervalParameterValue) defaultValue; - return new IntervalParameterDefinition(getName(), value.getValue(), getDescription()); - } else { - return this; - } - } - - /** - * @return String - text representation of the time value at the start of - * the interval - */ - public String getFormattedInterval() { - return ScheduleUtil.formatInterval(defaultValue); - } - - /** - * @return Date - the start of the interval as a date - */ - @Exported - public Date getIntervalFrom() { - if (defaultValue == null) { - return null; - } - return new Date(defaultValue.getStartMillis()); - } - - /** - * @return String - text representation of the time value at the start of - * the interval - */ - public String getIntervalFromText() { - return ScheduleUtil.formatStartTime(defaultValue); - } - - /** - * @return Date - the end of the interval as a date - */ - @Exported - public Date getIntervalTo() { - if (defaultValue == null) { - return null; - } - return new Date(defaultValue.getEndMillis()); - } - - /** - * @return String - text representation of the time value at the end of the - * interval - */ - public String getIntervalToText() { - return ScheduleUtil.formatEndTime(defaultValue); - } - - @Override - public IntervalParameterValue getDefaultParameterValue() { - return new IntervalParameterValue(getName(), defaultValue, getDescription()); - } - - @Override - public ParameterValue createValue(StaplerRequest req) { - // TODO Auto-generated method stub - // FileItem src; - // try { - // src = req.getFileItem( getName() ); - // } catch (ServletException e) { - // // Not sure what to do here. We might want to raise this - // // but that would involve changing the throws for this call - // return null; - // } catch (IOException e) { - // // ditto above - // return null; - // } - // if ( src == null ) { - // // the requested file parameter wasn't uploaded - // return null; - // } - // FileParameterValue p = new FileParameterValue(getName(), src, - // getFileName(src.getName())); - // p.setDescription(getDescription()); - // p.setLocation(getName()); - // return p; - return null; - } - - @Override - public ParameterValue createValue(StaplerRequest req, JSONObject jo) { - final IntervalParameterValue value = req.bindJSON(IntervalParameterValue.class, jo); - value.setDescription(getDescription()); - return value; - } - - /** - * @param value - * the value - * @return the interval parameter value object created - */ - public IntervalParameterValue createValue(Interval value) { - return new IntervalParameterValue(getName(), value, getDescription()); - } - - /** - * DescriptorImpl for this parameter definition - * - * @author rhirt - * - */ - @Extension - public static class DescriptorImpl extends ParameterDescriptor { - @Override - public String getDisplayName() { - return Messages.IntervalParameterDefinition_DisplayName(); - } - - @Override - public String getHelpFile() { - return "/plugin/build-pipeline-plugin/help/parameter/interval.html"; - } - - /** - * Checks if parameterised build intervals are valid. - * - * @param value - * - the value to check - * @return FormValidation - status whether validation succeeded - */ - public FormValidation doCheckTime(@QueryParameter String value) { - if (ScheduleUtil.parseTime(value) == null) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); - } - return FormValidation.ok(); - } - - /** - * Checks if parameterised build intervals are valid. - * - * @param fromValue - * - the value (from) to check - * @param toValue - * - the value (to) to check - * @return FormValidation - status whether validation succeeded - */ - public FormValidation doCheckInterval(@QueryParameter String fromValue, @QueryParameter String toValue) { - final DateTime from = ScheduleUtil.parseTime(fromValue); - final DateTime to = ScheduleUtil.parseTime(toValue); - if (from == null || to == null) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); - } else if (!from.isBefore(to)) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalInterval()); - } - return FormValidation.ok(); - } - } -} \ No newline at end of file diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java deleted file mode 100644 index 9094d796..00000000 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Luca Domenico Milanesio, Tom Huybrechts - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ch.srsx.timetable.model; - -import hudson.EnvVars; -import hudson.model.ParameterValue; -import hudson.model.AbstractBuild; -import hudson.model.Run; -import hudson.util.VariableResolver; - -import java.util.Locale; - -import org.joda.time.Interval; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.export.Exported; - -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; - -/** - * {@link ParameterValue} created from {@link IntervalParameterDefinition}. - * - * @author rhirt - */ -public class IntervalParameterValue extends ParameterValue { - /** The serial id */ - private static final long serialVersionUID = 1L; - - /** the interval in question */ - @Exported(visibility = 4) - private final Interval value; - - /** - * Constructor taking name and value. - * - * @param name - * the name - * @param value - * the value, given as a formatted String (HH:mm - HH:mm) - */ - @DataBoundConstructor - public IntervalParameterValue(String name, String value) { - this(name, value, null); - } - - /** - * Constructor taking name, value and description. - * - * @param name - * the name - * @param value - * the value, given as a formatted String (HH:mm - HH:mm) - * @param description - * the description - */ - public IntervalParameterValue(String name, String value, String description) { - super(name, description); - this.value = ScheduleUtil.parseInterval(value); - } - - /** - * Constructor taking name, interval and description. - * - * @param name - * the name - * @param interval - * the value, given as an interval object - * @param description - * the description - */ - public IntervalParameterValue(String name, Interval interval, String description) { - super(name, description); - this.value = interval; - } - - /** - * Exposes the name/value as an environment variable. - * - * @param build - * - the run - * @param env - * - the environment - */ - @Override - public void buildEnvironment(Run build, EnvVars env) { - env.put(name, value.toString()); - env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); // backward - // compatibility - // pre - // 1.345 - } - - @Override - public VariableResolver createVariableResolver(AbstractBuild build) { - return new VariableResolver() { - public String resolve(String name) { - return IntervalParameterValue.this.name.equals(name) ? ScheduleUtil.formatInterval(value) : null; - } - }; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final IntervalParameterValue other = (IntervalParameterValue) obj; - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "(IntervalParameterValue) " + getName() + "='" + value.toString() + "'"; - } - - @Override - public String getShortDescription() { - return name + '=' + ScheduleUtil.formatInterval(value); - } - - public Interval getValue() { - return value; - } -} diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 382cbc63..4b13d6f5 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -38,28 +38,17 @@ - {{#if build.isQueued}} -
- {{build.queuedForDate}} {{build.queuedForTime}} -
- {{else}} -
- {{build.startDate}} {{build.startTime}} -
- {{/if}} +
+ {{build.startDate}} {{build.startTime}} +
- {{#if build.isQueued}} - - {{build.queueWaitDuration}} - - {{/if}} {{#if build.isComplete}} - + {{build.duration}} {{/if}} {{#if build.isBuilding}} - + {{build.duration}} {{/if}} @@ -88,37 +77,34 @@
- {{#if build.isManualTrigger}} - This job is manually triggered - {{/if}} {{#if project.parameters.length}} This job has parameters {{/if}} {{#unless build.isPending}} {{#unless build.isReadyToBeManuallyBuilt}} - {{#unless build.isQueued}} - - - - - console - - - - - console - - - - - console - - - - {{/unless}} + + + + + console + + + + + console + + + + + console + + + {{/unless}} {{/unless}} + + {{#if build.isRerunnable}} {{#if build.isSuccess}} {{#if ${!from.triggerOnlyLatestJob}}} @@ -157,11 +143,6 @@ {{/if}} {{/if}} - {{#if build.isQueued}} - - cancel this build - - {{/if}}
{{/unless}} {{#if project.disabled}} @@ -247,28 +228,20 @@
- -
+ +
Trigger a Pipeline
-
- + + - +
Run
- -
-
- M2Release in a Pipeline -
-
Release
-
-
@@ -327,103 +300,66 @@ - - + + - - - - + + - - - - - - - - - - - + + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - + @@ -450,7 +386,7 @@
- + @@ -479,31 +415,12 @@
${buildParam.getKey()}: ${buildParam.getValue()}
- - -
- - - - - - +
+ - - - - - - - - - - - - - + - + + - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/src/main/resources/ch/srsx/timetable/Messages.properties b/src/main/resources/ch/srsx/timetable/Messages.properties deleted file mode 100644 index 7073c54d..00000000 --- a/src/main/resources/ch/srsx/timetable/Messages.properties +++ /dev/null @@ -1,4 +0,0 @@ -IntervalParameterDefinition.DisplayName=Interval Parameter -IntervalParameterDefinition.IllegalInterval=Illegal Interval. -IntervalParameterDefinition.IllegalTime=Illegal Time-Format (should be HH:mm). - diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly deleted file mode 100644 index 2badb0d2..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties deleted file mode 100644 index b218a253..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -Name=Name -Interval\ From=Interval Startzeit -Interval\ To=Interval Endzeit -Description=Beschreibung diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html deleted file mode 100644 index d4f5f8b6..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html +++ /dev/null @@ -1,3 +0,0 @@ -
- The start-time of the interval to be used for allowed job-execution. -
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html deleted file mode 100644 index d4f5f8b6..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html +++ /dev/null @@ -1,3 +0,0 @@ -
- The start-time of the interval to be used for allowed job-execution. -
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly deleted file mode 100644 index daddbb86..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -
- - -
-
-
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly deleted file mode 100644 index 1a8e24f0..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index a7e3982a..9e27519e 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -61,7 +61,7 @@ height: 48px; margin-left: auto; margin-right: auto; - width: 392px; + width: 340px; } .icon-container { @@ -132,11 +132,6 @@ } .build-duration-container { - text-align: left; - text-overflow: clip; - overflow: hidden; - white-space: nowrap; - font-size: 0.9em; width: 135px; } @@ -290,11 +285,6 @@ tr.spacerRow td { color: #222; } -.QUEUED { - background-color: #eb642d; - color: #222; -} - .BUILDING { background-color: #f7f94b; color: #222; @@ -382,132 +372,3 @@ a:hover { .hidden { display: none; } - -.tile { - background: url(../images/tile.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileCrossBottom { - background: url(../images/tile-cross-bottom.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileCross { - background: url(../images/tile-cross.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileDown { - background: url(../images/tile-down.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileLeft { - background: url(../images/tile-left.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileRight { - background: url(../images/tile-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.tileTopRight { - background: url(../images/tile-top-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} - -.hTile { - background: url(../images/h-tile.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileCrossBottom { - background: url(../images/h-tile-cross-bottom.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileCross { - background: url(../images/h-tile-cross.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileDown { - background: url(../images/h-tile-down.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileLeft { - background: url(../images/h-tile-left.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileRight { - background: url(../images/h-tile-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.hTileTopRight { - background: url(../images/h-tile-top-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} - -.rTile { - background: url(../images/r-tile.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileCrossBottom { - background: url(../images/r-tile-cross-bottom.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileCross { - background: url(../images/r-tile-cross.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileDown { - background: url(../images/r-tile-down.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileLeft { - background: url(../images/r-tile-left.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileRight { - background: url(../images/r-tile-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} -.rTileTopRight { - background: url(../images/r-tile-top-right.png); - background-size: 100% 100%; - background-repeat: no-repeat; - vertical-align: middle; -} diff --git a/src/main/webapp/help/parameter/interval.html b/src/main/webapp/help/parameter/interval.html deleted file mode 100644 index 7f4c7b9f..00000000 --- a/src/main/webapp/help/parameter/interval.html +++ /dev/null @@ -1,4 +0,0 @@ -
- Defines an interval parameter, where users can enter a start time and an end time, - which you can use to identify times when job execution is allowed. -
\ No newline at end of file diff --git a/src/main/webapp/images/h-tile-cross-bottom.png b/src/main/webapp/images/h-tile-cross-bottom.png deleted file mode 100644 index c09666e727ed14d85ac1d79947955da0238bc8c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH*D1Jf!`7srr_xVLu$^Hw=XI3ylC!n^bT4yNl# zj}~_ZxzCs)wZLN0w*Qv#MT@s@ZLUgTtJrw!ZP{6`hga{eZ8A1#S*Xavo0!4j>?|R5 z=z>67n*sfVLfQ{6j9h>HZvE+4z3I1eW9`4+jLe!7^#0zzpEdjLPBY1vl@=O$HMi$W z{^pw{`+sWMF0+#BuX``*FxO7>yvO`$YwGLQeKG%jeqqX-lowlhdgu4%E-(3AfAqrV z^~>*d?Av(!YyGr$_P^A6dv{)$ZEJSz%InCg->UDn+kgMrvv=KXCzJFEJN5THY31$R zeZ?aA*s=4;Q*!E0D4$>XzV3JN{PQ;++q{AX$6v>#ufHpQnw9&4Kk8LOzSlnk$zQKz ztFJBJtMcV#$*Xs|$tORyEm~&#`fBy{D?irXD3de3-}dj-|F>ng@6Jx&f4}_uqrz8h z9P+a^{5vOT_ht36D;$%-d)jUDS7elikz%fNqttoUQNa?#|SYylfMc&pA&e%?%|Oep!4(ma{n8zzhr%7eYx$y1Aphm RtAR!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?cI&M%?2V4j%WT^Zxm*Bn8I<& zLo-OFuDntU0%LPFvcMiHT48PTWmkDYRmfbjSIdklW zKErkXX=x=0N9^H!d&_kC_Jy&#>yDd8N!+b}FmK)Q!adh+FSkEXJb(Fr0)wZkpUXO@geCy0SD-fl diff --git a/src/main/webapp/images/h-tile-down.png b/src/main/webapp/images/h-tile-down.png deleted file mode 100644 index 88fe3a2fd0d541e05a31c226111925e5037ed91a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hU$6V@O5Z+dI~IEd~s1i5$}`_`a{VV0`Yl z$bhpe+;1YswlnshO+Q{!wohVtyldTafvm`B3#VMFyL)@v+TKq!TlP=9*EvVeYRj${ z7oJP**50dYRkC(#uZV2btYv1Cs!dqbzF73-9%&;^0EHUpEEg^E19 zi5VQu#0Yh~xUkRnrfd7R+*3fC@`KCXT?8A#qPFF|yYr8_Edp?}l&rS!n*!bPd*Akn zi=5AYzT?)#_xAE?zG8{bTq1|G0~B$%AfDsN#zfzX=C`oLtt N;OXk;vd$@?2>@=z_&@*v diff --git a/src/main/webapp/images/h-tile-left.png b/src/main/webapp/images/h-tile-left.png deleted file mode 100644 index 08a0b263316f9001a5ed1a69a17a496819558a56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1|(&F^?3p*#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0rOCF{C2y?Onxn%?=E%fh!y~{(rlH@!CZx znTong8yc3anqS>F)28;QN^)Avw5KvVwjM7nN>*a;kWnaVIKfC?M$hYA^FG_ePxhBR zZusVR-P-$RdAG~_E|>V)eXX%;zy7*(?fq{8KmOf0iR!KoeKviMs|)<+neP9xex7M< sd42u6CkzvrUHAkn9q7uqE!}3nZoaapp*V2m($y85}Sb4q9e02OPUkpKVy diff --git a/src/main/webapp/images/h-tile-right.png b/src/main/webapp/images/h-tile-right.png deleted file mode 100644 index c03f7b42e9dd82f5dc0a89728ca16da023390482..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA}&+iyz!UFFtubAIjp^#@=7y{E6QUeasT^SQ>q z=9NWXdTZSE(wy66K#q<7>fH&m)D~*D^a)rwz2I1=3}H;)z3zYY4B?CekNp!lvI6;>1s;*b3=DjSK$uZf!>fUTfr-V_#WAEJ?(N;|ye0<`hr~GTLx0;vH>fP= zo4c|l+|{H#~pz@{>%D^DcdQB^h@m zr4C&XXxpbW=QMBaR-MFrn>q=wNSlF4%RukTv)Qu$=#_vj$ibXphio- zLu&$x0-z;4yr_E6gckmnz4Wx`WbyMepJPnVW^enOIVbP0l+XkKCiwmg diff --git a/src/main/webapp/images/h-tile.png b/src/main/webapp/images/h-tile.png deleted file mode 100644 index 64a3d1d384fbb9f0189ad24095daad968cc40abb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1SBWTkdy;bjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4cwkCjv*CsZ!Z`!GBEHQUhvaAG`skqs7gWi frLC7o6x?KMmB*zw+b=N&7*-6Pu6{1-oD!MPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rg2lK~y+Tos-{F z5>XV#`OEq%>JO-%ee9VT2p>Y-vCYsWHAT&78x{r#h?uojhDrG)phJKyl(aSptMR}C z58w9O3mc?OO%Irhn{vMjUMXf#0Yf3KZ-AgFLrT!L@q z8I))e>C6F((&{?AbMw$N%__S=FdPn{C<<1r!s;gjr{_PeQSbC1hhp&i{TK`eR@;Pt zEjJWJ^ZW`@qRgN?cBo@9oX4w7ojnt3Hjh9c@JnDA2E?b!sGW6jUr6BAgB`}5*|41x z@_ro+8zQU3m&pK zvz)ebij~hjMoIq;x7*DHl}ZKvjct_My?;iIyg@3J;sS#1d{oV%)b9Qjt=xpq=L6kd zTR?+CFT-#5QEYYpNHSA^D2kxNr#@RijOSrVT0=0ghx9=e1^ohQzJ^drgY8z$};rI!!lvI6;>1s;*b3=DjSK$uZf!>a+PVV|dqV@O5Z+q<^ew*w>^0yPV7AKBu(@c;kg zDd%P>Mla`E_+a7D(%Jmi{X|YYdGp+|fJ9sZ#zWiJAx$~>X edmqnQ_P->>BsND}{68>y89ZJ6T-G@yGywpY8rk&# diff --git a/src/main/webapp/images/r-tile-cross.png b/src/main/webapp/images/r-tile-cross.png deleted file mode 100644 index 6e268563430bf92b98b8d6504818b902670302eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0Q69F{C2y?cKG0M;s&^F7k15KKwhMW3O1F zv$6n(QF57GR>A*#^23%s0V}5$91E4{%ZOM%{rlgS zKR?!_E}H-Ter|1D&D-nU>#u$6U6>cV{lw?U#meU{TsM6C?9j}$3Fl7 diff --git a/src/main/webapp/images/r-tile-down.png b/src/main/webapp/images/r-tile-down.png deleted file mode 100644 index 5fb5c59052e312c6b5c711603a158302cebd5987..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0`gFF{C2y?G;5oXGal-i!c2-0@@F4_}{wV zCWBaTr?$YPX<~H+0ruZzJ{wJbo|k6k-+%tN;mX@@zwW5tA$@vNWd6S*ncdT`f86#z z;{4~xYnDrAzrCvYsp`k|PkXI5uXO&u!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~ln2F{C2y?cI%D%#I=s7w@Vx{rDfBGWXh{ zbElkIM7%f%=EMNzrxSYsw=m*Pp7NfVFC8(%e1HLyRIt#)atDl1x5~or>mdK II;Vst0L=5LI{*Lx diff --git a/src/main/webapp/images/r-tile-right.png b/src/main/webapp/images/r-tile-right.png deleted file mode 100644 index 495819724f8414097fdccd0c16a004b7eff5956e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA>Y%*F{C2y?OjJNW=8>si+z%YoP~-N{}1mt z$66#PDC5cUoZ;ZzGxhwlmR*yt3Y>i})^6SQgU@Vr&85{#S}q7WxpT-W(v@*!)jnsd zd2z=ce%a$=^Xm2I>ez+%@15WOymV&86`TJRySSHS>X)>lx@>P{{ci*93k_G-I{dWv jFCP$hw_M;%zQed+n@r2UAL(y_@xkEf>gTe~DWM4f^j)5T diff --git a/src/main/webapp/images/r-tile-top-right.png b/src/main/webapp/images/r-tile-top-right.png deleted file mode 100644 index d1e5a7c24f4c0eed81713b2349514c71abb563b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 569 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hd+7V@O5Z+q)b6L;^(`9-eMGCDh~L#KZaU z@BG8J#5Zi+xFz~xtV63KXXkH*@4xq_6{%hRJ2UvsGr5{yKYrHvWYvC3tJzUyAGW&r z>Dm8x`q#(r>wQ04{fdz#-B!lvI6;>1s;*b3=DjSK$uZf!>a+PVY#P^V@O5Z+q-+cjyOm-TugBcu)e7NVQnqz z{KL1-UAyFU^IZI$9q*Qjz25m;gmZG{O z*vilA$&h8X|04S0{k~_L|G&+xye+qm%e`a=lWgUe|Ic1k-HM4@zk2`MIJxC_KR@0j z)o)CHkAXrn>BWWp$qm!)Uf=cWw9y-lC5tnEF}5x@!lvI6;>1s;*b3=DjSK$uZf!>a+Pp}^C{F{C2y?Oj7JXG4aDhf5SZw#(huuHZ{I zUw>h1bf#$Kv|oM`|Nm}lN%^bTs5A3>|MuJsdk))ewpgg#(kEc$^n!!FjA!4U|2FGi zUVE}@+w0Ub_dj1Zi;MfNJMBwxwai_m_3tmFVdQ&MBb@0AzKl6aWAK diff --git a/src/main/webapp/images/tile-down.png b/src/main/webapp/images/tile-down.png deleted file mode 100644 index 3dd1c8e3e66dc9f53026c5479e05b77d4e773b3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~=(5F{C2y?G-~W7DoYxi|<&4jg#L$sNLJY zary^_*}5kdE#_l(TUYu2`=mv)PT75_sjs|uY|*p7hb?UFdMkHX$Nk&X+rRz##@KVm z_szR(IQ@%o)w%yl^|Ooj$KJfOu>A9k?K;1g|BbRYv0kX$(kEc$^nzn#Gp6zFjXjtA ldym|0-P?7$*BS0(Fx@TYxi~g!lvI6;>1s;*b3=DjSK$uZf!>a+Pq1@BOF{C2y?cI%D&V~{V7iT*;-l?gHUwHoE zn`gH!o_wUHaYOscZ-%2MpINg{`f&cfz==s8e}37a=aajAu8+?XhKbBBd;*pZbY(Pr z{8|%n{O@72qP1Ti|J{^#@JC&330~jw1p00i_>zopr0P*>zC;$Ke diff --git a/src/main/webapp/images/tile-right.png b/src/main/webapp/images/tile-right.png deleted file mode 100644 index 59e18e64648106b66693ba866eef23d7de9daff8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?Oj8zLk{vXk}v(^6ma2m5TE{rsyD@0xoS_t(t1An4@IA*)!@LSIJrFTTvp_BDL_ zXTIB>+A8P&Jke&}bMg3U+n#qVZ~b0yEmUsl6R>i6!2xDey?s_XLp0;Sqp$^k&c8sC ffUBSfBT?eso(^gIj9W#ZSYYsU^>bP0l+XkKhG(GI diff --git a/src/main/webapp/images/tile-top-right.png b/src/main/webapp/images/tile-top-right.png deleted file mode 100644 index e7580c793f9a26808f3b699da60e1ea411f55e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>fUTfl1NR#WAEJ?(JP)ze5f(4j*SKbrzcShZY47uD}PF{6;_iJmes41MeYxeCayHjTu z`*_Yi-X1dVTCUZ+b$i>cw4eJK@j4ef|P{Rp$h_SZ3ZSS z3l({I6EirRooOc|XjYHdk$()U+7Q{c3`x`EMb8`94R|)UzZmk#BE`G52UG9q5JI$w-FFOA> u;E#Bsvpe_K=tSr2`E&>|)Ch6%ntw;gzy4>B6)*uYFnGH9xvX Date: Tue, 1 Jul 2014 16:44:47 +0200 Subject: [PATCH 03/21] Fix Queued Entries for multi-use of jobs in pipeline --- pom.xml | 16 +- .../buildpipeline/BuildJSONBuilder.groovy | 22 +- .../buildpipeline/ProjectJSONBuilder.groovy | 2 + .../plugin/buildpipeline/BuildForm.java | 85 +++++- .../plugin/buildpipeline/BuildGrid.java | 9 + .../buildpipeline/BuildPipelineForm.java | 8 +- .../buildpipeline/BuildPipelineView.java | 210 ++++++++++--- .../buildpipeline/DefaultBuildGridImpl.java | 22 +- .../buildpipeline/DefaultProjectGridImpl.java | 15 +- .../DownstreamProjectGridBuilder.java | 33 ++- .../hudson/plugin/buildpipeline/Grid.java | 31 ++ .../plugin/buildpipeline/PipelineBuild.java | 181 +++++++++--- .../plugin/buildpipeline/ProjectForm.java | 128 +++++++- .../plugin/buildpipeline/ProjectGrid.java | 10 + .../trigger/BuildPipelineTrigger.java | 18 +- .../hudson/plugin/util/BuildUtil.java | 34 +++ .../hudson/plugin/util/HudsonResult.java | 2 +- .../hudson/plugin/util/QueueEntry.java | 89 ++++++ .../hudson/plugin/util/QueueUtil.java | 138 +++++++++ .../hudson/plugin/util/ScheduleUtil.java | 213 ++++++++++++++ .../model/IntervalParameterDefinition.java | 220 ++++++++++++++ .../model/IntervalParameterValue.java | 165 +++++++++++ .../buildpipeline/BuildPipelineView/bpp.jelly | 275 ++++++++++++------ .../ch/srsx/timetable/Messages.properties | 4 + .../IntervalParameterDefinition/config.jelly | 41 +++ .../config_de.properties | 4 + .../help-endTime.html | 3 + .../help-startTime.html | 3 + .../IntervalParameterDefinition/index.jelly | 35 +++ .../model/IntervalParameterValue/value.jelly | 33 +++ src/main/webapp/css/main.css | 141 ++++++++- src/main/webapp/help/parameter/interval.html | 4 + .../webapp/images/h-tile-cross-bottom.png | Bin 0 -> 789 bytes src/main/webapp/images/h-tile-cross.png | Bin 0 -> 464 bytes src/main/webapp/images/h-tile-down.png | Bin 0 -> 609 bytes src/main/webapp/images/h-tile-left.png | Bin 0 -> 476 bytes src/main/webapp/images/h-tile-right.png | Bin 0 -> 465 bytes src/main/webapp/images/h-tile-top-right.png | Bin 0 -> 623 bytes src/main/webapp/images/h-tile.png | Bin 0 -> 374 bytes src/main/webapp/images/manual.png | Bin 0 -> 608 bytes .../webapp/images/r-tile-cross-bottom.png | Bin 0 -> 553 bytes src/main/webapp/images/r-tile-cross.png | Bin 0 -> 497 bytes src/main/webapp/images/r-tile-down.png | Bin 0 -> 500 bytes src/main/webapp/images/r-tile-left.png | Bin 0 -> 487 bytes src/main/webapp/images/r-tile-right.png | Bin 0 -> 474 bytes src/main/webapp/images/r-tile-top-right.png | Bin 0 -> 569 bytes src/main/webapp/images/r-tile.png | Bin 0 -> 380 bytes src/main/webapp/images/tile-cross-bottom.png | Bin 0 -> 530 bytes src/main/webapp/images/tile-cross.png | Bin 0 -> 475 bytes src/main/webapp/images/tile-down.png | Bin 0 -> 493 bytes src/main/webapp/images/tile-left.png | Bin 0 -> 482 bytes src/main/webapp/images/tile-right.png | Bin 0 -> 464 bytes src/main/webapp/images/tile-top-right.png | Bin 0 -> 652 bytes src/main/webapp/images/tile.png | Bin 0 -> 380 bytes src/main/webapp/js/build-pipeline.js | 16 +- .../plugin/buildpipeline/BuildFormTest.java | 4 +- .../trigger/BuildPipelineTriggerTest.java | 8 +- 57 files changed, 1985 insertions(+), 237 deletions(-) create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java create mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java create mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java create mode 100644 src/main/resources/ch/srsx/timetable/Messages.properties create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly create mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly create mode 100644 src/main/webapp/help/parameter/interval.html create mode 100644 src/main/webapp/images/h-tile-cross-bottom.png create mode 100644 src/main/webapp/images/h-tile-cross.png create mode 100644 src/main/webapp/images/h-tile-down.png create mode 100644 src/main/webapp/images/h-tile-left.png create mode 100644 src/main/webapp/images/h-tile-right.png create mode 100644 src/main/webapp/images/h-tile-top-right.png create mode 100644 src/main/webapp/images/h-tile.png create mode 100644 src/main/webapp/images/manual.png create mode 100644 src/main/webapp/images/r-tile-cross-bottom.png create mode 100644 src/main/webapp/images/r-tile-cross.png create mode 100644 src/main/webapp/images/r-tile-down.png create mode 100644 src/main/webapp/images/r-tile-left.png create mode 100644 src/main/webapp/images/r-tile-right.png create mode 100644 src/main/webapp/images/r-tile-top-right.png create mode 100644 src/main/webapp/images/r-tile.png create mode 100644 src/main/webapp/images/tile-cross-bottom.png create mode 100644 src/main/webapp/images/tile-cross.png create mode 100644 src/main/webapp/images/tile-down.png create mode 100644 src/main/webapp/images/tile-left.png create mode 100644 src/main/webapp/images/tile-right.png create mode 100644 src/main/webapp/images/tile-top-right.png create mode 100644 src/main/webapp/images/tile.png diff --git a/pom.xml b/pom.xml index 17943d09..858118b6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 1.509.3 build-pipeline-plugin - 1.4.3-SNAPSHOT + 1.4.4-SNAPSHOT hpi Build Pipeline Plugin This plugin provides build pipeline functionality to Hudson and Jenkins. This allows a chain of jobs to be visualised in a new view. Manual jobs in the pipeline can be triggered by a user with the appropriate permissions manually confirming. @@ -95,7 +95,7 @@ org.hamcrest hamcrest-core - 1.2 + 1.3 test @@ -109,6 +109,11 @@ parameterized-trigger 2.17 + + org.jenkins-ci.plugins.m2release + m2release + 0.14.0 + org.seleniumhq.selenium @@ -143,6 +148,11 @@ 1.8.3 test + + joda-time + joda-time + 1.6 + @@ -221,6 +231,7 @@ 2.7.1 + 1.6 ${pmd.config.file} @@ -247,6 +258,7 @@ Medium true findbugs-exclude.xml + false diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy index af7e0d3a..4ea856cc 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy @@ -3,10 +3,11 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline import groovy.json.JsonBuilder import hudson.model.Cause import hudson.model.Item +import hudson.model.ItemGroup class BuildJSONBuilder { - static String asJSON(PipelineBuild pipelineBuild, Integer formId, Integer projectId, List buildDependencyIds, ArrayList params) { + static String asJSON(ItemGroup context, PipelineBuild pipelineBuild, Integer formId, Integer projectId, List buildDependencyIds, ArrayList params) { def builder = new JsonBuilder() def buildStatus = pipelineBuild.currentBuildResult def root = builder { @@ -18,16 +19,17 @@ class BuildJSONBuilder { extId(pipelineBuild.currentBuild?.externalizableId) hasPermission(pipelineBuild.project?.hasPermission(Item.BUILD)); hasUpstreamBuild(null != pipelineBuild.upstreamBuild) + isQueued(buildStatus == 'QUEUED') isBuilding(buildStatus == 'BUILDING') - isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL') + isComplete(buildStatus != 'BUILDING' && buildStatus != 'PENDING' && buildStatus != 'MANUAL' && buildStatus != 'QUEUED') isPending(buildStatus == 'PENDING') isSuccess(buildStatus == 'SUCCESS') isReadyToBeManuallyBuilt(pipelineBuild.isReadyToBeManuallyBuilt()) isManualTrigger(pipelineBuild.isManualTrigger()) isRerunnable(pipelineBuild.isRerunnable()) - isLatestBuild(null != pipelineBuild.currentBuild?.number && pipelineBuild.currentBuild?.number == pipelineBuild.project.getLastBuild()?.number) - isUpstreamBuildLatest(null != pipelineBuild.upstreamBuild?.number && pipelineBuild.upstreamBuild?.number == pipelineBuild.upstreamPipelineBuild?.project?.getLastBuild()?.number) - isUpstreamBuildLatestSuccess(null != pipelineBuild.upstreamBuild?.number && pipelineBuild.upstreamBuild?.number == pipelineBuild.upstreamPipelineBuild?.project?.lastSuccessfulBuild?.number) + isLatestBuild(pipelineBuild.latestBuild) + isUpstreamBuildLatest(pipelineBuild.upstreamBuildLatest) + isUpstreamBuildLatestSuccess(pipelineBuild.upstreamBuildLatestSuccess) number(pipelineBuild.currentBuild?.number) progress(pipelineBuild.buildProgress) progressLeft(100 - pipelineBuild.buildProgress) @@ -37,20 +39,24 @@ class BuildJSONBuilder { url(pipelineBuild.buildResultURL ? pipelineBuild.buildResultURL : pipelineBuild.projectURL) userId(pipelineBuild.currentBuild?.getCause(Cause.UserIdCause.class)?.getUserId()) estimatedRemainingTime(pipelineBuild.currentBuild?.executor?.estimatedRemainingTime) + queuedForDate(pipelineBuild.formattedQueuedForDate) + queuedForTime(pipelineBuild.formattedQueuedForTime) + queueWaitDuration(pipelineBuild.queueWaitDuration) + queueId(pipelineBuild.queueId) } project { disabled(pipelineBuild.projectDisabled) - name(pipelineBuild.project.name) + name(pipelineBuild.project.getRelativeNameFrom(context)) url(pipelineBuild.projectURL) health(pipelineBuild.projectHealth) id(projectId) parameters(params) } upstream { - projectName(pipelineBuild.upstreamPipelineBuild?.project?.name) + projectName(pipelineBuild.upstreamPipelineBuild?.project?.getRelativeNameFrom(context)) buildNumber(pipelineBuild.upstreamBuild?.number) } } return builder.toString() } -} \ No newline at end of file +} diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy index a8a6fc2e..5b710643 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectJSONBuilder.groovy @@ -16,6 +16,8 @@ class ProjectJSONBuilder { def builder = new JsonBuilder() def root = builder { id(projectForm.id) + row(projectForm.row) + col(projectForm.col) name(projectForm.name) health(projectForm.health) url(projectForm.url) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 163e7dd9..752c3f63 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -2,15 +2,22 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import hudson.model.ItemGroup; import hudson.model.ParametersDefinitionProperty; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; + /** * @author Centrum Systems * @@ -54,17 +61,25 @@ public class BuildForm { * project stringfied list of parameters for the project * */ private final ArrayList parameters; - + + /** + * The item group pipeline view belongs to + */ + private final ItemGroup context; + /** * @param pipelineBuild * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names */ - public BuildForm(final PipelineBuild pipelineBuild) { + public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { + this.context = context; this.pipelineBuild = pipelineBuild; status = pipelineBuild.getCurrentBuildResult(); dependencies = new ArrayList(); for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { - dependencies.add(new BuildForm(downstream)); + dependencies.add(new BuildForm(context, downstream)); } id = hashCode(); final AbstractProject project = pipelineBuild.getProject(); @@ -103,7 +118,7 @@ public List getDependencyIds() { */ @JavaScriptMethod public String asJSON() { - return BuildJSONBuilder.asJSON(pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); + return BuildJSONBuilder.asJSON(context, pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); } public int getId() { @@ -123,6 +138,36 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing by searching for the queue-item from the upstream-build + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { + updated = true; + } + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry.getQueueId() == queueId) { + QueueUtil.removeQueueEntry(qentry); + LOGGER.info("Removed queue entry from map: " + qentry.toString()); + updated = true; + } } return updated; } @@ -135,6 +180,10 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -143,7 +192,11 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - + + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } + public ArrayList getParameterList() { return parameters; } @@ -152,4 +205,26 @@ public Integer getProjectId() { return projectId; } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java index 40c11deb..53d76a0c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildGrid.java @@ -1,5 +1,7 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import hudson.model.AbstractBuild; + /** * {@link Grid} of {@link BuildForm}s, which represents one instance * of a pipeline execution. @@ -7,4 +9,11 @@ * @author Kohsuke Kawaguchi */ public abstract class BuildGrid extends Grid { + /** + * Search for a BuildForm by a given Build in the current BuildGrid + * + * @param build - the build to search for in the BuildGrid + * @return BuildForm the form containing the given build + */ + public abstract BuildForm findBuildForm(AbstractBuild build); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java index 4b4918b9..c87a7e62 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineForm.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.List; -import java.util.logging.Logger; /** * @author Centrum Systems @@ -13,11 +12,6 @@ * */ public class BuildPipelineForm { - /** - * logger - */ - private static final Logger LOGGER = Logger.getLogger(BuildPipelineForm.class.getName()); - /** * projects laid out in a grid using maps to ease accessing (or maybe I made it way too complicated by not using a 2-dimensional array) * Outside map holds rows and inner map has ProjectForm at a particular position (defined with key) @@ -38,6 +32,8 @@ public class BuildPipelineForm { public BuildPipelineForm(final ProjectGrid grid, final Iterable builds) { projectGrid = grid; buildGrids = Arrays.asList(Iterables.toArray(builds, BuildGrid.class)); + // Handle lastSuccessful Builds correctly, when some jobs are used in multiple Views + grid.correctProjectForms(builds); } public ProjectGrid getProjectGrid() { diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 3e9f56c1..7c0c8b8c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -24,12 +24,12 @@ */ package au.com.centrumsystems.hudson.plugin.buildpipeline; -import com.google.common.collect.Iterables; - import hudson.Extension; import hudson.model.Action; import hudson.model.Item; +import hudson.model.ItemGroup; import hudson.model.ParameterValue; +import hudson.model.Queue; import hudson.model.TaskListener; import hudson.model.TopLevelItem; import hudson.model.AbstractBuild; @@ -37,6 +37,7 @@ import hudson.model.Cause; import hudson.model.Cause.UserIdCause; import hudson.model.CauseAction; +import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; import hudson.model.Hudson; import hudson.model.ParametersAction; @@ -45,10 +46,13 @@ import hudson.model.View; import hudson.model.ViewDescriptor; import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; +import hudson.plugins.parameterizedtrigger.BuildTrigger; +import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; import hudson.security.Permission; +import hudson.tasks.Publisher; +import hudson.util.DescribableList; import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; -import jenkins.model.Jenkins; import java.io.IOException; import java.net.URISyntaxException; @@ -56,6 +60,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; @@ -63,6 +68,9 @@ import javax.servlet.ServletException; +import jenkins.model.Jenkins; + +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -71,6 +79,13 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; /** * This view displays the set of jobs that are related @@ -227,9 +242,9 @@ public void print(final TaskListener listener) { * parameters */ public BuildPipelineView(final String name, final String buildViewTitle, - final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { - super(name, Hudson.getInstance()); + final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { + super(name, Jenkins.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; this.noOfDisplayedBuilds = noOfDisplayedBuilds; @@ -390,13 +405,11 @@ public boolean hasDownstreamProjects(final AbstractProject currentProject) public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid project = gridBuilder.build(this); - if (project.isEmpty()) { + final ProjectGrid projectGrid = gridBuilder.build(this); + if (projectGrid.isEmpty()) { return null; } - return new BuildPipelineForm( - project, - Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); } /** @@ -426,21 +439,21 @@ public String getProjectURL(final AbstractProject project) throws URISynta */ @JavaScriptMethod public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final AbstractProject triggerProject = (AbstractProject) super.getJob(triggerProjectName); - final AbstractProject upstreamProject = (AbstractProject) super.getJob(upstreamProjectName); + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); // Get parameters from upstream build - if (upstreamBuild != null) { + if (upstreamBuild == null) { + return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + } else { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } - Action buildParametersAction = null; - if (upstreamBuild != null) { - buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - } - - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -507,6 +520,33 @@ public int rerunBuild(final String externalizableId) { return build; } + /** + * Schedules a build to start (no upstream build). + * + * @param triggerProject + * - Schedule a build to start on this AbstractProject + * @param delay + * - The delay for triggering the build (handle queueing) + * @return next build number + */ + private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { + LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ + final Cause.UpstreamCause upstreamCause = null; + final List buildActions = new ArrayList(); + final CauseAction causeAction = new CauseAction(new MyUserIdCause()); + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: + causeAction.getCauses().add(upstreamCause); + buildActions.add(causeAction); + + final ParametersAction parametersAction = new ParametersAction(); + buildActions.add(parametersAction); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + + return triggerProject.getNextBuildNumber(); + } + /** * Schedules a build to start. * @@ -518,51 +558,100 @@ public int rerunBuild(final String externalizableId) { * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * @param delay + * - The delay for triggering the build (handle queueing) * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction) { + final Action buildParametersAction, final int delay) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); + final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = - buildParametersAction instanceof ParametersAction - ? (ParametersAction) buildParametersAction : new ParametersAction(); - - if (upstreamBuild != null) { + ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction + : new ParametersAction(); - final BuildPipelineTrigger trigger = upstreamBuild.getProject().getPublishersList().get(BuildPipelineTrigger.class); + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - final List configs = trigger.getConfigs(); + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - parametersAction = mergeParameters(parametersAction, (ParametersAction) action); - } else { - buildActions.add(action); - } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + // TODO exchange base and overlay (last param wins) ! + parametersAction = mergeParameters(parametersAction, (ParametersAction) action); + } else { + buildActions.add(action); } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } } buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); + // When triggering here, no queued job for the current upstream-build should be in the queueEntry list -> clear it + // Save the highest queue-id for later comparison + final List oldItems = QueueUtil.getQueuedItemsFor(triggerProject); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + + // Here, the queued item is already available -> save the link from + // the upstream build to that queue entry for later retrieval + final int newId = QueueUtil.getNewQueuedItemId(triggerProject, oldItems); + if (newId > 0) { + QueueUtil.addQueueEntry(new QueueEntry(upstreamBuild, newId)); + } + return triggerProject.getNextBuildNumber(); } + /** + * Used to retrieve the parameters from the upstream project build trigger relative to the given downstream project + * @param project the downstream project + * @param upstreamBuild the upstream project build + * @return the trigger config relative to the given downstream project + */ + private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, + final AbstractBuild upstreamBuild) { + final DescribableList> upstreamProjectPublishersList = + upstreamBuild.getProject().getPublishersList(); + + List configs = null; + + final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); + if (manualTrigger != null) { + final Set downstreamProjectsNames = + Sets.newHashSet(Splitter.on(",").split(manualTrigger.getDownstreamProjectNames())); + if (downstreamProjectsNames.contains(project.getName())) { + configs = manualTrigger.getConfigs(); + } + } + + final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); + if (autoTrigger != null) { + for (BuildTriggerConfig config : autoTrigger.getConfigs()) { + final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").split(config.getProjects())); + if (downstreamProjectsNames.contains(project.getName())) { + configs = config.getConfigs(); + } + } + } + + return configs; + } + /** * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java * @@ -764,7 +853,7 @@ public void setShowPipelineParameters(final boolean showPipelineParameters) { } public boolean isStartsWithParameters() { - return startsWithParameters; + return startsWithParameters && gridBuilder.startsWithParameters(this); } public String getStartsWithParameters() { @@ -852,7 +941,9 @@ public boolean contains(final TopLevelItem item) { public void onJobRenamed(final Item item, final String oldName, final String newName) { LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); try { - gridBuilder.onJobRenamed(this, item, oldName, newName); + if (gridBuilder != null) { + gridBuilder.onJobRenamed(this, item, oldName, newName); + } } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); } @@ -878,16 +969,39 @@ private static final class LinkStyle { @Override public boolean hasPermission(final Permission p) { boolean display = true; - //tester la liste vide seulement en lecture + // tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - //Pas en lecture => permission standard + // Pas en lecture => permission standard display = super.hasPermission(p); } return display; } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = jenkins != null ? jenkins.getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class) : null; + if (project != null) { + // If a M2ReleaseAction is found + final List actions = project.getActions(M2ReleaseAction.class); + if (actions != null && !actions.isEmpty()) { + return true; + } + } + return false; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java index b66e9641..499b8c1e 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultBuildGridImpl.java @@ -1,5 +1,7 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import hudson.model.AbstractBuild; + import java.util.HashMap; import java.util.Map; @@ -12,7 +14,7 @@ public class DefaultBuildGridImpl extends BuildGrid { /** * Actual data. */ - private final Map> data = new HashMap>(); + private final Map> data = new HashMap>(); /** * Dimension of the {@link #data} @@ -59,4 +61,22 @@ public int getColumns() { public int getRows() { return rows; } + + @Override + public BuildForm findBuildForm(AbstractBuild build) { + if (build != null) { + final String fullBuildName = build.getFullDisplayName(); + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + final BuildForm form = get(r, c); + if (form != null) { + if (fullBuildName.equals(form.getFullBuildName())) { + return form; + } + } + } + } + } + return null; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java index 213035dc..8da51701 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java @@ -1,6 +1,8 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -37,7 +39,8 @@ public void set(int row, int col, ProjectForm p) { data.put(row, c); } c.put(col, p); - + p.setCoords(row, col); + rows = Math.max(rows, row + 1); cols = Math.max(cols, col + 1); } @@ -71,4 +74,14 @@ public int getRows() { return rows; } + @Override + public void correctProjectForms(Iterable buildGrids) { + final Collection> maps = data.values(); + for (Map map : maps) { + final Collection forms = map.values(); + for (ProjectForm projectForm : forms) { + projectForm.correctLastSuccessfulBuilds(buildGrids); + } + } + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java index c7a21048..f037e65c 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java @@ -9,6 +9,13 @@ import hudson.util.AdaptedIterator; import hudson.util.HttpResponses; import hudson.util.ListBoxModel; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + +import javax.servlet.ServletException; + import jenkins.model.Jenkins; import jenkins.util.TimeDuration; @@ -19,12 +26,6 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.servlet.ServletException; - -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; - /** * {@link ProjectGridBuilder} based on the upstream/downstream relationship. * @@ -54,9 +55,17 @@ private static final class GridImpl extends DefaultProjectGridImpl { private final AbstractProject start; /** - * @param start The first project to lead the pipeline. + * The item group pipeline view belongs to + */ + private final ItemGroup context; + + /** + * @param context + * item group pipeline view belongs to, used to compute relative item names + * @param start The first project to lead the pipeline. */ - private GridImpl(AbstractProject start) { + private GridImpl(ItemGroup context, AbstractProject start) { + this.context = context; this.start = start; placeProjectInGrid(0, 0, ProjectForm.as(start)); } @@ -78,8 +87,8 @@ private void placeProjectInGrid(final int startingRow, final int startingColumn, set(row, startingColumn, projectForm); final int childrensColumn = startingColumn + 1; - for (final ProjectForm downstreamProject : projectForm.getDependencies()) { - placeProjectInGrid(row, childrensColumn, downstreamProject); + for (final ProjectForm downstreamForm : projectForm.getDependencies()) { + placeProjectInGrid(row, childrensColumn, downstreamForm); row++; } } @@ -98,7 +107,7 @@ public Iterator iterator() { return new AdaptedIterator, BuildGrid>(base) { @Override protected BuildGrid adapt(AbstractBuild item) { - return new BuildGridImpl(new BuildForm(new PipelineBuild(item))); + return new BuildGridImpl(new BuildForm(context, new PipelineBuild(item))); } }; } @@ -194,7 +203,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod @Override public ProjectGrid build(BuildPipelineView owner) { - return new GridImpl(getFirstJob(owner)); + return new GridImpl(owner.getOwnerItemGroup(), getFirstJob(owner)); } @Override diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java index e750f4cf..ca9362b8 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/Grid.java @@ -92,4 +92,35 @@ private boolean hasDataToRight(final int row, final int col) { } return false; } + + /** + * Search for dependencies of the starting project further down the rows. + * + * @param startRow - the row of the project to search from + * @param startCol - the col of the project to search from + * @return boolean - whether there is a project further down from the current position + */ + public boolean hasDependencyFurtherDown(int startRow, int startCol) { + // Search for a further down projectForm + final int nextRow = startRow + 1; + final int nextCol = startCol + 1; + int nextTRow = -1; + for (int locRow = nextRow; locRow < getRows(); locRow++) { + if (get(locRow, nextCol) != null) { + nextTRow = locRow; + break; + } + } + // Now test, that there is no other project between startRow and row of nextPf + // only if there is none, then the furtherDown project is correct for the current starting point + if (nextTRow != -1) { + for (int locRow = nextRow; locRow <= nextTRow; locRow++) { + if (get(locRow, startCol) != null) { + return false; + } + } + return true; + } + return false; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 53fe2510..859d14d6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,19 +29,21 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Hudson; +import hudson.model.Queue.WaitingItem; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; +import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; @@ -180,7 +182,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -277,21 +279,25 @@ private String getBuildResult(final AbstractBuild build) { */ private String getPendingStatus() { - String pendingStatus = HudsonResult.PENDING.toString(); - final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + // Retrieve the correct queued item based on the upstream build (display name + queue id) + if (upstreamBuild != null) { + // Search for Queue-Entry based on the upstream build + final QueueEntry queueEntry = QueueUtil.getQueueEntry(upstreamBuild); + if (queueEntry != null && QueueUtil.getQueuedItem(project, queueEntry) != null) { + return HudsonResult.QUEUED.toString(); + } + } - if (upstreamPB != null) { - if (this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || - getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { - if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { - pendingStatus = HudsonResult.MANUAL.toString(); - } + final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + if (upstreamPB != null && this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { + return HudsonResult.MANUAL.toString(); } } } - return pendingStatus; + return HudsonResult.PENDING.toString(); } @@ -311,13 +317,11 @@ public PipelineBuild getUpstreamPipelineBuild() { } else { upstreamBuildName = ""; } - if (upstreamProjects.size() > 0) { - if (isManualTrigger()) { - for (AbstractProject upstreamProject : upstreamProjects) { - if (upstreamProject.getName().equals(upstreamBuildName)) { - previousProject = upstreamProject; - break; - } + if (upstreamProjects.size() > 0) { + for (AbstractProject upstreamProject : upstreamProjects) { + if (upstreamProject.getName().equals(upstreamBuildName)) { + previousProject = upstreamProject; + break; } } if (previousProject == null) { @@ -342,6 +346,35 @@ public String getBuildDuration() { } } + /** + * Returns the current wait duration (when queued). + * + * @return - Current wait duration or an empty String if no queueing in + * progress is null. + */ + public String getQueueWaitDuration() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); + } else { + return ""; //$NON-NLS-1$ + } + } + + /** + * Returns the current id of the queue item. + * + * @return - Current id of the queue item + */ + public Integer getQueueId() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return wItem.id; + } else { + return 0; //$NON-NLS-1$ + } + } + /** * {@inheritDoc} */ @@ -408,7 +441,7 @@ public String getPipelineVersion() { final String displayName = currentBuild.getDisplayName(); if (displayName == null || displayName.trim().length() == 0) { version = currentBuild.getNumber() > 0 ? String.valueOf(currentBuild.getNumber()) : Strings - .getString("PipelineBuild.RevisionNotAvailable"); + .getString("PipelineBuild.RevisionNotAvailable"); } else { version = displayName; } @@ -427,7 +460,7 @@ public String getPipelineVersion() { public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Hudson.getInstance().isUseSecurity()) { + if (!Jenkins.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -442,14 +475,13 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); + return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) + && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() - && !"PENDING".equals(getCurrentBuildResult()) - && !"BUILDING".equals(getCurrentBuildResult()) - && hasBuildPermission(); + return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) + && !"QUEUED".equals(getCurrentBuildResult()) && hasBuildPermission(); } /** @@ -459,13 +491,50 @@ private boolean upstreamBuildSucceeded() { return this.getUpstreamBuild() != null && HudsonResult.SUCCESS.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * @return upstream build exists and unstable. - */ - private boolean upstreamBuildUnstable() { - return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); - } + /** + * @return upstream build exists and unstable. + */ + private boolean upstreamBuildUnstable() { + return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); + } + + /** + * + * @return whether this is the latest build of this pipeline view + */ + public boolean isLatestBuild() { + if (currentBuild == null || project.getLastBuild() == null) { + return false; + } + // now check for last build on this pipeline view + // how do we find out, if the last build is run on this view? + return currentBuild.getNumber() == project.getLastBuild().getNumber(); + } + + /** + * + * @return whether the upstream build is the latest for this pipeline view + */ + public boolean isUpstreamBuildLatest() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null + || getUpstreamPipelineBuild().getProject().getLastBuild() == null) { + return false; + } + return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastBuild().getNumber(); + } + /** + * + * @return whether the upstream build is the latest successful one for this + * pipeline view + */ + public boolean isUpstreamBuildLatestSuccess() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null + || getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild() == null) { + return false; + } + return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild().getNumber(); + } /** * Determine if the project is triggered manually, regardless of the state of its upstream builds @@ -476,6 +545,13 @@ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); + } else { + for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { + manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); + if (manualTrigger) { + break; + } + } } return manualTrigger; } @@ -504,23 +580,42 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartTime = ""; //$NON-NLS-1$ + String formattedStartDate = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); + formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedStartTime; + return formattedStartDate; } /** - * @return a map including build parameters + * @return Formatted queued for time */ - public Map getBuildParameters() { - final Map retval = new HashMap(); - if (currentBuild != null) { - retval.putAll(currentBuild.getBuildVariables()); + public String getFormattedQueuedForTime() { + String formattedQueuedForTime = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); } + return formattedQueuedForTime; + } - return retval; + /** + * @return Formatted queued for date + */ + public String getFormattedQueuedForDate() { + String formattedQueuedForDate = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForDate; + } + + /** + * @return a map including build parameters + */ + public Map getBuildParameters() { + return BuildUtil.getUnsensitiveParameters(currentBuild); } public boolean isProjectDisabled() { diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 4dbde52a..7b67dd2a 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -1,16 +1,18 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Hudson; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import jenkins.model.Jenkins; import hudson.plugins.parameterizedtrigger.SubProjectsAction; + import org.kohsuke.stapler.bind.JavaScriptMethod; /** @@ -48,18 +50,33 @@ public class ProjectForm { /** * the latest successful build number */ - private final String lastSuccessfulBuildNumber; + private String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private final Map lastSuccessfulBuildParams; + private Map lastSuccessfulBuildParams; /** * keep reference to the project so that we can update it */ private final AbstractProject project; + /** + * keep reference to the first project in the current pipeline view + */ + private final AbstractProject firstProject; + + /** + * Hold the row this project is placed in (handle duplicate project-ids) + */ + private int row; + + /** + * Hold the column this project is placed in (handle duplicate project-ids) + */ + private int col; + /** * @param name * project name @@ -74,6 +91,7 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; + firstProject = null; } /** @@ -81,6 +99,17 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { + this(project, null); + } + + /** + * @param project + * - the project to wrap + * @param firstProject + * - the first project associated with the grid for this + * projectform + */ + public ProjectForm(final AbstractProject project, final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); @@ -90,13 +119,13 @@ public ProjectForm(final AbstractProject project) { url = pipelineBuild.getProjectURL(); dependencies = new ArrayList(); for (final AbstractProject dependency : project.getDownstreamProjects()) { - dependencies.add(new ProjectForm(dependency)); + dependencies.add(new ProjectForm(dependency, firstProject)); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { - final ProjectForm candidate = new ProjectForm(dependency); + final ProjectForm candidate = new ProjectForm(dependency, firstProject); // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); @@ -107,28 +136,54 @@ public ProjectForm(final AbstractProject project) { } this.displayTrigger = true; - final AbstractBuild lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() : lastSuccessfulBuild.getBuildVariables(); + // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are + // used in different pipelines, to avoid wrong parameters in the + // headers) + handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); + this.project = project; + this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * + * This method is only called for a starting project in a pipeline view, + * therefore save it as such. + * * @param p * project to be wrapped. * @return * possibly null. */ public static ProjectForm as(AbstractProject p) { - return p != null ? new ProjectForm(p) : null; + return p != null ? new ProjectForm(p, p) : null; } public String getName() { return name; } + public int getRow() { + return row; + } + + public int getColumn() { + return col; + } + + /** + * Set the coordinates for this project form. + * + * @param row + * - the row this form is set into + * @param col + * - the column this form is set into + */ + public void setCoords(int row, int col) { + this.row = row; + this.col = col; + } + public String getHealth() { return health; } @@ -223,7 +278,56 @@ public int getId() { */ @JavaScriptMethod public String asJSON() { - return ProjectJSONBuilder.asJSON(new ProjectForm(project)); + return ProjectJSONBuilder.asJSON(this); } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + public Map filterSensitiveBuildVariables(AbstractBuild build) { + return BuildUtil.getUnsensitiveParameters(build); + } + + /** + * Correct the last successful build settings of this form, if that build is + * not on the current BuildPipelineView (when Jobs are used on multiple + * Pipelines) + * + * @param buildGrids + * - the build-grids present on this view + */ + public void correctLastSuccessfulBuilds(Iterable buildGrids) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } + } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + } + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); + } + + /** + * Set build-number and build-params for last successful build. + * + * @param lastSuccessfulBuild + * - the last successful build (can be null) + */ + private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() + : filterSensitiveBuildVariables(lastSuccessfulBuild); + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java index 88cb906c..f63aadac 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectGrid.java @@ -19,4 +19,14 @@ public abstract class ProjectGrid extends Grid { * @return never null. */ public abstract Iterable builds(); + + /** + * Correct things in project forms due to usage of jobs in multiple + * pipelines + * + * @param buildGrids + * - the buildGrids available to search for each + * lastSuccessfulBuild of a projectform + */ + public abstract void correctProjectForms(Iterable buildGrids); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java index 9e342e08..d90e3e69 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java @@ -26,17 +26,16 @@ import hudson.Extension; import hudson.Launcher; -import hudson.PluginWrapper; import hudson.Util; +import hudson.PluginWrapper; import hudson.model.BuildListener; -import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; import hudson.model.Item; +import hudson.model.ItemGroup; import hudson.model.Items; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Descriptor; -import hudson.model.Hudson; import hudson.model.Project; import hudson.model.listeners.ItemListener; import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; @@ -54,13 +53,14 @@ import java.util.logging.Level; import java.util.logging.Logger; +import jenkins.model.DependencyDeclarer; +import jenkins.model.Jenkins; + +import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import au.com.centrumsystems.hudson.plugin.buildpipeline.Strings; -import hudson.model.ItemGroup; -import jenkins.model.Jenkins; -import org.kohsuke.stapler.AncestorInPath; /** * The build pipeline trigger allows the creation of downstream jobs which aren't triggered automatically. This allows us to have manual @@ -258,7 +258,7 @@ public static class DescriptorImpl extends BuildStepDescriptor { * @return true if it is possible to add parameters to the trigger */ public boolean canAddParameters() { - final PluginWrapper plugin = Hudson.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ + final PluginWrapper plugin = Jenkins.getInstance().getPluginManager().getPlugin("parameterized-trigger"); //$NON-NLS-1$ return plugin != null && plugin.isActive(); } @@ -296,7 +296,7 @@ public String getHelpFile() { * @param context - the context * @return hudson.util.FormValidation */ - public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, + public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, @QueryParameter("downstreamProjectNames") final String value) { final StringTokenizer tokens = new StringTokenizer(Util.fixNull(value), ","); //$NON-NLS-1$ boolean some = false; @@ -322,7 +322,7 @@ public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup co } public List> getBuilderConfigDescriptors() { - return Hudson.getInstance().>getDescriptorList(AbstractBuildParameters.class); } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java index 26cb865f..7298d2f9 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java @@ -110,6 +110,15 @@ public static ParametersAction getBuildParametersAction(final AbstractBuild parameters = new ArrayList(); + for (ParameterValue parameter : buildParametersAction.getParameters()) { + // FileParameterValue is currently not reusable, so omit these: + if (!(parameter instanceof FileParameterValue)) { + parameters.add(parameter); + } + } + buildParametersAction = new ParametersAction(parameters); } } } @@ -143,4 +152,29 @@ public static ParametersAction mergeParameters(final ParametersAction base, fina return new ParametersAction(params.values().toArray(new ParameterValue[params.size()])); } + /** + * Retrieve build parameters in String format without sensitive parameters (passwords, ...) + * + * @param build the build we retrieve the parameters from + * @return a map of parameters names and values + */ + public static Map getUnsensitiveParameters(final AbstractBuild build) { + final Map retval = new HashMap(); + if (build != null) { + retval.putAll(build.getBuildVariables()); + final Set sensitiveBuildVariables = build.getSensitiveBuildVariables(); + if (sensitiveBuildVariables != null) { + for (String paramName : sensitiveBuildVariables) { + if (retval.containsKey(paramName)) { + // We have the choice to hide the parameter or to replace it with special characters + retval.put(paramName, "********"); + //retval.remove(paramName); + } + } + } + } + + return retval; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java index 95d267c6..e05da161 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/HudsonResult.java @@ -33,5 +33,5 @@ public enum HudsonResult { /** * Hudson Result */ - SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, BUILDING, PENDING, MANUAL + SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORT, QUEUED, BUILDING, PENDING, MANUAL } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java new file mode 100644 index 00000000..a21d62b2 --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java @@ -0,0 +1,89 @@ +package au.com.centrumsystems.hudson.plugin.util; + +import hudson.model.AbstractBuild; +/** + * Utility Class to capture a link from a build form to a queue item. + * @author rhirt + * + */ +public class QueueEntry { + /** + * The name of the upstream project + */ + private String upstreamProjectName; + + /** + * The externalizable id of the upstream build. + */ + private String upstreamExtId; + + /** + * The id of the item in the queue. + */ + private int queueId; + + /** + * Construct from upstreamBuild + * @param upstreamBuild - the upstream build this entry is referring to + */ + public QueueEntry(AbstractBuild upstreamBuild) { + this(upstreamBuild.getProject().getName(), upstreamBuild.getExternalizableId()); + } + /** + * Construct from projectName and externalizable id of upstreamBuild + * @param upstreamProjectName - the project name of the build upstream + * @param upstreamExtId - the externalizable id of the build upstream + */ + public QueueEntry(String upstreamProjectName, String upstreamExtId) { + this(upstreamProjectName, upstreamExtId, 0); + } + /** + * Construct from projectName and externalizable id of upstreamBuild, add a queueId + * @param upstreamProjectName - the project name of the build upstream + * @param upstreamExtId - the ext id of the build upstream + * @param queueId - the id of the build in the queue + */ + public QueueEntry(String upstreamProjectName, String upstreamExtId, int queueId) { + this.upstreamProjectName = upstreamProjectName; + this.upstreamExtId = upstreamExtId; + this.queueId = queueId; + } + + /** + * Derive a map key from the ext-id + * @return the key for the map + */ + public String getKey() { + return upstreamExtId; + } + + public String getUpstreamExtId() { + return upstreamExtId; + } + + public void setUpstreamExtId(String upstreamExtId) { + this.upstreamExtId = upstreamExtId; + } + + public int getQueueId() { + return queueId; + } + + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + public String getUpstreamProjectName() { + return upstreamProjectName; + } + + public void setUpstreamProjectName(String upstreamProjectName) { + this.upstreamProjectName = upstreamProjectName; + } + + @Override + public String toString() { + return "QueueEntry [upstreamProjectName=" + upstreamProjectName + ", upstreamExtId=" + upstreamExtId + ", queueId=" + queueId + "]"; + } + +} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java new file mode 100644 index 00000000..e3467dbd --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java @@ -0,0 +1,138 @@ +package au.com.centrumsystems.hudson.plugin.util; + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Queue; +import hudson.model.Queue.WaitingItem; + +import java.util.HashMap; +import java.util.List; +import java.util.logging.Logger; + +import jenkins.model.Jenkins; + +/** + * Utitily class to help scheduling Jobs in the context of the build-pipeline + * plugin. + * + * @author rhirt + * + */ +public final class QueueUtil { + /** A Logger object is used to log messages */ + private static final Logger LOGGER = Logger.getLogger(QueueUtil.class.getName()); + + /** A static hashmap to bind queued jobs to builds **/ + public static final HashMap QUEUE_ENTRIES = new HashMap(); + + /** + * Adding an entry into the list of queued items. + * @param queueEntry - entry to be added + */ + public static void addQueueEntry(QueueEntry queueEntry) { + LOGGER.info("Add entry to QueueUtil: " + queueEntry.toString()); + QUEUE_ENTRIES.put(queueEntry.getKey(), queueEntry); + } + + /** + * Remove an entry from the list of queued items. + * @param queueEntry - entry to be removed + */ + public static void removeQueueEntry(QueueEntry queueEntry) { + LOGGER.info("Remove entry from QueueUtil: " + queueEntry.toString()); + QUEUE_ENTRIES.remove(queueEntry.getKey()); + } + + /** + * Retrieve a queue entry based on an upstream build + * @param upstreamBuild - the upstream build to look for + * @return QueueEntry - the entry if found + */ + public static QueueEntry getQueueEntry(AbstractBuild upstreamBuild) { + if (upstreamBuild == null) { + return null; + } + return QUEUE_ENTRIES.get(new QueueEntry(upstreamBuild).getKey()); + } + + /** + * Retrieve the queued items for a project + * + * @param project - the project to search queued items for + * @return List - the items + */ + public static List getQueuedItemsFor(AbstractProject project) { + return Jenkins.getInstance().getQueue().getItems(project); + } + + /** + * Retrieve the id of a new queue item relative to the list of items before + * + * @param project - the project to search queued items for + * @param oldItems - list of items to check the new list against + * @return int - the id of the new queue item + */ + public static int getNewQueuedItemId(AbstractProject project, List oldItems) { + List newItems = Jenkins.getInstance().getQueue().getItems(project); + + // Now search the new list for items that are not in the old list, if one does exist, return it's id + for (Queue.Item qitem : newItems) { + if (!oldItems.contains(qitem)) { + LOGGER.fine("Found last queued item for project " + project.getDisplayName() + " with id " + qitem.id); + return qitem.id; + } + } + return 0; + } + + /** + * Retrieve a queued item from the build history + * + * @param project - the project to search queued items for + * @param qentry + * the entry to derive items from + * @return Item - the item from the queue for the given name + */ + public static Queue.Item getQueuedItem(AbstractProject project, QueueEntry qentry) { + return getQueuedItem(project, qentry.getQueueId()); + } + + /** + * Retrieve a Queue.Item of type WaitingItem + * + * @param project - the project to search queued items for + * @param upstreamBuild the upstream build we derive the queue item from + * @return WaitingItem - the waiting item from the queue for the given upstreamBuild + */ + public static WaitingItem getQueuedWaitingItem(AbstractProject project, AbstractBuild upstreamBuild) { + final QueueEntry qentry = getQueueEntry(upstreamBuild); + if (qentry != null) { + final Queue.Item qItem = QueueUtil.getQueuedItem(project, qentry); + + if (qItem instanceof WaitingItem) { + return (WaitingItem) qItem; + } + } + return null; + } + + /** + * Retrieve a queued item with the given id from the build history + * + * @param project - the project to search queued items for + * @param queueId - the id of the queued item + * @return Item - the item from the queue for the given name + */ + private static Queue.Item getQueuedItem(AbstractProject project, int queueId) { + final List qitems = Jenkins.getInstance().getQueue().getItems(project); + + // Now search queued items for given queue id + for (Queue.Item qitem : qitems) { + if (qitem.id == queueId) { + LOGGER.fine("Found queued item for Project " + project.getDisplayName() + " and id " + queueId); + return qitem; + } + } + return null; + } +} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java new file mode 100644 index 00000000..836c8350 --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java @@ -0,0 +1,213 @@ +package au.com.centrumsystems.hudson.plugin.util; + +import hudson.model.ParameterValue; +import hudson.model.ParametersAction; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Logger; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import ch.srsx.timetable.model.IntervalParameterValue; + +/** + * Utitily class to help scheduling Jobs in the context of the build-pipeline + * plugin. + * + * @author rhirt + * + */ +public final class ScheduleUtil { + + /** A Logger object is used to log messages */ + private static final Logger LOGGER = Logger.getLogger(ScheduleUtil.class.getName()); + + /** The format used for specifying time */ + private static DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm"); + + /** Delimiter for time-range on one line */ + public static final String TIMES_DELIMITER = "-"; + + /** Define a comparator for intervals */ + public static class IntervalComparator implements Comparator, Serializable { + /** + * Serial ID + */ + private static final long serialVersionUID = -1905472266853377782L; + + @Override + public int compare(Interval o1, Interval o2) { + return o1.getStart().compareTo(o2.getStart()); + } + } + + /** + * Calculate a delay, depending on the windows defined for the job. + * + * @param action + * - the action + * @return int - the calculated delay for the job-execution + */ + public static int calcDelay(ParametersAction action) { + + // Check for instances of IntervalParameterValue on the job-definition + if (action == null || (!hasIntervalParameterValues(action))) { + LOGGER.info("No action or intervals present, delay 0"); + return 0; + } + + // Now, determine the deployment-windows on the job + // Derive it from the IntervalParameterValues: + final List intervals = getIntervals(action); + + // Check the intervals for a match + final DateTime now = new DateTime(); + final int nowOfDay = now.getMillisOfDay(); + for (Interval interval : intervals) { + // check for interval being after now + if (nowOfDay < interval.getStart().getMillisOfDay()) { + // the interval is after now, take the delay from there and + // return + final int delay = (interval.getStart().getMillisOfDay() - nowOfDay) / 1000; + LOGGER.info("Now before the next interval, delay is " + delay); + return delay; + } else { + // check for interval containing now + if (nowOfDay >= interval.getStart().getMillisOfDay() && nowOfDay < interval.getEnd().getMillisOfDay()) { + // now is inside an interval, no delay necessary + LOGGER.info("Now inside an interval, delay is 0"); + return 0; + } + } + } + + // We are after the last interval, but have at least one interval + // now take the start of the first interval and add a day to it, + // than take the time from then - now + final int delayNextDay = intervals.get(0).getStart().getMillisOfDay(); + final int delay = (int) (delayNextDay + 86400000 - nowOfDay) / 1000; + LOGGER.info("Now before the first interval next day, delay is " + delay); + return delay; + } + + /** + * Read the provided intervals from the action of the job + * + * @param action + * the action + * @return List - the list of the intervals read from the action, + * sorted ascending + */ + private static List getIntervals(ParametersAction action) { + final List intervals = new ArrayList(); + final List pvs = action.getParameters(); + for (ParameterValue pv : pvs) { + if (pv instanceof IntervalParameterValue) { + intervals.add(((IntervalParameterValue) pv).getValue()); + } + } + Collections.sort(intervals, new ScheduleUtil.IntervalComparator()); + return intervals; + } + + /** + * Parse a given time of the format HH:mm and convert it into a DateTime + * today + * + * @param value + * - the value to parse + * @return DateTime - the parsed time, moved into today + * */ + public static DateTime parseTime(String value) { + final DateTime today = new DateTime(); + try { + return fmt.parseDateTime(value.trim()).withDate(today.getYear(), today.getMonthOfYear(), today.getDayOfMonth()); + } catch (IllegalArgumentException iaex) { + LOGGER.warning("Time could not be parsed: " + value); + } + return null; + } + + /** + * Parse the given String to create an interval from a compact + * representation in the form (HH:mm - HH:mm) + * + * @param value + * - the value in the format HH:mm - HH:mm + * @return Interval - the interval parsed + */ + public static Interval parseInterval(String value) { + final String[] timeValues = value.split(TIMES_DELIMITER); + if (timeValues.length == 2) { + return new Interval(parseTime(timeValues[0]), parseTime(timeValues[1])); + } + return null; + } + + /** + * Format the start time of the given interval in the form (HH:mm) + * + * @param value + * - the interval from which the start-time will be formatted + * @return String - the formatted String + */ + public static String formatStartTime(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getStart()); + } + + /** + * Format the end time of the given interval in the form (HH:mm) + * + * @param value + * - the interval from which the end-time will be formatted + * @return String - the formatted String + */ + public static String formatEndTime(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getEnd()); + } + + /** + * Format the given interval for a compact representation in the form (HH:mm + * - HH:mm) + * + * @param value + * - the interval to be formatted + * @return String - the formatted String + */ + public static String formatInterval(Interval value) { + if (value == null) { + return ""; + } + return fmt.print(value.getStart()) + TIMES_DELIMITER + fmt.print(value.getEnd()); + } + + /** + * Check whether an IntervalParameterValue is present on the job + * + * @param action + * - the action + * @return boolean - whether IntervalParameterValue(s) are present + */ + private static boolean hasIntervalParameterValues(ParametersAction action) { + final List pvs = action.getParameters(); + for (ParameterValue pv : pvs) { + if (pv instanceof IntervalParameterValue) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java new file mode 100644 index 00000000..0b2b5b54 --- /dev/null +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java @@ -0,0 +1,220 @@ +package ch.srsx.timetable.model; + +import hudson.Extension; +import hudson.model.ParameterValue; +import hudson.model.ParameterDefinition; +import hudson.util.FormValidation; + +import java.util.Date; + +import net.sf.json.JSONObject; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.export.Exported; + +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; +import ch.srsx.timetable.Messages; + +/** + * @author rhirt + */ +public class IntervalParameterDefinition extends ParameterDefinition { + /** Serial ID */ + private static final long serialVersionUID = -8142303813745641667L; + + /** Default value */ + private Interval defaultValue; + + /** + * Constructor requesting name, startTime, endTime and description + * + * @param name + * the name + * @param startTime + * - string in the format HH:mm for the start of the interval + * @param endTime + * - string in the format HH:mm for the end of the interval + * @param description + * the description + */ + @DataBoundConstructor + public IntervalParameterDefinition(String name, String startTime, String endTime, String description) { + super(name, description); + this.defaultValue = new Interval(ScheduleUtil.parseTime(startTime), ScheduleUtil.parseTime(endTime)); + } + + /** + * Constructor requesting name, interval and description + * + * @param name + * the name + * @param value + * - interval to derive this interval from + * @param description + * the description + */ + public IntervalParameterDefinition(String name, Interval value, String description) { + super(name, description); + this.defaultValue = value; + } + + @Override + public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { + if (defaultValue instanceof IntervalParameterValue) { + final IntervalParameterValue value = (IntervalParameterValue) defaultValue; + return new IntervalParameterDefinition(getName(), value.getValue(), getDescription()); + } else { + return this; + } + } + + /** + * @return String - text representation of the time value at the start of + * the interval + */ + public String getFormattedInterval() { + return ScheduleUtil.formatInterval(defaultValue); + } + + /** + * @return Date - the start of the interval as a date + */ + @Exported + public Date getIntervalFrom() { + if (defaultValue == null) { + return null; + } + return new Date(defaultValue.getStartMillis()); + } + + /** + * @return String - text representation of the time value at the start of + * the interval + */ + public String getIntervalFromText() { + return ScheduleUtil.formatStartTime(defaultValue); + } + + /** + * @return Date - the end of the interval as a date + */ + @Exported + public Date getIntervalTo() { + if (defaultValue == null) { + return null; + } + return new Date(defaultValue.getEndMillis()); + } + + /** + * @return String - text representation of the time value at the end of the + * interval + */ + public String getIntervalToText() { + return ScheduleUtil.formatEndTime(defaultValue); + } + + @Override + public IntervalParameterValue getDefaultParameterValue() { + return new IntervalParameterValue(getName(), defaultValue, getDescription()); + } + + @Override + public ParameterValue createValue(StaplerRequest req) { + // TODO Auto-generated method stub + // FileItem src; + // try { + // src = req.getFileItem( getName() ); + // } catch (ServletException e) { + // // Not sure what to do here. We might want to raise this + // // but that would involve changing the throws for this call + // return null; + // } catch (IOException e) { + // // ditto above + // return null; + // } + // if ( src == null ) { + // // the requested file parameter wasn't uploaded + // return null; + // } + // FileParameterValue p = new FileParameterValue(getName(), src, + // getFileName(src.getName())); + // p.setDescription(getDescription()); + // p.setLocation(getName()); + // return p; + return null; + } + + @Override + public ParameterValue createValue(StaplerRequest req, JSONObject jo) { + final IntervalParameterValue value = req.bindJSON(IntervalParameterValue.class, jo); + value.setDescription(getDescription()); + return value; + } + + /** + * @param value + * the value + * @return the interval parameter value object created + */ + public IntervalParameterValue createValue(Interval value) { + return new IntervalParameterValue(getName(), value, getDescription()); + } + + /** + * DescriptorImpl for this parameter definition + * + * @author rhirt + * + */ + @Extension + public static class DescriptorImpl extends ParameterDescriptor { + @Override + public String getDisplayName() { + return Messages.IntervalParameterDefinition_DisplayName(); + } + + @Override + public String getHelpFile() { + return "/plugin/build-pipeline-plugin/help/parameter/interval.html"; + } + + /** + * Checks if parameterised build intervals are valid. + * + * @param value + * - the value to check + * @return FormValidation - status whether validation succeeded + */ + public FormValidation doCheckTime(@QueryParameter String value) { + if (ScheduleUtil.parseTime(value) == null) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); + } + return FormValidation.ok(); + } + + /** + * Checks if parameterised build intervals are valid. + * + * @param fromValue + * - the value (from) to check + * @param toValue + * - the value (to) to check + * @return FormValidation - status whether validation succeeded + */ + public FormValidation doCheckInterval(@QueryParameter String fromValue, @QueryParameter String toValue) { + final DateTime from = ScheduleUtil.parseTime(fromValue); + final DateTime to = ScheduleUtil.parseTime(toValue); + if (from == null || to == null) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); + } else if (!from.isBefore(to)) { + return FormValidation.error(Messages.IntervalParameterDefinition_IllegalInterval()); + } + return FormValidation.ok(); + } + } +} \ No newline at end of file diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java new file mode 100644 index 00000000..1993a643 --- /dev/null +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java @@ -0,0 +1,165 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Luca Domenico Milanesio, Tom Huybrechts + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package ch.srsx.timetable.model; + +import hudson.EnvVars; +import hudson.model.ParameterValue; +import hudson.model.AbstractBuild; +import hudson.model.Run; +import hudson.util.VariableResolver; + +import java.util.Locale; + +import org.joda.time.Interval; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.export.Exported; + +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + +/** + * {@link ParameterValue} created from {@link IntervalParameterDefinition}. + * + * @author rhirt + */ +public class IntervalParameterValue extends ParameterValue { + /** The serial id */ + private static final long serialVersionUID = 1L; + + /** the interval in question */ + @Exported(visibility = 4) + private final Interval value; + + /** + * Constructor taking name and value. + * + * @param name + * the name + * @param value + * the value, given as a formatted String (HH:mm - HH:mm) + */ + @DataBoundConstructor + public IntervalParameterValue(String name, String value) { + this(name, value, null); + } + + /** + * Constructor taking name, value and description. + * + * @param name + * the name + * @param value + * the value, given as a formatted String (HH:mm - HH:mm) + * @param description + * the description + */ + public IntervalParameterValue(String name, String value, String description) { + super(name, description); + this.value = ScheduleUtil.parseInterval(value); + } + + /** + * Constructor taking name, interval and description. + * + * @param name + * the name + * @param interval + * the value, given as an interval object + * @param description + * the description + */ + public IntervalParameterValue(String name, Interval interval, String description) { + super(name, description); + this.value = interval; + } + + /** + * Exposes the name/value as an environment variable. + * + * @param build + * - the run + * @param env + * - the environment + */ + @Override + public void buildEnvironment(Run build, EnvVars env) { + env.put(name, value.toString()); + env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); // backward + // compatibility + // pre + // 1.345 + } + + @Override + public VariableResolver createVariableResolver(AbstractBuild build) { + return new VariableResolver() { + public String resolve(String name) { + return IntervalParameterValue.this.name.equals(name) ? ScheduleUtil.formatInterval(value) : null; + } + }; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final IntervalParameterValue other = (IntervalParameterValue) obj; + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "(IntervalParameterValue) " + getName() + "='" + value.toString() + "'"; + } + + @Override + public String getShortDescription() { + return name + '=' + ScheduleUtil.formatInterval(value); + } + + public Interval getValue() { + return value; + } +} diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 4b13d6f5..449960f6 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -38,17 +38,28 @@ -
- {{build.startDate}} {{build.startTime}} -
+ {{#if build.isQueued}} +
+ {{build.queuedForDate}} {{build.queuedForTime}} +
+ {{else}} +
+ {{build.startDate}} {{build.startTime}} +
+ {{/if}}
+ {{#if build.isQueued}} + + {{build.queueWaitDuration}} + + {{/if}} {{#if build.isComplete}} - + {{build.duration}} {{/if}} {{#if build.isBuilding}} - + {{build.duration}} {{/if}} @@ -77,33 +88,38 @@
+ {{#if build.isManualTrigger}} + This job is manually triggered + {{/if}} {{#if project.parameters.length}} This job has parameters {{/if}} {{#unless build.isPending}} {{#unless build.isReadyToBeManuallyBuilt}} - - - - - console - - - - - console - - - - - console - - - + {{#unless build.isQueued}} + + + + + console + + + + + console + + + + + console + + + + {{/unless}} {{/unless}} {{/unless}} - + {{#if build.isRerunnable}} {{#if build.isSuccess}} @@ -143,6 +159,11 @@ {{/if}} {{/if}} + {{#if build.isQueued}} + + cancel this build + + {{/if}}
{{/unless}} {{#if project.disabled}} @@ -228,20 +249,28 @@
- -
+ +
Trigger a Pipeline
-
- + + - +
Run
+ +
+
+ M2Release in a Pipeline +
+
Release
+
+
@@ -300,66 +329,103 @@ - - + + - + + + + - - - - + + + + + + + + + + + - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -386,7 +452,7 @@
- + @@ -415,12 +481,31 @@
${buildParam.getKey()}: ${buildParam.getValue()}
-
- + + +
+ + + + + + + + - + + + + + + + + + + + - - + - - - - + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/srsx/timetable/Messages.properties b/src/main/resources/ch/srsx/timetable/Messages.properties new file mode 100644 index 00000000..7073c54d --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/Messages.properties @@ -0,0 +1,4 @@ +IntervalParameterDefinition.DisplayName=Interval Parameter +IntervalParameterDefinition.IllegalInterval=Illegal Interval. +IntervalParameterDefinition.IllegalTime=Illegal Time-Format (should be HH:mm). + diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly new file mode 100644 index 00000000..2badb0d2 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties new file mode 100644 index 00000000..b218a253 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties @@ -0,0 +1,4 @@ +Name=Name +Interval\ From=Interval Startzeit +Interval\ To=Interval Endzeit +Description=Beschreibung diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html new file mode 100644 index 00000000..d4f5f8b6 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html @@ -0,0 +1,3 @@ +
+ The start-time of the interval to be used for allowed job-execution. +
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html new file mode 100644 index 00000000..d4f5f8b6 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html @@ -0,0 +1,3 @@ +
+ The start-time of the interval to be used for allowed job-execution. +
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly new file mode 100644 index 00000000..daddbb86 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly @@ -0,0 +1,35 @@ + + + + + +
+ + +
+
+
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly new file mode 100644 index 00000000..1a8e24f0 --- /dev/null +++ b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 9e27519e..a7e3982a 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -61,7 +61,7 @@ height: 48px; margin-left: auto; margin-right: auto; - width: 340px; + width: 392px; } .icon-container { @@ -132,6 +132,11 @@ } .build-duration-container { + text-align: left; + text-overflow: clip; + overflow: hidden; + white-space: nowrap; + font-size: 0.9em; width: 135px; } @@ -285,6 +290,11 @@ tr.spacerRow td { color: #222; } +.QUEUED { + background-color: #eb642d; + color: #222; +} + .BUILDING { background-color: #f7f94b; color: #222; @@ -372,3 +382,132 @@ a:hover { .hidden { display: none; } + +.tile { + background: url(../images/tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileCrossBottom { + background: url(../images/tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileCross { + background: url(../images/tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileDown { + background: url(../images/tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileLeft { + background: url(../images/tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileRight { + background: url(../images/tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.tileTopRight { + background: url(../images/tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} + +.hTile { + background: url(../images/h-tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileCrossBottom { + background: url(../images/h-tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileCross { + background: url(../images/h-tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileDown { + background: url(../images/h-tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileLeft { + background: url(../images/h-tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileRight { + background: url(../images/h-tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.hTileTopRight { + background: url(../images/h-tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} + +.rTile { + background: url(../images/r-tile.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileCrossBottom { + background: url(../images/r-tile-cross-bottom.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileCross { + background: url(../images/r-tile-cross.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileDown { + background: url(../images/r-tile-down.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileLeft { + background: url(../images/r-tile-left.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileRight { + background: url(../images/r-tile-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} +.rTileTopRight { + background: url(../images/r-tile-top-right.png); + background-size: 100% 100%; + background-repeat: no-repeat; + vertical-align: middle; +} diff --git a/src/main/webapp/help/parameter/interval.html b/src/main/webapp/help/parameter/interval.html new file mode 100644 index 00000000..7f4c7b9f --- /dev/null +++ b/src/main/webapp/help/parameter/interval.html @@ -0,0 +1,4 @@ +
+ Defines an interval parameter, where users can enter a start time and an end time, + which you can use to identify times when job execution is allowed. +
\ No newline at end of file diff --git a/src/main/webapp/images/h-tile-cross-bottom.png b/src/main/webapp/images/h-tile-cross-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..c09666e727ed14d85ac1d79947955da0238bc8c9 GIT binary patch literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH*D1Jf!`7srr_xVLu$^Hw=XI3ylC!n^bT4yNl# zj}~_ZxzCs)wZLN0w*Qv#MT@s@ZLUgTtJrw!ZP{6`hga{eZ8A1#S*Xavo0!4j>?|R5 z=z>67n*sfVLfQ{6j9h>HZvE+4z3I1eW9`4+jLe!7^#0zzpEdjLPBY1vl@=O$HMi$W z{^pw{`+sWMF0+#BuX``*FxO7>yvO`$YwGLQeKG%jeqqX-lowlhdgu4%E-(3AfAqrV z^~>*d?Av(!YyGr$_P^A6dv{)$ZEJSz%InCg->UDn+kgMrvv=KXCzJFEJN5THY31$R zeZ?aA*s=4;Q*!E0D4$>XzV3JN{PQ;++q{AX$6v>#ufHpQnw9&4Kk8LOzSlnk$zQKz ztFJBJtMcV#$*Xs|$tORyEm~&#`fBy{D?irXD3de3-}dj-|F>ng@6Jx&f4}_uqrz8h z9P+a^{5vOT_ht36D;$%-d)jUDS7elikz%fNqttoUQNa?#|SYylfMc&pA&e%?%|Oep!4(ma{n8zzhr%7eYx$y1Aphm RtAR!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?cI&M%?2V4j%WT^Zxm*Bn8I<& zLo-OFuDntU0%LPFvcMiHT48PTWmkDYRmfbjSIdklW zKErkXX=x=0N9^H!d&_kC_Jy&#>yDd8N!+b}FmK)Q!adh+FSkEXJb(Fr0)wZkpUXO@geCy0SD-fl literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-down.png b/src/main/webapp/images/h-tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..88fe3a2fd0d541e05a31c226111925e5037ed91a GIT binary patch literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hU$6V@O5Z+dI~IEd~s1i5$}`_`a{VV0`Yl z$bhpe+;1YswlnshO+Q{!wohVtyldTafvm`B3#VMFyL)@v+TKq!TlP=9*EvVeYRj${ z7oJP**50dYRkC(#uZV2btYv1Cs!dqbzF73-9%&;^0EHUpEEg^E19 zi5VQu#0Yh~xUkRnrfd7R+*3fC@`KCXT?8A#qPFF|yYr8_Edp?}l&rS!n*!bPd*Akn zi=5AYzT?)#_xAE?zG8{bTq1|G0~B$%AfDsN#zfzX=C`oLtt N;OXk;vd$@?2>@=z_&@*v literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-left.png b/src/main/webapp/images/h-tile-left.png new file mode 100644 index 0000000000000000000000000000000000000000..08a0b263316f9001a5ed1a69a17a496819558a56 GIT binary patch literal 476 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1|(&F^?3p*#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0rOCF{C2y?Onxn%?=E%fh!y~{(rlH@!CZx znTong8yc3anqS>F)28;QN^)Avw5KvVwjM7nN>*a;kWnaVIKfC?M$hYA^FG_ePxhBR zZusVR-P-$RdAG~_E|>V)eXX%;zy7*(?fq{8KmOf0iR!KoeKviMs|)<+neP9xex7M< sd42u6CkzvrUHAkn9q7uqE!}3nZoaapp*V2m($y85}Sb4q9e02OPUkpKVy literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile-right.png b/src/main/webapp/images/h-tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c03f7b42e9dd82f5dc0a89728ca16da023390482 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA}&+iyz!UFFtubAIjp^#@=7y{E6QUeasT^SQ>q z=9NWXdTZSE(wy66K#q<7>fH&m)D~*D^a)rwz2I1=3}H;)z3zYY4B?CekNp!lvI6;>1s;*b3=DjSK$uZf!>fUTfr-V_#WAEJ?(N;|ye0<`hr~GTLx0;vH>fP= zo4c|l+|{H#~pz@{>%D^DcdQB^h@m zr4C&XXxpbW=QMBaR-MFrn>q=wNSlF4%RukTv)Qu$=#_vj$ibXphio- zLu&$x0-z;4yr_E6gckmnz4Wx`WbyMepJPnVW^enOIVbP0l+XkKCiwmg literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/h-tile.png b/src/main/webapp/images/h-tile.png new file mode 100644 index 0000000000000000000000000000000000000000..64a3d1d384fbb9f0189ad24095daad968cc40abb GIT binary patch literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1SBWTkdy;bjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4cwkCjv*CsZ!Z`!GBEHQUhvaAG`skqs7gWi frLC7o6x?KMmB*zw+b=N&7*-6Pu6{1-oD!MPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rg2lK~y+Tos-{F z5>XV#`OEq%>JO-%ee9VT2p>Y-vCYsWHAT&78x{r#h?uojhDrG)phJKyl(aSptMR}C z58w9O3mc?OO%Irhn{vMjUMXf#0Yf3KZ-AgFLrT!L@q z8I))e>C6F((&{?AbMw$N%__S=FdPn{C<<1r!s;gjr{_PeQSbC1hhp&i{TK`eR@;Pt zEjJWJ^ZW`@qRgN?cBo@9oX4w7ojnt3Hjh9c@JnDA2E?b!sGW6jUr6BAgB`}5*|41x z@_ro+8zQU3m&pK zvz)ebij~hjMoIq;x7*DHl}ZKvjct_My?;iIyg@3J;sS#1d{oV%)b9Qjt=xpq=L6kd zTR?+CFT-#5QEYYpNHSA^D2kxNr#@RijOSrVT0=0ghx9=e1^ohQzJ^drgY8z$};rI!!lvI6;>1s;*b3=DjSK$uZf!>a+PVV|dqV@O5Z+q<^ew*w>^0yPV7AKBu(@c;kg zDd%P>Mla`E_+a7D(%Jmi{X|YYdGp+|fJ9sZ#zWiJAx$~>X edmqnQ_P->>BsND}{68>y89ZJ6T-G@yGywpY8rk&# literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-cross.png b/src/main/webapp/images/r-tile-cross.png new file mode 100644 index 0000000000000000000000000000000000000000..6e268563430bf92b98b8d6504818b902670302eb GIT binary patch literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0Q69F{C2y?cKG0M;s&^F7k15KKwhMW3O1F zv$6n(QF57GR>A*#^23%s0V}5$91E4{%ZOM%{rlgS zKR?!_E}H-Ter|1D&D-nU>#u$6U6>cV{lw?U#meU{TsM6C?9j}$3Fl7 literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-down.png b/src/main/webapp/images/r-tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb5c59052e312c6b5c711603a158302cebd5987 GIT binary patch literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0`gFF{C2y?G;5oXGal-i!c2-0@@F4_}{wV zCWBaTr?$YPX<~H+0ruZzJ{wJbo|k6k-+%tN;mX@@zwW5tA$@vNWd6S*ncdT`f86#z z;{4~xYnDrAzrCvYsp`k|PkXI5uXO&u!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~ln2F{C2y?cI%D%#I=s7w@Vx{rDfBGWXh{ zbElkIM7%f%=EMNzrxSYsw=m*Pp7NfVFC8(%e1HLyRIt#)atDl1x5~or>mdK II;Vst0L=5LI{*Lx literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-right.png b/src/main/webapp/images/r-tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..495819724f8414097fdccd0c16a004b7eff5956e GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA>Y%*F{C2y?OjJNW=8>si+z%YoP~-N{}1mt z$66#PDC5cUoZ;ZzGxhwlmR*yt3Y>i})^6SQgU@Vr&85{#S}q7WxpT-W(v@*!)jnsd zd2z=ce%a$=^Xm2I>ez+%@15WOymV&86`TJRySSHS>X)>lx@>P{{ci*93k_G-I{dWv jFCP$hw_M;%zQed+n@r2UAL(y_@xkEf>gTe~DWM4f^j)5T literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/r-tile-top-right.png b/src/main/webapp/images/r-tile-top-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e5a7c24f4c0eed81713b2349514c71abb563b5 GIT binary patch literal 569 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hd+7V@O5Z+q)b6L;^(`9-eMGCDh~L#KZaU z@BG8J#5Zi+xFz~xtV63KXXkH*@4xq_6{%hRJ2UvsGr5{yKYrHvWYvC3tJzUyAGW&r z>Dm8x`q#(r>wQ04{fdz#-B!lvI6;>1s;*b3=DjSK$uZf!>a+PVY#P^V@O5Z+q-+cjyOm-TugBcu)e7NVQnqz z{KL1-UAyFU^IZI$9q*Qjz25m;gmZG{O z*vilA$&h8X|04S0{k~_L|G&+xye+qm%e`a=lWgUe|Ic1k-HM4@zk2`MIJxC_KR@0j z)o)CHkAXrn>BWWp$qm!)Uf=cWw9y-lC5tnEF}5x@!lvI6;>1s;*b3=DjSK$uZf!>a+Pp}^C{F{C2y?Oj7JXG4aDhf5SZw#(huuHZ{I zUw>h1bf#$Kv|oM`|Nm}lN%^bTs5A3>|MuJsdk))ewpgg#(kEc$^n!!FjA!4U|2FGi zUVE}@+w0Ub_dj1Zi;MfNJMBwxwai_m_3tmFVdQ&MBb@0AzKl6aWAK literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-down.png b/src/main/webapp/images/tile-down.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd1c8e3e66dc9f53026c5479e05b77d4e773b3d GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~=(5F{C2y?G-~W7DoYxi|<&4jg#L$sNLJY zary^_*}5kdE#_l(TUYu2`=mv)PT75_sjs|uY|*p7hb?UFdMkHX$Nk&X+rRz##@KVm z_szR(IQ@%o)w%yl^|Ooj$KJfOu>A9k?K;1g|BbRYv0kX$(kEc$^nzn#Gp6zFjXjtA ldym|0-P?7$*BS0(Fx@TYxi~g!lvI6;>1s;*b3=DjSK$uZf!>a+Pq1@BOF{C2y?cI%D&V~{V7iT*;-l?gHUwHoE zn`gH!o_wUHaYOscZ-%2MpINg{`f&cfz==s8e}37a=aajAu8+?XhKbBBd;*pZbY(Pr z{8|%n{O@72qP1Ti|J{^#@JC&330~jw1p00i_>zopr0P*>zC;$Ke literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-right.png b/src/main/webapp/images/tile-right.png new file mode 100644 index 0000000000000000000000000000000000000000..59e18e64648106b66693ba866eef23d7de9daff8 GIT binary patch literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?Oj8zLk{vXk}v(^6ma2m5TE{rsyD@0xoS_t(t1An4@IA*)!@LSIJrFTTvp_BDL_ zXTIB>+A8P&Jke&}bMg3U+n#qVZ~b0yEmUsl6R>i6!2xDey?s_XLp0;Sqp$^k&c8sC ffUBSfBT?eso(^gIj9W#ZSYYsU^>bP0l+XkKhG(GI literal 0 HcmV?d00001 diff --git a/src/main/webapp/images/tile-top-right.png b/src/main/webapp/images/tile-top-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e7580c793f9a26808f3b699da60e1ea411f55e05 GIT binary patch literal 652 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>fUTfl1NR#WAEJ?(JP)ze5f(4j*SKbrzcShZY47uD}PF{6;_iJmes41MeYxeCayHjTu z`*_Yi-X1dVTCUZ+b$i>cw4eJK@j4ef|P{Rp$h_SZ3ZSS z3l({I6EirRooOc|XjYHdk$()U+7Q{c3`x`EMb8`94R|)UzZmk#BE`G52UG9q5JI$w-FFOA> u;E#Bsvpe_K=tSr2`E&>|)Ch6%ntw;gzy4>B6)*uYFnGH9xvX Date: Wed, 20 Aug 2014 21:21:33 +0200 Subject: [PATCH 04/21] update queueing --- pom.xml | 2 +- .../buildpipeline/BuildJSONBuilder.groovy | 4 +- .../plugin/buildpipeline/BuildForm.java | 31 ++++- .../buildpipeline/BuildPipelineView.java | 125 +++++++----------- .../buildpipeline/DefaultProjectGridImpl.java | 1 - .../DownstreamProjectGridBuilder.java | 24 ++-- .../plugin/buildpipeline/PipelineBuild.java | 33 +++-- .../plugin/buildpipeline/ProjectForm.java | 117 ++++++++++------ .../dashboard/BuildPipelineDashboard.java | 2 +- .../dashboard/ReadOnlyBuildPipelineView.java | 6 +- .../trigger/BuildPipelineTrigger.java | 18 ++- .../trigger/DownstreamDependency.java | 22 ++- .../hudson/plugin/util/BuildUtil.java | 10 ++ .../hudson/plugin/util/QueueEntry.java | 9 ++ .../hudson/plugin/util/QueueUtil.java | 61 ++++++--- .../model/IntervalParameterDefinition.java | 21 --- .../model/IntervalParameterValue.java | 17 +-- .../buildpipeline/BuildPipelineView/bpp.jelly | 22 ++- .../BuildPipelineView/configure-entries.jelly | 4 - src/main/webapp/css/main.css | 8 +- src/main/webapp/js/build-pipeline.js | 11 +- .../plugin/buildpipeline/BuildFormTest.java | 16 +++ .../BuildPipelineViewConstructorTest.java | 30 ++--- .../buildpipeline/BuildPipelineViewTest.java | 57 +++----- .../DownstreamProjectGridBuilderTest.java | 2 +- .../plugin/buildpipeline/ProjectFormTest.java | 16 +++ .../PipelineWebDriverTestBase.java | 2 +- .../trigger/BuildPipelineTriggerTest.java | 2 +- 28 files changed, 371 insertions(+), 302 deletions(-) diff --git a/pom.xml b/pom.xml index 858118b6..f4af1581 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 1.509.3 + 1.554.2 build-pipeline-plugin 1.4.4-SNAPSHOT diff --git a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy index 4ea856cc..ff9fe1c9 100644 --- a/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy +++ b/src/main/groovy/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildJSONBuilder.groovy @@ -46,14 +46,14 @@ class BuildJSONBuilder { } project { disabled(pipelineBuild.projectDisabled) - name(pipelineBuild.project.getRelativeNameFrom(context)) + name(pipelineBuild.project.getRelativeNameFromGroup(context)) url(pipelineBuild.projectURL) health(pipelineBuild.projectHealth) id(projectId) parameters(params) } upstream { - projectName(pipelineBuild.upstreamPipelineBuild?.project?.getRelativeNameFrom(context)) + projectName(pipelineBuild.upstreamPipelineBuild?.project?.getRelativeNameFromGroup(context)) buildNumber(pipelineBuild.upstreamBuild?.number) } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 752c3f63..a5caa8cd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -6,7 +6,10 @@ import hudson.model.ParametersDefinitionProperty; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -74,12 +77,27 @@ public class BuildForm { * item group pipeline view belongs to, used to compute relative item names */ public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { + this(context, pipelineBuild, new LinkedHashSet>(Arrays.asList(pipelineBuild.getProject()))); + } + + /** + * @param pipelineBuild + * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names + * @param parentPath + * already traversed projects + */ + private BuildForm(ItemGroup context, final PipelineBuild pipelineBuild, final Collection> parentPath) { this.context = context; this.pipelineBuild = pipelineBuild; status = pipelineBuild.getCurrentBuildResult(); dependencies = new ArrayList(); for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { - dependencies.add(new BuildForm(context, downstream)); + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(downstream.getProject())) { + dependencies.add(new BuildForm(context, downstream, forkedPath)); + } } id = hashCode(); final AbstractProject project = pipelineBuild.getProject(); @@ -164,8 +182,6 @@ public boolean cancelQueued(final int queueId) { if (upstreamBuild != null) { final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); if (qentry.getQueueId() == queueId) { - QueueUtil.removeQueueEntry(qentry); - LOGGER.info("Removed queue entry from map: " + qentry.toString()); updated = true; } } @@ -189,8 +205,14 @@ public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); } + + /** + * @return Map the map with the parameters, enriched with a version parameter. + */ public Map getParameters() { - return pipelineBuild.getBuildParameters(); + final Map params = new HashMap(); + params.putAll(pipelineBuild.getBuildParameters()); + return params; } public Map getFilteredParameters() { @@ -226,5 +248,4 @@ private Map filterSensitiveBuildVariables(AbstractBuild bu } return resultVars; } - } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 7c0c8b8c..3136074d 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -24,12 +24,15 @@ */ package au.com.centrumsystems.hudson.plugin.buildpipeline; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + +import com.google.common.collect.Sets; import hudson.Extension; import hudson.model.Action; import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.ParameterValue; -import hudson.model.Queue; import hudson.model.TaskListener; import hudson.model.TopLevelItem; import hudson.model.AbstractBuild; @@ -53,6 +56,7 @@ import hudson.util.DescribableList; import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; import java.io.IOException; import java.net.URISyntaxException; @@ -61,15 +65,11 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; -import jenkins.model.Jenkins; - import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -79,14 +79,8 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; - /** * This view displays the set of jobs that are related * based on their upstream\downstream relationships as a pipeline. Each @@ -131,10 +125,13 @@ public class BuildPipelineView extends View { /** showPipelineParametersInHeaders */ private boolean showPipelineParametersInHeaders; - + /** - * informs if the first job has parameters + * @deprecated + * + * Don't need an input from UI store this */ + @Deprecated private boolean startsWithParameters; /** @@ -237,20 +234,16 @@ public void print(final TaskListener listener) { * Indicates whether only the latest job will be triggered. * @param cssUrl * URL for the custom CSS file. - * @param startsWithParameters - * Indicates whether the first job of the pipeline takes - * parameters */ public BuildPipelineView(final String name, final String buildViewTitle, - final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { - super(name, Jenkins.getInstance()); + final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final String cssUrl) { + super(name, Hudson.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; this.noOfDisplayedBuilds = noOfDisplayedBuilds; this.triggerOnlyLatestJob = triggerOnlyLatestJob; this.cssUrl = cssUrl; - this.startsWithParameters = startsWithParameters; } /** @@ -280,21 +273,18 @@ public BuildPipelineView(final String name, final String buildViewTitle, * URL for the custom CSS file. * @param selectedJob * the first job name in the pipeline. it can be set to null when gridBuilder is passed. - * @param startsWithParameters - * indicates whether the first job of the pipeline takes parameters */ @DataBoundConstructor public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, - final int refreshFrequency, final String cssUrl, final String selectedJob, final boolean startsWithParameters) { - this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl, startsWithParameters); + final int refreshFrequency, final String cssUrl, final String selectedJob) { + this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; this.showPipelineParameters = showPipelineParameters; this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; - this.startsWithParameters = startsWithParameters; this.selectedJob = selectedJob; //not exactly understanding the lifecycle here, but I want a default of 3 //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) @@ -471,28 +461,35 @@ public int retryBuild(final String triggerProjectName) { } /** - * @param externalizableId - * the externalizableId - * @return the number of re-run build + * Trigger a manual build + * + * @param buildNumber + * the number of the build of the current project + * @param upstreamBuildNumber + * upstream build number + * @param triggerProjectName + * project that is triggered + * @param upstreamProjectName + * upstream project + * @return next build number that has been scheduled */ @JavaScriptMethod - public int rerunBuild(final String externalizableId) { - LOGGER.fine("Running build again: " + externalizableId); //$NON-NLS-1$ - final AbstractBuild triggerBuild = (AbstractBuild) Run.fromExternalizableId(externalizableId); - final AbstractProject triggerProject = triggerBuild.getProject(); - final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), - removeUserIdCauseActions(triggerBuild.getActions())); - - AbstractBuild result = triggerBuild; - try { - result = (AbstractBuild) future.get(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } catch (final ExecutionException e) { - e.printStackTrace(); - } - - return result.getNumber(); + public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, + final String upstreamProjectName) { + LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + + // Retrieve the current build and then clear the upstream cause for the current link + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + + LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + + return nextBuildNumber; } /** @@ -585,8 +582,8 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr try { final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); if (action instanceof ParametersAction) { - // TODO exchange base and overlay (last param wins) ! - parametersAction = mergeParameters(parametersAction, (ParametersAction) action); + // Exchange base and overlay (last param wins) ! + parametersAction = mergeParameters((ParametersAction) action, parametersAction); } else { buildActions.add(action); } @@ -601,19 +598,7 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr buildActions.add(parametersAction); - // When triggering here, no queued job for the current upstream-build should be in the queueEntry list -> clear it - // Save the highest queue-id for later comparison - final List oldItems = QueueUtil.getQueuedItemsFor(triggerProject); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); - - // Here, the queued item is already available -> save the link from - // the upstream build to that queue entry for later retrieval - final int newId = QueueUtil.getNewQueuedItemId(triggerProject, oldItems); - if (newId > 0) { - QueueUtil.addQueueEntry(new QueueEntry(upstreamBuild, newId)); - } - return triggerProject.getNextBuildNumber(); } @@ -624,7 +609,7 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr * @return the trigger config relative to the given downstream project */ private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, - final AbstractBuild upstreamBuild) { + final AbstractBuild upstreamBuild) { final DescribableList> upstreamProjectPublishersList = upstreamBuild.getProject().getPublishersList(); @@ -633,7 +618,7 @@ private List retrieveUpstreamProjectTriggerConfig(final final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); if (manualTrigger != null) { final Set downstreamProjectsNames = - Sets.newHashSet(Splitter.on(",").split(manualTrigger.getDownstreamProjectNames())); + Sets.newHashSet(Splitter.on(",").trimResults().split(manualTrigger.getDownstreamProjectNames())); if (downstreamProjectsNames.contains(project.getName())) { configs = manualTrigger.getConfigs(); } @@ -642,7 +627,7 @@ private List retrieveUpstreamProjectTriggerConfig(final final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); if (autoTrigger != null) { for (BuildTriggerConfig config : autoTrigger.getConfigs()) { - final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").split(config.getProjects())); + final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").trimResults().split(config.getProjects())); if (downstreamProjectsNames.contains(project.getName())) { configs = config.getConfigs(); } @@ -851,18 +836,6 @@ public String getShowPipelineParameters() { public void setShowPipelineParameters(final boolean showPipelineParameters) { this.showPipelineParameters = showPipelineParameters; } - - public boolean isStartsWithParameters() { - return startsWithParameters && gridBuilder.startsWithParameters(this); - } - - public String getStartsWithParameters() { - return Boolean.toString(startsWithParameters); - } - - public void setStartsWithParameters(final boolean startsWithParameters) { - this.startsWithParameters = startsWithParameters; - } public boolean isShowPipelineParametersInHeaders() { return showPipelineParametersInHeaders; @@ -992,8 +965,8 @@ public boolean isM2Release() { return false; } final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); - final AbstractProject project = jenkins != null ? jenkins.getItem(firstJobName, this.getOwnerItemGroup(), - AbstractProject.class) : null; + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class); if (project != null) { // If a M2ReleaseAction is found final List actions = project.getActions(M2ReleaseAction.class); diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java index 8da51701..41592292 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DefaultProjectGridImpl.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java index f037e65c..ee54007d 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilder.java @@ -10,12 +10,6 @@ import hudson.util.HttpResponses; import hudson.util.ListBoxModel; -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; - -import javax.servlet.ServletException; - import jenkins.model.Jenkins; import jenkins.util.TimeDuration; @@ -26,6 +20,14 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; +import javax.servlet.ServletException; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + +import org.acegisecurity.AccessDeniedException; + /** * {@link ProjectGridBuilder} based on the upstream/downstream relationship. * @@ -62,7 +64,7 @@ private static final class GridImpl extends DefaultProjectGridImpl { /** * @param context * item group pipeline view belongs to, used to compute relative item names - * @param start The first project to lead the pipeline. + * @param start The first project to lead the pipeline. */ private GridImpl(ItemGroup context, AbstractProject start) { this.context = context; @@ -161,7 +163,11 @@ public String getFirstJob() { * @return possibly null */ public AbstractProject getFirstJob(BuildPipelineView owner) { - return Jenkins.getInstance().getItem(firstJob, owner.getOwnerItemGroup(), AbstractProject.class); + try { + return Jenkins.getInstance().getItem(firstJob, owner.getOwnerItemGroup(), AbstractProject.class); + } catch (final AccessDeniedException ex) { + return null; + } } @Override @@ -169,7 +175,7 @@ public boolean hasBuildPermission(BuildPipelineView owner) { final AbstractProject job = getFirstJob(owner); return job != null && job.hasPermission(Item.BUILD); } - + @Override public boolean startsWithParameters(BuildPipelineView owner) { final AbstractProject firstJob = this.getFirstJob(owner); diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 859d14d6..c5bb7b20 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -30,6 +30,7 @@ import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; import hudson.model.Queue.WaitingItem; +import hudson.model.Result; import java.text.DateFormat; import java.util.ArrayList; @@ -260,7 +261,11 @@ private String getBuildResult(final AbstractBuild build) { if (build.isBuilding()) { buildResult = HudsonResult.BUILDING.toString(); } else { - buildResult = HudsonResult.values()[build.getResult().ordinal].toString(); + final Result result = build.getResult(); + if (result == null) { + throw new IllegalStateException("Build with a null result after build has finished"); + } + buildResult = HudsonResult.values()[result.ordinal].toString(); } } else { // Otherwise determine its pending status @@ -441,7 +446,7 @@ public String getPipelineVersion() { final String displayName = currentBuild.getDisplayName(); if (displayName == null || displayName.trim().length() == 0) { version = currentBuild.getNumber() > 0 ? String.valueOf(currentBuild.getNumber()) : Strings - .getString("PipelineBuild.RevisionNotAvailable"); + .getString("PipelineBuild.RevisionNotAvailable"); } else { version = displayName; } @@ -475,13 +480,15 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return isManualTrigger() && this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) + return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } public boolean isRerunnable() { return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) - && !"QUEUED".equals(getCurrentBuildResult()) && hasBuildPermission(); + && !"QUEUED".equals(getCurrentBuildResult()) + && hasBuildPermission() && getUpstreamPipelineBuild().getCurrentBuild() != null + && QueueUtil.getQueueEntry(currentBuild) == null; } /** @@ -503,12 +510,13 @@ private boolean upstreamBuildUnstable() { * @return whether this is the latest build of this pipeline view */ public boolean isLatestBuild() { - if (currentBuild == null || project.getLastBuild() == null) { + final AbstractBuild lastBuild = project.getLastBuild(); + if (currentBuild == null || lastBuild == null) { return false; } // now check for last build on this pipeline view // how do we find out, if the last build is run on this view? - return currentBuild.getNumber() == project.getLastBuild().getNumber(); + return currentBuild.getNumber() == lastBuild.getNumber(); } /** @@ -516,11 +524,18 @@ public boolean isLatestBuild() { * @return whether the upstream build is the latest for this pipeline view */ public boolean isUpstreamBuildLatest() { - if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null - || getUpstreamPipelineBuild().getProject().getLastBuild() == null) { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null) { + return false; + } + final AbstractProject upstreamPBProject = getUpstreamPipelineBuild().getProject(); + if (upstreamPBProject == null || upstreamPBProject.getLastBuild() == null) { + return false; + } + final AbstractBuild lastBuild = upstreamPBProject.getLastBuild(); + if (lastBuild == null) { return false; } - return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastBuild().getNumber(); + return upstreamBuild.getNumber() == lastBuild.getNumber(); } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 7b67dd2a..ebe80975 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -6,7 +6,10 @@ import hudson.model.AbstractProject; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -17,9 +20,9 @@ /** * @author Centrum Systems - * + * * Representation of a set of projects - * + * */ public class ProjectForm { /** @@ -57,6 +60,11 @@ public class ProjectForm { */ private Map lastSuccessfulBuildParams; + /** + * Whether last successful build is already handled + */ + private boolean lastSuccessfulHandled; + /** * keep reference to the project so that we can update it */ @@ -99,18 +107,20 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { - this(project, null); + this(project, new LinkedHashSet>(Arrays.asList(project)), project); } /** * @param project * - the project to wrap + * @param parentPath + * already traversed projects * @param firstProject * - the first project associated with the grid for this * projectform */ - public ProjectForm(final AbstractProject project, final AbstractProject firstProject) { - + public ProjectForm(final AbstractProject project, final Collection> parentPath, + final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); name = pipelineBuild.getProject().getFullName(); @@ -119,16 +129,23 @@ public ProjectForm(final AbstractProject project, final AbstractProject(); for (final AbstractProject dependency : project.getDownstreamProjects()) { - dependencies.add(new ProjectForm(dependency, firstProject)); + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(dependency)) { + dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); + } } if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { - final ProjectForm candidate = new ProjectForm(dependency, firstProject); - // if subprojects come back as downstreams someday, no duplicates wanted - if (!dependencies.contains(candidate)) { - dependencies.add(candidate); + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(dependency)) { + final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); + // if subprojects come back as downstreams someday, + // no duplicates wanted + if (!dependencies.contains(candidate)) { + dependencies.add(candidate); + } } } } @@ -151,12 +168,12 @@ public ProjectForm(final AbstractProject project, final AbstractProject p) { - return p != null ? new ProjectForm(p, p) : null; + return p != null ? new ProjectForm(p) : null; } public String getName() { @@ -204,18 +221,30 @@ public Map getLastSuccessfulBuildParams() { return lastSuccessfulBuildParams; } + public boolean isLastSuccessfulHandled() { + return lastSuccessfulHandled; + } + + public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { + this.lastSuccessfulHandled = lastSuccessfulHandled; + } + public List getDependencies() { return dependencies; } /** - * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Gets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -223,15 +252,20 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Sets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @param display - * - boolean to indicate whether the trigger button should be shown + * - boolean to indicate whether the trigger button should be + * shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -273,12 +307,12 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod public String asJSON() { - return ProjectJSONBuilder.asJSON(this); + return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } /** @@ -302,21 +336,24 @@ public Map filterSensitiveBuildVariables(AbstractBuild bui * - the build-grids present on this view */ public void correctLastSuccessfulBuilds(Iterable buildGrids) { - AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); - - outer: while (lastSuccessfulBuild != null) { - // Search the buildGrid for the lastSuccessfulBuild given above - BuildForm buildForm = null; - for (BuildGrid buildGrid : buildGrids) { - buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); - if (buildForm != null) { - break outer; + if (!isLastSuccessfulHandled()) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + // Determine whether the lastSuccessful Build belongs to this View + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); } - lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); + setLastSuccessfulHandled(true); } - // Set the (may be) newly found last successful build - handleLastSuccessfulBuild(lastSuccessfulBuild); } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/BuildPipelineDashboard.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/BuildPipelineDashboard.java index bfbc0727..b91fde9f 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/BuildPipelineDashboard.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/BuildPipelineDashboard.java @@ -136,7 +136,7 @@ public String getCssUrl() { public BuildPipelineView getBuildPipelineView() { return new ReadOnlyBuildPipelineView(getDisplayName(), getDescription(), getGridBuilder(), - getNoOfDisplayedBuilds(), false, getCssUrl(), false); + getNoOfDisplayedBuilds(), false, getCssUrl()); } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/ReadOnlyBuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/ReadOnlyBuildPipelineView.java index 413f1e68..3d4ff402 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/ReadOnlyBuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/dashboard/ReadOnlyBuildPipelineView.java @@ -36,12 +36,10 @@ public class ReadOnlyBuildPipelineView extends BuildPipelineView { * is trigger only latest job? * @param cssUrl * URL for the custom CSS file. - * @param startsWithParameters - * indicates if the first job in the view takes parameters */ public ReadOnlyBuildPipelineView(final String displayName, final String description, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl, final boolean startsWithParameters) { - super(displayName, displayName, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl, startsWithParameters); + final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl) { + super(displayName, displayName, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); // this is ugly, but there is no other way to set the description of the view super.description = description; } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java index d90e3e69..73d171cb 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTrigger.java @@ -26,12 +26,12 @@ import hudson.Extension; import hudson.Launcher; -import hudson.Util; import hudson.PluginWrapper; +import hudson.Util; import hudson.model.BuildListener; +import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; import hudson.model.Item; -import hudson.model.ItemGroup; import hudson.model.Items; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; @@ -53,14 +53,12 @@ import java.util.logging.Level; import java.util.logging.Logger; -import jenkins.model.DependencyDeclarer; -import jenkins.model.Jenkins; - -import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import au.com.centrumsystems.hudson.plugin.buildpipeline.Strings; +import jenkins.model.Jenkins; +import org.kohsuke.stapler.AncestorInPath; /** * The build pipeline trigger allows the creation of downstream jobs which aren't triggered automatically. This allows us to have manual @@ -293,10 +291,10 @@ public String getHelpFile() { * Validates that the downstream project names entered are valid projects. * * @param value - The entered project names - * @param context - the context + * @param project - the containing project * @return hudson.util.FormValidation */ - public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup context, + public FormValidation doCheckDownstreamProjectNames(@AncestorInPath AbstractProject project, @QueryParameter("downstreamProjectNames") final String value) { final StringTokenizer tokens = new StringTokenizer(Util.fixNull(value), ","); //$NON-NLS-1$ boolean some = false; @@ -306,10 +304,10 @@ public FormValidation doCheckDownstreamProjectNames(@AncestorInPath ItemGroup continue; } some = true; - final Item item = Jenkins.getInstance().getItem(projectName, context, Item.class); + final Item item = Jenkins.getInstance().getItem(projectName, project, Item.class); if (item == null) { return FormValidation.error(Messages.BuildTrigger_NoSuchProject(projectName, - AbstractProject.findNearest(projectName, context).getRelativeNameFrom(context))); + AbstractProject.findNearest(projectName, project.getParent()).getRelativeNameFrom(project))); } if (!(item instanceof AbstractProject)) { return FormValidation.error(Messages.BuildTrigger_NotBuildable(projectName)); diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/DownstreamDependency.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/DownstreamDependency.java index f6c5c45d..c6251f80 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/DownstreamDependency.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/DownstreamDependency.java @@ -38,7 +38,7 @@ /** * Defines downstream dependency for the build pipeline trigger - * + * * @author Centrum Systems */ public class DownstreamDependency extends Dependency { @@ -49,7 +49,7 @@ public class DownstreamDependency extends Dependency { /** * Downstream Dependency - * + * * @param upstream * the upstream job * @param downstream @@ -69,10 +69,20 @@ public boolean shouldTriggerBuild(final AbstractBuild build, final TaskListener // If the upstream project has an automatic trigger to the downstream project // and the current build result was SUCCESS then return true. - final boolean retval = ((!ProjectUtil.isManualTrigger(build.getProject(), getDownstreamProject())) && (build.getResult() - .isBetterOrEqualTo(Result.SUCCESS))); - LOGGER.fine("" + retval); + if (ProjectUtil.isManualTrigger(build.getProject(), getDownstreamProject())) { + LOGGER.fine("(shouldn't trigger: manual)"); + return false; + } - return retval; + final Result result = build.getResult(); + if (result == null) { + throw new IllegalStateException("Build with a null result in DownstreamDependency#shouldTriggerBuilder"); + } + if (result.isWorseThan(Result.SUCCESS)) { + LOGGER.fine("(shouldn't trigger: unsuccessful build)"); + return false; + } + LOGGER.fine("(should trigger)"); + return true; } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java index 7298d2f9..4efd6a31 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/BuildUtil.java @@ -31,10 +31,15 @@ import hudson.model.Cause; import hudson.model.Cause.UpstreamCause; import hudson.model.CauseAction; +import hudson.model.FileParameterValue; import hudson.model.ParametersAction; +import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; /** * Provides helper methods for #hudson.model.AbstractBuild @@ -56,6 +61,11 @@ public final class BuildUtil { public static AbstractBuild getDownstreamBuild(final AbstractProject downstreamProject, final AbstractBuild upstreamBuild) { if ((downstreamProject != null) && (upstreamBuild != null)) { + // First, test the queue for any builds under way + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry != null) { + return null; + } @SuppressWarnings("unchecked") final List> downstreamBuilds = (List>) downstreamProject.getBuilds(); for (final AbstractBuild innerBuild : downstreamBuilds) { diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java index a21d62b2..c677e24e 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java @@ -1,6 +1,7 @@ package au.com.centrumsystems.hudson.plugin.util; import hudson.model.AbstractBuild; + /** * Utility Class to capture a link from a build form to a queue item. * @author rhirt @@ -37,6 +38,14 @@ public QueueEntry(AbstractBuild upstreamBuild) { public QueueEntry(String upstreamProjectName, String upstreamExtId) { this(upstreamProjectName, upstreamExtId, 0); } + /** + * Construct from projectName and externalizable id of upstreamBuild + * @param upstreamProjectName - the project name of the build upstream + * @param upstreamBuildId - the build number of the build upstream + */ + public QueueEntry(String upstreamProjectName, int upstreamBuildId) { + this(upstreamProjectName, upstreamProjectName + "#" + upstreamBuildId, 0); + } /** * Construct from projectName and externalizable id of upstreamBuild, add a queueId * @param upstreamProjectName - the project name of the build upstream diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java index e3467dbd..40c1738e 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java @@ -4,6 +4,7 @@ import hudson.model.AbstractProject; import hudson.model.Queue; import hudson.model.Queue.WaitingItem; +import hudson.model.queue.QueueListener; import java.util.HashMap; import java.util.List; @@ -18,34 +19,42 @@ * @author rhirt * */ -public final class QueueUtil { +public final class QueueUtil extends QueueListener { /** A Logger object is used to log messages */ private static final Logger LOGGER = Logger.getLogger(QueueUtil.class.getName()); /** A static hashmap to bind queued jobs to builds **/ - public static final HashMap QUEUE_ENTRIES = new HashMap(); + private static final HashMap QUEUE_ENTRIES = new HashMap(); /** * Adding an entry into the list of queued items. - * @param queueEntry - entry to be added + * + * @param queueEntry + * - entry to be added */ public static void addQueueEntry(QueueEntry queueEntry) { LOGGER.info("Add entry to QueueUtil: " + queueEntry.toString()); QUEUE_ENTRIES.put(queueEntry.getKey(), queueEntry); } - + /** * Remove an entry from the list of queued items. - * @param queueEntry - entry to be removed + * + * @param queueEntry + * - entry to be removed */ public static void removeQueueEntry(QueueEntry queueEntry) { LOGGER.info("Remove entry from QueueUtil: " + queueEntry.toString()); - QUEUE_ENTRIES.remove(queueEntry.getKey()); + if (QUEUE_ENTRIES.remove(queueEntry.getKey()) != null) { + LOGGER.info("Done - removed entry from QueueUtil: " + queueEntry.toString()); + } } - + /** * Retrieve a queue entry based on an upstream build - * @param upstreamBuild - the upstream build to look for + * + * @param upstreamBuild + * - the upstream build to look for * @return QueueEntry - the entry if found */ public static QueueEntry getQueueEntry(AbstractBuild upstreamBuild) { @@ -54,11 +63,12 @@ public static QueueEntry getQueueEntry(AbstractBuild upstreamBuild) { } return QUEUE_ENTRIES.get(new QueueEntry(upstreamBuild).getKey()); } - + /** * Retrieve the queued items for a project * - * @param project - the project to search queued items for + * @param project + * - the project to search queued items for * @return List - the items */ public static List getQueuedItemsFor(AbstractProject project) { @@ -68,14 +78,17 @@ public static List getQueuedItemsFor(AbstractProject project) /** * Retrieve the id of a new queue item relative to the list of items before * - * @param project - the project to search queued items for - * @param oldItems - list of items to check the new list against + * @param project + * - the project to search queued items for + * @param oldItems + * - list of items to check the new list against * @return int - the id of the new queue item */ public static int getNewQueuedItemId(AbstractProject project, List oldItems) { - List newItems = Jenkins.getInstance().getQueue().getItems(project); - - // Now search the new list for items that are not in the old list, if one does exist, return it's id + final List newItems = Jenkins.getInstance().getQueue().getItems(project); + + // Now search the new list for items that are not in the old list, if + // one does exist, return it's id for (Queue.Item qitem : newItems) { if (!oldItems.contains(qitem)) { LOGGER.fine("Found last queued item for project " + project.getDisplayName() + " with id " + qitem.id); @@ -88,7 +101,8 @@ public static int getNewQueuedItemId(AbstractProject project, List project, QueueEntry /** * Retrieve a Queue.Item of type WaitingItem * - * @param project - the project to search queued items for - * @param upstreamBuild the upstream build we derive the queue item from - * @return WaitingItem - the waiting item from the queue for the given upstreamBuild + * @param project + * - the project to search queued items for + * @param upstreamBuild + * the upstream build we derive the queue item from + * @return WaitingItem - the waiting item from the queue for the given + * upstreamBuild */ public static WaitingItem getQueuedWaitingItem(AbstractProject project, AbstractBuild upstreamBuild) { final QueueEntry qentry = getQueueEntry(upstreamBuild); @@ -119,8 +136,10 @@ public static WaitingItem getQueuedWaitingItem(AbstractProject project, Ab /** * Retrieve a queued item with the given id from the build history * - * @param project - the project to search queued items for - * @param queueId - the id of the queued item + * @param project + * - the project to search queued items for + * @param queueId + * - the id of the queued item * @return Item - the item from the queue for the given name */ private static Queue.Item getQueuedItem(AbstractProject project, int queueId) { diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java index 0b2b5b54..9d93c414 100644 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java @@ -125,27 +125,6 @@ public IntervalParameterValue getDefaultParameterValue() { @Override public ParameterValue createValue(StaplerRequest req) { - // TODO Auto-generated method stub - // FileItem src; - // try { - // src = req.getFileItem( getName() ); - // } catch (ServletException e) { - // // Not sure what to do here. We might want to raise this - // // but that would involve changing the throws for this call - // return null; - // } catch (IOException e) { - // // ditto above - // return null; - // } - // if ( src == null ) { - // // the requested file parameter wasn't uploaded - // return null; - // } - // FileParameterValue p = new FileParameterValue(getName(), src, - // getFileName(src.getName())); - // p.setDescription(getDescription()); - // p.setLocation(getName()); - // return p; return null; } diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java index 1993a643..5ed27eef 100644 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java +++ b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java @@ -26,7 +26,6 @@ import hudson.EnvVars; import hudson.model.ParameterValue; import hudson.model.AbstractBuild; -import hudson.model.Run; import hudson.util.VariableResolver; import java.util.Locale; @@ -93,21 +92,11 @@ public IntervalParameterValue(String name, Interval interval, String description this.value = interval; } - /** - * Exposes the name/value as an environment variable. - * - * @param build - * - the run - * @param env - * - the environment - */ + @Override - public void buildEnvironment(Run build, EnvVars env) { + public void buildEnvVars(AbstractBuild build, EnvVars env) { env.put(name, value.toString()); - env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); // backward - // compatibility - // pre - // 1.345 + env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); } @Override diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 449960f6..c096ac62 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -125,7 +125,7 @@ {{#if build.isSuccess}} {{#if ${!from.triggerOnlyLatestJob}}} - + re-run @@ -134,7 +134,7 @@ {{#if ${from.triggerOnlyLatestJob}}} {{#if build.isLatestBuild}} {{#if build.isUpstreamBuildLatest}} - + retry {{/if}} @@ -226,7 +226,7 @@ background: url("${rootURL}/images/16x16/clock.png") no-repeat center center !important; } -
+ @@ -249,17 +249,15 @@
- -
+ + Trigger a Pipeline -
+
- -
- - Trigger a Pipeline - -
+ + + Trigger a Pipeline +
Run
diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/configure-entries.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/configure-entries.jelly index 7798bdce..30135469 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/configure-entries.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/configure-entries.jelly @@ -20,10 +20,6 @@ - - - - diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index a7e3982a..9a2a957a 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -91,7 +91,7 @@ /*************************** build card *******************************/ .rounded { height: 70px; - width: 141px !important; + width: 141px; -webkit-opacity: 0.9; opacity: 0.9; @@ -157,6 +157,11 @@ width: 102px; } +.build-card { + float: left; + width: 100%; +} + .build-card .build-body { height: 38px; } @@ -333,7 +338,6 @@ tr.spacerRow td { } .PROJECT .header-name div.header-wrapper { - width: 141px; text-align: left; } diff --git a/src/main/webapp/js/build-pipeline.js b/src/main/webapp/js/build-pipeline.js index 60e7db22..1d3366af 100644 --- a/src/main/webapp/js/build-pipeline.js +++ b/src/main/webapp/js/build-pipeline.js @@ -81,6 +81,7 @@ BuildPipeline.prototype = { buildPipeline.viewProxy.triggerManualBuild(upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); + buildPipeline.showProgress(id, {}); }, cancelQueued : function(id, queueId) { var buildPipeline = this; @@ -92,6 +93,7 @@ BuildPipeline.prototype = { } }); }, buildPipeline.refreshFrequency); + buildPipeline.showProgress(id, {}); }, retryBuild : function(id, triggerProjectName, dependencyIds) { var buildPipeline = this; @@ -99,11 +101,12 @@ BuildPipeline.prototype = { buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); }, - rerunBuild : function(id, buildExternalizableId, dependencyIds) { + rerunBuild : function(id, buildNumber, upstreamProjectName, upstreamBuildNumber, triggerProjectName, dependencyIds) { var buildPipeline = this; - buildPipeline.viewProxy.rerunBuild(buildExternalizableId, function(data){ + buildPipeline.viewProxy.rerunBuild(buildNumber, upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); + buildPipeline.showProgress(id, {}); }, showSpinner : function(id){ jQuery("#status-bar-" + id).html('
'); @@ -114,7 +117,7 @@ BuildPipeline.prototype = { type: 'iframe', title: title, titlePosition: 'outside', - href: href, + href: '/' + href, transitionIn : 'elastic', transitionOut : 'elastic', width: '90%', @@ -131,4 +134,4 @@ BuildPipeline.prototype = { jQuery.fancybox.hideActivity(); } -} \ No newline at end of file +} diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildFormTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildFormTest.java index 28a86df8..09229e02 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildFormTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildFormTest.java @@ -1,6 +1,7 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertThat; import java.util.ArrayList; @@ -67,4 +68,19 @@ public void testGetParameterList() throws Exception { assertEquals(paramList, bf.getParameterList()); } + + @Test + public void testNoInfiniteRecursion() throws Exception { + final String proj1 = "Project1"; + final String proj2 = "Project2"; + final FreeStyleProject project1 = createFreeStyleProject(proj1); + final FreeStyleProject project2 = createFreeStyleProject(proj2); + project1.getPublishersList().add(new BuildTrigger(proj2, false)); + project2.getPublishersList().add(new BuildTrigger(proj1, false)); + hudson.rebuildDependencyGraph(); + + final BuildForm form1 = new BuildForm(jenkins, new PipelineBuild(null, project1, null)); + assertThat(form1.getDependencies(), hasSize(1)); + assertThat(form1.getDependencies().get(0).getDependencies(), hasSize(0)); + } } diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewConstructorTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewConstructorTest.java index 20b7ae5a..02ebf0a2 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewConstructorTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewConstructorTest.java @@ -24,11 +24,11 @@ public class BuildPipelineViewConstructorTest { @Test public void testAlwaysAllowManualTrigger() throws IOException { // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, true, false, false, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, true, false, false, false, 2, null, null); assertTrue(testView.isAlwaysAllowManualTrigger()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse(testView.isAlwaysAllowManualTrigger()); } @@ -36,11 +36,11 @@ public void testAlwaysAllowManualTrigger() throws IOException { public void testShowPipelineParameters() throws IOException { // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, false, true, false, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, false, true, false, false, 2, null, null); assertTrue(testView.isShowPipelineParameters()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse(testView.isShowPipelineParameters()); } @@ -48,11 +48,11 @@ public void testShowPipelineParameters() throws IOException { public void testShowPipelineParametersInHeaders() throws IOException { // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, false, true, true, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, false, true, true, false, 2, null, null); assertTrue(testView.isShowPipelineParametersInHeaders()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse(testView.isShowPipelineParametersInHeaders()); } @@ -60,31 +60,19 @@ public void testShowPipelineParametersInHeaders() throws IOException { public void testRefreshFrequency() throws IOException { // False - final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null, false); + final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null); assertThat(testView.getRefreshFrequency(), is(2)); assertThat(testView.getRefreshFrequencyInMillis(), is(2000)); } - @Test - public void testStartsWithParameters() throws IOException { - - // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, true, false, true, false, false, 2, null, null, true); - assertTrue(testView.isStartsWithParameters()); - - // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, nullGridBuilder, noOfBuilds, true, false, false, false, false, 2, null, null, false); - assertFalse(testView.isStartsWithParameters()); - } - /** * This is a factory to create an instance of the class under test. This helps to avoid a NPE in View.java when calling * getOwnerItemGroup and it's not set. This doesn't solve the root cause and it't only intended to make our tests succeed. */ static class BuildPipelineViewFactory { public static BuildPipelineView getBuildPipelineView(final String bpViewName, final String bpViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfBuilds, final boolean triggerOnlyLatestJob, final boolean startsWithParameters) { - return new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, triggerOnlyLatestJob, null, startsWithParameters) { + final String noOfBuilds, final boolean triggerOnlyLatestJob) { + return new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, triggerOnlyLatestJob, null) { @Override public ItemGroup getOwnerItemGroup() { diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewTest.java index 403391f4..11a4cde4 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineViewTest.java @@ -71,7 +71,7 @@ public void testGetSelectedProject() throws IOException { // Test a valid case DownstreamProjectGridBuilder gridBuilder = new DownstreamProjectGridBuilder(proj1); - BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, false, false); + BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, false); Job testSelectedProject = gridBuilder.getFirstJob(testView); @@ -79,7 +79,7 @@ public void testGetSelectedProject() throws IOException { // Test the null case gridBuilder = new DownstreamProjectGridBuilder(""); - testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, false, false); + testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, false); testSelectedProject = gridBuilder.getFirstJob(testView); assertNull(testSelectedProject); @@ -94,7 +94,7 @@ public void testHasBuildPermission() throws IOException { final FreeStyleProject project1 = createFreeStyleProject(proj1); final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, - new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null, false); + new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null); assertTrue(testView.hasBuildPermission()); } @@ -107,11 +107,11 @@ public void testTriggerOnlyLatestJob() throws IOException { createFreeStyleProject(proj1); // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, null); assertTrue(proj1, testView.isTriggerOnlyLatestJob()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, false, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, false, null); assertFalse(proj1, testView.isTriggerOnlyLatestJob()); } @@ -124,11 +124,11 @@ public void testAlwaysAllowManualTrigger() throws IOException { createFreeStyleProject(proj1); // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, true, false, false, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, true, false, false, false, 2, null, null); assertTrue("Failed to set AlwaysAllowManualTrigger flag", testView.isAlwaysAllowManualTrigger()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse("Failed to unset AlwaysAllowManualTrigger flag", testView.isAlwaysAllowManualTrigger()); } @@ -141,11 +141,11 @@ public void testShowPipelineDefinitionHeader() throws IOException { createFreeStyleProject(proj1); // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, false, false, true, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, false, false, true, 2, null, null); assertTrue("Failed to set ShowPipelineDefinitionHeader flag", testView.isShowPipelineDefinitionHeader()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse("Failed to unset ShowPipelineDefinitionHeader flag", testView.isShowPipelineDefinitionHeader()); } @@ -158,11 +158,11 @@ public void testShowPipelineParameters() throws IOException { createFreeStyleProject(proj1); // True - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, false, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, false, false, 2, null, null); assertTrue("Failed to set ShowPipelineParameters flag", testView.isShowPipelineParameters()); // False - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(""), noOfBuilds, true, false, false, false, false, 2, null, null); assertFalse("Failed to unset ShowPipelineParameters flag", testView.isShowPipelineParameters()); } @@ -174,28 +174,13 @@ public void testShowPipelineParametersInHeaders() throws IOException { final String noOfBuilds = "5"; createFreeStyleProject(proj1); - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, true, false, 2, null, null, false); + BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, true, false, 2, null, null); assertTrue("Failed to set ShowPipelineParametersInHeaders flag", testView.isShowPipelineParametersInHeaders()); - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, false, false, 2, null, null, false); + testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, false, false, 2, null, null); assertFalse("Failed to unset ShowPipelineParametersInHeaders flag", testView.isShowPipelineParametersInHeaders()); } - @Test - public void testStartsWithParameters() throws IOException { - final String bpViewName = "MyTestView"; - final String bpViewTitle = "MyTestViewTitle"; - final String proj1 = "Proj1"; - final String noOfBuilds = "5"; - createFreeStyleProject(proj1); - - BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, true, false, 2, null, null, true); - assertTrue("Failed to set StartsWithPArameters flag", testView.isStartsWithParameters()); - - testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, true, false, true, false, false, 2, null, null, false); - assertFalse("Failed to unset StartsWithPArameters flag", testView.isStartsWithParameters()); - } - @Test public void testHasDownstreamProjects() throws IOException { final String bpViewName = "MyTestView"; @@ -214,7 +199,7 @@ public void testHasDownstreamProjects() throws IOException { Hudson.getInstance().rebuildDependencyGraph(); // Test a valid case - final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null, false); + final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null); assertTrue(testView.hasDownstreamProjects(project1)); assertFalse(testView.hasDownstreamProjects(project2)); @@ -238,7 +223,7 @@ public void testGetDownstreamProjects() throws IOException { Hudson.getInstance().rebuildDependencyGraph(); // Test a valid case - final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null, false); + final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null); assertEquals(testView.getDownstreamProjects(project1).get(0), project2); assertEquals(testView.getDownstreamProjects(project2).size(), 0); @@ -270,7 +255,7 @@ public void testGetBuildPipelineForm() throws Exception { waitUntilNoActivity(); // Test a valid case - final BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, false); + final BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false); UpstreamCause upstreamCause = new hudson.model.Cause.UpstreamCause( (Run) build1); @@ -314,7 +299,7 @@ public void testOnJobRenamed() throws IOException { Hudson.getInstance().rebuildDependencyGraph(); // Test a valid case - final BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, false); + final BuildPipelineView testView = BuildPipelineViewFactory.getBuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), noOfBuilds, false); assertEquals(testView.getJob(proj1), project1); project1.renameTo(proj3); @@ -330,7 +315,7 @@ public void testGetItems() throws IOException { final FreeStyleProject project1 = createFreeStyleProject(proj1); final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, - new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null, false); + new DownstreamProjectGridBuilder(proj1), noOfBuilds, false, null); TopLevelItem item1 = Jenkins.getInstance().getItem(proj1); assertNotNull(item1); assertTrue(testView.getItems().contains(item1)); @@ -351,7 +336,7 @@ public void testHasPermission() throws IOException { final BuildPipelineView testView = new BuildPipelineView(bpViewName, bpViewTitle, new DownstreamProjectGridBuilder(proj1), - noOfBuilds, false, null, false); + noOfBuilds, false, null); assertTrue(testView.hasPermission(Permission.READ)); } @@ -364,8 +349,8 @@ bpViewTitle, new DownstreamProjectGridBuilder(proj1), */ static class BuildPipelineViewFactory { public static BuildPipelineView getBuildPipelineView(final String bpViewName, final String bpViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfBuilds, final boolean triggerOnlyLatestJob, final boolean startsWithParameters) { - return new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, triggerOnlyLatestJob, null, startsWithParameters) { + final String noOfBuilds, final boolean triggerOnlyLatestJob) { + return new BuildPipelineView(bpViewName, bpViewTitle, gridBuilder, noOfBuilds, triggerOnlyLatestJob, null) { @Override public ItemGroup getOwnerItemGroup() { diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilderTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilderTest.java index 87b4edec..1ff31073 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilderTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/DownstreamProjectGridBuilderTest.java @@ -11,7 +11,7 @@ public class DownstreamProjectGridBuilderTest extends HudsonTestCase { */ public void testConfigRoundtrip() throws Exception { DownstreamProjectGridBuilder gridBuilder = new DownstreamProjectGridBuilder("something"); - BuildPipelineView v = new BuildPipelineView("foo","Title", gridBuilder, "5", true, null, false); + BuildPipelineView v = new BuildPipelineView("foo","Title", gridBuilder, "5", true, null); jenkins.addView(v); configRoundtrip(v); BuildPipelineView av = (BuildPipelineView)jenkins.getView(v.getViewName()); diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectFormTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectFormTest.java index 585ec081..953407a0 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectFormTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectFormTest.java @@ -1,6 +1,7 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertThat; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; @@ -60,4 +61,19 @@ public void testEquals() throws IOException { assertFalse(pf.equals(pf3)); } + + @Test + public void testNoInfiniteRecursion() throws IOException { + final String proj1 = "Project1"; + final String proj2 = "Project2"; + final FreeStyleProject project1 = createFreeStyleProject(proj1); + final FreeStyleProject project2 = createFreeStyleProject(proj2); + project1.getPublishersList().add(new BuildTrigger(proj2, false)); + project2.getPublishersList().add(new BuildTrigger(proj1, false)); + hudson.rebuildDependencyGraph(); + + final ProjectForm form1 = new ProjectForm(project1); + assertThat(form1.getDependencies(), hasSize(1)); + assertThat(form1.getDependencies().get(0).getDependencies(), hasSize(0)); + } } diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/testsupport/PipelineWebDriverTestBase.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/testsupport/PipelineWebDriverTestBase.java index acd07ca3..2b79a3c6 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/testsupport/PipelineWebDriverTestBase.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/testsupport/PipelineWebDriverTestBase.java @@ -31,7 +31,7 @@ public class PipelineWebDriverTestBase { public void initSharedComponents() throws Exception { realm = jr.createDummySecurityRealm(); jr.jenkins.setSecurityRealm(realm); - pipelineView = new BuildPipelineView("pipeline", "Pipeline", new DownstreamProjectGridBuilder(INITIAL_JOB), "5", false, true, false, false, false, 1, null, null, false); + pipelineView = new BuildPipelineView("pipeline", "Pipeline", new DownstreamProjectGridBuilder(INITIAL_JOB), "5", false, true, false, false, false, 1, null, null); jr.jenkins.addView(pipelineView); initialJob = jr.createFreeStyleProject(INITIAL_JOB); diff --git a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTriggerTest.java b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTriggerTest.java index 82d0c993..14e1510f 100644 --- a/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTriggerTest.java +++ b/src/test/java/au/com/centrumsystems/hudson/plugin/buildpipeline/trigger/BuildPipelineTriggerTest.java @@ -170,7 +170,7 @@ public void testDoCheckDownstreamProjectNames() throws IOException, InterruptedE final BuildPipelineTrigger.DescriptorImpl di = new BuildPipelineTrigger.DescriptorImpl(); assertEquals(FormValidation.ok(), di.doCheckDownstreamProjectNames(upstreamProject, proj1)); - assertThat(FormValidation.error("No such project '" + proj2 + "'. Did you mean '" + proj1 + "'?").toString(), is(di + assertThat(FormValidation.error("No such project ‘" + proj2 + "’. Did you mean ‘" + proj1 + "’?").toString(), is(di .doCheckDownstreamProjectNames(upstreamProject, proj2).toString())); } From 8c69472fe9d7a2f18343c0dbf66f30634e68fd4f Mon Sep 17 00:00:00 2001 From: rhirt Date: Wed, 20 Aug 2014 21:23:46 +0200 Subject: [PATCH 05/21] update queuing --- .../plugin/util/PipelineQueueListener.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java new file mode 100644 index 00000000..f5ea288a --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java @@ -0,0 +1,50 @@ +package au.com.centrumsystems.hudson.plugin.util; + +import hudson.Extension; +import hudson.model.Cause; +import hudson.model.CauseAction; +import hudson.model.Cause.UpstreamCause; +import hudson.model.Queue.LeftItem; +import hudson.model.Queue.WaitingItem; +import hudson.model.queue.QueueListener; + +import java.util.logging.Logger; + +/** + * Utility Class to provide listener functionality for the queue. + * @author rhirt + */ +@Extension +public class PipelineQueueListener extends QueueListener { + /** A Logger object is used to log messages */ + private static final Logger LOGGER = Logger.getLogger(PipelineQueueListener.class.getName()); + + @Override + public void onEnterWaiting(WaitingItem wi) { + super.onEnterWaiting(wi); + for (final CauseAction action : wi.getActions(CauseAction.class)) { + for (final Cause cause : action.getCauses()) { + if (cause instanceof UpstreamCause) { + final UpstreamCause upstreamCause = (UpstreamCause) cause; + final QueueEntry qentry = new QueueEntry(upstreamCause.getUpstreamProject(), upstreamCause.getUpstreamBuild()); + qentry.setQueueId(wi.id); + QueueUtil.addQueueEntry(qentry); + } + } + } + } + + @Override + public void onLeft(LeftItem li) { + super.onLeft(li); + LOGGER.info("onLeft: " + li.toString()); + for (final CauseAction action : li.getActions(CauseAction.class)) { + for (final Cause cause : action.getCauses()) { + if (cause instanceof UpstreamCause) { + final UpstreamCause upstreamCause = (UpstreamCause) cause; + QueueUtil.removeQueueEntry(new QueueEntry(upstreamCause.getUpstreamProject(), upstreamCause.getUpstreamBuild())); + } + } + } + } +} From c4c023b592de71c633cb181e54ab0723e1c0faba Mon Sep 17 00:00:00 2001 From: rhirt Date: Wed, 20 Aug 2014 21:35:32 +0200 Subject: [PATCH 06/21] update queueing --- .../hudson/plugin/buildpipeline/PipelineBuild.java | 12 ++++++------ .../hudson/plugin/buildpipeline/ProjectForm.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index c5bb7b20..67fc5192 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -498,12 +498,12 @@ private boolean upstreamBuildSucceeded() { return this.getUpstreamBuild() != null && HudsonResult.SUCCESS.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * @return upstream build exists and unstable. - */ - private boolean upstreamBuildUnstable() { - return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); - } + /** + * @return upstream build exists and unstable. + */ + private boolean upstreamBuildUnstable() { + return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); + } /** * diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index ebe80975..9fafc1ba 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -20,9 +20,9 @@ /** * @author Centrum Systems - * + * * Representation of a set of projects - * + * */ public class ProjectForm { /** From c28bcbe914c47b9268ee3afbaa1b82ef83649a5c Mon Sep 17 00:00:00 2001 From: rhirt Date: Wed, 27 Aug 2014 16:44:32 +0200 Subject: [PATCH 07/21] take back changes in pom --- pom.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index f4af1581..1bb3301a 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.hamcrest hamcrest-core - 1.3 + 1.2 test @@ -231,7 +231,6 @@ 2.7.1 - 1.6 ${pmd.config.file} @@ -258,7 +257,6 @@ Medium true findbugs-exclude.xml - false From 83f303bec6395244917340355cef6e47ee4af1eb Mon Sep 17 00:00:00 2001 From: rhirt Date: Wed, 27 Aug 2014 21:01:29 +0200 Subject: [PATCH 08/21] latest changes --- src/main/webapp/css/main.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 9a2a957a..53075de1 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -91,7 +91,7 @@ /*************************** build card *******************************/ .rounded { height: 70px; - width: 141px; + width: 141px !important; -webkit-opacity: 0.9; opacity: 0.9; @@ -338,6 +338,7 @@ tr.spacerRow td { } .PROJECT .header-name div.header-wrapper { + width: 141px; text-align: left; } From 4e02328e528f7ad36c7e396369ed9065bba7b9b4 Mon Sep 17 00:00:00 2001 From: Reto X Hirt Date: Tue, 2 Sep 2014 06:55:13 +0200 Subject: [PATCH 09/21] try fix merge conflicts --- .../plugin/buildpipeline/BuildForm.java | 10 ++------- .../buildpipeline/BuildPipelineView.java | 6 ++--- .../plugin/buildpipeline/PipelineBuild.java | 9 ++++---- .../plugin/buildpipeline/ProjectForm.java | 19 +++++++--------- .../buildpipeline/BuildPipelineView/bpp.jelly | 22 +++++++++---------- src/main/webapp/css/main.css | 3 +-- src/main/webapp/js/build-pipeline.js | 2 -- 7 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index a5caa8cd..94351def 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -205,20 +205,14 @@ public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); } - - /** - * @return Map the map with the parameters, enriched with a version parameter. - */ public Map getParameters() { - final Map params = new HashMap(); - params.putAll(pipelineBuild.getBuildParameters()); - return params; + return pipelineBuild.getBuildParameters(); } public Map getFilteredParameters() { return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); } - + public ArrayList getParameterList() { return parameters; } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 3136074d..c3fb07f6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -883,9 +883,9 @@ public Collection getItems() { for (int col = 0; col < grid.getColumns(); col++) { final ProjectForm form = grid.get(row, col); if (form != null) { - final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); - if (item != null) { - items.add(item); + final Item item = Jenkins.getInstance().getItem(form.getName(), getOwnerItemGroup()); + if (item != null && item instanceof TopLevelItem) { + items.add((TopLevelItem) item); } } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 67fc5192..25ad6ecd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Queue.WaitingItem; +import jenkins.model.Jenkins; import hudson.model.Result; import java.text.DateFormat; @@ -39,15 +39,16 @@ import java.util.Map; import java.util.logging.Logger; -import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; +import hudson.model.Queue.WaitingItem; + /** * @author Centrum Systems * diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 9fafc1ba..6c23d929 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -4,6 +4,7 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import jenkins.model.Jenkins; import java.util.ArrayList; import java.util.Arrays; @@ -13,9 +14,7 @@ import java.util.List; import java.util.Map; -import jenkins.model.Jenkins; import hudson.plugins.parameterizedtrigger.SubProjectsAction; - import org.kohsuke.stapler.bind.JavaScriptMethod; /** @@ -141,8 +140,7 @@ public ProjectForm(final AbstractProject project, final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); - // if subprojects come back as downstreams someday, - // no duplicates wanted + // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -168,9 +166,9 @@ public ProjectForm(final AbstractProject project, final Collection p) { return p != null ? new ProjectForm(p) : null; @@ -244,7 +242,7 @@ public List getDependencies() { * job which should show the trigger button will render and then a call will * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -262,10 +260,9 @@ public Boolean getDisplayTrigger() { * job which should show the trigger button will render and then a call will * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @param display - * - boolean to indicate whether the trigger button should be - * shown + * - boolean to indicate whether the trigger button should be shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -307,7 +304,7 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index c096ac62..73f96ee5 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -248,17 +248,17 @@ -
- - - Trigger a Pipeline - - - - - Trigger a Pipeline - - +
+ + + Trigger a Pipeline + + + + + Trigger a Pipeline + +
Run
diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 53075de1..fa7c130f 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -91,7 +91,7 @@ /*************************** build card *******************************/ .rounded { height: 70px; - width: 141px !important; + width: 141px; -webkit-opacity: 0.9; opacity: 0.9; @@ -338,7 +338,6 @@ tr.spacerRow td { } .PROJECT .header-name div.header-wrapper { - width: 141px; text-align: left; } diff --git a/src/main/webapp/js/build-pipeline.js b/src/main/webapp/js/build-pipeline.js index 1d3366af..1f92bf6e 100644 --- a/src/main/webapp/js/build-pipeline.js +++ b/src/main/webapp/js/build-pipeline.js @@ -81,7 +81,6 @@ BuildPipeline.prototype = { buildPipeline.viewProxy.triggerManualBuild(upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); - buildPipeline.showProgress(id, {}); }, cancelQueued : function(id, queueId) { var buildPipeline = this; @@ -106,7 +105,6 @@ BuildPipeline.prototype = { buildPipeline.viewProxy.rerunBuild(buildNumber, upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); - buildPipeline.showProgress(id, {}); }, showSpinner : function(id){ jQuery("#status-bar-" + id).html('
'); From c085d969be56111aa78ae90b3b5b86f736f18b99 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 2 Sep 2014 18:07:49 +0200 Subject: [PATCH 10/21] revert for merge later --- .../plugin/buildpipeline/ProjectForm.java | 164 ++------ .../plugin/buildpipeline/ProjectForm2.java | 367 ++++++++++++++++++ 2 files changed, 390 insertions(+), 141 deletions(-) create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 6c23d929..99996b78 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -4,7 +4,7 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import jenkins.model.Jenkins; +import hudson.model.Hudson; import java.util.ArrayList; import java.util.Arrays; @@ -52,38 +52,18 @@ public class ProjectForm { /** * the latest successful build number */ - private String lastSuccessfulBuildNumber; + private final String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private Map lastSuccessfulBuildParams; - - /** - * Whether last successful build is already handled - */ - private boolean lastSuccessfulHandled; + private final Map lastSuccessfulBuildParams; /** * keep reference to the project so that we can update it */ private final AbstractProject project; - /** - * keep reference to the first project in the current pipeline view - */ - private final AbstractProject firstProject; - - /** - * Hold the row this project is placed in (handle duplicate project-ids) - */ - private int row; - - /** - * Hold the column this project is placed in (handle duplicate project-ids) - */ - private int col; - /** * @param name * project name @@ -98,7 +78,6 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; - firstProject = null; } /** @@ -106,20 +85,16 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { - this(project, new LinkedHashSet>(Arrays.asList(project)), project); + this(project, new LinkedHashSet>(Arrays.asList(project))); } /** * @param project - * - the project to wrap + * project * @param parentPath * already traversed projects - * @param firstProject - * - the first project associated with the grid for this - * projectform */ - public ProjectForm(final AbstractProject project, final Collection> parentPath, - final AbstractProject firstProject) { + private ProjectForm(final AbstractProject project, final Collection> parentPath) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); name = pipelineBuild.getProject().getFullName(); @@ -130,16 +105,16 @@ public ProjectForm(final AbstractProject project, final Collection dependency : project.getDownstreamProjects()) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); + dependencies.add(new ProjectForm(dependency, forkedPath)); } } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); + final ProjectForm candidate = new ProjectForm(dependency, forkedPath); // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); @@ -151,20 +126,16 @@ public ProjectForm(final AbstractProject project, final Collection lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = BuildUtil.getUnsensitiveParameters(lastSuccessfulBuild); this.project = project; - this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * This method is only called for a starting project in a pipeline view, - * therefore save it as such. - * + * * @param p * project to be wrapped. * @return @@ -178,27 +149,6 @@ public String getName() { return name; } - public int getRow() { - return row; - } - - public int getColumn() { - return col; - } - - /** - * Set the coordinates for this project form. - * - * @param row - * - the row this form is set into - * @param col - * - the column this form is set into - */ - public void setCoords(int row, int col) { - this.row = row; - this.col = col; - } - public String getHealth() { return health; } @@ -219,28 +169,16 @@ public Map getLastSuccessfulBuildParams() { return lastSuccessfulBuildParams; } - public boolean isLastSuccessfulHandled() { - return lastSuccessfulHandled; - } - - public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { - this.lastSuccessfulHandled = lastSuccessfulHandled; - } - public List getDependencies() { return dependencies; } /** - * Gets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both + * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly * * @return boolean whether to display or not @@ -250,15 +188,11 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both + * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly * * @param display @@ -312,56 +246,4 @@ public String asJSON() { return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - public Map filterSensitiveBuildVariables(AbstractBuild build) { - return BuildUtil.getUnsensitiveParameters(build); - } - - /** - * Correct the last successful build settings of this form, if that build is - * not on the current BuildPipelineView (when Jobs are used on multiple - * Pipelines) - * - * @param buildGrids - * - the build-grids present on this view - */ - public void correctLastSuccessfulBuilds(Iterable buildGrids) { - if (!isLastSuccessfulHandled()) { - AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); - // Determine whether the lastSuccessful Build belongs to this View - outer: while (lastSuccessfulBuild != null) { - // Search the buildGrid for the lastSuccessfulBuild given above - BuildForm buildForm = null; - for (BuildGrid buildGrid : buildGrids) { - buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); - if (buildForm != null) { - break outer; - } - } - lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); - } - // Set the (may be) newly found last successful build - handleLastSuccessfulBuild(lastSuccessfulBuild); - setLastSuccessfulHandled(true); - } - } - - /** - * Set build-number and build-params for last successful build. - * - * @param lastSuccessfulBuild - * - the last successful build (can be null) - */ - private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() - : filterSensitiveBuildVariables(lastSuccessfulBuild); - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java new file mode 100644 index 00000000..6c23d929 --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java @@ -0,0 +1,367 @@ +package au.com.centrumsystems.hudson.plugin.buildpipeline; + +import au.com.centrumsystems.hudson.plugin.util.BuildUtil; +import hudson.Util; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import jenkins.model.Jenkins; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import org.kohsuke.stapler.bind.JavaScriptMethod; + +/** + * @author Centrum Systems + * + * Representation of a set of projects + * + */ +public class ProjectForm { + /** + * project name + */ + private final String name; + /** + * last build result + */ + private final String result; + /** + * overall health + */ + private final String health; + /** + * project url + */ + private final String url; + /** + * downstream projects + */ + private final List dependencies; + /** + * display manual build + */ + private Boolean displayTrigger; + + /** + * the latest successful build number + */ + private String lastSuccessfulBuildNumber; + + /** + * the parameters used in the last successful build + */ + private Map lastSuccessfulBuildParams; + + /** + * Whether last successful build is already handled + */ + private boolean lastSuccessfulHandled; + + /** + * keep reference to the project so that we can update it + */ + private final AbstractProject project; + + /** + * keep reference to the first project in the current pipeline view + */ + private final AbstractProject firstProject; + + /** + * Hold the row this project is placed in (handle duplicate project-ids) + */ + private int row; + + /** + * Hold the column this project is placed in (handle duplicate project-ids) + */ + private int col; + + /** + * @param name + * project name + */ + public ProjectForm(final String name) { + this.name = name; + result = ""; + health = ""; + url = ""; + lastSuccessfulBuildNumber = ""; + lastSuccessfulBuildParams = new HashMap(); + dependencies = new ArrayList(); + this.displayTrigger = true; + project = null; + firstProject = null; + } + + /** + * @param project + * project + */ + public ProjectForm(final AbstractProject project) { + this(project, new LinkedHashSet>(Arrays.asList(project)), project); + } + + /** + * @param project + * - the project to wrap + * @param parentPath + * already traversed projects + * @param firstProject + * - the first project associated with the grid for this + * projectform + */ + public ProjectForm(final AbstractProject project, final Collection> parentPath, + final AbstractProject firstProject) { + final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); + + name = pipelineBuild.getProject().getFullName(); + result = pipelineBuild.getCurrentBuildResult(); + health = pipelineBuild.getProject().getBuildHealth().getIconUrl().replaceAll("\\.gif", "\\.png"); + url = pipelineBuild.getProjectURL(); + dependencies = new ArrayList(); + for (final AbstractProject dependency : project.getDownstreamProjects()) { + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(dependency)) { + dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); + } + } + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { + for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { + for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(dependency)) { + final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); + // if subprojects come back as downstreams someday, no duplicates wanted + if (!dependencies.contains(candidate)) { + dependencies.add(candidate); + } + } + } + } + } + } + this.displayTrigger = true; + + // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are + // used in different pipelines, to avoid wrong parameters in the + // headers) + handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); + + this.project = project; + this.firstProject = firstProject; + } + + /** + * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. + * This method is only called for a starting project in a pipeline view, + * therefore save it as such. + * + * @param p + * project to be wrapped. + * @return + * possibly null. + */ + public static ProjectForm as(AbstractProject p) { + return p != null ? new ProjectForm(p) : null; + } + + public String getName() { + return name; + } + + public int getRow() { + return row; + } + + public int getColumn() { + return col; + } + + /** + * Set the coordinates for this project form. + * + * @param row + * - the row this form is set into + * @param col + * - the column this form is set into + */ + public void setCoords(int row, int col) { + this.row = row; + this.col = col; + } + + public String getHealth() { + return health; + } + + public String getResult() { + return result; + } + + public String getUrl() { + return url; + } + + public String getLastSuccessfulBuildNumber() { + return lastSuccessfulBuildNumber; + } + + public Map getLastSuccessfulBuildParams() { + return lastSuccessfulBuildParams; + } + + public boolean isLastSuccessfulHandled() { + return lastSuccessfulHandled; + } + + public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { + this.lastSuccessfulHandled = lastSuccessfulHandled; + } + + public List getDependencies() { + return dependencies; + } + + /** + * Gets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both + * so all future jobs will not display the trigger. see main.jelly + * + * @return boolean whether to display or not + */ + public Boolean getDisplayTrigger() { + return displayTrigger; + } + + /** + * Sets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both + * so all future jobs will not display the trigger. see main.jelly + * + * @param display + * - boolean to indicate whether the trigger button should be shown + */ + public void setDisplayTrigger(final Boolean display) { + displayTrigger = display; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ProjectForm other = (ProjectForm) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + return true; + } + + public int getId() { + return name.hashCode(); + } + + /** + * Project as JSON + * + * @return JSON string + */ + @JavaScriptMethod + public String asJSON() { + return ProjectJSONBuilder.asJSON(new ProjectForm(project)); + } + + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + public Map filterSensitiveBuildVariables(AbstractBuild build) { + return BuildUtil.getUnsensitiveParameters(build); + } + + /** + * Correct the last successful build settings of this form, if that build is + * not on the current BuildPipelineView (when Jobs are used on multiple + * Pipelines) + * + * @param buildGrids + * - the build-grids present on this view + */ + public void correctLastSuccessfulBuilds(Iterable buildGrids) { + if (!isLastSuccessfulHandled()) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + // Determine whether the lastSuccessful Build belongs to this View + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } + } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + } + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); + setLastSuccessfulHandled(true); + } + } + + /** + * Set build-number and build-params for last successful build. + * + * @param lastSuccessfulBuild + * - the last successful build (can be null) + */ + private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() + : filterSensitiveBuildVariables(lastSuccessfulBuild); + } +} From 4fdb3f3de595d14648e328e34f9290cb26875cf8 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 2 Sep 2014 21:16:18 +0200 Subject: [PATCH 11/21] revert back changes (to resolve conflicts) --- .../plugin/buildpipeline/BuildForm.java | 63 --- .../buildpipeline/BuildPipelineView.java | 188 ++++----- .../plugin/buildpipeline/PipelineBuild.java | 168 ++------ .../plugin/buildpipeline/ProjectForm2.java | 367 ------------------ 4 files changed, 95 insertions(+), 691 deletions(-) delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 94351def..33960c70 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -8,19 +8,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; - /** * @author Centrum Systems * @@ -156,34 +150,6 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); - } else { - // try to see if the build went into queueing by searching for the queue-item from the upstream-build - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { - updated = true; - } - } - } - return updated; - } - - /** - * - * @param queueId the id of the queue-item that was cancelled - * - * @return is the queued item cancelled. - */ - @JavaScriptMethod - public boolean cancelQueued(final int queueId) { - boolean updated = false; - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (qentry.getQueueId() == queueId) { - updated = true; - } } return updated; } @@ -196,10 +162,6 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } - public String getFullBuildName() { - return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); - } - @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -208,10 +170,6 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - - public Map getFilteredParameters() { - return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); - } public ArrayList getParameterList() { return parameters; @@ -221,25 +179,4 @@ public Integer getProjectId() { return projectId; } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - private Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index c3fb07f6..316179e5 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -65,12 +65,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; -import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -79,7 +80,6 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; /** * This view displays the set of jobs that are related @@ -395,11 +395,13 @@ public boolean hasDownstreamProjects(final AbstractProject currentProject) public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid projectGrid = gridBuilder.build(this); - if (projectGrid.isEmpty()) { + final ProjectGrid project = gridBuilder.build(this); + if (project.isEmpty()) { return null; } - return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm( + project, + Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); } /** @@ -429,21 +431,22 @@ public String getProjectURL(final AbstractProject project) throws URISynta */ @JavaScriptMethod public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final ItemGroup context = getOwnerItemGroup(); + final ItemGroup context = getOwnerItemGroup(); final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); // Get parameters from upstream build - if (upstreamBuild == null) { - return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - } else { + if (upstreamBuild != null) { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } + Action buildParametersAction = null; + if (upstreamBuild != null) { + buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + } + + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -461,35 +464,28 @@ public int retryBuild(final String triggerProjectName) { } /** - * Trigger a manual build - * - * @param buildNumber - * the number of the build of the current project - * @param upstreamBuildNumber - * upstream build number - * @param triggerProjectName - * project that is triggered - * @param upstreamProjectName - * upstream project - * @return next build number that has been scheduled + * @param externalizableId + * the externalizableId + * @return the number of re-run build */ @JavaScriptMethod - public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, - final String upstreamProjectName) { - LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ - final ItemGroup context = getOwnerItemGroup(); - final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); - final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); - - // Retrieve the current build and then clear the upstream cause for the current link - final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); - - LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - - return nextBuildNumber; + public int rerunBuild(final String externalizableId) { + LOGGER.fine("Running build again: " + externalizableId); //$NON-NLS-1$ + final AbstractBuild triggerBuild = (AbstractBuild) Run.fromExternalizableId(externalizableId); + final AbstractProject triggerProject = triggerBuild.getProject(); + final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), + removeUserIdCauseActions(triggerBuild.getActions())); + + AbstractBuild result = triggerBuild; + try { + result = (AbstractBuild) future.get(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } catch (final ExecutionException e) { + e.printStackTrace(); + } + + return result.getNumber(); } /** @@ -517,33 +513,6 @@ public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumb return build; } - /** - * Schedules a build to start (no upstream build). - * - * @param triggerProject - * - Schedule a build to start on this AbstractProject - * @param delay - * - The delay for triggering the build (handle queueing) - * @return next build number - */ - private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { - LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = null; - final List buildActions = new ArrayList(); - final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) - // available: - causeAction.getCauses().add(upstreamCause); - buildActions.add(causeAction); - - final ParametersAction parametersAction = new ParametersAction(); - buildActions.add(parametersAction); - - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); - - return triggerProject.getNextBuildNumber(); - } - /** * Schedules a build to start. * @@ -555,50 +524,52 @@ private int triggerFirstBuild(final AbstractProject triggerProject, final * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. - * @param delay - * - The delay for triggering the build (handle queueing) * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction, final int delay) { + final Action buildParametersAction) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); + final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction - : new ParametersAction(); + ParametersAction parametersAction = + buildParametersAction instanceof ParametersAction + ? (ParametersAction) buildParametersAction : new ParametersAction(); - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + if (upstreamBuild != null) { - if (configs == null) { - LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - } - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - // Exchange base and overlay (last param wins) ! - parametersAction = mergeParameters((ParametersAction) action, parametersAction); - } else { - buildActions.add(action); + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } + + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + parametersAction = mergeParameters(parametersAction, (ParametersAction) action); + } else { + buildActions.add(action); + } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } } buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } @@ -914,9 +885,7 @@ public boolean contains(final TopLevelItem item) { public void onJobRenamed(final Item item, final String oldName, final String newName) { LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); try { - if (gridBuilder != null) { - gridBuilder.onJobRenamed(this, item, oldName, newName); - } + gridBuilder.onJobRenamed(this, item, oldName, newName); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); } @@ -942,39 +911,16 @@ private static final class LinkStyle { @Override public boolean hasPermission(final Permission p) { boolean display = true; - // tester la liste vide seulement en lecture + //tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - // Pas en lecture => permission standard + //Pas en lecture => permission standard display = super.hasPermission(p); } return display; - } - - /** - * Check for M2ReleaseAction, if present, then provide an icon on top - * - * @return boolean - whether the first job is a m2release job - */ - public boolean isM2Release() { - if (Jenkins.getInstance().getPlugin("m2release") == null) { - return false; - } - final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); - final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), - AbstractProject.class); - if (project != null) { - // If a M2ReleaseAction is found - final List actions = project.getActions(M2ReleaseAction.class); - if (actions != null && !actions.isEmpty()) { - return true; - } - } - return false; - } - + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 25ad6ecd..4d6ed4c6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import jenkins.model.Jenkins; +import hudson.model.Hudson; import hudson.model.Result; import java.text.DateFormat; @@ -45,10 +45,6 @@ import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; -import hudson.model.Queue.WaitingItem; - /** * @author Centrum Systems * @@ -184,7 +180,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -285,25 +281,21 @@ private String getBuildResult(final AbstractBuild build) { */ private String getPendingStatus() { - // Retrieve the correct queued item based on the upstream build (display name + queue id) - if (upstreamBuild != null) { - // Search for Queue-Entry based on the upstream build - final QueueEntry queueEntry = QueueUtil.getQueueEntry(upstreamBuild); - if (queueEntry != null && QueueUtil.getQueuedItem(project, queueEntry) != null) { - return HudsonResult.QUEUED.toString(); - } - } - + String pendingStatus = HudsonResult.PENDING.toString(); final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); - if (upstreamPB != null && this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { - if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { - return HudsonResult.MANUAL.toString(); + + if (upstreamPB != null) { + if (this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || + getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { + pendingStatus = HudsonResult.MANUAL.toString(); + } } } } - return HudsonResult.PENDING.toString(); + return pendingStatus; } @@ -324,10 +316,12 @@ public PipelineBuild getUpstreamPipelineBuild() { upstreamBuildName = ""; } if (upstreamProjects.size() > 0) { - for (AbstractProject upstreamProject : upstreamProjects) { - if (upstreamProject.getName().equals(upstreamBuildName)) { - previousProject = upstreamProject; - break; + if (isManualTrigger()) { + for (AbstractProject upstreamProject : upstreamProjects) { + if (upstreamProject.getName().equals(upstreamBuildName)) { + previousProject = upstreamProject; + break; + } } } if (previousProject == null) { @@ -352,35 +346,6 @@ public String getBuildDuration() { } } - /** - * Returns the current wait duration (when queued). - * - * @return - Current wait duration or an empty String if no queueing in - * progress is null. - */ - public String getQueueWaitDuration() { - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); - } else { - return ""; //$NON-NLS-1$ - } - } - - /** - * Returns the current id of the queue item. - * - * @return - Current id of the queue item - */ - public Integer getQueueId() { - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - return wItem.id; - } else { - return 0; //$NON-NLS-1$ - } - } - /** * {@inheritDoc} */ @@ -466,7 +431,7 @@ public String getPipelineVersion() { public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Jenkins.getInstance().isUseSecurity()) { + if (!Hudson.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -481,15 +446,14 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) - && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); + return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) - && !"QUEUED".equals(getCurrentBuildResult()) - && hasBuildPermission() && getUpstreamPipelineBuild().getCurrentBuild() != null - && QueueUtil.getQueueEntry(currentBuild) == null; + return !isReadyToBeManuallyBuilt() + && !"PENDING".equals(getCurrentBuildResult()) + && !"BUILDING".equals(getCurrentBuildResult()) + && hasBuildPermission(); } /** @@ -506,51 +470,6 @@ private boolean upstreamBuildUnstable() { return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * - * @return whether this is the latest build of this pipeline view - */ - public boolean isLatestBuild() { - final AbstractBuild lastBuild = project.getLastBuild(); - if (currentBuild == null || lastBuild == null) { - return false; - } - // now check for last build on this pipeline view - // how do we find out, if the last build is run on this view? - return currentBuild.getNumber() == lastBuild.getNumber(); - } - - /** - * - * @return whether the upstream build is the latest for this pipeline view - */ - public boolean isUpstreamBuildLatest() { - if (upstreamBuild == null || getUpstreamPipelineBuild() == null) { - return false; - } - final AbstractProject upstreamPBProject = getUpstreamPipelineBuild().getProject(); - if (upstreamPBProject == null || upstreamPBProject.getLastBuild() == null) { - return false; - } - final AbstractBuild lastBuild = upstreamPBProject.getLastBuild(); - if (lastBuild == null) { - return false; - } - return upstreamBuild.getNumber() == lastBuild.getNumber(); - } - - /** - * - * @return whether the upstream build is the latest successful one for this - * pipeline view - */ - public boolean isUpstreamBuildLatestSuccess() { - if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null - || getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild() == null) { - return false; - } - return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild().getNumber(); - } /** * Determine if the project is triggered manually, regardless of the state of its upstream builds @@ -561,13 +480,6 @@ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); - } else { - for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { - manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); - if (manualTrigger) { - break; - } - } } return manualTrigger; } @@ -596,35 +508,11 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartDate = ""; //$NON-NLS-1$ + String formattedStartTime = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); - } - return formattedStartDate; - } - - /** - * @return Formatted queued for time - */ - public String getFormattedQueuedForTime() { - String formattedQueuedForTime = ""; //$NON-NLS-1$ - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedQueuedForTime; - } - - /** - * @return Formatted queued for date - */ - public String getFormattedQueuedForDate() { - String formattedQueuedForDate = ""; //$NON-NLS-1$ - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); - } - return formattedQueuedForDate; + return formattedStartTime; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java deleted file mode 100644 index 6c23d929..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java +++ /dev/null @@ -1,367 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.buildpipeline; - -import au.com.centrumsystems.hudson.plugin.util.BuildUtil; -import hudson.Util; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import jenkins.model.Jenkins; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -import hudson.plugins.parameterizedtrigger.SubProjectsAction; -import org.kohsuke.stapler.bind.JavaScriptMethod; - -/** - * @author Centrum Systems - * - * Representation of a set of projects - * - */ -public class ProjectForm { - /** - * project name - */ - private final String name; - /** - * last build result - */ - private final String result; - /** - * overall health - */ - private final String health; - /** - * project url - */ - private final String url; - /** - * downstream projects - */ - private final List dependencies; - /** - * display manual build - */ - private Boolean displayTrigger; - - /** - * the latest successful build number - */ - private String lastSuccessfulBuildNumber; - - /** - * the parameters used in the last successful build - */ - private Map lastSuccessfulBuildParams; - - /** - * Whether last successful build is already handled - */ - private boolean lastSuccessfulHandled; - - /** - * keep reference to the project so that we can update it - */ - private final AbstractProject project; - - /** - * keep reference to the first project in the current pipeline view - */ - private final AbstractProject firstProject; - - /** - * Hold the row this project is placed in (handle duplicate project-ids) - */ - private int row; - - /** - * Hold the column this project is placed in (handle duplicate project-ids) - */ - private int col; - - /** - * @param name - * project name - */ - public ProjectForm(final String name) { - this.name = name; - result = ""; - health = ""; - url = ""; - lastSuccessfulBuildNumber = ""; - lastSuccessfulBuildParams = new HashMap(); - dependencies = new ArrayList(); - this.displayTrigger = true; - project = null; - firstProject = null; - } - - /** - * @param project - * project - */ - public ProjectForm(final AbstractProject project) { - this(project, new LinkedHashSet>(Arrays.asList(project)), project); - } - - /** - * @param project - * - the project to wrap - * @param parentPath - * already traversed projects - * @param firstProject - * - the first project associated with the grid for this - * projectform - */ - public ProjectForm(final AbstractProject project, final Collection> parentPath, - final AbstractProject firstProject) { - final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); - - name = pipelineBuild.getProject().getFullName(); - result = pipelineBuild.getCurrentBuildResult(); - health = pipelineBuild.getProject().getBuildHealth().getIconUrl().replaceAll("\\.gif", "\\.png"); - url = pipelineBuild.getProjectURL(); - dependencies = new ArrayList(); - for (final AbstractProject dependency : project.getDownstreamProjects()) { - final Collection> forkedPath = new LinkedHashSet>(parentPath); - if (forkedPath.add(dependency)) { - dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); - } - } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { - for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { - for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { - for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { - final Collection> forkedPath = new LinkedHashSet>(parentPath); - if (forkedPath.add(dependency)) { - final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); - // if subprojects come back as downstreams someday, no duplicates wanted - if (!dependencies.contains(candidate)) { - dependencies.add(candidate); - } - } - } - } - } - } - this.displayTrigger = true; - - // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are - // used in different pipelines, to avoid wrong parameters in the - // headers) - handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); - - this.project = project; - this.firstProject = firstProject; - } - - /** - * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * This method is only called for a starting project in a pipeline view, - * therefore save it as such. - * - * @param p - * project to be wrapped. - * @return - * possibly null. - */ - public static ProjectForm as(AbstractProject p) { - return p != null ? new ProjectForm(p) : null; - } - - public String getName() { - return name; - } - - public int getRow() { - return row; - } - - public int getColumn() { - return col; - } - - /** - * Set the coordinates for this project form. - * - * @param row - * - the row this form is set into - * @param col - * - the column this form is set into - */ - public void setCoords(int row, int col) { - this.row = row; - this.col = col; - } - - public String getHealth() { - return health; - } - - public String getResult() { - return result; - } - - public String getUrl() { - return url; - } - - public String getLastSuccessfulBuildNumber() { - return lastSuccessfulBuildNumber; - } - - public Map getLastSuccessfulBuildParams() { - return lastSuccessfulBuildParams; - } - - public boolean isLastSuccessfulHandled() { - return lastSuccessfulHandled; - } - - public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { - this.lastSuccessfulHandled = lastSuccessfulHandled; - } - - public List getDependencies() { - return dependencies; - } - - /** - * Gets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both - * so all future jobs will not display the trigger. see main.jelly - * - * @return boolean whether to display or not - */ - public Boolean getDisplayTrigger() { - return displayTrigger; - } - - /** - * Sets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both - * so all future jobs will not display the trigger. see main.jelly - * - * @param display - * - boolean to indicate whether the trigger button should be shown - */ - public void setDisplayTrigger(final Boolean display) { - displayTrigger = display; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ProjectForm other = (ProjectForm) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - return true; - } - - public int getId() { - return name.hashCode(); - } - - /** - * Project as JSON - * - * @return JSON string - */ - @JavaScriptMethod - public String asJSON() { - return ProjectJSONBuilder.asJSON(new ProjectForm(project)); - } - - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - public Map filterSensitiveBuildVariables(AbstractBuild build) { - return BuildUtil.getUnsensitiveParameters(build); - } - - /** - * Correct the last successful build settings of this form, if that build is - * not on the current BuildPipelineView (when Jobs are used on multiple - * Pipelines) - * - * @param buildGrids - * - the build-grids present on this view - */ - public void correctLastSuccessfulBuilds(Iterable buildGrids) { - if (!isLastSuccessfulHandled()) { - AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); - // Determine whether the lastSuccessful Build belongs to this View - outer: while (lastSuccessfulBuild != null) { - // Search the buildGrid for the lastSuccessfulBuild given above - BuildForm buildForm = null; - for (BuildGrid buildGrid : buildGrids) { - buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); - if (buildForm != null) { - break outer; - } - } - lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); - } - // Set the (may be) newly found last successful build - handleLastSuccessfulBuild(lastSuccessfulBuild); - setLastSuccessfulHandled(true); - } - } - - /** - * Set build-number and build-params for last successful build. - * - * @param lastSuccessfulBuild - * - the last successful build (can be null) - */ - private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() - : filterSensitiveBuildVariables(lastSuccessfulBuild); - } -} From d4d48a65bd5bda7f4c5feb12eb846141e4387e06 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 2 Sep 2014 21:23:36 +0200 Subject: [PATCH 12/21] retry changes --- .../plugin/buildpipeline/BuildForm.java | 63 ++++++ .../buildpipeline/BuildPipelineView.java | 188 +++++++++++------- .../plugin/buildpipeline/PipelineBuild.java | 168 +++++++++++++--- .../plugin/buildpipeline/ProjectForm.java | 164 ++++++++++++--- 4 files changed, 465 insertions(+), 118 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 33960c70..94351def 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -8,13 +8,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; + /** * @author Centrum Systems * @@ -150,6 +156,34 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing by searching for the queue-item from the upstream-build + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { + updated = true; + } + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry.getQueueId() == queueId) { + updated = true; + } } return updated; } @@ -162,6 +196,10 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -170,6 +208,10 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } + + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } public ArrayList getParameterList() { return parameters; @@ -179,4 +221,25 @@ public Integer getProjectId() { return projectId; } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 316179e5..c3fb07f6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -65,13 +65,12 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -80,6 +79,7 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; /** * This view displays the set of jobs that are related @@ -395,13 +395,11 @@ public boolean hasDownstreamProjects(final AbstractProject currentProject) public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid project = gridBuilder.build(this); - if (project.isEmpty()) { + final ProjectGrid projectGrid = gridBuilder.build(this); + if (projectGrid.isEmpty()) { return null; } - return new BuildPipelineForm( - project, - Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); } /** @@ -431,22 +429,21 @@ public String getProjectURL(final AbstractProject project) throws URISynta */ @JavaScriptMethod public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final ItemGroup context = getOwnerItemGroup(); + final ItemGroup context = getOwnerItemGroup(); final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); // Get parameters from upstream build - if (upstreamBuild != null) { + if (upstreamBuild == null) { + return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + } else { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } - Action buildParametersAction = null; - if (upstreamBuild != null) { - buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - } - - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -464,28 +461,35 @@ public int retryBuild(final String triggerProjectName) { } /** - * @param externalizableId - * the externalizableId - * @return the number of re-run build + * Trigger a manual build + * + * @param buildNumber + * the number of the build of the current project + * @param upstreamBuildNumber + * upstream build number + * @param triggerProjectName + * project that is triggered + * @param upstreamProjectName + * upstream project + * @return next build number that has been scheduled */ @JavaScriptMethod - public int rerunBuild(final String externalizableId) { - LOGGER.fine("Running build again: " + externalizableId); //$NON-NLS-1$ - final AbstractBuild triggerBuild = (AbstractBuild) Run.fromExternalizableId(externalizableId); - final AbstractProject triggerProject = triggerBuild.getProject(); - final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), - removeUserIdCauseActions(triggerBuild.getActions())); - - AbstractBuild result = triggerBuild; - try { - result = (AbstractBuild) future.get(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } catch (final ExecutionException e) { - e.printStackTrace(); - } - - return result.getNumber(); + public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, + final String upstreamProjectName) { + LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + + // Retrieve the current build and then clear the upstream cause for the current link + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + + LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + + return nextBuildNumber; } /** @@ -513,6 +517,33 @@ public int rerunBuild(final String externalizableId) { return build; } + /** + * Schedules a build to start (no upstream build). + * + * @param triggerProject + * - Schedule a build to start on this AbstractProject + * @param delay + * - The delay for triggering the build (handle queueing) + * @return next build number + */ + private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { + LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ + final Cause.UpstreamCause upstreamCause = null; + final List buildActions = new ArrayList(); + final CauseAction causeAction = new CauseAction(new MyUserIdCause()); + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: + causeAction.getCauses().add(upstreamCause); + buildActions.add(causeAction); + + final ParametersAction parametersAction = new ParametersAction(); + buildActions.add(parametersAction); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + + return triggerProject.getNextBuildNumber(); + } + /** * Schedules a build to start. * @@ -524,52 +555,50 @@ public int rerunBuild(final String externalizableId) { * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * @param delay + * - The delay for triggering the build (handle queueing) * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction) { + final Action buildParametersAction, final int delay) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); + final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = - buildParametersAction instanceof ParametersAction - ? (ParametersAction) buildParametersAction : new ParametersAction(); - - if (upstreamBuild != null) { + ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction + : new ParametersAction(); + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - - if (configs == null) { - LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - } + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - parametersAction = mergeParameters(parametersAction, (ParametersAction) action); - } else { - buildActions.add(action); - } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + // Exchange base and overlay (last param wins) ! + parametersAction = mergeParameters((ParametersAction) action, parametersAction); + } else { + buildActions.add(action); } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } } buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } @@ -885,7 +914,9 @@ public boolean contains(final TopLevelItem item) { public void onJobRenamed(final Item item, final String oldName, final String newName) { LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); try { - gridBuilder.onJobRenamed(this, item, oldName, newName); + if (gridBuilder != null) { + gridBuilder.onJobRenamed(this, item, oldName, newName); + } } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); } @@ -911,16 +942,39 @@ private static final class LinkStyle { @Override public boolean hasPermission(final Permission p) { boolean display = true; - //tester la liste vide seulement en lecture + // tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - //Pas en lecture => permission standard + // Pas en lecture => permission standard display = super.hasPermission(p); } return display; - } + } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class); + if (project != null) { + // If a M2ReleaseAction is found + final List actions = project.getActions(M2ReleaseAction.class); + if (actions != null && !actions.isEmpty()) { + return true; + } + } + return false; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 4d6ed4c6..25ad6ecd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Hudson; +import jenkins.model.Jenkins; import hudson.model.Result; import java.text.DateFormat; @@ -45,6 +45,10 @@ import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; +import hudson.model.Queue.WaitingItem; + /** * @author Centrum Systems * @@ -180,7 +184,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -281,21 +285,25 @@ private String getBuildResult(final AbstractBuild build) { */ private String getPendingStatus() { - String pendingStatus = HudsonResult.PENDING.toString(); - final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + // Retrieve the correct queued item based on the upstream build (display name + queue id) + if (upstreamBuild != null) { + // Search for Queue-Entry based on the upstream build + final QueueEntry queueEntry = QueueUtil.getQueueEntry(upstreamBuild); + if (queueEntry != null && QueueUtil.getQueuedItem(project, queueEntry) != null) { + return HudsonResult.QUEUED.toString(); + } + } - if (upstreamPB != null) { - if (this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || - getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { - if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { - pendingStatus = HudsonResult.MANUAL.toString(); - } + final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + if (upstreamPB != null && this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { + return HudsonResult.MANUAL.toString(); } } } - return pendingStatus; + return HudsonResult.PENDING.toString(); } @@ -316,12 +324,10 @@ public PipelineBuild getUpstreamPipelineBuild() { upstreamBuildName = ""; } if (upstreamProjects.size() > 0) { - if (isManualTrigger()) { - for (AbstractProject upstreamProject : upstreamProjects) { - if (upstreamProject.getName().equals(upstreamBuildName)) { - previousProject = upstreamProject; - break; - } + for (AbstractProject upstreamProject : upstreamProjects) { + if (upstreamProject.getName().equals(upstreamBuildName)) { + previousProject = upstreamProject; + break; } } if (previousProject == null) { @@ -346,6 +352,35 @@ public String getBuildDuration() { } } + /** + * Returns the current wait duration (when queued). + * + * @return - Current wait duration or an empty String if no queueing in + * progress is null. + */ + public String getQueueWaitDuration() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); + } else { + return ""; //$NON-NLS-1$ + } + } + + /** + * Returns the current id of the queue item. + * + * @return - Current id of the queue item + */ + public Integer getQueueId() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return wItem.id; + } else { + return 0; //$NON-NLS-1$ + } + } + /** * {@inheritDoc} */ @@ -431,7 +466,7 @@ public String getPipelineVersion() { public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Hudson.getInstance().isUseSecurity()) { + if (!Jenkins.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -446,14 +481,15 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); + return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) + && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() - && !"PENDING".equals(getCurrentBuildResult()) - && !"BUILDING".equals(getCurrentBuildResult()) - && hasBuildPermission(); + return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) + && !"QUEUED".equals(getCurrentBuildResult()) + && hasBuildPermission() && getUpstreamPipelineBuild().getCurrentBuild() != null + && QueueUtil.getQueueEntry(currentBuild) == null; } /** @@ -470,6 +506,51 @@ private boolean upstreamBuildUnstable() { return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); } + /** + * + * @return whether this is the latest build of this pipeline view + */ + public boolean isLatestBuild() { + final AbstractBuild lastBuild = project.getLastBuild(); + if (currentBuild == null || lastBuild == null) { + return false; + } + // now check for last build on this pipeline view + // how do we find out, if the last build is run on this view? + return currentBuild.getNumber() == lastBuild.getNumber(); + } + + /** + * + * @return whether the upstream build is the latest for this pipeline view + */ + public boolean isUpstreamBuildLatest() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null) { + return false; + } + final AbstractProject upstreamPBProject = getUpstreamPipelineBuild().getProject(); + if (upstreamPBProject == null || upstreamPBProject.getLastBuild() == null) { + return false; + } + final AbstractBuild lastBuild = upstreamPBProject.getLastBuild(); + if (lastBuild == null) { + return false; + } + return upstreamBuild.getNumber() == lastBuild.getNumber(); + } + + /** + * + * @return whether the upstream build is the latest successful one for this + * pipeline view + */ + public boolean isUpstreamBuildLatestSuccess() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null + || getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild() == null) { + return false; + } + return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild().getNumber(); + } /** * Determine if the project is triggered manually, regardless of the state of its upstream builds @@ -480,6 +561,13 @@ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); + } else { + for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { + manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); + if (manualTrigger) { + break; + } + } } return manualTrigger; } @@ -508,11 +596,35 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartTime = ""; //$NON-NLS-1$ + String formattedStartDate = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); + formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedStartTime; + return formattedStartDate; + } + + /** + * @return Formatted queued for time + */ + public String getFormattedQueuedForTime() { + String formattedQueuedForTime = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForTime; + } + + /** + * @return Formatted queued for date + */ + public String getFormattedQueuedForDate() { + String formattedQueuedForDate = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForDate; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 99996b78..6c23d929 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -4,7 +4,7 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Hudson; +import jenkins.model.Jenkins; import java.util.ArrayList; import java.util.Arrays; @@ -52,18 +52,38 @@ public class ProjectForm { /** * the latest successful build number */ - private final String lastSuccessfulBuildNumber; + private String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private final Map lastSuccessfulBuildParams; + private Map lastSuccessfulBuildParams; + + /** + * Whether last successful build is already handled + */ + private boolean lastSuccessfulHandled; /** * keep reference to the project so that we can update it */ private final AbstractProject project; + /** + * keep reference to the first project in the current pipeline view + */ + private final AbstractProject firstProject; + + /** + * Hold the row this project is placed in (handle duplicate project-ids) + */ + private int row; + + /** + * Hold the column this project is placed in (handle duplicate project-ids) + */ + private int col; + /** * @param name * project name @@ -78,6 +98,7 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; + firstProject = null; } /** @@ -85,16 +106,20 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { - this(project, new LinkedHashSet>(Arrays.asList(project))); + this(project, new LinkedHashSet>(Arrays.asList(project)), project); } /** * @param project - * project + * - the project to wrap * @param parentPath * already traversed projects + * @param firstProject + * - the first project associated with the grid for this + * projectform */ - private ProjectForm(final AbstractProject project, final Collection> parentPath) { + public ProjectForm(final AbstractProject project, final Collection> parentPath, + final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); name = pipelineBuild.getProject().getFullName(); @@ -105,16 +130,16 @@ private ProjectForm(final AbstractProject project, final Collection dependency : project.getDownstreamProjects()) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - dependencies.add(new ProjectForm(dependency, forkedPath)); + dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); } } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - final ProjectForm candidate = new ProjectForm(dependency, forkedPath); + final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); @@ -126,16 +151,20 @@ private ProjectForm(final AbstractProject project, final Collection lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = BuildUtil.getUnsensitiveParameters(lastSuccessfulBuild); + // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are + // used in different pipelines, to avoid wrong parameters in the + // headers) + handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); this.project = project; + this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * + * This method is only called for a starting project in a pipeline view, + * therefore save it as such. + * * @param p * project to be wrapped. * @return @@ -149,6 +178,27 @@ public String getName() { return name; } + public int getRow() { + return row; + } + + public int getColumn() { + return col; + } + + /** + * Set the coordinates for this project form. + * + * @param row + * - the row this form is set into + * @param col + * - the column this form is set into + */ + public void setCoords(int row, int col) { + this.row = row; + this.col = col; + } + public String getHealth() { return health; } @@ -169,16 +219,28 @@ public Map getLastSuccessfulBuildParams() { return lastSuccessfulBuildParams; } + public boolean isLastSuccessfulHandled() { + return lastSuccessfulHandled; + } + + public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { + this.lastSuccessfulHandled = lastSuccessfulHandled; + } + public List getDependencies() { return dependencies; } /** - * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Gets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly * * @return boolean whether to display or not @@ -188,11 +250,15 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Sets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly * * @param display @@ -246,4 +312,56 @@ public String asJSON() { return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + public Map filterSensitiveBuildVariables(AbstractBuild build) { + return BuildUtil.getUnsensitiveParameters(build); + } + + /** + * Correct the last successful build settings of this form, if that build is + * not on the current BuildPipelineView (when Jobs are used on multiple + * Pipelines) + * + * @param buildGrids + * - the build-grids present on this view + */ + public void correctLastSuccessfulBuilds(Iterable buildGrids) { + if (!isLastSuccessfulHandled()) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + // Determine whether the lastSuccessful Build belongs to this View + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } + } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + } + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); + setLastSuccessfulHandled(true); + } + } + + /** + * Set build-number and build-params for last successful build. + * + * @param lastSuccessfulBuild + * - the last successful build (can be null) + */ + private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() + : filterSensitiveBuildVariables(lastSuccessfulBuild); + } } From 853ecbb68ec4958cc11e3f40ab512ee8f2a5b949 Mon Sep 17 00:00:00 2001 From: rhirt Date: Thu, 4 Sep 2014 08:17:11 +0200 Subject: [PATCH 13/21] recommit changes (where are the conflicts?) --- .../plugin/buildpipeline/BuildForm.java | 10 +++++++-- .../buildpipeline/BuildPipelineView.java | 6 ++--- .../plugin/buildpipeline/PipelineBuild.java | 9 ++++---- .../plugin/buildpipeline/ProjectForm.java | 19 +++++++++------- .../buildpipeline/BuildPipelineView/bpp.jelly | 22 +++++++++---------- src/main/webapp/css/main.css | 3 ++- src/main/webapp/js/build-pipeline.js | 2 ++ 7 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 94351def..a5caa8cd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -205,14 +205,20 @@ public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); } + + /** + * @return Map the map with the parameters, enriched with a version parameter. + */ public Map getParameters() { - return pipelineBuild.getBuildParameters(); + final Map params = new HashMap(); + params.putAll(pipelineBuild.getBuildParameters()); + return params; } public Map getFilteredParameters() { return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); } - + public ArrayList getParameterList() { return parameters; } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index c3fb07f6..3136074d 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -883,9 +883,9 @@ public Collection getItems() { for (int col = 0; col < grid.getColumns(); col++) { final ProjectForm form = grid.get(row, col); if (form != null) { - final Item item = Jenkins.getInstance().getItem(form.getName(), getOwnerItemGroup()); - if (item != null && item instanceof TopLevelItem) { - items.add((TopLevelItem) item); + final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); + if (item != null) { + items.add(item); } } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 25ad6ecd..67fc5192 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import jenkins.model.Jenkins; +import hudson.model.Queue.WaitingItem; import hudson.model.Result; import java.text.DateFormat; @@ -39,15 +39,14 @@ import java.util.Map; import java.util.logging.Logger; +import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; -import hudson.plugins.parameterizedtrigger.SubProjectsAction; - import au.com.centrumsystems.hudson.plugin.util.QueueEntry; import au.com.centrumsystems.hudson.plugin.util.QueueUtil; -import hudson.model.Queue.WaitingItem; +import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; +import hudson.plugins.parameterizedtrigger.SubProjectsAction; /** * @author Centrum Systems diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 6c23d929..9fafc1ba 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -4,7 +4,6 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import jenkins.model.Jenkins; import java.util.ArrayList; import java.util.Arrays; @@ -14,7 +13,9 @@ import java.util.List; import java.util.Map; +import jenkins.model.Jenkins; import hudson.plugins.parameterizedtrigger.SubProjectsAction; + import org.kohsuke.stapler.bind.JavaScriptMethod; /** @@ -140,7 +141,8 @@ public ProjectForm(final AbstractProject project, final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); - // if subprojects come back as downstreams someday, no duplicates wanted + // if subprojects come back as downstreams someday, + // no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -166,9 +168,9 @@ public ProjectForm(final AbstractProject project, final Collection p) { return p != null ? new ProjectForm(p) : null; @@ -242,7 +244,7 @@ public List getDependencies() { * job which should show the trigger button will render and then a call will * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -260,9 +262,10 @@ public Boolean getDisplayTrigger() { * job which should show the trigger button will render and then a call will * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @param display - * - boolean to indicate whether the trigger button should be shown + * - boolean to indicate whether the trigger button should be + * shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -304,7 +307,7 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 73f96ee5..c096ac62 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -248,17 +248,17 @@ -
- - - Trigger a Pipeline - - - - - Trigger a Pipeline - - +
+ + + Trigger a Pipeline + + + + + Trigger a Pipeline + +
Run
diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index fa7c130f..53075de1 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -91,7 +91,7 @@ /*************************** build card *******************************/ .rounded { height: 70px; - width: 141px; + width: 141px !important; -webkit-opacity: 0.9; opacity: 0.9; @@ -338,6 +338,7 @@ tr.spacerRow td { } .PROJECT .header-name div.header-wrapper { + width: 141px; text-align: left; } diff --git a/src/main/webapp/js/build-pipeline.js b/src/main/webapp/js/build-pipeline.js index 1f92bf6e..1d3366af 100644 --- a/src/main/webapp/js/build-pipeline.js +++ b/src/main/webapp/js/build-pipeline.js @@ -81,6 +81,7 @@ BuildPipeline.prototype = { buildPipeline.viewProxy.triggerManualBuild(upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); + buildPipeline.showProgress(id, {}); }, cancelQueued : function(id, queueId) { var buildPipeline = this; @@ -105,6 +106,7 @@ BuildPipeline.prototype = { buildPipeline.viewProxy.rerunBuild(buildNumber, upstreamBuildNumber, triggerProjectName, upstreamProjectName, function(data){ buildPipeline.updateNextBuildAndShowProgress(id, data.responseObject(), dependencyIds); }); + buildPipeline.showProgress(id, {}); }, showSpinner : function(id){ jQuery("#status-bar-" + id).html('
'); From cccf5bce2e393851a0f0772e008d14e23c299cca Mon Sep 17 00:00:00 2001 From: rhirt Date: Thu, 4 Sep 2014 17:39:33 +0200 Subject: [PATCH 14/21] revert again... --- .../plugin/buildpipeline/BuildForm.java | 73 +------ .../buildpipeline/BuildPipelineView.java | 194 +++++++----------- .../plugin/buildpipeline/PipelineBuild.java | 167 +++------------ .../plugin/buildpipeline/ProjectForm.java | 181 +++------------- 4 files changed, 130 insertions(+), 485 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index a5caa8cd..33960c70 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -8,19 +8,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; - /** * @author Centrum Systems * @@ -156,34 +150,6 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); - } else { - // try to see if the build went into queueing by searching for the queue-item from the upstream-build - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { - updated = true; - } - } - } - return updated; - } - - /** - * - * @param queueId the id of the queue-item that was cancelled - * - * @return is the queued item cancelled. - */ - @JavaScriptMethod - public boolean cancelQueued(final int queueId) { - boolean updated = false; - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (qentry.getQueueId() == queueId) { - updated = true; - } } return updated; } @@ -196,29 +162,15 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } - public String getFullBuildName() { - return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); - } - @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); } - - /** - * @return Map the map with the parameters, enriched with a version parameter. - */ public Map getParameters() { - final Map params = new HashMap(); - params.putAll(pipelineBuild.getBuildParameters()); - return params; + return pipelineBuild.getBuildParameters(); } - - public Map getFilteredParameters() { - return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); - } - + public ArrayList getParameterList() { return parameters; } @@ -227,25 +179,4 @@ public Integer getProjectId() { return projectId; } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - private Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 3136074d..316179e5 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -65,12 +65,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; -import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -79,7 +80,6 @@ import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; /** * This view displays the set of jobs that are related @@ -395,11 +395,13 @@ public boolean hasDownstreamProjects(final AbstractProject currentProject) public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid projectGrid = gridBuilder.build(this); - if (projectGrid.isEmpty()) { + final ProjectGrid project = gridBuilder.build(this); + if (project.isEmpty()) { return null; } - return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm( + project, + Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); } /** @@ -429,21 +431,22 @@ public String getProjectURL(final AbstractProject project) throws URISynta */ @JavaScriptMethod public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final ItemGroup context = getOwnerItemGroup(); + final ItemGroup context = getOwnerItemGroup(); final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); // Get parameters from upstream build - if (upstreamBuild == null) { - return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - } else { + if (upstreamBuild != null) { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } + Action buildParametersAction = null; + if (upstreamBuild != null) { + buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + } + + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -461,35 +464,28 @@ public int retryBuild(final String triggerProjectName) { } /** - * Trigger a manual build - * - * @param buildNumber - * the number of the build of the current project - * @param upstreamBuildNumber - * upstream build number - * @param triggerProjectName - * project that is triggered - * @param upstreamProjectName - * upstream project - * @return next build number that has been scheduled + * @param externalizableId + * the externalizableId + * @return the number of re-run build */ @JavaScriptMethod - public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, - final String upstreamProjectName) { - LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ - final ItemGroup context = getOwnerItemGroup(); - final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); - final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); - - // Retrieve the current build and then clear the upstream cause for the current link - final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); - - LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - - return nextBuildNumber; + public int rerunBuild(final String externalizableId) { + LOGGER.fine("Running build again: " + externalizableId); //$NON-NLS-1$ + final AbstractBuild triggerBuild = (AbstractBuild) Run.fromExternalizableId(externalizableId); + final AbstractProject triggerProject = triggerBuild.getProject(); + final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), + removeUserIdCauseActions(triggerBuild.getActions())); + + AbstractBuild result = triggerBuild; + try { + result = (AbstractBuild) future.get(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } catch (final ExecutionException e) { + e.printStackTrace(); + } + + return result.getNumber(); } /** @@ -517,33 +513,6 @@ public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumb return build; } - /** - * Schedules a build to start (no upstream build). - * - * @param triggerProject - * - Schedule a build to start on this AbstractProject - * @param delay - * - The delay for triggering the build (handle queueing) - * @return next build number - */ - private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { - LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = null; - final List buildActions = new ArrayList(); - final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) - // available: - causeAction.getCauses().add(upstreamCause); - buildActions.add(causeAction); - - final ParametersAction parametersAction = new ParametersAction(); - buildActions.add(parametersAction); - - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); - - return triggerProject.getNextBuildNumber(); - } - /** * Schedules a build to start. * @@ -555,50 +524,52 @@ private int triggerFirstBuild(final AbstractProject triggerProject, final * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. - * @param delay - * - The delay for triggering the build (handle queueing) * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction, final int delay) { + final Action buildParametersAction) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); + final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction - : new ParametersAction(); + ParametersAction parametersAction = + buildParametersAction instanceof ParametersAction + ? (ParametersAction) buildParametersAction : new ParametersAction(); - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + if (upstreamBuild != null) { - if (configs == null) { - LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - } - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - // Exchange base and overlay (last param wins) ! - parametersAction = mergeParameters((ParametersAction) action, parametersAction); - } else { - buildActions.add(action); + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } + + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + parametersAction = mergeParameters(parametersAction, (ParametersAction) action); + } else { + buildActions.add(action); + } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } } buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } @@ -883,9 +854,9 @@ public Collection getItems() { for (int col = 0; col < grid.getColumns(); col++) { final ProjectForm form = grid.get(row, col); if (form != null) { - final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); - if (item != null) { - items.add(item); + final Item item = Jenkins.getInstance().getItem(form.getName(), getOwnerItemGroup()); + if (item != null && item instanceof TopLevelItem) { + items.add((TopLevelItem) item); } } } @@ -914,9 +885,7 @@ public boolean contains(final TopLevelItem item) { public void onJobRenamed(final Item item, final String oldName, final String newName) { LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); try { - if (gridBuilder != null) { - gridBuilder.onJobRenamed(this, item, oldName, newName); - } + gridBuilder.onJobRenamed(this, item, oldName, newName); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); } @@ -942,39 +911,16 @@ private static final class LinkStyle { @Override public boolean hasPermission(final Permission p) { boolean display = true; - // tester la liste vide seulement en lecture + //tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - // Pas en lecture => permission standard + //Pas en lecture => permission standard display = super.hasPermission(p); } return display; - } - - /** - * Check for M2ReleaseAction, if present, then provide an icon on top - * - * @return boolean - whether the first job is a m2release job - */ - public boolean isM2Release() { - if (Jenkins.getInstance().getPlugin("m2release") == null) { - return false; - } - final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); - final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), - AbstractProject.class); - if (project != null) { - // If a M2ReleaseAction is found - final List actions = project.getActions(M2ReleaseAction.class); - if (actions != null && !actions.isEmpty()) { - return true; - } - } - return false; - } - + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java index 67fc5192..4d6ed4c6 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Queue.WaitingItem; +import hudson.model.Hudson; import hudson.model.Result; import java.text.DateFormat; @@ -39,12 +39,9 @@ import java.util.Map; import java.util.logging.Logger; -import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; @@ -183,7 +180,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -284,25 +281,21 @@ private String getBuildResult(final AbstractBuild build) { */ private String getPendingStatus() { - // Retrieve the correct queued item based on the upstream build (display name + queue id) - if (upstreamBuild != null) { - // Search for Queue-Entry based on the upstream build - final QueueEntry queueEntry = QueueUtil.getQueueEntry(upstreamBuild); - if (queueEntry != null && QueueUtil.getQueuedItem(project, queueEntry) != null) { - return HudsonResult.QUEUED.toString(); - } - } - + String pendingStatus = HudsonResult.PENDING.toString(); final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); - if (upstreamPB != null && this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { - if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { - return HudsonResult.MANUAL.toString(); + + if (upstreamPB != null) { + if (this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || + getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { + pendingStatus = HudsonResult.MANUAL.toString(); + } } } } - return HudsonResult.PENDING.toString(); + return pendingStatus; } @@ -323,10 +316,12 @@ public PipelineBuild getUpstreamPipelineBuild() { upstreamBuildName = ""; } if (upstreamProjects.size() > 0) { - for (AbstractProject upstreamProject : upstreamProjects) { - if (upstreamProject.getName().equals(upstreamBuildName)) { - previousProject = upstreamProject; - break; + if (isManualTrigger()) { + for (AbstractProject upstreamProject : upstreamProjects) { + if (upstreamProject.getName().equals(upstreamBuildName)) { + previousProject = upstreamProject; + break; + } } } if (previousProject == null) { @@ -351,35 +346,6 @@ public String getBuildDuration() { } } - /** - * Returns the current wait duration (when queued). - * - * @return - Current wait duration or an empty String if no queueing in - * progress is null. - */ - public String getQueueWaitDuration() { - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); - } else { - return ""; //$NON-NLS-1$ - } - } - - /** - * Returns the current id of the queue item. - * - * @return - Current id of the queue item - */ - public Integer getQueueId() { - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - return wItem.id; - } else { - return 0; //$NON-NLS-1$ - } - } - /** * {@inheritDoc} */ @@ -465,7 +431,7 @@ public String getPipelineVersion() { public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Jenkins.getInstance().isUseSecurity()) { + if (!Hudson.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -480,15 +446,14 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) - && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); + return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) - && !"QUEUED".equals(getCurrentBuildResult()) - && hasBuildPermission() && getUpstreamPipelineBuild().getCurrentBuild() != null - && QueueUtil.getQueueEntry(currentBuild) == null; + return !isReadyToBeManuallyBuilt() + && !"PENDING".equals(getCurrentBuildResult()) + && !"BUILDING".equals(getCurrentBuildResult()) + && hasBuildPermission(); } /** @@ -505,51 +470,6 @@ private boolean upstreamBuildUnstable() { return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); } - /** - * - * @return whether this is the latest build of this pipeline view - */ - public boolean isLatestBuild() { - final AbstractBuild lastBuild = project.getLastBuild(); - if (currentBuild == null || lastBuild == null) { - return false; - } - // now check for last build on this pipeline view - // how do we find out, if the last build is run on this view? - return currentBuild.getNumber() == lastBuild.getNumber(); - } - - /** - * - * @return whether the upstream build is the latest for this pipeline view - */ - public boolean isUpstreamBuildLatest() { - if (upstreamBuild == null || getUpstreamPipelineBuild() == null) { - return false; - } - final AbstractProject upstreamPBProject = getUpstreamPipelineBuild().getProject(); - if (upstreamPBProject == null || upstreamPBProject.getLastBuild() == null) { - return false; - } - final AbstractBuild lastBuild = upstreamPBProject.getLastBuild(); - if (lastBuild == null) { - return false; - } - return upstreamBuild.getNumber() == lastBuild.getNumber(); - } - - /** - * - * @return whether the upstream build is the latest successful one for this - * pipeline view - */ - public boolean isUpstreamBuildLatestSuccess() { - if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null - || getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild() == null) { - return false; - } - return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild().getNumber(); - } /** * Determine if the project is triggered manually, regardless of the state of its upstream builds @@ -560,13 +480,6 @@ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); - } else { - for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { - manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); - if (manualTrigger) { - break; - } - } } return manualTrigger; } @@ -595,35 +508,11 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartDate = ""; //$NON-NLS-1$ + String formattedStartTime = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); - } - return formattedStartDate; - } - - /** - * @return Formatted queued for time - */ - public String getFormattedQueuedForTime() { - String formattedQueuedForTime = ""; //$NON-NLS-1$ - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedQueuedForTime; - } - - /** - * @return Formatted queued for date - */ - public String getFormattedQueuedForDate() { - String formattedQueuedForDate = ""; //$NON-NLS-1$ - final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); - if (wItem != null) { - formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); - } - return formattedQueuedForDate; + return formattedStartTime; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java index 9fafc1ba..99996b78 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java @@ -4,6 +4,7 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import hudson.model.Hudson; import java.util.ArrayList; import java.util.Arrays; @@ -13,9 +14,7 @@ import java.util.List; import java.util.Map; -import jenkins.model.Jenkins; import hudson.plugins.parameterizedtrigger.SubProjectsAction; - import org.kohsuke.stapler.bind.JavaScriptMethod; /** @@ -53,38 +52,18 @@ public class ProjectForm { /** * the latest successful build number */ - private String lastSuccessfulBuildNumber; + private final String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private Map lastSuccessfulBuildParams; - - /** - * Whether last successful build is already handled - */ - private boolean lastSuccessfulHandled; + private final Map lastSuccessfulBuildParams; /** * keep reference to the project so that we can update it */ private final AbstractProject project; - /** - * keep reference to the first project in the current pipeline view - */ - private final AbstractProject firstProject; - - /** - * Hold the row this project is placed in (handle duplicate project-ids) - */ - private int row; - - /** - * Hold the column this project is placed in (handle duplicate project-ids) - */ - private int col; - /** * @param name * project name @@ -99,7 +78,6 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; - firstProject = null; } /** @@ -107,20 +85,16 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { - this(project, new LinkedHashSet>(Arrays.asList(project)), project); + this(project, new LinkedHashSet>(Arrays.asList(project))); } /** * @param project - * - the project to wrap + * project * @param parentPath * already traversed projects - * @param firstProject - * - the first project associated with the grid for this - * projectform */ - public ProjectForm(final AbstractProject project, final Collection> parentPath, - final AbstractProject firstProject) { + private ProjectForm(final AbstractProject project, final Collection> parentPath) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); name = pipelineBuild.getProject().getFullName(); @@ -131,18 +105,17 @@ public ProjectForm(final AbstractProject project, final Collection dependency : project.getDownstreamProjects()) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); + dependencies.add(new ProjectForm(dependency, forkedPath)); } } - if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { + if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); - // if subprojects come back as downstreams someday, - // no duplicates wanted + final ProjectForm candidate = new ProjectForm(dependency, forkedPath); + // if subprojects come back as downstreams someday, no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -153,24 +126,20 @@ public ProjectForm(final AbstractProject project, final Collection lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = BuildUtil.getUnsensitiveParameters(lastSuccessfulBuild); this.project = project; - this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * This method is only called for a starting project in a pipeline view, - * therefore save it as such. - * + * * @param p - * project to be wrapped. + * project to be wrapped. * @return - * possibly null. + * possibly null. */ public static ProjectForm as(AbstractProject p) { return p != null ? new ProjectForm(p) : null; @@ -180,27 +149,6 @@ public String getName() { return name; } - public int getRow() { - return row; - } - - public int getColumn() { - return col; - } - - /** - * Set the coordinates for this project form. - * - * @param row - * - the row this form is set into - * @param col - * - the column this form is set into - */ - public void setCoords(int row, int col) { - this.row = row; - this.col = col; - } - public String getHealth() { return health; } @@ -221,30 +169,18 @@ public Map getLastSuccessfulBuildParams() { return lastSuccessfulBuildParams; } - public boolean isLastSuccessfulHandled() { - return lastSuccessfulHandled; - } - - public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { - this.lastSuccessfulHandled = lastSuccessfulHandled; - } - public List getDependencies() { return dependencies; } /** - * Gets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both + * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -252,20 +188,15 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button - * will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to - * run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob - * is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will - * be made to 'setDisplayTrigger' to change the value to both + * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @param display - * - boolean to indicate whether the trigger button should be - * shown + * - boolean to indicate whether the trigger button should be shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -307,7 +238,7 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod @@ -315,56 +246,4 @@ public String asJSON() { return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - public Map filterSensitiveBuildVariables(AbstractBuild build) { - return BuildUtil.getUnsensitiveParameters(build); - } - - /** - * Correct the last successful build settings of this form, if that build is - * not on the current BuildPipelineView (when Jobs are used on multiple - * Pipelines) - * - * @param buildGrids - * - the build-grids present on this view - */ - public void correctLastSuccessfulBuilds(Iterable buildGrids) { - if (!isLastSuccessfulHandled()) { - AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); - // Determine whether the lastSuccessful Build belongs to this View - outer: while (lastSuccessfulBuild != null) { - // Search the buildGrid for the lastSuccessfulBuild given above - BuildForm buildForm = null; - for (BuildGrid buildGrid : buildGrids) { - buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); - if (buildForm != null) { - break outer; - } - } - lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); - } - // Set the (may be) newly found last successful build - handleLastSuccessfulBuild(lastSuccessfulBuild); - setLastSuccessfulHandled(true); - } - } - - /** - * Set build-number and build-params for last successful build. - * - * @param lastSuccessfulBuild - * - the last successful build (can be null) - */ - private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() - : filterSensitiveBuildVariables(lastSuccessfulBuild); - } } From 14dbcc2b48d52bd66c44bca5564190fc74ef4f70 Mon Sep 17 00:00:00 2001 From: rhirt Date: Fri, 5 Sep 2014 06:55:07 +0200 Subject: [PATCH 15/21] solve merge conflicts --- .../plugin/buildpipeline/BuildForm.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 33960c70..75d7e0c5 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -8,13 +8,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; + /** * @author Centrum Systems * @@ -150,6 +156,34 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing by searching for the queue-item from the upstream-build + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { + updated = true; + } + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry.getQueueId() == queueId) { + updated = true; + } } return updated; } @@ -162,6 +196,10 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -175,8 +213,33 @@ public ArrayList getParameterList() { return parameters; } + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } + public Integer getProjectId() { return projectId; } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } } From cdfbb6b83f5c6f9a394eb0287cb0bdc12c5cf110 Mon Sep 17 00:00:00 2001 From: rhirt Date: Fri, 5 Sep 2014 21:45:56 +0200 Subject: [PATCH 16/21] Queue handling --- .../plugin/buildpipeline/BuildForm.java | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 75d7e0c5..321b5852 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -2,14 +2,10 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.ItemGroup; import hudson.model.ParametersDefinitionProperty; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -64,40 +60,17 @@ public class BuildForm { * project stringfied list of parameters for the project * */ private final ArrayList parameters; - - /** - * The item group pipeline view belongs to - */ - private final ItemGroup context; - - /** - * @param pipelineBuild - * pipeline build domain used to see the form - * @param context - * item group pipeline view belongs to, used to compute relative item names - */ - public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { - this(context, pipelineBuild, new LinkedHashSet>(Arrays.asList(pipelineBuild.getProject()))); - } - + /** * @param pipelineBuild * pipeline build domain used to see the form - * @param context - * item group pipeline view belongs to, used to compute relative item names - * @param parentPath - * already traversed projects */ - private BuildForm(ItemGroup context, final PipelineBuild pipelineBuild, final Collection> parentPath) { - this.context = context; + public BuildForm(final PipelineBuild pipelineBuild) { this.pipelineBuild = pipelineBuild; status = pipelineBuild.getCurrentBuildResult(); dependencies = new ArrayList(); for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { - final Collection> forkedPath = new LinkedHashSet>(parentPath); - if (forkedPath.add(downstream.getProject())) { - dependencies.add(new BuildForm(context, downstream, forkedPath)); - } + dependencies.add(new BuildForm(downstream)); } id = hashCode(); final AbstractProject project = pipelineBuild.getProject(); @@ -136,7 +109,7 @@ public List getDependencyIds() { */ @JavaScriptMethod public String asJSON() { - return BuildJSONBuilder.asJSON(context, pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); + return BuildJSONBuilder.asJSON(pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); } public int getId() { @@ -208,14 +181,14 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - - public ArrayList getParameterList() { - return parameters; - } public Map getFilteredParameters() { return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); } + + public ArrayList getParameterList() { + return parameters; + } public Integer getProjectId() { return projectId; From 9c4bd1edb614eb05b951386cd7ca3491ef14b601 Mon Sep 17 00:00:00 2001 From: rhirt Date: Fri, 5 Sep 2014 21:59:40 +0200 Subject: [PATCH 17/21] update queuing --- .../plugin/buildpipeline/BuildForm.java | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 321b5852..86de8cb9 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -1,15 +1,10 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.ParametersDefinitionProperty; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; @@ -55,12 +50,6 @@ public class BuildForm { */ private List dependencies = new ArrayList(); - - /** - * project stringfied list of parameters for the project - * */ - private final ArrayList parameters; - /** * @param pipelineBuild * pipeline build domain used to see the form @@ -73,16 +62,7 @@ public BuildForm(final PipelineBuild pipelineBuild) { dependencies.add(new BuildForm(downstream)); } id = hashCode(); - final AbstractProject project = pipelineBuild.getProject(); - projectId = project.getFullName().hashCode(); - final ParametersDefinitionProperty params = project.getProperty(ParametersDefinitionProperty.class); - final ArrayList paramList = new ArrayList(); - if (params != null) { - for (String p : params.getParameterDefinitionNames()) { - paramList.add(p); - } - } - parameters = paramList; + projectId = pipelineBuild.getProject().getFullName().hashCode(); } public String getStatus() { @@ -109,7 +89,7 @@ public List getDependencyIds() { */ @JavaScriptMethod public String asJSON() { - return BuildJSONBuilder.asJSON(pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); + return BuildJSONBuilder.asJSON(pipelineBuild, id, projectId, getDependencyIds()); } public int getId() { @@ -189,7 +169,7 @@ public Map getFilteredParameters() { public ArrayList getParameterList() { return parameters; } - + public Integer getProjectId() { return projectId; } From 9a215a01b437407f9e04659174a43e026775bd59 Mon Sep 17 00:00:00 2001 From: rhirt Date: Fri, 5 Sep 2014 22:14:18 +0200 Subject: [PATCH 18/21] queueing update --- .../plugin/buildpipeline/BuildForm.java | 114 +- .../plugin/buildpipeline/BuildForm2.java | 251 +++++ .../buildpipeline/BuildPipelineView.java | 202 ++-- .../buildpipeline/BuildPipelineView2.java | 980 ++++++++++++++++++ ...PipelineBuild.java => PipelineBuild2.java} | 167 ++- .../{ProjectForm.java => ProjectForm2.java} | 181 +++- .../{bpp.jelly => bpp2.jelly} | 0 7 files changed, 1661 insertions(+), 234 deletions(-) create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java create mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java rename src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/{PipelineBuild.java => PipelineBuild2.java} (72%) rename src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/{ProjectForm.java => ProjectForm2.java} (56%) rename src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/{bpp.jelly => bpp2.jelly} (100%) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 86de8cb9..33960c70 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -1,17 +1,20 @@ package au.com.centrumsystems.hudson.plugin.buildpipeline; import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.ItemGroup; +import hudson.model.ParametersDefinitionProperty; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; - /** * @author Centrum Systems * @@ -50,19 +53,57 @@ public class BuildForm { */ private List dependencies = new ArrayList(); + + /** + * project stringfied list of parameters for the project + * */ + private final ArrayList parameters; + + /** + * The item group pipeline view belongs to + */ + private final ItemGroup context; + /** * @param pipelineBuild * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names */ - public BuildForm(final PipelineBuild pipelineBuild) { + public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { + this(context, pipelineBuild, new LinkedHashSet>(Arrays.asList(pipelineBuild.getProject()))); + } + + /** + * @param pipelineBuild + * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names + * @param parentPath + * already traversed projects + */ + private BuildForm(ItemGroup context, final PipelineBuild pipelineBuild, final Collection> parentPath) { + this.context = context; this.pipelineBuild = pipelineBuild; status = pipelineBuild.getCurrentBuildResult(); dependencies = new ArrayList(); for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { - dependencies.add(new BuildForm(downstream)); + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(downstream.getProject())) { + dependencies.add(new BuildForm(context, downstream, forkedPath)); + } } id = hashCode(); - projectId = pipelineBuild.getProject().getFullName().hashCode(); + final AbstractProject project = pipelineBuild.getProject(); + projectId = project.getFullName().hashCode(); + final ParametersDefinitionProperty params = project.getProperty(ParametersDefinitionProperty.class); + final ArrayList paramList = new ArrayList(); + if (params != null) { + for (String p : params.getParameterDefinitionNames()) { + paramList.add(p); + } + } + parameters = paramList; } public String getStatus() { @@ -89,7 +130,7 @@ public List getDependencyIds() { */ @JavaScriptMethod public String asJSON() { - return BuildJSONBuilder.asJSON(pipelineBuild, id, projectId, getDependencyIds()); + return BuildJSONBuilder.asJSON(context, pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); } public int getId() { @@ -109,34 +150,6 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); - } else { - // try to see if the build went into queueing by searching for the queue-item from the upstream-build - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { - updated = true; - } - } - } - return updated; - } - - /** - * - * @param queueId the id of the queue-item that was cancelled - * - * @return is the queued item cancelled. - */ - @JavaScriptMethod - public boolean cancelQueued(final int queueId) { - boolean updated = false; - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (qentry.getQueueId() == queueId) { - updated = true; - } } return updated; } @@ -149,10 +162,6 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } - public String getFullBuildName() { - return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); - } - @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); @@ -161,38 +170,13 @@ public boolean isManualTrigger() { public Map getParameters() { return pipelineBuild.getBuildParameters(); } - - public Map getFilteredParameters() { - return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); - } public ArrayList getParameterList() { return parameters; } - + public Integer getProjectId() { return projectId; } - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - private Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java new file mode 100644 index 00000000..a5caa8cd --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java @@ -0,0 +1,251 @@ +package au.com.centrumsystems.hudson.plugin.buildpipeline; + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.ItemGroup; +import hudson.model.ParametersDefinitionProperty; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.logging.Logger; + +import org.kohsuke.stapler.bind.JavaScriptMethod; + +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; + +/** + * @author Centrum Systems + * + * Representation of a build results pipeline + * + */ +public class BuildForm { + /** + * logger + */ + private static final Logger LOGGER = Logger.getLogger(BuildForm.class.getName()); + + /** + * status + */ + private String status = ""; + + /** + * pipeline build + */ + private PipelineBuild pipelineBuild; + + /** + * id + */ + private final Integer id; + + /** + * project id used to update project cards + */ + // TODO refactor to get rid of this coupling + private final Integer projectId; + + /** + * downstream builds + */ + private List dependencies = new ArrayList(); + + + /** + * project stringfied list of parameters for the project + * */ + private final ArrayList parameters; + + /** + * The item group pipeline view belongs to + */ + private final ItemGroup context; + + /** + * @param pipelineBuild + * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names + */ + public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { + this(context, pipelineBuild, new LinkedHashSet>(Arrays.asList(pipelineBuild.getProject()))); + } + + /** + * @param pipelineBuild + * pipeline build domain used to see the form + * @param context + * item group pipeline view belongs to, used to compute relative item names + * @param parentPath + * already traversed projects + */ + private BuildForm(ItemGroup context, final PipelineBuild pipelineBuild, final Collection> parentPath) { + this.context = context; + this.pipelineBuild = pipelineBuild; + status = pipelineBuild.getCurrentBuildResult(); + dependencies = new ArrayList(); + for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { + final Collection> forkedPath = new LinkedHashSet>(parentPath); + if (forkedPath.add(downstream.getProject())) { + dependencies.add(new BuildForm(context, downstream, forkedPath)); + } + } + id = hashCode(); + final AbstractProject project = pipelineBuild.getProject(); + projectId = project.getFullName().hashCode(); + final ParametersDefinitionProperty params = project.getProperty(ParametersDefinitionProperty.class); + final ArrayList paramList = new ArrayList(); + if (params != null) { + for (String p : params.getParameterDefinitionNames()) { + paramList.add(p); + } + } + parameters = paramList; + } + + public String getStatus() { + return status; + } + + public List getDependencies() { + return dependencies; + } + + /** + * @return All ids for existing depencies. + */ + public List getDependencyIds() { + final List ids = new ArrayList(); + for (final BuildForm dependency : dependencies) { + ids.add(dependency.getId()); + } + return ids; + } + + /** + * @return convert pipelineBuild as json format. + */ + @JavaScriptMethod + public String asJSON() { + return BuildJSONBuilder.asJSON(context, pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); + } + + public int getId() { + return id; + } + + /** + * + * @param nextBuildNumber + * nextBuildNumber + * @return is the build pipeline updated. + */ + @JavaScriptMethod + public boolean updatePipelineBuild(final int nextBuildNumber) { + boolean updated = false; + final AbstractBuild newBuild = pipelineBuild.getProject().getBuildByNumber(nextBuildNumber); + if (newBuild != null) { + updated = true; + pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing by searching for the queue-item from the upstream-build + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { + updated = true; + } + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry.getQueueId() == queueId) { + updated = true; + } + } + return updated; + } + + public int getNextBuildNumber() { + return pipelineBuild.getProject().getNextBuildNumber(); + } + + public String getRevision() { + return pipelineBuild.getPipelineVersion(); + } + + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + + @JavaScriptMethod + public boolean isManualTrigger() { + return pipelineBuild.isManualTrigger(); + } + + + /** + * @return Map the map with the parameters, enriched with a version parameter. + */ + public Map getParameters() { + final Map params = new HashMap(); + params.putAll(pipelineBuild.getBuildParameters()); + return params; + } + + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } + + public ArrayList getParameterList() { + return parameters; + } + + public Integer getProjectId() { + return projectId; + } + + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } +} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 316179e5..87db278a 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -26,7 +26,6 @@ import com.google.common.base.Splitter; import com.google.common.collect.Iterables; - import com.google.common.collect.Sets; import hudson.Extension; import hudson.model.Action; @@ -57,7 +56,6 @@ import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; import jenkins.model.Jenkins; - import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -69,14 +67,11 @@ import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; - import javax.servlet.ServletException; - import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.bind.JavaScriptMethod; - import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; @@ -90,70 +85,58 @@ * */ public class BuildPipelineView extends View { - /** * @deprecated - * For backward compatibility. Back when we didn't have {@link #gridBuilder}, - * this field stored the first job to display. + * For backward compatibility. Back when we didn't have + * {@link #gridBuilder}, + * this field stored the first job to display. */ @Deprecated private volatile String selectedJob; - /** Builds {@link ProjectGrid} */ private ProjectGridBuilder gridBuilder; - /** noOfDisplayedBuilds. */ private String noOfDisplayedBuilds; - /** buildViewTitle. */ private String buildViewTitle = ""; //$NON-NLS-1$ - /** consoleOutputLinkStyle. */ private String consoleOutputLinkStyle = LinkStyle.LIGHTBOX; - /** URL for custom CSS file */ private String cssUrl = ""; - /** Indicates whether only the latest job will be triggered. **/ private boolean triggerOnlyLatestJob; - /** alwaysAllowManualTrigger. */ private boolean alwaysAllowManualTrigger = true; - /** showPipelineParameters. */ private boolean showPipelineParameters = true; - /** showPipelineParametersInHeaders */ private boolean showPipelineParametersInHeaders; - /** * @deprecated * - * Don't need an input from UI store this + * Don't need an input from UI store this */ - @Deprecated + @Deprecated private boolean startsWithParameters; - /** - * Frequency at which the Build Pipeline Plugin updates the build cards in seconds + * Frequency at which the Build Pipeline Plugin updates the build cards in + * seconds */ private int refreshFrequency = 3; - /** showPipelineDefinitionHeader. */ private boolean showPipelineDefinitionHeader; - /* - * Keep feature flag properties in one place so that it is easy to refactor them out later. + * Keep feature flag properties in one place so that it is easy to refactor + * them out later. */ /* Feature flags - START */ - /* Feature flags - END */ - /** A Logger object is used to log messages */ private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); /** - * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the + * An instance of {@link Cause.UserIdCause} related to the current user. + * Must be transient, or xstream will include it in the * serialization */ private class MyUserIdCause extends Cause.UserIdCause { @@ -163,11 +146,12 @@ private class MyUserIdCause extends Cause.UserIdCause { private User user; /** - * - */ +* +*/ public MyUserIdCause() { try { - // this block can generate a CyclicGraphDetector.CycleDetectedException + // this block can generate a + // CyclicGraphDetector.CycleDetectedException // in cases that I haven't quite figured out yet // also an org.acegisecurity.AccessDeniedException when the user // is not logged in @@ -210,7 +194,6 @@ public boolean equals(final Object o) { if (!(o instanceof Cause.UserIdCause)) { return false; } - return hashCode() == o.hashCode(); } @@ -235,9 +218,8 @@ public void print(final TaskListener listener) { * @param cssUrl * URL for the custom CSS file. */ - public BuildPipelineView(final String name, final String buildViewTitle, - final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final String cssUrl) { + public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, + final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl) { super(name, Hudson.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; @@ -268,39 +250,40 @@ public BuildPipelineView(final String name, final String buildViewTitle, * @param showPipelineDefinitionHeader * Indicates whether the pipeline headers should be shown. * @param refreshFrequency - * Frequency at which the build pipeline plugin refreshes build cards + * Frequency at which the build pipeline plugin refreshes build + * cards * @param cssUrl * URL for the custom CSS file. * @param selectedJob - * the first job name in the pipeline. it can be set to null when gridBuilder is passed. + * the first job name in the pipeline. it can be set to null when + * gridBuilder is passed. */ @DataBoundConstructor public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, - final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, - final int refreshFrequency, final String cssUrl, final String selectedJob) { + final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, + final boolean showPipelineParameters, final boolean showPipelineParametersInHeaders, + final boolean showPipelineDefinitionHeader, final int refreshFrequency, final String cssUrl, final String selectedJob) { this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; this.showPipelineParameters = showPipelineParameters; this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; this.selectedJob = selectedJob; - //not exactly understanding the lifecycle here, but I want a default of 3 - //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) + // not exactly understanding the lifecycle here, but I want a default of + // 3 + // (this is what the class variable is set to 3, if it's 0, set it to + // default, refresh of 0 does not make sense anyway) if (refreshFrequency < 1) { this.refreshFrequency = 3; } else { this.refreshFrequency = refreshFrequency; } - - //for remote api support + // for remote api support if (this.gridBuilder == null) { if (this.selectedJob != null) { this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); } } - if (this.selectedJob == null) { if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); @@ -310,7 +293,7 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P /** * @return - * must be always 'this' + * must be always 'this' */ protected Object readResolve() { if (gridBuilder == null) { @@ -339,9 +322,11 @@ protected void submit(final StaplerRequest req) throws IOException, ServletExcep } /** - * Checks whether the user has a permission to start a new instance of the pipeline. + * Checks whether the user has a permission to start a new instance of the + * pipeline. * - * @return - true: Has Build permission; false: Does not have Build permission + * @return - true: Has Build permission; false: Does not have Build + * permission * @see hudson.model.Item */ public boolean hasBuildPermission() { @@ -351,7 +336,8 @@ public boolean hasBuildPermission() { /** * Checks whether the user has Configure permission for the current project. * - * @return - true: Has Configure permission; false: Does not have Configure permission + * @return - true: Has Configure permission; false: Does not have Configure + * permission */ public boolean hasConfigurePermission() { return this.hasPermission(CONFIGURE); @@ -381,27 +367,26 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { * * @param currentProject * - The project from which we are testing. - * @return - true; has downstream projects; false: does not have downstream projects + * @return - true; has downstream projects; false: does not have downstream + * projects */ public boolean hasDownstreamProjects(final AbstractProject currentProject) { return (getDownstreamProjects(currentProject).size() > 0); } - + /** * Returns BuildPipelineForm containing the build pipeline to display. * - * @return - Representation of the projects and their related builds making up the build pipeline view + * @return - Representation of the projects and their related builds making + * up the build pipeline view */ public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid project = gridBuilder.build(this); if (project.isEmpty()) { return null; } - return new BuildPipelineForm( - project, - Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm(project, Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); } /** @@ -434,9 +419,7 @@ public int triggerManualBuild(final Integer upstreamBuildNumber, final String tr final ItemGroup context = getOwnerItemGroup(); final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); - final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); - // Get parameters from upstream build if (upstreamBuild != null) { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ @@ -445,7 +428,6 @@ public int triggerManualBuild(final Integer upstreamBuildNumber, final String tr if (upstreamBuild != null) { buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); } - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } @@ -459,7 +441,6 @@ public int retryBuild(final String triggerProjectName) { LOGGER.fine("Retrying build again: " + triggerProjectName); //$NON-NLS-1$ final AbstractProject triggerProject = (AbstractProject) super.getJob(triggerProjectName); triggerProject.scheduleBuild(new MyUserIdCause()); - return triggerProject.getNextBuildNumber(); } @@ -475,7 +456,6 @@ public int rerunBuild(final String externalizableId) { final AbstractProject triggerProject = triggerBuild.getProject(); final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), removeUserIdCauseActions(triggerBuild.getActions())); - AbstractBuild result = triggerBuild; try { result = (AbstractBuild) future.get(); @@ -484,23 +464,23 @@ public int rerunBuild(final String externalizableId) { } catch (final ExecutionException e) { e.printStackTrace(); } - return result.getNumber(); } /** - * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. + * Given an AbstractProject and a build number the associated AbstractBuild + * will be retrieved. * * @param buildNo * - Build number * @param project * - AbstractProject - * @return The AbstractBuild associated with the AbstractProject and build number. + * @return The AbstractBuild associated with the AbstractProject and build + * number. */ @SuppressWarnings("unchecked") private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { AbstractBuild build = null; - if (project != null) { for (final AbstractBuild tmpUpBuild : (List>) project.getBuilds()) { if (tmpUpBuild.getNumber() == buildNo) { @@ -509,21 +489,23 @@ public int rerunBuild(final String externalizableId) { } } } - return build; } /** * Schedules a build to start. * - * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. + * The build will take an upstream build as its Cause and a set of + * ParametersAction from the upstream build. * * @param triggerProject * - Schedule a build to start on this AbstractProject * @param upstreamBuild - * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. + * - The upstream AbstractBuild that will be used as a Cause for + * the triggerProject's build. * @param buildParametersAction - * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * - The upstream ParametersAction that will be used as an Action + * for the triggerProject's build. * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, @@ -532,23 +514,18 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); - ParametersAction parametersAction = - buildParametersAction instanceof ParametersAction - ? (ParametersAction) buildParametersAction : new ParametersAction(); - + ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction + : new ParametersAction(); if (upstreamBuild != null) { - - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - if (configs == null) { LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); } - for (final AbstractBuildParameters config : configs) { try { final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); @@ -566,35 +543,34 @@ private int triggerBuild(final AbstractProject triggerProject, final Abstr } } } - buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } /** - * Used to retrieve the parameters from the upstream project build trigger relative to the given downstream project - * @param project the downstream project - * @param upstreamBuild the upstream project build + * Used to retrieve the parameters from the upstream project build trigger + * relative to the given downstream project + * + * @param project + * the downstream project + * @param upstreamBuild + * the upstream project build * @return the trigger config relative to the given downstream project */ private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, - final AbstractBuild upstreamBuild) { - final DescribableList> upstreamProjectPublishersList = - upstreamBuild.getProject().getPublishersList(); - + final AbstractBuild upstreamBuild) { + final DescribableList> upstreamProjectPublishersList = upstreamBuild.getProject() + .getPublishersList(); List configs = null; - final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); if (manualTrigger != null) { - final Set downstreamProjectsNames = - Sets.newHashSet(Splitter.on(",").trimResults().split(manualTrigger.getDownstreamProjectNames())); + final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").trimResults() + .split(manualTrigger.getDownstreamProjectNames())); if (downstreamProjectsNames.contains(project.getName())) { configs = manualTrigger.getConfigs(); } } - final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); if (autoTrigger != null) { for (BuildTriggerConfig config : autoTrigger.getConfigs()) { @@ -604,19 +580,19 @@ private List retrieveUpstreamProjectTriggerConfig(final } } } - return configs; } /** - * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java + * From parameterized trigger plugin + * src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java * * @param base - * One of the two parameters to merge. + * One of the two parameters to merge. * @param overlay - * The other parameters to merge + * The other parameters to merge * @return - * Result of the merge. + * Result of the merge. */ private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { final LinkedHashMap params = new LinkedHashMap(); @@ -630,11 +606,13 @@ private static ParametersAction mergeParameters(final ParametersAction base, fin } /** - * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. + * Checks whether the given {@link Action} contains a reference to a + * {@link UserIdCause} object. * * @param buildAction * the action to check. - * @return true if the action has a reference to a userId cause. + * @return true if the action has a reference to a userId + * cause. */ private boolean isUserIdCauseAction(final Action buildAction) { boolean retval = false; @@ -650,9 +628,12 @@ private boolean isUserIdCauseAction(final Action buildAction) { } /** - * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. - * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the - * downstream build needs to remove the old user action inherited from upstream, and add its own. + * Removes any UserId cause action from the given actions collection. This + * is used by downstream builds that inherit upstream actions. + * The downstream build can be initiated by another user that is different + * from the user who initiated the upstream build, so the + * downstream build needs to remove the old user action inherited from + * upstream, and add its own. * * @param actions * a collection of build actions. @@ -674,9 +655,9 @@ private List removeUserIdCauseActions(final List actions) { */ @Extension public static final class DescriptorImpl extends ViewDescriptor { - /** - * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of + * descriptor impl constructor This empty constructor is required for + * stapler. If you remove this constructor, text name of * "Build Pipeline View" will be not displayed in the "NewView" page */ public DescriptorImpl() { @@ -711,7 +692,6 @@ public ListBoxModel doFillNoOfDisplayedBuildsItems() { noOfBuilds.add("100"); //$NON-NLS-1$ noOfBuilds.add("200"); //$NON-NLS-1$ noOfBuilds.add("500"); //$NON-NLS-1$ - for (final String noOfBuild : noOfBuilds) { options.add(noOfBuild); } @@ -871,7 +851,8 @@ public boolean contains(final TopLevelItem item) { } /** - * If a project name is changed we check if the selected job for this view also needs to be changed. + * If a project name is changed we check if the selected job for this view + * also needs to be changed. * * @param item * - The Item that has been renamed @@ -907,20 +888,19 @@ private static final class LinkStyle { /** this window link style option */ static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ } - + @Override public boolean hasPermission(final Permission p) { boolean display = true; - //tester la liste vide seulement en lecture + // tester la liste vide seulement en lecture if (READ.name.equals(p.name)) { if (this.getItems() == null || this.getItems().isEmpty()) { display = false; } } else { - //Pas en lecture => permission standard + // Pas en lecture => permission standard display = super.hasPermission(p); } - return display; - } + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java new file mode 100644 index 00000000..3136074d --- /dev/null +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java @@ -0,0 +1,980 @@ +/* + * The MIT License + * + * Copyright (c) 2011, Centrum Systems Pty Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package au.com.centrumsystems.hudson.plugin.buildpipeline; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + +import com.google.common.collect.Sets; +import hudson.Extension; +import hudson.model.Action; +import hudson.model.Item; +import hudson.model.ItemGroup; +import hudson.model.ParameterValue; +import hudson.model.TaskListener; +import hudson.model.TopLevelItem; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Cause; +import hudson.model.Cause.UserIdCause; +import hudson.model.CauseAction; +import hudson.model.Descriptor; +import hudson.model.Descriptor.FormException; +import hudson.model.Hudson; +import hudson.model.ParametersAction; +import hudson.model.Run; +import hudson.model.User; +import hudson.model.View; +import hudson.model.ViewDescriptor; +import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; +import hudson.plugins.parameterizedtrigger.BuildTrigger; +import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; +import hudson.security.Permission; +import hudson.tasks.Publisher; +import hudson.util.DescribableList; +import hudson.util.LogTaskListener; +import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.ServletException; + +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.bind.JavaScriptMethod; + +import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; +import au.com.centrumsystems.hudson.plugin.util.BuildUtil; +import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; + +/** + * This view displays the set of jobs that are related + * based on their upstream\downstream relationships as a pipeline. Each + * build pipeline becomes a row on the view. + * + * @author Centrum Systems + * + */ +public class BuildPipelineView extends View { + + /** + * @deprecated + * For backward compatibility. Back when we didn't have {@link #gridBuilder}, + * this field stored the first job to display. + */ + @Deprecated + private volatile String selectedJob; + + /** Builds {@link ProjectGrid} */ + private ProjectGridBuilder gridBuilder; + + /** noOfDisplayedBuilds. */ + private String noOfDisplayedBuilds; + + /** buildViewTitle. */ + private String buildViewTitle = ""; //$NON-NLS-1$ + + /** consoleOutputLinkStyle. */ + private String consoleOutputLinkStyle = LinkStyle.LIGHTBOX; + + /** URL for custom CSS file */ + private String cssUrl = ""; + + /** Indicates whether only the latest job will be triggered. **/ + private boolean triggerOnlyLatestJob; + + /** alwaysAllowManualTrigger. */ + private boolean alwaysAllowManualTrigger = true; + + /** showPipelineParameters. */ + private boolean showPipelineParameters = true; + + /** showPipelineParametersInHeaders */ + private boolean showPipelineParametersInHeaders; + + /** + * @deprecated + * + * Don't need an input from UI store this + */ + @Deprecated + private boolean startsWithParameters; + + /** + * Frequency at which the Build Pipeline Plugin updates the build cards in seconds + */ + private int refreshFrequency = 3; + + /** showPipelineDefinitionHeader. */ + private boolean showPipelineDefinitionHeader; + + /* + * Keep feature flag properties in one place so that it is easy to refactor them out later. + */ + /* Feature flags - START */ + + /* Feature flags - END */ + + /** A Logger object is used to log messages */ + private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); + + /** + * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the + * serialization + */ + private class MyUserIdCause extends Cause.UserIdCause { + /** + * user + */ + private User user; + + /** + * + */ + public MyUserIdCause() { + try { + // this block can generate a CyclicGraphDetector.CycleDetectedException + // in cases that I haven't quite figured out yet + // also an org.acegisecurity.AccessDeniedException when the user + // is not logged in + user = Hudson.getInstance().getMe(); + } catch (final Exception e) { + // do nothing + LOGGER.fine(e.getMessage()); + } + } + + @Override + public String getUserId() { + return (null == user) ? null : user.getId(); + } + + @Override + public String getUserName() { + return (null == user) ? null : user.getDisplayName(); + } + + @Override + public String toString() { + return getUserName(); + } + + @Override + public int hashCode() { + if (getUserId() == null) { + return super.hashCode(); + } else { + return getUserId().hashCode(); + } + } + + @Override + public boolean equals(final Object o) { + if (null == o) { + return false; + } + if (!(o instanceof Cause.UserIdCause)) { + return false; + } + + return hashCode() == o.hashCode(); + } + + @Override + public void print(final TaskListener listener) { + // do nothing + } + } + + /** + * + * @param name + * the name of the pipeline build view. + * @param buildViewTitle + * the build view title. + * @param gridBuilder + * controls the data to be displayed. + * @param noOfDisplayedBuilds + * a count of the number of builds displayed on the view + * @param triggerOnlyLatestJob + * Indicates whether only the latest job will be triggered. + * @param cssUrl + * URL for the custom CSS file. + */ + public BuildPipelineView(final String name, final String buildViewTitle, + final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final String cssUrl) { + super(name, Hudson.getInstance()); + this.buildViewTitle = buildViewTitle; + this.gridBuilder = gridBuilder; + this.noOfDisplayedBuilds = noOfDisplayedBuilds; + this.triggerOnlyLatestJob = triggerOnlyLatestJob; + this.cssUrl = cssUrl; + } + + /** + * + * @param name + * the name of the pipeline build view. + * @param buildViewTitle + * the build view title. + * @param gridBuilder + * controls the data to be displayed. + * @param noOfDisplayedBuilds + * a count of the number of builds displayed on the view + * @param triggerOnlyLatestJob + * Indicates whether only the latest job will be triggered. + * @param alwaysAllowManualTrigger + * Indicates whether manual trigger will always be available. + * @param showPipelineParameters + * Indicates whether pipeline parameter values should be shown. + * @param showPipelineParametersInHeaders + * Indicates whether the pipeline headers should show the + * pipeline parameter values for the last successful instance. + * @param showPipelineDefinitionHeader + * Indicates whether the pipeline headers should be shown. + * @param refreshFrequency + * Frequency at which the build pipeline plugin refreshes build cards + * @param cssUrl + * URL for the custom CSS file. + * @param selectedJob + * the first job name in the pipeline. it can be set to null when gridBuilder is passed. + */ + @DataBoundConstructor + public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, + final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, + final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, + final int refreshFrequency, final String cssUrl, final String selectedJob) { + this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); + this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; + this.showPipelineParameters = showPipelineParameters; + this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; + this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; + this.selectedJob = selectedJob; + //not exactly understanding the lifecycle here, but I want a default of 3 + //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) + if (refreshFrequency < 1) { + this.refreshFrequency = 3; + } else { + this.refreshFrequency = refreshFrequency; + } + + //for remote api support + if (this.gridBuilder == null) { + if (this.selectedJob != null) { + this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); + } + } + + if (this.selectedJob == null) { + if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { + this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); + } + } + } + + /** + * @return + * must be always 'this' + */ + protected Object readResolve() { + if (gridBuilder == null) { + if (selectedJob != null) { + gridBuilder = new DownstreamProjectGridBuilder(selectedJob); + } + } + return this; + } + + /** + * Handles the configuration submission + * + * @param req + * Stapler Request + * @throws FormException + * Form Exception + * @throws IOException + * IO Exception + * @throws ServletException + * Servlet Exception + */ + @Override + protected void submit(final StaplerRequest req) throws IOException, ServletException, FormException { + req.bindJSON(this, req.getSubmittedForm()); + } + + /** + * Checks whether the user has a permission to start a new instance of the pipeline. + * + * @return - true: Has Build permission; false: Does not have Build permission + * @see hudson.model.Item + */ + public boolean hasBuildPermission() { + return getGridBuilder().hasBuildPermission(this); + } + + /** + * Checks whether the user has Configure permission for the current project. + * + * @return - true: Has Configure permission; false: Does not have Configure permission + */ + public boolean hasConfigurePermission() { + return this.hasPermission(CONFIGURE); + } + + public ProjectGridBuilder getGridBuilder() { + return gridBuilder; + } + + public void setGridBuilder(ProjectGridBuilder gridBuilder) { + this.gridBuilder = gridBuilder; + } + + /** + * Get a List of downstream projects. + * + * @param currentProject + * - The project from which we want the downstream projects + * @return - A List of downstream projects + */ + public List> getDownstreamProjects(final AbstractProject currentProject) { + return ProjectUtil.getDownstreamProjects(currentProject); + } + + /** + * Determines if the current project has any downstream projects + * + * @param currentProject + * - The project from which we are testing. + * @return - true; has downstream projects; false: does not have downstream projects + */ + public boolean hasDownstreamProjects(final AbstractProject currentProject) { + return (getDownstreamProjects(currentProject).size() > 0); + } + + /** + * Returns BuildPipelineForm containing the build pipeline to display. + * + * @return - Representation of the projects and their related builds making up the build pipeline view + */ + public BuildPipelineForm getBuildPipelineForm() { + final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); + + final ProjectGrid projectGrid = gridBuilder.build(this); + if (projectGrid.isEmpty()) { + return null; + } + return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); + } + + /** + * Retrieves the project URL + * + * @param project + * - The project + * @return URL - of the project + * @throws URISyntaxException + * @throws URISyntaxException + * {@link URISyntaxException} + */ + public String getProjectURL(final AbstractProject project) throws URISyntaxException { + return project.getUrl(); + } + + /** + * Trigger a manual build + * + * @param upstreamBuildNumber + * upstream build number + * @param triggerProjectName + * project that is triggered + * @param upstreamProjectName + * upstream project + * @return next build number that has been scheduled + */ + @JavaScriptMethod + public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + + // Get parameters from upstream build + if (upstreamBuild == null) { + return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + } else { + LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + } + } + + /** + * @param triggerProjectName + * the triggerProjectName + * @return the number of re-tried build + */ + @JavaScriptMethod + public int retryBuild(final String triggerProjectName) { + LOGGER.fine("Retrying build again: " + triggerProjectName); //$NON-NLS-1$ + final AbstractProject triggerProject = (AbstractProject) super.getJob(triggerProjectName); + triggerProject.scheduleBuild(new MyUserIdCause()); + + return triggerProject.getNextBuildNumber(); + } + + /** + * Trigger a manual build + * + * @param buildNumber + * the number of the build of the current project + * @param upstreamBuildNumber + * upstream build number + * @param triggerProjectName + * project that is triggered + * @param upstreamProjectName + * upstream project + * @return next build number that has been scheduled + */ + @JavaScriptMethod + public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, + final String upstreamProjectName) { + LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + + // Retrieve the current build and then clear the upstream cause for the current link + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + + LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + + return nextBuildNumber; + } + + /** + * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. + * + * @param buildNo + * - Build number + * @param project + * - AbstractProject + * @return The AbstractBuild associated with the AbstractProject and build number. + */ + @SuppressWarnings("unchecked") + private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { + AbstractBuild build = null; + + if (project != null) { + for (final AbstractBuild tmpUpBuild : (List>) project.getBuilds()) { + if (tmpUpBuild.getNumber() == buildNo) { + build = tmpUpBuild; + break; + } + } + } + + return build; + } + + /** + * Schedules a build to start (no upstream build). + * + * @param triggerProject + * - Schedule a build to start on this AbstractProject + * @param delay + * - The delay for triggering the build (handle queueing) + * @return next build number + */ + private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { + LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ + final Cause.UpstreamCause upstreamCause = null; + final List buildActions = new ArrayList(); + final CauseAction causeAction = new CauseAction(new MyUserIdCause()); + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: + causeAction.getCauses().add(upstreamCause); + buildActions.add(causeAction); + + final ParametersAction parametersAction = new ParametersAction(); + buildActions.add(parametersAction); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + + return triggerProject.getNextBuildNumber(); + } + + /** + * Schedules a build to start. + * + * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. + * + * @param triggerProject + * - Schedule a build to start on this AbstractProject + * @param upstreamBuild + * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. + * @param buildParametersAction + * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * @param delay + * - The delay for triggering the build (handle queueing) + * @return next build number + */ + private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, + final Action buildParametersAction, final int delay) { + LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ + final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); + final List buildActions = new ArrayList(); + final CauseAction causeAction = new CauseAction(new MyUserIdCause()); + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: + causeAction.getCauses().add(upstreamCause); + buildActions.add(causeAction); + ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction + : new ParametersAction(); + + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } + + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + // Exchange base and overlay (last param wins) ! + parametersAction = mergeParameters((ParametersAction) action, parametersAction); + } else { + buildActions.add(action); + } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ + } + } + + buildActions.add(parametersAction); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + return triggerProject.getNextBuildNumber(); + } + + /** + * Used to retrieve the parameters from the upstream project build trigger relative to the given downstream project + * @param project the downstream project + * @param upstreamBuild the upstream project build + * @return the trigger config relative to the given downstream project + */ + private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, + final AbstractBuild upstreamBuild) { + final DescribableList> upstreamProjectPublishersList = + upstreamBuild.getProject().getPublishersList(); + + List configs = null; + + final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); + if (manualTrigger != null) { + final Set downstreamProjectsNames = + Sets.newHashSet(Splitter.on(",").trimResults().split(manualTrigger.getDownstreamProjectNames())); + if (downstreamProjectsNames.contains(project.getName())) { + configs = manualTrigger.getConfigs(); + } + } + + final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); + if (autoTrigger != null) { + for (BuildTriggerConfig config : autoTrigger.getConfigs()) { + final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").trimResults().split(config.getProjects())); + if (downstreamProjectsNames.contains(project.getName())) { + configs = config.getConfigs(); + } + } + } + + return configs; + } + + /** + * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java + * + * @param base + * One of the two parameters to merge. + * @param overlay + * The other parameters to merge + * @return + * Result of the merge. + */ + private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { + final LinkedHashMap params = new LinkedHashMap(); + for (final ParameterValue param : base.getParameters()) { + params.put(param.getName(), param); + } + for (final ParameterValue param : overlay.getParameters()) { + params.put(param.getName(), param); + } + return new ParametersAction(params.values().toArray(new ParameterValue[params.size()])); + } + + /** + * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. + * + * @param buildAction + * the action to check. + * @return true if the action has a reference to a userId cause. + */ + private boolean isUserIdCauseAction(final Action buildAction) { + boolean retval = false; + if (buildAction instanceof CauseAction) { + for (final Cause cause : ((CauseAction) buildAction).getCauses()) { + if (cause instanceof UserIdCause) { + retval = true; + break; + } + } + } + return retval; + } + + /** + * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. + * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the + * downstream build needs to remove the old user action inherited from upstream, and add its own. + * + * @param actions + * a collection of build actions. + * @return a collection of build actions with all UserId causes removed. + */ + private List removeUserIdCauseActions(final List actions) { + final List retval = new ArrayList(); + for (final Action action : actions) { + if (!isUserIdCauseAction(action)) { + retval.add(action); + } + } + return retval; + } + + /** + * This descriptor class is required to configure the View Page + * + */ + @Extension + public static final class DescriptorImpl extends ViewDescriptor { + + /** + * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of + * "Build Pipeline View" will be not displayed in the "NewView" page + */ + public DescriptorImpl() { + super(); + } + + /** + * get the display name + * + * @return display name + */ + @Override + public String getDisplayName() { + return Strings.getString("BuildPipelineView.DisplayText"); //$NON-NLS-1$ + } + + /** + * Display No Of Builds Items in the Edit View Page + * + * @return ListBoxModel + */ + public ListBoxModel doFillNoOfDisplayedBuildsItems() { + final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); + final List noOfBuilds = new ArrayList(); + noOfBuilds.add("1"); //$NON-NLS-1$ + noOfBuilds.add("2"); //$NON-NLS-1$ + noOfBuilds.add("3"); //$NON-NLS-1$ + noOfBuilds.add("5"); //$NON-NLS-1$ + noOfBuilds.add("10"); //$NON-NLS-1$ + noOfBuilds.add("20"); //$NON-NLS-1$ + noOfBuilds.add("50"); //$NON-NLS-1$ + noOfBuilds.add("100"); //$NON-NLS-1$ + noOfBuilds.add("200"); //$NON-NLS-1$ + noOfBuilds.add("500"); //$NON-NLS-1$ + + for (final String noOfBuild : noOfBuilds) { + options.add(noOfBuild); + } + return options; + } + + /** + * Display Console Output Link Style Items in the Edit View Page + * + * @return ListBoxModel + */ + public ListBoxModel doFillConsoleOutputLinkStyleItems() { + final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); + options.add(LinkStyle.LIGHTBOX); + options.add(LinkStyle.NEW_WINDOW); + options.add(LinkStyle.THIS_WINDOW); + return options; + } + } + + public String getBuildViewTitle() { + return buildViewTitle; + } + + public void setBuildViewTitle(final String buildViewTitle) { + this.buildViewTitle = buildViewTitle; + } + + public String getCssUrl() { + return cssUrl; + } + + public void setCssUrl(final String cssUrl) { + this.cssUrl = cssUrl; + } + + public String getNoOfDisplayedBuilds() { + return noOfDisplayedBuilds; + } + + public void setNoOfDisplayedBuilds(final String noOfDisplayedBuilds) { + this.noOfDisplayedBuilds = noOfDisplayedBuilds; + } + + public String getConsoleOutputLinkStyle() { + return consoleOutputLinkStyle; + } + + public void setConsoleOutputLinkStyle(String consoleOutputLinkStyle) { + this.consoleOutputLinkStyle = consoleOutputLinkStyle; + } + + public boolean isNewWindowConsoleOutputLinkStyle() { + return LinkStyle.NEW_WINDOW.equals(consoleOutputLinkStyle); + } + + public boolean isThisWindowConsoleOutputLinkStyle() { + return LinkStyle.THIS_WINDOW.equals(consoleOutputLinkStyle); + } + + public boolean isTriggerOnlyLatestJob() { + return triggerOnlyLatestJob; + } + + public String getTriggerOnlyLatestJob() { + return Boolean.toString(triggerOnlyLatestJob); + } + + public void setTriggerOnlyLatestJob(final boolean triggerOnlyLatestJob) { + this.triggerOnlyLatestJob = triggerOnlyLatestJob; + } + + public boolean isAlwaysAllowManualTrigger() { + return alwaysAllowManualTrigger; + } + + public String getAlwaysAllowManualTrigger() { + return Boolean.toString(alwaysAllowManualTrigger); + } + + public void setAlwaysAllowManualTrigger(final boolean alwaysAllowManualTrigger) { + this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; + } + + public boolean isShowPipelineParameters() { + return showPipelineParameters; + } + + public String getShowPipelineParameters() { + return Boolean.toString(showPipelineParameters); + } + + public void setShowPipelineParameters(final boolean showPipelineParameters) { + this.showPipelineParameters = showPipelineParameters; + } + + public boolean isShowPipelineParametersInHeaders() { + return showPipelineParametersInHeaders; + } + + public String getShowPipelineParametersInHeaders() { + return Boolean.toString(showPipelineParametersInHeaders); + } + + public void setShowPipelineParametersInHeaders(final boolean showPipelineParametersInHeaders) { + this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; + } + + public int getRefreshFrequency() { + return refreshFrequency; + } + + public void setRefreshFrequency(final int refreshFrequency) { + this.refreshFrequency = refreshFrequency; + } + + public int getRefreshFrequencyInMillis() { + return refreshFrequency * 1000; + } + + public boolean isShowPipelineDefinitionHeader() { + return showPipelineDefinitionHeader; + } + + public String getShowPipelineDefinitionHeader() { + return Boolean.toString(showPipelineDefinitionHeader); + } + + public void setShowPipelineDefinitionHeader(final boolean showPipelineDefinitionHeader) { + this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; + } + + @Override + public Collection getItems() { + final Collection items = new ArrayList(); + final BuildPipelineForm buildPipelineForm = getBuildPipelineForm(); + if (buildPipelineForm != null) { + final ProjectGrid grid = buildPipelineForm.getProjectGrid(); + for (int row = 0; row < grid.getRows(); row++) { + for (int col = 0; col < grid.getColumns(); col++) { + final ProjectForm form = grid.get(row, col); + if (form != null) { + final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); + if (item != null) { + items.add(item); + } + } + } + } + } + return items; + } + + @Override + public boolean contains(final TopLevelItem item) { + return this.getItems().contains(item); + } + + /** + * If a project name is changed we check if the selected job for this view also needs to be changed. + * + * @param item + * - The Item that has been renamed + * @param oldName + * - The old name of the Item + * @param newName + * - The new name of the Item + * + */ + @Override + public void onJobRenamed(final Item item, final String oldName, final String newName) { + LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); + try { + if (gridBuilder != null) { + gridBuilder.onJobRenamed(this, item, oldName, newName); + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); + } + } + + @Override + public Item doCreateItem(final StaplerRequest req, final StaplerResponse rsp) throws IOException, ServletException { + return Hudson.getInstance().doCreateItem(req, rsp); + } + + /** + * A class that groups together the console output link style options + */ + private static final class LinkStyle { + /** lightbox link style option */ + static final String LIGHTBOX = "Lightbox"; //$NON-NLS-1$ + /** new window link style option */ + static final String NEW_WINDOW = "New Window"; //$NON-NLS-1$ + /** this window link style option */ + static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ + } + + @Override + public boolean hasPermission(final Permission p) { + boolean display = true; + // tester la liste vide seulement en lecture + if (READ.name.equals(p.name)) { + if (this.getItems() == null || this.getItems().isEmpty()) { + display = false; + } + } else { + // Pas en lecture => permission standard + display = super.hasPermission(p); + } + + return display; + } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class); + if (project != null) { + // If a M2ReleaseAction is found + final List actions = project.getActions(M2ReleaseAction.class); + if (actions != null && !actions.isEmpty()) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild2.java similarity index 72% rename from src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java rename to src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild2.java index 4d6ed4c6..67fc5192 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild2.java @@ -29,7 +29,7 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.FreeStyleProject; -import hudson.model.Hudson; +import hudson.model.Queue.WaitingItem; import hudson.model.Result; import java.text.DateFormat; @@ -39,9 +39,12 @@ import java.util.Map; import java.util.logging.Logger; +import jenkins.model.Jenkins; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.HudsonResult; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; import hudson.plugins.parameterizedtrigger.SubProjectsAction; @@ -180,7 +183,7 @@ public List getDownstreamPipeline() { final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild); pbList.add(newPB); } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(currentProject.getActions(), SubProjectsAction.class)) { for (BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(currentProject.getParent(), null)) { @@ -281,21 +284,25 @@ private String getBuildResult(final AbstractBuild build) { */ private String getPendingStatus() { - String pendingStatus = HudsonResult.PENDING.toString(); - final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + // Retrieve the correct queued item based on the upstream build (display name + queue id) + if (upstreamBuild != null) { + // Search for Queue-Entry based on the upstream build + final QueueEntry queueEntry = QueueUtil.getQueueEntry(upstreamBuild); + if (queueEntry != null && QueueUtil.getQueuedItem(project, queueEntry) != null) { + return HudsonResult.QUEUED.toString(); + } + } - if (upstreamPB != null) { - if (this.getUpstreamBuild() != null) { - if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) - || - getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { - if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { - pendingStatus = HudsonResult.MANUAL.toString(); - } + final PipelineBuild upstreamPB = getUpstreamPipelineBuild(); + if (upstreamPB != null && this.getUpstreamBuild() != null) { + if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString()) + || getUpstreamBuildResult().equals(HudsonResult.UNSTABLE.toString())) { + if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) { + return HudsonResult.MANUAL.toString(); } } } - return pendingStatus; + return HudsonResult.PENDING.toString(); } @@ -316,12 +323,10 @@ public PipelineBuild getUpstreamPipelineBuild() { upstreamBuildName = ""; } if (upstreamProjects.size() > 0) { - if (isManualTrigger()) { - for (AbstractProject upstreamProject : upstreamProjects) { - if (upstreamProject.getName().equals(upstreamBuildName)) { - previousProject = upstreamProject; - break; - } + for (AbstractProject upstreamProject : upstreamProjects) { + if (upstreamProject.getName().equals(upstreamBuildName)) { + previousProject = upstreamProject; + break; } } if (previousProject == null) { @@ -346,6 +351,35 @@ public String getBuildDuration() { } } + /** + * Returns the current wait duration (when queued). + * + * @return - Current wait duration or an empty String if no queueing in + * progress is null. + */ + public String getQueueWaitDuration() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return Util.getTimeSpanString(wItem.timestamp.getTimeInMillis() - System.currentTimeMillis()); + } else { + return ""; //$NON-NLS-1$ + } + } + + /** + * Returns the current id of the queue item. + * + * @return - Current id of the queue item + */ + public Integer getQueueId() { + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + return wItem.id; + } else { + return 0; //$NON-NLS-1$ + } + } + /** * {@inheritDoc} */ @@ -431,7 +465,7 @@ public String getPipelineVersion() { public boolean hasBuildPermission() { boolean buildPermission = false; // If no security is enabled then allow builds - if (!Hudson.getInstance().isUseSecurity()) { + if (!Jenkins.getInstance().isUseSecurity()) { LOGGER.fine("Security is not enabled."); buildPermission = true; } else if (this.project != null) { @@ -446,14 +480,15 @@ public boolean hasBuildPermission() { * @return is ready to be manually built. */ public boolean isReadyToBeManuallyBuilt() { - return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) && hasBuildPermission(); + return this.currentBuild == null && (upstreamBuildSucceeded() || upstreamBuildUnstable()) + && hasBuildPermission() && (!"QUEUED".equals(getCurrentBuildResult())); } public boolean isRerunnable() { - return !isReadyToBeManuallyBuilt() - && !"PENDING".equals(getCurrentBuildResult()) - && !"BUILDING".equals(getCurrentBuildResult()) - && hasBuildPermission(); + return !isReadyToBeManuallyBuilt() && !"PENDING".equals(getCurrentBuildResult()) && !"BUILDING".equals(getCurrentBuildResult()) + && !"QUEUED".equals(getCurrentBuildResult()) + && hasBuildPermission() && getUpstreamPipelineBuild().getCurrentBuild() != null + && QueueUtil.getQueueEntry(currentBuild) == null; } /** @@ -470,6 +505,51 @@ private boolean upstreamBuildUnstable() { return this.getUpstreamBuild() != null && HudsonResult.UNSTABLE.toString().equals(getBuildResult(this.upstreamBuild)); } + /** + * + * @return whether this is the latest build of this pipeline view + */ + public boolean isLatestBuild() { + final AbstractBuild lastBuild = project.getLastBuild(); + if (currentBuild == null || lastBuild == null) { + return false; + } + // now check for last build on this pipeline view + // how do we find out, if the last build is run on this view? + return currentBuild.getNumber() == lastBuild.getNumber(); + } + + /** + * + * @return whether the upstream build is the latest for this pipeline view + */ + public boolean isUpstreamBuildLatest() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null) { + return false; + } + final AbstractProject upstreamPBProject = getUpstreamPipelineBuild().getProject(); + if (upstreamPBProject == null || upstreamPBProject.getLastBuild() == null) { + return false; + } + final AbstractBuild lastBuild = upstreamPBProject.getLastBuild(); + if (lastBuild == null) { + return false; + } + return upstreamBuild.getNumber() == lastBuild.getNumber(); + } + + /** + * + * @return whether the upstream build is the latest successful one for this + * pipeline view + */ + public boolean isUpstreamBuildLatestSuccess() { + if (upstreamBuild == null || getUpstreamPipelineBuild() == null || getUpstreamPipelineBuild().getProject() == null + || getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild() == null) { + return false; + } + return upstreamBuild.getNumber() == getUpstreamPipelineBuild().getProject().getLastSuccessfulBuild().getNumber(); + } /** * Determine if the project is triggered manually, regardless of the state of its upstream builds @@ -480,6 +560,13 @@ public boolean isManualTrigger() { boolean manualTrigger = false; if (this.upstreamBuild != null) { manualTrigger = ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project); + } else { + for (AbstractProject upstreamProject : this.project.getUpstreamProjects()) { + manualTrigger = ProjectUtil.isManualTrigger(upstreamProject, this.project); + if (manualTrigger) { + break; + } + } } return manualTrigger; } @@ -508,11 +595,35 @@ public String getFormattedStartTime() { * @return Formatted start date */ public String getFormattedStartDate() { - String formattedStartTime = ""; //$NON-NLS-1$ + String formattedStartDate = ""; //$NON-NLS-1$ if (getStartTime() != null) { - formattedStartTime = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); + formattedStartDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(getStartTime()); } - return formattedStartTime; + return formattedStartDate; + } + + /** + * @return Formatted queued for time + */ + public String getFormattedQueuedForTime() { + String formattedQueuedForTime = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForTime = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForTime; + } + + /** + * @return Formatted queued for date + */ + public String getFormattedQueuedForDate() { + String formattedQueuedForDate = ""; //$NON-NLS-1$ + final WaitingItem wItem = QueueUtil.getQueuedWaitingItem(project, getUpstreamBuild()); + if (wItem != null) { + formattedQueuedForDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(wItem.timestamp.getTime()); + } + return formattedQueuedForDate; } /** diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java similarity index 56% rename from src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java rename to src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java index 99996b78..9fafc1ba 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java @@ -4,7 +4,6 @@ import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Hudson; import java.util.ArrayList; import java.util.Arrays; @@ -14,7 +13,9 @@ import java.util.List; import java.util.Map; +import jenkins.model.Jenkins; import hudson.plugins.parameterizedtrigger.SubProjectsAction; + import org.kohsuke.stapler.bind.JavaScriptMethod; /** @@ -52,18 +53,38 @@ public class ProjectForm { /** * the latest successful build number */ - private final String lastSuccessfulBuildNumber; + private String lastSuccessfulBuildNumber; /** * the parameters used in the last successful build */ - private final Map lastSuccessfulBuildParams; + private Map lastSuccessfulBuildParams; + + /** + * Whether last successful build is already handled + */ + private boolean lastSuccessfulHandled; /** * keep reference to the project so that we can update it */ private final AbstractProject project; + /** + * keep reference to the first project in the current pipeline view + */ + private final AbstractProject firstProject; + + /** + * Hold the row this project is placed in (handle duplicate project-ids) + */ + private int row; + + /** + * Hold the column this project is placed in (handle duplicate project-ids) + */ + private int col; + /** * @param name * project name @@ -78,6 +99,7 @@ public ProjectForm(final String name) { dependencies = new ArrayList(); this.displayTrigger = true; project = null; + firstProject = null; } /** @@ -85,16 +107,20 @@ public ProjectForm(final String name) { * project */ public ProjectForm(final AbstractProject project) { - this(project, new LinkedHashSet>(Arrays.asList(project))); + this(project, new LinkedHashSet>(Arrays.asList(project)), project); } /** * @param project - * project + * - the project to wrap * @param parentPath * already traversed projects + * @param firstProject + * - the first project associated with the grid for this + * projectform */ - private ProjectForm(final AbstractProject project, final Collection> parentPath) { + public ProjectForm(final AbstractProject project, final Collection> parentPath, + final AbstractProject firstProject) { final PipelineBuild pipelineBuild = new PipelineBuild(project.getLastBuild(), project, null); name = pipelineBuild.getProject().getFullName(); @@ -105,17 +131,18 @@ private ProjectForm(final AbstractProject project, final Collection dependency : project.getDownstreamProjects()) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - dependencies.add(new ProjectForm(dependency, forkedPath)); + dependencies.add(new ProjectForm(dependency, forkedPath, firstProject)); } } - if (Hudson.getInstance().getPlugin("parameterized-trigger") != null) { + if (Jenkins.getInstance().getPlugin("parameterized-trigger") != null) { for (SubProjectsAction action : Util.filter(project.getActions(), SubProjectsAction.class)) { for (hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig config : action.getConfigs()) { for (final AbstractProject dependency : config.getProjectList(project.getParent(), null)) { final Collection> forkedPath = new LinkedHashSet>(parentPath); if (forkedPath.add(dependency)) { - final ProjectForm candidate = new ProjectForm(dependency, forkedPath); - // if subprojects come back as downstreams someday, no duplicates wanted + final ProjectForm candidate = new ProjectForm(dependency, forkedPath, firstProject); + // if subprojects come back as downstreams someday, + // no duplicates wanted if (!dependencies.contains(candidate)) { dependencies.add(candidate); } @@ -126,20 +153,24 @@ private ProjectForm(final AbstractProject project, final Collection lastSuccessfulBuild = pipelineBuild.getProject().getLastSuccessfulBuild(); - lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); - lastSuccessfulBuildParams = BuildUtil.getUnsensitiveParameters(lastSuccessfulBuild); + // Adjust retrieval of lastSuccessfulBuild per pipeline (if jobs are + // used in different pipelines, to avoid wrong parameters in the + // headers) + handleLastSuccessfulBuild(pipelineBuild.getProject().getLastSuccessfulBuild()); this.project = project; + this.firstProject = firstProject; } /** * Wraps possibly null {@link AbstractProject} into {@link ProjectForm}. - * + * This method is only called for a starting project in a pipeline view, + * therefore save it as such. + * * @param p - * project to be wrapped. + * project to be wrapped. * @return - * possibly null. + * possibly null. */ public static ProjectForm as(AbstractProject p) { return p != null ? new ProjectForm(p) : null; @@ -149,6 +180,27 @@ public String getName() { return name; } + public int getRow() { + return row; + } + + public int getColumn() { + return col; + } + + /** + * Set the coordinates for this project form. + * + * @param row + * - the row this form is set into + * @param col + * - the column this form is set into + */ + public void setCoords(int row, int col) { + this.row = row; + this.col = col; + } + public String getHealth() { return health; } @@ -169,18 +221,30 @@ public Map getLastSuccessfulBuildParams() { return lastSuccessfulBuildParams; } + public boolean isLastSuccessfulHandled() { + return lastSuccessfulHandled; + } + + public void setLastSuccessfulHandled(boolean lastSuccessfulHandled) { + this.lastSuccessfulHandled = lastSuccessfulHandled; + } + public List getDependencies() { return dependencies; } /** - * Gets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Gets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @return boolean whether to display or not */ public Boolean getDisplayTrigger() { @@ -188,15 +252,20 @@ public Boolean getDisplayTrigger() { } /** - * Sets a display value to determine whether a manual jobs 'trigger' button will be shown. This is used along with - * isTriggerOnlyLatestJob property allow only the latest version of a job to run. - * - * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob is set to true then as the html code is rendered the first - * job which should show the trigger button will render and then a call will be made to 'setDisplayTrigger' to change the value to both + * Sets a display value to determine whether a manual jobs 'trigger' button + * will be shown. This is used along with + * isTriggerOnlyLatestJob property allow only the latest version of a job to + * run. + * + * Works by: Initially always defaulted to true. If isTriggerOnlyLatestJob + * is set to true then as the html code is rendered the first + * job which should show the trigger button will render and then a call will + * be made to 'setDisplayTrigger' to change the value to both * so all future jobs will not display the trigger. see main.jelly - * + * * @param display - * - boolean to indicate whether the trigger button should be shown + * - boolean to indicate whether the trigger button should be + * shown */ public void setDisplayTrigger(final Boolean display) { displayTrigger = display; @@ -238,7 +307,7 @@ public int getId() { /** * Project as JSON - * + * * @return JSON string */ @JavaScriptMethod @@ -246,4 +315,56 @@ public String asJSON() { return ProjectJSONBuilder.asJSON(new ProjectForm(project)); } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + public Map filterSensitiveBuildVariables(AbstractBuild build) { + return BuildUtil.getUnsensitiveParameters(build); + } + + /** + * Correct the last successful build settings of this form, if that build is + * not on the current BuildPipelineView (when Jobs are used on multiple + * Pipelines) + * + * @param buildGrids + * - the build-grids present on this view + */ + public void correctLastSuccessfulBuilds(Iterable buildGrids) { + if (!isLastSuccessfulHandled()) { + AbstractBuild lastSuccessfulBuild = project.getLastSuccessfulBuild(); + // Determine whether the lastSuccessful Build belongs to this View + outer: while (lastSuccessfulBuild != null) { + // Search the buildGrid for the lastSuccessfulBuild given above + BuildForm buildForm = null; + for (BuildGrid buildGrid : buildGrids) { + buildForm = buildGrid.findBuildForm(lastSuccessfulBuild); + if (buildForm != null) { + break outer; + } + } + lastSuccessfulBuild = lastSuccessfulBuild.getPreviousSuccessfulBuild(); + } + // Set the (may be) newly found last successful build + handleLastSuccessfulBuild(lastSuccessfulBuild); + setLastSuccessfulHandled(true); + } + } + + /** + * Set build-number and build-params for last successful build. + * + * @param lastSuccessfulBuild + * - the last successful build (can be null) + */ + private void handleLastSuccessfulBuild(AbstractBuild lastSuccessfulBuild) { + lastSuccessfulBuildNumber = (null == lastSuccessfulBuild) ? "" : "" + lastSuccessfulBuild.getNumber(); + lastSuccessfulBuildParams = (null == lastSuccessfulBuild) ? new HashMap() + : filterSensitiveBuildVariables(lastSuccessfulBuild); + } } diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp2.jelly similarity index 100% rename from src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly rename to src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp2.jelly From 84974208a70d3fef13952443304d0111731c4fc7 Mon Sep 17 00:00:00 2001 From: rhirt Date: Mon, 8 Sep 2014 21:24:14 +0200 Subject: [PATCH 19/21] reapply original changes --- .../plugin/buildpipeline/BuildForm.java | 73 +- .../plugin/buildpipeline/BuildForm2.java | 251 ----- .../buildpipeline/BuildPipelineView.java | 358 ++++--- .../buildpipeline/BuildPipelineView2.java | 980 ------------------ ...PipelineBuild2.java => PipelineBuild.java} | 0 .../{ProjectForm2.java => ProjectForm.java} | 0 .../{bpp2.jelly => bpp.jelly} | 0 7 files changed, 287 insertions(+), 1375 deletions(-) delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java rename src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/{PipelineBuild2.java => PipelineBuild.java} (100%) rename src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/{ProjectForm2.java => ProjectForm.java} (100%) rename src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/{bpp2.jelly => bpp.jelly} (100%) diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java index 33960c70..a5caa8cd 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm.java @@ -8,13 +8,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Logger; import org.kohsuke.stapler.bind.JavaScriptMethod; +import au.com.centrumsystems.hudson.plugin.util.QueueEntry; +import au.com.centrumsystems.hudson.plugin.util.QueueUtil; + /** * @author Centrum Systems * @@ -150,6 +156,34 @@ public boolean updatePipelineBuild(final int nextBuildNumber) { if (newBuild != null) { updated = true; pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); + } else { + // try to see if the build went into queueing by searching for the queue-item from the upstream-build + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { + updated = true; + } + } + } + return updated; + } + + /** + * + * @param queueId the id of the queue-item that was cancelled + * + * @return is the queued item cancelled. + */ + @JavaScriptMethod + public boolean cancelQueued(final int queueId) { + boolean updated = false; + final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); + if (upstreamBuild != null) { + final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); + if (qentry.getQueueId() == queueId) { + updated = true; + } } return updated; } @@ -162,15 +196,29 @@ public String getRevision() { return pipelineBuild.getPipelineVersion(); } + public String getFullBuildName() { + return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); + } + @JavaScriptMethod public boolean isManualTrigger() { return pipelineBuild.isManualTrigger(); } + + /** + * @return Map the map with the parameters, enriched with a version parameter. + */ public Map getParameters() { - return pipelineBuild.getBuildParameters(); + final Map params = new HashMap(); + params.putAll(pipelineBuild.getBuildParameters()); + return params; } - + + public Map getFilteredParameters() { + return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); + } + public ArrayList getParameterList() { return parameters; } @@ -179,4 +227,25 @@ public Integer getProjectId() { return projectId; } + /** + * Filter last successful build variables with sensitive information. + * + * @param build + * the Build object to get the Variables from + * + * @return Map the vars, pixeled out sensitive information + */ + private Map filterSensitiveBuildVariables(AbstractBuild build) { + final Map allVars = build.getBuildVariables(); + final Set sensitives = build.getSensitiveBuildVariables(); + final HashMap resultVars = new HashMap(); + for (Entry item : allVars.entrySet()) { + if (sensitives.contains(item.getKey())) { + resultVars.put(item.getKey(), "******"); + } else { + resultVars.put(item.getKey(), item.getValue()); + } + } + return resultVars; + } } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java deleted file mode 100644 index a5caa8cd..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildForm2.java +++ /dev/null @@ -1,251 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.buildpipeline; - -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.ItemGroup; -import hudson.model.ParametersDefinitionProperty; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; -import java.util.logging.Logger; - -import org.kohsuke.stapler.bind.JavaScriptMethod; - -import au.com.centrumsystems.hudson.plugin.util.QueueEntry; -import au.com.centrumsystems.hudson.plugin.util.QueueUtil; - -/** - * @author Centrum Systems - * - * Representation of a build results pipeline - * - */ -public class BuildForm { - /** - * logger - */ - private static final Logger LOGGER = Logger.getLogger(BuildForm.class.getName()); - - /** - * status - */ - private String status = ""; - - /** - * pipeline build - */ - private PipelineBuild pipelineBuild; - - /** - * id - */ - private final Integer id; - - /** - * project id used to update project cards - */ - // TODO refactor to get rid of this coupling - private final Integer projectId; - - /** - * downstream builds - */ - private List dependencies = new ArrayList(); - - - /** - * project stringfied list of parameters for the project - * */ - private final ArrayList parameters; - - /** - * The item group pipeline view belongs to - */ - private final ItemGroup context; - - /** - * @param pipelineBuild - * pipeline build domain used to see the form - * @param context - * item group pipeline view belongs to, used to compute relative item names - */ - public BuildForm(ItemGroup context, final PipelineBuild pipelineBuild) { - this(context, pipelineBuild, new LinkedHashSet>(Arrays.asList(pipelineBuild.getProject()))); - } - - /** - * @param pipelineBuild - * pipeline build domain used to see the form - * @param context - * item group pipeline view belongs to, used to compute relative item names - * @param parentPath - * already traversed projects - */ - private BuildForm(ItemGroup context, final PipelineBuild pipelineBuild, final Collection> parentPath) { - this.context = context; - this.pipelineBuild = pipelineBuild; - status = pipelineBuild.getCurrentBuildResult(); - dependencies = new ArrayList(); - for (final PipelineBuild downstream : pipelineBuild.getDownstreamPipeline()) { - final Collection> forkedPath = new LinkedHashSet>(parentPath); - if (forkedPath.add(downstream.getProject())) { - dependencies.add(new BuildForm(context, downstream, forkedPath)); - } - } - id = hashCode(); - final AbstractProject project = pipelineBuild.getProject(); - projectId = project.getFullName().hashCode(); - final ParametersDefinitionProperty params = project.getProperty(ParametersDefinitionProperty.class); - final ArrayList paramList = new ArrayList(); - if (params != null) { - for (String p : params.getParameterDefinitionNames()) { - paramList.add(p); - } - } - parameters = paramList; - } - - public String getStatus() { - return status; - } - - public List getDependencies() { - return dependencies; - } - - /** - * @return All ids for existing depencies. - */ - public List getDependencyIds() { - final List ids = new ArrayList(); - for (final BuildForm dependency : dependencies) { - ids.add(dependency.getId()); - } - return ids; - } - - /** - * @return convert pipelineBuild as json format. - */ - @JavaScriptMethod - public String asJSON() { - return BuildJSONBuilder.asJSON(context, pipelineBuild, id, projectId, getDependencyIds(), getParameterList()); - } - - public int getId() { - return id; - } - - /** - * - * @param nextBuildNumber - * nextBuildNumber - * @return is the build pipeline updated. - */ - @JavaScriptMethod - public boolean updatePipelineBuild(final int nextBuildNumber) { - boolean updated = false; - final AbstractBuild newBuild = pipelineBuild.getProject().getBuildByNumber(nextBuildNumber); - if (newBuild != null) { - updated = true; - pipelineBuild = new PipelineBuild(newBuild, newBuild.getProject(), pipelineBuild.getUpstreamBuild()); - } else { - // try to see if the build went into queueing by searching for the queue-item from the upstream-build - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (QueueUtil.getQueuedItem(pipelineBuild.getProject(), qentry) != null) { - updated = true; - } - } - } - return updated; - } - - /** - * - * @param queueId the id of the queue-item that was cancelled - * - * @return is the queued item cancelled. - */ - @JavaScriptMethod - public boolean cancelQueued(final int queueId) { - boolean updated = false; - final AbstractBuild upstreamBuild = pipelineBuild.getUpstreamBuild(); - if (upstreamBuild != null) { - final QueueEntry qentry = QueueUtil.getQueueEntry(upstreamBuild); - if (qentry.getQueueId() == queueId) { - updated = true; - } - } - return updated; - } - - public int getNextBuildNumber() { - return pipelineBuild.getProject().getNextBuildNumber(); - } - - public String getRevision() { - return pipelineBuild.getPipelineVersion(); - } - - public String getFullBuildName() { - return pipelineBuild.getProject().getDisplayName() + " #" + pipelineBuild.getCurrentBuildNumber(); - } - - @JavaScriptMethod - public boolean isManualTrigger() { - return pipelineBuild.isManualTrigger(); - } - - - /** - * @return Map the map with the parameters, enriched with a version parameter. - */ - public Map getParameters() { - final Map params = new HashMap(); - params.putAll(pipelineBuild.getBuildParameters()); - return params; - } - - public Map getFilteredParameters() { - return filterSensitiveBuildVariables(pipelineBuild.getCurrentBuild()); - } - - public ArrayList getParameterList() { - return parameters; - } - - public Integer getProjectId() { - return projectId; - } - - /** - * Filter last successful build variables with sensitive information. - * - * @param build - * the Build object to get the Variables from - * - * @return Map the vars, pixeled out sensitive information - */ - private Map filterSensitiveBuildVariables(AbstractBuild build) { - final Map allVars = build.getBuildVariables(); - final Set sensitives = build.getSensitiveBuildVariables(); - final HashMap resultVars = new HashMap(); - for (Entry item : allVars.entrySet()) { - if (sensitives.contains(item.getKey())) { - resultVars.put(item.getKey(), "******"); - } else { - resultVars.put(item.getKey(), item.getValue()); - } - } - return resultVars; - } -} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 87db278a..3136074d 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -26,6 +26,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.Iterables; + import com.google.common.collect.Sets; import hudson.Extension; import hudson.model.Action; @@ -56,6 +57,7 @@ import hudson.util.LogTaskListener; import hudson.util.ListBoxModel; import jenkins.model.Jenkins; + import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -63,18 +65,21 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; + import javax.servlet.ServletException; + +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.bind.JavaScriptMethod; + import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; import au.com.centrumsystems.hudson.plugin.util.BuildUtil; import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; +import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; /** * This view displays the set of jobs that are related @@ -85,58 +90,70 @@ * */ public class BuildPipelineView extends View { + /** * @deprecated - * For backward compatibility. Back when we didn't have - * {@link #gridBuilder}, - * this field stored the first job to display. + * For backward compatibility. Back when we didn't have {@link #gridBuilder}, + * this field stored the first job to display. */ @Deprecated private volatile String selectedJob; + /** Builds {@link ProjectGrid} */ private ProjectGridBuilder gridBuilder; + /** noOfDisplayedBuilds. */ private String noOfDisplayedBuilds; + /** buildViewTitle. */ private String buildViewTitle = ""; //$NON-NLS-1$ + /** consoleOutputLinkStyle. */ private String consoleOutputLinkStyle = LinkStyle.LIGHTBOX; + /** URL for custom CSS file */ private String cssUrl = ""; + /** Indicates whether only the latest job will be triggered. **/ private boolean triggerOnlyLatestJob; + /** alwaysAllowManualTrigger. */ private boolean alwaysAllowManualTrigger = true; + /** showPipelineParameters. */ private boolean showPipelineParameters = true; + /** showPipelineParametersInHeaders */ private boolean showPipelineParametersInHeaders; + /** * @deprecated * - * Don't need an input from UI store this + * Don't need an input from UI store this */ - @Deprecated + @Deprecated private boolean startsWithParameters; + /** - * Frequency at which the Build Pipeline Plugin updates the build cards in - * seconds + * Frequency at which the Build Pipeline Plugin updates the build cards in seconds */ private int refreshFrequency = 3; + /** showPipelineDefinitionHeader. */ private boolean showPipelineDefinitionHeader; + /* - * Keep feature flag properties in one place so that it is easy to refactor - * them out later. + * Keep feature flag properties in one place so that it is easy to refactor them out later. */ /* Feature flags - START */ + /* Feature flags - END */ + /** A Logger object is used to log messages */ private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); /** - * An instance of {@link Cause.UserIdCause} related to the current user. - * Must be transient, or xstream will include it in the + * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the * serialization */ private class MyUserIdCause extends Cause.UserIdCause { @@ -146,12 +163,11 @@ private class MyUserIdCause extends Cause.UserIdCause { private User user; /** -* -*/ + * + */ public MyUserIdCause() { try { - // this block can generate a - // CyclicGraphDetector.CycleDetectedException + // this block can generate a CyclicGraphDetector.CycleDetectedException // in cases that I haven't quite figured out yet // also an org.acegisecurity.AccessDeniedException when the user // is not logged in @@ -194,6 +210,7 @@ public boolean equals(final Object o) { if (!(o instanceof Cause.UserIdCause)) { return false; } + return hashCode() == o.hashCode(); } @@ -218,8 +235,9 @@ public void print(final TaskListener listener) { * @param cssUrl * URL for the custom CSS file. */ - public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final String cssUrl) { + public BuildPipelineView(final String name, final String buildViewTitle, + final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final String cssUrl) { super(name, Hudson.getInstance()); this.buildViewTitle = buildViewTitle; this.gridBuilder = gridBuilder; @@ -250,40 +268,39 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P * @param showPipelineDefinitionHeader * Indicates whether the pipeline headers should be shown. * @param refreshFrequency - * Frequency at which the build pipeline plugin refreshes build - * cards + * Frequency at which the build pipeline plugin refreshes build cards * @param cssUrl * URL for the custom CSS file. * @param selectedJob - * the first job name in the pipeline. it can be set to null when - * gridBuilder is passed. + * the first job name in the pipeline. it can be set to null when gridBuilder is passed. */ @DataBoundConstructor public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, - final boolean showPipelineParameters, final boolean showPipelineParametersInHeaders, - final boolean showPipelineDefinitionHeader, final int refreshFrequency, final String cssUrl, final String selectedJob) { + final String noOfDisplayedBuilds, + final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, + final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, + final int refreshFrequency, final String cssUrl, final String selectedJob) { this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; this.showPipelineParameters = showPipelineParameters; this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; this.selectedJob = selectedJob; - // not exactly understanding the lifecycle here, but I want a default of - // 3 - // (this is what the class variable is set to 3, if it's 0, set it to - // default, refresh of 0 does not make sense anyway) + //not exactly understanding the lifecycle here, but I want a default of 3 + //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) if (refreshFrequency < 1) { this.refreshFrequency = 3; } else { this.refreshFrequency = refreshFrequency; } - // for remote api support + + //for remote api support if (this.gridBuilder == null) { if (this.selectedJob != null) { this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); } } + if (this.selectedJob == null) { if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); @@ -293,7 +310,7 @@ public BuildPipelineView(final String name, final String buildViewTitle, final P /** * @return - * must be always 'this' + * must be always 'this' */ protected Object readResolve() { if (gridBuilder == null) { @@ -322,11 +339,9 @@ protected void submit(final StaplerRequest req) throws IOException, ServletExcep } /** - * Checks whether the user has a permission to start a new instance of the - * pipeline. + * Checks whether the user has a permission to start a new instance of the pipeline. * - * @return - true: Has Build permission; false: Does not have Build - * permission + * @return - true: Has Build permission; false: Does not have Build permission * @see hudson.model.Item */ public boolean hasBuildPermission() { @@ -336,8 +351,7 @@ public boolean hasBuildPermission() { /** * Checks whether the user has Configure permission for the current project. * - * @return - true: Has Configure permission; false: Does not have Configure - * permission + * @return - true: Has Configure permission; false: Does not have Configure permission */ public boolean hasConfigurePermission() { return this.hasPermission(CONFIGURE); @@ -367,26 +381,25 @@ public void setGridBuilder(ProjectGridBuilder gridBuilder) { * * @param currentProject * - The project from which we are testing. - * @return - true; has downstream projects; false: does not have downstream - * projects + * @return - true; has downstream projects; false: does not have downstream projects */ public boolean hasDownstreamProjects(final AbstractProject currentProject) { return (getDownstreamProjects(currentProject).size() > 0); } - + /** * Returns BuildPipelineForm containing the build pipeline to display. * - * @return - Representation of the projects and their related builds making - * up the build pipeline view + * @return - Representation of the projects and their related builds making up the build pipeline view */ public BuildPipelineForm getBuildPipelineForm() { final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - final ProjectGrid project = gridBuilder.build(this); - if (project.isEmpty()) { + + final ProjectGrid projectGrid = gridBuilder.build(this); + if (projectGrid.isEmpty()) { return null; } - return new BuildPipelineForm(project, Iterables.limit(project.builds(), maxNoOfDisplayBuilds)); + return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); } /** @@ -416,19 +429,21 @@ public String getProjectURL(final AbstractProject project) throws URISynta */ @JavaScriptMethod public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final ItemGroup context = getOwnerItemGroup(); + final ItemGroup context = getOwnerItemGroup(); final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + // Get parameters from upstream build - if (upstreamBuild != null) { + if (upstreamBuild == null) { + return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + } else { LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); } - Action buildParametersAction = null; - if (upstreamBuild != null) { - buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - } - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction); } /** @@ -441,46 +456,55 @@ public int retryBuild(final String triggerProjectName) { LOGGER.fine("Retrying build again: " + triggerProjectName); //$NON-NLS-1$ final AbstractProject triggerProject = (AbstractProject) super.getJob(triggerProjectName); triggerProject.scheduleBuild(new MyUserIdCause()); + return triggerProject.getNextBuildNumber(); } /** - * @param externalizableId - * the externalizableId - * @return the number of re-run build + * Trigger a manual build + * + * @param buildNumber + * the number of the build of the current project + * @param upstreamBuildNumber + * upstream build number + * @param triggerProjectName + * project that is triggered + * @param upstreamProjectName + * upstream project + * @return next build number that has been scheduled */ @JavaScriptMethod - public int rerunBuild(final String externalizableId) { - LOGGER.fine("Running build again: " + externalizableId); //$NON-NLS-1$ - final AbstractBuild triggerBuild = (AbstractBuild) Run.fromExternalizableId(externalizableId); - final AbstractProject triggerProject = triggerBuild.getProject(); - final Future future = triggerProject.scheduleBuild2(triggerProject.getQuietPeriod(), new MyUserIdCause(), - removeUserIdCauseActions(triggerBuild.getActions())); - AbstractBuild result = triggerBuild; - try { - result = (AbstractBuild) future.get(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } catch (final ExecutionException e) { - e.printStackTrace(); - } - return result.getNumber(); + public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, + final String upstreamProjectName) { + LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ + final ItemGroup context = getOwnerItemGroup(); + final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); + final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); + + // Retrieve the current build and then clear the upstream cause for the current link + final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); + + LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ + final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); + final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, + ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); + + return nextBuildNumber; } /** - * Given an AbstractProject and a build number the associated AbstractBuild - * will be retrieved. + * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. * * @param buildNo * - Build number * @param project * - AbstractProject - * @return The AbstractBuild associated with the AbstractProject and build - * number. + * @return The AbstractBuild associated with the AbstractProject and build number. */ @SuppressWarnings("unchecked") private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { AbstractBuild build = null; + if (project != null) { for (final AbstractBuild tmpUpBuild : (List>) project.getBuilds()) { if (tmpUpBuild.getNumber() == buildNo) { @@ -489,88 +513,117 @@ public int rerunBuild(final String externalizableId) { } } } + return build; } + /** + * Schedules a build to start (no upstream build). + * + * @param triggerProject + * - Schedule a build to start on this AbstractProject + * @param delay + * - The delay for triggering the build (handle queueing) + * @return next build number + */ + private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { + LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ + final Cause.UpstreamCause upstreamCause = null; + final List buildActions = new ArrayList(); + final CauseAction causeAction = new CauseAction(new MyUserIdCause()); + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) + // available: + causeAction.getCauses().add(upstreamCause); + buildActions.add(causeAction); + + final ParametersAction parametersAction = new ParametersAction(); + buildActions.add(parametersAction); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); + + return triggerProject.getNextBuildNumber(); + } + /** * Schedules a build to start. * - * The build will take an upstream build as its Cause and a set of - * ParametersAction from the upstream build. + * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. * * @param triggerProject * - Schedule a build to start on this AbstractProject * @param upstreamBuild - * - The upstream AbstractBuild that will be used as a Cause for - * the triggerProject's build. + * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. * @param buildParametersAction - * - The upstream ParametersAction that will be used as an Action - * for the triggerProject's build. + * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. + * @param delay + * - The delay for triggering the build (handle queueing) * @return next build number */ private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction) { + final Action buildParametersAction, final int delay) { LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = (null == upstreamBuild) ? null : new Cause.UpstreamCause((Run) upstreamBuild); + final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); final List buildActions = new ArrayList(); final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) - // available: + // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: causeAction.getCauses().add(upstreamCause); buildActions.add(causeAction); ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction : new ParametersAction(); - if (upstreamBuild != null) { - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - if (configs == null) { - LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - } - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - parametersAction = mergeParameters(parametersAction, (ParametersAction) action); - } else { - buildActions.add(action); - } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ + + final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); + + if (configs == null) { + LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); + } + + for (final AbstractBuildParameters config : configs) { + try { + final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); + if (action instanceof ParametersAction) { + // Exchange base and overlay (last param wins) ! + parametersAction = mergeParameters((ParametersAction) action, parametersAction); + } else { + buildActions.add(action); } + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ + } catch (final AbstractBuildParameters.DontTriggerException e) { + LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ } } + buildActions.add(parametersAction); - triggerProject.scheduleBuild(triggerProject.getQuietPeriod(), null, buildActions.toArray(new Action[buildActions.size()])); + + triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); return triggerProject.getNextBuildNumber(); } /** - * Used to retrieve the parameters from the upstream project build trigger - * relative to the given downstream project - * - * @param project - * the downstream project - * @param upstreamBuild - * the upstream project build + * Used to retrieve the parameters from the upstream project build trigger relative to the given downstream project + * @param project the downstream project + * @param upstreamBuild the upstream project build * @return the trigger config relative to the given downstream project */ private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, - final AbstractBuild upstreamBuild) { - final DescribableList> upstreamProjectPublishersList = upstreamBuild.getProject() - .getPublishersList(); + final AbstractBuild upstreamBuild) { + final DescribableList> upstreamProjectPublishersList = + upstreamBuild.getProject().getPublishersList(); + List configs = null; + final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); if (manualTrigger != null) { - final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").trimResults() - .split(manualTrigger.getDownstreamProjectNames())); + final Set downstreamProjectsNames = + Sets.newHashSet(Splitter.on(",").trimResults().split(manualTrigger.getDownstreamProjectNames())); if (downstreamProjectsNames.contains(project.getName())) { configs = manualTrigger.getConfigs(); } } + final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); if (autoTrigger != null) { for (BuildTriggerConfig config : autoTrigger.getConfigs()) { @@ -580,19 +633,19 @@ private List retrieveUpstreamProjectTriggerConfig(final } } } + return configs; } /** - * From parameterized trigger plugin - * src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java + * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java * * @param base - * One of the two parameters to merge. + * One of the two parameters to merge. * @param overlay - * The other parameters to merge + * The other parameters to merge * @return - * Result of the merge. + * Result of the merge. */ private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { final LinkedHashMap params = new LinkedHashMap(); @@ -606,13 +659,11 @@ private static ParametersAction mergeParameters(final ParametersAction base, fin } /** - * Checks whether the given {@link Action} contains a reference to a - * {@link UserIdCause} object. + * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. * * @param buildAction * the action to check. - * @return true if the action has a reference to a userId - * cause. + * @return true if the action has a reference to a userId cause. */ private boolean isUserIdCauseAction(final Action buildAction) { boolean retval = false; @@ -628,12 +679,9 @@ private boolean isUserIdCauseAction(final Action buildAction) { } /** - * Removes any UserId cause action from the given actions collection. This - * is used by downstream builds that inherit upstream actions. - * The downstream build can be initiated by another user that is different - * from the user who initiated the upstream build, so the - * downstream build needs to remove the old user action inherited from - * upstream, and add its own. + * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. + * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the + * downstream build needs to remove the old user action inherited from upstream, and add its own. * * @param actions * a collection of build actions. @@ -655,9 +703,9 @@ private List removeUserIdCauseActions(final List actions) { */ @Extension public static final class DescriptorImpl extends ViewDescriptor { + /** - * descriptor impl constructor This empty constructor is required for - * stapler. If you remove this constructor, text name of + * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of * "Build Pipeline View" will be not displayed in the "NewView" page */ public DescriptorImpl() { @@ -692,6 +740,7 @@ public ListBoxModel doFillNoOfDisplayedBuildsItems() { noOfBuilds.add("100"); //$NON-NLS-1$ noOfBuilds.add("200"); //$NON-NLS-1$ noOfBuilds.add("500"); //$NON-NLS-1$ + for (final String noOfBuild : noOfBuilds) { options.add(noOfBuild); } @@ -834,9 +883,9 @@ public Collection getItems() { for (int col = 0; col < grid.getColumns(); col++) { final ProjectForm form = grid.get(row, col); if (form != null) { - final Item item = Jenkins.getInstance().getItem(form.getName(), getOwnerItemGroup()); - if (item != null && item instanceof TopLevelItem) { - items.add((TopLevelItem) item); + final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); + if (item != null) { + items.add(item); } } } @@ -851,8 +900,7 @@ public boolean contains(final TopLevelItem item) { } /** - * If a project name is changed we check if the selected job for this view - * also needs to be changed. + * If a project name is changed we check if the selected job for this view also needs to be changed. * * @param item * - The Item that has been renamed @@ -866,7 +914,9 @@ public boolean contains(final TopLevelItem item) { public void onJobRenamed(final Item item, final String oldName, final String newName) { LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); try { - gridBuilder.onJobRenamed(this, item, oldName, newName); + if (gridBuilder != null) { + gridBuilder.onJobRenamed(this, item, oldName, newName); + } } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); } @@ -888,7 +938,7 @@ private static final class LinkStyle { /** this window link style option */ static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ } - + @Override public boolean hasPermission(final Permission p) { boolean display = true; @@ -901,6 +951,30 @@ public boolean hasPermission(final Permission p) { // Pas en lecture => permission standard display = super.hasPermission(p); } + return display; } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class); + if (project != null) { + // If a M2ReleaseAction is found + final List actions = project.getActions(M2ReleaseAction.class); + if (actions != null && !actions.isEmpty()) { + return true; + } + } + return false; + } + } diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java deleted file mode 100644 index 3136074d..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView2.java +++ /dev/null @@ -1,980 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011, Centrum Systems Pty Ltd - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ -package au.com.centrumsystems.hudson.plugin.buildpipeline; - -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; - -import com.google.common.collect.Sets; -import hudson.Extension; -import hudson.model.Action; -import hudson.model.Item; -import hudson.model.ItemGroup; -import hudson.model.ParameterValue; -import hudson.model.TaskListener; -import hudson.model.TopLevelItem; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.Cause; -import hudson.model.Cause.UserIdCause; -import hudson.model.CauseAction; -import hudson.model.Descriptor; -import hudson.model.Descriptor.FormException; -import hudson.model.Hudson; -import hudson.model.ParametersAction; -import hudson.model.Run; -import hudson.model.User; -import hudson.model.View; -import hudson.model.ViewDescriptor; -import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; -import hudson.plugins.parameterizedtrigger.BuildTrigger; -import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; -import hudson.security.Permission; -import hudson.tasks.Publisher; -import hudson.util.DescribableList; -import hudson.util.LogTaskListener; -import hudson.util.ListBoxModel; -import jenkins.model.Jenkins; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.ServletException; - -import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.bind.JavaScriptMethod; - -import au.com.centrumsystems.hudson.plugin.buildpipeline.trigger.BuildPipelineTrigger; -import au.com.centrumsystems.hudson.plugin.util.BuildUtil; -import au.com.centrumsystems.hudson.plugin.util.ProjectUtil; -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; - -/** - * This view displays the set of jobs that are related - * based on their upstream\downstream relationships as a pipeline. Each - * build pipeline becomes a row on the view. - * - * @author Centrum Systems - * - */ -public class BuildPipelineView extends View { - - /** - * @deprecated - * For backward compatibility. Back when we didn't have {@link #gridBuilder}, - * this field stored the first job to display. - */ - @Deprecated - private volatile String selectedJob; - - /** Builds {@link ProjectGrid} */ - private ProjectGridBuilder gridBuilder; - - /** noOfDisplayedBuilds. */ - private String noOfDisplayedBuilds; - - /** buildViewTitle. */ - private String buildViewTitle = ""; //$NON-NLS-1$ - - /** consoleOutputLinkStyle. */ - private String consoleOutputLinkStyle = LinkStyle.LIGHTBOX; - - /** URL for custom CSS file */ - private String cssUrl = ""; - - /** Indicates whether only the latest job will be triggered. **/ - private boolean triggerOnlyLatestJob; - - /** alwaysAllowManualTrigger. */ - private boolean alwaysAllowManualTrigger = true; - - /** showPipelineParameters. */ - private boolean showPipelineParameters = true; - - /** showPipelineParametersInHeaders */ - private boolean showPipelineParametersInHeaders; - - /** - * @deprecated - * - * Don't need an input from UI store this - */ - @Deprecated - private boolean startsWithParameters; - - /** - * Frequency at which the Build Pipeline Plugin updates the build cards in seconds - */ - private int refreshFrequency = 3; - - /** showPipelineDefinitionHeader. */ - private boolean showPipelineDefinitionHeader; - - /* - * Keep feature flag properties in one place so that it is easy to refactor them out later. - */ - /* Feature flags - START */ - - /* Feature flags - END */ - - /** A Logger object is used to log messages */ - private static final Logger LOGGER = Logger.getLogger(BuildPipelineView.class.getName()); - - /** - * An instance of {@link Cause.UserIdCause} related to the current user. Must be transient, or xstream will include it in the - * serialization - */ - private class MyUserIdCause extends Cause.UserIdCause { - /** - * user - */ - private User user; - - /** - * - */ - public MyUserIdCause() { - try { - // this block can generate a CyclicGraphDetector.CycleDetectedException - // in cases that I haven't quite figured out yet - // also an org.acegisecurity.AccessDeniedException when the user - // is not logged in - user = Hudson.getInstance().getMe(); - } catch (final Exception e) { - // do nothing - LOGGER.fine(e.getMessage()); - } - } - - @Override - public String getUserId() { - return (null == user) ? null : user.getId(); - } - - @Override - public String getUserName() { - return (null == user) ? null : user.getDisplayName(); - } - - @Override - public String toString() { - return getUserName(); - } - - @Override - public int hashCode() { - if (getUserId() == null) { - return super.hashCode(); - } else { - return getUserId().hashCode(); - } - } - - @Override - public boolean equals(final Object o) { - if (null == o) { - return false; - } - if (!(o instanceof Cause.UserIdCause)) { - return false; - } - - return hashCode() == o.hashCode(); - } - - @Override - public void print(final TaskListener listener) { - // do nothing - } - } - - /** - * - * @param name - * the name of the pipeline build view. - * @param buildViewTitle - * the build view title. - * @param gridBuilder - * controls the data to be displayed. - * @param noOfDisplayedBuilds - * a count of the number of builds displayed on the view - * @param triggerOnlyLatestJob - * Indicates whether only the latest job will be triggered. - * @param cssUrl - * URL for the custom CSS file. - */ - public BuildPipelineView(final String name, final String buildViewTitle, - final ProjectGridBuilder gridBuilder, final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final String cssUrl) { - super(name, Hudson.getInstance()); - this.buildViewTitle = buildViewTitle; - this.gridBuilder = gridBuilder; - this.noOfDisplayedBuilds = noOfDisplayedBuilds; - this.triggerOnlyLatestJob = triggerOnlyLatestJob; - this.cssUrl = cssUrl; - } - - /** - * - * @param name - * the name of the pipeline build view. - * @param buildViewTitle - * the build view title. - * @param gridBuilder - * controls the data to be displayed. - * @param noOfDisplayedBuilds - * a count of the number of builds displayed on the view - * @param triggerOnlyLatestJob - * Indicates whether only the latest job will be triggered. - * @param alwaysAllowManualTrigger - * Indicates whether manual trigger will always be available. - * @param showPipelineParameters - * Indicates whether pipeline parameter values should be shown. - * @param showPipelineParametersInHeaders - * Indicates whether the pipeline headers should show the - * pipeline parameter values for the last successful instance. - * @param showPipelineDefinitionHeader - * Indicates whether the pipeline headers should be shown. - * @param refreshFrequency - * Frequency at which the build pipeline plugin refreshes build cards - * @param cssUrl - * URL for the custom CSS file. - * @param selectedJob - * the first job name in the pipeline. it can be set to null when gridBuilder is passed. - */ - @DataBoundConstructor - public BuildPipelineView(final String name, final String buildViewTitle, final ProjectGridBuilder gridBuilder, - final String noOfDisplayedBuilds, - final boolean triggerOnlyLatestJob, final boolean alwaysAllowManualTrigger, final boolean showPipelineParameters, - final boolean showPipelineParametersInHeaders, final boolean showPipelineDefinitionHeader, - final int refreshFrequency, final String cssUrl, final String selectedJob) { - this(name, buildViewTitle, gridBuilder, noOfDisplayedBuilds, triggerOnlyLatestJob, cssUrl); - this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; - this.showPipelineParameters = showPipelineParameters; - this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; - this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; - this.selectedJob = selectedJob; - //not exactly understanding the lifecycle here, but I want a default of 3 - //(this is what the class variable is set to 3, if it's 0, set it to default, refresh of 0 does not make sense anyway) - if (refreshFrequency < 1) { - this.refreshFrequency = 3; - } else { - this.refreshFrequency = refreshFrequency; - } - - //for remote api support - if (this.gridBuilder == null) { - if (this.selectedJob != null) { - this.gridBuilder = new DownstreamProjectGridBuilder(this.selectedJob); - } - } - - if (this.selectedJob == null) { - if (this.gridBuilder != null && this.gridBuilder instanceof DownstreamProjectGridBuilder) { - this.selectedJob = ((DownstreamProjectGridBuilder) this.gridBuilder).getFirstJob(); - } - } - } - - /** - * @return - * must be always 'this' - */ - protected Object readResolve() { - if (gridBuilder == null) { - if (selectedJob != null) { - gridBuilder = new DownstreamProjectGridBuilder(selectedJob); - } - } - return this; - } - - /** - * Handles the configuration submission - * - * @param req - * Stapler Request - * @throws FormException - * Form Exception - * @throws IOException - * IO Exception - * @throws ServletException - * Servlet Exception - */ - @Override - protected void submit(final StaplerRequest req) throws IOException, ServletException, FormException { - req.bindJSON(this, req.getSubmittedForm()); - } - - /** - * Checks whether the user has a permission to start a new instance of the pipeline. - * - * @return - true: Has Build permission; false: Does not have Build permission - * @see hudson.model.Item - */ - public boolean hasBuildPermission() { - return getGridBuilder().hasBuildPermission(this); - } - - /** - * Checks whether the user has Configure permission for the current project. - * - * @return - true: Has Configure permission; false: Does not have Configure permission - */ - public boolean hasConfigurePermission() { - return this.hasPermission(CONFIGURE); - } - - public ProjectGridBuilder getGridBuilder() { - return gridBuilder; - } - - public void setGridBuilder(ProjectGridBuilder gridBuilder) { - this.gridBuilder = gridBuilder; - } - - /** - * Get a List of downstream projects. - * - * @param currentProject - * - The project from which we want the downstream projects - * @return - A List of downstream projects - */ - public List> getDownstreamProjects(final AbstractProject currentProject) { - return ProjectUtil.getDownstreamProjects(currentProject); - } - - /** - * Determines if the current project has any downstream projects - * - * @param currentProject - * - The project from which we are testing. - * @return - true; has downstream projects; false: does not have downstream projects - */ - public boolean hasDownstreamProjects(final AbstractProject currentProject) { - return (getDownstreamProjects(currentProject).size() > 0); - } - - /** - * Returns BuildPipelineForm containing the build pipeline to display. - * - * @return - Representation of the projects and their related builds making up the build pipeline view - */ - public BuildPipelineForm getBuildPipelineForm() { - final int maxNoOfDisplayBuilds = Integer.valueOf(noOfDisplayedBuilds); - - final ProjectGrid projectGrid = gridBuilder.build(this); - if (projectGrid.isEmpty()) { - return null; - } - return new BuildPipelineForm(projectGrid, Iterables.limit(projectGrid.builds(), maxNoOfDisplayBuilds)); - } - - /** - * Retrieves the project URL - * - * @param project - * - The project - * @return URL - of the project - * @throws URISyntaxException - * @throws URISyntaxException - * {@link URISyntaxException} - */ - public String getProjectURL(final AbstractProject project) throws URISyntaxException { - return project.getUrl(); - } - - /** - * Trigger a manual build - * - * @param upstreamBuildNumber - * upstream build number - * @param triggerProjectName - * project that is triggered - * @param upstreamProjectName - * upstream project - * @return next build number that has been scheduled - */ - @JavaScriptMethod - public int triggerManualBuild(final Integer upstreamBuildNumber, final String triggerProjectName, final String upstreamProjectName) { - final ItemGroup context = getOwnerItemGroup(); - final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); - final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); - - final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); - - // Get parameters from upstream build - if (upstreamBuild == null) { - return triggerFirstBuild(triggerProject, ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - } else { - LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - return triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - } - } - - /** - * @param triggerProjectName - * the triggerProjectName - * @return the number of re-tried build - */ - @JavaScriptMethod - public int retryBuild(final String triggerProjectName) { - LOGGER.fine("Retrying build again: " + triggerProjectName); //$NON-NLS-1$ - final AbstractProject triggerProject = (AbstractProject) super.getJob(triggerProjectName); - triggerProject.scheduleBuild(new MyUserIdCause()); - - return triggerProject.getNextBuildNumber(); - } - - /** - * Trigger a manual build - * - * @param buildNumber - * the number of the build of the current project - * @param upstreamBuildNumber - * upstream build number - * @param triggerProjectName - * project that is triggered - * @param upstreamProjectName - * upstream project - * @return next build number that has been scheduled - */ - @JavaScriptMethod - public int rerunBuild(final Integer buildNumber, final Integer upstreamBuildNumber, final String triggerProjectName, - final String upstreamProjectName) { - LOGGER.fine("Running build again: " + triggerProjectName); //$NON-NLS-1$ - final ItemGroup context = getOwnerItemGroup(); - final AbstractProject triggerProject = (AbstractProject) Jenkins.getInstance().getItem(triggerProjectName, context); - final AbstractProject upstreamProject = (AbstractProject) Jenkins.getInstance().getItem(upstreamProjectName, context); - - // Retrieve the current build and then clear the upstream cause for the current link - final AbstractBuild upstreamBuild = retrieveBuild(upstreamBuildNumber, upstreamProject); - - LOGGER.fine("Getting parameters from upstream build " + upstreamBuild.getExternalizableId()); //$NON-NLS-1$ - final Action buildParametersAction = BuildUtil.getAllBuildParametersAction(upstreamBuild, triggerProject); - final int nextBuildNumber = triggerBuild(triggerProject, upstreamBuild, buildParametersAction, - ScheduleUtil.calcDelay(ProjectUtil.getProjectParametersAction(triggerProject))); - - return nextBuildNumber; - } - - /** - * Given an AbstractProject and a build number the associated AbstractBuild will be retrieved. - * - * @param buildNo - * - Build number - * @param project - * - AbstractProject - * @return The AbstractBuild associated with the AbstractProject and build number. - */ - @SuppressWarnings("unchecked") - private AbstractBuild retrieveBuild(final int buildNo, final AbstractProject project) { - AbstractBuild build = null; - - if (project != null) { - for (final AbstractBuild tmpUpBuild : (List>) project.getBuilds()) { - if (tmpUpBuild.getNumber() == buildNo) { - build = tmpUpBuild; - break; - } - } - } - - return build; - } - - /** - * Schedules a build to start (no upstream build). - * - * @param triggerProject - * - Schedule a build to start on this AbstractProject - * @param delay - * - The delay for triggering the build (handle queueing) - * @return next build number - */ - private int triggerFirstBuild(final AbstractProject triggerProject, final int delay) { - LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = null; - final List buildActions = new ArrayList(); - final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) - // available: - causeAction.getCauses().add(upstreamCause); - buildActions.add(causeAction); - - final ParametersAction parametersAction = new ParametersAction(); - buildActions.add(parametersAction); - - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); - - return triggerProject.getNextBuildNumber(); - } - - /** - * Schedules a build to start. - * - * The build will take an upstream build as its Cause and a set of ParametersAction from the upstream build. - * - * @param triggerProject - * - Schedule a build to start on this AbstractProject - * @param upstreamBuild - * - The upstream AbstractBuild that will be used as a Cause for the triggerProject's build. - * @param buildParametersAction - * - The upstream ParametersAction that will be used as an Action for the triggerProject's build. - * @param delay - * - The delay for triggering the build (handle queueing) - * @return next build number - */ - private int triggerBuild(final AbstractProject triggerProject, final AbstractBuild upstreamBuild, - final Action buildParametersAction, final int delay) { - LOGGER.fine("Triggering build for project: " + triggerProject.getFullDisplayName()); //$NON-NLS-1$ - final Cause.UpstreamCause upstreamCause = new Cause.UpstreamCause((Run) upstreamBuild); - final List buildActions = new ArrayList(); - final CauseAction causeAction = new CauseAction(new MyUserIdCause()); - // TODO hack obsolete as of 1.531 when CauseAction.(Cause...) available: - causeAction.getCauses().add(upstreamCause); - buildActions.add(causeAction); - ParametersAction parametersAction = buildParametersAction instanceof ParametersAction ? (ParametersAction) buildParametersAction - : new ParametersAction(); - - final List configs = retrieveUpstreamProjectTriggerConfig(triggerProject, upstreamBuild); - - if (configs == null) { - LOGGER.log(Level.SEVERE, "No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - throw new IllegalStateException("No upstream trigger found for this project" + triggerProject.getFullDisplayName()); - } - - for (final AbstractBuildParameters config : configs) { - try { - final Action action = config.getAction(upstreamBuild, new LogTaskListener(LOGGER, Level.INFO)); - if (action instanceof ParametersAction) { - // Exchange base and overlay (last param wins) ! - parametersAction = mergeParameters((ParametersAction) action, parametersAction); - } else { - buildActions.add(action); - } - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "I/O exception while adding build parameter", e); //$NON-NLS-1$ - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, "Adding build parameter was interrupted", e); //$NON-NLS-1$ - } catch (final AbstractBuildParameters.DontTriggerException e) { - LOGGER.log(Level.FINE, "Not triggering : " + config); //$NON-NLS-1$ - } - } - - buildActions.add(parametersAction); - - triggerProject.scheduleBuild(triggerProject.getQuietPeriod() + delay, null, buildActions.toArray(new Action[buildActions.size()])); - return triggerProject.getNextBuildNumber(); - } - - /** - * Used to retrieve the parameters from the upstream project build trigger relative to the given downstream project - * @param project the downstream project - * @param upstreamBuild the upstream project build - * @return the trigger config relative to the given downstream project - */ - private List retrieveUpstreamProjectTriggerConfig(final AbstractProject project, - final AbstractBuild upstreamBuild) { - final DescribableList> upstreamProjectPublishersList = - upstreamBuild.getProject().getPublishersList(); - - List configs = null; - - final BuildPipelineTrigger manualTrigger = upstreamProjectPublishersList.get(BuildPipelineTrigger.class); - if (manualTrigger != null) { - final Set downstreamProjectsNames = - Sets.newHashSet(Splitter.on(",").trimResults().split(manualTrigger.getDownstreamProjectNames())); - if (downstreamProjectsNames.contains(project.getName())) { - configs = manualTrigger.getConfigs(); - } - } - - final BuildTrigger autoTrigger = upstreamProjectPublishersList.get(BuildTrigger.class); - if (autoTrigger != null) { - for (BuildTriggerConfig config : autoTrigger.getConfigs()) { - final Set downstreamProjectsNames = Sets.newHashSet(Splitter.on(",").trimResults().split(config.getProjects())); - if (downstreamProjectsNames.contains(project.getName())) { - configs = config.getConfigs(); - } - } - } - - return configs; - } - - /** - * From parameterized trigger plugin src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java - * - * @param base - * One of the two parameters to merge. - * @param overlay - * The other parameters to merge - * @return - * Result of the merge. - */ - private static ParametersAction mergeParameters(final ParametersAction base, final ParametersAction overlay) { - final LinkedHashMap params = new LinkedHashMap(); - for (final ParameterValue param : base.getParameters()) { - params.put(param.getName(), param); - } - for (final ParameterValue param : overlay.getParameters()) { - params.put(param.getName(), param); - } - return new ParametersAction(params.values().toArray(new ParameterValue[params.size()])); - } - - /** - * Checks whether the given {@link Action} contains a reference to a {@link UserIdCause} object. - * - * @param buildAction - * the action to check. - * @return true if the action has a reference to a userId cause. - */ - private boolean isUserIdCauseAction(final Action buildAction) { - boolean retval = false; - if (buildAction instanceof CauseAction) { - for (final Cause cause : ((CauseAction) buildAction).getCauses()) { - if (cause instanceof UserIdCause) { - retval = true; - break; - } - } - } - return retval; - } - - /** - * Removes any UserId cause action from the given actions collection. This is used by downstream builds that inherit upstream actions. - * The downstream build can be initiated by another user that is different from the user who initiated the upstream build, so the - * downstream build needs to remove the old user action inherited from upstream, and add its own. - * - * @param actions - * a collection of build actions. - * @return a collection of build actions with all UserId causes removed. - */ - private List removeUserIdCauseActions(final List actions) { - final List retval = new ArrayList(); - for (final Action action : actions) { - if (!isUserIdCauseAction(action)) { - retval.add(action); - } - } - return retval; - } - - /** - * This descriptor class is required to configure the View Page - * - */ - @Extension - public static final class DescriptorImpl extends ViewDescriptor { - - /** - * descriptor impl constructor This empty constructor is required for stapler. If you remove this constructor, text name of - * "Build Pipeline View" will be not displayed in the "NewView" page - */ - public DescriptorImpl() { - super(); - } - - /** - * get the display name - * - * @return display name - */ - @Override - public String getDisplayName() { - return Strings.getString("BuildPipelineView.DisplayText"); //$NON-NLS-1$ - } - - /** - * Display No Of Builds Items in the Edit View Page - * - * @return ListBoxModel - */ - public ListBoxModel doFillNoOfDisplayedBuildsItems() { - final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); - final List noOfBuilds = new ArrayList(); - noOfBuilds.add("1"); //$NON-NLS-1$ - noOfBuilds.add("2"); //$NON-NLS-1$ - noOfBuilds.add("3"); //$NON-NLS-1$ - noOfBuilds.add("5"); //$NON-NLS-1$ - noOfBuilds.add("10"); //$NON-NLS-1$ - noOfBuilds.add("20"); //$NON-NLS-1$ - noOfBuilds.add("50"); //$NON-NLS-1$ - noOfBuilds.add("100"); //$NON-NLS-1$ - noOfBuilds.add("200"); //$NON-NLS-1$ - noOfBuilds.add("500"); //$NON-NLS-1$ - - for (final String noOfBuild : noOfBuilds) { - options.add(noOfBuild); - } - return options; - } - - /** - * Display Console Output Link Style Items in the Edit View Page - * - * @return ListBoxModel - */ - public ListBoxModel doFillConsoleOutputLinkStyleItems() { - final hudson.util.ListBoxModel options = new hudson.util.ListBoxModel(); - options.add(LinkStyle.LIGHTBOX); - options.add(LinkStyle.NEW_WINDOW); - options.add(LinkStyle.THIS_WINDOW); - return options; - } - } - - public String getBuildViewTitle() { - return buildViewTitle; - } - - public void setBuildViewTitle(final String buildViewTitle) { - this.buildViewTitle = buildViewTitle; - } - - public String getCssUrl() { - return cssUrl; - } - - public void setCssUrl(final String cssUrl) { - this.cssUrl = cssUrl; - } - - public String getNoOfDisplayedBuilds() { - return noOfDisplayedBuilds; - } - - public void setNoOfDisplayedBuilds(final String noOfDisplayedBuilds) { - this.noOfDisplayedBuilds = noOfDisplayedBuilds; - } - - public String getConsoleOutputLinkStyle() { - return consoleOutputLinkStyle; - } - - public void setConsoleOutputLinkStyle(String consoleOutputLinkStyle) { - this.consoleOutputLinkStyle = consoleOutputLinkStyle; - } - - public boolean isNewWindowConsoleOutputLinkStyle() { - return LinkStyle.NEW_WINDOW.equals(consoleOutputLinkStyle); - } - - public boolean isThisWindowConsoleOutputLinkStyle() { - return LinkStyle.THIS_WINDOW.equals(consoleOutputLinkStyle); - } - - public boolean isTriggerOnlyLatestJob() { - return triggerOnlyLatestJob; - } - - public String getTriggerOnlyLatestJob() { - return Boolean.toString(triggerOnlyLatestJob); - } - - public void setTriggerOnlyLatestJob(final boolean triggerOnlyLatestJob) { - this.triggerOnlyLatestJob = triggerOnlyLatestJob; - } - - public boolean isAlwaysAllowManualTrigger() { - return alwaysAllowManualTrigger; - } - - public String getAlwaysAllowManualTrigger() { - return Boolean.toString(alwaysAllowManualTrigger); - } - - public void setAlwaysAllowManualTrigger(final boolean alwaysAllowManualTrigger) { - this.alwaysAllowManualTrigger = alwaysAllowManualTrigger; - } - - public boolean isShowPipelineParameters() { - return showPipelineParameters; - } - - public String getShowPipelineParameters() { - return Boolean.toString(showPipelineParameters); - } - - public void setShowPipelineParameters(final boolean showPipelineParameters) { - this.showPipelineParameters = showPipelineParameters; - } - - public boolean isShowPipelineParametersInHeaders() { - return showPipelineParametersInHeaders; - } - - public String getShowPipelineParametersInHeaders() { - return Boolean.toString(showPipelineParametersInHeaders); - } - - public void setShowPipelineParametersInHeaders(final boolean showPipelineParametersInHeaders) { - this.showPipelineParametersInHeaders = showPipelineParametersInHeaders; - } - - public int getRefreshFrequency() { - return refreshFrequency; - } - - public void setRefreshFrequency(final int refreshFrequency) { - this.refreshFrequency = refreshFrequency; - } - - public int getRefreshFrequencyInMillis() { - return refreshFrequency * 1000; - } - - public boolean isShowPipelineDefinitionHeader() { - return showPipelineDefinitionHeader; - } - - public String getShowPipelineDefinitionHeader() { - return Boolean.toString(showPipelineDefinitionHeader); - } - - public void setShowPipelineDefinitionHeader(final boolean showPipelineDefinitionHeader) { - this.showPipelineDefinitionHeader = showPipelineDefinitionHeader; - } - - @Override - public Collection getItems() { - final Collection items = new ArrayList(); - final BuildPipelineForm buildPipelineForm = getBuildPipelineForm(); - if (buildPipelineForm != null) { - final ProjectGrid grid = buildPipelineForm.getProjectGrid(); - for (int row = 0; row < grid.getRows(); row++) { - for (int col = 0; col < grid.getColumns(); col++) { - final ProjectForm form = grid.get(row, col); - if (form != null) { - final TopLevelItem item = Jenkins.getInstance().getItem(form.getName()); - if (item != null) { - items.add(item); - } - } - } - } - } - return items; - } - - @Override - public boolean contains(final TopLevelItem item) { - return this.getItems().contains(item); - } - - /** - * If a project name is changed we check if the selected job for this view also needs to be changed. - * - * @param item - * - The Item that has been renamed - * @param oldName - * - The old name of the Item - * @param newName - * - The new name of the Item - * - */ - @Override - public void onJobRenamed(final Item item, final String oldName, final String newName) { - LOGGER.fine(String.format("Renaming job: %s -> %s", oldName, newName)); - try { - if (gridBuilder != null) { - gridBuilder.onJobRenamed(this, item, oldName, newName); - } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to handle onJobRenamed", e); - } - } - - @Override - public Item doCreateItem(final StaplerRequest req, final StaplerResponse rsp) throws IOException, ServletException { - return Hudson.getInstance().doCreateItem(req, rsp); - } - - /** - * A class that groups together the console output link style options - */ - private static final class LinkStyle { - /** lightbox link style option */ - static final String LIGHTBOX = "Lightbox"; //$NON-NLS-1$ - /** new window link style option */ - static final String NEW_WINDOW = "New Window"; //$NON-NLS-1$ - /** this window link style option */ - static final String THIS_WINDOW = "This Window"; //$NON-NLS-1$ - } - - @Override - public boolean hasPermission(final Permission p) { - boolean display = true; - // tester la liste vide seulement en lecture - if (READ.name.equals(p.name)) { - if (this.getItems() == null || this.getItems().isEmpty()) { - display = false; - } - } else { - // Pas en lecture => permission standard - display = super.hasPermission(p); - } - - return display; - } - - /** - * Check for M2ReleaseAction, if present, then provide an icon on top - * - * @return boolean - whether the first job is a m2release job - */ - public boolean isM2Release() { - if (Jenkins.getInstance().getPlugin("m2release") == null) { - return false; - } - final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); - final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), - AbstractProject.class); - if (project != null) { - // If a M2ReleaseAction is found - final List actions = project.getActions(M2ReleaseAction.class); - if (actions != null && !actions.isEmpty()) { - return true; - } - } - return false; - } - -} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java similarity index 100% rename from src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild2.java rename to src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/PipelineBuild.java diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java similarity index 100% rename from src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm2.java rename to src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/ProjectForm.java diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp2.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly similarity index 100% rename from src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp2.jelly rename to src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly From 82f6a0bddb9572fcb22fe5ae3b79d0153fe868dd Mon Sep 17 00:00:00 2001 From: rhirt Date: Wed, 10 Sep 2014 07:00:46 +0200 Subject: [PATCH 20/21] back to upstream master --- .../plugin/util/PipelineQueueListener.java | 50 ---- .../hudson/plugin/util/QueueEntry.java | 98 -------- .../hudson/plugin/util/QueueUtil.java | 157 ------------- .../hudson/plugin/util/ScheduleUtil.java | 213 ------------------ .../model/IntervalParameterDefinition.java | 199 ---------------- .../model/IntervalParameterValue.java | 154 ------------- .../ch/srsx/timetable/Messages.properties | 4 - .../IntervalParameterDefinition/config.jelly | 41 ---- .../config_de.properties | 4 - .../help-endTime.html | 3 - .../help-startTime.html | 3 - .../IntervalParameterDefinition/index.jelly | 35 --- .../model/IntervalParameterValue/value.jelly | 33 --- src/main/webapp/help/parameter/interval.html | 4 - .../webapp/images/h-tile-cross-bottom.png | Bin 789 -> 0 bytes src/main/webapp/images/h-tile-cross.png | Bin 464 -> 0 bytes src/main/webapp/images/h-tile-down.png | Bin 609 -> 0 bytes src/main/webapp/images/h-tile-left.png | Bin 476 -> 0 bytes src/main/webapp/images/h-tile-right.png | Bin 465 -> 0 bytes src/main/webapp/images/h-tile-top-right.png | Bin 623 -> 0 bytes src/main/webapp/images/h-tile.png | Bin 374 -> 0 bytes src/main/webapp/images/manual.png | Bin 608 -> 0 bytes .../webapp/images/r-tile-cross-bottom.png | Bin 553 -> 0 bytes src/main/webapp/images/r-tile-cross.png | Bin 497 -> 0 bytes src/main/webapp/images/r-tile-down.png | Bin 500 -> 0 bytes src/main/webapp/images/r-tile-left.png | Bin 487 -> 0 bytes src/main/webapp/images/r-tile-right.png | Bin 474 -> 0 bytes src/main/webapp/images/r-tile-top-right.png | Bin 569 -> 0 bytes src/main/webapp/images/r-tile.png | Bin 380 -> 0 bytes src/main/webapp/images/tile-cross-bottom.png | Bin 530 -> 0 bytes src/main/webapp/images/tile-cross.png | Bin 475 -> 0 bytes src/main/webapp/images/tile-down.png | Bin 493 -> 0 bytes src/main/webapp/images/tile-left.png | Bin 482 -> 0 bytes src/main/webapp/images/tile-right.png | Bin 464 -> 0 bytes src/main/webapp/images/tile-top-right.png | Bin 652 -> 0 bytes src/main/webapp/images/tile.png | Bin 380 -> 0 bytes 36 files changed, 998 deletions(-) delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java delete mode 100644 src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java delete mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java delete mode 100644 src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java delete mode 100644 src/main/resources/ch/srsx/timetable/Messages.properties delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly delete mode 100644 src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly delete mode 100644 src/main/webapp/help/parameter/interval.html delete mode 100644 src/main/webapp/images/h-tile-cross-bottom.png delete mode 100644 src/main/webapp/images/h-tile-cross.png delete mode 100644 src/main/webapp/images/h-tile-down.png delete mode 100644 src/main/webapp/images/h-tile-left.png delete mode 100644 src/main/webapp/images/h-tile-right.png delete mode 100644 src/main/webapp/images/h-tile-top-right.png delete mode 100644 src/main/webapp/images/h-tile.png delete mode 100644 src/main/webapp/images/manual.png delete mode 100644 src/main/webapp/images/r-tile-cross-bottom.png delete mode 100644 src/main/webapp/images/r-tile-cross.png delete mode 100644 src/main/webapp/images/r-tile-down.png delete mode 100644 src/main/webapp/images/r-tile-left.png delete mode 100644 src/main/webapp/images/r-tile-right.png delete mode 100644 src/main/webapp/images/r-tile-top-right.png delete mode 100644 src/main/webapp/images/r-tile.png delete mode 100644 src/main/webapp/images/tile-cross-bottom.png delete mode 100644 src/main/webapp/images/tile-cross.png delete mode 100644 src/main/webapp/images/tile-down.png delete mode 100644 src/main/webapp/images/tile-left.png delete mode 100644 src/main/webapp/images/tile-right.png delete mode 100644 src/main/webapp/images/tile-top-right.png delete mode 100644 src/main/webapp/images/tile.png diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java deleted file mode 100644 index f5ea288a..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/PipelineQueueListener.java +++ /dev/null @@ -1,50 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.util; - -import hudson.Extension; -import hudson.model.Cause; -import hudson.model.CauseAction; -import hudson.model.Cause.UpstreamCause; -import hudson.model.Queue.LeftItem; -import hudson.model.Queue.WaitingItem; -import hudson.model.queue.QueueListener; - -import java.util.logging.Logger; - -/** - * Utility Class to provide listener functionality for the queue. - * @author rhirt - */ -@Extension -public class PipelineQueueListener extends QueueListener { - /** A Logger object is used to log messages */ - private static final Logger LOGGER = Logger.getLogger(PipelineQueueListener.class.getName()); - - @Override - public void onEnterWaiting(WaitingItem wi) { - super.onEnterWaiting(wi); - for (final CauseAction action : wi.getActions(CauseAction.class)) { - for (final Cause cause : action.getCauses()) { - if (cause instanceof UpstreamCause) { - final UpstreamCause upstreamCause = (UpstreamCause) cause; - final QueueEntry qentry = new QueueEntry(upstreamCause.getUpstreamProject(), upstreamCause.getUpstreamBuild()); - qentry.setQueueId(wi.id); - QueueUtil.addQueueEntry(qentry); - } - } - } - } - - @Override - public void onLeft(LeftItem li) { - super.onLeft(li); - LOGGER.info("onLeft: " + li.toString()); - for (final CauseAction action : li.getActions(CauseAction.class)) { - for (final Cause cause : action.getCauses()) { - if (cause instanceof UpstreamCause) { - final UpstreamCause upstreamCause = (UpstreamCause) cause; - QueueUtil.removeQueueEntry(new QueueEntry(upstreamCause.getUpstreamProject(), upstreamCause.getUpstreamBuild())); - } - } - } - } -} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java deleted file mode 100644 index c677e24e..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueEntry.java +++ /dev/null @@ -1,98 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.util; - -import hudson.model.AbstractBuild; - -/** - * Utility Class to capture a link from a build form to a queue item. - * @author rhirt - * - */ -public class QueueEntry { - /** - * The name of the upstream project - */ - private String upstreamProjectName; - - /** - * The externalizable id of the upstream build. - */ - private String upstreamExtId; - - /** - * The id of the item in the queue. - */ - private int queueId; - - /** - * Construct from upstreamBuild - * @param upstreamBuild - the upstream build this entry is referring to - */ - public QueueEntry(AbstractBuild upstreamBuild) { - this(upstreamBuild.getProject().getName(), upstreamBuild.getExternalizableId()); - } - /** - * Construct from projectName and externalizable id of upstreamBuild - * @param upstreamProjectName - the project name of the build upstream - * @param upstreamExtId - the externalizable id of the build upstream - */ - public QueueEntry(String upstreamProjectName, String upstreamExtId) { - this(upstreamProjectName, upstreamExtId, 0); - } - /** - * Construct from projectName and externalizable id of upstreamBuild - * @param upstreamProjectName - the project name of the build upstream - * @param upstreamBuildId - the build number of the build upstream - */ - public QueueEntry(String upstreamProjectName, int upstreamBuildId) { - this(upstreamProjectName, upstreamProjectName + "#" + upstreamBuildId, 0); - } - /** - * Construct from projectName and externalizable id of upstreamBuild, add a queueId - * @param upstreamProjectName - the project name of the build upstream - * @param upstreamExtId - the ext id of the build upstream - * @param queueId - the id of the build in the queue - */ - public QueueEntry(String upstreamProjectName, String upstreamExtId, int queueId) { - this.upstreamProjectName = upstreamProjectName; - this.upstreamExtId = upstreamExtId; - this.queueId = queueId; - } - - /** - * Derive a map key from the ext-id - * @return the key for the map - */ - public String getKey() { - return upstreamExtId; - } - - public String getUpstreamExtId() { - return upstreamExtId; - } - - public void setUpstreamExtId(String upstreamExtId) { - this.upstreamExtId = upstreamExtId; - } - - public int getQueueId() { - return queueId; - } - - public void setQueueId(int queueId) { - this.queueId = queueId; - } - - public String getUpstreamProjectName() { - return upstreamProjectName; - } - - public void setUpstreamProjectName(String upstreamProjectName) { - this.upstreamProjectName = upstreamProjectName; - } - - @Override - public String toString() { - return "QueueEntry [upstreamProjectName=" + upstreamProjectName + ", upstreamExtId=" + upstreamExtId + ", queueId=" + queueId + "]"; - } - -} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java deleted file mode 100644 index 40c1738e..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/QueueUtil.java +++ /dev/null @@ -1,157 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.util; - -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.Queue; -import hudson.model.Queue.WaitingItem; -import hudson.model.queue.QueueListener; - -import java.util.HashMap; -import java.util.List; -import java.util.logging.Logger; - -import jenkins.model.Jenkins; - -/** - * Utitily class to help scheduling Jobs in the context of the build-pipeline - * plugin. - * - * @author rhirt - * - */ -public final class QueueUtil extends QueueListener { - /** A Logger object is used to log messages */ - private static final Logger LOGGER = Logger.getLogger(QueueUtil.class.getName()); - - /** A static hashmap to bind queued jobs to builds **/ - private static final HashMap QUEUE_ENTRIES = new HashMap(); - - /** - * Adding an entry into the list of queued items. - * - * @param queueEntry - * - entry to be added - */ - public static void addQueueEntry(QueueEntry queueEntry) { - LOGGER.info("Add entry to QueueUtil: " + queueEntry.toString()); - QUEUE_ENTRIES.put(queueEntry.getKey(), queueEntry); - } - - /** - * Remove an entry from the list of queued items. - * - * @param queueEntry - * - entry to be removed - */ - public static void removeQueueEntry(QueueEntry queueEntry) { - LOGGER.info("Remove entry from QueueUtil: " + queueEntry.toString()); - if (QUEUE_ENTRIES.remove(queueEntry.getKey()) != null) { - LOGGER.info("Done - removed entry from QueueUtil: " + queueEntry.toString()); - } - } - - /** - * Retrieve a queue entry based on an upstream build - * - * @param upstreamBuild - * - the upstream build to look for - * @return QueueEntry - the entry if found - */ - public static QueueEntry getQueueEntry(AbstractBuild upstreamBuild) { - if (upstreamBuild == null) { - return null; - } - return QUEUE_ENTRIES.get(new QueueEntry(upstreamBuild).getKey()); - } - - /** - * Retrieve the queued items for a project - * - * @param project - * - the project to search queued items for - * @return List - the items - */ - public static List getQueuedItemsFor(AbstractProject project) { - return Jenkins.getInstance().getQueue().getItems(project); - } - - /** - * Retrieve the id of a new queue item relative to the list of items before - * - * @param project - * - the project to search queued items for - * @param oldItems - * - list of items to check the new list against - * @return int - the id of the new queue item - */ - public static int getNewQueuedItemId(AbstractProject project, List oldItems) { - final List newItems = Jenkins.getInstance().getQueue().getItems(project); - - // Now search the new list for items that are not in the old list, if - // one does exist, return it's id - for (Queue.Item qitem : newItems) { - if (!oldItems.contains(qitem)) { - LOGGER.fine("Found last queued item for project " + project.getDisplayName() + " with id " + qitem.id); - return qitem.id; - } - } - return 0; - } - - /** - * Retrieve a queued item from the build history - * - * @param project - * - the project to search queued items for - * @param qentry - * the entry to derive items from - * @return Item - the item from the queue for the given name - */ - public static Queue.Item getQueuedItem(AbstractProject project, QueueEntry qentry) { - return getQueuedItem(project, qentry.getQueueId()); - } - - /** - * Retrieve a Queue.Item of type WaitingItem - * - * @param project - * - the project to search queued items for - * @param upstreamBuild - * the upstream build we derive the queue item from - * @return WaitingItem - the waiting item from the queue for the given - * upstreamBuild - */ - public static WaitingItem getQueuedWaitingItem(AbstractProject project, AbstractBuild upstreamBuild) { - final QueueEntry qentry = getQueueEntry(upstreamBuild); - if (qentry != null) { - final Queue.Item qItem = QueueUtil.getQueuedItem(project, qentry); - - if (qItem instanceof WaitingItem) { - return (WaitingItem) qItem; - } - } - return null; - } - - /** - * Retrieve a queued item with the given id from the build history - * - * @param project - * - the project to search queued items for - * @param queueId - * - the id of the queued item - * @return Item - the item from the queue for the given name - */ - private static Queue.Item getQueuedItem(AbstractProject project, int queueId) { - final List qitems = Jenkins.getInstance().getQueue().getItems(project); - - // Now search queued items for given queue id - for (Queue.Item qitem : qitems) { - if (qitem.id == queueId) { - LOGGER.fine("Found queued item for Project " + project.getDisplayName() + " and id " + queueId); - return qitem; - } - } - return null; - } -} diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java b/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java deleted file mode 100644 index 836c8350..00000000 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/util/ScheduleUtil.java +++ /dev/null @@ -1,213 +0,0 @@ -package au.com.centrumsystems.hudson.plugin.util; - -import hudson.model.ParameterValue; -import hudson.model.ParametersAction; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.logging.Logger; - -import org.joda.time.DateTime; -import org.joda.time.Interval; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; - -import ch.srsx.timetable.model.IntervalParameterValue; - -/** - * Utitily class to help scheduling Jobs in the context of the build-pipeline - * plugin. - * - * @author rhirt - * - */ -public final class ScheduleUtil { - - /** A Logger object is used to log messages */ - private static final Logger LOGGER = Logger.getLogger(ScheduleUtil.class.getName()); - - /** The format used for specifying time */ - private static DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm"); - - /** Delimiter for time-range on one line */ - public static final String TIMES_DELIMITER = "-"; - - /** Define a comparator for intervals */ - public static class IntervalComparator implements Comparator, Serializable { - /** - * Serial ID - */ - private static final long serialVersionUID = -1905472266853377782L; - - @Override - public int compare(Interval o1, Interval o2) { - return o1.getStart().compareTo(o2.getStart()); - } - } - - /** - * Calculate a delay, depending on the windows defined for the job. - * - * @param action - * - the action - * @return int - the calculated delay for the job-execution - */ - public static int calcDelay(ParametersAction action) { - - // Check for instances of IntervalParameterValue on the job-definition - if (action == null || (!hasIntervalParameterValues(action))) { - LOGGER.info("No action or intervals present, delay 0"); - return 0; - } - - // Now, determine the deployment-windows on the job - // Derive it from the IntervalParameterValues: - final List intervals = getIntervals(action); - - // Check the intervals for a match - final DateTime now = new DateTime(); - final int nowOfDay = now.getMillisOfDay(); - for (Interval interval : intervals) { - // check for interval being after now - if (nowOfDay < interval.getStart().getMillisOfDay()) { - // the interval is after now, take the delay from there and - // return - final int delay = (interval.getStart().getMillisOfDay() - nowOfDay) / 1000; - LOGGER.info("Now before the next interval, delay is " + delay); - return delay; - } else { - // check for interval containing now - if (nowOfDay >= interval.getStart().getMillisOfDay() && nowOfDay < interval.getEnd().getMillisOfDay()) { - // now is inside an interval, no delay necessary - LOGGER.info("Now inside an interval, delay is 0"); - return 0; - } - } - } - - // We are after the last interval, but have at least one interval - // now take the start of the first interval and add a day to it, - // than take the time from then - now - final int delayNextDay = intervals.get(0).getStart().getMillisOfDay(); - final int delay = (int) (delayNextDay + 86400000 - nowOfDay) / 1000; - LOGGER.info("Now before the first interval next day, delay is " + delay); - return delay; - } - - /** - * Read the provided intervals from the action of the job - * - * @param action - * the action - * @return List - the list of the intervals read from the action, - * sorted ascending - */ - private static List getIntervals(ParametersAction action) { - final List intervals = new ArrayList(); - final List pvs = action.getParameters(); - for (ParameterValue pv : pvs) { - if (pv instanceof IntervalParameterValue) { - intervals.add(((IntervalParameterValue) pv).getValue()); - } - } - Collections.sort(intervals, new ScheduleUtil.IntervalComparator()); - return intervals; - } - - /** - * Parse a given time of the format HH:mm and convert it into a DateTime - * today - * - * @param value - * - the value to parse - * @return DateTime - the parsed time, moved into today - * */ - public static DateTime parseTime(String value) { - final DateTime today = new DateTime(); - try { - return fmt.parseDateTime(value.trim()).withDate(today.getYear(), today.getMonthOfYear(), today.getDayOfMonth()); - } catch (IllegalArgumentException iaex) { - LOGGER.warning("Time could not be parsed: " + value); - } - return null; - } - - /** - * Parse the given String to create an interval from a compact - * representation in the form (HH:mm - HH:mm) - * - * @param value - * - the value in the format HH:mm - HH:mm - * @return Interval - the interval parsed - */ - public static Interval parseInterval(String value) { - final String[] timeValues = value.split(TIMES_DELIMITER); - if (timeValues.length == 2) { - return new Interval(parseTime(timeValues[0]), parseTime(timeValues[1])); - } - return null; - } - - /** - * Format the start time of the given interval in the form (HH:mm) - * - * @param value - * - the interval from which the start-time will be formatted - * @return String - the formatted String - */ - public static String formatStartTime(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getStart()); - } - - /** - * Format the end time of the given interval in the form (HH:mm) - * - * @param value - * - the interval from which the end-time will be formatted - * @return String - the formatted String - */ - public static String formatEndTime(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getEnd()); - } - - /** - * Format the given interval for a compact representation in the form (HH:mm - * - HH:mm) - * - * @param value - * - the interval to be formatted - * @return String - the formatted String - */ - public static String formatInterval(Interval value) { - if (value == null) { - return ""; - } - return fmt.print(value.getStart()) + TIMES_DELIMITER + fmt.print(value.getEnd()); - } - - /** - * Check whether an IntervalParameterValue is present on the job - * - * @param action - * - the action - * @return boolean - whether IntervalParameterValue(s) are present - */ - private static boolean hasIntervalParameterValues(ParametersAction action) { - final List pvs = action.getParameters(); - for (ParameterValue pv : pvs) { - if (pv instanceof IntervalParameterValue) { - return true; - } - } - return false; - } -} diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java deleted file mode 100644 index 9d93c414..00000000 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterDefinition.java +++ /dev/null @@ -1,199 +0,0 @@ -package ch.srsx.timetable.model; - -import hudson.Extension; -import hudson.model.ParameterValue; -import hudson.model.ParameterDefinition; -import hudson.util.FormValidation; - -import java.util.Date; - -import net.sf.json.JSONObject; - -import org.joda.time.DateTime; -import org.joda.time.Interval; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.export.Exported; - -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; -import ch.srsx.timetable.Messages; - -/** - * @author rhirt - */ -public class IntervalParameterDefinition extends ParameterDefinition { - /** Serial ID */ - private static final long serialVersionUID = -8142303813745641667L; - - /** Default value */ - private Interval defaultValue; - - /** - * Constructor requesting name, startTime, endTime and description - * - * @param name - * the name - * @param startTime - * - string in the format HH:mm for the start of the interval - * @param endTime - * - string in the format HH:mm for the end of the interval - * @param description - * the description - */ - @DataBoundConstructor - public IntervalParameterDefinition(String name, String startTime, String endTime, String description) { - super(name, description); - this.defaultValue = new Interval(ScheduleUtil.parseTime(startTime), ScheduleUtil.parseTime(endTime)); - } - - /** - * Constructor requesting name, interval and description - * - * @param name - * the name - * @param value - * - interval to derive this interval from - * @param description - * the description - */ - public IntervalParameterDefinition(String name, Interval value, String description) { - super(name, description); - this.defaultValue = value; - } - - @Override - public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { - if (defaultValue instanceof IntervalParameterValue) { - final IntervalParameterValue value = (IntervalParameterValue) defaultValue; - return new IntervalParameterDefinition(getName(), value.getValue(), getDescription()); - } else { - return this; - } - } - - /** - * @return String - text representation of the time value at the start of - * the interval - */ - public String getFormattedInterval() { - return ScheduleUtil.formatInterval(defaultValue); - } - - /** - * @return Date - the start of the interval as a date - */ - @Exported - public Date getIntervalFrom() { - if (defaultValue == null) { - return null; - } - return new Date(defaultValue.getStartMillis()); - } - - /** - * @return String - text representation of the time value at the start of - * the interval - */ - public String getIntervalFromText() { - return ScheduleUtil.formatStartTime(defaultValue); - } - - /** - * @return Date - the end of the interval as a date - */ - @Exported - public Date getIntervalTo() { - if (defaultValue == null) { - return null; - } - return new Date(defaultValue.getEndMillis()); - } - - /** - * @return String - text representation of the time value at the end of the - * interval - */ - public String getIntervalToText() { - return ScheduleUtil.formatEndTime(defaultValue); - } - - @Override - public IntervalParameterValue getDefaultParameterValue() { - return new IntervalParameterValue(getName(), defaultValue, getDescription()); - } - - @Override - public ParameterValue createValue(StaplerRequest req) { - return null; - } - - @Override - public ParameterValue createValue(StaplerRequest req, JSONObject jo) { - final IntervalParameterValue value = req.bindJSON(IntervalParameterValue.class, jo); - value.setDescription(getDescription()); - return value; - } - - /** - * @param value - * the value - * @return the interval parameter value object created - */ - public IntervalParameterValue createValue(Interval value) { - return new IntervalParameterValue(getName(), value, getDescription()); - } - - /** - * DescriptorImpl for this parameter definition - * - * @author rhirt - * - */ - @Extension - public static class DescriptorImpl extends ParameterDescriptor { - @Override - public String getDisplayName() { - return Messages.IntervalParameterDefinition_DisplayName(); - } - - @Override - public String getHelpFile() { - return "/plugin/build-pipeline-plugin/help/parameter/interval.html"; - } - - /** - * Checks if parameterised build intervals are valid. - * - * @param value - * - the value to check - * @return FormValidation - status whether validation succeeded - */ - public FormValidation doCheckTime(@QueryParameter String value) { - if (ScheduleUtil.parseTime(value) == null) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); - } - return FormValidation.ok(); - } - - /** - * Checks if parameterised build intervals are valid. - * - * @param fromValue - * - the value (from) to check - * @param toValue - * - the value (to) to check - * @return FormValidation - status whether validation succeeded - */ - public FormValidation doCheckInterval(@QueryParameter String fromValue, @QueryParameter String toValue) { - final DateTime from = ScheduleUtil.parseTime(fromValue); - final DateTime to = ScheduleUtil.parseTime(toValue); - if (from == null || to == null) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalTime()); - } else if (!from.isBefore(to)) { - return FormValidation.error(Messages.IntervalParameterDefinition_IllegalInterval()); - } - return FormValidation.ok(); - } - } -} \ No newline at end of file diff --git a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java b/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java deleted file mode 100644 index 5ed27eef..00000000 --- a/src/main/java/ch/srsx/timetable/model/IntervalParameterValue.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Luca Domenico Milanesio, Tom Huybrechts - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ch.srsx.timetable.model; - -import hudson.EnvVars; -import hudson.model.ParameterValue; -import hudson.model.AbstractBuild; -import hudson.util.VariableResolver; - -import java.util.Locale; - -import org.joda.time.Interval; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.export.Exported; - -import au.com.centrumsystems.hudson.plugin.util.ScheduleUtil; - -/** - * {@link ParameterValue} created from {@link IntervalParameterDefinition}. - * - * @author rhirt - */ -public class IntervalParameterValue extends ParameterValue { - /** The serial id */ - private static final long serialVersionUID = 1L; - - /** the interval in question */ - @Exported(visibility = 4) - private final Interval value; - - /** - * Constructor taking name and value. - * - * @param name - * the name - * @param value - * the value, given as a formatted String (HH:mm - HH:mm) - */ - @DataBoundConstructor - public IntervalParameterValue(String name, String value) { - this(name, value, null); - } - - /** - * Constructor taking name, value and description. - * - * @param name - * the name - * @param value - * the value, given as a formatted String (HH:mm - HH:mm) - * @param description - * the description - */ - public IntervalParameterValue(String name, String value, String description) { - super(name, description); - this.value = ScheduleUtil.parseInterval(value); - } - - /** - * Constructor taking name, interval and description. - * - * @param name - * the name - * @param interval - * the value, given as an interval object - * @param description - * the description - */ - public IntervalParameterValue(String name, Interval interval, String description) { - super(name, description); - this.value = interval; - } - - - @Override - public void buildEnvVars(AbstractBuild build, EnvVars env) { - env.put(name, value.toString()); - env.put(name.toUpperCase(Locale.ENGLISH), value.toString()); - } - - @Override - public VariableResolver createVariableResolver(AbstractBuild build) { - return new VariableResolver() { - public String resolve(String name) { - return IntervalParameterValue.this.name.equals(name) ? ScheduleUtil.formatInterval(value) : null; - } - }; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final IntervalParameterValue other = (IntervalParameterValue) obj; - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "(IntervalParameterValue) " + getName() + "='" + value.toString() + "'"; - } - - @Override - public String getShortDescription() { - return name + '=' + ScheduleUtil.formatInterval(value); - } - - public Interval getValue() { - return value; - } -} diff --git a/src/main/resources/ch/srsx/timetable/Messages.properties b/src/main/resources/ch/srsx/timetable/Messages.properties deleted file mode 100644 index 7073c54d..00000000 --- a/src/main/resources/ch/srsx/timetable/Messages.properties +++ /dev/null @@ -1,4 +0,0 @@ -IntervalParameterDefinition.DisplayName=Interval Parameter -IntervalParameterDefinition.IllegalInterval=Illegal Interval. -IntervalParameterDefinition.IllegalTime=Illegal Time-Format (should be HH:mm). - diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly deleted file mode 100644 index 2badb0d2..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config.jelly +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties deleted file mode 100644 index b218a253..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/config_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -Name=Name -Interval\ From=Interval Startzeit -Interval\ To=Interval Endzeit -Description=Beschreibung diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html deleted file mode 100644 index d4f5f8b6..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-endTime.html +++ /dev/null @@ -1,3 +0,0 @@ -
- The start-time of the interval to be used for allowed job-execution. -
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html deleted file mode 100644 index d4f5f8b6..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/help-startTime.html +++ /dev/null @@ -1,3 +0,0 @@ -
- The start-time of the interval to be used for allowed job-execution. -
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly deleted file mode 100644 index daddbb86..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterDefinition/index.jelly +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -
- - -
-
-
\ No newline at end of file diff --git a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly b/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly deleted file mode 100644 index 1a8e24f0..00000000 --- a/src/main/resources/ch/srsx/timetable/model/IntervalParameterValue/value.jelly +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/help/parameter/interval.html b/src/main/webapp/help/parameter/interval.html deleted file mode 100644 index 7f4c7b9f..00000000 --- a/src/main/webapp/help/parameter/interval.html +++ /dev/null @@ -1,4 +0,0 @@ -
- Defines an interval parameter, where users can enter a start time and an end time, - which you can use to identify times when job execution is allowed. -
\ No newline at end of file diff --git a/src/main/webapp/images/h-tile-cross-bottom.png b/src/main/webapp/images/h-tile-cross-bottom.png deleted file mode 100644 index c09666e727ed14d85ac1d79947955da0238bc8c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH*D1Jf!`7srr_xVLu$^Hw=XI3ylC!n^bT4yNl# zj}~_ZxzCs)wZLN0w*Qv#MT@s@ZLUgTtJrw!ZP{6`hga{eZ8A1#S*Xavo0!4j>?|R5 z=z>67n*sfVLfQ{6j9h>HZvE+4z3I1eW9`4+jLe!7^#0zzpEdjLPBY1vl@=O$HMi$W z{^pw{`+sWMF0+#BuX``*FxO7>yvO`$YwGLQeKG%jeqqX-lowlhdgu4%E-(3AfAqrV z^~>*d?Av(!YyGr$_P^A6dv{)$ZEJSz%InCg->UDn+kgMrvv=KXCzJFEJN5THY31$R zeZ?aA*s=4;Q*!E0D4$>XzV3JN{PQ;++q{AX$6v>#ufHpQnw9&4Kk8LOzSlnk$zQKz ztFJBJtMcV#$*Xs|$tORyEm~&#`fBy{D?irXD3de3-}dj-|F>ng@6Jx&f4}_uqrz8h z9P+a^{5vOT_ht36D;$%-d)jUDS7elikz%fNqttoUQNa?#|SYylfMc&pA&e%?%|Oep!4(ma{n8zzhr%7eYx$y1Aphm RtAR!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?cI&M%?2V4j%WT^Zxm*Bn8I<& zLo-OFuDntU0%LPFvcMiHT48PTWmkDYRmfbjSIdklW zKErkXX=x=0N9^H!d&_kC_Jy&#>yDd8N!+b}FmK)Q!adh+FSkEXJb(Fr0)wZkpUXO@geCy0SD-fl diff --git a/src/main/webapp/images/h-tile-down.png b/src/main/webapp/images/h-tile-down.png deleted file mode 100644 index 88fe3a2fd0d541e05a31c226111925e5037ed91a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hU$6V@O5Z+dI~IEd~s1i5$}`_`a{VV0`Yl z$bhpe+;1YswlnshO+Q{!wohVtyldTafvm`B3#VMFyL)@v+TKq!TlP=9*EvVeYRj${ z7oJP**50dYRkC(#uZV2btYv1Cs!dqbzF73-9%&;^0EHUpEEg^E19 zi5VQu#0Yh~xUkRnrfd7R+*3fC@`KCXT?8A#qPFF|yYr8_Edp?}l&rS!n*!bPd*Akn zi=5AYzT?)#_xAE?zG8{bTq1|G0~B$%AfDsN#zfzX=C`oLtt N;OXk;vd$@?2>@=z_&@*v diff --git a/src/main/webapp/images/h-tile-left.png b/src/main/webapp/images/h-tile-left.png deleted file mode 100644 index 08a0b263316f9001a5ed1a69a17a496819558a56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1|(&F^?3p*#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0rOCF{C2y?Onxn%?=E%fh!y~{(rlH@!CZx znTong8yc3anqS>F)28;QN^)Avw5KvVwjM7nN>*a;kWnaVIKfC?M$hYA^FG_ePxhBR zZusVR-P-$RdAG~_E|>V)eXX%;zy7*(?fq{8KmOf0iR!KoeKviMs|)<+neP9xex7M< sd42u6CkzvrUHAkn9q7uqE!}3nZoaapp*V2m($y85}Sb4q9e02OPUkpKVy diff --git a/src/main/webapp/images/h-tile-right.png b/src/main/webapp/images/h-tile-right.png deleted file mode 100644 index c03f7b42e9dd82f5dc0a89728ca16da023390482..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA}&+iyz!UFFtubAIjp^#@=7y{E6QUeasT^SQ>q z=9NWXdTZSE(wy66K#q<7>fH&m)D~*D^a)rwz2I1=3}H;)z3zYY4B?CekNp!lvI6;>1s;*b3=DjSK$uZf!>fUTfr-V_#WAEJ?(N;|ye0<`hr~GTLx0;vH>fP= zo4c|l+|{H#~pz@{>%D^DcdQB^h@m zr4C&XXxpbW=QMBaR-MFrn>q=wNSlF4%RukTv)Qu$=#_vj$ibXphio- zLu&$x0-z;4yr_E6gckmnz4Wx`WbyMepJPnVW^enOIVbP0l+XkKCiwmg diff --git a/src/main/webapp/images/h-tile.png b/src/main/webapp/images/h-tile.png deleted file mode 100644 index 64a3d1d384fbb9f0189ad24095daad968cc40abb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q)1SBWTkdy;bjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4cwkCjv*CsZ!Z`!GBEHQUhvaAG`skqs7gWi frLC7o6x?KMmB*zw+b=N&7*-6Pu6{1-oD!MPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rg2lK~y+Tos-{F z5>XV#`OEq%>JO-%ee9VT2p>Y-vCYsWHAT&78x{r#h?uojhDrG)phJKyl(aSptMR}C z58w9O3mc?OO%Irhn{vMjUMXf#0Yf3KZ-AgFLrT!L@q z8I))e>C6F((&{?AbMw$N%__S=FdPn{C<<1r!s;gjr{_PeQSbC1hhp&i{TK`eR@;Pt zEjJWJ^ZW`@qRgN?cBo@9oX4w7ojnt3Hjh9c@JnDA2E?b!sGW6jUr6BAgB`}5*|41x z@_ro+8zQU3m&pK zvz)ebij~hjMoIq;x7*DHl}ZKvjct_My?;iIyg@3J;sS#1d{oV%)b9Qjt=xpq=L6kd zTR?+CFT-#5QEYYpNHSA^D2kxNr#@RijOSrVT0=0ghx9=e1^ohQzJ^drgY8z$};rI!!lvI6;>1s;*b3=DjSK$uZf!>a+PVV|dqV@O5Z+q<^ew*w>^0yPV7AKBu(@c;kg zDd%P>Mla`E_+a7D(%Jmi{X|YYdGp+|fJ9sZ#zWiJAx$~>X edmqnQ_P->>BsND}{68>y89ZJ6T-G@yGywpY8rk&# diff --git a/src/main/webapp/images/r-tile-cross.png b/src/main/webapp/images/r-tile-cross.png deleted file mode 100644 index 6e268563430bf92b98b8d6504818b902670302eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0Q69F{C2y?cKG0M;s&^F7k15KKwhMW3O1F zv$6n(QF57GR>A*#^23%s0V}5$91E4{%ZOM%{rlgS zKR?!_E}H-Ter|1D&D-nU>#u$6U6>cV{lw?U#meU{TsM6C?9j}$3Fl7 diff --git a/src/main/webapp/images/r-tile-down.png b/src/main/webapp/images/r-tile-down.png deleted file mode 100644 index 5fb5c59052e312c6b5c711603a158302cebd5987..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pq0`gFF{C2y?G;5oXGal-i!c2-0@@F4_}{wV zCWBaTr?$YPX<~H+0ruZzJ{wJbo|k6k-+%tN;mX@@zwW5tA$@vNWd6S*ncdT`f86#z z;{4~xYnDrAzrCvYsp`k|PkXI5uXO&u!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~ln2F{C2y?cI%D%#I=s7w@Vx{rDfBGWXh{ zbElkIM7%f%=EMNzrxSYsw=m*Pp7NfVFC8(%e1HLyRIt#)atDl1x5~or>mdK II;Vst0L=5LI{*Lx diff --git a/src/main/webapp/images/r-tile-right.png b/src/main/webapp/images/r-tile-right.png deleted file mode 100644 index 495819724f8414097fdccd0c16a004b7eff5956e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA>Y%*F{C2y?OjJNW=8>si+z%YoP~-N{}1mt z$66#PDC5cUoZ;ZzGxhwlmR*yt3Y>i})^6SQgU@Vr&85{#S}q7WxpT-W(v@*!)jnsd zd2z=ce%a$=^Xm2I>ez+%@15WOymV&86`TJRySSHS>X)>lx@>P{{ci*93k_G-I{dWv jFCP$hw_M;%zQed+n@r2UAL(y_@xkEf>gTe~DWM4f^j)5T diff --git a/src/main/webapp/images/r-tile-top-right.png b/src/main/webapp/images/r-tile-top-right.png deleted file mode 100644 index d1e5a7c24f4c0eed81713b2349514c71abb563b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 569 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+P;hd+7V@O5Z+q)b6L;^(`9-eMGCDh~L#KZaU z@BG8J#5Zi+xFz~xtV63KXXkH*@4xq_6{%hRJ2UvsGr5{yKYrHvWYvC3tJzUyAGW&r z>Dm8x`q#(r>wQ04{fdz#-B!lvI6;>1s;*b3=DjSK$uZf!>a+PVY#P^V@O5Z+q-+cjyOm-TugBcu)e7NVQnqz z{KL1-UAyFU^IZI$9q*Qjz25m;gmZG{O z*vilA$&h8X|04S0{k~_L|G&+xye+qm%e`a=lWgUe|Ic1k-HM4@zk2`MIJxC_KR@0j z)o)CHkAXrn>BWWp$qm!)Uf=cWw9y-lC5tnEF}5x@!lvI6;>1s;*b3=DjSK$uZf!>a+Pp}^C{F{C2y?Oj7JXG4aDhf5SZw#(huuHZ{I zUw>h1bf#$Kv|oM`|Nm}lN%^bTs5A3>|MuJsdk))ewpgg#(kEc$^n!!FjA!4U|2FGi zUVE}@+w0Ub_dj1Zi;MfNJMBwxwai_m_3tmFVdQ&MBb@0AzKl6aWAK diff --git a/src/main/webapp/images/tile-down.png b/src/main/webapp/images/tile-down.png deleted file mode 100644 index 3dd1c8e3e66dc9f53026c5479e05b77d4e773b3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+Pp~=(5F{C2y?G-~W7DoYxi|<&4jg#L$sNLJY zary^_*}5kdE#_l(TUYu2`=mv)PT75_sjs|uY|*p7hb?UFdMkHX$Nk&X+rRz##@KVm z_szR(IQ@%o)w%yl^|Ooj$KJfOu>A9k?K;1g|BbRYv0kX$(kEc$^nzn#Gp6zFjXjtA ldym|0-P?7$*BS0(Fx@TYxi~g!lvI6;>1s;*b3=DjSK$uZf!>a+Pq1@BOF{C2y?cI%D&V~{V7iT*;-l?gHUwHoE zn`gH!o_wUHaYOscZ-%2MpINg{`f&cfz==s8e}37a=aajAu8+?XhKbBBd;*pZbY(Pr z{8|%n{O@72qP1Ti|J{^#@JC&330~jw1p00i_>zopr0P*>zC;$Ke diff --git a/src/main/webapp/images/tile-right.png b/src/main/webapp/images/tile-right.png deleted file mode 100644 index 59e18e64648106b66693ba866eef23d7de9daff8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a+PA=T5xF{C2y?Oj8zLk{vXk}v(^6ma2m5TE{rsyD@0xoS_t(t1An4@IA*)!@LSIJrFTTvp_BDL_ zXTIB>+A8P&Jke&}bMg3U+n#qVZ~b0yEmUsl6R>i6!2xDey?s_XLp0;Sqp$^k&c8sC ffUBSfBT?eso(^gIj9W#ZSYYsU^>bP0l+XkKhG(GI diff --git a/src/main/webapp/images/tile-top-right.png b/src/main/webapp/images/tile-top-right.png deleted file mode 100644 index e7580c793f9a26808f3b699da60e1ea411f55e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmeAS@N?(olHy`uVBq!ia0vp^RUpj41|$n2-Jbv{#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>fUTfl1NR#WAEJ?(JP)ze5f(4j*SKbrzcShZY47uD}PF{6;_iJmes41MeYxeCayHjTu z`*_Yi-X1dVTCUZ+b$i>cw4eJK@j4ef|P{Rp$h_SZ3ZSS z3l({I6EirRooOc|XjYHdk$()U+7Q{c3`x`EMb8`94R|)UzZmk#BE`G52UG9q5JI$w-FFOA> u;E#Bsvpe_K=tSr2`E&>|)Ch6%ntw;gzy4>B6)*uYFnGH9xvX Date: Thu, 11 Sep 2014 22:07:50 +0200 Subject: [PATCH 21/21] M2Releases triggered from Pipeline View Added test-dependencies to avoid version clashes Needed to upgrade dashboard view to 2.9.4 --- pom.xml | 30 +++++++++++++++++-- .../buildpipeline/BuildPipelineView.java | 26 +++++++++++++++- .../buildpipeline/BuildPipelineView/bpp.jelly | 8 +++++ src/main/webapp/css/main.css | 2 +- src/main/webapp/js/build-pipeline.js | 2 +- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 28a13f43..ca95dc75 100644 --- a/pom.xml +++ b/pom.xml @@ -95,13 +95,14 @@ org.hamcrest hamcrest-core - 1.2 + 1.3 test + org.jenkins-ci.plugins dashboard-view - 2.2 + 2.9.4 true @@ -109,6 +110,31 @@ parameterized-trigger 2.17 + + + org.jenkins-ci.plugins.m2release + m2release + 0.14.0 + + + org.apache.maven + maven-artifact + 3.0.3 + test + + + org.apache.maven + maven-model + 3.0.3 + test + + + org.apache.maven + maven-settings + 3.0.3 + test + org.seleniumhq.selenium diff --git a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java index 07a6aee3..b999962d 100644 --- a/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java +++ b/src/main/java/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView.java @@ -72,6 +72,7 @@ import javax.servlet.ServletException; +import org.jvnet.hudson.plugins.m2release.M2ReleaseAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -931,5 +932,28 @@ public boolean hasPermission(final Permission p) { } return display; - } + } + + /** + * Check for M2ReleaseAction, if present, then provide an icon on top + * + * @return boolean - whether the first job is a m2release job + */ + public boolean isM2Release() { + if (Jenkins.getInstance().getPlugin("m2release") == null) { + return false; + } + final String firstJobName = ((DownstreamProjectGridBuilder) gridBuilder).getFirstJob(); + final AbstractProject project = Jenkins.getInstance().getItem(firstJobName, this.getOwnerItemGroup(), + AbstractProject.class); + if (project != null) { + // If a M2ReleaseAction is found + final List actions = project.getActions(M2ReleaseAction.class); + if (actions != null && !actions.isEmpty()) { + return true; + } + } + return false; + } + } diff --git a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly index 4806e19a..77e78016 100644 --- a/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly +++ b/src/main/resources/au/com/centrumsystems/hudson/plugin/buildpipeline/BuildPipelineView/bpp.jelly @@ -240,6 +240,14 @@
Run
+ +
+
+ M2Release in a Pipeline +
+
Release
+
+
diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 7dcd5c7e..1d326fc4 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -61,7 +61,7 @@ height: 48px; margin-left: auto; margin-right: auto; - width: 340px; + width: 392px; } .icon-container { diff --git a/src/main/webapp/js/build-pipeline.js b/src/main/webapp/js/build-pipeline.js index dca5d729..34b27d04 100644 --- a/src/main/webapp/js/build-pipeline.js +++ b/src/main/webapp/js/build-pipeline.js @@ -102,7 +102,7 @@ BuildPipeline.prototype = { type: 'iframe', title: title, titlePosition: 'outside', - href: '/' + href, + href: href, transitionIn : 'elastic', transitionOut : 'elastic', width: '90%',