Skip to content

Commit af17d13

Browse files
committed
dashboard: Add view for PR runs
Added a script that fetches PR data and created a separate view on the dashboard. Fixes #1 Signed-off-by: Anna Finn <[email protected]>
1 parent 054713a commit af17d13

File tree

5 files changed

+506
-240
lines changed

5 files changed

+506
-240
lines changed

.github/workflows/fectch-ci-data.yml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Fetch CI Data
2+
run-name: Fetch CI Data
3+
on:
4+
schedule:
5+
- cron: '0 */2 * * *'
6+
workflow_dispatch:
7+
8+
jobs:
9+
fetch-and-commit-data:
10+
runs-on: ubuntu-22.04
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
- name: Update dashboard data
16+
run: |
17+
# fetch ci nightly data as temporary file
18+
TOKEN=${{ secrets.GITHUB_TOKEN }} node scripts/fetch-ci-nightly-data.js | tee tmp-data.json
19+
TOKEN=${{ secrets.GITHUB_TOKEN }} node scripts/fetch-ci-pr-data.js | tee tmp-data2.json
20+
21+
# switch to a branch specifically for holding latest data
22+
git config --global user.name "GH Actions Workflow"
23+
git config --global user.email "<gha@runner>"
24+
git fetch --all
25+
git checkout latest-dashboard-data
26+
27+
# back out whatever data was there
28+
git reset HEAD~1
29+
30+
# overwrite the old data
31+
mkdir -p data/
32+
mv tmp-data.json data/job_stats.json
33+
mv tmp-data2.json data/check_stats.json
34+
35+
# commit
36+
git add data
37+
git commit -m '[skip ci] latest ci nightly data'
38+
git push --force

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
"private": true,
44
"scripts": {
55
"dev": "NODE_ENV=development next dev",
6+
"win-dev": "next dev",
67
"build": "next build",
78
"start": "next start",
89
"lint": "next lint"
910
},
1011
"dependencies": {
11-
"next": "^14.2.13",
12+
"next": "^15.0.2",
1213
"primeflex": "^3.3.1",
1314
"primeicons": "^6.0.1",
1415
"primereact": "^10.8.3",
@@ -18,9 +19,10 @@
1819
},
1920
"devDependencies": {
2021
"autoprefixer": "^10.4.20",
22+
"dotenv": "^16.4.5",
2123
"eslint": "8.57.0",
2224
"eslint-config-next": "^14.2.13",
2325
"postcss": "^8.4.47",
2426
"tailwindcss": "^3.4.13"
2527
}
26-
}
28+
}

pages/index.js

+170-65
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,38 @@ import { weatherTemplate, getWeatherIndex } from "../components/weatherTemplate"
66

77

