The main sweep — ~260 call sites across ~100 files move from string
concatenation on a single root to appDir/storageAppDirs/storageAppConfigs.
On a single-root install the resolved paths are identical, so this is a
no-op until a location is registered.
Enumerators were the interesting half. `for d in "$containers_dir"/*/`
appears in the menus, the registry/artifact scanners and the DNS setup —
and a shell glob cannot list a rootless 751 tree at all, which is the
same bug config_find_file.sh already documents in a comment. Routing them
through storageAppDirs (which enumerates as the owning user) fixes that
alongside the multi-root work.
Three places needed judgement rather than substitution:
db_app_scan.sh deletes database rows and port allocations for apps whose
folder is missing, and reaps "empty" app dirs. With a storage location
unmounted, every app on it looks exactly like that. Each of those
branches now gates on appStorageAvailable first — an app on an unplugged
drive is skipped with a notice, never deleted.
instance_create.sh rewrites cloned hooks so an instance touches its own
directory instead of the base app's. Its sed matched ${containers_dir}<type>,
which this sweep just replaced with $(appDir <type>) — so it would have
silently stopped redirecting, and an instance would have written to the
original's files (the adguard auth adapter case its own comment warns
about). Now matches both appDir forms, verified against bare, quoted,
unrelated-app, legacy and prose cases.
peer_shell/peer_pull streamed and extracted relative to the primary root.
Both now use the app's own root, and peer_shell keeps a single-root
fallback since it runs as a restricted SSH shell with no LibrePortal env.
Also fixes a pre-existing bug found on the way: webui_app_config.sh
tested "$containers_dir/frontend/data/last_update", one level short of the
real tree under the libreportal app dir, so the WebUI refresh trigger
after a config update has never once fired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
64 lines
2.6 KiB
Bash
64 lines
2.6 KiB
Bash
#!/bin/bash
|
|
|
|
# Apps with `build: .` in their docker compose need their Dockerfile +
|
|
# source tree to live in the deployed container dir (where compose runs
|
|
# from), not just in the install template dir. dockerComposeSetupFile
|
|
# only copies the compose itself; this helper covers the rest.
|
|
#
|
|
# Auto-detect: if a Dockerfile exists in the install dir for the app,
|
|
# we copy every file/folder from install→deployed except docker-compose
|
|
# variants (already handled) and the .config (per-app config has its
|
|
# own copy lifecycle in dockerConfigSetupToContainer).
|
|
#
|
|
# Idempotent: re-running mirrors the latest install template. Safe to
|
|
# call on every install action.
|
|
dockerCopyBuildContext()
|
|
{
|
|
local app_name="$1"
|
|
local source_dir="$install_containers_dir$app_name"
|
|
local target_dir="$(appDir "$app_name")"
|
|
|
|
if [[ -z "$app_name" ]]; then
|
|
isError "dockerCopyBuildContext: app_name is empty."
|
|
return 1
|
|
fi
|
|
if [[ ! -f "$source_dir/Dockerfile" ]]; then
|
|
# Nothing to do — app uses a pre-built image.
|
|
return 0
|
|
fi
|
|
if [[ ! -d "$target_dir" ]]; then
|
|
isError "dockerCopyBuildContext: target $target_dir doesn't exist."
|
|
return 1
|
|
fi
|
|
|
|
isNotice "Copying build context (Dockerfile + source)"
|
|
|
|
# Use rsync if available so re-installs only diff what changed; fall
|
|
# back to cp -R for systems without rsync. Either way we exclude the
|
|
# files that have their own copy lifecycle.
|
|
# --no-owner --no-group: don't carry the install template's ownership (the
|
|
# repo clone is owned by the human user, uid ~1000) onto the deployed
|
|
# container dir — the chown below sets the correct owner. Without this the
|
|
# rsync re-stamps the WebUI tree as uid 1000 every install.
|
|
if command -v rsync >/dev/null 2>&1; then
|
|
runFileOp rsync -a --no-owner --no-group \
|
|
--exclude="docker-compose.yml" \
|
|
--exclude="docker-compose.${app_name}.yml" \
|
|
--exclude="${app_name}.config" \
|
|
--exclude="${app_name}.tools.json" \
|
|
--exclude="${app_name}.svg" \
|
|
--exclude="data" \
|
|
"$source_dir/" "$target_dir/"
|
|
else
|
|
# Best-effort fallback. Copies everything then nukes the
|
|
# excluded items in the target.
|
|
runFileOp cp -R "$source_dir/." "$target_dir/"
|
|
runFileOp rm -f "$target_dir/${app_name}.config" "$target_dir/${app_name}.tools.json"
|
|
fi
|
|
|
|
# runFileOp above wrote the tree AS the container owner (rootless: the docker
|
|
# install user; rooted: the manager) with --no-owner so the repo-clone uid
|
|
# isn't carried — no root, no chown needed.
|
|
isSuccessful "Build context copied for $app_name."
|
|
}
|