Fix deployment script permission denied on marker files (#5272)

The saves directory is owned by root (created by Docker) but the deploy
script runs as the deploy user. Use docker exec to create marker files
from inside a running container that has the saves directory mounted.

- create_deployment_marker: uses active container (running before staging starts)
- create_flush_marker: uses staging container (running after active stops)
- cleanup_markers_on_failure: uses active container (still running on failure)
- remove_stale_deployment_marker: finds any running eagle container

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 07:02:05 -08:00
committed by GitHub
co-authored by Claude Opus 4.5
parent a3f185b6f6
commit e7745cc9ec
+46 -21
View File
@@ -46,6 +46,37 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Marker file operations use docker exec because saves directory is owned by root (Docker).
# We run commands inside a container that has the saves directory mounted.
create_deployment_marker() {
local deploy_id=$1
local container=$2 # Container to use for file operations
docker exec "${container}" rm -f /app/saves/.flush_complete
docker exec "${container}" sh -c "echo '${deploy_id}' > /app/saves/.deployment_in_progress"
}
create_flush_marker() {
local deploy_id=$1
local container=$2 # Container to use for file operations
docker exec "${container}" sh -c "echo '${deploy_id}' > /app/saves/.flush_complete"
docker exec "${container}" rm -f /app/saves/.deployment_in_progress
}
cleanup_markers_on_failure() {
local container=$1 # Container to use for file operations
docker exec "${container}" rm -f /app/saves/.deployment_in_progress 2>/dev/null || true
docker exec "${container}" touch /app/saves/.flush_complete 2>/dev/null || true
}
remove_stale_deployment_marker() {
# Try any running eagle container
local container
container=$(docker ps --filter "name=eagle-" --format "{{.Names}}" | head -1)
if [ -n "${container}" ]; then
docker exec "${container}" rm -f /app/saves/.deployment_in_progress 2>/dev/null || true
fi
}
# Determine which instance is currently running (not from nginx config)
get_running_instance() {
local blue_running green_running
@@ -154,13 +185,7 @@ main() {
cd "${APP_DIR}"
# Step 1: Signal deployment in progress
log_info "[DEPLOY:${deploy_id}] Step 1: Signaling deployment in progress..."
rm -f "${FLUSH_MARKER}"
echo "${deploy_id}" > "${DEPLOYMENT_IN_PROGRESS}"
log_info "[DEPLOY:${deploy_id}] Deployment marker created"
# Determine current active instance
# Determine current active instance (need this before creating marker)
local active=$(get_running_instance)
local staging
if [ "$active" = "blue" ] || [ "$active" = "none" ]; then
@@ -170,15 +195,20 @@ main() {
staging="blue"
fi
log_info "Active instance: eagle-${active}"
log_info "Staging instance: eagle-${staging}"
# Step 1: Signal deployment in progress
log_info "[DEPLOY:${deploy_id}] Step 1: Signaling deployment in progress..."
# Use active container for marker operations (it's the one currently running)
if [ "$active" != "none" ] && docker ps --filter "name=eagle-${active}" --format "{{.Names}}" | grep -q .; then
create_deployment_marker "${deploy_id}" "eagle-${active}"
log_info "[DEPLOY:${deploy_id}] Deployment marker created via eagle-${active}"
else
log_warn "[DEPLOY:${deploy_id}] No running container to create marker (first deploy?)"
fi
# Pull the new image (with retry for intermittent registry issues)
if ! pull_with_retry "${new_image}" 3; then
log_error "Failed to pull new image, aborting deployment"
# Clean up deployment markers on failure
rm -f "${DEPLOYMENT_IN_PROGRESS}"
touch "${FLUSH_MARKER}"
cleanup_markers_on_failure "eagle-${active}"
exit 1
fi
@@ -194,9 +224,7 @@ main() {
if ! wait_for_healthy "eagle-${staging}" 90; then
log_error "Staging instance failed health check, aborting deployment"
docker compose -f "${COMPOSE_FILE}" stop "eagle-${staging}"
# Clean up deployment markers on failure
rm -f "${DEPLOYMENT_IN_PROGRESS}"
touch "${FLUSH_MARKER}"
cleanup_markers_on_failure "eagle-${active}"
exit 1
fi
@@ -213,9 +241,7 @@ main() {
if ! "${WARMUP_SCRIPT}" "localhost:${staging_port}"; then
log_error "Warmup/smoke test failed, aborting deployment"
docker compose -f "${COMPOSE_FILE}" stop "eagle-${staging}"
# Clean up deployment markers on failure
rm -f "${DEPLOYMENT_IN_PROGRESS}"
touch "${FLUSH_MARKER}"
cleanup_markers_on_failure "eagle-${active}"
exit 1
fi
else
@@ -279,8 +305,7 @@ main() {
# Step 6: Create flush marker - signals that disk state is fresh
# Any waiting lazy-loads on staging will now proceed with fresh data.
log_info "[DEPLOY:${deploy_id}] Step 6: Creating flush marker..."
echo "${deploy_id}" > "${FLUSH_MARKER}"
rm -f "${DEPLOYMENT_IN_PROGRESS}"
create_flush_marker "${deploy_id}" "eagle-${staging}"
log_info "[DEPLOY:${deploy_id}] Flush marker created - waiting lazy-loads can now proceed"
# Update .env for admin service
@@ -416,7 +441,7 @@ check_requirements() {
# Clean up any stale deployment-in-progress marker from a previous failed deploy
if [ -f "${DEPLOYMENT_IN_PROGRESS}" ]; then
log_warn "Found stale deployment-in-progress marker, removing it"
rm -f "${DEPLOYMENT_IN_PROGRESS}"
remove_stale_deployment_marker
fi
}