88
export default function Home() {
9-
const [loading, setLoading] = useState(true);
10-
const [jobs, setJobs] = useState([]);
11-
const [rows, setRows] = useState([]);
12-
const [expandedRows, setExpandedRows] = useState([]);
9+
const [loading, setLoading] = useState(true);
10+
const [jobs, setJobs] = useState([]);
11+
const [checks, setChecks] = useState([]);
12+
const [rowsPR, setRowsPR] = useState([]);
13+
const [rowsNightly, setRowsNightly] = useState([]);
14+
const [expandedRows, setExpandedRows] = useState([]);
15+
const [display, setDisplay] = useState("nightly");
16+
1317

1418
useEffect(() => {
1519
const fetchData = async () => {
16-
let data = {};
20+
let nightlyData = {};
21+
let prData = {};
1722

1823
if (process.env.NODE_ENV === "development") {
19-
data = (await import("../job_stats.json")).default;
24+
nightlyData = (await import("../localData/job_stats.json")).default;
25+
prData = (await import("../localData/check_stats.json")).default;
2026
} else {
21-
const response = await fetch(
27+
nightlyData = await fetch(
2228
"https://raw.githubusercontent.com/kata-containers/kata-containers.github.io" +
2329
"/refs/heads/latest-dashboard-data/data/job_stats.json"
24-
);
25-
data = await response.json();
30+
).then((res) => res.json());
31+
prData = await fetch(
32+
"https://raw.githubusercontent.com/kata-containers/kata-containers.github.io" +
33+
"/refs/heads/latest-dashboard-data/data/check_stats.json"
34+
).then((res) => res.json());
2635
}
2736

2837
try {
29-
const jobData = Object.keys(data).map((key) => {
30-
const job = data[key];
31-
return { name: key, ...job };
32-
});
33-
setJobs(jobData);
38+
const mapData = (data) => Object.keys(data).map((key) => ({ name: key, ...data[key] }));
39+
setJobs(mapData(nightlyData));
40+
setChecks(mapData(prData));
3441
} catch (error) {
3542
// TODO: Add pop-up/toast message for error
3643
console.error("Error fetching data:", error);
@@ -42,17 +49,50 @@ export default function Home() {
4249
fetchData();
4350
}, []);
4451

52+
// Filter and set the rows for Nightly view.
53+
useEffect(() => {
54+
setLoading(true);
55+
let filteredJobs = jobs;
56+
//Set the rows for the table.
57+
setRowsNightly(
58+
filteredJobs.map((job) => ({
59+
name : job.name,
60+
runs : job.runs,
61+
fails : job.fails,
62+
skips : job.skips,
63+
required : job.required,
64+
weather : getWeatherIndex(job),
65+
}))
66+
);
67+
setLoading(false);
68+
}, [jobs]);
69+
70+
// Filter and set the rows for PR Checks view.
71+
useEffect(() => {
72+
setLoading(true);
73+
let filteredChecks = checks
74+
75+
//Set the rows for the table.
76+
setRowsPR(
77+
filteredChecks.map((check) => ({
78+
name : check.name,
79+
runs : check.runs,
80+
fails : check.fails,
81+
skips : check.skips,
82+
required : check.required,
83+
weather : getWeatherIndex(check),
84+
}))
85+
);
86+
setLoading(false);
87+
}, [checks]);
88+
89+
// Close all rows on view switch.
90+
// Needed because if view is switched, breaks expanded row toggling.
4591
useEffect(() => {
46-
setLoading(true);
92+
setExpandedRows([])
93+
}, [display]);
94+
4795

48-
// Create rows to set into table.
49-
const rows = jobs.map((job) => ({
50-
...job,
51-
weather: getWeatherIndex(job),
52-
}));
53-
setRows(rows);
54-
setLoading(false);
55-
}, [jobs]);
5696

5797
const toggleRow = (rowData) => {
5898
const isRowExpanded = expandedRows.includes(rowData);
@@ -67,6 +107,11 @@ export default function Home() {
67107
setExpandedRows(updatedExpandedRows);
68108
};
69109

110+
const tabClass = (active) => `tab md:px-4 px-2 py-2 border-b-2 focus:outline-none
111+
${active ? "border-blue-500 bg-gray-300"
112+
: "border-gray-300 bg-white hover:bg-gray-100"}`;
113+
114+
70115
// Template for rendering the Name column as a clickable item
71116
const nameTemplate = (rowData) => {
72117
return (
@@ -77,7 +122,10 @@ export default function Home() {
77122
};
78123

79124
const rowExpansionTemplate = (data) => {
80-
const job = jobs.find((job) => job.name === data.name);
125+
const job = (display === "nightly"
126+
? jobs
127+
: checks).find((job) => job.name === data.name);
128+
81129

82130
// Prepare run data
83131
const runs = [];
@@ -121,71 +169,128 @@ export default function Home() {
121169
);
122170
};
123171

124-
const renderTable = () => (
172+
// Render table for nightly view.
173+
const renderNightlyTable = () => (
125174
<DataTable
126-
value={rows}
175+
value={rowsNightly}
127176
expandedRows={expandedRows}
128177
stripedRows
129178
rowExpansionTemplate={rowExpansionTemplate}
130179
onRowToggle={(e) => setExpandedRows(e.data)}
131180
loading={loading}
132181
emptyMessage="No results found."
133182
>
134-
<Column expander style={{ width: "5rem" }} />
183+
<Column expander/>
135184
<Column
136185
field="name"
137186
header="Name"
138187
body={nameTemplate}
188+
className="select-all"
139189
filter
140190
sortable
141-
maxConstraints={4}
142-
filterHeader="Filter by Name"
143-
filterPlaceholder="Search..."
144191
/>
145-
<Column field="required" header="Required" sortable />
146-
<Column field="runs" header="Runs" sortable />
147-
<Column field="fails" header="Fails" sortable />
148-
<Column field="skips" header="Skips" sortable />
192+
<Column field = "required" header = "Required" sortable/>
193+
<Column
194+
field = "runs"
195+
header = "Runs"
196+
className="whitespace-nowrap px-2"
197+
sortable />
198+
<Column field = "fails" header = "Fails" sortable/>
199+
<Column field = "skips" header = "Skips" sortable/>
200+
<Column
201+
field = "weather"
202+
header = "Weather"
203+
body = {weatherTemplate}
204+
sortable />
205+
</DataTable>
206+
);
207+
208+
const renderPRTable = () => (
209+
<DataTable
210+
value={rowsPR}
211+
expandedRows={expandedRows}
212+
stripedRows
213+
rowExpansionTemplate={rowExpansionTemplate}
214+
onRowToggle={(e) => setExpandedRows(e.data)}
215+
loading={loading}
216+
emptyMessage="No results found."
217+
>
218+
<Column expander/>
149219
<Column
150-
field="weather"
151-
header="Weather"
152-
body={weatherTemplate}
220+
field="name"
221+
header="Name"
222+
body={nameTemplate}
223+
className="select-all"
224+
filter
153225
sortable
154226
/>
227+
<Column field = "required" header = "Required" sortable/>
228+
<Column
229+
field = "runs"
230+
header = "Runs"
231+
className="whitespace-nowrap px-2"
232+
sortable />
233+
<Column field = "fails" header = "Fails" sortable/>
234+
<Column field = "skips" header = "Skips" sortable/>
235+
<Column
236+
field = "weather"
237+
header = "Weather"
238+
body = {weatherTemplate}
239+
sortable />
155240
</DataTable>
156241
);
157242

243+
158244
return (
159-
<div className="text-center">
245+
<>
246+
160247
<Head>
161248
<title>Kata CI Dashboard</title>
162249
</Head>
250+
<div className="text-center text-xs md:text-base">
251+
<h1 className={"text-4xl mt-4 ml-4 mb-6 underline text-inherit \
252+
hover:text-blue-500"}>
253+
<a
254+
href={display === 'nightly'
255+
? "https://github.com/kata-containers/kata-containers/" +
256+
"actions/workflows/ci-nightly.yaml"
257+
: "https://github.com/kata-containers/kata-containers/" +
258+
"actions/workflows/ci-on-push.yaml"}
259+
target="_blank"
260+
rel="noopener noreferrer"
261+
>
262+
Kata CI Dashboard
263+
</a>
264+
</h1>
163265

164-
<h1
165-
className={
166-
"text-4xl mt-4 mb-0 underline text-inherit hover:text-blue-500"
167-
}
168-
>
169-
<a
170-
href={
171-
"https://github.com/kata-containers/kata-containers/" +
172-
"actions/workflows/ci-nightly.yaml"
173-
}
174-
target="_blank"
175-
rel="noopener noreferrer"
176-
>
177-
Kata CI Dashboard
178-
</a>
179-
</h1>
180-
181-
<main
182-
className={
183-
"m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
184-
}
185-
>
186-
<div>{renderTable()}</div>
187-
<div className="mt-4 text-lg">Total Rows: {rows.length}</div>
188-
</main>
189-
</div>
266+
<div className="flex flex-wrap mt-2 p-4 md:text-base text-xs">
267+
<div className="space-x-2 pb-2 pr-3 mx-auto flex">
268+
<button
269+
className={tabClass(display === "nightly")}
270+
onClick={() => {
271+
setDisplay("nightly");
272+
}}>
273+
Nightly Jobs
274+
</button>
275+
<button
276+
className={tabClass(display === "prchecks")}
277+
onClick={() => {
278+
setDisplay("prchecks");
279+
}}>
280+
PR Checks
281+
</button>
282+
</div>
283+
</div>
284+
285+
<div className="mt-1 text-center md:text-lg text-base">
286+
Total Rows: {display === "prchecks" ? rowsPR.length : rowsNightly.length}
287+
</div>
288+
289+
<main className={"m-0 h-full px-4 overflow-x-hidden overflow-y-auto \
290+
bg-surface-ground antialiased select-text"}>
291+
<div>{display === "prchecks" ? renderPRTable() : renderNightlyTable()}</div>
292+
</main>
293+
</div>
294+
</>
190295
);
191-
}
296+
}

0 commit comments

Comments
 (0)