Skip to content

Commit e40ec5f

Browse files
committed
ci: Add script for fetching past test stats from CI
1 parent 4dd5d79 commit e40ec5f

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Usage: ./fetch_stats_from_ci.sh
5+
#
6+
# This script is for fetching historic test stats from GitHub Actions CI.
7+
#
8+
# Requires gh with credentials.
9+
#
10+
# https://github.com/cli/cli/blob/trunk/pkg/cmd/run/view/view.go#L434
11+
12+
dir="$(dirname "$0")"/ci-stats
13+
mkdir -p "${dir}"
14+
15+
pushd "${dir}" >/dev/null
16+
17+
# Stats step name, used for filtering log.
18+
job_step_name="Print test stats"
19+
20+
if [[ ! -f list-ci.yaml.json ]]; then
21+
gh run list -w ci.yaml -L 1000 --json conclusion,createdAt,databaseId,displayTitle,event,headBranch,headSha,name,number,startedAt,status,updatedAt,url,workflowDatabaseId,workflowName \
22+
>list-ci.yaml.json || {
23+
rm -f list-ci.yaml.json
24+
exit 1
25+
}
26+
fi
27+
28+
runs="$(
29+
jq -r '.[] | select(.status == "completed") | select(.conclusion == "success" or .conclusion == "failure") | [.databaseId, .event, .displayTitle, .headBranch, .headSha] | @tsv' \
30+
<list-ci.yaml.json
31+
)"
32+
33+
while read -r run; do
34+
mapfile -d $'\t' -t parts <<<"${run}"
35+
parts[-1]="${parts[-1]%$'\n'}"
36+
37+
database_id="${parts[0]}"
38+
event="${parts[1]}"
39+
display_title="${parts[2]}"
40+
head_branch="${parts[3]}"
41+
head_sha="${parts[4]}"
42+
43+
run_jobs_file=run-"${database_id}"-"${event}"-jobs.json
44+
if [[ ! -f "${run_jobs_file}" ]]; then
45+
echo "Fetching jobs for run: ${display_title} (${database_id}, ${event}, ${head_branch})"
46+
gh run view "${database_id}" --json jobs >"${run_jobs_file}" || {
47+
rm -f "${run_jobs_file}"
48+
exit 1
49+
}
50+
fi
51+
52+
jobs="$(
53+
jq -r '.jobs[] | select(.name | startswith("test-go")) | select(.status == "completed") | select(.conclusion == "success" or .conclusion == "failure") | [.databaseId, .startedAt, .completedAt, .name, .url] | @tsv' \
54+
<"${run_jobs_file}"
55+
)"
56+
57+
while read -r job; do
58+
mapfile -d $'\t' -t parts <<<"${job}"
59+
parts[-1]="${parts[-1]%$'\n'}"
60+
61+
job_database_id="${parts[0]}"
62+
job_started_at="${parts[1]}"
63+
job_completed_at="${parts[2]}"
64+
job_name="${parts[3]}"
65+
job_url="${parts[4]}"
66+
67+
job_log=run-"${database_id}"-job-"${job_database_id}"-"${job_name}".log
68+
if [[ ! -f "${job_log}" ]]; then
69+
echo "Fetching log for: ${job_name} (${job_database_id}, ${job_url})"
70+
# Example log (partial).
71+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4063489Z ##[group]Run # Artifacts are not available after rerunning a job,
72+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4063872Z # Artifacts are not available after rerunning a job,
73+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4064188Z # so we need to print the test stats to the log.
74+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4064642Z go run ./scripts/ci-report/main.go gotests.json | tee gotests_stats.json
75+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4110112Z shell: /usr/bin/bash -e {0}
76+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:18.4110364Z ##[endgroup]
77+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3440469Z {
78+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3441078Z "packages": [
79+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3441448Z {
80+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3442927Z "name": "agent",
81+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3443311Z "time": 17.538
82+
# test-go (ubuntu-latest) Print test stats 2023-04-11T03:02:19.3444048Z },
83+
# ...
84+
gh run view --job "${job_database_id}" --log >"${job_log}" || {
85+
# Sometimes gh failes to extract ZIP, etc. :'(
86+
rm -f "${job_log}"
87+
echo "Failed to fetch log for: ${job_name} (${job_database_id}, ${job_url}), skipping..."
88+
continue
89+
}
90+
log_lines="$(wc -l "${job_log}" | awk '{print $1}')"
91+
if [[ ${log_lines} -lt 2 ]]; then
92+
# Sometimes gh returns nothing and gives no error :'(
93+
rm -f "${job_log}"
94+
echo "Log is empty for: ${job_name} (${job_database_id}, ${job_url}), skipping..."
95+
continue
96+
fi
97+
fi
98+
99+
job_stats="$(
100+
grep "${job_name}.*${job_step_name}" "${job_log}" \
101+
| sed -E 's/.*[0-9-]{10}T[0-9:]{8}\.[0-9]*Z //' \
102+
| grep -E "^[{}\ ].*"
103+
)"
104+
105+
if ! jq -e . >/dev/null 2>&1 <<<"${job_stats}"; then
106+
# Sometimes actions logs are partial when fetched via CLI :'(
107+
echo "Failed to parse stats for: ${job_name} (${job_database_id}, ${job_url}), skipping..."
108+
continue
109+
fi
110+
111+
job_stats_file=run-"${database_id}"-job-"${job_database_id}"-"${job_name}"-stats.json
112+
jq \
113+
--arg event "${event}" \
114+
--arg branch "${head_branch}" \
115+
--arg sha "${head_sha}" \
116+
--arg started_at "${job_started_at}" \
117+
--arg completed_at "${job_completed_at}" \
118+
--arg display_title "${display_title}" \
119+
--arg url "${job_url}" \
120+
'{event: $event, branch: $branch, sha: $sha, started_at: $started_at, completed_at: $completed_at, display_title: $display_title, url: $url, stats: .}' \
121+
<<<"${job_stats}" \
122+
>"${job_stats_file}"
123+
done <<<"${jobs}"
124+
done <<<"${runs}"

0 commit comments

Comments
 (0)