Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-38887] Add form validation
- Also parameterize scm-api version in pom
- Also minor tweaks in the step execution
  • Loading branch information
stephenc committed Mar 8, 2017
1 parent 042679c commit 0deb44c
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 16 deletions.
5 changes: 3 additions & 2 deletions pom.xml
Expand Up @@ -39,6 +39,7 @@
</pluginRepositories>
<properties>
<jenkins.version>1.642.3</jenkins.version>
<scm-api.version>2.0.7</scm-api.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -103,13 +104,13 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.7</version>
<version>${scm-api.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.7</version>
<version>${scm-api.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
Expand Down
Expand Up @@ -2,18 +2,24 @@

import hudson.Extension;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Describable;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.ItemVisitor;
import hudson.model.Job;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterValue;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Queue;
import hudson.model.TopLevelItem;
import hudson.util.FormValidation;
import java.util.ArrayList;
import java.util.List;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.jenkinsci.plugins.workflow.steps.Step;
Expand Down Expand Up @@ -126,8 +132,61 @@ public String getDisplayName() {
return "Build a job";
}

public AutoCompletionCandidates doAutoCompleteJob(@AncestorInPath ItemGroup<?> context, @QueryParameter String value) {
return AutoCompletionCandidates.ofJobNames(ParameterizedJobMixIn.ParameterizedJob.class, value, context);
public AutoCompletionCandidates doAutoCompleteJob(@AncestorInPath ItemGroup<?> container, @QueryParameter final String value) {
// TODO remove code copy&pasted from AutoCompletionCandidates.ofJobNames when it supports union of classes
final AutoCompletionCandidates candidates = new AutoCompletionCandidates();
class Visitor extends ItemVisitor {
String prefix;

Visitor(String prefix) {
this.prefix = prefix;
}

@Override
public void onItem(Item i) {
String n = contextualNameOf(i);
if ((n.startsWith(value) || value.startsWith(n))
// 'foobar' is a valid candidate if the current value is 'foo'.
// Also, we need to visit 'foo' if the current value is 'foo/bar'
&& (value.length() > n.length() || !n.substring(value.length()).contains("/"))
// but 'foobar/zot' isn't if the current value is 'foo'
// we'll first show 'foobar' and then wait for the user to type '/' to show the rest
&& i.hasPermission(Item.READ)
// and read permission required
) {
if ((i instanceof ParameterizedJobMixIn.ParameterizedJob || i instanceof Queue.Task) && n.startsWith(value))
candidates.add(n);

// recurse
String oldPrefix = prefix;
prefix = n;
super.onItem(i);
prefix = oldPrefix;
}
}

private String contextualNameOf(Item i) {
if (prefix.endsWith("/") || prefix.length() == 0)
return prefix + i.getName();
else
return prefix + '/' + i.getName();
}
}

if (container == null || container == Jenkins.getInstance()) {
new Visitor("").onItemGroup(Jenkins.getInstance());
} else {
new Visitor("").onItemGroup(container);
if (value.startsWith("/"))
new Visitor("/").onItemGroup(Jenkins.getInstance());

for (String p = "../"; value.startsWith(p); p += "../") {
container = ((Item) container).getParent();
new Visitor(p).onItemGroup(container);
}
}
return candidates;
// END of copy&paste
}

@Restricted(DoNotUse.class) // for use from config.jelly
Expand All @@ -143,5 +202,39 @@ public FormValidation doCheckPropagate(@QueryParameter boolean value, @QueryPara
return FormValidation.ok();
}

public FormValidation doCheckWait(@AncestorInPath ItemGroup<?> context, @QueryParameter boolean value, @QueryParameter String job) {
if (!value) {
return FormValidation.ok();
}
Item item = Jenkins.getActiveInstance().getItem(job, context, Item.class);
if (item == null) {
return FormValidation.ok();
}
if (item instanceof Job) {
return FormValidation.ok();
}
return FormValidation.error(Messages.BuildTriggerStep_no_wait_for_non_jobs());
}

public FormValidation doCheckJob(@AncestorInPath ItemGroup<?> context, @QueryParameter String value) {
if (StringUtils.isBlank(value)) {
return FormValidation.ok();
}
Item item = Jenkins.getActiveInstance().getItem(value, context, Item.class);
if (item == null) {
return FormValidation.error(Messages.BuildTriggerStep_cannot_find(value));
}
if (item instanceof Job) {
return FormValidation.ok();
}
if (item instanceof Queue.Task) {
return FormValidation.ok();
}
if (item instanceof Describable) {
return FormValidation.error(Messages.BuildTriggerStep_unsupported(((Describable)item).getDescriptor().getDisplayName()));
}
return FormValidation.error(Messages.BuildTriggerStep_unsupported(item.getClass().getName()));
}

}
}
Expand Up @@ -27,6 +27,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -102,29 +103,26 @@ protected Job asJob() {
throw new AbortException("Item type does not support parameters");
}
Queue.Task task = (Queue.Task) item;
listener.getLogger().println("Scheduling item: " + ModelHyperlinkNote.encodeTo(item));
node.addAction(new LabelAction(Messages.BuildTriggerStepExecution_building_(task.getFullDisplayName())));
List<Action> actions = new ArrayList<>();
if (step.getWait()) {
StepContext context = getContext();
actions.add(new BuildTriggerAction(context, step.isPropagate()));
LOGGER.log(Level.FINER, "scheduling a build of {0} from {1}", new Object[]{task, context});
}
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));
Integer quietPeriod = step.getQuietPeriod();
if (quietPeriod == null) {
try {
Method getQuietPeriod = task.getClass().getMethod("getQuietPeriod");
if (getQuietPeriod.getReturnType().equals(int.class)) {
quietPeriod = (Integer) getQuietPeriod.invoke(task);
}
} catch (NoSuchMethodException | IllegalAccessError | IllegalArgumentException | InvocationTargetException e) {
} catch (NoSuchMethodException e) {
// ignore, best effort only
} catch (IllegalAccessError | IllegalArgumentException | InvocationTargetException e) {
LOGGER.log(Level.WARNING, "Could not determine quiet period of " + item.getFullName(), e);
}
}
if (quietPeriod == null) {
quietPeriod = Jenkins.getActiveInstance().getQuietPeriod();
}
ScheduleResult scheduleResult = Jenkins.getActiveInstance().getQueue().schedule2(task, quietPeriod, actions);
ScheduleResult scheduleResult = Jenkins.getActiveInstance().getQueue().schedule2(task, quietPeriod,
Collections.<Action>singletonList(new CauseAction(new Cause.UpstreamCause(invokingRun))));
if (scheduleResult.isRefused() || scheduleResult.getItem() == null) {
throw new AbortException("Failed to trigger build of " + item.getFullName());
}
Expand Down
Expand Up @@ -7,8 +7,8 @@ l.ajax {
// Cf. BuildTriggerStepExecution:
def contextName = request.getParameter('context')
def context = contextName != null ? app.getItemByFullName(contextName) : null
def job = app.getItem(jobName, (hudson.model.Item) context, jenkins.model.ParameterizedJobMixIn.ParameterizedJob)
if (job != null) {
def job = app.getItem(jobName, (hudson.model.Item) context, hudson.model.Item)
if (job instanceof jenkins.model.ParameterizedJobMixIn.ParameterizedJob) {
def pdp = job.getProperty(hudson.model.ParametersDefinitionProperty)
if (pdp != null) {
// Cf. ParametersDefinitionProperty/index.jelly:
Expand All @@ -23,6 +23,10 @@ l.ajax {
} else {
text("${job.fullDisplayName} is not parameterized")
}
} else if (job instanceof hudson.model.Queue.Task) {
text("${job.fullDisplayName} is not parameterized")
} else if (job != null) {
text("${job.fullDisplayName} is not buildable")
} else {
text("no such job ${jobName}")
}
Expand Down
Expand Up @@ -23,4 +23,7 @@
BuildTriggerStep.explicitly_disabling_both_propagate_and_wait=\
Explicitly disabling both <code>propagate</code> and <code>wait</code> is redundant, since <code>propagate</code> is ignored when <code>wait</code> is disabled. \
For brevity, leave <code>propagate</code> at its default.
BuildTriggerStep.no_wait_for_non_jobs=Waiting for non-job items is not supported
BuildTriggerStep.cannot_find=No such job {0}
BuildTriggerStep.unsupported=Building a {0} is not supported
BuildTriggerStepExecution.building_=Building {0}
Expand Up @@ -404,7 +404,7 @@ public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name,
return false;
}

@TestExtension
@TestExtension("triggerOrgFolder")
public static class DescriptorImpl extends MultiBranchProjectFactoryDescriptor {

@Override
Expand Down

0 comments on commit 0deb44c

Please sign in to comment.