From db8b9f35609daab6193f71c7f48ba9a78fd46acf Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 10 Aug 2026 11:15:22 +0200 Subject: [PATCH] Harden Docker ownership startup --- CHANGELOG.md | 5 +++++ README.md | 2 ++ VERSION | 2 +- docker-entrypoint.sh | 19 +++++++++++-------- test_app.py | 4 +++- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d14858..08d4d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.52.10 - 2026-08-10 + +- Made Docker UID/GID startup ownership fixes best-effort per mount so read-only or policy-managed Jellyfin bind mounts do not prevent Kaizoku from starting. +- Removed an unused Docker entrypoint helper. + ## 0.52.9 - 2026-08-10 - Removed the unused legacy AllManga/AllAnime GraphQL metadata helper and constants from active code. diff --git a/README.md b/README.md index 1ae6ec4..fc5aee8 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ KAIZOKU_ALLOW_REMOTE=1 KAIZOKU_AUTH_USERNAME=admin KAIZOKU_AUTH_PASSWORD='change USER_UID=$(id -u) USER_GID=$(id -g) docker compose up -d ``` +When `USER_UID` and `USER_GID` are set, the entrypoint attempts to adjust ownership for `/downloads`, `/jellyfin/tv`, `/jellyfin/movies`, and Kaizoku state paths. Ownership changes are best-effort so policy-managed or read-only Jellyfin mounts can still be browsed and used when their existing permissions allow it. + Keep `./.kaizoku` mounted for production instances. That directory contains the SQLite database, configuration, queue history, watchlist data, thumbnail cache, remote sessions, and temporary staging files. Removing the mount starts Kaizoku with a fresh empty state. ## Download Flow diff --git a/VERSION b/VERSION index e26415d..ec45219 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.52.9 +0.52.10 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 910a61d..c1f2a4b 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,13 +1,6 @@ #!/bin/sh set -eu -is_true() { - case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in - 1 | true | yes | on) return 0 ;; - *) return 1 ;; - esac -} - is_integer() { case "${1:-}" in '' | *[!0-9]*) return 1 ;; @@ -15,6 +8,14 @@ is_integer() { esac } +chown_path() { + owner="$1" + path="$2" + if ! chown "$owner" "$path"; then + echo "Warning: could not change ownership of $path to $owner; continuing." >&2 + fi +} + run_as_requested_user() { uid="${USER_UID:-}" gid="${USER_GID:-}" @@ -34,7 +35,9 @@ run_as_requested_user() { fi mkdir -p /app/.kaizoku/home - chown "$uid:$gid" /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home + for path in /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home; do + chown_path "$uid:$gid" "$path" + done cd /app exec setpriv \ diff --git a/test_app.py b/test_app.py index 5f2048b..d333778 100644 --- a/test_app.py +++ b/test_app.py @@ -4088,7 +4088,9 @@ class TemplateHelperTests(unittest.TestCase): self.assertIn("${JELLYFIN_TV_DIR:-./jellyfin/tv}:/jellyfin/tv", compose) self.assertIn("${JELLYFIN_MOVIE_DIR:-./jellyfin/movies}:/jellyfin/movies", compose) self.assertIn("mkdir -p /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku", dockerfile) - self.assertIn("chown \"$uid:$gid\" /downloads /jellyfin/tv /jellyfin/movies", entrypoint) + self.assertIn("chown_path()", entrypoint) + self.assertIn("Warning: could not change ownership", entrypoint) + self.assertIn("for path in /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home", entrypoint) def test_active_code_does_not_reference_retired_metadata_source(self): active_files = ("app.py", "app_support.py", "http_handler.py", "provider_bridge.py", "provider_downloader.py")