Archived
feat(docker): add fail-closed host profiles
This commit is contained in:
Executable
+236
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
log "pi-web Docker dev compose: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
script_dir=$(unset CDPATH; cd "$(dirname "$0")" && pwd -P)
|
||||
repo_root=$(unset CDPATH; cd "$script_dir/../.." && pwd -P)
|
||||
dev_config_file=$repo_root/.pi-web/docker-compose-dev.local.env
|
||||
legacy_dev_env_file=$repo_root/.pi-web/docker-compose-dev.env
|
||||
generated_env_file=$repo_root/.pi-web/docker-compose-dev.generated.env
|
||||
|
||||
# shellcheck source=../lib/host-profile.sh
|
||||
# shellcheck disable=SC1091
|
||||
. "$repo_root/docker/lib/host-profile.sh"
|
||||
|
||||
strip_wrapping_quotes() {
|
||||
value=$1
|
||||
case "$value" in
|
||||
\"*\")
|
||||
case "$value" in
|
||||
*\") value=${value#\"}; value=${value%\"} ;;
|
||||
esac
|
||||
;;
|
||||
\'*\')
|
||||
case "$value" in
|
||||
*\') value=${value#\'}; value=${value%\'} ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
printf '%s\n' "$value"
|
||||
}
|
||||
|
||||
env_file_value() {
|
||||
file=$1
|
||||
key=$2
|
||||
[ -f "$file" ] || return 1
|
||||
raw=$(awk -v key="$key" '
|
||||
function trim(value) {
|
||||
sub(/^[ \t]+/, "", value)
|
||||
sub(/[ \t\r]+$/, "", value)
|
||||
return value
|
||||
}
|
||||
/^[ \t]*(#|$)/ { next }
|
||||
{
|
||||
line = $0
|
||||
sub(/^[ \t]*export[ \t]+/, "", line)
|
||||
name = line
|
||||
sub(/=.*/, "", name)
|
||||
name = trim(name)
|
||||
if (name == key) {
|
||||
sub(/^[^=]*=/, "", line)
|
||||
print trim(line)
|
||||
found = 1
|
||||
exit
|
||||
}
|
||||
}
|
||||
END { if (!found) exit 1 }
|
||||
' "$file") || return 1
|
||||
strip_wrapping_quotes "$raw"
|
||||
}
|
||||
|
||||
dev_config_value() {
|
||||
env_file_value "$dev_config_file" "$1"
|
||||
}
|
||||
|
||||
runtime_env_value() {
|
||||
env_file_value "$runtime_env_file" "$1"
|
||||
}
|
||||
|
||||
write_initial_dev_config() {
|
||||
[ ! -e "$dev_config_file" ] || return 0
|
||||
|
||||
temp_config=$dev_config_file.$$
|
||||
previous_umask=$(umask)
|
||||
umask 077
|
||||
cat >"$temp_config" <<'EOF'
|
||||
# PI WEB Docker dev settings. Safe to edit.
|
||||
#
|
||||
# docker/scripts/docker-compose-dev creates this file once and does not
|
||||
# overwrite it. Put persistent dev Docker settings here.
|
||||
#
|
||||
# Precedence for values used by the wrapper:
|
||||
# 1. current shell environment
|
||||
# 2. this file
|
||||
# 3. runtime installer env, usually ~/.local/share/pi-web-docker/.env
|
||||
# 4. built-in defaults
|
||||
#
|
||||
# Generated effective values are written to:
|
||||
# .pi-web/docker-compose-dev.generated.env
|
||||
#
|
||||
# Bind addresses:
|
||||
# - 127.0.0.1 exposes only to this machine.
|
||||
# - 0.0.0.0 exposes on all host interfaces. Use only on trusted networks.
|
||||
#
|
||||
# Uncomment or add values to persist them. PI_WEB_DEV_API_BIND_ADDR
|
||||
# controls the web/API server; PI_WEB_DEV_BIND_ADDR controls the Vite UI.
|
||||
# PI_WEB_DEV_API_BIND_ADDR=127.0.0.1
|
||||
# PI_WEB_DEV_BIND_ADDR=127.0.0.1
|
||||
# PI_WEB_DEV_API_PORT=8504
|
||||
# PI_WEB_DEV_PORT=8505
|
||||
#
|
||||
# Shared Docker/runtime-style defaults may also be set here:
|
||||
# PI_WEB_DOCKER_DATA_DIR=/absolute/path/to/pi-web-docker/data
|
||||
# PI_WEB_DOCKER_EXTRA_HOST_PATHS="/absolute/path/one /absolute/path/two"
|
||||
EOF
|
||||
umask "$previous_umask"
|
||||
|
||||
if [ -f "$legacy_dev_env_file" ]; then
|
||||
{
|
||||
printf '\n%s\n' "# Values copied from the previous generated dev env file."
|
||||
printf '%s\n' "# Keep, edit, or delete these lines as needed."
|
||||
for key in PI_WEB_DEV_API_BIND_ADDR PI_WEB_DEV_BIND_ADDR PI_WEB_DEV_API_PORT PI_WEB_DEV_PORT; do
|
||||
if value=$(env_file_value "$legacy_dev_env_file" "$key"); then
|
||||
printf '%s=%s\n' "$key" "$value"
|
||||
fi
|
||||
done
|
||||
} >>"$temp_config"
|
||||
fi
|
||||
|
||||
mv "$temp_config" "$dev_config_file"
|
||||
log "Created user-editable dev config: $dev_config_file"
|
||||
}
|
||||
|
||||
value_from_env_or_config_or_runtime_or_default() {
|
||||
key=$1
|
||||
default_value=$2
|
||||
eval "is_set=\${$key+x}"
|
||||
if [ "${is_set:-}" = x ]; then
|
||||
eval "printf '%s\n' \"\${$key}\""
|
||||
elif existing=$(dev_config_value "$key"); then
|
||||
printf '%s\n' "$existing"
|
||||
elif existing=$(runtime_env_value "$key"); then
|
||||
printf '%s\n' "$existing"
|
||||
else
|
||||
printf '%s\n' "$default_value"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! pi_web_docker_host_detect_profile; then
|
||||
pi_web_docker_host_print_detection_failure
|
||||
die "refusing to run Docker Compose for an unsupported or unknown host setup"
|
||||
fi
|
||||
|
||||
runtime_env_file=${PI_WEB_DOCKER_RUNTIME_ENV_FILE:-}
|
||||
if [ -z "$runtime_env_file" ] && [ -n "${HOME:-}" ]; then
|
||||
runtime_env_file=$HOME/.local/share/pi-web-docker/.env
|
||||
fi
|
||||
|
||||
mkdir -p "$repo_root/.pi-web" || die "could not create .pi-web directory"
|
||||
write_initial_dev_config
|
||||
|
||||
pi_web_uid=$(value_from_env_or_config_or_runtime_or_default PI_WEB_UID "$(id -u)")
|
||||
pi_web_gid=$(value_from_env_or_config_or_runtime_or_default PI_WEB_GID "$(id -g)")
|
||||
docker_gid=$(value_from_env_or_config_or_runtime_or_default DOCKER_GID "$(pi_web_docker_host_detect_docker_gid)")
|
||||
default_data_dir=${HOME:-$repo_root/.pi-web}/.local/share/pi-web-docker/data
|
||||
pi_web_data_dir=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DOCKER_DATA_DIR "$default_data_dir")
|
||||
pi_web_extra_host_paths=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DOCKER_EXTRA_HOST_PATHS "")
|
||||
pi_web_opensuse_image=$(value_from_env_or_config_or_runtime_or_default PI_WEB_OPENSUSE_IMAGE opensuse/tumbleweed)
|
||||
pi_web_nodejs_major=$(value_from_env_or_config_or_runtime_or_default PI_WEB_NODEJS_MAJOR 22)
|
||||
pi_web_nodejs_repo=$(value_from_env_or_config_or_runtime_or_default PI_WEB_NODEJS_REPO auto)
|
||||
pi_web_extra_zypper_packages=$(value_from_env_or_config_or_runtime_or_default PI_WEB_EXTRA_ZYPPER_PACKAGES "")
|
||||
pi_web_dev_image=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DEV_IMAGE pi-web:dev)
|
||||
hostexec_image=$(value_from_env_or_config_or_runtime_or_default HOSTEXEC_IMAGE alpine:3.22)
|
||||
pi_web_max_upload_bytes=$(value_from_env_or_config_or_runtime_or_default PI_WEB_MAX_UPLOAD_BYTES 67108864)
|
||||
default_dev_bind_addr=$(value_from_env_or_config_or_runtime_or_default PI_WEB_BIND_ADDR 127.0.0.1)
|
||||
pi_web_dev_api_bind_addr=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DEV_API_BIND_ADDR "$default_dev_bind_addr")
|
||||
pi_web_dev_bind_addr=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DEV_BIND_ADDR "$default_dev_bind_addr")
|
||||
pi_web_dev_api_port=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DEV_API_PORT 8504)
|
||||
pi_web_dev_port=$(value_from_env_or_config_or_runtime_or_default PI_WEB_DEV_PORT 8505)
|
||||
|
||||
mkdir -p "$pi_web_data_dir" || die "could not create data directory: $pi_web_data_dir"
|
||||
|
||||
env_file=$generated_env_file
|
||||
override_file=$repo_root/.pi-web/docker-compose-dev.host.generated.yml
|
||||
|
||||
if ! pi_web_docker_host_write_compose_override "$override_file" "$PI_WEB_DETECTED_DOCKER_HOST_PROFILE" "$pi_web_extra_host_paths"; then
|
||||
die "could not write host-specific Compose override"
|
||||
fi
|
||||
|
||||
umask 077
|
||||
temp_env=$env_file.$$
|
||||
cat >"$temp_env" <<EOF
|
||||
# Generated by docker/scripts/docker-compose-dev. Do not edit by hand.
|
||||
PI_WEB_UID=$pi_web_uid
|
||||
PI_WEB_GID=$pi_web_gid
|
||||
DOCKER_GID=$docker_gid
|
||||
PI_WEB_DOCKER_DATA_DIR=$pi_web_data_dir
|
||||
PI_WEB_DOCKER_HOST_PROFILE=$PI_WEB_DETECTED_DOCKER_HOST_PROFILE
|
||||
HOSTEXEC_MODE=$PI_WEB_DETECTED_HOSTEXEC_MODE
|
||||
PI_WEB_DOCKER_EXTRA_HOST_PATHS=$pi_web_extra_host_paths
|
||||
PI_WEB_OPENSUSE_IMAGE=$pi_web_opensuse_image
|
||||
PI_WEB_NODEJS_MAJOR=$pi_web_nodejs_major
|
||||
PI_WEB_NODEJS_REPO=$pi_web_nodejs_repo
|
||||
PI_WEB_EXTRA_ZYPPER_PACKAGES=$pi_web_extra_zypper_packages
|
||||
PI_WEB_DEV_IMAGE=$pi_web_dev_image
|
||||
HOSTEXEC_IMAGE=$hostexec_image
|
||||
PI_WEB_MAX_UPLOAD_BYTES=$pi_web_max_upload_bytes
|
||||
PI_WEB_DEV_API_BIND_ADDR=$pi_web_dev_api_bind_addr
|
||||
PI_WEB_DEV_BIND_ADDR=$pi_web_dev_bind_addr
|
||||
PI_WEB_DEV_API_PORT=$pi_web_dev_api_port
|
||||
PI_WEB_DEV_PORT=$pi_web_dev_port
|
||||
EOF
|
||||
mv "$temp_env" "$env_file"
|
||||
|
||||
log "Selected PI WEB Docker host profile: $PI_WEB_DETECTED_DOCKER_HOST_PROFILE"
|
||||
if [ -f "$runtime_env_file" ]; then
|
||||
log "Reused runtime Docker environment defaults from: $runtime_env_file"
|
||||
fi
|
||||
case "$PI_WEB_DETECTED_DOCKER_HOST_PROFILE" in
|
||||
linux-native-docker)
|
||||
log "Enabled Linux host mounts and hostexec namespace bridge."
|
||||
;;
|
||||
mac-docker-desktop)
|
||||
log "Enabled Docker Desktop for Mac project mounts. hostexec is disabled because containers cannot enter native macOS namespaces."
|
||||
;;
|
||||
esac
|
||||
log "User-editable dev config: $dev_config_file"
|
||||
log "Generated dev env: $env_file"
|
||||
log "Generated dev Compose override: $override_file"
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
set -- up --build
|
||||
fi
|
||||
|
||||
pi_web_docker_compose \
|
||||
--env-file "$env_file" \
|
||||
-f "$repo_root/docker/compose.dev.yml" \
|
||||
-f "$override_file" \
|
||||
"$@"
|
||||
Reference in New Issue
Block a user