Skip to content

Commit

Permalink
Merge pull request #18 from sophistifunk/feature/JENKINS-38339-link-d…
Browse files Browse the repository at this point in the history
…ownstream-builds-to-trigger-node

Mark downstream runs with pointer to upstream FlowNode
  • Loading branch information
abayer committed Dec 18, 2017
2 parents 2e4012e + 6e0f22d commit 4e115c5
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -3,3 +3,5 @@ work
.project
.classpath
.settings
.idea/
*.iml
Expand Up @@ -24,15 +24,6 @@
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;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import org.jenkinsci.plugins.workflow.actions.LabelAction;
Expand All @@ -41,6 +32,16 @@
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
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;
import java.util.logging.Logger;

public class BuildTriggerStepExecution extends AbstractStepExecutionImpl {

private static final Logger LOGGER = Logger.getLogger(BuildTriggerStepExecution.class.getName());
Expand All @@ -65,18 +66,23 @@ public boolean start() throws Exception {
// TODO find some way of allowing ComputedFolders to hook into the listener code
throw new AbortException("Waiting for non-job items is not supported");
}

List<Action> actions = new ArrayList<>();
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));
actions.add(new BuildUpstreamNodeAction(node, invokingRun));

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});
}
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));

List<ParameterValue> parameters = step.getParameters();
if (parameters != null) {
parameters = completeDefaultParameters(parameters, (Job) project);
Expand All @@ -103,13 +109,12 @@ protected Job asJob() {
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 {
Expand Down
@@ -0,0 +1,53 @@
package org.jenkinsci.plugins.workflow.support.steps.build;

import hudson.model.InvisibleAction;
import hudson.model.Run;
import org.jenkinsci.plugins.workflow.graph.FlowNode;

import java.util.Objects;

/**
* Attached to newly-created builds in order to point back to the triggering FlowNode.
*
* We annotate the downstream build instead of upstream in order to support no-wait builds,
* because the downstream run id is not available when they're still queued.
*
* Needed for Blue Ocean to annotate the correct step.
* See <a href="https://issues.jenkins-ci.org/browse/JENKINS-38339">#JENKINS-38339</a>
*/
public class BuildUpstreamNodeAction extends InvisibleAction {

private final String upstreamNodeId;
private final String upstreamRunId;

public BuildUpstreamNodeAction(FlowNode node, Run<?, ?> invokingRun) {
this.upstreamNodeId = node.getId();
this.upstreamRunId = invokingRun.getExternalizableId();
}

public String getUpstreamNodeId() {
return upstreamNodeId;
}

public String getUpstreamRunId() {
return upstreamRunId;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BuildUpstreamNodeAction that = (BuildUpstreamNodeAction) o;
return Objects.equals(upstreamNodeId, that.upstreamNodeId) &&
Objects.equals(upstreamRunId, that.upstreamRunId);
}

@Override
public int hashCode() {
return Objects.hash(upstreamNodeId, upstreamRunId);
}
}
Expand Up @@ -39,6 +39,8 @@
import static org.hamcrest.Matchers.nullValue;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
Expand All @@ -61,7 +63,7 @@
import org.jvnet.hudson.test.recipes.LocalData;

public class BuildTriggerStepTest {

@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
@Rule public JenkinsRule j = new JenkinsRule();
@Rule public LoggerRule logging = new LoggerRule();
Expand Down Expand Up @@ -93,6 +95,30 @@ public class BuildTriggerStepTest {
j.assertLogContains("ds.result=FAILURE", j.buildAndAssertSuccess(us));
}

@Issue("JENKINS-38339")
@Test public void upstreamNodeAction() throws Exception {
FreeStyleProject downstream = j.createFreeStyleProject("downstream");
WorkflowJob upstream = j.jenkins.createProject(WorkflowJob.class, "upstream");

upstream.setDefinition(new CpsFlowDefinition("build 'downstream'", true));
j.assertBuildStatus(Result.SUCCESS, upstream.scheduleBuild2(0));

WorkflowRun lastUpstreamRun = upstream.getLastBuild();
FreeStyleBuild lastDownstreamRun = downstream.getLastBuild();

final FlowExecution execution = lastUpstreamRun.getExecution();
List<FlowNode> nodes = execution.getCurrentHeads();
assertEquals("node count", 1, nodes.size());
FlowNode headNode = nodes.get(0);

List<BuildUpstreamNodeAction> actions = lastDownstreamRun.getActions(BuildUpstreamNodeAction.class);
assertEquals("action count", 1, actions.size());

BuildUpstreamNodeAction action = actions.get(0);
assertEquals("correct upstreamRunId", action.getUpstreamRunId(), lastUpstreamRun.getExternalizableId());
assertNotNull("valid upstreamNodeId", execution.getNode(action.getUpstreamNodeId()));
}

@SuppressWarnings("deprecation")
@Test
public void buildFolderProject() throws Exception {
Expand Down

0 comments on commit 4e115c5

Please sign in to comment.