feat(preflight): host/toolchain validation + VERSIONS pin-file — T01
- VERSIONS: 7 container images (CONTRACT_003 §3.2) + 13 host tools, KEY=value, source-able+greppable; images carry :PIN_DIGEST placeholders with a documented pin-digests procedure (D5 determinism — no real deploy until pinned). - preflight.sh: fails closed (non-zero on any required check), bash-3.2 safe, composable checks/ (versions,tools,env,docker) + gated (ssh,dns) that WARN-skip until the stack is configured. - env check honors D2 (passphrase presence only, never printed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
188e30e23e
commit
edc708b826
12 changed files with 763 additions and 0 deletions
82
preflight/lib/common.sh
Normal file
82
preflight/lib/common.sh
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# shellcheck shell=bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# preflight/lib/common.sh — shared helpers for all preflight checks.
|
||||
# Sourced (not executed). POSIX-friendly, macOS bash 3.2 compatible:
|
||||
# no associative arrays, no `mapfile`, no `${var^^}`, no process substitution.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# ANSI colors (disabled if not a tty or NO_COLOR set).
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
||||
PF_RED=$(printf '\033[31m'); PF_GRN=$(printf '\033[32m')
|
||||
PF_YEL=$(printf '\033[33m'); PF_RST=$(printf '\033[0m')
|
||||
else
|
||||
PF_RED=""; PF_GRN=""; PF_YEL=""; PF_RST=""
|
||||
fi
|
||||
|
||||
# Per-process result counters. Each check script reports into these and prints
|
||||
# its own lines; preflight.sh sums up by parsing the exit code + a summary line.
|
||||
PF_PASS=0
|
||||
PF_FAIL=0
|
||||
PF_WARN=0
|
||||
|
||||
pf_pass() { PF_PASS=$((PF_PASS + 1)); printf ' %sPASS%s %s\n' "$PF_GRN" "$PF_RST" "$*"; }
|
||||
pf_fail() { PF_FAIL=$((PF_FAIL + 1)); printf ' %sFAIL%s %s\n' "$PF_RED" "$PF_RST" "$*"; }
|
||||
pf_warn() { PF_WARN=$((PF_WARN + 1)); printf ' %sWARN%s %s\n' "$PF_YEL" "$PF_RST" "$*"; }
|
||||
pf_info() { printf ' %s----%s %s\n' "$PF_YEL" "$PF_RST" "$*"; }
|
||||
|
||||
# pf_summary <check-name> : print one machine-parseable line and return an exit
|
||||
# code: 0 if no failures, 1 if any failure. WARN never fails.
|
||||
pf_summary() {
|
||||
printf ' -> %s: %d pass, %d warn, %d fail\n' "$1" "$PF_PASS" "$PF_WARN" "$PF_FAIL"
|
||||
[ "$PF_FAIL" -eq 0 ]
|
||||
}
|
||||
|
||||
# pf_have <cmd> : true if command exists on PATH.
|
||||
pf_have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# pf_vercmp <a> <b> : numeric dotted-version compare. Echoes:
|
||||
# 0 if a == b, 1 if a > b, 2 if a < b. Missing components treated as 0.
|
||||
# Non-numeric leading tokens are stripped (e.g. "v3.243.0" -> "3.243.0").
|
||||
pf_vercmp() {
|
||||
a=$(printf '%s' "$1" | sed -e 's/^[^0-9]*//' -e 's/[^0-9.].*$//')
|
||||
b=$(printf '%s' "$2" | sed -e 's/^[^0-9]*//' -e 's/[^0-9.].*$//')
|
||||
# Normalize to dot-separated fields and compare field by field.
|
||||
IFS=.
|
||||
# shellcheck disable=SC2086
|
||||
set -- $a; a1=${1:-0}; a2=${2:-0}; a3=${3:-0}; a4=${4:-0}
|
||||
# shellcheck disable=SC2086
|
||||
set -- $b; b1=${1:-0}; b2=${2:-0}; b3=${3:-0}; b4=${4:-0}
|
||||
unset IFS
|
||||
i=1
|
||||
for pair in "$a1 $b1" "$a2 $b2" "$a3 $b3" "$a4 $b4"; do
|
||||
# shellcheck disable=SC2086
|
||||
set -- $pair
|
||||
av=$(printf '%s' "$1" | sed 's/[^0-9].*$//'); av=${av:-0}
|
||||
bv=$(printf '%s' "$2" | sed 's/[^0-9].*$//'); bv=${bv:-0}
|
||||
if [ "$av" -gt "$bv" ] 2>/dev/null; then echo 1; return 0; fi
|
||||
if [ "$av" -lt "$bv" ] 2>/dev/null; then echo 2; return 0; fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo 0
|
||||
}
|
||||
|
||||
# pf_ge <have> <min> : true (0) if version `have` >= `min`.
|
||||
pf_ge() {
|
||||
r=$(pf_vercmp "$1" "$2")
|
||||
[ "$r" = "0" ] || [ "$r" = "1" ]
|
||||
}
|
||||
|
||||
# pf_versions_file : absolute path to the VERSIONS pin-file (repo root).
|
||||
pf_versions_file() {
|
||||
# lib/ is preflight/lib, repo root is two levels up.
|
||||
printf '%s/VERSIONS' "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../.." && pwd)"
|
||||
}
|
||||
|
||||
# pf_versions_get <KEY> : echo the value of KEY from VERSIONS (greppable form).
|
||||
pf_versions_get() {
|
||||
vf=$(pf_versions_file)
|
||||
[ -f "$vf" ] || return 1
|
||||
line=$(grep "^$1=" "$vf" 2>/dev/null | head -1)
|
||||
[ -n "$line" ] || return 1
|
||||
printf '%s' "${line#*=}"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue