ops(dev): add release evidence auto-generator script

Made-with: Cursor
This commit is contained in:
Apple
2026-03-02 10:13:06 -08:00
parent 47073ba761
commit e75fd334bf

192
ops/generate_release_evidence.sh Executable file
View File

@@ -0,0 +1,192 @@
#!/usr/bin/env bash
# Generate partially prefilled release evidence markdown.
#
# Usage:
# bash ops/generate_release_evidence.sh
# bash ops/generate_release_evidence.sh --out release-evidence-custom.md
# bash ops/generate_release_evidence.sh --sofiia-url http://127.0.0.1:8002 --skip-preflight
#
# Exit codes:
# 0 = file created
# 1 = critical error
set -euo pipefail
SOFIIA_URL="http://127.0.0.1:8002"
OUT_FILE=""
SKIP_PREFLIGHT=0
SKIP_HEALTH=0
while [ "$#" -gt 0 ]; do
case "$1" in
--out)
OUT_FILE="${2:-}"
shift 2
;;
--sofiia-url)
SOFIIA_URL="${2:-}"
shift 2
;;
--skip-preflight)
SKIP_PREFLIGHT=1
shift
;;
--skip-health)
SKIP_HEALTH=1
shift
;;
-h|--help)
cat <<'EOF'
Usage: bash ops/generate_release_evidence.sh [options]
Options:
--out <file> Output markdown file path
--sofiia-url <url> Sofiia console base URL (default: http://127.0.0.1:8002)
--skip-preflight Skip preflight execution
--skip-health Skip health/metrics checks
EOF
exit 0
;;
*)
echo "Unknown argument: $1"
exit 1
;;
esac
done
if ! command -v git >/dev/null 2>&1; then
echo "ERROR: git is required"
exit 1
fi
if ! command -v date >/dev/null 2>&1; then
echo "ERROR: date is required"
exit 1
fi
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "${REPO_ROOT}" ]; then
echo "ERROR: not inside a git repository"
exit 1
fi
cd "${REPO_ROOT}"
TS_TAG="$(date -u '+%Y%m%d-%H%M%S')"
if [ -z "${OUT_FILE}" ]; then
OUT_FILE="release-evidence-${TS_TAG}.md"
fi
UTC_TS="$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
KYIV_TS="$(TZ=Europe/Kyiv date '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
GIT_SHA="$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")"
GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")"
GIT_MSG="$(git log -1 --pretty=%s 2>/dev/null || echo "unknown")"
HOST_NAME="$(hostname 2>/dev/null || echo "unknown")"
NODE_ID_VALUE="${NODE_ID:-unset}"
OPERATOR_VALUE="${USER:-unknown}"
PREFLIGHT_STATUS="SKIPPED"
PREFLIGHT_CMD='bash ops/preflight_sofiia_console.sh'
if [ "${SKIP_PREFLIGHT}" = "0" ]; then
set +e
bash ops/preflight_sofiia_console.sh >/dev/null 2>&1
PREFLIGHT_CODE=$?
set -e
if [ "${PREFLIGHT_CODE}" = "0" ]; then
PREFLIGHT_STATUS="PASS"
elif [ "${PREFLIGHT_CODE}" = "2" ]; then
PREFLIGHT_STATUS="WARN"
else
PREFLIGHT_STATUS="FAIL"
fi
fi
HEALTH_CODE="SKIPPED"
METRICS_CODE="SKIPPED"
if [ "${SKIP_HEALTH}" = "0" ]; then
set +e
HEALTH_CODE="$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "${SOFIIA_URL}/api/health" 2>/dev/null || echo "000")"
METRICS_CODE="$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "${SOFIIA_URL}/metrics" 2>/dev/null || echo "000")"
set -e
fi
cat > "${OUT_FILE}" <<EOF
# Release Evidence: Sofiia Console
## 1) Release metadata
- Release ID: ${TS_TAG}
- Date/Time UTC: ${UTC_TS}
- Date/Time Europe/Kyiv: ${KYIV_TS}
- Operator: ${OPERATOR_VALUE}
- Hostname: ${HOST_NAME}
- NODE_ID (env): ${NODE_ID_VALUE}
- Target nodes: NODA1 / NODA2
- Deployed SHAs:
- sofiia-console: ${GIT_SHA}
- router:
- gateway:
- memory-service:
- Git branch: ${GIT_BRANCH}
- HEAD commit message: ${GIT_MSG}
- Change summary (1-3 bullets):
-
## 2) Preflight results
- Command: \`${PREFLIGHT_CMD}\`
- Status: ${PREFLIGHT_STATUS}
- WARN summary (if any):
-
## 3) Deploy steps performed
- NODA2 precheck: OK / FAIL
- Notes:
- NODA1 rollout: OK / FAIL
- Method (docker/systemd/manual):
- Notes:
- NODA2 finalize: OK / FAIL
- Notes:
## 4) Smoke evidence
- GET /api/health: HTTP ${HEALTH_CODE}
- GET /metrics: HTTP ${METRICS_CODE}
- Idempotency A/B smoke:
- Command: \`bash ops/redis_idempotency_smoke.sh\`
- Result: PASS / FAIL
- message_id:
- /api/audit auth checks:
- without key -> 401 confirmed: yes / no
- with key -> 200 confirmed: yes / no
## 5) Post-release checks
- Key metrics deltas (optional):
- sofiia_rate_limited_total:
- sofiia_idempotency_replays_total:
- Audit write/read quick check: OK / FAIL
- Retention dry-run:
- Command: \`python3 ops/prune_audit_db.py --dry-run\`
- candidates=
- Notes:
## 6) Rollback plan & outcome
- Rollback needed: no / yes
- If yes:
- reason:
- rollback commands used:
- result:
- Final service state: healthy / degraded
## 7) Sign-off
- Reviewer / approver:
- Timestamp UTC:
- Notes:
EOF
echo "Release evidence file created: ${OUT_FILE}"
exit 0