#!/usr/bin/env bash # retention-verify-archive.sh — retention piece 1 (board bug b67084660385). # Verifies one archive file: checksum, integrity, clean listing, spot-extract. # Usage: retention-verify-archive.sh # Exits non-zero on any verification failure. Missing file = SKIP (rc 0). set -uo pipefail fail() { echo "FAIL verify $1: $2"; exit 1; } P="${1:?usage: $0 }" if [ ! -f "$P" ]; then echo "SKIP verify: $P does not exist" exit 0 fi echo "== verify $P ==" # 1. checksum sidecar if [ -f "$P.sha256" ]; then ( cd "$(dirname "$P")" && sha256sum -c "$(basename "$P").sha256" ) \ || fail "$P" "sha256 mismatch" echo " checksum: OK" else echo " checksum: no sidecar (archiver writes one for new .gz files)" fi # 2. integrity + clean listing for gzip archives if [[ "$P" == *.gz ]]; then gzip -t "$P" || fail "$P" "gzip integrity test failed" echo " integrity: gzip -t OK" echo " listing:"; gzip -l "$P" | sed 's/^/ /' reader="zcat" else reader="cat" fi # 3. spot-extract: first 3 and last 3 lines must be valid JSON n=0 while IFS= read -r line; do n=$((n+1)) echo "$line" | python3 -c 'import json,sys; json.loads(sys.stdin.read())' \ || fail "$P" "line $n is not valid JSON" done < <( { $reader "$P" | head -3; $reader "$P" | tail -3; } 2>/dev/null ) total="$( $reader "$P" | wc -l )" echo " spot-extract: first/last 3 lines valid JSON ($total total lines)" # 4. spot-extract timestamps: report newest/oldest ts seen in the sample sample="$($reader "$P" | head -2000)" ts_line="$(echo "$sample" | python3 -c ' import json,sys keys=("ts","timestamp","time","created_at","sent_at","resolved_at") seen=[] for line in sys.stdin: line=line.strip() if not line: continue try: rec=json.loads(line) except Exception: continue for k in keys: if rec.get(k): seen.append(str(rec[k])); break seen=sorted(set(seen)) print(("oldest="+seen[0]+" newest="+seen[-1]) if seen else "no-ts-field") ')" echo " timestamps: $ts_line" echo "OK verify $P"