diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f8153..58b465d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.30.2 - 2026-05-17 + +- Added container runtime support for `USER_UID` and `USER_GID`, letting `ani-cli-web` run under a chosen numeric host user/group so files created in mounted folders are not owned by `root`. +- Updated the Docker and Compose documentation to show how to pass host UID/GID values into the container. + ## 0.30.1 - 2026-05-17 - Added a `docker-compose.yaml` file that builds the bundled Docker image, mounts `./downloads` and `./.ani-cli-web`, and exposes the ani-cli git remotes, branch, and startup flags through Compose environment defaults. diff --git a/Dockerfile b/Dockerfile index f426d18..27b45cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,9 @@ ENV DEBIAN_FRONTEND=noninteractive \ ANI_CLI_DOWNLOAD_DIR=/downloads \ ANI_CLI_WEB_HOST=0.0.0.0 \ ANI_CLI_WEB_PORT=8421 \ - UPDATE_ON_START=false + UPDATE_ON_START=false \ + USER_UID= \ + USER_GID= RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -27,6 +29,7 @@ RUN apt-get update \ patch \ sed \ sudo \ + util-linux \ && rm -rf /var/lib/apt/lists/* RUN git clone --depth 1 --branch "${ANI_CLI_BRANCH}" "${ANI_CLI}" /tmp/ani-cli-src \ diff --git a/README.md b/README.md index bcbf100..4106fc0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A local web UI for a system-wide `ani-cli` install. -Current version: `0.30.1` +Current version: `0.30.2` ## Project Layout @@ -87,6 +87,8 @@ Run the container with the requested persistent mounts: docker run --rm \ -p 8421:8421 \ -e UPDATE_ON_START=true \ + -e USER_UID="$(id -u)" \ + -e USER_GID="$(id -g)" \ -v "$(pwd)/downloads:/downloads" \ -v "$(pwd)/.ani-cli-web:/app/.ani-cli-web" \ ani-cli-web @@ -98,6 +100,12 @@ Or start the same setup with Compose: docker compose up --build -d ``` +To keep mounted downloads owned by your host user, start Compose with: + +```sh +USER_UID="$(id -u)" USER_GID="$(id -g)" docker compose up --build -d +``` + The included `docker-compose.yaml` uses the same defaults as the `Dockerfile`, mounts `./downloads` to `/downloads`, mounts `./.ani-cli-web` to `/app/.ani-cli-web`, and lets you override build/runtime settings through environment variables such as: ```text @@ -106,6 +114,8 @@ ANI_CLI_BRANCH=master ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git ANI_CLI_WEB_PORT=8421 UPDATE_ON_START=false +USER_UID= +USER_GID= ANI_CLI_WEB_ALLOW_REMOTE=false ANI_CLI_WEB_REMOTE_TOKEN= ``` @@ -118,6 +128,7 @@ Container defaults: - App state stays under `/app/.ani-cli-web`, so bind-mount `./.ani-cli-web:/app/.ani-cli-web`. - The image sets `ANI_CLI_WEB_HOST=0.0.0.0` and exposes port `8421`. - `UPDATE_ON_START=true` runs `sudo ani-cli --update` before the web app starts. +- Set `USER_UID` and `USER_GID` together to run the web app as a specific numeric host user/group, which keeps new files on mounted folders such as `./downloads` from being owned by `root`. The installed `ani-cli` update URL is rewritten during the image build so `ani-cli --update` follows the same `ANI_CLI_BRANCH` you built into the image when the `ANI_CLI` remote is a GitHub repository. @@ -364,6 +375,7 @@ Environment variables: - `ANI_CLI_WEB_DEBUG`: enable debug logging when set to `1`, `true`, `yes`, or `on`. - `ANI_CLI_WEB_STATE_ROOT`: override the project-local state directory path; useful for isolated test runs or custom storage locations. - `UPDATE_ON_START`: when set to `1`, `true`, `yes`, or `on` in the container, runs `sudo ani-cli --update` before `ani-cli-web` starts. +- `USER_UID` and `USER_GID`: optional numeric UID/GID pair for running the container process as a specific host user/group so mounted files are created with that ownership. Saved defaults from the UI are written into the project-local `config.json`. diff --git a/VERSION b/VERSION index 1a44cad..0f72177 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.30.1 +0.30.2 diff --git a/app_support.py b/app_support.py index 6f83c1a..0efffef 100644 --- a/app_support.py +++ b/app_support.py @@ -16,7 +16,7 @@ from uuid import uuid4 PROJECT_ROOT = Path(__file__).resolve().parent ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli" APP_NAME = "ani-cli-web" -VERSION = "0.30.1" +VERSION = "0.30.2" ALLANIME_BASE = "allanime.day" ALLANIME_API = f"https://api.{ALLANIME_BASE}" ALLANIME_REFERER = "https://allmanga.to" diff --git a/docker-compose.yaml b/docker-compose.yaml index eeced11..a77ed44 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,6 +13,8 @@ services: - "${ANI_CLI_WEB_PORT:-8421}:8421" environment: UPDATE_ON_START: ${UPDATE_ON_START:-false} + USER_UID: ${USER_UID:-} + USER_GID: ${USER_GID:-} ANI_CLI_WEB_HOST: 0.0.0.0 ANI_CLI_WEB_PORT: ${ANI_CLI_WEB_PORT:-8421} ANI_CLI_WEB_ALLOW_REMOTE: ${ANI_CLI_WEB_ALLOW_REMOTE:-false} diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 1b9f7ff..2d58ae6 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -8,6 +8,42 @@ is_true() { esac } +is_integer() { + case "${1:-}" in + '' | *[!0-9]*) return 1 ;; + *) return 0 ;; + esac +} + +run_as_requested_user() { + uid="${USER_UID:-}" + gid="${USER_GID:-}" + + if [ -z "$uid" ] && [ -z "$gid" ]; then + return 1 + fi + + if [ -z "$uid" ] || [ -z "$gid" ]; then + echo "Both USER_UID and USER_GID must be set together." >&2 + exit 1 + fi + + if ! is_integer "$uid" || ! is_integer "$gid"; then + echo "USER_UID and USER_GID must be numeric." >&2 + exit 1 + fi + + mkdir -p /app/.ani-cli-web/home + chown "$uid:$gid" /downloads /app/.ani-cli-web /app/.ani-cli-web/home + + cd /app + exec setpriv \ + --reuid "$uid" \ + --regid "$gid" \ + --clear-groups \ + env HOME=/app/.ani-cli-web/home ./ani-cli-web "$@" +} + mkdir -p /downloads /app/.ani-cli-web if is_true "${UPDATE_ON_START:-false}"; then @@ -15,5 +51,7 @@ if is_true "${UPDATE_ON_START:-false}"; then sudo ani-cli --update fi +run_as_requested_user "$@" || true + cd /app exec ./ani-cli-web "$@"