Skip to content

Commit dbb7b1f

Browse files
authored
Combine graph and console view (#666)
1 parent df24443 commit dbb7b1f

File tree

48 files changed

+1417
-698
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1417
-698
lines changed

README.md

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
# Pipeline Graph View Plugin
22

3+
[![Build Status](https://ci.jenkins.io/job/Plugins/job/pipeline-graph-view-plugin/job/main/badge/icon)](https://ci.jenkins.io/job/Plugins/job/pipeline-graph-view-plugin/job/main/)
4+
[![Gitter](https://badges.gitter.im/jenkinsci/ux-sig.svg)](https://gitter.im/jenkinsci/ux-sig?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
5+
[![Jenkins Plugin](https://img.shields.io/jenkins/plugin/v/pipeline-graph-view.svg)](https://plugins.jenkins.io/pipeline-graph-view)
6+
[![Jenkins Plugin Installs](https://img.shields.io/jenkins/plugin/i/pipeline-graph-view.svg?color=blue)](https://plugins.jenkins.io/pipeline-graph-view)
7+
8+
![preview.png](docs/images/preview.png)
9+
310
## Introduction
411

5-
This plugin provides new Action and View to view a `WorkflowRun` via the "Pipeline Graph" visualization that was popularized in the [Blue Ocean plugin](https://github.com/jenkinsci/blueocean-plugin).
12+
This plugin adds a visual representation of Jenkins pipelines, showing each stage of a run in a clear and easy-to-follow graph format. It’s designed to make pipeline progress and structure easier to understand at a glance.
13+
14+
## Features
15+
16+
- Visualize pipelines as an interactive, nested graph
17+
- Navigate pipeline stages in a clear, collapsible list view
18+
- View logs in real time without leaving the interface
19+
- Toggle between graph and stage views; move and resize panes to suit your workflow
20+
- Quickly access details of each step and its results
21+
- Designed for better readability and faster troubleshooting
622

723
## Getting started
824

9-
1. Install the `pipeline-graph-view` plugin
25+
1. Install the [pipeline-graph-view](https://plugins.jenkins.io/pipeline-graph-view/) plugin
1026
2. Go to a pipeline run (not a job page)
11-
3. Click 'Pipeline Overview'
27+
3. Click 'Pipeline Console'
1228

1329
## Screenshots
1430

15-
![Pipeline Graph link](docs/images/action.png)
16-
1731
Basic pipeline:
1832

1933
![Different statuses](./docs/images/different-statuses.png)
@@ -28,20 +42,10 @@ See a live demonstration from a Jenkins Contributor Summit:
2842

2943
[![Demo of Pipeline Graph View plugin](https://img.youtube.com/vi/MBI3MBY2eJ8/0.jpg)](https://www.youtube.com/watch?v=MBI3MBY2eJ8&t=3295 "Pipeline Graph View plugin")
3044

31-
## Vision
32-
33-
This plugin aims to bring the best of Blue Ocean into the regular Jenkins UI.
34-
35-
That means functionality like:
36-
37-
- [x] Pipeline graph
38-
- [x] Summary of runs in a job (like [Pipeline Stage View Plugin](https://github.com/jenkinsci/pipeline-stage-view-plugin/), but simpler, more modern and more performant)
39-
- [x] Modern logs viewing
40-
41-
The plugin should be lightweight, using or providing extension points where possible rather than building everything into one plugin.
42-
4345
## Contributing
4446

45-
Any help is much appreciated, the frontend code is written in React, the backend code is in Java, designers would be greatly appreciated as well.
47+
Refer to our [contribution guidelines](./CONTRIBUTING.md).
48+
49+
## LICENSE
4650

47-
Refer to our [contribution guidelines](./CONTRIBUTING.md)
51+
Licensed under MIT, see [LICENSE](LICENSE.md).

docs/images/action.png

-9.93 KB
Binary file not shown.

docs/images/preview.png

1.78 MB
Loading

eslint.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export default [
1515
},
1616
},
1717
},
18-
plugins.n.configs["flat/recommended"],
1918
plugins.promise.configs["flat/recommended"],
2019
{
2120
plugins: {

package-lock.json

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"@tippyjs/react": "^4.2.6",
3636
"react": "^18.3.1",
3737
"react-dom": "^18.3.1",
38-
"react-virtuoso": "^4.12.6"
38+
"react-virtuoso": "^4.12.6",
39+
"react-zoom-pan-pinch": "3.7.0"
3940
},
4041
"devDependencies": {
4142
"@testing-library/jest-dom": "^6.6.3",

src/main/frontend/common/RestClient.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function getRunStatusFromPath(
4343
url: string,
4444
): Promise<RunStatus | null> {
4545
try {
46-
const response = await fetch(url + "pipeline-graph/tree");
46+
const response = await fetch(url + "pipeline-console/tree");
4747
if (!response.ok) {
4848
throw response.statusText;
4949
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
4+
interface DropdownPortalProps {
5+
children: React.ReactNode;
6+
}
7+
8+
export default function DropdownPortal({ children }: DropdownPortalProps) {
9+
const container = document.getElementById("console-pipeline-overflow-root");
10+
11+
if (!container) {
12+
console.error("DropdownPortal: Target container not found!");
13+
return null;
14+
}
15+
16+
return ReactDOM.createPortal(children, container);
17+
}

src/main/frontend/common/components/dropdown.tsx

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import Tooltip from "./tooltip.tsx";
66
/**
77
* A customized (and customizable) implementation of Tippy dropdowns
88
*/
9-
export default function Dropdown({ items, disabled }: DropdownProps) {
9+
export default function Dropdown({
10+
items,
11+
disabled,
12+
className,
13+
}: DropdownProps) {
1014
const [visible, setVisible] = useState(false);
1115
const show = () => setVisible(true);
1216
const hide = () => setVisible(false);
@@ -19,23 +23,45 @@ export default function Dropdown({ items, disabled }: DropdownProps) {
1923
{...DefaultDropdownProps}
2024
content={
2125
<div className="jenkins-dropdown">
22-
{items.map((item, index) => (
23-
<a
24-
key={index}
25-
className="jenkins-dropdown__item"
26-
href={item.href}
27-
target={item.target}
28-
download={item.download}
29-
>
30-
<div className="jenkins-dropdown__item__icon">{item.icon}</div>
31-
{item.text}
32-
</a>
33-
))}
26+
{items.map((item, index) => {
27+
if (item === "separator") {
28+
return (
29+
<div
30+
key={`separator-${index}`}
31+
className="jenkins-dropdown__separator"
32+
/>
33+
);
34+
}
35+
36+
if (React.isValidElement(item)) {
37+
return (
38+
<div key={index} className="jenkins-dropdown__custom-item">
39+
{item}
40+
</div>
41+
);
42+
}
43+
44+
const dropdownItem = item as DropdownItem;
45+
return (
46+
<a
47+
key={index}
48+
className="jenkins-dropdown__item"
49+
href={dropdownItem.href}
50+
target={dropdownItem.target}
51+
download={dropdownItem.download}
52+
>
53+
<div className="jenkins-dropdown__item__icon">
54+
{dropdownItem.icon}
55+
</div>
56+
{dropdownItem.text}
57+
</a>
58+
);
59+
})}
3460
</div>
3561
}
3662
>
3763
<button
38-
className="jenkins-button jenkins-button--tertiary"
64+
className={"jenkins-button " + className}
3965
type="button"
4066
disabled={disabled}
4167
onClick={visible ? hide : show}
@@ -63,8 +89,9 @@ export const DefaultDropdownProps: TippyProps = {
6389
};
6490

6591
interface DropdownProps {
66-
items: DropdownItem[];
92+
items: (DropdownItem | React.ReactElement | "separator")[];
6793
disabled?: boolean;
94+
className?: string;
6895
}
6996

7097
interface DropdownItem {

src/main/frontend/multi-pipeline-graph-view/multi-pipeline-graph/main/SingleRun.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import "./single-run.scss";
22

3-
import React, { useState } from "react";
3+
import React from "react";
44

55
import StatusIcon from "../../../common/components/status-icon.tsx";
6+
import useRunPoller from "../../../common/tree-api.ts";
67
import { time, Total } from "../../../common/utils/timings.tsx";
7-
import {
8-
LayoutInfo,
9-
StageInfo,
10-
} from "../../../pipeline-graph-view/pipeline-graph/main/index.ts";
8+
import { LayoutInfo } from "../../../pipeline-graph-view/pipeline-graph/main/index.ts";
119
import { PipelineGraph } from "../../../pipeline-graph-view/pipeline-graph/main/PipelineGraph.tsx";
1210
import { defaultLayout } from "../../../pipeline-graph-view/pipeline-graph/main/PipelineGraphModel.tsx";
1311
import { RunInfo } from "./MultiPipelineGraphModel.ts";
1412

1513
export default function SingleRun({ run, currentJobPath }: SingleRunProps) {
16-
const [stages, setStages] = useState<Array<StageInfo>>([]);
14+
const { run: runInfo } = useRunPoller({
15+
currentRunPath: currentJobPath + run.id + "/",
16+
});
1717

1818
const layout: LayoutInfo = {
1919
...defaultLayout,
@@ -31,13 +31,7 @@ export default function SingleRun({ run, currentJobPath }: SingleRunProps) {
3131
</span>
3232
</a>
3333
</div>
34-
<PipelineGraph
35-
stages={stages}
36-
setStages={setStages}
37-
currentRunPath={currentJobPath + run.id + "/"}
38-
layout={layout}
39-
collapsed
40-
/>
34+
<PipelineGraph stages={runInfo?.stages || []} layout={layout} collapsed />
4135
</div>
4236
);
4337
}

0 commit comments

Comments
 (0)