feat(deploy): conflict-free 'git pull' update flow (D001/D004/D005)
- deploy.sh: clean-tree guard + git fetch && git pull --ff-only + optional dep reinstall + app.db --migrate + restart. - restart-service.sh: supervisor-agnostic restart (systemd or fallback), waits for an in-flight scan. - check_runtime_git.sh: proves no scan writes git-tracked runtime data. - ids.service: systemd unit template. - tests/test_deployment.py: stdlib unittest invariant tests for the deployment runtime-isolation guarantee.
This commit is contained in:
1 parent
4960eef312
commit
5d81f617f0
6 files changed
+305
No files matched your search
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# D001 — runtime write-path guard.
|
||||
#
|
||||
# Proves the deployment invariant: after running a scan, `git status` shows
|
||||
# NO tracked file modified by the scan — every runtime write lands in a
|
||||
# gitignored location (data/, reports/, *.db). This is what makes `git pull`
|
||||
# conflict-free on a server where scans keep running.
|
||||
#
|
||||
# If this fails, a scan wrote into a git-tracked path and `git pull` on the
|
||||
# server can conflict. Investigate the printed files before deploying.
|
||||
#
|
||||
# Usage:
|
||||
# deploy/check_runtime_git.sh [--live]
|
||||
# --live additionally run a real scan first (requires log sources) and
|
||||
# then assert the tree is still clean. Without --live it only
|
||||
# verifies that whatever runtime files already exist are untracked.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [[ "${1:-}" == "--live" ]]; then
|
||||
echo "== Running a real scan to exercise every write path =="
|
||||
python app.py scan --quiet --dry-run || true
|
||||
fi
|
||||
|
||||
echo "== Checking that runtime data is gitignored / untracked =="
|
||||
# Paths a scan writes to. Any of these being tracked OR showing as (non-ignored)
|
||||
# untracked means the invariant is broken and a git pull could conflict.
|
||||
RUNTIME_PATHS=(
|
||||
"app/data"
|
||||
"app/reports"
|
||||
"app/data/ids.db"
|
||||
"app/data/ids.db-wal"
|
||||
"app/data/ids.db-shm"
|
||||
)
|
||||
|
||||
fail=0
|
||||
# 1) No runtime path may be TRACKED, except the single allowed read-only
|
||||
# PaloAlto seed. `git ls-files` reflects the index (including staged
|
||||
# changes), so unborn commits don't cause false failures.
|
||||
allowed="app/data/paloalto/scanner-ips.txt"
|
||||
tracked=$(git ls-files app/data app/reports)
|
||||
problem=$(printf '%s\n' "$tracked" | grep -v '^'"/\$allowed"'$' || true)
|
||||
if [[ -n "$problem" ]]; then
|
||||
echo "FAIL: unexpected tracked files under runtime dirs:" >&2
|
||||
echo "$problem" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# 2) Nothing may show as untracked-but-not-ignored under the runtime dirs, i.e.
|
||||
# a scan wrote a file git would happily add (and later conflict on).
|
||||
for p in "${RUNTIME_PATHS[@]}"; do
|
||||
dirty=$(git status --porcelain --untracked-files=all -- "$p" 2>/dev/null || true)
|
||||
if [[ -n "$dirty" ]]; then
|
||||
echo "FAIL: runtime path has changes git cares about:" >&2
|
||||
echo "$dirty" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$fail" -eq 0 ]]; then
|
||||
echo "OK: no tracked runtime data; git pull will not conflict with scan writes."
|
||||
else
|
||||
echo "BROKEN: a scan wrote into git-tracked paths — fix before deploying." >&2
|
||||
exit 1
|
||||
fi
|
||||
Reference in new issue
Block a user