Skip to content

Commit

Permalink
[JENKINS-38887] Allow triggering ComputedFolders and other Queue.Task…
Browse files Browse the repository at this point in the history
… implementations

- Cannot wait for non-Job tasks to complete though
  • Loading branch information
stephenc committed Mar 6, 2017
1 parent 9a3db1e commit f02e64e
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 29 deletions.
19 changes: 19 additions & 0 deletions pom.xml
Expand Up @@ -94,5 +94,24 @@
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>branch-api</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.7</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Expand Up @@ -9,7 +9,9 @@
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.Computer;
import hudson.model.Describable;
import hudson.model.Executor;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterValue;
Expand All @@ -20,7 +22,10 @@
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.queue.QueueTaskFuture;
import hudson.model.queue.ScheduleResult;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
Expand Down Expand Up @@ -53,39 +58,86 @@ public class BuildTriggerStepExecution extends AbstractStepExecutionImpl {
@Override
public boolean start() throws Exception {
String job = step.getJob();
final ParameterizedJobMixIn.ParameterizedJob project = Jenkins.getActiveInstance().getItem(job, invokingRun.getParent(), ParameterizedJobMixIn.ParameterizedJob.class);
if (project == null) {
throw new AbortException("No parameterized job named " + job + " found");
Item item = Jenkins.getActiveInstance().getItem(job, invokingRun.getParent(), Item.class);
if (item == null) {
throw new AbortException("No item named " + job + " found");
}
listener.getLogger().println("Scheduling project: " + ModelHyperlinkNote.encodeTo(project));

node.addAction(new LabelAction(Messages.BuildTriggerStepExecution_building_(project.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[] {project, context});
}
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));
List<ParameterValue> parameters = step.getParameters();
if (parameters != null) {
parameters = completeDefaultParameters(parameters, (Job) project);
actions.add(new ParametersAction(parameters));
if (step.getWait() && !(item instanceof Job)) {
// TODO find some way of allowing ComputedFolders to hook into the listener code
throw new AbortException("Waiting for non-job items is not supported");
}
Integer quietPeriod = step.getQuietPeriod();
// TODO use new convenience method in 1.621
if (quietPeriod == null) {
quietPeriod = project.getQuietPeriod();
}
QueueTaskFuture<?> f = new ParameterizedJobMixIn() {
@Override protected Job asJob() {
return (Job) project;
if (item instanceof ParameterizedJobMixIn.ParameterizedJob) {
final ParameterizedJobMixIn.ParameterizedJob project = (ParameterizedJobMixIn.ParameterizedJob) item;
listener.getLogger().println("Scheduling project: " + ModelHyperlinkNote.encodeTo(project));

node.addAction(new LabelAction(Messages.BuildTriggerStepExecution_building_(project.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[]{project, context});
}
}.scheduleBuild2(quietPeriod, actions.toArray(new Action[actions.size()]));
if (f == null) {
throw new AbortException("Failed to trigger build of " + project.getFullName());
}
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));
List<ParameterValue> parameters = step.getParameters();
if (parameters != null) {
parameters = completeDefaultParameters(parameters, (Job) project);
actions.add(new ParametersAction(parameters));
}
Integer quietPeriod = step.getQuietPeriod();
// TODO use new convenience method in 1.621
if (quietPeriod == null) {
quietPeriod = project.getQuietPeriod();
}
QueueTaskFuture<?> f = new ParameterizedJobMixIn() {
@Override
protected Job asJob() {
return (Job) project;
}
}.scheduleBuild2(quietPeriod, actions.toArray(new Action[actions.size()]));
if (f == null) {
throw new AbortException("Failed to trigger build of " + project.getFullName());
}
} else if (item instanceof Queue.Task){
if (step.getParameters() != null && !step.getParameters().isEmpty()) {
throw new AbortException("Item type does not support parameters");
}
Queue.Task task = (Queue.Task) 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) {
// ignore, best effort only
}
}
if (quietPeriod == null) {
quietPeriod = Jenkins.getActiveInstance().getQuietPeriod();
}
ScheduleResult scheduleResult = Jenkins.getInstance().getQueue().schedule2(task, quietPeriod, actions);
if (scheduleResult.isRefused()) {
throw new AbortException("Failed to trigger build of " + item.getFullName());
}
QueueTaskFuture<?> f =
scheduleResult.getItem().getFuture();

} else {
throw new AbortException("The item named " + job + " is a "
+ (item instanceof Describable
? ((Describable) item).getDescriptor().getDisplayName()
: item.getClass().getName())
+ " which is not something that can be built");
}
if (step.getWait()) {
return false;
} else {
Expand Down
@@ -1,27 +1,43 @@
package org.jenkinsci.plugins.workflow.support.steps.build;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.Action;
import hudson.model.BooleanParameterDefinition;
import hudson.model.Cause;
import hudson.model.Executor;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ItemGroup;
import hudson.model.Label;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Queue;
import hudson.model.Result;
import hudson.model.StringParameterDefinition;
import hudson.model.TaskListener;
import hudson.model.queue.QueueTaskFuture;
import hudson.tasks.Shell;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import jenkins.branch.MultiBranchProjectFactory;
import jenkins.branch.MultiBranchProjectFactoryDescriptor;
import jenkins.branch.OrganizationFolder;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMSource;
import jenkins.scm.impl.mock.MockSCMController;
import jenkins.scm.impl.mock.MockSCMNavigator;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;

import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.*;
import org.junit.ClassRule;
import org.junit.Rule;
Expand Down Expand Up @@ -359,4 +375,42 @@ public void cancelBuildQueue() throws Exception {
j.assertBuildStatusSuccess(j.waitForCompletion(us1));
}

@SuppressWarnings("deprecation")
@Test
@Issue("JENKINS-38887")
public void triggerOrgFolder() throws Exception {
try (MockSCMController c = MockSCMController.create()) {
c.createRepository("foo");
WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us");
us.setDefinition(new CpsFlowDefinition("build job:'ds', wait:false"));
OrganizationFolder ds = j.jenkins.createProject(OrganizationFolder.class, "ds");
ds.getSCMNavigators().add(new MockSCMNavigator(c, true, false, false));
ds.getProjectFactories().add(new DummyMultiBranchProjectFactory());
j.waitUntilNoActivity();
assertThat(ds.getComputation().getResult(), nullValue());
j.buildAndAssertSuccess(us);
j.waitUntilNoActivity();
assertThat(ds.getComputation().getResult(), notNullValue());
}
}

public static class DummyMultiBranchProjectFactory extends MultiBranchProjectFactory {
@Override
public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name,
@NonNull List<? extends SCMSource> scmSources,
@NonNull Map<String, Object> attributes,
@CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener)
throws IOException, InterruptedException {
return false;
}

@TestExtension
public static class DescriptorImpl extends MultiBranchProjectFactoryDescriptor {

@Override
public MultiBranchProjectFactory newInstance() {
return new DummyMultiBranchProjectFactory();
}
}
}
}

0 comments on commit f02e64e

Please sign in to comment.