-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Save information about child jobs in parent job #13
Changes from 1 commit
46b0391
755b480
05a8806
66aa284
42aec90
9b0afd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package org.jenkinsci.plugins.workflow.support.steps.build; | ||
|
||
import hudson.model.InvisibleAction; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
|
||
public class BuildInfoAction extends InvisibleAction { | ||
private final List<BuildInfo> buildInfoList = new ArrayList<>(); | ||
|
||
public BuildInfoAction(String projectName, int buildNumber) { | ||
buildInfoList.add(new BuildInfo(projectName, buildNumber)); | ||
} | ||
|
||
public List<BuildInfo> getBuildInfoList() { | ||
return buildInfoList; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap with unmodifyableList() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gone. |
||
} | ||
|
||
public void addBuildInfo(String projectName, int buildNumber) { | ||
buildInfoList.add(new BuildInfo(projectName, buildNumber)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems there may be concurrency issues with this field. I would recommend CopyOnWrite list or There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CopyOnWrite looks like overkill, |
||
} | ||
|
||
static class BuildInfo { | ||
private final String projectName; | ||
private final int buildNumber; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Easier just to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does not look easier for me, in this case I would need one String concatenation + |
||
|
||
public BuildInfo(String projectName, int buildNumber) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach may start leaking job existence fact if the API user has no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How can I prevent this? For now I made this class private and only list of runs can be retrieved from action. I hope that it would be safer. |
||
this.projectName = projectName; | ||
this.buildNumber = buildNumber; | ||
} | ||
|
||
public String getProjectName() { | ||
return projectName; | ||
} | ||
|
||
public int getBuildNumber() { | ||
return buildNumber; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
import hudson.model.TaskListener; | ||
import hudson.model.listeners.RunListener; | ||
import java.util.logging.Level; | ||
import org.jenkinsci.plugins.workflow.graph.FlowNode; | ||
import org.jenkinsci.plugins.workflow.steps.StepContext; | ||
|
||
import java.util.logging.Logger; | ||
|
@@ -29,6 +30,15 @@ public void onStarted(Run<?, ?> run, TaskListener listener) { | |
TaskListener taskListener = stepContext.get(TaskListener.class); | ||
// encodeTo(Run) calls getDisplayName, which does not include the project name. | ||
taskListener.getLogger().println("Starting building: " + ModelHyperlinkNote.encodeTo("/" + run.getUrl(), run.getFullDisplayName())); | ||
FlowNode parentNode = stepContext.get(FlowNode.class); | ||
if (parentNode != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @oleg-nenashev, I suppose that this code is not thread-safe as well. Is it correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to check for null if defined via |
||
BuildInfoAction buildInfoAction = parentNode.getAction(BuildInfoAction.class); | ||
if (buildInfoAction == null) { | ||
parentNode.addAction(new BuildInfoAction(run.getParent().getFullName(), run.getNumber())); | ||
} else { | ||
buildInfoAction.addBuildInfo(run.getParent().getFullName(), run.getNumber()); | ||
} | ||
} | ||
} catch (Exception e) { | ||
LOGGER.log(WARNING, null, e); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,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.graph.FlowGraphWalker; | ||
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; | ||
|
@@ -69,20 +71,30 @@ public class BuildTriggerStepTest { | |
us.setDefinition(new CpsFlowDefinition( | ||
"def ds = build 'ds'\n" + | ||
"echo \"ds.result=${ds.result} ds.number=${ds.number}\"", true)); | ||
j.assertLogContains("ds.result=SUCCESS ds.number=1", j.buildAndAssertSuccess(us)); | ||
WorkflowRun usRun = j.buildAndAssertSuccess(us); | ||
j.assertLogContains("ds.result=SUCCESS ds.number=1", usRun); | ||
// TODO JENKINS-28673 assert no warnings, as in StartupTest.noWarnings | ||
// (but first need to deal with `WARNING: Failed to instantiate optional component org.jenkinsci.plugins.workflow.steps.scm.SubversionStep$DescriptorImpl; skipping`) | ||
ds.getBuildByNumber(1).delete(); | ||
|
||
assertBuildInfoAction(usRun, "ds", 1); | ||
} | ||
|
||
@Issue("JENKINS-25851") | ||
@Test public void failingBuild() throws Exception { | ||
j.createFreeStyleProject("ds").getBuildersList().add(new FailureBuilder()); | ||
FreeStyleProject ds = j.createFreeStyleProject("ds"); | ||
ds.getBuildersList().add(new FailureBuilder()); | ||
WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); | ||
us.setDefinition(new CpsFlowDefinition("build 'ds'", true)); | ||
j.assertBuildStatus(Result.FAILURE, us.scheduleBuild2(0)); | ||
WorkflowRun usRun = j.assertBuildStatus(Result.FAILURE, us.scheduleBuild2(0)); | ||
assertBuildInfoAction(usRun, "ds", ds.getLastBuild().getNumber()); | ||
|
||
us.setDefinition(new CpsFlowDefinition("echo \"ds.result=${build(job: 'ds', propagate: false).result}\"", true)); | ||
j.assertLogContains("ds.result=FAILURE", j.buildAndAssertSuccess(us)); | ||
|
||
usRun = j.buildAndAssertSuccess(us); | ||
j.assertLogContains("ds.result=FAILURE", usRun); | ||
|
||
assertBuildInfoAction(usRun, "ds", ds.getLastBuild().getNumber()); | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
|
@@ -115,7 +127,10 @@ public void buildParallelTests() throws Exception { | |
" build('test2');\n" + | ||
" })"), "\n"), true)); | ||
|
||
j.buildAndAssertSuccess(foo); | ||
WorkflowRun usRun = j.buildAndAssertSuccess(foo); | ||
|
||
assertBuildInfoAction(usRun, "test1", p1.getLastBuild().getNumber()); | ||
assertBuildInfoAction(usRun, "test2", p2.getLastBuild().getNumber()); | ||
} | ||
|
||
|
||
|
@@ -141,7 +156,11 @@ public void abortBuild() throws Exception { | |
fb.getExecutor().interrupt(); | ||
|
||
j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(fb)); | ||
j.assertBuildStatus(Result.FAILURE,q.get()); | ||
|
||
WorkflowRun usRun = q.get(); | ||
j.assertBuildStatus(Result.FAILURE, q.get()); | ||
|
||
assertBuildInfoAction(usRun, "test1", p.getLastBuild().getNumber()); | ||
} | ||
|
||
@Test | ||
|
@@ -420,4 +439,21 @@ public MultiBranchProjectFactory newInstance() { | |
} | ||
} | ||
} | ||
|
||
private void assertBuildInfoAction(WorkflowRun workflowRun, String projectName, int buildNumber) { | ||
FlowGraphWalker walker = new FlowGraphWalker(workflowRun.getExecution()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐜 Deprecated, use DepthFirstScanner instead There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It needs @svanoort, @jglick, @oleg-nenashev So, what should I do? |
||
|
||
for(FlowNode step : walker) { | ||
BuildInfoAction buildInfoAction = step.getAction(BuildInfoAction.class); | ||
if (buildInfoAction != null) { | ||
for (BuildInfoAction.BuildInfo buildInfo : buildInfoAction.getBuildInfoList()) { | ||
if (buildInfo.getBuildNumber() == buildNumber && buildInfo.getProjectName().equals(projectName)) { | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
|
||
throw new AssertionError("Didn't find expected buildInfoAction"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no problem with making it visible, but maybe it's outside the scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can put this comment to the code ;)