Skip to content

Commit 28ee969

Browse files
lidiams96claude
andauthored
fix: handle null FlowExecution in PipelineLogExtractor (#97)
* fix: handle null FlowExecution in PipelineLogExtractor WorkflowRun.getExecution() can return null when the pipeline execution has not yet started, was interrupted very early, or has been cleaned up. Without this check, FlowGraphWalker throws a NullPointerException. Fall back to the overall build log when execution is null, which is consistent with the existing fallback behavior for non-pipeline builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add PipelineLogExtractor tests for null execution fallback Add test coverage for the null FlowExecution guard: - testNullFlowExecutionFallsBackToBuildLog: verifies that a WorkflowRun with null execution falls back to run.getLog() without throwing NullPointerException - testNonPipelineBuildFallsBackToBuildLog: verifies that non-pipeline builds (FreeStyleBuild) also fall back correctly Add mockito-core as a test dependency to enable mocking WorkflowRun. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d45bb45 commit 28ee969

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@
144144
<scope>test</scope>
145145
</dependency>
146146

147+
<dependency>
148+
<groupId>org.mockito</groupId>
149+
<artifactId>mockito-core</artifactId>
150+
<scope>test</scope>
151+
</dependency>
152+
147153
<dependency>
148154
<groupId>dev.langchain4j</groupId>
149155
<artifactId>langchain4j</artifactId>

src/main/java/io/jenkins/plugins/explain_error/PipelineLogExtractor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ public List<String> getFailedStepLog() throws IOException {
101101

102102
if (this.run instanceof WorkflowRun) {
103103
FlowExecution execution = ((WorkflowRun) this.run).getExecution();
104+
if (execution == null) {
105+
setUrl("0");
106+
return run.getLog(maxLines);
107+
}
104108

105109
FlowGraphWalker walker = new FlowGraphWalker(execution);
106110
for (FlowNode node : walker) {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.jenkins.plugins.explain_error;
2+
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.when;
10+
11+
import hudson.model.FreeStyleBuild;
12+
import hudson.model.FreeStyleProject;
13+
import java.util.List;
14+
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
15+
import org.junit.jupiter.api.Test;
16+
import org.jvnet.hudson.test.JenkinsRule;
17+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
18+
19+
@WithJenkins
20+
class PipelineLogExtractorTest {
21+
22+
@Test
23+
void testNullFlowExecutionFallsBackToBuildLog(JenkinsRule jenkins) throws Exception {
24+
// Create a mock WorkflowRun where getExecution() returns null
25+
WorkflowRun mockRun = mock(WorkflowRun.class);
26+
when(mockRun.getExecution()).thenReturn(null);
27+
when(mockRun.getLog(100)).thenReturn(List.of("Build started", "ERROR: Something failed"));
28+
when(mockRun.getUrl()).thenReturn("job/test/1/");
29+
30+
PipelineLogExtractor extractor = new PipelineLogExtractor(mockRun, 100);
31+
32+
// Should not throw NullPointerException
33+
List<String> logLines = assertDoesNotThrow(() -> extractor.getFailedStepLog());
34+
35+
// Should fall back to build log
36+
assertNotNull(logLines);
37+
assertEquals(2, logLines.size());
38+
assertEquals("ERROR: Something failed", logLines.get(1));
39+
40+
// URL should be set (either console or stages depending on plugin availability)
41+
String url = extractor.getUrl();
42+
assertNotNull(url, "URL should not be null after getFailedStepLog()");
43+
assertTrue(url.contains("job/test/1/"), "URL should reference the build");
44+
}
45+
46+
@Test
47+
void testNonPipelineBuildFallsBackToBuildLog(JenkinsRule jenkins) throws Exception {
48+
// FreeStyleBuild is not a WorkflowRun, so it should skip the pipeline path entirely
49+
FreeStyleProject project = jenkins.createFreeStyleProject();
50+
FreeStyleBuild build = jenkins.buildAndAssertSuccess(project);
51+
52+
PipelineLogExtractor extractor = new PipelineLogExtractor(build, 100);
53+
List<String> logLines = extractor.getFailedStepLog();
54+
55+
assertNotNull(logLines);
56+
assertFalse(logLines.isEmpty());
57+
58+
String url = extractor.getUrl();
59+
assertNotNull(url);
60+
assertTrue(url.contains(build.getUrl()), "URL should reference the build");
61+
}
62+
}

0 commit comments

Comments
 (0)