#!/usr/bin/env bash set -euo pipefail # ─── Colors ─────────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' RESET='\033[0m' ok() { echo -e " ${GREEN}✓${RESET} $*"; } fail() { echo -e " ${RED}✗${RESET} $*" >&2; } info() { echo -e " ${CYAN}→${RESET} $*"; } warn() { echo -e " ${YELLOW}⚠${RESET} $*"; } header() { echo -e "\n${BOLD}$*${RESET}\n"; } ask() { local prompt="$1" local default="${2:-y}" local answer if [[ "$default" == "y" ]]; then echo -en " ${CYAN}?${RESET} $prompt ${DIM}[Y/n]${RESET} " else echo -en " ${CYAN}?${RESET} $prompt ${DIM}[y/N]${RESET} " fi read -r answer < /dev/tty answer="${answer:-$default}" [[ "$answer" =~ ^[Yy]$ ]] } die() { fail "$*"; exit 1; } # ─── Checks ─────────────────────────────────────────────────────────────────── [[ "$(uname -s)" == "Linux" ]] || die "OpsVault only supports Linux." [[ "$EUID" -eq 0 ]] || die "Please run as root: curl -fsSL https://get.opsvault.dev | sudo bash" # ─── Detect architecture ────────────────────────────────────────────────────── ARCH="$(uname -m)" case "$ARCH" in x86_64) ARCH_SUFFIX="amd64" ;; aarch64) ARCH_SUFFIX="arm64" ;; *) die "Unsupported architecture: $ARCH" ;; esac # ─── Detect package manager ─────────────────────────────────────────────────── if command -v apt-get &>/dev/null; then PKG_MGR="apt" elif command -v dnf &>/dev/null; then PKG_MGR="dnf" elif command -v yum &>/dev/null; then PKG_MGR="yum" else PKG_MGR="unknown" fi pkg_install() { case "$PKG_MGR" in apt) apt-get install -y -q "$@" ;; dnf) dnf install -y -q "$@" ;; yum) yum install -y -q "$@" ;; *) warn "Unknown package manager — please install manually: $*" ;; esac } # ─── Banner ─────────────────────────────────────────────────────────────────── echo -e "" echo -e " ${BOLD}${CYAN}OpsVault${RESET} — Automated database backup installer" echo -e " ${DIM}https://get.opsvault.dev${RESET}" echo -e "" # ─── Version ────────────────────────────────────────────────────────────────── RELEASE_URL="https://github.com/ArdaGnsrn/opsvault/releases/latest/download" BINARY_URL="${RELEASE_URL}/opsvault-linux-${ARCH_SUFFIX}" INSTALL_PATH="/usr/local/bin/opsvault" # ─── Step 1: Install OpsVault binary ───────────────────────────────────────── header "1 / 5 Install OpsVault" info "Downloading binary for linux/${ARCH_SUFFIX}..." TMP_BIN="$(mktemp)" if curl -fsSL "$BINARY_URL" -o "$TMP_BIN" /dev/null || echo 'unknown')" ok "$VERSION" # ─── Step 2: Optional — pg_dump ────────────────────────────────────────────── header "2 / 5 PostgreSQL client (pg_dump)" if command -v pg_dump &>/dev/null; then ok "pg_dump already installed: $(command -v pg_dump)" elif ask "Install pg_dump (postgresql-client)?"; then info "Installing postgresql-client..." case "$PKG_MGR" in apt) pkg_install postgresql-client ;; dnf) pkg_install postgresql ;; yum) pkg_install postgresql ;; *) warn "Install manually: postgresql-client" ;; esac command -v pg_dump &>/dev/null && ok "pg_dump installed" || warn "pg_dump not found after install — check manually" else warn "Skipped. Required if you back up PostgreSQL databases." fi # ─── Step 3: Optional — mysqldump ──────────────────────────────────────────── header "3 / 5 MySQL client (mysqldump)" if command -v mysqldump &>/dev/null; then ok "mysqldump already installed: $(command -v mysqldump)" elif ask "Install mysqldump (default-mysql-client)?"; then info "Installing mysql-client..." case "$PKG_MGR" in apt) pkg_install default-mysql-client ;; dnf) pkg_install mysql ;; yum) pkg_install mysql ;; *) warn "Install manually: default-mysql-client" ;; esac command -v mysqldump &>/dev/null && ok "mysqldump installed" || warn "mysqldump not found after install — check manually" else warn "Skipped. Required if you back up MySQL databases." fi # ─── Step 4: Optional — rclone ─────────────────────────────────────────────── header "4 / 5 rclone (remote storage)" if command -v rclone &>/dev/null; then ok "rclone already installed: $(command -v rclone)" elif ask "Install rclone (for remote storage uploads)?"; then info "Installing rclone..." if curl -fsSL https://rclone.org/install.sh | bash -s -- --quiet; then ok "rclone installed" else warn "rclone install failed — install manually: curl https://rclone.org/install.sh | sudo bash" fi else warn "Skipped. Required if you want to upload backups to remote storage." fi # ─── Step 5: Config + Service ──────────────────────────────────────────────── header "5 / 5 Configure & install service" CONFIG_PATH="/etc/opsvault/config.yaml" if [[ -f "$CONFIG_PATH" ]]; then ok "Config already exists: $CONFIG_PATH" else info "Creating default config at $CONFIG_PATH..." "$INSTALL_PATH" config init --config "$CONFIG_PATH" ok "Config created" fi if ask "Install OpsVault as a systemd service?"; then "$INSTALL_PATH" service install --config "$CONFIG_PATH" ok "Service installed and enabled" info "To start the service: systemctl start opsvault" else warn "Skipped. Run 'opsvault service install' later to set up the service." fi # ─── Setup shell completion ─────────────────────────────────────────────────── setup_completion() { local shell_name="$1" local comp_dir comp_file case "$shell_name" in bash) comp_dir="/etc/bash_completion.d" comp_file="$comp_dir/opsvault" if [[ -d "$comp_dir" ]]; then "$INSTALL_PATH" completion bash > "$comp_file" 2>/dev/null && ok "Bash completion installed ($comp_file)" fi ;; zsh) comp_dir="/usr/local/share/zsh/site-functions" comp_file="$comp_dir/_opsvault" mkdir -p "$comp_dir" "$INSTALL_PATH" completion zsh > "$comp_file" 2>/dev/null && ok "Zsh completion installed ($comp_file)" if ! grep -q "site-functions" ~/.zshrc 2>/dev/null; then { echo '' echo 'fpath=(/usr/local/share/zsh/site-functions $fpath)' echo 'autoload -U compinit && compinit' } >> ~/.zshrc ok "Added fpath entry to ~/.zshrc" fi ;; fish) comp_dir="/etc/fish/completions" comp_file="$comp_dir/opsvault.fish" mkdir -p "$comp_dir" "$INSTALL_PATH" completion fish > "$comp_file" 2>/dev/null && ok "Fish completion installed ($comp_file)" ;; esac } # Detect shells that are actually installed and set up completion for all of them DETECTED_SHELLS=() command -v bash &>/dev/null && DETECTED_SHELLS+=("bash") command -v zsh &>/dev/null && DETECTED_SHELLS+=("zsh") command -v fish &>/dev/null && DETECTED_SHELLS+=("fish") if [[ ${#DETECTED_SHELLS[@]} -gt 0 ]]; then info "Installing shell completions for: ${DETECTED_SHELLS[*]}" for sh in "${DETECTED_SHELLS[@]}"; do setup_completion "$sh" done fi # ─── Done ───────────────────────────────────────────────────────────────────── echo "" echo -e " ${BOLD}${GREEN}Installation complete!${RESET}" echo "" echo -e " ${BOLD}Next steps:${RESET}" echo -e " ${DIM}1.${RESET} Edit your config: ${CYAN}nano $CONFIG_PATH${RESET}" echo -e " ${DIM}2.${RESET} Validate the config: ${CYAN}opsvault config validate${RESET}" echo -e " ${DIM}3.${RESET} Test a backup: ${CYAN}opsvault backup run${RESET}" echo -e " ${DIM}4.${RESET} Start the service: ${CYAN}systemctl start opsvault${RESET}" echo -e " ${DIM}5.${RESET} After config changes: ${CYAN}opsvault reload${RESET}" echo "" echo -e " ${DIM}Docs: https://opsvault.dev/docs${RESET}" echo -e " ${DIM}GitHub: https://github.com/ArdaGnsrn/opsvault${RESET}" echo ""