feat(distribution): production install/update/backup scripts + public-repo sync (#106)

Builds the customer-facing distribution: a curated distribution/ directory
containing only what an end customer needs (production docker-compose.yml
with no build: sections, install.sh/update.sh/uninstall.sh/healthcheck.sh/
backup.sh/restore.sh, a customer-facing CHANGELOG.md, and README/LICENSE/
NOTICE/THIRD_PARTY_LICENSES) — pushed as the initial content of the new
public alleyviper/argus repo.

install.sh: detects OS, validates Docker/Compose, fetches all deployment
files, generates a secure DB password, creates the required Docker
network, pulls images, starts the stack, waits for health, and rotates
the default admin/admin credentials via the auth API — printing the
generated password once at the end.

update.sh/healthcheck.sh/backup.sh/restore.sh/uninstall.sh formalize what
was previously ad-hoc README snippets into real, safe-by-default scripts
(uninstall.sh keeps data unless --remove-data is explicitly passed and
confirmed; restore.sh requires typed confirmation and documents that it
targets a fresh install, not a live merge).

Added a sync-distribution job to release.yml: after a successful release,
mirrors distribution/ into the public repo and creates a matching
(customer-facing, commit-log-free) release marker there. Needs a one-time
setup step — a fine-grained PAT scoped to alleyviper/argus added as the
ARGUS_PUBLIC_REPO_TOKEN secret — the job cleanly no-ops until that's added.

Not yet done: GHCR package visibility (argus-secure-api/-ui/-nginx) is
still private, which blocks a genuine end-to-end curl-install test from a
clean, unauthenticated environment — deferred at the user's request until
they flip it manually (GitHub does not expose this via API).
This commit is contained in:
alleyviper
2026-07-19 18:06:02 +01:00
committed by GitHub
commit 44f662afc0
14 changed files with 1213 additions and 0 deletions
Executable
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#
# ARGUS restore — loads a backup created by backup.sh into a FRESH ARGUS
# installation (empty database, no prior data). To restore onto a machine
# that already has data on it, uninstall.sh --remove-data && install.sh
# first, then run this.
#
# cd ~/argus && ./restore.sh argus-backup-20260719-120000.tar.gz
#
set -euo pipefail
INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}"
ARCHIVE="${1:-}"
bold() { printf '\033[1m%s\033[0m\n' "$1"; }
info() { printf ' %s\n' "$1"; }
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
warn() { printf ' \033[33m!\033[0m %s\n' "$1"; }
fail() { printf ' \033[31m✗\033[0m %s\n' "$1" >&2; exit 1; }
[ -n "$ARCHIVE" ] || fail "Usage: ./restore.sh <path-to-backup.tar.gz>"
[ -f "$ARCHIVE" ] || fail "No such file: $ARCHIVE"
[ -f "$INSTALL_DIR/docker-compose.yml" ] || fail "No ARGUS installation found at $INSTALL_DIR (set ARGUS_INSTALL_DIR if you installed elsewhere)."
cd "$INSTALL_DIR"
warn "This assumes a freshly installed, empty ARGUS instance."
warn "Restoring onto an instance that already has data will fail or corrupt it."
printf " Type YES to continue: "
read -r CONFIRM
[ "$CONFIRM" = "YES" ] || fail "Aborted."
WORKDIR="$(mktemp -d)"
trap 'rm -rf "$WORKDIR"' EXIT
tar xzf "$ARCHIVE" -C "$WORKDIR"
[ -f "$WORKDIR/db.sql.gz" ] && [ -f "$WORKDIR/api-data.tar.gz" ] || fail "Not a valid ARGUS backup archive (missing db.sql.gz / api-data.tar.gz)."
bold "Stopping the API (keeping the database up)..."
docker compose stop api ui nginx >/dev/null
bold "Restoring database..."
gunzip -c "$WORKDIR/db.sql.gz" | docker compose exec -T db psql -U "${DB_USER:-postgres}" "${DB_NAME:-argus}" >/dev/null
ok "Database restored"
bold "Restoring application data..."
docker run --rm -v argus_api_data:/data -v "$WORKDIR":/backup alpine \
sh -c "rm -rf /data/* && tar xzf /backup/api-data.tar.gz -C /data" >/dev/null 2>&1
ok "Application data restored"
bold "Starting ARGUS..."
docker compose up -d
echo
ok "Restore complete"
info "Give it a minute to finish starting, then check: ./healthcheck.sh"