rickub/ci-benchpublic Fork 0
d5ea3aa48e8aae659056c5b0f4f3a77656083ec6
Commits
Clone
git clone https://git.rickub.com/rickub/ci-bench.git
git clone ssh://git@rickub.com/rickub/ci-bench.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

emit_timing.sh · 143 lines · 5.4 KBBash Blame HistoryRaw
ci-bench: cross-platform CI race scaffold (rickub CI vs GitHub Actions) 0baf736 Olivier Girardot yesterday1#!/usr/bin/env bash
2# emit_timing.sh — sourced by every benchmark step (CI and local).
3#
4# Emits ONE JSON line per step to $GITHUB_WORKSPACE/results.jsonl (falling back
5# to ./results.jsonl). Schema:
6# {"step": <name>, "platform": <github|rickub|local>, "run_id": <id>,
7# "start": <iso8601>, "end": <iso8601>, "status": <ok|fail|skipped>,
8# "duration_ms": <int>, # convenience, = end - start
9# "value": <json>, "unit": <string>, "reason": <string>} # optional extras
10#
11# Public API:
12# bench_step STEP CMD [ARGS...] run CMD, record ok/fail (compound bodies:
13# bench_step x bash -c '...; ...')
14# bench_skip STEP REASON record a skipped step (e.g. no docker)
15# bench_metric STEP VALUE UNIT record a measurement now (value is raw JSON:
16# number, "string", or null)
17# bench_mark STEP zero-duration marker (e.g. job-start)
18#
19# Determined at source time (env overrides win):
20# BENCH_PLATFORM github | rickub | local (default: inferred)
21# BENCH_RUN_ID stable identifier for the whole run (default: GITHUB_RUN_ID,
22# else local-<utcstamp>-<pid>)
23#
24# This file sets NO shell options on purpose: it is sourced into the caller's
25# shell and must not change its behaviour.
26
27# --- clock helpers -----------------------------------------------------------
28# Prefer GNU date's %N (all Linux CI guests); BSD date (macOS) prints a literal
29# "N" which the regex rejects; then python3; then whole-second granularity.
30_bench_now_iso() {
31 local t
32 t=$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ 2>/dev/null || true)
33 # A valid timestamp contains no "N"; BSD date prints a literal one for %3N.
34 case $t in
35 *N*) ;; # %3N unsupported (BSD date) — fall through
36 *) printf '%s\n' "$t"; return 0 ;;
37 esac
38 t=$(python3 -c 'import datetime
39print(datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]+"Z")' 2>/dev/null || true)
40 case $t in
41 2[0-9][0-9][0-9]-*) printf '%s\n' "$t"; return 0 ;;
42 esac
43 date -u +%Y-%m-%dT%H:%M:%S.000Z
44}
45
46_bench_iso_to_ms() { # ISO (above format) -> epoch ms; python3 when available
47 python3 - "$1" <<'PY' 2>/dev/null || printf '%s\n' "$(_bench_iso_to_ms_shell "$1")"
48import sys, datetime
49s = sys.argv[1]
50print(int(datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=datetime.timezone.utc).timestamp() * 1000))
51PY
52}
53
54_bench_iso_to_ms_shell() { # fallback: second granularity (documented coarser)
55 local s=${1%%.*}
56 date -u -j -f '%Y-%m-%dT%H:%M:%S' "$s" +%s 2>/dev/null \
57 || date -u -d "$s" +%s 2>/dev/null \
58 || printf '0\n'
59}
60
61# --- identity ----------------------------------------------------------------
62_bench_platform() {
63 if [ -n "${BENCH_PLATFORM:-}" ]; then
64 printf '%s\n' "$BENCH_PLATFORM"
65 elif [ -n "${GITHUB_SERVER_URL:-}" ]; then
66 case $GITHUB_SERVER_URL in
67 *github.com*) printf 'github\n' ;;
68 *) printf 'rickub\n' ;;
69 esac
70 else
71 printf 'local\n'
72 fi
73}
74
75_bench_run_id() {
76 if [ -n "${BENCH_RUN_ID:-}" ]; then printf '%s\n' "$BENCH_RUN_ID"
77 elif [ -n "${GITHUB_RUN_ID:-}" ]; then
78 printf '%s-attempt%s\n' "${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT:-1}"
79 else
80 printf 'local-%s-%s\n' "$(date -u +%Y%m%dT%H%M%SZ)" "$$"
81 fi
82}
83
84# --- record emission ---------------------------------------------------------
85_bench_results_file() {
86 local dir=${GITHUB_WORKSPACE:-$PWD}
87 printf '%s/results.jsonl\n' "$dir"
88}
89
90_bench_esc() { # minimal JSON string escaping (control chars flattened)
91 printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | tr '\n\r\t' ' '
92}
93
94# _bench_emit STEP STATUS START END [VALUE UNIT REASON]
95_bench_emit() {
96 local step=$1 status=$2 start=$3 end=$4 value=${5-} unit=${6-} reason=${7-}
97 local line f dur_ms
98 f=$(_bench_results_file)
99 dur_ms=$(( $(_bench_iso_to_ms "$end") - $(_bench_iso_to_ms "$start") ))
100 [ "$dur_ms" -lt 0 ] && dur_ms=0
101 line='{"step": "'$(_bench_esc "$step")'"'
102 line+=', "platform": "'$(_bench_esc "$(_bench_platform)")'"'
103 line+=', "run_id": "'$(_bench_esc "$(_bench_run_id)")'"'
104 line+=', "start": "'$(_bench_esc "$start")'"'
105 line+=', "end": "'$(_bench_esc "$end")'"'
106 line+=', "status": "'$(_bench_esc "$status")'"'
107 line+=', "duration_ms": '"$dur_ms"
108 [ -n "$value" ] && line+=', "value": '"$value"
109 [ -n "$unit" ] && line+=', "unit": "'$(_bench_esc "$unit")'"'
110 [ -n "$reason" ] && line+=', "reason": "'$(_bench_esc "$reason")'"'
111 line+='}'
112 printf '%s\n' "$line" >>"$f"
113}
114
115# --- public API --------------------------------------------------------------
116bench_step() { # STEP CMD [ARGS...]
117 # Records ok/fail AND propagates the command's exit code, so a failed
118 # workload fails the CI step (not silently recorded as a red data point).
119 local step=$1; shift
120 local start end status rc
121 start=$(_bench_now_iso)
122 rc=0
123 "$@" || rc=$?
124 [ "$rc" -eq 0 ] && status=ok || status=fail
125 end=$(_bench_now_iso)
126 _bench_emit "$step" "$status" "$start" "$end"
127 return "$rc"
128}
129
130bench_skip() { # STEP REASON
131 local now=$(_bench_now_iso)
132 _bench_emit "$1" skipped "$now" "$now" '' '' "$2"
133}
134
135bench_metric() { # STEP VALUE UNIT (VALUE is raw JSON: 123, "ext4", null)
136 local now=$(_bench_now_iso)
137 _bench_emit "$1" ok "$now" "$now" "$2" "$3"
138}
139
140bench_mark() { # STEP
141 local now=$(_bench_now_iso)
142 _bench_emit "$1" ok "$now" "$now"
143}