#!/bin/bash # Normalize permissions across storage/ and local-storage/ so host processes # (and CI tools) can read/write module metadata without manual chown. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" ENV_FILE="$PROJECT_ROOT/.env" TEMPLATE_FILE="$PROJECT_ROOT/.env.template" usage(){ cat <<'EOF' Usage: repair-storage-permissions.sh [options] Ensures common storage directories are writable by the current host user. Options: --path Additional directory to fix (can be passed multiple times) --silent Reduce output (only errors/warnings) -h, --help Show this help message EOF } read_env(){ local key="$1" default="$2" env_path="$ENV_FILE" value="" if [ -f "$env_path" ]; then value="$(grep -E "^${key}=" "$env_path" | tail -n1 | cut -d'=' -f2- | tr -d '\r')" fi if [ -z "$value" ] && [ -f "$TEMPLATE_FILE" ]; then value="$(grep -E "^${key}=" "$TEMPLATE_FILE" | tail -n1 | cut -d'=' -f2- | tr -d '\r')" fi if [ -z "$value" ]; then value="$default" fi printf '%s\n' "$value" } silent=0 declare -a extra_paths=() while [ $# -gt 0 ]; do case "$1" in --path) shift [ $# -gt 0 ] || { echo "Missing value for --path" >&2; exit 1; } extra_paths+=("$1") ;; --silent) silent=1 ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" >&2 usage exit 1 ;; esac shift done log(){ [ "$silent" -eq 1 ] || echo "$*"; } warn(){ echo "⚠️ $*" >&2; } resolve_path(){ local path="$1" if [[ "$path" != /* ]]; then path="${path#./}" path="$PROJECT_ROOT/$path" fi printf '%s\n' "$(cd "$(dirname "$path")" 2>/dev/null && pwd 2>/dev/null)/$(basename "$path")" } ensure_host_writable(){ local target="$1" [ -n "$target" ] || return 0 mkdir -p "$target" 2>/dev/null || true [ -d "$target" ] || { warn "Path not found: $target"; return 0; } local uid gid uid="$(id -u)" gid="$(id -g)" if chown -R "$uid":"$gid" "$target" 2>/dev/null; then : elif command -v docker >/dev/null 2>&1; then local helper_image helper_image="$(read_env ALPINE_IMAGE "alpine:latest")" if ! docker run --rm -u 0:0 -v "$target":/workspace "$helper_image" \ sh -c "chown -R ${uid}:${gid} /workspace" >/dev/null 2>&1; then warn "Failed to adjust ownership for $target" return 1 fi else warn "Cannot adjust ownership for $target (docker unavailable)" return 1 fi chmod -R ug+rwX "$target" 2>/dev/null || true return 0 } STORAGE_PATH="$(read_env STORAGE_PATH "./storage")" LOCAL_STORAGE_PATH="$(read_env STORAGE_PATH_LOCAL "./local-storage")" declare -a targets=( "$STORAGE_PATH" "$STORAGE_PATH/modules" "$STORAGE_PATH/modules/.modules-meta" "$STORAGE_PATH/backups" "$STORAGE_PATH/logs" "$STORAGE_PATH/lua_scripts" "$STORAGE_PATH/install-markers" "$STORAGE_PATH/client-data" "$STORAGE_PATH/config" "$LOCAL_STORAGE_PATH" "$LOCAL_STORAGE_PATH/modules" "$LOCAL_STORAGE_PATH/client-data-cache" "$LOCAL_STORAGE_PATH/source" "$LOCAL_STORAGE_PATH/images" ) targets+=("${extra_paths[@]}") declare -A seen=() for raw in "${targets[@]}"; do [ -n "$raw" ] || continue resolved="$(resolve_path "$raw")" if [ -n "${seen[$resolved]:-}" ]; then continue fi seen["$resolved"]=1 log "🔧 Fixing permissions for $resolved" ensure_host_writable "$resolved" done log "✅ Storage permissions refreshed"