#!/usr/bin/env bash set -euo pipefail # prole.sh - Unified launcher for Prole infrastructure and installer ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "${ROOT_DIR}" # Defaults VERBOSE=0 DEBUG=0 LOG=0 COVERAGE=0 SILENT=0 RESET=0 EXTRA_ARGS=() usage() { echo "Usage: $0 [options] [command] [command-options]" echo echo "Options:" echo " -l, --log Create a datestamped logfile in prole/logs including all stdout/stderr" echo " -v, --verbose Pass a verbose switch to all functions called by ansible and install.py" echo " -d, --debug Enable a debug logfile in prole/logs with high verbosity from install.py" echo " -c, --coverage Create a coverage report for the run of install.py" echo " -s, --silent Run unattended install (passes -S to install.py)" echo " -r, --reset Perform a reset before action (site/deploy/install)" echo echo "Commands:" echo " install Run the ncurses-based terminal installer" echo " ansible Run ansible-playbook via ansible.sh" echo " site Shortcut for running the site deployment" echo " reset Shortcut for k3s factory reset (full clear)" echo " deploy Full end-to-end: site run, and silent install (reset optional)" echo echo "Any other arguments are passed directly to ansible.sh" } # Parse global options while [[ $# -gt 0 ]]; do case "$1" in -l|--log|--logs) LOG=1; shift ;; -v|--verbose) VERBOSE=1; shift ;; -d|--debug) DEBUG=1; shift ;; -c|--coverage) COVERAGE=1; shift ;; -s|--silent) SILENT=1; shift ;; -r|--reset) RESET=1; shift ;; -h|--help) usage; exit 0 ;; install|ansible|site|reset|deploy) break ;; *) EXTRA_ARGS+=("$1"); shift ;; esac done if [[ $# -eq 0 && ${#EXTRA_ARGS[@]} -eq 0 ]]; then usage exit 1 fi CMD="${1:-ansible}" [[ $# -gt 0 ]] && shift # Prepare command arguments CMD_ARGS=() if [[ "${VERBOSE}" -eq 1 ]]; then CMD_ARGS+=("--verbose") fi # We separate arguments that are only for install.py INSTALL_ONLY_ARGS=() if [[ "${DEBUG}" -eq 1 ]]; then INSTALL_ONLY_ARGS+=("--debug") fi if [[ "${SILENT}" -eq 1 ]]; then if [[ ! -f "${ROOT_DIR}/conf/prole.cfg" ]]; then echo "Error: --silent requires ${ROOT_DIR}/conf/prole.cfg" >&2 exit 1 fi INSTALL_ONLY_ARGS+=("-S") fi if [[ "${RESET}" -eq 1 ]]; then INSTALL_ONLY_ARGS+=("--reset") fi CMD_ARGS+=("${EXTRA_ARGS[@]}" "$@") # Helper for logging run_with_log() { local logfile="$1" shift mkdir -p "$(dirname "${logfile}")" echo "Logging to ${logfile}" "$@" 2>&1 | tee "${logfile}" } # Run command TS="$(date +%Y%m%d-%H%M%S)" # Prep for Ansible truthfulness if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then # We use the same vault pass logic as ansible.sh if available export PYTHONPATH="${PYTHONPATH:-}:${ROOT_DIR}" python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi case "${CMD}" in install) LOGFILE="${ROOT_DIR}/logs/install-${TS}.log" PY_CMD=("python3") if [[ "${COVERAGE}" -eq 1 ]]; then PY_CMD=("coverage" "run" "--source=installer") fi PY_CMD+=("${ROOT_DIR}/install.py" "--no-gui") if [[ "${LOG}" -eq 1 ]]; then run_with_log "${LOGFILE}" "${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}" elif [[ "${COVERAGE}" -eq 1 ]]; then "${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}" else exec "${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}" fi if [[ "${COVERAGE}" -eq 1 ]]; then echo "Generating coverage report..." coverage report -m coverage html echo "HTML report generated in ${ROOT_DIR}/htmlcov/index.html" fi ;; ansible) LOGFILE="${ROOT_DIR}/logs/ansible-${TS}.log" if [[ "${LOG}" -eq 1 ]]; then run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" "${CMD_ARGS[@]}" else "${ROOT_DIR}/ansible.sh" "${CMD_ARGS[@]}" fi # SYNC AFTER ANSIBLE if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi ;; site) LOGFILE="${ROOT_DIR}/logs/site-${TS}.log" ( set -e if [[ "${RESET}" -eq 1 ]]; then echo "Resetting k3s before site run..." "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}" fi if [[ "${LOG}" -eq 1 ]]; then run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}" else "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}" fi # SYNC AFTER SITE if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi ) ;; reset) LOGFILE="${ROOT_DIR}/logs/reset-${TS}.log" if [[ "${LOG}" -eq 1 ]]; then run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}" else "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}" fi # SYNC AFTER RESET (to clear token) if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi ;; deploy) LOGFILE="${ROOT_DIR}/logs/deploy-${TS}.log" echo "Starting full end-to-end deployment..." ( set -e if [[ "${RESET}" -eq 1 ]]; then echo "Step 1/3: Resetting k3s (full)..." "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}" else echo "Step 1/3: Skipping k3s reset (use -r to force reset)" fi echo "Step 2/3: Running site deployment..." "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}" if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi echo "Step 3/3: Running silent installer..." # INSTALL_ONLY_ARGS includes -S and --reset if flags were set python3 "${ROOT_DIR}/install.py" "--no-gui" "-c" "${ROOT_DIR}/conf/prole.cfg" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}" echo "Deployment completed successfully!" ) | tee "${LOGFILE}" ;; *) # Default to ansible if command not matched LOGFILE="${ROOT_DIR}/logs/${CMD}-${TS}.log" if [[ "${LOG}" -eq 1 ]]; then run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" "${CMD}" "${CMD_ARGS[@]}" else "${ROOT_DIR}/ansible.sh" "${CMD}" "${CMD_ARGS[@]}" fi # SYNC AFTER ANSIBLE if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true fi ;; esac