diff --git a/.github/workflows/unified-tests.yml b/.github/workflows/unified-tests.yml index 40a10ba7..bfc27018 100644 --- a/.github/workflows/unified-tests.yml +++ b/.github/workflows/unified-tests.yml @@ -3,6 +3,9 @@ name: Unified Tests (Fly Runner) on: push: branches: [main] + paths: + - 'src/**' + - 'tests/**' permissions: contents: read @@ -58,7 +61,7 @@ jobs: which python3.12 - name: Install the project - run: uv sync --all-extras --dev + run: uv sync --all-extras - name: Run unified tests run: uv run python -m tests.unified.run diff --git a/Dockerfile b/Dockerfile index 02340993..30a8d09b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,41 @@ # https://pythonspeed.com/articles/base-image-python-docker-images/ # https://testdriven.io/blog/docker-best-practices/ -FROM python:3.11-slim-bullseye +FROM python:3.13-slim-bookworm -COPY --from=ghcr.io/astral-sh/uv:0.4.9 /uv /bin/uv +COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv # Set Working directory WORKDIR /app -RUN addgroup --system app && adduser --system --group app -RUN chown -R app:app /app -USER app - # Enable bytecode compilation ENV UV_COMPILE_BYTECODE=1 # Copy from the cache instead of linking since it's a mounted volume ENV UV_LINK_MODE=copy +# Python optimizations +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + # Install the project's dependencies using the lockfile and settings RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project --no-dev + uv sync --frozen --no-install-project --no-group dev # Copy only requirements to cache them in docker layer COPY uv.lock pyproject.toml /app/ # Sync the project RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev + uv sync --frozen --no-group dev # Place executables in the environment at the front of the path ENV PATH="/app/.venv/bin:$PATH" +# Create non-root user and set ownership +RUN addgroup --system app && adduser --system --group app && chown -R app:app /app + COPY --chown=app:app src/ /app/src/ COPY --chown=app:app migrations/ /app/migrations/ COPY --chown=app:app scripts/ /app/scripts/ @@ -40,7 +43,12 @@ COPY --chown=app:app alembic.ini /app/alembic.ini # Copy config files - this will copy config.toml if it exists, and config.toml.example COPY --chown=app:app config.toml* /app/ +# Switch to non-root user +USER app + EXPOSE 8000 -# https://stackoverflow.com/questions/29663459/python-app-does-not-print-anything-when-running-detached-in-docker +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/openapi.json')" || exit 1 + CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"] diff --git a/pyproject.toml b/pyproject.toml index d18f697e..df367820 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,8 @@ dependencies = [ "cashews[redis]==7.4.4", "scikit-learn>=1.6.0", ] -[tool.uv] -dev-dependencies = [ +[dependency-groups] +dev = [ "pytest>=8.2.2", "sqlalchemy-utils>=0.41.2", "pytest-asyncio>=0.23.7",