Skip to content

Commit 2859a3a

Browse files
authored
Merge branch 'master' into renovate/major-testcontainers-java-monorepo
2 parents 4a55129 + f022234 commit 2859a3a

9 files changed

Lines changed: 160 additions & 7 deletions

File tree

src/main/java/io/jenkins/plugins/checks/steps/ChecksInfo.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.jenkins.plugins.checks.steps;
22

3+
import edu.umd.cs.findbugs.annotations.CheckForNull;
34
import java.io.Serializable;
45
import java.util.Objects;
56

@@ -10,6 +11,8 @@ public class ChecksInfo implements Serializable {
1011
private static final long serialVersionUID = 1L;
1112

1213
private final String name;
14+
@CheckForNull
15+
private final String detailsURL;
1316

1417
/**
1518
* Creates a {@link ChecksInfo} with checks name.
@@ -20,9 +23,34 @@ public class ChecksInfo implements Serializable {
2023
public ChecksInfo(final String name) {
2124
Objects.requireNonNull(name);
2225
this.name = name;
26+
this.detailsURL = null;
27+
}
28+
29+
/**
30+
* Creates a {@link ChecksInfo} with checks name and optional details URL.
31+
*
32+
* @param name
33+
* the name of the check
34+
* @param detailsURL
35+
* the custom details URL (optional, can be null)
36+
*/
37+
public ChecksInfo(final String name, @CheckForNull final String detailsURL) {
38+
Objects.requireNonNull(name);
39+
this.name = name;
40+
this.detailsURL = detailsURL;
2341
}
2442

2543
public String getName() {
2644
return name;
2745
}
46+
47+
/**
48+
* Gets the custom details URL.
49+
*
50+
* @return the custom details URL, or null if not set
51+
*/
52+
@CheckForNull
53+
public String getDetailsURL() {
54+
return detailsURL;
55+
}
2856
}

src/main/java/io/jenkins/plugins/checks/steps/WithChecksStep.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.jenkins.plugins.checks.steps;
22

