Make restore and bootstrap work on clean server

This commit is contained in:
2026-06-08 10:33:50 +02:00
parent b5932f7d6c
commit bc23f979f3
2 changed files with 196 additions and 188 deletions
+100 -122
View File
@@ -4,20 +4,21 @@ set -euo pipefail
APPFACTORY_ROOT="/opt/appfactory"
ENV_FILE="$APPFACTORY_ROOT/config/appfactory.env"
PREFLIGHT_SCRIPT="$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance/preflight-check.sh"
DEPLOY_CORE_SCRIPT="$APPFACTORY_ROOT/workspace/appfactory-tools/scripts/deploy-core-service.sh"
CORE_SERVICES=(
"caddy"
"gitea"
"registry"
"portal"
"worker"
"monitor"
"webhook"
)
GENERATE_CADDY="$APPFACTORY_ROOT/workspace/appfactory-tools/scripts/generate-caddyfile.sh"
COMPOSE_FILE="$APPFACTORY_ROOT/deploy/docker-compose.yml"
MODE="${1:-}"
CORE_CONTAINERS=(
"appfactory-caddy"
"appfactory-gitea"
"appfactory-registry"
"appfactory-portal"
"appfactory-worker"
"appfactory-monitor"
"appfactory-webhook"
)
log(){ echo "[bootstrap-v2] $*"; }
ok(){ echo "[bootstrap-v2][OK] $*"; }
warn(){ echo "[bootstrap-v2][WARN] $*"; }
@@ -29,11 +30,6 @@ Usage:
bootstrap-v2.sh check
bootstrap-v2.sh repair
bootstrap-v2.sh deploy
Modes:
check - read-only validation, does not change filesystem or deploy services
repair - creates directories, repairs ownership/chmod, then runs preflight
deploy - repair + deploys core services + waits for containers + runs preflight
USAGE
}
@@ -42,24 +38,6 @@ require_file() {
[ -f "$path" ] || fail "Required file missing: $path"
}
load_env() {
require_file "$ENV_FILE"
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
[ -n "${APPFACTORY_UID:-}" ] || fail "APPFACTORY_UID missing in $ENV_FILE"
[ -n "${APPFACTORY_GID:-}" ] || fail "APPFACTORY_GID missing in $ENV_FILE"
[ -n "${APPFACTORY_DOCKER_GID:-}" ] || fail "APPFACTORY_DOCKER_GID missing in $ENV_FILE"
ok "Environment loaded"
ok "APPFACTORY_UID=$APPFACTORY_UID"
ok "APPFACTORY_GID=$APPFACTORY_GID"
ok "APPFACTORY_DOCKER_GID=$APPFACTORY_DOCKER_GID"
}
check_mode() {
case "$MODE" in
check|repair|deploy)
@@ -72,128 +50,127 @@ check_mode() {
esac
}
load_env() {
require_file "$ENV_FILE"
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
[ -n "${APPFACTORY_UID:-}" ] || fail "APPFACTORY_UID missing"
[ -n "${APPFACTORY_GID:-}" ] || fail "APPFACTORY_GID missing"
[ -n "${APPFACTORY_DOCKER_GID:-}" ] || fail "APPFACTORY_DOCKER_GID missing"
ok "Environment loaded"
ok "APPFACTORY_UID=$APPFACTORY_UID"
ok "APPFACTORY_GID=$APPFACTORY_GID"
ok "APPFACTORY_DOCKER_GID=$APPFACTORY_DOCKER_GID"
}
check_docker() {
command -v docker >/dev/null 2>&1 || fail "docker command missing"
command -v docker >/dev/null 2>&1 || fail "docker missing"
docker version >/dev/null 2>&1 || fail "docker daemon not available"
docker compose version >/dev/null 2>&1 || fail "docker compose not available"
docker compose version >/dev/null 2>&1 || fail "docker compose missing"
[ -S /var/run/docker.sock ] || fail "Docker socket missing: /var/run/docker.sock"
[ -S /var/run/docker.sock ] || fail "Docker socket missing"
local sock_gid
sock_gid="$(stat -c '%g' /var/run/docker.sock)"
if [ "$sock_gid" != "$APPFACTORY_DOCKER_GID" ]; then
fail "Docker socket GID mismatch. APPFACTORY_DOCKER_GID=$APPFACTORY_DOCKER_GID, /var/run/docker.sock gid=$sock_gid"
fi
[ "$sock_gid" = "$APPFACTORY_DOCKER_GID" ] || fail "Docker socket GID mismatch. env=$APPFACTORY_DOCKER_GID sock=$sock_gid"
docker ps >/dev/null 2>&1 || fail "docker ps failed"
ok "Docker ready"
}
check_directories_exist() {
ensure_directories() {
mkdir -p \
"$APPFACTORY_ROOT/apps" \
"$APPFACTORY_ROOT/backups" \
"$APPFACTORY_ROOT/config" \
"$APPFACTORY_ROOT/data" \
"$APPFACTORY_ROOT/deploy" \
"$APPFACTORY_ROOT/gateway" \
"$APPFACTORY_ROOT/services" \
"$APPFACTORY_ROOT/templates" \
"$APPFACTORY_ROOT/tools" \
"$APPFACTORY_ROOT/workspace"
ok "Directories ready"
}
check_directories() {
for dir in apps backups config data deploy gateway services templates tools workspace; do
path="$APPFACTORY_ROOT/$dir"
[ -d "$path" ] && ok "Directory exists: $path" || fail "Directory missing: $path"
done
}
ensure_directories() {
for dir in apps backups config data deploy gateway services templates tools workspace; do
path="$APPFACTORY_ROOT/$dir"
mkdir -p "$path"
ok "Directory ready: $path"
done
}
check_required_scripts() {
require_file "$PREFLIGHT_SCRIPT"
if [ "$MODE" = "deploy" ]; then
require_file "$DEPLOY_CORE_SCRIPT"
else
if [ -f "$DEPLOY_CORE_SCRIPT" ]; then
ok "Deploy core script exists: $DEPLOY_CORE_SCRIPT"
else
warn "Deploy core script missing: $DEPLOY_CORE_SCRIPT"
fi
fi
repair_ownership() {
log "Repairing ownership"
chown -R "$APPFACTORY_UID:$APPFACTORY_GID" "$APPFACTORY_ROOT"
ok "Ownership repaired"
}
check_ownership() {
for path in "$APPFACTORY_ROOT" "$APPFACTORY_ROOT/data" "$APPFACTORY_ROOT/workspace" "$APPFACTORY_ROOT/backups"; do
[ -e "$path" ] || fail "Path missing for ownership check: $path"
[ -e "$path" ] || fail "Path missing: $path"
actual_uid="$(stat -c '%u' "$path")"
actual_gid="$(stat -c '%g' "$path")"
if [ "$actual_uid" = "$APPFACTORY_UID" ] && [ "$actual_gid" = "$APPFACTORY_GID" ]; then
ok "Ownership OK: $path ($actual_uid:$actual_gid)"
ok "Ownership OK: $path"
else
fail "Ownership mismatch: $path is $actual_uid:$actual_gid, expected $APPFACTORY_UID:$APPFACTORY_GID"
fail "Ownership mismatch: $path is $actual_uid:$actual_gid expected $APPFACTORY_UID:$APPFACTORY_GID"
fi
done
}
repair_ownership() {
log "Repairing ownership for $APPFACTORY_ROOT to $APPFACTORY_UID:$APPFACTORY_GID"
chown -R "$APPFACTORY_UID:$APPFACTORY_GID" "$APPFACTORY_ROOT"
ok "Ownership repaired"
}
ensure_scripts_executable() {
if [ -d "$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance" ]; then
find "$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance" -maxdepth 1 -type f -name "*.sh" -exec chmod +x {} \;
ok "Maintenance scripts executable"
else
warn "Maintenance scripts directory missing"
fi
if [ -d "$APPFACTORY_ROOT/workspace/appfactory-tools/alerts" ]; then
find "$APPFACTORY_ROOT/workspace/appfactory-tools/alerts" -maxdepth 1 -type f -name "*.sh" -exec chmod +x {} \;
ok "Alert scripts executable"
else
warn "Alert scripts directory missing"
fi
if [ -f "$DEPLOY_CORE_SCRIPT" ]; then
chmod +x "$DEPLOY_CORE_SCRIPT"
ok "Deploy core script executable"
fi
find "$APPFACTORY_ROOT/workspace/appfactory-tools" -type f -name '*.sh' -exec chmod +x {} \; 2>/dev/null || true
ok "Scripts executable"
}
deploy_core_services() {
require_file "$DEPLOY_CORE_SCRIPT"
build_image() {
local image="$1"
local dir="$2"
for service in "${CORE_SERVICES[@]}"; do
log "Deploying core service: $service"
[ -d "$dir" ] || fail "Missing build directory: $dir"
[ -f "$dir/Dockerfile" ] || fail "Missing Dockerfile: $dir/Dockerfile"
if "$DEPLOY_CORE_SCRIPT" "$service"; then
ok "Deploy requested: $service"
else
fail "Deploy failed for core service: $service"
fi
done
log "Building image: $image"
docker build -t "$image:latest" "$dir"
}
deploy_core_stack() {
require_file "$COMPOSE_FILE"
[ -f "$APPFACTORY_ROOT/data/appfactory/appfactory.db" ] || fail "Missing AppFactory DB"
if [ -f "$GENERATE_CADDY" ]; then
chmod +x "$GENERATE_CADDY"
"$GENERATE_CADDY" || true
fi
build_image "appfactory-portal" "$APPFACTORY_ROOT/workspace/appfactory-portal"
build_image "appfactory-worker" "$APPFACTORY_ROOT/workspace/appfactory-worker"
build_image "appfactory-monitor" "$APPFACTORY_ROOT/workspace/appfactory-monitor"
build_image "appfactory-webhook" "$APPFACTORY_ROOT/workspace/appfactory-webhook"
log "Deploying compose stack"
docker compose \
--env-file "$ENV_FILE" \
-f "$COMPOSE_FILE" \
up -d --force-recreate
ok "Core compose stack deployed"
}
wait_for_containers() {
log "Waiting for core containers"
local container_names=(
"appfactory-caddy"
"appfactory-gitea"
"appfactory-registry"
"appfactory-portal"
"appfactory-worker"
"appfactory-monitor"
"appfactory-webhook"
)
local deadline
deadline=$((SECONDS + 180))
for name in "${container_names[@]}"; do
for name in "${CORE_CONTAINERS[@]}"; do
log "Waiting for container: $name"
while true; do
@@ -208,7 +185,7 @@ wait_for_containers() {
fi
if [ "$SECONDS" -ge "$deadline" ]; then
docker ps --format "table {{.Names}}\t{{.Status}}" || true
docker ps -a
fail "Timeout waiting for container: $name"
fi
@@ -220,8 +197,6 @@ wait_for_containers() {
run_preflight() {
require_file "$PREFLIGHT_SCRIPT"
chmod +x "$PREFLIGHT_SCRIPT"
log "Running preflight"
"$PREFLIGHT_SCRIPT"
}
@@ -233,10 +208,9 @@ main() {
check_mode
load_env
check_docker
check_required_scripts
if [ "$MODE" = "check" ]; then
check_directories_exist
check_directories
check_ownership
run_preflight
echo
@@ -249,14 +223,18 @@ main() {
ensure_scripts_executable
if [ "$MODE" = "repair" ]; then
run_preflight
echo
echo "APPFACTORY BOOTSTRAP REPAIR: READY"
exit 0
fi
deploy_core_services
deploy_core_stack
wait_for_containers
if [ -f "$GENERATE_CADDY" ]; then
"$GENERATE_CADDY"
fi
run_preflight
echo