Skip to content

Commit 787fc13

Browse files
authored
Merge pull request #1963 from Jay-oao/JENKINS-57636
Display Project Actions from previous builds when missing in latest build
2 parents f3fb201 + 8554517 commit 787fc13

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package io.jenkins.plugins.analysis.core.model;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
import edu.umd.cs.findbugs.annotations.NonNull;
9+
10+
import hudson.Extension;
11+
import hudson.model.Action;
12+
import hudson.model.Job;
13+
import hudson.model.Run;
14+
import jenkins.model.TransientActionFactory;
15+
16+
/**
17+
* Registers this class as a Jenkins extension that provides fallback for analysis builds.
18+
* This helps display warnings in the job view even when no analysis is present in the latest build.
19+
* The actions are rendered by finding the most recent build with valid {@link ResultAction} instances,
20+
* and then attaching the corresponding {@link JobAction} and {@link TransientProjectResultAction} to the job.
21+
*/
22+
@Extension
23+
public final class MissingResultFallbackHandler extends TransientActionFactory<Job<?, ?>> {
24+
@Override
25+
@SuppressWarnings("unchecked")
26+
public Class<Job<?, ?>> type() {
27+
return (Class) Job.class;
28+
}
29+
30+
@NonNull
31+
@Override
32+
public Collection<? extends Action> createFor(@NonNull final Job<?, ?> target) {
33+
Run<?, ?> currentBuild = target.getLastBuild();
34+
if (currentBuild == null || currentBuild.isBuilding()) {
35+
return Collections.emptyList();
36+
}
37+
38+
List<ResultAction> currentResultActions = currentBuild.getActions(ResultAction.class);
39+
if (!currentResultActions.isEmpty()) {
40+
return Collections.emptyList();
41+
}
42+
43+
for (Run<?, ?> previousBuild = currentBuild.getPreviousBuild(); previousBuild != null; previousBuild = previousBuild.getPreviousBuild()) {
44+
List<ResultAction> resultActions = previousBuild.getActions(ResultAction.class);
45+
46+
List<Action> actions = new ArrayList<>();
47+
for (ResultAction action : resultActions) {
48+
actions.addAll(action.getProjectActions());
49+
actions.add(new TransientProjectResultAction(action));
50+
}
51+
52+
if (!resultActions.isEmpty()) {
53+
return actions;
54+
}
55+
}
56+
57+
return Collections.emptyList();
58+
}
59+
60+
/**
61+
* A wrapper record for {@link ResultAction} that provides an absolute URL for the link on the side panel.
62+
*
63+
* @param resultAction
64+
* Valid Result Action
65+
*/
66+
private record TransientProjectResultAction(ResultAction resultAction) implements Action {
67+
@Override
68+
public String getIconFileName() {
69+
return resultAction.getIconFileName();
70+
}
71+
72+
@Override
73+
public String getDisplayName() {
74+
return resultAction.getDisplayName();
75+
}
76+
77+
@Override
78+
public String getUrlName() {
79+
return resultAction.getAbsoluteUrl();
80+
}
81+
}
82+
}

plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/JobActionITest.java

+38
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import hudson.model.Run;
1313

1414
import io.jenkins.plugins.analysis.core.model.AggregatedTrendAction;
15+
import io.jenkins.plugins.analysis.core.model.MissingResultFallbackHandler;
1516
import io.jenkins.plugins.analysis.core.model.AnalysisResult;
1617
import io.jenkins.plugins.analysis.core.model.JobAction;
1718
import io.jenkins.plugins.analysis.core.model.ReportScanningTool;
@@ -228,6 +229,43 @@ void shouldHaveSidebarLinkEvenWhenLastActionHasNoResults() {
228229
assertThat(jobAction).isNotNull();
229230
}
230231

232+
/**
233+
* Verifies the behaviour of {@link MissingResultFallbackHandler}.
234+
*/
235+
@Test
236+
void shouldAttachJobActionsWithoutRecordIssues() {
237+
FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix(ECLIPSE_LOG);
238+
enableEclipseWarnings(project);
239+
240+
Run<?, ?> firstBuild = buildWithResult(project, Result.SUCCESS);
241+
List<ResultAction> firstBuildResultActions = firstBuild.getActions(ResultAction.class);
242+
assertThat(firstBuildResultActions).isNotEmpty();
243+
244+
List<JobAction> jobActionsAfterFirstBuild = project.getActions(JobAction.class);
245+
assertThat(jobActionsAfterFirstBuild).isNotEmpty();
246+
assertThatTrendChartIsHidden(jobActionsAfterFirstBuild.get(0));
247+
248+
Run<?, ?> secondBuild = buildWithResult(project, Result.SUCCESS);
249+
List<ResultAction> secondBuildResultActions = secondBuild.getActions(ResultAction.class);
250+
assertThat(secondBuildResultActions).isNotEmpty();
251+
252+
List<JobAction> jobActionsAfterSecondBuild = project.getActions(JobAction.class);
253+
assertThat(jobActionsAfterSecondBuild).isNotEmpty();
254+
assertThatTrendChartIsVisible(jobActionsAfterSecondBuild.get(0));
255+
256+
project.getPublishersList().clear();
257+
258+
Run<?, ?> thirdBuild = buildWithResult(project, Result.SUCCESS);
259+
List<ResultAction> thirdBuildResultActions = thirdBuild.getActions(ResultAction.class);
260+
assertThat(thirdBuildResultActions).isEmpty();
261+
262+
List<JobAction> jobActionsAfterThirdBuild = project.getActions(JobAction.class);
263+
assertThat(jobActionsAfterThirdBuild).isNotEmpty();
264+
assertThat(jobActionsAfterSecondBuild.get(0).getId()).isEqualTo(jobActionsAfterThirdBuild.get(0).getId());
265+
assertThat(jobActionsAfterSecondBuild.get(0).getUrlName()).isEqualTo(jobActionsAfterThirdBuild.get(0).getUrlName());
266+
assertThatTrendChartIsVisible(jobActionsAfterThirdBuild.get(0));
267+
}
268+
231269
private void assertThatTrendChartIsVisible(final AsyncConfigurableTrendChart trendChart) {
232270
assertThat(trendChart.isTrendVisible()).isTrue();
233271
}

0 commit comments

Comments
 (0)