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,75 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# D004 — conflict-free deployment update wrapper.
|
||||
#
|
||||
# Updates a deployed instance with `git pull` WITHOUT ever hitting a merge
|
||||
# conflict from files a scan wrote since the last pull. Guarantees:
|
||||
#
|
||||
# 1. Refuses to update over uncommitted tracked changes (clean-tree guard).
|
||||
# 2. Fast-forwards only (--ff-only): never creates merge commits and never
|
||||
# silently conflicts.
|
||||
# 3. Runtime data (data/, reports/, *.db) is gitignored, so a running scan
|
||||
# leaves no dirty tracked paths and never blocks the pull.
|
||||
# 4. Reinstalls dependencies only if requirements.txt changed.
|
||||
# 5. Applies DB migrations idempotently.
|
||||
# 6. Restarts the app under its supervisor.
|
||||
#
|
||||
# Usage:
|
||||
# sudo deploy/deploy.sh # update + restart (default env: production)
|
||||
# ENV=production deploy/deploy.sh # use deploy/ids@production.service
|
||||
# deploy/deploy.sh --no-restart # update deps+migrate but don't restart
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ENV="${ENV:-production}"
|
||||
RESTART="${1:-restart}"
|
||||
ROOT="$(pwd)"
|
||||
PY="${PY:-python}"
|
||||
|
||||
echo "== Intrusion Detection deploy (env=$ENV) =="
|
||||
|
||||
# 1) Clean-tree guard. Runtime data is gitignored, so this only fails if a
|
||||
# human edited tracked source without committing.
|
||||
if ! git diff --quiet HEAD || ! git diff --cached --quiet; then
|
||||
echo "REFUSING: working tree is not clean." >&2
|
||||
echo "Commit or stash tracked changes, then re-run." >&2
|
||||
echo " git status --short" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2) Pre-flight: prove runtime isolation holds (fail fast before touching repo).
|
||||
if "$ROOT/deploy/check_runtime_git.sh"; then
|
||||
echo "== runtime isolation ok =="
|
||||
else
|
||||
echo "ABORT: runtime isolation check failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3) Fast-forward only.
|
||||
echo "== fetching upstream =="
|
||||
git fetch origin
|
||||
echo "== fast-forward pull =="
|
||||
git pull --ff-only
|
||||
|
||||
# 4) Dependencies (only if the requirement set changed since the last pull).
|
||||
if git diff --quiet HEAD@{1} HEAD -- requirements.txt; then
|
||||
echo "== requirements.txt unchanged; skipping pip =="
|
||||
else
|
||||
echo "== requirements.txt changed; installing deps =="
|
||||
command -v "$PY" >/dev/null && "$PY" -m pip install -r requirements.txt \
|
||||
|| pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
# 5) Apply DB migrations (idempotent).
|
||||
echo "== applying DB migrations =="
|
||||
"$PY" -m app.db --migrate
|
||||
|
||||
# 6) Restart under the supervisor.
|
||||
if [[ "$RESTART" == "--no-restart" ]]; then
|
||||
echo "== --no-restart: update complete; restart manually =="
|
||||
else
|
||||
echo "== restarting service =="
|
||||
"$ROOT/deploy/restart-service.sh" "$ENV"
|
||||
fi
|
||||
|
||||
echo "== deploy complete =="
|
||||
Reference in new issue
Block a user