Files

366 lines
9.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/../.." && pwd)"
profile=""
env_file="${repo_root}/.env.devprod"
compose_file=""
mode="auto"
occ_path=""
dry_run="0"
skip_finish="0"
bootstrap_now="0"
declare -a bootstrap_args=()
usage() {
cat <<USAGE
Usage: ./scripts/topology/apply-topology-profile.sh --profile <name> [options]
Apply an installation/repair profile selected by topology detection.
Profiles:
repo-devprod-bootstrap Uses repo Docker scripts (recreate + finish)
host-nextcloud-hybrid Host-Nextcloud adoption installer
Options:
--profile <name> Profile to apply (required)
--repo-root <path> Repository root (default: auto)
--env-file <path> Env file path (default: .env.devprod)
--compose-file <path> Compose file (auto-detected when omitted)
--mode auto|ssl|nossl Recreate mode (default: auto)
--occ-path <path> Host Nextcloud occ path (for host profile)
--dry-run Print commands without executing
--skip-finish Skip finish-initial-setup.sh after recreate
--bootstrap-now For host-nextcloud-hybrid, run install-host-nextcloud-hybrid.sh
--bootstrap-arg <arg> Extra argument passed through to the install script (repeatable)
-h, --help Show this help
USAGE
}
read_kv() {
local key="$1"
local file="$2"
local line
line="$(grep -m1 -E "^${key}=" "${file}" 2>/dev/null || true)"
if [[ -z "${line}" ]]; then
return 1
fi
printf '%s' "${line#*=}"
}
host_occ_cmd() {
if [[ -z "${occ_path:-}" || ! -f "${occ_path:-}" ]]; then
return 1
fi
if [[ -z "$(command -v php || true)" ]]; then
return 1
fi
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
if command -v sudo >/dev/null 2>&1; then
sudo -u www-data php "${occ_path}" "$@"
return $?
fi
if command -v runuser >/dev/null 2>&1; then
runuser -u www-data -- php "${occ_path}" "$@"
return $?
fi
fi
php "${occ_path}" "$@"
}
while [[ $# -gt 0 ]]; do
case "$1" in
--profile)
profile="${2:-}"
shift 2
;;
--profile=*)
profile="${1#*=}"
shift
;;
--repo-root)
repo_root="${2:-}"
shift 2
;;
--repo-root=*)
repo_root="${1#*=}"
shift
;;
--env-file)
env_file="${2:-}"
shift 2
;;
--env-file=*)
env_file="${1#*=}"
shift
;;
--compose-file)
compose_file="${2:-}"
shift 2
;;
--compose-file=*)
compose_file="${1#*=}"
shift
;;
--mode)
mode="${2:-}"
shift 2
;;
--mode=*)
mode="${1#*=}"
shift
;;
--occ-path)
occ_path="${2:-}"
shift 2
;;
--occ-path=*)
occ_path="${1#*=}"
shift
;;
--dry-run)
dry_run="1"
shift
;;
--skip-finish)
skip_finish="1"
shift
;;
--bootstrap-now)
bootstrap_now="1"
shift
;;
--bootstrap-arg)
if [[ -z "${2:-}" ]]; then
echo "Missing value for --bootstrap-arg"
exit 1
fi
bootstrap_args+=("${2}")
shift 2
;;
--bootstrap-arg=*)
bootstrap_args+=("${1#*=}")
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
if [[ -z "${profile}" ]]; then
echo "--profile is required."
usage
exit 1
fi
if [[ "${repo_root}" != /* ]]; then
repo_root="$(cd "${repo_root}" 2>/dev/null && pwd)"
fi
if [[ "${env_file}" != /* ]]; then
env_file="${repo_root}/${env_file}"
fi
if [[ -n "${compose_file}" && "${compose_file}" != /* ]]; then
compose_file="${repo_root}/${compose_file}"
fi
if [[ "${mode}" != "auto" && "${mode}" != "ssl" && "${mode}" != "nossl" ]]; then
echo "Invalid --mode value: ${mode}"
exit 1
fi
if [[ -z "${compose_file}" ]]; then
if [[ -f "${repo_root}/docker-compose.devprod.nossl.yml" ]]; then
compose_file="${repo_root}/docker-compose.devprod.nossl.yml"
elif [[ -f "${repo_root}/docker-compose.devprod.yml" ]]; then
compose_file="${repo_root}/docker-compose.devprod.yml"
else
compose_file="${repo_root}/docker-compose.devprod.nossl.yml"
fi
fi
run_cmd() {
if [[ "${dry_run}" == "1" ]]; then
printf '[dry-run] '
printf '%q ' "$@"
printf '\n'
return 0
fi
"$@"
}
detect_mode_from_compose_path() {
local input="${1:-}"
if [[ "${mode}" != "auto" ]]; then
printf '%s' "${mode}"
return 0
fi
if [[ "${input}" == *".nossl.yml" ]]; then
printf 'nossl'
else
printf 'ssl'
fi
}
apply_repo_devprod_bootstrap() {
if [[ ! -f "${env_file}" ]]; then
echo "Missing env file: ${env_file}"
exit 1
fi
if [[ ! -f "${compose_file}" ]]; then
echo "Missing compose file: ${compose_file}"
exit 1
fi
local effective_mode
effective_mode="$(detect_mode_from_compose_path "${compose_file}")"
local recreate_cmd=("${repo_root}/recreate-devprod.sh")
if [[ "${effective_mode}" == "nossl" ]]; then
recreate_cmd+=("--nossl")
else
recreate_cmd+=("--ssl")
fi
local external_auth_base_url
external_auth_base_url="$(read_kv "QORTAL_EXTERNAL_AUTH_BASE_URL" "${env_file}" || true)"
if [[ "${external_auth_base_url}" == *"external_auth"* ]]; then
recreate_cmd+=("--extauth")
fi
echo "Applying profile: repo-devprod-bootstrap"
echo "Repo root: ${repo_root}"
echo "Env file: ${env_file}"
echo "Compose file: ${compose_file}"
if [[ -x "${repo_root}/scripts/ensure-env-keys.sh" ]]; then
run_cmd "${repo_root}/scripts/ensure-env-keys.sh" --env-file "${env_file}" --template-file "${repo_root}/.env.example"
fi
run_cmd "${recreate_cmd[@]}"
if [[ "${skip_finish}" == "1" ]]; then
echo "Skipping finish-initial-setup by request (--skip-finish)."
return 0
fi
local finish_cmd=("${repo_root}/scripts/finish-initial-setup.sh")
if [[ "${effective_mode}" == "nossl" ]]; then
finish_cmd+=("--nossl")
else
finish_cmd+=("--ssl")
fi
finish_cmd+=("--env-file" "${env_file}")
run_cmd "${finish_cmd[@]}"
}
apply_host_nextcloud_hybrid() {
local nc_url=""
local dbtype=""
local docker_reachable="0"
local apps_list=""
local has_spreed="0"
local has_richdocuments="0"
local has_notify_push="0"
local install_script="${repo_root}/scripts/install-host-nextcloud-hybrid.sh"
echo "Applying profile: host-nextcloud-hybrid"
if [[ -z "${occ_path}" ]]; then
occ_path="/var/www/nextcloud/occ"
fi
echo "Detected/selected OCC path: ${occ_path}"
echo
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
docker_reachable="1"
fi
nc_url="$(host_occ_cmd config:system:get overwrite.cli.url 2>/dev/null | tr -d '\r' || true)"
dbtype="$(host_occ_cmd config:system:get dbtype 2>/dev/null | tr -d '\r' || true)"
apps_list="$(host_occ_cmd app:list 2>/dev/null || true)"
if grep -qE '^[[:space:]]+- spreed([[:space:]:]|$)' <<< "${apps_list}"; then
has_spreed="1"
fi
if grep -qE '^[[:space:]]+- richdocuments([[:space:]:]|$)' <<< "${apps_list}"; then
has_richdocuments="1"
fi
if grep -qE '^[[:space:]]+- notify_push([[:space:]:]|$)' <<< "${apps_list}"; then
has_notify_push="1"
fi
echo "Observed host state:"
echo " nextcloud_url: ${nc_url:-<unknown>}"
echo " dbtype: ${dbtype:-<unknown>}"
echo " docker_daemon_reachable: ${docker_reachable}"
echo " spreed_enabled: ${has_spreed}"
echo " richdocuments_enabled: ${has_richdocuments}"
echo " notify_push_enabled: ${has_notify_push}"
echo
echo "Recommended adoption path for this topology:"
echo "1) Keep host Nextcloud, Apache, DB, and Redis in place."
if [[ "${has_spreed}" == "1" || "${has_richdocuments}" == "1" || "${has_notify_push}" == "1" ]]; then
echo "2) Reuse existing host-integrated Nextcloud apps and sidecars where present:"
[[ "${has_notify_push}" == "1" ]] && echo " - notify_push already enabled"
[[ "${has_richdocuments}" == "1" ]] && echo " - richdocuments already enabled"
[[ "${has_spreed}" == "1" ]] && echo " - spreed already enabled"
echo " Prefer adding NuQloud auth/broker pieces around them before replacing any Talk/Office setup."
else
echo "2) No existing Talk/Office app enablement detected; this host may be a cleaner candidate for fuller NuQloud sidecar adoption."
fi
echo "3) Provision Docker sidecars for the missing NuQloud services:"
echo " - broker"
echo " - broker_db"
echo " - external_auth"
echo " - optional qortal_node"
echo "4) Apply the host-hybrid installer:"
echo " ./scripts/install-host-nextcloud-hybrid.sh --occ-path ${occ_path}"
echo "5) Use the lifecycle helpers for later operations:"
echo " ./scripts/update-host-nextcloud-hybrid.sh"
echo " ./scripts/restart-host-nextcloud-hybrid.sh"
echo " ./scripts/stop-host-nextcloud-hybrid.sh"
echo "6) Re-run topology detection after changes:"
echo " ./scripts/topology/discover-cloud-topology.sh --text --repo-root ${repo_root}"
if [[ "${bootstrap_now}" != "1" ]]; then
return 0
fi
if [[ ! -x "${install_script}" ]]; then
echo
echo "Cannot run install: missing executable ${install_script}"
exit 1
fi
echo
echo "Running host installer now (--bootstrap-now)..."
local bootstrap_cmd=("${install_script}" "--repo-root" "${repo_root}" "--occ-path" "${occ_path}")
if [[ "${#bootstrap_args[@]}" -gt 0 ]]; then
bootstrap_cmd+=("${bootstrap_args[@]}")
fi
run_cmd "${bootstrap_cmd[@]}"
}
case "${profile}" in
repo-devprod-bootstrap)
apply_repo_devprod_bootstrap
;;
host-nextcloud-hybrid)
apply_host_nextcloud_hybrid
;;
*)
echo "Unknown profile: ${profile}"
usage
exit 1
;;
esac