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:
Executable
+144
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# ARGUS installer — fetches the production compose stack, generates secrets,
|
||||
# starts the platform, and prints your admin credentials.
|
||||
#
|
||||
# curl -fsSL https://raw.githubusercontent.com/alleyviper/argus/main/install.sh | bash
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
REPO_RAW_BASE="https://raw.githubusercontent.com/alleyviper/argus/main"
|
||||
INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}"
|
||||
NETWORK_NAME="argus-network"
|
||||
|
||||
# ── Output helpers ────────────────────────────────────────────────────────────
|
||||
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; }
|
||||
|
||||
bold "ARGUS Installer"
|
||||
echo
|
||||
|
||||
# ── 1. OS detection (informational — Linux is the supported target) ──────────
|
||||
OS="$(uname -s)"
|
||||
case "$OS" in
|
||||
Linux) ok "Detected Linux" ;;
|
||||
Darwin) warn "Detected macOS — supported for local evaluation via Docker Desktop, not for production deployment." ;;
|
||||
*) warn "Unrecognized OS ($OS) — proceeding, but only Linux is officially supported." ;;
|
||||
esac
|
||||
|
||||
# ── 2. Docker / Compose availability ──────────────────────────────────────────
|
||||
command -v docker >/dev/null 2>&1 || fail "Docker is not installed. Install Docker first: https://docs.docker.com/engine/install/"
|
||||
docker info >/dev/null 2>&1 || fail "Docker is installed but not running (or this user lacks permission). Start Docker / add your user to the docker group, then re-run."
|
||||
docker compose version >/dev/null 2>&1 || fail "Docker Compose v2 (the 'docker compose' plugin) is required. See https://docs.docker.com/compose/install/"
|
||||
ok "Docker and Docker Compose are available"
|
||||
|
||||
# ── 3. Fetch deployment files ──────────────────────────────────────────────────
|
||||
mkdir -p "$INSTALL_DIR/docker-socket-proxy"
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
fetch() {
|
||||
curl -fsSL "$REPO_RAW_BASE/$1" -o "$2" || fail "Failed to download $1"
|
||||
}
|
||||
|
||||
fetch "docker-compose.yml" "docker-compose.yml"
|
||||
fetch "docker-socket-proxy/haproxy.cfg.template" "docker-socket-proxy/haproxy.cfg.template"
|
||||
for script in update.sh uninstall.sh healthcheck.sh backup.sh restore.sh; do
|
||||
fetch "$script" "$script"
|
||||
chmod +x "$script"
|
||||
done
|
||||
if [ ! -f .env ]; then
|
||||
fetch ".env.example" ".env"
|
||||
ok "Fetched deployment files into $INSTALL_DIR"
|
||||
else
|
||||
ok "Fetched compose file (kept your existing .env)"
|
||||
fi
|
||||
|
||||
# ── 4. Generate secrets (first install only — never overwrite an existing .env) ──
|
||||
if grep -q '^DB_PASSWORD=change-me-in-production' .env 2>/dev/null; then
|
||||
DB_PASSWORD="$(openssl rand -base64 24 | tr -d '\n')"
|
||||
sed -i.bak "s#^DB_PASSWORD=.*#DB_PASSWORD=${DB_PASSWORD}#" .env && rm -f .env.bak
|
||||
ok "Generated a secure database password"
|
||||
fi
|
||||
|
||||
# ── 5. Docker network ─────────────────────────────────────────────────────────
|
||||
if docker network inspect "$NETWORK_NAME" >/dev/null 2>&1; then
|
||||
ok "Docker network '$NETWORK_NAME' already exists"
|
||||
else
|
||||
docker network create "$NETWORK_NAME" >/dev/null
|
||||
ok "Created Docker network '$NETWORK_NAME'"
|
||||
fi
|
||||
|
||||
# ── 6. Pull and start ──────────────────────────────────────────────────────────
|
||||
bold "Pulling images and starting ARGUS..."
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
echo
|
||||
|
||||
# ── 7. Wait for the API to become healthy ─────────────────────────────────────
|
||||
API_HOST_PORT="$(grep -oP '^API_HOST_PORT=\K.*' .env 2>/dev/null || true)"
|
||||
API_HOST_PORT="${API_HOST_PORT:-9080}"
|
||||
bold "Waiting for ARGUS to finish starting (this can take a few minutes on first boot)..."
|
||||
READY=false
|
||||
for _ in $(seq 1 90); do
|
||||
if curl -fsS "http://127.0.0.1:${API_HOST_PORT}/health" >/dev/null 2>&1; then
|
||||
READY=true
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
if [ "$READY" != "true" ]; then
|
||||
warn "ARGUS did not report healthy within 7.5 minutes."
|
||||
warn "Check container status with: docker compose -f $INSTALL_DIR/docker-compose.yml ps"
|
||||
warn "and logs with: docker compose -f $INSTALL_DIR/docker-compose.yml logs api"
|
||||
exit 1
|
||||
fi
|
||||
ok "ARGUS is up"
|
||||
|
||||
# ── 8. Rotate the default admin credentials ───────────────────────────────────
|
||||
ADMIN_USER="admin"
|
||||
ADMIN_PASSWORD="Ax9!$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 20)"
|
||||
API_BASE="http://127.0.0.1:${API_HOST_PORT}/api/v1"
|
||||
|
||||
LOGIN_RESP="$(curl -fsS -X POST "${API_BASE}/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"admin","password":"admin"}' 2>/dev/null || true)"
|
||||
TOKEN="$(printf '%s' "${LOGIN_RESP}" | grep -oP '"token"\s*:\s*"\K[^"]+' || true)"
|
||||
|
||||
CREDS_ROTATED=false
|
||||
if [ -n "$TOKEN" ]; then
|
||||
CHANGE_RESP="$(curl -fsS -o /dev/null -w '%{http_code}' -X POST "${API_BASE}/auth/change-credentials" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H "Authorization: Bearer ${TOKEN}" \
|
||||
-d "{\"current_password\":\"admin\",\"new_username\":\"${ADMIN_USER}\",\"new_password\":\"${ADMIN_PASSWORD}\",\"new_password_confirm\":\"${ADMIN_PASSWORD}\"}" 2>/dev/null || echo "000")"
|
||||
if [ "$CHANGE_RESP" = "200" ]; then
|
||||
CREDS_ROTATED=true
|
||||
ok "Rotated the default admin password"
|
||||
fi
|
||||
fi
|
||||
if [ "$CREDS_ROTATED" != "true" ]; then
|
||||
warn "Could not auto-rotate admin credentials (already changed on a prior run?). Log in with your existing credentials, or admin/admin on a genuinely fresh install."
|
||||
fi
|
||||
|
||||
# ── 9. Summary ─────────────────────────────────────────────────────────────────
|
||||
UI_PORT="$(grep -oP '^UI_PORT=\K.*' .env 2>/dev/null || true)"
|
||||
UI_PORT="${UI_PORT:-81}"
|
||||
|
||||
echo
|
||||
bold "ARGUS is installed and running."
|
||||
echo
|
||||
info "Admin Panel: https://localhost:${UI_PORT} (accept the self-signed certificate)"
|
||||
if [ "$CREDS_ROTATED" = "true" ]; then
|
||||
info "Username: ${ADMIN_USER}"
|
||||
info "Password: ${ADMIN_PASSWORD}"
|
||||
warn "Save this password now — it is not stored anywhere and cannot be recovered."
|
||||
else
|
||||
info "Log in with your existing admin credentials."
|
||||
fi
|
||||
echo
|
||||
info "Configuration: ${INSTALL_DIR}/.env and ${INSTALL_DIR}/docker-compose.yml"
|
||||
info "Upgrade: cd ${INSTALL_DIR} && docker compose pull && docker compose up -d"
|
||||
info "Backups: see https://github.com/alleyviper/argus#backups"
|
||||
echo
|
||||
Reference in New Issue
Block a user