forked from coop-cloud/nextcloud
Merges 49 commits from coop-cloud/nextcloud upstream: nginx 1.31.6, redis 8.10.2, whiteboard v2.0.0, postgres overlay on pgautoupgrade 17, MariaDB auto-upgrade, an optional metrics overlay, a check_major_upgrade helper, a fix for install_collabora, and smtp-in-cron wiring. See release/16.1.0+35.0.1-fpm for details. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
367 lines
14 KiB
Bash
367 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
export FPM_TUNE_VERSION=v5
|
|
export NGINX_CONF_VERSION=v9
|
|
export MY_CNF_VERSION=v6
|
|
export ENTRYPOINT_VERSION=v3
|
|
export ENTRYPOINT_WHITEBOARD_VERSION=v1
|
|
export ENTRYPOINT_TALK_VERSION=v1
|
|
export ENTRYPOINT_EUROOFFICE_VERSION=v3
|
|
export EUROOFFICE_CREATEDB_VERSION=v1
|
|
export CRONTAB_VERSION=v1
|
|
export PG_BACKUP_VERSION=v2
|
|
|
|
run_occ() {
|
|
# NOTE: uses $* (not $@) so this still works when called with multiple args as seperate words.
|
|
su -p www-data -s /bin/sh -c "/var/www/html/occ $*"
|
|
}
|
|
|
|
install_apps() {
|
|
install_apps="$@"
|
|
if [ -z "$install_apps" ]; then
|
|
install_apps=$APPS
|
|
fi
|
|
for app in $install_apps; do
|
|
run_occ "app:install $app"
|
|
done
|
|
}
|
|
|
|
set_app_config() {
|
|
APP=$1
|
|
KEY=$2
|
|
VALUE=$3
|
|
run_occ "config:app:set $APP $KEY --value '$VALUE'"
|
|
}
|
|
|
|
set_system_config() {
|
|
KEY=$1
|
|
VALUE=$2
|
|
run_occ "config:system:set $KEY --value '$VALUE'"
|
|
}
|
|
|
|
set_trusted_proxies() {
|
|
trusted_proxies="$@"
|
|
if [ -z "$1" ]; then
|
|
trusted_proxies="$TRUSTED_PROXIES"
|
|
fi
|
|
set_system_config trusted_proxies "$trusted_proxies"
|
|
}
|
|
|
|
set_logfile_stdout() {
|
|
set_system_config logfile '/dev/stdout'
|
|
}
|
|
|
|
customize() {
|
|
if [ -z "$1" ]
|
|
then
|
|
echo "Usage: ... customize <assets_path>"
|
|
exit 1
|
|
fi
|
|
asset_dir=$1
|
|
for asset in $COPY_ASSETS; do
|
|
source=$(echo $asset | cut -d "|" -f1)
|
|
target=$(echo $asset | cut -d "|" -f2)
|
|
echo copy $source to $target
|
|
abra app cp $APP_NAME $asset_dir/$source $target
|
|
done
|
|
|
|
abra app cmd -T $APP_NAME app set_app_config theming color \"$THEMING_COLOR\"
|
|
abra app cmd -T $APP_NAME app set_app_config theming slogan \"$THEMING_SLOGAN\"
|
|
abra app cmd -T $APP_NAME app run_occ '"theming:config background \"/var/www/html/themes/flow_background.jpg\""'
|
|
abra app cmd -T $APP_NAME app run_occ '"theming:config logo \"/var/www/html/themes/icon_left_brand.svg\""'
|
|
abra app cmd -T $APP_NAME app run_occ '"theming:config logoheader \"/var/www/html/themes/icon.png\""'
|
|
}
|
|
|
|
install_bbb() {
|
|
install_apps bbb
|
|
set_app_config bbb app.navigation true
|
|
set_app_config bbb api.url "$BBB_URL"
|
|
set_app_config bbb api.secret "$(cat /run/secrets/bbb_secret)"
|
|
}
|
|
|
|
install_onlyoffice() {
|
|
install_apps onlyoffice
|
|
set_app_config onlyoffice DocumentServerUrl "$ONLYOFFICE_URL"
|
|
set_app_config onlyoffice jwt_secret "$(cat /run/secrets/onlyoffice_jwt)"
|
|
set_app_config onlyoffice customizationForcesave true
|
|
}
|
|
|
|
install_eurooffice() {
|
|
install_apps eurooffice
|
|
set_app_config eurooffice DocumentServerUrl "https://${EUROOFFICE_DOMAIN}"
|
|
set_app_config eurooffice jwt_secret "$(cat /run/secrets/eurooffice_jwt)"
|
|
set_app_config eurooffice customizationForcesave true
|
|
}
|
|
|
|
install_collabora() {
|
|
install_apps richdocuments
|
|
set_app_config richdocuments wopi_url "$COLLABORA_URL"
|
|
# important for security reaosns
|
|
# https://docs.nextcloud.com/server/latest/admin_manual/office/configuration.html#wopi-settings
|
|
set_app_config richdocuments wopi_allowlist "$COLLABORA_ALLOWLIST"
|
|
run_occ "richdocuments:activate-config"
|
|
}
|
|
|
|
install_whiteboard() {
|
|
install_apps whiteboard
|
|
set_app_config whiteboard collabBackendUrl "https://${DOMAIN}/whiteboard"
|
|
set_app_config whiteboard jwt_secret_key "$(cat /run/secrets/whiteboard_jwt)"
|
|
}
|
|
|
|
|
|
install_talk() {
|
|
install_apps spreed
|
|
run_occ "talk:signaling:add --verify 'wss://${TALK_DOMAIN}' '$(cat /run/secrets/talk_signaling_secret)'"
|
|
run_occ "talk:stun:add '${TALK_DOMAIN}:3478'"
|
|
run_occ "talk:stun:add '${TALK_DOMAIN}:443'"
|
|
run_occ "talk:turn:add --secret='$(cat /run/secrets/talk_turn_secret)' turn '${TALK_DOMAIN}:3478' udp,tcp"
|
|
|
|
}
|
|
|
|
install_fulltextsearch() {
|
|
install_apps fulltextsearch
|
|
install_apps fulltextsearch_elasticsearch
|
|
install_apps files_fulltextsearch
|
|
set_app_config fulltextsearch search_platform "OCA\\FullTextSearch_Elasticsearch\\Platform\\ElasticSearchPlatform"
|
|
set_app_config fulltextsearch_elasticsearch elastic_host "http://elastic:$(cat /run/secrets/elasticsearch_password)@elasticsearch:9200/"
|
|
set_app_config fulltextsearch_elasticsearch elastic_index "nextcloud"
|
|
set_app_config files_fulltextsearch files_local "1"
|
|
}
|
|
|
|
set_default_quota() {
|
|
set_app_config files default_quota "$DEFAULT_QUOTA"
|
|
}
|
|
|
|
set_authentik() {
|
|
install_apps sociallogin
|
|
AUTHENTIK_SECRET=$(cat /run/secrets/authentik_secret)
|
|
AUTHENTIK_ID=$(cat /run/secrets/authentik_id)
|
|
set_system_config logo_url https://$AUTHENTIK_DOMAIN
|
|
set_app_config sociallogin custom_providers "
|
|
{
|
|
\"custom_oidc\":[
|
|
{
|
|
\"name\":\"$AUTHENTIK_USER_PREFIX\",
|
|
\"title\":\"authentik\",
|
|
\"authorizeUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/authorize/\",
|
|
\"tokenUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/token/\",
|
|
\"displayNameClaim\":\"preferred_username\",
|
|
\"userInfoUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/userinfo/\",
|
|
\"logoutUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/nextcloud/end-session/\",
|
|
\"clientId\":\"$AUTHENTIK_ID\",
|
|
\"clientSecret\":\"$AUTHENTIK_SECRET\",
|
|
\"scope\":\"openid profile email nextcloud\",
|
|
\"groupsClaim\":\"nextcloud_groups\",
|
|
\"style\":\"openid\",
|
|
\"defaultGroup\":\"\",
|
|
\"groupMapping\": {
|
|
\"admin\": \"admin\",
|
|
\"authentik Admins\": \"admin\"
|
|
}
|
|
}
|
|
]
|
|
}"
|
|
|
|
set_app_config sociallogin update_profile_on_login 1
|
|
set_app_config sociallogin auto_create_groups 1
|
|
set_app_config sociallogin hide_default_login 1
|
|
run_occ 'config:system:set social_login_auto_redirect --value true'
|
|
run_occ 'config:system:set allow_user_to_change_display_name --value=false'
|
|
run_occ 'config:system:set lost_password_link --value=disabled'
|
|
}
|
|
|
|
set_user_oidc() {
|
|
install_apps user_oidc
|
|
USER_OIDC_SECRET=$(cat /run/secrets/user_oidc_secret)
|
|
run_occ "user_oidc:provider \
|
|
--clientid=${USER_OIDC_ID} \
|
|
--clientsecret=${USER_OIDC_SECRET} \
|
|
--discoveryuri=${USER_OIDC_DISCOVERY_URI} \
|
|
--endsessionendpointuri=${USER_OIDC_END_SESSION_URI} \
|
|
--postlogouturi=https://${DOMAIN} \
|
|
--scope='openid email profile' \
|
|
${USER_OIDC_PROVIDER}"
|
|
# disable non user_oidc login
|
|
if [[ ${USER_OIDC_LOGIN_ONLY:-false} = "true" ]]; then
|
|
run_occ "config:app:set --value=0 user_oidc allow_multiple_user_backends"
|
|
fi
|
|
}
|
|
|
|
disable_skeletondirectory() {
|
|
run_occ "config:system:set skeletondirectory --value ''"
|
|
}
|
|
|
|
set_windowsfriendly_filenames() {
|
|
run_occ 'config:system:set forbidden_filename_characters 0 --value=?'
|
|
run_occ 'config:system:set forbidden_filename_characters 1 --value=\<'
|
|
run_occ 'config:system:set forbidden_filename_characters 2 --value=\>'
|
|
run_occ 'config:system:set forbidden_filename_characters 3 --value=:'
|
|
run_occ 'config:system:set forbidden_filename_characters 4 --value=*'
|
|
run_occ 'config:system:set forbidden_filename_characters 5 --value=\|'
|
|
run_occ 'config:system:set forbidden_filename_characters 6 --value=\"'
|
|
}
|
|
|
|
upgrade_mariadb() {
|
|
mariadb-upgrade -p`cat /run/secrets/db_root_password`
|
|
}
|
|
|
|
configure_metrics() {
|
|
run_occ "config:system:set openmetrics_allowed_clients 0 --value='10.0.0.0/8'"
|
|
}
|
|
|
|
# Checks whether this instance looks ready to update to the next Nextcloud
|
|
# major version.
|
|
#
|
|
# Usage:
|
|
# abra app cmd <app-name> app check_major_upgrade
|
|
# abra app cmd <app-name> app check_major_upgrade 33 # check readiness for a specific target
|
|
#
|
|
# What it checks:
|
|
# - current version is exactly one major behind the target
|
|
# - no pending DB upgrade from a previous, unfinished update
|
|
# - whether a newer release is available on the current major
|
|
# (recommended before upgradeing to the next major)
|
|
# - every enabled, non-shipped app's compatibility with the target major
|
|
# - for apps that don't, whether apps.nextcloud.com already has a newer
|
|
# release that does
|
|
#
|
|
# It does NOT check every precondition, always read the release notes
|
|
# from Nextcloud too.
|
|
check_major_upgrade() {
|
|
target_major=$1
|
|
|
|
echo "=== Nextcloud major upgrade readiness check ==="
|
|
|
|
status_json=$(run_occ status --output=json 2>/dev/null)
|
|
if [ -z "$status_json" ]; then
|
|
echo "[FAIL] Could not read 'occ status' - is Nextcloud installed and reachable?"
|
|
return 1
|
|
fi
|
|
|
|
current_version=$(echo "$status_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo $d["versionstring"] ?? "";')
|
|
current_major=${current_version%%.*}
|
|
needs_db_upgrade=$(echo "$status_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo ($d["needsDbUpgrade"] ?? false) ? "true" : "false";')
|
|
|
|
if [ -z "$current_major" ]; then
|
|
echo "[FAIL] Could not determine the current Nextcloud version from 'occ status'."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$target_major" ]; then
|
|
target_major=$((current_major + 1))
|
|
fi
|
|
|
|
echo "Current version: $current_version"
|
|
echo "Target major version: $target_major"
|
|
|
|
ok=true
|
|
|
|
if [ "$target_major" -le "$current_major" ]; then
|
|
echo "[FAIL] Target major ($target_major) is not newer than the current major ($current_major)."
|
|
ok=false
|
|
elif [ "$target_major" -gt "$((current_major + 1))" ]; then
|
|
echo "[FAIL] Cannot skip major versions. Upgrade to $((current_major + 1)) first."
|
|
ok=false
|
|
fi
|
|
|
|
if [ "$needs_db_upgrade" = "true" ]; then
|
|
echo "[FAIL] A pending database upgrade was detected. Run 'occ upgrade' for the current version first."
|
|
ok=false
|
|
fi
|
|
|
|
echo
|
|
echo "--- occ update:check ---"
|
|
update_check_output=$(run_occ "update:check" 2>&1)
|
|
if [ -z "$update_check_output" ]; then
|
|
echo "[WARN] 'occ update:check' produced no output, could not verify."
|
|
elif echo "$update_check_output" | grep -q "Everything up to date"; then
|
|
echo "[OK] Everything up to date."
|
|
else
|
|
available_version=$(echo "$update_check_output" | grep -oE 'Nextcloud [0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | awk '{print $2}')
|
|
available_major=${available_version%%.*}
|
|
if [ -z "$available_major" ]; then
|
|
echo "[WARN] Could not parse 'occ update:check' output to determine the available version."
|
|
elif [ "$available_major" = "$current_major" ]; then
|
|
echo "[WARN] $available_version is available on the current major. Recommended to update to that before upgrading to $target_major."
|
|
else
|
|
echo "[OK] Already on the latest release of major $current_major (next available update is $available_version)."
|
|
fi
|
|
fi
|
|
echo
|
|
|
|
echo "--- Non-shipped app compatibility with Nextcloud $target_major ---"
|
|
echo "(shipped apps are skipped, they come bundled with the docker image)"
|
|
apps_json=$(run_occ "app:list --shipped=false --enabled --output=json" 2>/dev/null)
|
|
|
|
if [ -z "$apps_json" ]; then
|
|
echo "[WARN] 'occ app:list' returned no output, could not check non-shipped app compatibility."
|
|
enabled_apps=""
|
|
else
|
|
apps_json_valid=$(echo "$apps_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo (json_last_error() === JSON_ERROR_NONE && is_array($d)) ? "1" : "0";')
|
|
if [ "$apps_json_valid" != "1" ]; then
|
|
echo "[WARN] Could not parse 'occ app:list' output, could not check non-shipped app compatibility."
|
|
enabled_apps=""
|
|
else
|
|
enabled_apps=$(echo "$apps_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); foreach(array_keys($d["enabled"] ?? []) as $a) echo $a."\n";')
|
|
if [ -z "$enabled_apps" ]; then
|
|
echo "No non-shipped apps are enabled - nothing to check here."
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
compatible_apps=""
|
|
compatible_apps_fetched=0
|
|
|
|
for app in $enabled_apps; do
|
|
info_file=$(find /var/www/html/apps /var/www/html/custom_apps -maxdepth 3 -type f -ipath "*/$app/appinfo/info.xml" 2>/dev/null | head -n1)
|
|
|
|
if [ -z "$info_file" ]; then
|
|
echo "[WARN] $app: could not locate appinfo/info.xml, skipping"
|
|
continue
|
|
fi
|
|
|
|
max_version=$(php -r '
|
|
$x = @simplexml_load_file($argv[1]);
|
|
$dep = $x ? ($x->dependencies->nextcloud ?? null) : null;
|
|
echo $dep !== null ? (string)$dep["max-version"] : "";
|
|
' "$info_file")
|
|
|
|
if [ -z "$max_version" ]; then
|
|
echo "[WARN] $app: no max-version declared in info.xml, assume compatible but verify manually"
|
|
continue
|
|
fi
|
|
|
|
if [ "${max_version%%.*}" -ge "$target_major" ] 2>/dev/null; then
|
|
echo "[OK] $app: installed version supports up to Nextcloud $max_version"
|
|
continue
|
|
fi
|
|
|
|
echo "[INFO] $app: installed version only supports up to Nextcloud $max_version"
|
|
|
|
if [ "$compatible_apps_fetched" != "1" ]; then
|
|
compatible_apps_fetched=1
|
|
compatible_apps=$(curl -fsSL --max-time 30 "https://apps.nextcloud.com/api/v1/platform/${target_major}.0.0/apps.json" 2>/dev/null \
|
|
| php -r '$d=json_decode(stream_get_contents(STDIN),true); if(is_array($d)) foreach($d as $a) echo $a["id"]."\n";')
|
|
fi
|
|
|
|
if [ -z "$compatible_apps" ]; then
|
|
echo "[FAIL] $app: could not reach apps.nextcloud.com to check for a newer compatible release, verify manually"
|
|
ok=false
|
|
elif echo "$compatible_apps" | grep -qxF "$app"; then
|
|
echo "[WARN] $app: apps.nextcloud.com has a release that supports $target_major. It may not update until Nextcloud is upgraded, occ upgrade will try to update it automatically"
|
|
else
|
|
echo "[FAIL] $app: no apps.nextcloud.com release supports $target_major yet, it will be disabled during the upgrade"
|
|
ok=false
|
|
fi
|
|
done
|
|
|
|
echo
|
|
if [ "$ok" = true ]; then
|
|
echo "=== READY: no blocking issues found for upgrade to major $target_major ==="
|
|
return 0
|
|
else
|
|
echo "=== NOT READY: resolve the [FAIL] items above before running the upgrade ==="
|
|
return 1
|
|
fi
|
|
}
|