From 95474c3ac551b5b07395b035f60deaf96368b2d0 Mon Sep 17 00:00:00 2001 From: anoracleofra-code Date: Sat, 14 Mar 2026 14:34:11 -0600 Subject: [PATCH] fix: updater resolves project_root to / in Docker containers In Docker, main.py lives at /app/main.py so Path.parent.parent resolves to filesystem root /, causing PermissionError on .github and other dirs. Now detects this case and falls back to cwd. Also grants backenduser write access to /app for auto-update. Co-Authored-By: Claude Opus 4.6 Former-commit-id: 12c8bb5816a70161d5ab5d79f9240e7eab6e6e15 --- backend/Dockerfile | 4 +++- backend/main.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index f7bfa8c..fe7a5fe 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -23,8 +23,10 @@ RUN npm ci --omit=dev COPY . . # Create a non-root user for security +# Grant write access to /app so the auto-updater can extract files RUN adduser --system --uid 1001 backenduser \ - && chown -R backenduser /app + && chown -R backenduser /app \ + && chmod -R u+w /app # Switch to the non-root user USER backenduser diff --git a/backend/main.py b/backend/main.py index 3c3bdc1..42c1e49 100644 --- a/backend/main.py +++ b/backend/main.py @@ -498,7 +498,14 @@ from services.updater import perform_update, schedule_restart @limiter.limit("1/minute") async def system_update(request: Request): """Download latest release, backup current files, extract update, and restart.""" - project_root = str(Path(__file__).resolve().parent.parent) + # In Docker, __file__ is /app/main.py so .parent.parent resolves to / + # which causes PermissionError. Use cwd as fallback when parent.parent + # doesn't contain frontend/ or backend/ (i.e. we're already at project root). + candidate = Path(__file__).resolve().parent.parent + if (candidate / "frontend").is_dir() or (candidate / "backend").is_dir(): + project_root = str(candidate) + else: + project_root = os.getcwd() result = perform_update(project_root) if result.get("status") == "error": return Response(