ops(dev): add release evidence auto-generator script
Made-with: Cursor
This commit is contained in:
192
ops/generate_release_evidence.sh
Executable file
192
ops/generate_release_evidence.sh
Executable 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
|
||||
Reference in New Issue
Block a user