#!/usr/bin/env bash RR_ENABLED=0 RR_OUTPUT_PATH="" RR_TMP_DIR="" RR_STEPS_TSV="" RR_ROOT_DIR="" RR_SCHEMA_VERSION="1.0" rr_now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ" } rr_now_ms() { if date +%s%3N >/dev/null 2>&1; then date +%s%3N else echo "$(( $(date +%s) * 1000 ))" fi } rr_sanitize() { local value="${1//$'\t'/ }" value="${value//$'\n'/ }" printf '%s' "$value" } rr_prepare() { local output_path="$1" local root_dir="$2" RR_ENABLED=1 RR_OUTPUT_PATH="$output_path" RR_ROOT_DIR="$root_dir" RR_TMP_DIR="$(mktemp -d)" RR_STEPS_TSV="$RR_TMP_DIR/steps.tsv" : > "$RR_STEPS_TSV" } rr_record_step() { if [[ "$RR_ENABLED" != "1" ]]; then return 0 fi local step_id="$1" local name="$2" local enabled="$3" local command="$4" local status="$5" local exit_code="$6" local started_at="$7" local ended_at="$8" local duration_ms="$9" printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ "$(rr_sanitize "$step_id")" \ "$(rr_sanitize "$name")" \ "$(rr_sanitize "$enabled")" \ "$(rr_sanitize "$command")" \ "$(rr_sanitize "$status")" \ "$(rr_sanitize "$exit_code")" \ "$(rr_sanitize "$started_at")" \ "$(rr_sanitize "$ended_at")" \ "$(rr_sanitize "$duration_ms")" \ >> "$RR_STEPS_TSV" } rr_git_sha() { if command -v git >/dev/null 2>&1 && git -C "$RR_ROOT_DIR" rev-parse --short HEAD >/dev/null 2>&1; then git -C "$RR_ROOT_DIR" rev-parse --short HEAD else echo "unknown" fi } rr_git_branch() { if command -v git >/dev/null 2>&1 && git -C "$RR_ROOT_DIR" rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then git -C "$RR_ROOT_DIR" rev-parse --abbrev-ref HEAD else echo "unknown" fi } rr_finalize() { if [[ "$RR_ENABLED" != "1" ]]; then return 0 fi local overall_status="$1" local overall_duration_ms="$2" local passed="$3" local failed="$4" local skipped="$5" local total="$6" local output_dir output_dir="$(dirname "$RR_OUTPUT_PATH")" mkdir -p "$output_dir" local git_sha local git_branch local generated_at git_sha="$(rr_git_sha)" git_branch="$(rr_git_branch)" generated_at="$(rr_now_iso)" python3 - "$RR_STEPS_TSV" "$RR_OUTPUT_PATH" "$RR_SCHEMA_VERSION" "$generated_at" "$git_sha" "$git_branch" "$overall_status" "$overall_duration_ms" "$passed" "$failed" "$skipped" "$total" <<'PY' import json import sys from pathlib import Path steps_path = Path(sys.argv[1]) output_path = Path(sys.argv[2]) schema_version = sys.argv[3] generated_at = sys.argv[4] git_sha = sys.argv[5] git_branch = sys.argv[6] overall_status = sys.argv[7] overall_duration_ms = int(sys.argv[8]) passed = int(sys.argv[9]) failed = int(sys.argv[10]) skipped = int(sys.argv[11]) total = int(sys.argv[12]) steps = [] if steps_path.exists(): for line in steps_path.read_text(encoding="utf-8").splitlines(): if not line.strip(): continue parts = line.split("\t") if len(parts) != 9: continue step_id, name, enabled, command, status, exit_code, started_at, ended_at, duration_ms = parts steps.append( { "id": step_id, "name": name, "enabled": enabled == "1", "status": status, "exit_code": int(exit_code), "duration_ms": int(duration_ms), "started_at": started_at, "ended_at": ended_at, "command": command, } ) payload = { "schema_version": schema_version, "generated_at": generated_at, "git": { "sha": git_sha, "branch": git_branch, }, "overall": { "status": overall_status, "duration_ms": overall_duration_ms, "passed": passed, "failed": failed, "skipped": skipped, "total": total, }, "steps": steps, } output_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") PY } rr_cleanup() { if [[ -n "$RR_TMP_DIR" && -d "$RR_TMP_DIR" ]]; then rm -rf "$RR_TMP_DIR" fi }