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
+ }
0 commit comments