From 48efe840eb40679945fda0012ed10e7677e6d3b2 Mon Sep 17 00:00:00 2001 From: Dymas Date: Sun, 17 May 2026 11:26:40 +0200 Subject: [PATCH] Add Docker image workflow for ani-cli-web --- .dockerignore | 12 ++++++++++ CHANGELOG.md | 6 +++++ Dockerfile | 53 +++++++++++++++++++++++++++++++++++++++++ README.md | 56 +++++++++++++++++++++++++++++++++++++++++++- VERSION | 2 +- app_support.py | 2 +- docker-entrypoint.sh | 19 +++++++++++++++ 7 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..adc4cf0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.ani-cli-web +downloads +__pycache__ +**/__pycache__/ +*.py[cod] +*.pyo +.python-version +.venv +venv +env +*.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fbee00..cc2efc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.30.0 - 2026-05-17 + +- Added a Docker image build that clones `ani-cli` and `ani-cli-web` from configurable git remotes, installs `ani-cli` system-wide, and starts `ani-cli-web` from `/app`. +- Added container startup support for `UPDATE_ON_START=true`, which runs `sudo ani-cli --update` before launching the web app. +- Documented the Docker build arguments, runtime mounts for `./downloads` and `./.ani-cli-web`, and the default container networking and storage layout. + ## 0.29.5 - 2026-05-16 - Fixed `/api/config` malformed-body handling so non-object JSON payloads now return `400 Bad Request` instead of silently resetting saved settings to defaults. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f426d18 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +FROM python:3.12-slim + +ARG ANI_CLI=https://github.com/pystardust/ani-cli.git +ARG ANI_CLI_BRANCH=master +ARG ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git + +ENV DEBIAN_FRONTEND=noninteractive \ + ANI_CLI_REPO=${ANI_CLI} \ + ANI_CLI_BRANCH=${ANI_CLI_BRANCH} \ + ANI_CLI_WEB_REPO=${ANI_CLI_WEB} \ + ANI_CLI_BIN=ani-cli \ + ANI_CLI_DOWNLOAD_DIR=/downloads \ + ANI_CLI_WEB_HOST=0.0.0.0 \ + ANI_CLI_WEB_PORT=8421 \ + UPDATE_ON_START=false + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + aria2 \ + ca-certificates \ + curl \ + ffmpeg \ + fzf \ + git \ + grep \ + openssl \ + patch \ + sed \ + sudo \ + && rm -rf /var/lib/apt/lists/* + +RUN git clone --depth 1 --branch "${ANI_CLI_BRANCH}" "${ANI_CLI}" /tmp/ani-cli-src \ + && install -m 0755 /tmp/ani-cli-src/ani-cli /usr/local/bin/ani-cli \ + && ani_cli_raw_repo="$(printf '%s' "${ANI_CLI}" | sed -E 's#^https://github.com/##; s#\\.git$##')" \ + && case "${ANI_CLI}" in \ + https://github.com/*) \ + sed -i "s|https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli|https://raw.githubusercontent.com/${ani_cli_raw_repo}/${ANI_CLI_BRANCH}/ani-cli|" /usr/local/bin/ani-cli ;; \ + esac \ + && rm -rf /tmp/ani-cli-src + +RUN git clone --depth 1 "${ANI_CLI_WEB}" /app \ + && rm -rf /app/.git + +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN chmod 0755 /usr/local/bin/docker-entrypoint.sh \ + && mkdir -p /downloads /app/.ani-cli-web + +WORKDIR /app + +EXPOSE 8421 + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/README.md b/README.md index e2b27c0..3402c64 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.29.5` +Current version: `0.30.0` ## Project Layout @@ -10,11 +10,13 @@ Current version: `0.29.5` ani-cli-web/ app.py Public entrypoint, runtime wiring, watchlist domain logic, and CLI startup. app_support.py Shared runtime/state/config helpers plus queue and library file utilities. + Dockerfile Container image build for system-wide ani-cli plus /app ani-cli-web. queue_jobs.py Download queue and background watchlist-refresh workers. watchlist_identity.py Safer watchlist identity resolution for queued download jobs. title_matching.py Shared anime-title normalization and matching helpers. http_handler.py HTTP route handling, request parsing, and remote-access guard. web_templates.py Shared Search, Config, and Watchlist page markup aggregation. + docker-entrypoint.sh Container startup wrapper with optional ani-cli self-update. search_page.py Search page template. config_page.py Config page template. watchlist_page.py Watchlist page template. @@ -50,6 +52,56 @@ http://127.0.0.1:8421/ By default the server binds to `127.0.0.1:8421`. +## Docker + +The container image installs `ani-cli` system-wide and clones `ani-cli-web` into `/app` during `docker build`. + +Default build arguments: + +```text +ANI_CLI=https://github.com/pystardust/ani-cli.git +ANI_CLI_BRANCH=master +ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git +``` + +Build the image: + +```sh +docker build -t ani-cli-web . +``` + +Override the `ani-cli` branch when needed: + +```sh +docker build \ + --build-arg ANI_CLI_BRANCH=my-feature-branch \ + --build-arg ANI_CLI=https://github.com/pystardust/ani-cli.git \ + --build-arg ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git \ + -t ani-cli-web . +``` + +Run the container with the requested persistent mounts: + +```sh +docker run --rm \ + -p 8421:8421 \ + -e UPDATE_ON_START=true \ + -v "$(pwd)/downloads:/downloads" \ + -v "$(pwd)/.ani-cli-web:/app/.ani-cli-web" \ + ani-cli-web +``` + +Container defaults: + +- `ani-cli` is installed at `/usr/local/bin/ani-cli`. +- `ani-cli-web` is cloned into `/app` and started from there. +- Downloads are written to `/downloads`, so bind-mount `./downloads:/downloads`. +- 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. + +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. + ## Overview `ani-cli-web` keeps search and download management on `/`, saved defaults on `/config`, and the watchlist on `/watchlist`, all with the same sidebar navigation layout and a Tsuki-inspired visual style. @@ -292,6 +344,7 @@ Environment variables: - `ANI_CLI_WEB_REMOTE_TOKEN`: required shared secret for non-loopback clients when remote access is enabled. - `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. Saved defaults from the UI are written into the project-local `config.json`. @@ -303,4 +356,5 @@ Saved defaults from the UI are written into the project-local `config.json`. - The CLI startup path itself also stays lazy now; runtime bootstrap happens when the first runtime-backed request arrives. - Watchlist and queue data are stored in the same project-local SQLite database file, `state.sqlite3`. - Runtime folders such as `.ani-cli-web/`, `downloads/`, and Python bytecode cache directories at any depth are ignored by git. +- The Docker image clones both upstream repositories during build, so the app source inside the container comes from the configured git remotes rather than the local build context. - Focused regression tests live in `test_app.py`, run against an isolated temporary state root, and can be started with `python3 -m unittest test_app.py`. diff --git a/VERSION b/VERSION index 88f8ee8..c25c8e5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.29.5 +0.30.0 diff --git a/app_support.py b/app_support.py index fc4157d..9affe74 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.29.5" +VERSION = "0.30.0" ALLANIME_BASE = "allanime.day" ALLANIME_API = f"https://api.{ALLANIME_BASE}" ALLANIME_REFERER = "https://allmanga.to" diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..1b9f7ff --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +is_true() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + 1 | true | yes | on) return 0 ;; + *) return 1 ;; + esac +} + +mkdir -p /downloads /app/.ani-cli-web + +if is_true "${UPDATE_ON_START:-false}"; then + echo "Checking for ani-cli updates with sudo ani-cli --update" + sudo ani-cli --update +fi + +cd /app +exec ./ani-cli-web "$@"