#!/usr/bin/env bash set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd "${script_dir}/.." && pwd)" env_file="${repo_root}/.env.devprod" mode="nossl" compose_file="" skip_finish="0" skip_auth_repair="0" switch_service_user="0" legacy_service_user="sovereign_oidc_service" nuqloud_service_user="nuqloud_oidc_service" usage() { cat < Env file to migrate (default: .env.devprod) --mode ssl|nossl Compose mode for runtime actions (default: nossl) --compose-file Explicit compose file for runtime actions --switch-service-user Set NEXTCLOUD_SERVICE_USER to nuqloud_oidc_service --legacy-service-user Legacy service username (default: sovereign_oidc_service) --nuqloud-service-user New NuQloud service username (default: nuqloud_oidc_service) --skip-finish Skip scripts/finish-initial-setup.sh runtime sync --skip-auth-repair Skip scripts/ensure-nextcloud-service-auth.sh --repair -h, --help Show this help USAGE } log() { echo "[nuqloud-migrate] $*" } trim() { local value="${1:-}" value="$(echo "${value}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" echo "${value}" } read_kv() { local key="$1" local line line="$(grep -m1 -E "^${key}=" "${env_file}" || true)" if [[ -z "${line}" ]]; then return 1 fi echo "${line#*=}" } set_kv() { local key="$1" local value="$2" local esc esc="${value//\\/\\\\}" esc="${esc//&/\\&}" esc="${esc//|/\\|}" if grep -q -E "^${key}=" "${env_file}"; then sed -i -E "s|^${key}=.*|${key}=${esc}|" "${env_file}" else echo "${key}=${value}" >> "${env_file}" fi } seed_alias_if_missing() { local dest_key="$1" shift local current current="$(trim "$(read_kv "${dest_key}" || true)")" if [[ -n "${current}" ]]; then return 0 fi local source_key source_value for source_key in "$@"; do source_value="$(trim "$(read_kv "${source_key}" || true)")" if [[ -n "${source_value}" ]]; then set_kv "${dest_key}" "${source_value}" log "Seeded ${dest_key} from ${source_key}." return 0 fi done } while [[ $# -gt 0 ]]; do case "$1" in --env-file) env_file="${2:-}" shift 2 ;; --env-file=*) env_file="${1#*=}" shift ;; --mode) mode="${2:-}" shift 2 ;; --mode=*) mode="${1#*=}" shift ;; --compose-file) compose_file="${2:-}" shift 2 ;; --compose-file=*) compose_file="${1#*=}" shift ;; --switch-service-user) switch_service_user="1" shift ;; --legacy-service-user) legacy_service_user="${2:-}" shift 2 ;; --legacy-service-user=*) legacy_service_user="${1#*=}" shift ;; --nuqloud-service-user) nuqloud_service_user="${2:-}" shift 2 ;; --nuqloud-service-user=*) nuqloud_service_user="${1#*=}" shift ;; --skip-finish) skip_finish="1" shift ;; --skip-auth-repair) skip_auth_repair="1" shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done if [[ "${env_file}" != /* ]]; then env_file="${repo_root}/${env_file}" fi if [[ ! -f "${env_file}" ]]; then echo "Missing env file: ${env_file}" exit 1 fi if [[ "${mode}" != "ssl" && "${mode}" != "nossl" ]]; then echo "Invalid mode: ${mode} (expected ssl or nossl)" exit 1 fi if [[ -z "${compose_file}" ]]; then if [[ "${mode}" == "ssl" ]]; then compose_file="${repo_root}/docker-compose.devprod.yml" else compose_file="${repo_root}/docker-compose.devprod.nossl.yml" fi elif [[ "${compose_file}" != /* ]]; then compose_file="${repo_root}/${compose_file}" fi run_runtime_actions="1" if [[ "${skip_finish}" == "1" && "${skip_auth_repair}" == "1" ]]; then run_runtime_actions="0" fi if [[ "${run_runtime_actions}" == "1" && ! -f "${compose_file}" ]]; then echo "Missing compose file: ${compose_file}" exit 1 fi if [[ "${legacy_service_user}" == "${nuqloud_service_user}" ]]; then echo "legacy/new service usernames must differ." exit 1 fi backup_file="${env_file}.nuqloud-migrate.$(date +%Y%m%d-%H%M%S).bak" cp "${env_file}" "${backup_file}" log "Backed up env file to ${backup_file}" seed_alias_if_missing "NUQLOUD_MODE" "SOVEREIGN_MODE" "SC_MODE" seed_alias_if_missing "NUQLOUD_BILLING_ENABLED" "SOVEREIGN_BILLING_ENABLED" "SC_BILLING_ENABLED" seed_alias_if_missing "NUQLOUD_BILLING_MANAGED_BY_CHD" "SOVEREIGN_BILLING_MANAGED_BY_CHD" "SC_BILLING_MANAGED_BY_CHD" seed_alias_if_missing "NUQLOUD_BILLING_PROVIDER" "SOVEREIGN_BILLING_PROVIDER" "SC_BILLING_PROVIDER" seed_alias_if_missing "NUQLOUD_BILLING_SYNC_MODE" "SOVEREIGN_BILLING_SYNC_MODE" "SC_BILLING_SYNC_MODE" seed_alias_if_missing "NUQLOUD_CHD_BASE_URL" "SOVEREIGN_CHD_BASE_URL" "SC_CHD_BASE_URL" seed_alias_if_missing "NUQLOUD_CHD_SYNC_PATH" "SOVEREIGN_CHD_SYNC_PATH" "SC_CHD_SYNC_PATH" seed_alias_if_missing "NUQLOUD_CHD_REGISTER_PATH" "SOVEREIGN_CHD_REGISTER_PATH" "SC_CHD_REGISTER_PATH" seed_alias_if_missing "NUQLOUD_CHD_CHECKOUT_PATH" "SOVEREIGN_CHD_CHECKOUT_PATH" "SC_CHD_CHECKOUT_PATH" seed_alias_if_missing "NUQLOUD_CHD_CATALOG_PATH" "SOVEREIGN_CHD_CATALOG_PATH" "SC_CHD_CATALOG_PATH" seed_alias_if_missing "NUQLOUD_CHD_CATALOG_CATEGORY_ID" "SOVEREIGN_CHD_CATALOG_CATEGORY_ID" "SC_CHD_CATALOG_CATEGORY_ID" seed_alias_if_missing "NUQLOUD_CHD_REGISTRATION_TOKEN" "SOVEREIGN_CHD_REGISTRATION_TOKEN" "SC_CHD_REGISTRATION_TOKEN" seed_alias_if_missing "NUQLOUD_CHD_INSTANCE_ID" "SOVEREIGN_CHD_INSTANCE_ID" "SC_CHD_INSTANCE_ID" seed_alias_if_missing "NUQLOUD_CHD_INSTANCE_TOKEN" "SOVEREIGN_CHD_INSTANCE_TOKEN" "SC_CHD_INSTANCE_TOKEN" seed_alias_if_missing "NUQLOUD_CHD_INSTANCE_SECRET" "SOVEREIGN_CHD_INSTANCE_SECRET" "SC_CHD_INSTANCE_SECRET" seed_alias_if_missing "NUQLOUD_BILLING_CONTACT_NAME" "SOVEREIGN_BILLING_CONTACT_NAME" "SC_BILLING_CONTACT_NAME" seed_alias_if_missing "NUQLOUD_BILLING_CONTACT_EMAIL" "SOVEREIGN_BILLING_CONTACT_EMAIL" "SC_BILLING_CONTACT_EMAIL" seed_alias_if_missing "NUQLOUD_BILLING_COMPANY_NAME" "SOVEREIGN_BILLING_COMPANY_NAME" "SC_BILLING_COMPANY_NAME" seed_alias_if_missing "NUQLOUD_BILLING_PHONE" "SOVEREIGN_BILLING_PHONE" "SC_BILLING_PHONE" seed_alias_if_missing "NUQLOUD_BILLING_ADDRESS1" "SOVEREIGN_BILLING_ADDRESS1" "SC_BILLING_ADDRESS1" seed_alias_if_missing "NUQLOUD_BILLING_ADDRESS2" "SOVEREIGN_BILLING_ADDRESS2" "SC_BILLING_ADDRESS2" seed_alias_if_missing "NUQLOUD_BILLING_CITY" "SOVEREIGN_BILLING_CITY" "SC_BILLING_CITY" seed_alias_if_missing "NUQLOUD_BILLING_STATE" "SOVEREIGN_BILLING_STATE" "SC_BILLING_STATE" seed_alias_if_missing "NUQLOUD_BILLING_POSTAL_CODE" "SOVEREIGN_BILLING_POSTAL_CODE" "SC_BILLING_POSTAL_CODE" seed_alias_if_missing "NUQLOUD_BILLING_COUNTRY" "SOVEREIGN_BILLING_COUNTRY" "SC_BILLING_COUNTRY" seed_alias_if_missing "NUQLOUD_MSP_MODE_ENABLED" "MSP_MODE_ENABLED" "SC_MSP_MODE_ENABLED" seed_alias_if_missing "NUQLOUD_MSP_ADMIN_GROUP" "MSP_ADMIN_GROUP" "SC_MSP_ADMIN_GROUP" service_user_before="$(trim "$(read_kv "NEXTCLOUD_SERVICE_USER" || true)")" if [[ -z "${service_user_before}" ]]; then service_user_before="${legacy_service_user}" set_kv "NEXTCLOUD_SERVICE_USER" "${service_user_before}" log "NEXTCLOUD_SERVICE_USER was empty; set to legacy-compatible default '${service_user_before}'." fi if [[ "${switch_service_user}" == "1" ]]; then if [[ "${service_user_before}" != "${nuqloud_service_user}" ]]; then set_kv "NEXTCLOUD_SERVICE_USER" "${nuqloud_service_user}" log "Switched NEXTCLOUD_SERVICE_USER from '${service_user_before}' to '${nuqloud_service_user}'." else log "NEXTCLOUD_SERVICE_USER is already '${nuqloud_service_user}'." fi else log "Keeping NEXTCLOUD_SERVICE_USER='${service_user_before}'." fi if [[ "${skip_finish}" != "1" ]]; then if [[ ! -x "${repo_root}/scripts/finish-initial-setup.sh" ]]; then echo "Missing executable: scripts/finish-initial-setup.sh" exit 1 fi log "Running finish-initial-setup for runtime branding sync..." "${repo_root}/scripts/finish-initial-setup.sh" \ --mode "${mode}" \ --env-file "${env_file}" \ --skip-register \ --skip-api-key fi if [[ "${switch_service_user}" == "1" && "${skip_finish}" == "1" && "${skip_auth_repair}" != "1" ]]; then log "Recreating broker service so runtime env picks up NEXTCLOUD_SERVICE_USER change..." docker compose -f "${compose_file}" --env-file "${env_file}" up -d --force-recreate broker fi if [[ "${skip_auth_repair}" != "1" ]]; then if [[ ! -x "${repo_root}/scripts/ensure-nextcloud-service-auth.sh" ]]; then echo "Missing executable: scripts/ensure-nextcloud-service-auth.sh" exit 1 fi log "Validating and repairing broker service auth (supports legacy or NuQloud username)..." "${repo_root}/scripts/ensure-nextcloud-service-auth.sh" \ --compose-file "${compose_file}" \ --env-file "${env_file}" \ --repair fi log "Migration complete." log "Env file: ${env_file}" log "Compose file used for runtime actions: ${compose_file}"