From 7d943a7a19c84f4263ea5bad32e535b596e1700d Mon Sep 17 00:00:00 2001 From: MrSphay Date: Fri, 5 Jun 2026 12:57:17 +0200 Subject: [PATCH] Add Gitea container image workflow --- .dockerignore | 2 + .gitea/workflows/container-image.yml | 71 +++++++++++++ docker-compose.dockge.yml | 145 +++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 .gitea/workflows/container-image.yml create mode 100644 docker-compose.dockge.yml diff --git a/.dockerignore b/.dockerignore index aed7e93..80d80a1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -13,6 +13,8 @@ build/ /data/ /logs/ .git/ +.gitea/ +.github/ .claude/ .playwright-mcp/ .pytest_cache/ diff --git a/.gitea/workflows/container-image.yml b/.gitea/workflows/container-image.yml new file mode 100644 index 0000000..6c01562 --- /dev/null +++ b/.gitea/workflows/container-image.yml @@ -0,0 +1,71 @@ +name: Container Image + +on: + push: + branches: + - dev + - main + - master + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: container-image-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-push: + runs-on: ubuntu-latest + env: + REGISTRY: git.wilkensxl.de + IMAGE_NAME: odysseus + FALLBACK_OWNER: mrsphay + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + + - name: Check Docker + run: docker version + + - name: Build and push image + shell: bash + run: | + set -euo pipefail + + registry_token="${REGISTRY_TOKEN:-${GITEA_TOKEN:-}}" + if [ -z "${registry_token}" ]; then + echo "REGISTRY_TOKEN or GITEA_TOKEN is required to publish ${REGISTRY}/${FALLBACK_OWNER}/${IMAGE_NAME}." + exit 1 + fi + + owner="${GITHUB_REPOSITORY_OWNER:-${FALLBACK_OWNER}}" + owner="$(printf '%s' "${owner}" | tr '[:upper:]' '[:lower:]')" + registry_user="${GITHUB_ACTOR:-${owner}}" + image="${REGISTRY}/${owner}/${IMAGE_NAME}" + short_sha="$(printf '%s' "${GITHUB_SHA}" | cut -c1-7)" + ref_name="${GITHUB_REF_NAME:-dev}" + ref_tag="$(printf '%s' "${ref_name}" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9._-' '-' | sed 's/^-//; s/-$//')" + + echo "${registry_token}" | docker login "${REGISTRY}" --username "${registry_user}" --password-stdin + + docker build --pull \ + --tag "${image}:sha-${short_sha}" \ + --tag "${image}:${ref_tag}" \ + --tag "${image}:latest" \ + . + + docker push "${image}:sha-${short_sha}" + docker push "${image}:${ref_tag}" + docker push "${image}:latest" + + { + echo "Published image tags:" + echo "- ${image}:latest" + echo "- ${image}:${ref_tag}" + echo "- ${image}:sha-${short_sha}" + } >> "${GITHUB_STEP_SUMMARY}" diff --git a/docker-compose.dockge.yml b/docker-compose.dockge.yml new file mode 100644 index 0000000..2bbe390 --- /dev/null +++ b/docker-compose.dockge.yml @@ -0,0 +1,145 @@ +services: + odysseus: + image: ${ODYSSEUS_IMAGE:-git.wilkensxl.de/mrsphay/odysseus:latest} + ports: + - "${APP_BIND:-127.0.0.1}:${APP_PORT:-7000}:7000" + volumes: + - ./data:/app/data:z + - ./logs:/app/logs:z + # Cookbook remote-server SSH identity. Odysseus can generate a key here; + # add the shown public key to each remote server's authorized_keys. + - ./data/ssh:/app/.ssh:z + # Cookbook local model cache. Inside Docker, "Local" means the Odysseus + # container, so persist its HuggingFace cache under ./data/huggingface. + - ./data/huggingface:/app/.cache/huggingface:z + # Cookbook-installed Python CLIs/packages (vLLM, llama-cpp-python, etc.) + # land under /app/.local for the odysseus user. Persist them so a + # container recreate does not silently remove installed serve engines. + - ./data/local:/app/.local:z + extra_hosts: + # Lets the container reach local services on the Docker host, including + # Ollama at http://host.docker.internal:11434. + - "host.docker.internal:host-gateway" + environment: + - LLM_HOST=${LLM_HOST:-localhost} + - LLM_HOSTS=${LLM_HOSTS:-} + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-} + - RESEARCH_LLM_ENDPOINT=${RESEARCH_LLM_ENDPOINT:-} + - HF_TOKEN=${HF_TOKEN:-} + - HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN:-} + - SEARXNG_INSTANCE=http://searxng:8080 + - CHROMADB_HOST=chromadb + - CHROMADB_PORT=8000 + - DATABASE_URL=${DATABASE_URL:-sqlite:///./data/app.db} + - AUTH_ENABLED=${AUTH_ENABLED:-true} + - LOCALHOST_BYPASS=${LOCALHOST_BYPASS:-false} + - ODYSSEUS_ADMIN_USER=${ODYSSEUS_ADMIN_USER:-admin} + - ODYSSEUS_ADMIN_PASSWORD=${ODYSSEUS_ADMIN_PASSWORD:-} + - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost,http://127.0.0.1} + - SECURE_COOKIES=${SECURE_COOKIES:-false} + - EMBEDDING_URL=${EMBEDDING_URL:-} + - EMBEDDING_MODEL=${EMBEDDING_MODEL:-} + - FASTEMBED_MODEL=${FASTEMBED_MODEL:-sentence-transformers/all-MiniLM-L6-v2} + - FASTEMBED_CACHE_PATH=${FASTEMBED_CACHE_PATH:-} + - CLEANUP_INTERVAL_HOURS=${CLEANUP_INTERVAL_HOURS:-24} + - ODYSSEUS_INPROCESS_POLLERS=${ODYSSEUS_INPROCESS_POLLERS:-1} + - ODYSSEUS_INPROCESS_TASKS=${ODYSSEUS_INPROCESS_TASKS:-1} + - ODYSSEUS_SCRIPT_HOST=${ODYSSEUS_SCRIPT_HOST:-localhost} + - DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-} + - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} + - GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-} + - TAVILY_API_KEY=${TAVILY_API_KEY:-} + - SERPER_API_KEY=${SERPER_API_KEY:-} + # PUID / PGID — the user/group the container drops to before + # running uvicorn (entrypoint also chowns /app/data + /app/logs + # to match, so bind-mounted files stay editable from the host). + # 1000 is the default first user on most Linux installs. If your + # host user has a different id, override here or via .env, e.g.: + # PUID=1001 + # PGID=1001 + # Find yours with: id -u / id -g + - PUID=${PUID:-1000} + - PGID=${PGID:-1000} + depends_on: + searxng: + condition: service_healthy + chromadb: + condition: service_started + restart: unless-stopped + + chromadb: + image: docker.io/chromadb/chroma:latest + ports: + - "${CHROMADB_BIND:-127.0.0.1}:8100:8000" + volumes: + - chromadb-data:/chroma/chroma + environment: + - ANONYMIZED_TELEMETRY=FALSE + restart: unless-stopped + + searxng: + # Pinned, not :latest — odysseus waits on searxng's healthcheck + # (depends_on: condition: service_healthy), so a broken upstream `latest` + # tag blocks the whole app from starting. 2026.6.2 crashes on boot with + # `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414). + # Bump this deliberately after verifying a newer tag boots clean. + image: docker.io/searxng/searxng:2026.5.31-7159b8aed + entrypoint: + - /bin/sh + - -c + - | + set -eu + if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then + secret="$${SEARXNG_SECRET:-}" + if [ -z "$$secret" ]; then + secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')" + fi + sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml + fi + exec /usr/local/searxng/entrypoint.sh + ports: + - "127.0.0.1:8080:8080" + volumes: + - searxng-data:/etc/searxng + - ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z + environment: + - SEARXNG_BASE_URL=http://localhost:8080/ + - SEARXNG_SECRET=${SEARXNG_SECRET:-} + # The official searxng image runs as the non-root `searxng` user, but its + # entrypoint still needs to chown /etc/searxng on first boot, drop privs via + # su-exec, and (with our wrapper above) write settings.yml into the named + # volume. Without these capabilities the wrapper aborts at the redirection + # with EACCES and the container fails its healthcheck with permission + # errors during setup. Mirrors the cap set recommended by the upstream + # searxng-docker compose file. See issue #721. + cap_drop: + - ALL + cap_add: + - CHOWN + - SETGID + - SETUID + - DAC_OVERRIDE + healthcheck: + test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""] + interval: 5s + timeout: 6s + retries: 20 + start_period: 10s + restart: unless-stopped + + ntfy: + image: docker.io/binwiederhier/ntfy + command: serve + ports: + - "${NTFY_BIND:-127.0.0.1}:8091:80" + volumes: + - ntfy-cache:/var/cache/ntfy + environment: + - NTFY_BASE_URL=${NTFY_BASE_URL:-http://localhost:8091} + restart: unless-stopped + +volumes: + searxng-data: + chromadb-data: + ntfy-cache: