diff --git a/src/main/frontend/pipeline-graph-view/app.scss b/src/main/frontend/pipeline-graph-view/app.scss
index 45fce486b..7b95005b0 100644
--- a/src/main/frontend/pipeline-graph-view/app.scss
+++ b/src/main/frontend/pipeline-graph-view/app.scss
@@ -26,7 +26,8 @@
}
&:not(:disabled) {
- &:hover, &:active {
+ &:hover,
+ &:active {
svg {
color: var(--text-color);
}
diff --git a/src/main/frontend/pipeline-graph-view/app.tsx b/src/main/frontend/pipeline-graph-view/app.tsx
index 1addc49b1..87491a8c1 100644
--- a/src/main/frontend/pipeline-graph-view/app.tsx
+++ b/src/main/frontend/pipeline-graph-view/app.tsx
@@ -18,7 +18,11 @@ export default function App() {
return (
-
+
);
From f3697367e8c7df307560cd7c0fa6caa4c138b8e3 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Fri, 10 Oct 2025 22:43:26 +0100
Subject: [PATCH 15/28] Update app.tsx
---
src/main/frontend/pipeline-graph-view/app.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/main/frontend/pipeline-graph-view/app.tsx b/src/main/frontend/pipeline-graph-view/app.tsx
index 87491a8c1..837c1e076 100644
--- a/src/main/frontend/pipeline-graph-view/app.tsx
+++ b/src/main/frontend/pipeline-graph-view/app.tsx
@@ -16,7 +16,6 @@ export default function App() {
});
return (
-
-
);
}
From e005e2b407635b116d1de2eaf67172e6909cae00 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Fri, 10 Oct 2025 22:53:19 +0100
Subject: [PATCH 16/28] Push
---
.../pipeline-console/main/components/stages.scss | 3 +--
src/main/frontend/pipeline-graph-view/app.scss | 1 +
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss b/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
index d8fdba22b..d71f54ca1 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
@@ -82,11 +82,10 @@
.pgv-stages-graph__controls {
position: absolute;
- inset: 0.1rem;
+ inset: 0.1875rem;
display: flex;
z-index: 1;
backdrop-filter: blur(10px);
- background: red;
border-radius: 0.45rem;
align-items: center;
color: var(--text-color-secondary);
diff --git a/src/main/frontend/pipeline-graph-view/app.scss b/src/main/frontend/pipeline-graph-view/app.scss
index 7b95005b0..e1b7a82aa 100644
--- a/src/main/frontend/pipeline-graph-view/app.scss
+++ b/src/main/frontend/pipeline-graph-view/app.scss
@@ -23,6 +23,7 @@
svg {
width: 1rem;
height: 1rem;
+ transition: color var(--standard-transition);
}
&:not(:disabled) {
From d5cdb8fa2af5265729d258d99cdca02e0471cd3b Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Fri, 10 Oct 2025 22:56:26 +0100
Subject: [PATCH 17/28] Tidy up
---
.../pipeline-console/main/components/stages.scss | 3 +--
src/main/frontend/pipeline-graph-view/app.scss | 1 +
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss b/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
index 2b9d7869e..d57735ec4 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/components/stages.scss
@@ -87,11 +87,10 @@
.pgv-stages-graph__controls {
position: absolute;
- inset: 0.1rem;
+ inset: 0.1875rem;
display: flex;
z-index: 1;
backdrop-filter: blur(10px);
- background: red;
border-radius: 0.45rem;
align-items: center;
color: var(--text-color-secondary);
diff --git a/src/main/frontend/pipeline-graph-view/app.scss b/src/main/frontend/pipeline-graph-view/app.scss
index 7b95005b0..e1b7a82aa 100644
--- a/src/main/frontend/pipeline-graph-view/app.scss
+++ b/src/main/frontend/pipeline-graph-view/app.scss
@@ -23,6 +23,7 @@
svg {
width: 1rem;
height: 1rem;
+ transition: color var(--standard-transition);
}
&:not(:disabled) {
From 531541ddcb8cac0a07c3c662b93896f25cab095e Mon Sep 17 00:00:00 2001
From: Jan
Date: Tue, 14 Oct 2025 16:18:13 +0100
Subject: [PATCH 18/28] Init
---
src/main/resources/components/taglib | 0
.../components/temporary-wrapper.jelly | 135 ++++++++++++++++++
.../PipelineConsoleViewAction/index.jelly | 6 +-
3 files changed, 138 insertions(+), 3 deletions(-)
create mode 100644 src/main/resources/components/taglib
create mode 100644 src/main/resources/components/temporary-wrapper.jelly
diff --git a/src/main/resources/components/taglib b/src/main/resources/components/taglib
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/main/resources/components/temporary-wrapper.jelly b/src/main/resources/components/temporary-wrapper.jelly
new file mode 100644
index 000000000..6759e8934
--- /dev/null
+++ b/src/main/resources/components/temporary-wrapper.jelly
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${it.buildDisplayName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
index fdb6f861f..5cbc63e98 100644
--- a/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
+++ b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
@@ -1,8 +1,8 @@
-
-
+
+
@@ -21,5 +21,5 @@
data-user-locale="${request2.getLocale().toLanguageTag()}"/>
-
+
From c03093aba3e65f71189811dffa88d7deae8ca84f Mon Sep 17 00:00:00 2001
From: Jan
Date: Tue, 14 Oct 2025 16:19:57 +0100
Subject: [PATCH 19/28] Update style.css
---
src/main/webapp/js/style.css | 37 ------------------------------------
1 file changed, 37 deletions(-)
diff --git a/src/main/webapp/js/style.css b/src/main/webapp/js/style.css
index 1f082dedf..9aba53564 100644
--- a/src/main/webapp/js/style.css
+++ b/src/main/webapp/js/style.css
@@ -33,43 +33,6 @@ h1 {
}
}
-.jp-pill {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: 0.375rem;
- border-radius: 100px;
- padding: 0 0.75rem;
- min-height: 1.875rem;
- text-box: cap alphabetic;
- /*background: color-mix(in srgb, var(--color, var(--text-color-secondary)) 10%, transparent);*/
- /*border: var(--jenkins-border-width) solid color-mix(in srgb, var(--color, var(--text-color-secondary)) 10%, transparent);*/
- color: var(--color, var(--text-color)) !important;
- text-decoration: none !important;
- transition: var(--standard-transition);
-
- svg {
- width: 1rem;
- height: 1rem;
- }
-}
-
-a.jp-pill, button.jp-pill {
- cursor: pointer;
-
- &:hover {
- background: color-mix(in srgb, var(--color, var(--text-color-secondary)) 15%, transparent);
- }
-
- &:active, &:focus {
- background: color-mix(in srgb, var(--color, var(--text-color-secondary)) 20%, transparent);
- }
-}
-
-.jp-pill--tertiary {
- color: var(--text-color-secondary) !important;
-}
-
.pgv-description {
display: grid;
grid-template-columns: auto 1fr;
From 4f6eb3e9e9d4ada6f718a8620b22268a09e80299 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:06:07 +0100
Subject: [PATCH 20/28] Push
---
.../pipeline-console/main/PipelineConsole.tsx | 2 +-
.../components/temporary-wrapper.jelly | 4 +++-
.../PipelineConsoleViewAction/index.jelly | 9 +++++++++
src/main/webapp/js/style.css | 17 ++++++++---------
4 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
index f9d72b051..35aa4256c 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
@@ -49,7 +49,7 @@ export default function PipelineConsole() {
container={document.getElementById("console-pipeline-overflow-root")}
>
-
+
+
+
diff --git a/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
index 5cbc63e98..2dd9c1ec5 100644
--- a/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
+++ b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/index.jelly
@@ -11,6 +11,15 @@
+
+
+
+
svg {
width: 1.125rem;
@@ -52,6 +47,10 @@ h1 {
}
}
+.pgv-details + .pgv-description {
+ margin-left: 0.25rem;
+}
+
.pgv-dropdown-item {
display: grid;
grid-template-columns: auto 1fr;
From 92f721f3250ce0a0dd88105b02949b2d3c9a4915 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:09:53 +0100
Subject: [PATCH 21/28] Create action.jelly
---
.../consoleview/PipelineConsoleViewAction/action.jelly | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/action.jelly
diff --git a/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/action.jelly b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/action.jelly
new file mode 100644
index 000000000..c41dac986
--- /dev/null
+++ b/src/main/resources/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction/action.jelly
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
From 67537e8b574d9026b31736092ff023558631437d Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:14:59 +0100
Subject: [PATCH 22/28] Update style.css
---
src/main/webapp/js/style.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/webapp/js/style.css b/src/main/webapp/js/style.css
index d6a021c2e..a80d8057c 100644
--- a/src/main/webapp/js/style.css
+++ b/src/main/webapp/js/style.css
@@ -33,7 +33,7 @@ h1 {
grid-template-columns: auto 1fr;
gap: 1ch;
align-items: start;
- margin: 0 0 1rem 0;
+ margin: -0.5rem 0 1rem 0;
& > svg {
width: 1.125rem;
@@ -48,7 +48,7 @@ h1 {
}
.pgv-details + .pgv-description {
- margin-left: 0.25rem;
+ margin: 0 0 1rem 0.25rem;
}
.pgv-dropdown-item {
From 5d02430d33b0d3937452151f8026d130ae44c87b Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:15:35 +0100
Subject: [PATCH 23/28] Format
---
.../pipeline-console/main/PipelineConsole.tsx | 6 +++++-
src/main/frontend/pipeline-graph-view/app.tsx | 14 +++++++-------
src/main/webapp/js/style.css | 6 +++---
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
index 35aa4256c..7439b0641 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
@@ -49,7 +49,11 @@ export default function PipelineConsole() {
container={document.getElementById("console-pipeline-overflow-root")}
>
-
-
+
+
+
);
}
diff --git a/src/main/webapp/js/style.css b/src/main/webapp/js/style.css
index a80d8057c..ffbe4415f 100644
--- a/src/main/webapp/js/style.css
+++ b/src/main/webapp/js/style.css
@@ -23,9 +23,9 @@ h1 {
}
.pgv-details {
- margin-left: 0.25rem;
- margin-bottom: 0.5rem;
- margin-top: -0.5rem;
+ margin-left: 0.25rem;
+ margin-bottom: 0.5rem;
+ margin-top: -0.5rem;
}
.pgv-description {
From 2e556ebd7f7488404bed9a82cd330ece9e04ecdf Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:15:56 +0100
Subject: [PATCH 24/28] Update pom.xml
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 603bef277..04e7b3dd7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,7 +34,7 @@
jenkinsci/pipeline-graph-view-plugin
2.504
- 2.532-rc37622.66926b_1a_e24b_
+ 2.532
24.2.0
11.3.0
false
From 5944b700867939a04b4ed3adbcbe3e9e991c0ae9 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Tue, 14 Oct 2025 21:18:20 +0100
Subject: [PATCH 25/28] Update PipelineConsole.tsx
---
.../pipeline-console/main/PipelineConsole.tsx | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
index 7439b0641..9dd7f2e40 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
@@ -9,6 +9,7 @@ import {
DOCUMENT,
SETTINGS,
} from "../../../common/components/symbols.tsx";
+import { useUserPermissions } from "../../../common/user/user-permission-provider.tsx";
import Skeleton from "./components/skeleton.tsx";
import Stages from "./components/stages.tsx";
import StagesCustomization from "./components/stages-customization.tsx";
@@ -43,6 +44,8 @@ export default function PipelineConsole() {
const isOnlyPlaceholderNode = stages.length === 1 && stages[0].placeholder;
+ const { canConfigure } = useUserPermissions();
+
return (
<>
>
+ ),
]}
/>
From 44527df8d2b5fd4dc5b07ff68a569d6d0f60bfe0 Mon Sep 17 00:00:00 2001
From: Tim Jacomb <21194782+timja@users.noreply.github.com>
Date: Tue, 14 Oct 2025 22:04:57 +0100
Subject: [PATCH 26/28] Update pom.xml
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 04e7b3dd7..caefddac9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
999999-SNAPSHOT
jenkinsci/pipeline-graph-view-plugin
- 2.504
+ 2.516
2.532
24.2.0
11.3.0
From 0f8b230d026160533267772d064ff7de4e524fec Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Wed, 15 Oct 2025 09:17:57 +0100
Subject: [PATCH 27/28] Restore Changes/Tests/Artifacts
---
.../cards/RunDetailsItem.java | 74 +++++++++++++++++++
.../cards/items/ArtifactRunDetailsItem.java | 22 ++++++
.../cards/items/ChangesRunDetailsItem.java | 46 ++++++++++++
.../cards/items/TestResultRunDetailsItem.java | 37 ++++++++++
.../PipelineConsoleViewAction.java | 17 +++++
.../components/temporary-wrapper.jelly | 16 +++-
.../items/ChangesRunDetailsItemTest.java | 62 ++++++++++++++++
7 files changed, 273 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/RunDetailsItem.java
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ArtifactRunDetailsItem.java
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItem.java
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/TestResultRunDetailsItem.java
create mode 100644 src/test/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItemTest.java
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/RunDetailsItem.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/RunDetailsItem.java
new file mode 100644
index 000000000..d63539760
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/RunDetailsItem.java
@@ -0,0 +1,74 @@
+package io.jenkins.plugins.pipelinegraphview.cards;
+
+import static java.util.Objects.requireNonNull;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+public sealed interface RunDetailsItem {
+
+ final class RunDetail implements RunDetailsItem {
+ private final @NonNull Icon icon;
+ private final @NonNull ItemContent content;
+ private final @Nullable String tooltip;
+
+ public RunDetail(@NonNull Icon icon, @NonNull ItemContent content, @Nullable String tooltip) {
+ this.icon = requireNonNull(icon);
+ this.content = requireNonNull(content);
+ this.tooltip = tooltip;
+ }
+
+ public RunDetail(@NonNull Icon icon, @NonNull ItemContent content) {
+ this(icon, content, null);
+ }
+
+ @Nullable
+ public String tooltip() {
+ return tooltip;
+ }
+
+ @NonNull
+ public ItemContent content() {
+ return content;
+ }
+
+ @NonNull
+ public String icon() {
+ return icon.value();
+ }
+ }
+
+ sealed interface Icon {
+ String value();
+
+ record Ionicon(@NonNull String value) implements Icon {
+ public Ionicon(@NonNull String value) {
+ requireNonNull(value);
+ this.value = String.format("symbol-%s plugin-ionicons-api", value);
+ }
+ }
+ }
+
+ sealed interface ItemContent {
+ static ItemContent of(@NonNull String text) {
+ return new PlainContent(text);
+ }
+
+ static ItemContent of(String href, @NonNull String text) {
+ return href == null || href.isBlank() ? new PlainContent(text) : new LinkContent(href, text);
+ }
+
+ record PlainContent(@NonNull String text) implements ItemContent {
+ public PlainContent {
+ requireNonNull(text);
+ }
+ }
+
+ record LinkContent(@NonNull String href, @NonNull String text) implements ItemContent {
+ public LinkContent {
+ requireNonNull(href);
+ requireNonNull(text);
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ArtifactRunDetailsItem.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ArtifactRunDetailsItem.java
new file mode 100644
index 000000000..cbd6c01ec
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ArtifactRunDetailsItem.java
@@ -0,0 +1,22 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import io.jenkins.plugins.pipelinegraphview.Messages;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.Icon.Ionicon;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.ItemContent;
+import java.util.Optional;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+
+public class ArtifactRunDetailsItem {
+
+ public static Optional get(WorkflowRun run) {
+ boolean hasArtifacts = run.getHasArtifacts();
+
+ if (!hasArtifacts) {
+ return Optional.empty();
+ }
+ RunDetailsItem artifacts = new RunDetailsItem.RunDetail(
+ new Ionicon("cube-outline"), ItemContent.of("../artifact", Messages.artifacts()));
+ return Optional.of(artifacts);
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItem.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItem.java
new file mode 100644
index 000000000..a8b7289b0
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItem.java
@@ -0,0 +1,46 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import static io.jenkins.plugins.pipelinegraphview.utils.ChangesUtil.getChanges;
+
+import hudson.scm.ChangeLogSet;
+import io.jenkins.plugins.pipelinegraphview.Messages;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.Icon.Ionicon;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.ItemContent;
+import java.util.List;
+import java.util.Optional;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+
+public class ChangesRunDetailsItem {
+
+ public static Optional get(WorkflowRun run) {
+ List changeEntries = getChanges(run);
+
+ if (changeEntries.isEmpty()) {
+ return Optional.empty();
+ }
+
+ ChangeLogSet.Entry changeEntry = changeEntries.get(0);
+
+ StringBuilder toolTipBuilder = new StringBuilder();
+ String commitId = changeEntry.getCommitId();
+ if (commitId != null) {
+ String author = changeEntry.getAuthor().getDisplayName();
+ toolTipBuilder.append("%s by %s".formatted(commitId, author));
+ toolTipBuilder.append(System.lineSeparator());
+ }
+ toolTipBuilder.append(changeEntry.getMsg());
+
+ int numEntries = changeEntries.size();
+ if (numEntries > 1) {
+ toolTipBuilder.append(System.lineSeparator()).append(System.lineSeparator());
+ toolTipBuilder.append(Messages.changes_other(numEntries - 1));
+ }
+
+ RunDetailsItem changes = new RunDetailsItem.RunDetail(
+ new Ionicon("code-slash-outline"),
+ ItemContent.of("../changes", Messages.changes()),
+ toolTipBuilder.toString());
+ return Optional.of(changes);
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/TestResultRunDetailsItem.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/TestResultRunDetailsItem.java
new file mode 100644
index 000000000..2aa3a06ac
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/TestResultRunDetailsItem.java
@@ -0,0 +1,37 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import hudson.tasks.test.AbstractTestResultAction;
+import io.jenkins.plugins.pipelinegraphview.Messages;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.Icon.Ionicon;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.ItemContent;
+import java.util.Optional;
+import jenkins.model.Jenkins;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+
+public class TestResultRunDetailsItem {
+
+ public static Optional get(WorkflowRun run) {
+ boolean junitInstalled = Jenkins.get().getPlugin("junit") != null;
+ if (!junitInstalled) {
+ return Optional.empty();
+ }
+
+ AbstractTestResultAction> action = run.getAction(AbstractTestResultAction.class);
+
+ if (action == null) {
+ return Optional.empty();
+ }
+
+ String passed =
+ Messages.testResults_passed(action.getTotalCount() - action.getFailCount() - action.getSkipCount());
+ String failed = Messages.testResults_failed(action.getFailCount());
+ String skipped = Messages.testResults_skipped(action.getSkipCount());
+ String total = Messages.testResults_total(action.getTotalCount());
+ RunDetailsItem testResult = new RunDetailsItem.RunDetail(
+ new Ionicon("flask-outline"),
+ ItemContent.of("../" + action.getUrlName(), Messages.testResults()),
+ passed + "\n" + failed + "\n" + skipped + "\n" + total);
+ return Optional.of(testResult);
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
index cc508f6bf..e47c12bfc 100644
--- a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
@@ -13,6 +13,10 @@
import hudson.util.HttpResponses;
import io.jenkins.plugins.pipelinegraphview.Messages;
import io.jenkins.plugins.pipelinegraphview.PipelineGraphViewConfiguration;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.items.ArtifactRunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.items.ChangesRunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.items.TestResultRunDetailsItem;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineGraph;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineGraphApi;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineNodeUtil;
@@ -21,6 +25,8 @@
import io.jenkins.plugins.pipelinegraphview.utils.PipelineStepList;
import jakarta.servlet.ServletException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import jenkins.model.Jenkins;
import jenkins.model.Tab;
@@ -306,6 +312,17 @@ private static long parseIntWithDefault(String s, long defaultValue) {
}
}
+ @SuppressWarnings("unused")
+ public List getRunDetailsItems() {
+ List runDetailsItems = new ArrayList<>();
+
+ ChangesRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
+ TestResultRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
+ ArtifactRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
+
+ return runDetailsItems;
+ }
+
public boolean isShowGraphOnBuildPage() {
return PipelineGraphViewConfiguration.get().isShowGraphOnBuildPage();
}
diff --git a/src/main/resources/components/temporary-wrapper.jelly b/src/main/resources/components/temporary-wrapper.jelly
index c03882e4b..0427ceb2f 100644
--- a/src/main/resources/components/temporary-wrapper.jelly
+++ b/src/main/resources/components/temporary-wrapper.jelly
@@ -107,8 +107,22 @@
+
+
diff --git a/src/test/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItemTest.java b/src/test/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItemTest.java
new file mode 100644
index 000000000..478dfffa9
--- /dev/null
+++ b/src/test/java/io/jenkins/plugins/pipelinegraphview/cards/items/ChangesRunDetailsItemTest.java
@@ -0,0 +1,62 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import hudson.model.Result;
+import hudson.plugins.git.GitSCM;
+import io.jenkins.plugins.pipelinegraphview.Messages;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.utils.TestUtils;
+import java.util.Optional;
+import org.jenkinsci.plugins.workflow.job.WorkflowJob;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+import org.junit.jupiter.api.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
+
+@WithJenkins
+class ChangesRunDetailsItemTest {
+
+ @Test
+ void get_noChanges(JenkinsRule j) throws Exception {
+ // build once, there should be no change set
+ WorkflowRun run =
+ TestUtils.createAndRunJob(j, "simpleWithSCM", "singleStagePipelineWithSCM.jenkinsfile", Result.SUCCESS);
+ Optional detailsItem = ChangesRunDetailsItem.get(run);
+ assertTrue(detailsItem.isEmpty());
+ }
+
+ @Test
+ void get_changes(JenkinsRule j) throws Exception {
+ WorkflowJob job = TestUtils.createJob(j, "simpleWithSCM", "singleStagePipelineWithSCM.jenkinsfile", true);
+ // build twice to establish a change set
+ j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0));
+ assertEquals(1, job.getLastBuild().getNumber());
+ assertEquals(
+ "b6dc82faf9248fece30bc704c09edbb4708c9756",
+ ((GitSCM) (job.getLastBuild().getSCMs().get(0)))
+ .getBranches()
+ .get(0)
+ .getName());
+ j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0));
+
+ WorkflowRun run = job.getLastBuild();
+ assertEquals(2, run.getNumber());
+ assertEquals(
+ "1dc41d9c9758a111baebebe5f6bcd39c66040941",
+ ((GitSCM) (run.getSCMs().get(0))).getBranches().get(0).getName());
+ assertEquals(1, run.getChangeSets().size());
+ assertEquals(21, run.getChangeSets().get(0).getItems().length);
+
+ Optional detailsItem = ChangesRunDetailsItem.get(run);
+
+ assertTrue(detailsItem.isPresent());
+
+ RunDetailsItem.RunDetail changesDetails = (RunDetailsItem.RunDetail) detailsItem.get();
+
+ assertEquals(
+ new RunDetailsItem.ItemContent.LinkContent("../changes", Messages.changes()), changesDetails.content());
+ assertEquals("symbol-code-slash-outline plugin-ionicons-api", changesDetails.icon());
+ }
+}
From 7be81556e7c95fd9034f611a2e55e9e2d31b1f50 Mon Sep 17 00:00:00 2001
From: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date: Wed, 15 Oct 2025 10:34:16 +0100
Subject: [PATCH 28/28] Restore Git details
---
.../GitHubBranchSourceRunDetailsItems.java | 54 +++++++++++++++++++
.../cards/items/SCMRunDetailsItems.java | 50 +++++++++++++++++
.../PipelineConsoleViewAction.java | 5 +-
3 files changed, 106 insertions(+), 3 deletions(-)
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/GitHubBranchSourceRunDetailsItems.java
create mode 100644 src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/SCMRunDetailsItems.java
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/GitHubBranchSourceRunDetailsItems.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/GitHubBranchSourceRunDetailsItems.java
new file mode 100644
index 000000000..6da01afe7
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/GitHubBranchSourceRunDetailsItems.java
@@ -0,0 +1,54 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.Icon.Ionicon;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.ItemContent;
+import java.util.List;
+import java.util.Optional;
+import jenkins.scm.api.SCMRevisionAction;
+import jenkins.scm.api.metadata.ObjectMetadataAction;
+import org.jenkinsci.plugins.github_branch_source.GitHubLink;
+import org.jenkinsci.plugins.github_branch_source.PullRequestSCMHead;
+import org.jenkinsci.plugins.github_branch_source.PullRequestSCMRevision;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+
+public class GitHubBranchSourceRunDetailsItems {
+
+ public static String getGitCommit(SCMRevisionAction scmRevisionAction) {
+ PullRequestSCMRevision revision = (PullRequestSCMRevision) scmRevisionAction.getRevision();
+ return revision.getPullHash().substring(0, 7);
+ }
+
+ public static List getGitInformation(SCMRevisionAction scmRevisionAction) {
+ PullRequestSCMRevision revision = (PullRequestSCMRevision) scmRevisionAction.getRevision();
+
+ // TODO see if there's a way to get this for branch builds, may need to make changes to
+ // github-branch-source-plugin
+ PullRequestSCMHead head = (PullRequestSCMHead) revision.getHead();
+ String sourceOwner = head.getSourceOwner();
+ String sourceRepo = head.getSourceRepo();
+ String sourceBranch = head.getSourceBranch();
+
+ RunDetailsItem gitRepositoryItem = new RunDetailsItem.RunDetail(
+ new Ionicon("logo-github"), ItemContent.of(sourceOwner + "/" + sourceRepo));
+
+ RunDetailsItem gitBranchItem =
+ new RunDetailsItem.RunDetail(new Ionicon("git-branch-outline"), ItemContent.of(sourceBranch));
+
+ return List.of(gitRepositoryItem, gitBranchItem);
+ }
+
+ public static Optional getGitHubLink(WorkflowRun run) {
+ GitHubLink gitHubLink = run.getParent().getAction(GitHubLink.class);
+ if (gitHubLink == null) {
+ return Optional.empty();
+ }
+ ObjectMetadataAction action = run.getParent().getAction(ObjectMetadataAction.class);
+ String name = action.getObjectDisplayName();
+ if (name == null) {
+ return Optional.empty();
+ }
+ return Optional.of(new RunDetailsItem.RunDetail(
+ new Ionicon("git-pull-request-outline"), ItemContent.of(gitHubLink.getUrl(), name)));
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/SCMRunDetailsItems.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/SCMRunDetailsItems.java
new file mode 100644
index 000000000..9d18d751d
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/cards/items/SCMRunDetailsItems.java
@@ -0,0 +1,50 @@
+package io.jenkins.plugins.pipelinegraphview.cards.items;
+
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.Icon.Ionicon;
+import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem.ItemContent;
+import java.util.ArrayList;
+import java.util.List;
+import jenkins.model.Jenkins;
+import jenkins.plugins.git.AbstractGitSCMSource;
+import jenkins.scm.api.SCMRevisionAction;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+
+public class SCMRunDetailsItems {
+
+ public static List get(WorkflowRun run) {
+ SCMRevisionAction scmRevisionAction = run.getAction(SCMRevisionAction.class);
+ if (scmRevisionAction == null) {
+ return List.of();
+ }
+
+ List runDetailsItems = new ArrayList<>();
+ boolean githubBranchSourceInstalled = Jenkins.get().getPlugin("github-branch-source") != null;
+ String commit = null;
+
+ if (githubBranchSourceInstalled
+ && scmRevisionAction
+ .getRevision()
+ .getClass()
+ .getName()
+ .equals("org.jenkinsci.plugins.github_branch_source.PullRequestSCMRevision")) {
+
+ runDetailsItems.addAll(GitHubBranchSourceRunDetailsItems.getGitInformation(scmRevisionAction));
+ commit = GitHubBranchSourceRunDetailsItems.getGitCommit(scmRevisionAction);
+
+ } else if (scmRevisionAction.getRevision() instanceof AbstractGitSCMSource.SCMRevisionImpl revision) {
+ commit = revision.getHash().substring(0, 7);
+ }
+
+ if (githubBranchSourceInstalled) {
+ GitHubBranchSourceRunDetailsItems.getGitHubLink(run).ifPresent(runDetailsItems::add);
+ }
+
+ if (commit != null) {
+ runDetailsItems.add(
+ new RunDetailsItem.RunDetail(new Ionicon("git-commit-outline"), ItemContent.of(commit)));
+ }
+
+ return runDetailsItems;
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
index e47c12bfc..2c0de9ed8 100644
--- a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
+++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java
@@ -16,6 +16,7 @@
import io.jenkins.plugins.pipelinegraphview.cards.RunDetailsItem;
import io.jenkins.plugins.pipelinegraphview.cards.items.ArtifactRunDetailsItem;
import io.jenkins.plugins.pipelinegraphview.cards.items.ChangesRunDetailsItem;
+import io.jenkins.plugins.pipelinegraphview.cards.items.SCMRunDetailsItems;
import io.jenkins.plugins.pipelinegraphview.cards.items.TestResultRunDetailsItem;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineGraph;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineGraphApi;
@@ -314,12 +315,10 @@ private static long parseIntWithDefault(String s, long defaultValue) {
@SuppressWarnings("unused")
public List getRunDetailsItems() {
- List runDetailsItems = new ArrayList<>();
-
+ List runDetailsItems = new ArrayList<>(SCMRunDetailsItems.get(run));
ChangesRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
TestResultRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
ArtifactRunDetailsItem.get(run).ifPresent(runDetailsItems::add);
-
return runDetailsItems;
}