Files

396 lines
11 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)"
action=""
env_file=".env.devprod"
compose_file=""
mode="auto"
topology="auto"
broker_dir="/opt/qortal-broker"
broker_dir_explicit="0"
env_file_explicit="0"
compose_file_explicit="0"
dry_run="0"
force_refresh_contexts="0"
finish_host_nextcloud_upgrade="0"
rebuild_qortal="0"
full_rebuild="0"
extauth="auto"
qortal_branch=""
qortal_branch_next="0"
usage() {
cat <<USAGE
Usage: ./scripts/nuqloud-lifecycle.sh <action> [options]
Smart lifecycle wrapper for NuQloud installs. Detects repo Docker devprod vs
host Nextcloud hybrid installs and delegates to the existing lifecycle scripts.
Actions:
detect Print detected topology
start Start/recreate services for the detected topology
update Refresh code/config and recreate managed services
rebuild Rebuild containers where supported, then recreate services
stop Stop managed services without deleting volumes
Options:
--env-file <path> Repo devprod env or host broker env hint
--compose-file <path> Compose file hint for detection/host start/stop
--mode auto|ssl|nossl Repo devprod mode (default: auto)
--topology auto|repo-devprod|host-hybrid
Override topology detection
--broker-dir <path> Host-hybrid broker dir (default: /opt/qortal-broker)
--extauth|--no-extauth Repo devprod external-auth profile hint
--qortal Include/rebuild qortal_node where supported
--qortal-branch <branch> Rebuild Qortal Core from a specific branch
--full Full repo devprod rebuild where supported
--force-refresh-contexts Allow dirty git context refreshes in host update
--finish-host-nextcloud-upgrade Permit host Nextcloud occ upgrade repair flow
--dry-run Print the delegated command only
-h, --help Show this help
USAGE
}
resolve_path() {
local raw="${1:-}"
if [[ -z "${raw}" ]]; then
printf '%s' ""
elif [[ "${raw}" == /* ]]; then
printf '%s' "${raw}"
else
printf '%s' "${repo_root}/${raw}"
fi
}
print_cmd() {
printf 'Would run:'
printf ' %q' "$@"
printf '\n'
}
run_or_print() {
if [[ "${dry_run}" == "1" ]]; then
print_cmd "$@"
return 0
fi
"$@"
}
json_string_value() {
local key="$1"
sed -nE "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*/\\1/p" | head -n1
}
infer_mode_from_compose() {
local file="${1:-}"
if [[ "${mode}" != "auto" ]]; then
printf '%s' "${mode}"
elif [[ "${file}" == *".nossl."* || "${file}" == *"nossl"* ]]; then
printf '%s' "nossl"
else
printf '%s' "ssl"
fi
}
detect_topology_json() {
local args=(--repo-root "${repo_root}" --env-file "${resolved_env_file}" --json)
if [[ -n "${resolved_compose_file}" ]]; then
args+=(--compose-file "${resolved_compose_file}")
fi
"${script_dir}/discover-cloud-topology.sh" "${args[@]}"
}
detect_topology_id() {
local json id
json="$(detect_topology_json 2>/dev/null || true)"
id="$(printf '%s\n' "${json}" | json_string_value "id")"
if [[ -z "${id}" && -f "${resolved_broker_dir}/.env" && -f "${resolved_broker_dir}/docker-compose.yml" ]]; then
id="host_nextcloud_plus_docker"
fi
if [[ -z "${id}" && -f "${resolved_env_file}" ]]; then
id="docker_compose_generic"
fi
printf '%s' "${id:-unknown}"
}
repo_devprod_cmd() {
local delegated_mode="$1"
local cmd=()
case "${action}" in
start)
cmd=("${repo_root}/start-devprod.sh" "--${delegated_mode}")
if [[ "${extauth}" == "1" ]]; then
cmd+=(--extauth)
elif [[ "${extauth}" == "0" ]]; then
cmd+=(--no-extauth)
fi
;;
update|rebuild)
cmd=("${repo_root}/recreate-devprod.sh" "--${delegated_mode}")
[[ "${extauth}" == "1" ]] && cmd+=(--extauth)
[[ "${rebuild_qortal}" == "1" ]] && cmd+=(--qortal)
[[ "${full_rebuild}" == "1" ]] && cmd+=(--full)
[[ -n "${qortal_branch}" ]] && cmd+=(--qortal-branch "${qortal_branch}")
;;
stop)
cmd=("${repo_root}/stop-devprod.sh" "--mode" "${delegated_mode}")
;;
*)
echo "Unsupported repo devprod action: ${action}" >&2
exit 1
;;
esac
run_or_print "${cmd[@]}"
}
host_hybrid_cmd() {
local host_env_file="${resolved_broker_dir}/.env"
local host_compose_file="${resolved_broker_dir}/docker-compose.yml"
local cmd=()
if [[ -n "${compose_file}" ]]; then
host_compose_file="${resolved_compose_file}"
fi
if [[ "${env_file}" != ".env.devprod" || -f "${resolved_env_file}" && "${resolved_env_file}" == "${resolved_broker_dir}/.env" ]]; then
host_env_file="${resolved_env_file}"
fi
case "${action}" in
start)
cmd=("${script_dir}/restart-host-nextcloud-hybrid.sh" --broker-dir "${resolved_broker_dir}" --env-file "${host_env_file}" --compose-file "${host_compose_file}")
[[ "${rebuild_qortal}" == "1" ]] && cmd+=(--qortal)
;;
update|rebuild)
cmd=("${script_dir}/update-host-nextcloud-hybrid.sh" --broker-dir "${resolved_broker_dir}")
[[ "${force_refresh_contexts}" == "1" ]] && cmd+=(--force-refresh-contexts)
[[ "${finish_host_nextcloud_upgrade}" == "1" ]] && cmd+=(--finish-host-nextcloud-upgrade)
;;
stop)
cmd=("${script_dir}/stop-host-nextcloud-hybrid.sh" --broker-dir "${resolved_broker_dir}" --env-file "${host_env_file}" --compose-file "${host_compose_file}")
;;
*)
echo "Unsupported host-hybrid action: ${action}" >&2
exit 1
;;
esac
run_or_print "${cmd[@]}"
}
while [[ $# -gt 0 ]]; do
if [[ "${qortal_branch_next}" == "1" ]]; then
qortal_branch="${1:-}"
qortal_branch_next="0"
shift
continue
fi
case "$1" in
detect|start|update|rebuild|stop)
action="$1"
shift
;;
--env-file)
env_file="${2:-}"
env_file_explicit="1"
shift 2
;;
--env-file=*)
env_file="${1#*=}"
env_file_explicit="1"
shift
;;
--compose-file)
compose_file="${2:-}"
compose_file_explicit="1"
shift 2
;;
--compose-file=*)
compose_file="${1#*=}"
compose_file_explicit="1"
shift
;;
--mode)
mode="${2:-}"
shift 2
;;
--mode=*)
mode="${1#*=}"
shift
;;
--topology)
topology="${2:-}"
shift 2
;;
--topology=*)
topology="${1#*=}"
shift
;;
--ssl)
mode="ssl"
shift
;;
--nossl)
mode="nossl"
shift
;;
--broker-dir)
broker_dir="${2:-}"
broker_dir_explicit="1"
shift 2
;;
--broker-dir=*)
broker_dir="${1#*=}"
broker_dir_explicit="1"
shift
;;
--extauth)
extauth="1"
shift
;;
--no-extauth)
extauth="0"
shift
;;
--qortal)
rebuild_qortal="1"
shift
;;
--qortal=*)
rebuild_qortal="1"
qortal_branch="${1#*=}"
shift
;;
--qortal-branch)
rebuild_qortal="1"
qortal_branch_next="1"
shift
;;
--qortal-branch=*)
rebuild_qortal="1"
qortal_branch="${1#*=}"
shift
;;
--full)
full_rebuild="1"
rebuild_qortal="1"
shift
;;
--force-refresh-contexts)
force_refresh_contexts="1"
shift
;;
--finish-host-nextcloud-upgrade)
finish_host_nextcloud_upgrade="1"
shift
;;
--dry-run)
dry_run="1"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
esac
done
if [[ -z "${action}" ]]; then
usage
exit 1
fi
if [[ "${qortal_branch_next}" == "1" ]]; then
echo "--qortal-branch requires a value" >&2
exit 1
fi
if [[ "${mode}" != "auto" && "${mode}" != "ssl" && "${mode}" != "nossl" ]]; then
echo "Invalid mode: ${mode}" >&2
exit 1
fi
if [[ "${topology}" != "auto" && "${topology}" != "repo-devprod" && "${topology}" != "host-hybrid" ]]; then
echo "Invalid topology: ${topology}" >&2
exit 1
fi
if [[ -n "${qortal_branch}" ]] && ! git check-ref-format --branch "${qortal_branch}" >/dev/null 2>&1; then
echo "Invalid Qortal branch name: ${qortal_branch}" >&2
exit 1
fi
resolved_env_file="$(resolve_path "${env_file}")"
resolved_compose_file=""
if [[ -n "${compose_file}" ]]; then
resolved_compose_file="$(resolve_path "${compose_file}")"
elif [[ -f "${repo_root}/docker-compose.devprod.nossl.yml" ]]; then
resolved_compose_file="${repo_root}/docker-compose.devprod.nossl.yml"
elif [[ -f "${repo_root}/docker-compose.devprod.yml" ]]; then
resolved_compose_file="${repo_root}/docker-compose.devprod.yml"
fi
resolved_broker_dir="$(resolve_path "${broker_dir}")"
if [[ "${broker_dir_explicit}" == "0" && "${env_file_explicit}" == "1" && "$(basename "${resolved_env_file}")" == ".env" ]]; then
resolved_broker_dir="$(dirname "${resolved_env_file}")"
elif [[ "${broker_dir_explicit}" == "0" && "${compose_file_explicit}" == "1" && "$(basename "${resolved_compose_file}")" == "docker-compose.yml" ]]; then
resolved_broker_dir="$(dirname "${resolved_compose_file}")"
fi
if [[ "${action}" == "detect" ]]; then
args=(--repo-root "${repo_root}" --env-file "${resolved_env_file}" --text)
[[ -n "${resolved_compose_file}" ]] && args+=(--compose-file "${resolved_compose_file}")
run_or_print "${script_dir}/discover-cloud-topology.sh" "${args[@]}"
exit 0
fi
delegated_mode="$(infer_mode_from_compose "${resolved_compose_file}")"
case "${topology}" in
repo-devprod)
repo_devprod_cmd "${delegated_mode}"
exit 0
;;
host-hybrid)
host_hybrid_cmd
exit 0
;;
esac
if [[ "${broker_dir_explicit}" == "1" && -f "${resolved_broker_dir}/.env" && -f "${resolved_broker_dir}/docker-compose.yml" ]]; then
topology_id="host_nextcloud_plus_docker"
elif [[ "${env_file_explicit}" == "1" && "${resolved_env_file}" == "${resolved_broker_dir}/.env" ]]; then
topology_id="host_nextcloud_plus_docker"
elif [[ "${compose_file_explicit}" == "1" && "${resolved_compose_file}" == "${resolved_broker_dir}/docker-compose.yml" ]]; then
topology_id="host_nextcloud_plus_docker"
else
topology_id="$(detect_topology_id)"
fi
case "${topology_id}" in
host_nextcloud|host_nextcloud_plus_docker)
host_hybrid_cmd
;;
repo_docker_devprod|docker_compose_generic)
repo_devprod_cmd "${delegated_mode}"
;;
*)
if [[ -f "${resolved_broker_dir}/.env" && -f "${resolved_broker_dir}/docker-compose.yml" ]]; then
host_hybrid_cmd
elif [[ -f "${resolved_env_file}" ]]; then
repo_devprod_cmd "${delegated_mode}"
else
echo "Could not detect a supported NuQloud install." >&2
echo "Provide --env-file/--compose-file for repo devprod or --broker-dir for host-hybrid." >&2
exit 1
fi
;;
esac