nextcloud: preserve installed apps during ordinary restarts

This commit is contained in:
jenkins 2026-10-04 04:25:46 -05:00
parent 5ba2211ad4
commit 7a61fff82d
2 changed files with 82 additions and 26 deletions

View File

@ -129,8 +129,8 @@ spec:
if [ "${installed}" = "true" ]; then
configure_oidc() {
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_provider_url --value='https://sso.bstein.dev/realms/atlas'"
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_id --value='${OIDC_CLIENT_ID}'"
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_secret --value='${OIDC_CLIENT_SECRET}'"
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_id --value='${OIDC_CLIENT_ID}'" >/dev/null
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_secret --value='${OIDC_CLIENT_SECRET}'" >/dev/null
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_auto_redirect --type=boolean --value=true"
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_hide_password_form --type=boolean --value=true"
su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_disable_registration --type=boolean --value=false"
@ -143,10 +143,10 @@ spec:
cfg_dir="/var/www/html/resources/config"
mkdir -p "${cfg_dir}"
if [ ! -s "${cfg_dir}/mimetypemapping.dist.json" ]; then
curl -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypemapping.dist.json -o "${cfg_dir}/mimetypemapping.dist.json" || true
curl --connect-timeout 10 --max-time 120 -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypemapping.dist.json -o "${cfg_dir}/mimetypemapping.dist.json" || true
fi
if [ ! -s "${cfg_dir}/mimetypealiases.dist.json" ]; then
curl -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypealiases.dist.json -o "${cfg_dir}/mimetypealiases.dist.json" || true
curl --connect-timeout 10 --max-time 120 -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypealiases.dist.json -o "${cfg_dir}/mimetypealiases.dist.json" || true
fi
chown -R 33:33 "${cfg_dir}" || true
}
@ -154,35 +154,40 @@ spec:
app="$1"
url="$2"
target="/var/www/html/custom_apps/${app}"
rm -rf "${target}"
mkdir -p /tmp/nextcloud-apps
curl -fsSL "${url}" -o "/tmp/nextcloud-apps/${app}.tar.gz"
tar -xzf "/tmp/nextcloud-apps/${app}.tar.gz" -C /var/www/html/custom_apps
rm -f "/tmp/nextcloud-apps/${app}.tar.gz"
# Installed apps survive pod replacement; upgrades are maintenance.
if [ -s "${target}/appinfo/info.xml" ]; then
return 0
fi
stage="$(mktemp -d /var/www/html/custom_apps/.bootstrap-XXXXXX)"
if ! curl --connect-timeout 10 --max-time 180 -fsSL "${url}" -o "${stage}/app.tar.gz" \
|| ! tar -xzf "${stage}/app.tar.gz" -C "${stage}" \
|| [ ! -s "${stage}/${app}/appinfo/info.xml" ]; then
rm -rf "${stage}"
return 1
fi
# Rename on the same PVC only after extraction is complete.
if [ -e "${target}" ]; then
mv "${target}" "${stage}/previous"
fi
mv "${stage}/${app}" "${target}"
chown -R 33:33 "${target}"
su -s /bin/sh www-data -c "php /var/www/html/occ app:enable --force ${app}" || true
}
reset_external_config() {
su -s /bin/sh www-data -c "php /var/www/html/occ app:remove external" || true
su -s /bin/sh www-data -c "php /var/www/html/occ config:app:delete external jwt_token_privkey_es256" || true
su -s /bin/sh www-data -c "php /var/www/html/occ config:app:delete external jwt_token_pubkey_es256" || true
su -s /bin/sh www-data -c "php /var/www/html/occ config:app:delete external jwt_token_privkey_ed25519" || true
su -s /bin/sh www-data -c "php /var/www/html/occ config:app:delete external jwt_token_pubkey_ed25519" || true
rm -rf "${stage}"
su -s /bin/sh www-data -c "php /var/www/html/occ app:enable --force ${app}"
}
ensure_app() {
app="$1"
target="/var/www/html/custom_apps/${app}"
rm -rf "${target}"
su -s /bin/sh www-data -c "php /var/www/html/occ app:remove ${app}" || true
su -s /bin/sh www-data -c "php /var/www/html/occ app:install --force ${app}" || true
su -s /bin/sh www-data -c "php /var/www/html/occ app:enable --force ${app}" || true
if [ -s "${target}/appinfo/info.xml" ]; then
return 0
fi
su -s /bin/sh www-data -c "php /var/www/html/occ app:install ${app}" || return 1
su -s /bin/sh www-data -c "php /var/www/html/occ app:enable ${app}"
}
ensure_mime_defaults
reset_external_config
install_app external https://github.com/nextcloud-releases/external/releases/download/v5.4.1/external-v5.4.1.tar.gz
install_app mail https://github.com/nextcloud-releases/mail/releases/download/v3.7.24/mail-stable3.7.tar.gz
install_app oidc_login https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v3.2.2/oidc_login.tar.gz
ensure_app richdocuments
install_app external https://github.com/nextcloud-releases/external/releases/download/v5.4.1/external-v5.4.1.tar.gz || exit 1
install_app mail https://github.com/nextcloud-releases/mail/releases/download/v3.7.24/mail-stable3.7.tar.gz || exit 1
install_app oidc_login https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v3.2.2/oidc_login.tar.gz || exit 1
ensure_app richdocuments || exit 1
configure_office
configure_oidc
fi

View File

@ -0,0 +1,51 @@
"""Check that a routine Nextcloud restart preserves installed app files."""
from pathlib import Path
import shlex
import subprocess
import yaml
ROOT = Path(__file__).resolve().parents[2]
def functions(tmp_path: Path) -> str:
"""Extract the deployed shell functions into a synthetic filesystem."""
manifest = yaml.safe_load((ROOT / "services/nextcloud/deployment.yaml").read_text())
containers = manifest["spec"]["template"]["spec"]["initContainers"]
script = next(c for c in containers if c["name"] == "install-nextcloud")["args"][0]
start = script.index(" install_app() {")
end = script.index(" ensure_mime_defaults\n", start)
return script[start:end].replace("/var/www/html", str(tmp_path))
def test_existing_apps_need_no_download_or_reinstallation(tmp_path: Path):
"""Unchanged app files survive without network or CLI configuration calls."""
app = tmp_path / "custom_apps" / "synthetic"
(app / "appinfo").mkdir(parents=True)
(app / "appinfo/info.xml").write_text("<info/>")
preserved = app / "keep.txt"
preserved.write_text("original")
shell = functions(tmp_path) + """
curl() { exit 91; }
su() { exit 92; }
install_app synthetic https://invalid.example/test.tar.gz
ensure_app synthetic
"""
subprocess.run(["sh", "-eu", "-c", shell], check=True)
assert preserved.read_text() == "original"
def test_failed_download_preserves_existing_directory(tmp_path: Path):
"""A failed first install never removes an existing incomplete app tree."""
app = tmp_path / "custom_apps" / "synthetic"
app.mkdir(parents=True)
(app / "keep.txt").write_text("original")
shell = functions(tmp_path) + """
curl() { return 1; }
if install_app synthetic https://invalid.example/test.tar.gz; then exit 93; fi
"""
subprocess.run(["sh", "-eu", "-c", shell], check=True)
assert (app / "keep.txt").read_text() == "original"
assert not list((tmp_path / "custom_apps").glob(".bootstrap-*"))