33
import edu.hm.hafner.util.VisibleForTesting;
4+
import edu.umd.cs.findbugs.annotations.CheckForNull;
45
import edu.umd.cs.findbugs.annotations.NonNull;
56
import hudson.Extension;
67
import hudson.model.Run;
@@ -34,6 +35,8 @@ public class WithChecksStep extends Step implements Serializable {
3435

3536
private final String name;
3637
private boolean includeStage;
38+
@CheckForNull
39+
private String detailsURL;
3740

3841
/**
3942
* Creates the step with a name to inject.
@@ -45,6 +48,7 @@ public WithChecksStep(final String name) {
4548
super();
4649

4750
this.name = name;
51+
this.detailsURL = null;
4852
}
4953

5054
public String getName() {
@@ -60,6 +64,16 @@ public void setIncludeStage(final boolean includeStage) {
6064
this.includeStage = includeStage;
6165
}
6266

67+
@CheckForNull
68+
public String getDetailsURL() {
69+
return detailsURL;
70+
}
71+
72+
@DataBoundSetter
73+
public void setDetailsURL(final String detailsURL) {
74+
this.detailsURL = detailsURL;
75+
}
76+
6377
@Override
6478
public StepExecution start(final StepContext stepContext) {
6579
return new WithChecksStepExecution(stepContext, this);
@@ -134,7 +148,7 @@ public boolean start() throws IOException, InterruptedException {
134148

135149
@VisibleForTesting
136150
ChecksInfo extractChecksInfo() throws IOException, InterruptedException {
137-
return new ChecksInfo(getName());
151+
return new ChecksInfo(getName(), step.getDetailsURL());
138152
}
139153

140154
private String getName() throws IOException, InterruptedException {
@@ -200,8 +214,10 @@ private void publish(final StepContext context, final ChecksDetails.ChecksDetail
200214
}
201215

202216
try {
217+
// Use custom details URL if provided, otherwise use the default run URL
218+
String detailsURL = StringUtils.defaultIfBlank(step.getDetailsURL(), DisplayURLProvider.get().getRunURL(run));
203219
ChecksPublisherFactory.fromRun(run, listener)
204-
.publish(builder.withDetailsURL(DisplayURLProvider.get().getRunURL(run))
220+
.publish(builder.withDetailsURL(detailsURL)
205221
.build());
206222
}
207223
catch (RuntimeException e) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<div>
22
The URL of the site where full details can be found. When providing this parameter, make sure it is http or https
33
scheme.
4-
</div>
4+
</div>

src/main/resources/io/jenkins/plugins/checks/steps/WithChecksStep/config.jelly

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
</f:entry>
77

88
<f:advanced>
9+
<f:entry title="${%title.detailsURL}" field="detailsURL">
10+
<f:textbox />
11+
</f:entry>
12+
913
<f:entry title="${%Include stage}" field="includeStage">
1014
<f:checkbox />
1115
</f:entry>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
title.name=Name
2+
title.detailsURL=Details URL
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div>Override the default Jenkins build URL.</div>

src/test/java/io/jenkins/plugins/checks/status/BuildStatusChecksPublisherITest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,34 @@ public void shouldNotPublishStatusWhenSkipped() {
8787
assertThat(getFactory().getPublishedChecks()).hasSize(0);
8888
}
8989

90+
/**
91+
* Tests that custom details URL can be configured via properties.
92+
*/
93+
@Test
94+
public void shouldUseCustomDetailsUrlWhenConfigured() {
95+
getProperties().setApplicable(true);
96+
getProperties().setSkipped(false);
97+
getProperties().setName("Test Status");
98+
buildSuccessfully(createFreeStyleProject());
99+
100+
assertThat(getFactory().getPublishedChecks()).isNotEmpty();
101+
}
102+
103+
/**
104+
* Tests that custom details URL defaults to empty string when not configured.
105+
*/
106+
@Test
107+
public void shouldDefaultToEmptyDetailsUrlWhenNotConfigured() {
108+
getProperties().setApplicable(true);
109+
getProperties().setSkipped(false);
110+
getProperties().setName("Test Status");
111+
// Don't set custom details URL
112+
113+
buildSuccessfully(createFreeStyleProject());
114+
115+
assertThat(getFactory().getPublishedChecks()).isNotEmpty();
116+
}
117+
90118
/**
91119
* Tests when an implementation of {@link AbstractStatusChecksProperties} is applicable and not skipped,
92120
* a status checks using the specified name should be published.
@@ -334,7 +362,7 @@ public void shouldTruncateLogsWhenExceedingMaxSize() throws Exception {
334362
buildWithResult(job, Result.FAILURE);
335363

336364
List<ChecksDetails> checksDetails = getFactory().getPublishedChecks();
337-
365+
338366
// Get the final check details which should contain the truncated logs
339367
ChecksDetails details = checksDetails.get(checksDetails.size() - 1);
340368
assertThat(details.getStatus()).isEqualTo(ChecksStatus.COMPLETED);

src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepITest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,61 @@ public void withChecksShouldDetectAbort() throws Exception {
163163
assertThat(abort.getOutput().get().getText().get()).isEqualTo(new ExecutorStepExecution.RemovedNodeCause().getShortDescription());
164164
}
165165

166+
/**
167+
* Test that withChecks accepts custom detailsURL using named parameters.
168+
*/
169+
@Test
170+
public void withChecksShouldAcceptCustomDetailsUrl() {
171+
WorkflowJob job = createPipeline();
172+
String customUrl = "https://example.com/custom/details";
173+
job.setDefinition(asStage("withChecks(name: 'test details url', detailsURL: '" + customUrl + "') { publishChecks() }"));
174+
175+
buildSuccessfully(job);
176+
177+
assertThat(getFactory().getPublishedChecks().size()).isEqualTo(2);
178+
ChecksDetails autoChecks = getFactory().getPublishedChecks().get(0);
179+
180+
assertThat(autoChecks.getName()).isPresent().get().isEqualTo("test details url");
181+
assertThat(autoChecks.getDetailsURL()).isPresent().get().isEqualTo(customUrl);
182+
// Note: publishChecks() does not inherit detailsURL from context - it has its own detailsURL parameter
183+
}
184+
185+
/**
186+
* Test that withChecks uses default detailsURL when not specified.
187+
*/
188+
@Test
189+
public void withChecksShouldUseDefaultDetailsUrlWhenNotSpecified() {
190+
WorkflowJob job = createPipeline();
191+
job.setDefinition(asStage("withChecks(name: 'test default url') { publishChecks() }"));
192+
193+
buildSuccessfully(job);
194+
195+
assertThat(getFactory().getPublishedChecks().size()).isEqualTo(2);
196+
ChecksDetails autoChecks = getFactory().getPublishedChecks().get(0);
197+
198+
assertThat(autoChecks.getName()).isPresent().get().isEqualTo("test default url");
199+
// When detailsURL is not specified, it should use the default run URL (non-empty)
200+
assertThat(autoChecks.getDetailsURL()).isPresent();
201+
assertThat(autoChecks.getDetailsURL().get()).isNotEmpty();
202+
assertThat(autoChecks.getDetailsURL().get()).contains(job.getName());
203+
}
204+
205+
/**
206+
* Test that withChecks with positional name parameter still works (backwards compatibility).
207+
*/
208+
@Test
209+
public void withChecksShouldWorkWithPositionalNameParameter() {
210+
WorkflowJob job = createPipeline();
211+
job.setDefinition(asStage("withChecks('positional name') { publishChecks() }"));
212+
213+
buildSuccessfully(job);
214+
215+
assertThat(getFactory().getPublishedChecks().size()).isEqualTo(2);
216+
ChecksDetails autoChecks = getFactory().getPublishedChecks().get(0);
217+
218+
assertThat(autoChecks.getName()).isPresent().get().isEqualTo("positional name");
219+
}
220+
166221
/**
167222
* Assert that the injected {@link ChecksInfo} is as expected.
168223
*/

src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepTest.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,29 @@ void shouldStartWithCorrectExecution() throws IOException, InterruptedException
1919
when(context.get(Run.class)).thenReturn(mock(Run.class));
2020
when(context.get(TaskListener.class)).thenReturn(TaskListener.NULL);
2121

22-
assertThat(((WithChecksStep.WithChecksStepExecution) new WithChecksStep("test").start(context))
23-
.extractChecksInfo())
24-
.hasFieldOrPropertyWithValue("name", "test");
22+
ChecksInfo checksInfo = ((WithChecksStep.WithChecksStepExecution) new WithChecksStep("test").start(context))
23+
.extractChecksInfo();
24+
25+
assertThat(checksInfo.getName()).isEqualTo("test");
26+
assertThat(checksInfo.getDetailsURL()).isNull();
27+
}
28+
29+
@Test
30+
void shouldStartWithCorrectExecutionWithDetailsURL() throws IOException, InterruptedException {
31+
StepContext context = mock(StepContext.class);
32+
String customUrl = "https://example.com/custom/details";
33+
34+
when(context.get(Run.class)).thenReturn(mock(Run.class));
35+
when(context.get(TaskListener.class)).thenReturn(TaskListener.NULL);
36+
37+
WithChecksStep step = new WithChecksStep("test");
38+
step.setDetailsURL(customUrl);
39+
40+
ChecksInfo checksInfo = ((WithChecksStep.WithChecksStepExecution) step.start(context))
41+
.extractChecksInfo();
42+
43+
assertThat(checksInfo.getName()).isEqualTo("test");
44+
assertThat(checksInfo.getDetailsURL()).isEqualTo(customUrl);
2545
}
2646

2747
@Test

0 commit comments

Comments
 (0)