405 lines
11 KiB
Bash
Executable File
405 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)"
|
|
source "${script_dir}/lib-compose-project.sh"
|
|
|
|
compose_file="${repo_root}/docker-compose.devprod.yml"
|
|
env_file="${repo_root}/.env.devprod"
|
|
skip_indices="0"
|
|
skip_bundle="0"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./scripts/ensure-nextcloud-default-app-bundle.sh [options]
|
|
|
|
Ensures optional Nextcloud default apps are installed/enabled and applies the
|
|
configured default dashboard/apps order. Also runs db:add-missing-indices unless
|
|
explicitly skipped.
|
|
|
|
Options:
|
|
--compose-file <path> Compose file to use (default: docker-compose.devprod.yml)
|
|
--env-file <path> Env file to use (default: .env.devprod)
|
|
--skip-indices Do not run occ db:add-missing-indices
|
|
--skip-bundle Do not install/enable the default app bundle
|
|
-h, --help Show this help
|
|
USAGE
|
|
}
|
|
|
|
read_env_value() {
|
|
local key="$1"
|
|
local default_value="${2:-}"
|
|
local value=""
|
|
if command -v rg >/dev/null 2>&1; then
|
|
value="$(rg -m1 "^${key}=" "${env_file}" | cut -d= -f2- || true)"
|
|
else
|
|
value="$(grep -m1 -E "^${key}=" "${env_file}" | cut -d= -f2- || true)"
|
|
fi
|
|
if [[ -z "${value}" ]]; then
|
|
echo "${default_value}"
|
|
return 0
|
|
fi
|
|
if [[ "${value}" =~ ^\".*\"$ ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
elif [[ "${value}" =~ ^\'.*\'$ ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
fi
|
|
echo "${value}"
|
|
}
|
|
|
|
normalize_bool_01() {
|
|
local raw="${1:-}"
|
|
case "$(echo "${raw}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" in
|
|
1|true|yes|y|on)
|
|
echo "1"
|
|
;;
|
|
0|false|no|n|off)
|
|
echo "0"
|
|
;;
|
|
*)
|
|
echo ""
|
|
;;
|
|
esac
|
|
}
|
|
|
|
trim_csv() {
|
|
local raw="${1:-}"
|
|
echo "${raw}" \
|
|
| tr '\n' ',' \
|
|
| sed -E 's/[[:space:]]*,[[:space:]]*/,/g; s/^,+//; s/,+$//; s/,+/,/g'
|
|
}
|
|
|
|
ensure_docker_access() {
|
|
local docker_path=""
|
|
docker_path="$(type -P docker || true)"
|
|
if [[ -z "${docker_path}" ]]; then
|
|
echo "docker is required"
|
|
exit 1
|
|
fi
|
|
|
|
if "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=("${docker_path}")
|
|
return 0
|
|
fi
|
|
|
|
if ! command -v sudo >/dev/null 2>&1; then
|
|
echo "docker is installed but this user cannot access it, and sudo is unavailable."
|
|
exit 1
|
|
fi
|
|
|
|
if sudo -n "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=(sudo "${docker_path}")
|
|
echo "Using sudo for docker commands."
|
|
return 0
|
|
fi
|
|
|
|
echo "docker requires elevated privileges on this machine."
|
|
echo "You may be prompted for your sudo password."
|
|
if sudo "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=(sudo "${docker_path}")
|
|
echo "Using sudo for docker commands."
|
|
return 0
|
|
fi
|
|
|
|
echo "Unable to access docker, even with sudo."
|
|
exit 1
|
|
}
|
|
|
|
docker() {
|
|
"${DOCKER_CMD[@]}" "$@"
|
|
}
|
|
|
|
compose_cmd() {
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" "$@"
|
|
}
|
|
|
|
ensure_app_enabled() {
|
|
local app_id="${1:-}"
|
|
if [[ -z "${app_id}" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
compose_cmd exec -T --user www-data app php occ app:enable --force "${app_id}" >/dev/null 2>&1
|
|
if [[ $? -eq 0 ]]; then
|
|
return 0
|
|
fi
|
|
|
|
compose_cmd exec -T --user www-data app php occ app:install "${app_id}" >/dev/null 2>&1
|
|
if [[ $? -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
compose_cmd exec -T --user www-data app php occ app:enable --force "${app_id}" >/dev/null 2>&1
|
|
}
|
|
|
|
occ_app_get() {
|
|
local app_id="${1:-}"
|
|
local key="${2:-}"
|
|
local value=""
|
|
if [[ -z "${app_id}" || -z "${key}" ]]; then
|
|
return 1
|
|
fi
|
|
value="$(compose_cmd exec -T --user www-data app php occ config:app:get "${app_id}" "${key}" 2>/dev/null | tr -d '\r' || true)"
|
|
if [[ "${value}" == "false" ]]; then
|
|
value=""
|
|
fi
|
|
echo "${value}"
|
|
}
|
|
|
|
is_app_enabled() {
|
|
local app_id="${1:-}"
|
|
local app_list_file=""
|
|
if [[ -z "${app_id}" ]]; then
|
|
return 1
|
|
fi
|
|
app_list_file="$(mktemp)"
|
|
if ! compose_cmd exec -T --user www-data app php occ app:list >"${app_list_file}" 2>/dev/null; then
|
|
rm -f "${app_list_file}" >/dev/null 2>&1 || true
|
|
return 1
|
|
fi
|
|
if grep -qE "^[[:space:]]+- ${app_id}([[:space:]:]|$)" "${app_list_file}"; then
|
|
rm -f "${app_list_file}" >/dev/null 2>&1 || true
|
|
return 0
|
|
fi
|
|
rm -f "${app_list_file}" >/dev/null 2>&1 || true
|
|
return 1
|
|
}
|
|
|
|
libresign_default_cn() {
|
|
local cn=""
|
|
local domain=""
|
|
cn="$(read_env_value "LIBRESIGN_CERT_CN" "")"
|
|
if [[ -n "${cn}" ]]; then
|
|
echo "${cn}"
|
|
return 0
|
|
fi
|
|
domain="$(read_env_value "NEXTCLOUD_DOMAIN" "")"
|
|
if [[ -z "${domain}" ]]; then
|
|
domain="$(read_env_value "NEXTCLOUD_TRUSTED_DOMAINS" "")"
|
|
domain="${domain%%,*}"
|
|
domain="${domain%% *}"
|
|
fi
|
|
if [[ -n "${domain}" ]]; then
|
|
echo "NuQloud ${domain} Root CA"
|
|
return 0
|
|
fi
|
|
echo "NuQloud Root CA"
|
|
}
|
|
|
|
ensure_libresign_setup() {
|
|
local auto_setup_raw=""
|
|
local auto_setup="1"
|
|
local auto_install_raw=""
|
|
local auto_install="1"
|
|
local java_path=""
|
|
local jsignpdf_path=""
|
|
local pdftk_path=""
|
|
local root_cert=""
|
|
local engine=""
|
|
local cn=""
|
|
local org=""
|
|
local country=""
|
|
local state=""
|
|
local locality=""
|
|
local ou_raw=""
|
|
local status_local=0
|
|
local occ_args=()
|
|
local ou_value=""
|
|
|
|
if ! is_app_enabled "libresign"; then
|
|
return 0
|
|
fi
|
|
|
|
auto_setup_raw="$(read_env_value "LIBRESIGN_AUTO_SETUP" "1")"
|
|
auto_setup="$(normalize_bool_01 "${auto_setup_raw}")"
|
|
if [[ -z "${auto_setup}" ]]; then
|
|
auto_setup="1"
|
|
fi
|
|
auto_install_raw="$(read_env_value "LIBRESIGN_AUTO_INSTALL_BINARIES" "1")"
|
|
auto_install="$(normalize_bool_01 "${auto_install_raw}")"
|
|
if [[ -z "${auto_install}" ]]; then
|
|
auto_install="1"
|
|
fi
|
|
|
|
java_path="$(occ_app_get "libresign" "java_path")"
|
|
jsignpdf_path="$(occ_app_get "libresign" "jsignpdf_jar_path")"
|
|
pdftk_path="$(occ_app_get "libresign" "pdftk_path")"
|
|
root_cert="$(occ_app_get "libresign" "rootCert")"
|
|
|
|
if [[ "${auto_install}" == "1" && -z "${java_path}" ]]; then
|
|
if ! compose_cmd exec -T --user www-data app php occ libresign:install --java >/dev/null 2>&1; then
|
|
echo "Warning: could not install LibreSign Java runtime."
|
|
status_local=1
|
|
fi
|
|
java_path="$(occ_app_get "libresign" "java_path")"
|
|
fi
|
|
if [[ "${auto_install}" == "1" && -z "${jsignpdf_path}" ]]; then
|
|
if ! compose_cmd exec -T --user www-data app php occ libresign:install --jsignpdf >/dev/null 2>&1; then
|
|
echo "Warning: could not install LibreSign JSignPdf runtime."
|
|
status_local=1
|
|
fi
|
|
jsignpdf_path="$(occ_app_get "libresign" "jsignpdf_jar_path")"
|
|
fi
|
|
if [[ "${auto_install}" == "1" && -z "${pdftk_path}" ]]; then
|
|
if ! compose_cmd exec -T --user www-data app php occ libresign:install --pdftk >/dev/null 2>&1; then
|
|
echo "Warning: could not install LibreSign PDFtk runtime."
|
|
status_local=1
|
|
fi
|
|
pdftk_path="$(occ_app_get "libresign" "pdftk_path")"
|
|
fi
|
|
|
|
if [[ "${auto_setup}" == "1" && -z "${root_cert}" ]]; then
|
|
engine="$(echo "$(read_env_value "LIBRESIGN_CERT_ENGINE" "openssl")" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
|
if [[ "${engine}" != "cfssl" ]]; then
|
|
engine="openssl"
|
|
fi
|
|
cn="$(libresign_default_cn)"
|
|
org="$(read_env_value "LIBRESIGN_CERT_O" "$(read_env_value "NUQLOUD_BILLING_COMPANY_NAME" "NuQloud")")"
|
|
country="$(read_env_value "LIBRESIGN_CERT_C" "")"
|
|
state="$(read_env_value "LIBRESIGN_CERT_ST" "")"
|
|
locality="$(read_env_value "LIBRESIGN_CERT_L" "")"
|
|
ou_raw="$(read_env_value "LIBRESIGN_CERT_OU" "Document Signing")"
|
|
occ_args=("libresign:configure:${engine}" "--cn=${cn}")
|
|
if [[ -n "${org}" ]]; then
|
|
occ_args+=("--o=${org}")
|
|
fi
|
|
if [[ -n "${country}" ]]; then
|
|
occ_args+=("--c=${country}")
|
|
fi
|
|
if [[ -n "${state}" ]]; then
|
|
occ_args+=("--st=${state}")
|
|
fi
|
|
if [[ -n "${locality}" ]]; then
|
|
occ_args+=("--l=${locality}")
|
|
fi
|
|
IFS=',;' read -r -a ou_values <<< "${ou_raw}"
|
|
for ou_value in "${ou_values[@]}"; do
|
|
ou_value="$(echo "${ou_value}" | xargs)"
|
|
if [[ -n "${ou_value}" ]]; then
|
|
occ_args+=("--ou=${ou_value}")
|
|
fi
|
|
done
|
|
if ! compose_cmd exec -T --user www-data app php occ "${occ_args[@]}" >/dev/null 2>&1; then
|
|
echo "Warning: could not configure LibreSign root certificate."
|
|
status_local=1
|
|
fi
|
|
root_cert="$(occ_app_get "libresign" "rootCert")"
|
|
fi
|
|
|
|
if [[ "${auto_install}" == "1" ]]; then
|
|
if [[ -z "${java_path}" || -z "${jsignpdf_path}" || -z "${pdftk_path}" ]]; then
|
|
status_local=1
|
|
fi
|
|
fi
|
|
if [[ "${auto_setup}" == "1" && -z "${root_cert}" ]]; then
|
|
status_local=1
|
|
fi
|
|
|
|
return "${status_local}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--compose-file)
|
|
compose_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--compose-file=*)
|
|
compose_file="${1#*=}"
|
|
shift
|
|
;;
|
|
--env-file)
|
|
env_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--env-file=*)
|
|
env_file="${1#*=}"
|
|
shift
|
|
;;
|
|
--skip-indices)
|
|
skip_indices="1"
|
|
shift
|
|
;;
|
|
--skip-bundle)
|
|
skip_bundle="1"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ "${compose_file}" != /* ]]; then
|
|
compose_file="${repo_root}/${compose_file}"
|
|
fi
|
|
if [[ "${env_file}" != /* ]]; then
|
|
env_file="${repo_root}/${env_file}"
|
|
fi
|
|
|
|
if [[ ! -f "${compose_file}" ]]; then
|
|
echo "Missing compose file: ${compose_file}"
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "${env_file}" ]]; then
|
|
echo "Missing env file: ${env_file}"
|
|
exit 1
|
|
fi
|
|
|
|
export_compose_project_name_from_env "${env_file}" || true
|
|
|
|
ensure_docker_access
|
|
|
|
bundle_enabled_raw="$(read_env_value "INSTALL_DEFAULT_APP_BUNDLE" "$(read_env_value "INSTALL_DEFAULT_NEXTCLOUD_APP_BUNDLE" "0")")"
|
|
bundle_enabled="$(normalize_bool_01 "${bundle_enabled_raw}")"
|
|
if [[ -z "${bundle_enabled}" ]]; then
|
|
bundle_enabled="0"
|
|
fi
|
|
|
|
default_app_bundle="$(trim_csv "$(read_env_value "NEXTCLOUD_DEFAULT_APP_BUNDLE" "notify_push,user_oidc,activity,announcementcenter,calendar,collectives,systemtags,comments,contacts,deck,external,files_accesscontrol,libresign,mail,notes,passwords,circles,groupfolders")")"
|
|
default_app_order="$(trim_csv "$(read_env_value "NEXTCLOUD_DEFAULT_APP_ORDER" "dashboard,files,calendar,mail,deck,contacts,notes,collectives")")"
|
|
|
|
status=0
|
|
|
|
if [[ "${skip_bundle}" != "1" && "${bundle_enabled}" == "1" && -n "${default_app_bundle}" ]]; then
|
|
IFS=',' read -r -a app_ids <<< "${default_app_bundle}"
|
|
for raw_app_id in "${app_ids[@]}"; do
|
|
app_id="$(echo "${raw_app_id}" | xargs)"
|
|
if [[ -z "${app_id}" ]]; then
|
|
continue
|
|
fi
|
|
if ensure_app_enabled "${app_id}"; then
|
|
continue
|
|
fi
|
|
echo "Warning: could not install/enable Nextcloud app bundle item '${app_id}'."
|
|
status=1
|
|
done
|
|
|
|
if [[ -n "${default_app_order}" ]]; then
|
|
if ! compose_cmd exec -T --user www-data app php occ config:system:set defaultapp --value="${default_app_order}" >/dev/null 2>&1; then
|
|
echo "Warning: could not set Nextcloud defaultapp order to '${default_app_order}'."
|
|
status=1
|
|
fi
|
|
fi
|
|
|
|
if ! ensure_libresign_setup; then
|
|
status=1
|
|
fi
|
|
fi
|
|
|
|
if [[ "${skip_indices}" != "1" ]]; then
|
|
if ! compose_cmd exec -T --user www-data app php occ db:add-missing-indices >/dev/null 2>&1; then
|
|
echo "Warning: occ db:add-missing-indices did not complete successfully."
|
|
status=1
|
|
fi
|
|
fi
|
|
|
|
exit "${status}"
|