-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathlocal-test.sh
More file actions
executable file
·417 lines (339 loc) · 18.2 KB
/
Copy pathlocal-test.sh
File metadata and controls
executable file
·417 lines (339 loc) · 18.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
#!/bin/bash
# Local testing script for CI Dashboard
# Usage: ./local-test.sh [fetch|serve|both]
#
# Requirements:
# - Docker
# - GH_TOKEN environment variable (GitHub token with repo access)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Check for GH_TOKEN, try to get from gh CLI if not set
if [ -z "$GH_TOKEN" ]; then
echo "🔑 GH_TOKEN not set, trying to get from gh CLI..."
if command -v gh &> /dev/null; then
GH_TOKEN=$(gh auth token 2>/dev/null || true)
if [ -n "$GH_TOKEN" ]; then
echo " ✅ Got token from gh CLI"
export GH_TOKEN
fi
fi
fi
if [ -z "$GH_TOKEN" ]; then
echo "❌ Error: GH_TOKEN environment variable is required"
echo " Either:"
echo " - Export your GitHub token: export GH_TOKEN=ghp_..."
echo " - Or login with gh CLI: gh auth login"
exit 1
fi
# Build the container
build_container() {
echo "🔨 Building container..."
docker build -f Dockerfile.local -t ci-dashboard-local .
}
# Fetch nightly data
fetch_nightly_data() {
echo "📥 Fetching nightly CI data..."
docker run --rm \
-e GH_TOKEN="$GH_TOKEN" \
-v "$SCRIPT_DIR:/app" \
ci-dashboard-local \
bash -c '
set -e
cd /app
echo "Fetching nightly workflow runs (last 10 days)..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/workflows/ci-nightly.yaml/runs?created=>$(date -d "10 days ago" +%Y-%m-%d)" \
--jq ".workflow_runs" | jq -s "add // []" > nightly-runs.json
echo "Found $(jq "length" nightly-runs.json) nightly runs"
# Fetch jobs for each run
echo "[]" > all-jobs.json
for run_id in $(jq -r ".[0:15] | .[].id" nightly-runs.json); do
echo "Fetching jobs for run $run_id..."
# Fetch ALL jobs (no filter - we want everything for the dashboard)
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/runs/$run_id/jobs?per_page=100&filter=all" \
--jq ".jobs[]" | \
jq -s --arg run_id "$run_id" "[.[] | . + {workflow_run_id: \$run_id}]" > run-jobs.json
echo " Found $(jq "length" run-jobs.json) jobs"
jq -s "add" all-jobs.json run-jobs.json > temp-jobs.json
mv temp-jobs.json all-jobs.json
done
# Fetch s390x nightly workflow runs
echo ""
echo "Fetching s390x nightly workflow runs..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/workflows/ci-nightly-s390x.yaml/runs?created=>$(date -d "10 days ago" +%Y-%m-%d)" \
--jq ".workflow_runs" | jq -s "add // []" > s390x-runs.json
echo "Found $(jq "length" s390x-runs.json) s390x nightly runs"
# Fetch jobs for each s390x run
echo "[]" > s390x-jobs.json
for run_id in $(jq -r ".[0:15] | .[].id" s390x-runs.json); do
echo "Fetching jobs for s390x run $run_id..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/runs/$run_id/jobs?per_page=100&filter=all" \
--jq ".jobs[]" | \
jq -s --arg run_id "$run_id" "[.[] | . + {workflow_run_id: \$run_id, source_workflow: \"ci-nightly-s390x\"}]" > run-jobs.json
echo " Found $(jq "length" run-jobs.json) jobs"
jq -s "add" s390x-jobs.json run-jobs.json > temp-jobs.json
mv temp-jobs.json s390x-jobs.json
done
echo "Total s390x jobs: $(jq "length" s390x-jobs.json)"
# Merge s390x jobs into all-jobs.json
jq -s "add" all-jobs.json s390x-jobs.json > temp-jobs.json
mv temp-jobs.json all-jobs.json
# Create final format
echo "{\"jobs\":" > raw-runs.json
cat all-jobs.json >> raw-runs.json
echo "}" >> raw-runs.json
echo "Total jobs: $(jq ".jobs | length" raw-runs.json)"
# Fetch required tests from gatekeeper
echo "Fetching required-tests.yaml from gatekeeper..."
curl -sL "https://raw.githubusercontent.com/kata-containers/kata-containers/refs/heads/main/tools/testing/gatekeeper/required-tests.yaml" \
-o required-tests.yaml
echo " Downloaded required-tests.yaml ($(wc -c < required-tests.yaml) bytes)"
# Fetch CoCo Charts E2E test data (scheduled runs only)
echo ""
echo "Fetching CoCo Charts E2E test data (scheduled runs)..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/confidential-containers/charts/actions/workflows/e2e-tests.yaml/runs?event=schedule&created=>$(date -d "10 days ago" +%Y-%m-%d)" \
--jq ".workflow_runs" | jq -s "add // []" > coco-charts-runs.json
echo "Found $(jq "length" coco-charts-runs.json) CoCo Charts scheduled runs"
# Fetch jobs for each CoCo Charts run
echo "[]" > coco-charts-jobs.json
for run_id in $(jq -r ".[0:15] | .[].id" coco-charts-runs.json); do
echo "Fetching jobs for CoCo Charts run $run_id..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/confidential-containers/charts/actions/runs/$run_id/jobs?per_page=100&filter=all" \
--jq ".jobs[]" | \
jq -s --arg run_id "$run_id" "[.[] | . + {workflow_run_id: \$run_id, source_repo: \"confidential-containers/charts\"}]" > run-jobs.json
echo " Found $(jq "length" run-jobs.json) jobs"
jq -s "add" coco-charts-jobs.json run-jobs.json > temp-jobs.json
mv temp-jobs.json coco-charts-jobs.json
done
echo "Total CoCo Charts jobs: $(jq "length" coco-charts-jobs.json)"
# Fetch CoCo Cloud API Adaptor E2E test data (scheduled runs only)
echo ""
echo "Fetching Cloud API Adaptor daily E2E test data (scheduled runs)..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/confidential-containers/cloud-api-adaptor/actions/workflows/daily-e2e-tests.yaml/runs?event=schedule&created=>$(date -d "10 days ago" +%Y-%m-%d)" \
--jq ".workflow_runs" | jq -s "add // []" > coco-caa-runs.json
echo "Found $(jq "length" coco-caa-runs.json) CAA scheduled runs"
# Fetch jobs for each CAA run
echo "[]" > coco-caa-jobs.json
for run_id in $(jq -r ".[0:15] | .[].id" coco-caa-runs.json); do
echo "Fetching jobs for CAA run $run_id..."
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/confidential-containers/cloud-api-adaptor/actions/runs/$run_id/jobs?per_page=100&filter=all" \
--jq ".jobs[]" | \
jq -s --arg run_id "$run_id" "[.[] | . + {workflow_run_id: \$run_id, source_repo: \"confidential-containers/cloud-api-adaptor\"}]" > run-jobs.json
echo " Found $(jq "length" run-jobs.json) jobs"
jq -s "add" coco-caa-jobs.json run-jobs.json > temp-jobs.json
mv temp-jobs.json coco-caa-jobs.json
done
echo "Total CAA jobs: $(jq "length" coco-caa-jobs.json)"
# Fetch logs for ALL failed jobs
echo "Fetching logs for failed jobs..."
mkdir -p job-logs
failed_count=$(jq -r ".jobs[] | select(.conclusion == \"failure\") | .id" raw-runs.json | wc -l)
echo "Found $failed_count failed jobs to fetch logs for"
for job_id in $(jq -r ".jobs[] | select(.conclusion == \"failure\") | .id" raw-runs.json); do
echo "Fetching log for job $job_id..."
curl -sL \
-H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/kata-containers/kata-containers/actions/jobs/$job_id/logs" \
-o "job-logs/$job_id.log"
size=$(wc -c < "job-logs/$job_id.log")
not_ok=$(grep -c "not ok" "job-logs/$job_id.log" 2>/dev/null || echo 0)
echo " Log: $size bytes, $not_ok \"not ok\" lines"
done
echo ""
echo "✅ Nightly data fetched!"
echo " Runs: $(jq "length" nightly-runs.json)"
echo " Jobs: $(jq ".jobs | length" raw-runs.json)"
echo " Logs: $(ls job-logs/*.log 2>/dev/null | wc -l)"
'
}
# Fetch flaky test data from PRs
fetch_flaky_data() {
echo "📥 Fetching flaky test data from PRs..."
docker run --rm \
-e GH_TOKEN="$GH_TOKEN" \
-v "$SCRIPT_DIR:/app" \
ci-dashboard-local \
bash -c '
set -e
cd /app
# For local testing, use 7 days; for production use 14 days
SINCE_DATE=$(date -d "7 days ago" +%Y-%m-%d)
echo "Fetching PR runs since $SINCE_DATE (local testing mode - 7 days)..."
# Fetch PR runs from "Kata Containers CI" workflow (ID 52911914 = ci-on-push.yaml)
# triggered by pull_request_target. Filter out skipped runs as they have no test results
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/workflows/52911914/runs?event=pull_request_target&created=>$SINCE_DATE&per_page=100" \
--jq "[.workflow_runs[] | select(.conclusion != \"skipped\")]" | jq -s "add // []" > pr-runs.json
echo "Found $(jq "length" pr-runs.json) PR CI runs (non-skipped)"
# Show some sample runs
echo "Sample runs:"
jq -r ".[0:3] | .[] | \" Run \(.id): \(.event) - \(.display_title | .[0:60])\"" pr-runs.json
# Fetch jobs for PR runs
echo "[]" > all-pr-jobs.json
# Build a cache of branch -> PR info mappings as JSON file (including merge status)
echo "Building PR cache from recent PRs..."
gh api "repos/kata-containers/kata-containers/pulls?state=all&per_page=100" \
--jq "[.[] | {branch: .head.ref, number: .number, title: .title, merged: (.merged_at != null), state: .state, merged_at: .merged_at}]" > pr-cache.json
echo " Cached $(jq length pr-cache.json) PR mappings"
echo " Merged PRs: $(jq "[.[] | select(.merged == true)] | length" pr-cache.json)"
# For local testing, process only 20 runs; for production use 100
for run_id in $(jq -r "sort_by(.created_at) | reverse | .[0:20] | .[].id" pr-runs.json); do
run_info=$(jq -r --arg id "$run_id" ".[] | select(.id == (\$id | tonumber))" pr-runs.json)
pr_number=$(echo "$run_info" | jq -r ".pull_requests[0].number // null")
head_sha=$(echo "$run_info" | jq -r ".head_sha // \"unknown\"")
display_title=$(echo "$run_info" | jq -r ".display_title // \"unknown\"")
run_attempt=$(echo "$run_info" | jq -r ".run_attempt // 1")
created_at=$(echo "$run_info" | jq -r ".created_at")
# For pull_request_target, head_branch is "main", so we need to find PR by title match
pr_title="$display_title"
pr_info=$(jq -r --arg title "$display_title" "[.[] | select(.title == \$title)] | .[0] // empty" pr-cache.json)
if [ "$pr_number" = "null" ] || [ -z "$pr_number" ]; then
pr_number=$(echo "$pr_info" | jq -r ".number // empty" 2>/dev/null)
fi
pr_merged=$(echo "$pr_info" | jq -r ".merged // false" 2>/dev/null)
pr_state=$(echo "$pr_info" | jq -r ".state // empty" 2>/dev/null)
# Skip if we still dont have a PR number
if [ -z "$pr_number" ]; then
echo "Skipping run $run_id (title: ${display_title:0:40}...) - no PR found"
continue
fi
# Truncate title for display
display_title=$(echo "$pr_title" | cut -c1-50)
[ ${#pr_title} -gt 50 ] && display_title="${display_title}..."
echo "Fetching jobs for run $run_id (PR #$pr_number: $display_title, attempt $run_attempt)..."
# Fetch ALL k8s test jobs from:
# - run-k8s-tests-on-aks.yaml (run-k8s-tests with matrix)
# - run-k8s-tests-on-arm64.yaml (run-k8s-tests-on-arm64)
# - run-k8s-tests-on-nvidia-gpu.yaml (run-nvidia-gpu-tests, run-nvidia-gpu-snp-tests)
# - run-k8s-tests-on-zvsi.yaml (run-k8s-tests with matrix)
# - run-k8s-tests-on-tdx.yaml, run-k8s-tests-on-sev.yaml, etc.
gh api \
-H "Accept: application/vnd.github+json" \
--paginate \
"repos/kata-containers/kata-containers/actions/runs/$run_id/jobs?per_page=100&filter=all" \
--jq ".jobs[] | select(.name | test(\"run-k8s-tests|run-nvidia-gpu|run-kata-coco\"; \"i\"))" | \
jq -s --arg run_id "$run_id" --arg pr "$pr_number" --arg title "$pr_title" --arg sha "$head_sha" --arg attempt "$run_attempt" --arg created "$created_at" --arg merged "$pr_merged" --arg state "$pr_state" \
"[.[] | . + {workflow_run_id: \$run_id, pr_number: \$pr, pr_title: \$title, head_sha: \$sha, run_attempt: (\$attempt | tonumber), run_created_at: \$created, pr_merged: (\$merged == \"true\"), pr_state: \$state}]" > run-jobs.json
job_count=$(jq "length" run-jobs.json)
if [ "$job_count" -gt 0 ]; then
echo " Found $job_count test jobs"
jq -s "add" all-pr-jobs.json run-jobs.json > temp-jobs.json
mv temp-jobs.json all-pr-jobs.json
fi
done
# Create final format
echo "{\"jobs\":" > raw-pr-runs.json
cat all-pr-jobs.json >> raw-pr-runs.json
echo "}" >> raw-pr-runs.json
echo "Total PR jobs: $(jq ".jobs | length" raw-pr-runs.json)"
# Fetch logs for failed PR jobs
echo "Fetching logs for failed PR jobs..."
mkdir -p pr-job-logs
for job_id in $(jq -r "[.jobs[] | select(.conclusion == \"failure\") | .id][0:30] | .[]" raw-pr-runs.json); do
echo "Fetching log for job $job_id..."
curl -sL \
-H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/kata-containers/kata-containers/actions/jobs/$job_id/logs" \
-o "pr-job-logs/$job_id.log"
size=$(wc -c < "pr-job-logs/$job_id.log")
not_ok=$(grep -c "not ok" "pr-job-logs/$job_id.log" 2>/dev/null || echo 0)
echo " Log: $size bytes, $not_ok \"not ok\" lines"
done
echo ""
echo "✅ PR flaky data fetched!"
echo " PR Runs: $(jq "length" pr-runs.json)"
echo " PR Jobs: $(jq ".jobs | length" raw-pr-runs.json)"
echo " PR Logs: $(ls pr-job-logs/*.log 2>/dev/null | wc -l)"
'
}
# Process data
process_data() {
echo "⚙️ Processing data..."
docker run --rm \
-v "$SCRIPT_DIR:/app" \
ci-dashboard-local \
bash -c '
cd /app
# Install dependencies if needed
if [ ! -d node_modules ]; then
echo "Installing npm dependencies..."
npm install
fi
echo "Processing nightly data..."
node scripts/process-data.js
if [ -f raw-pr-runs.json ]; then
echo ""
echo "Processing flaky data..."
node scripts/process-flaky-data.js
fi
echo ""
echo "✅ Data processing complete!"
'
}
# Serve locally
serve() {
echo "🌐 Starting local server at http://localhost:8080"
echo " Press Ctrl+C to stop"
echo ""
docker run --rm \
-v "$SCRIPT_DIR:/app" \
-p 8080:8080 \
ci-dashboard-local \
npx http-server /app -p 8080 -c-1
}
# Main
case "${1:-both}" in
fetch)
build_container
fetch_nightly_data
fetch_flaky_data
process_data
;;
serve)
build_container
serve
;;
both)
build_container
fetch_nightly_data
fetch_flaky_data
process_data
serve
;;
*)
echo "Usage: $0 [fetch|serve|both]"
echo ""
echo " fetch - Fetch and process data only"
echo " serve - Start local server only (requires data.json)"
echo " both - Fetch data and start server (default)"
exit 1
;;
esac