diff --git a/Dockerfile b/Dockerfile index b77ba84..52b50fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,43 +1,54 @@ -# syntax=docker/dockerfile:1 - -FROM python:3.12-slim AS builder - -COPY --from=ghcr.io/astral-sh/uv:0.8.4 /uv /uvx /bin/ +FROM python:3.14-slim AS builder +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ UV_COMPILE_BYTECODE=1 \ - UV_LINK_MODE=copy + UV_LINK_MODE=copy \ + UV_LOCKED=1 WORKDIR /app -COPY pyproject.toml uv.lock ./ -COPY src ./src +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=zensical.toml,target=zensical.toml \ + --mount=type=bind,source=docs/,target=docs/ \ + uvx zensical build -RUN uv sync --frozen --no-dev +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 --no-install-project -COPY docs ./docs -COPY zensical.toml ./ +# COPY --chown=appuser:appuser . /app -RUN uv run zensical build +# RUN --mount=type=cache,target=/root/.cache/uv \ +# uv sync --no-editable -FROM python:3.12-slim AS runtime +FROM python:3.14-slim AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PATH="/app/.venv/bin:$PATH" \ - PERSONAL_MCP_HOST=0.0.0.0 \ - PERSONAL_MCP_PORT=8765 + PERSONAL_MCP_MOUNTS__DOCS_DIR=/app/docs WORKDIR /app -RUN groupadd --system --gid 1001 appuser \ - && useradd --system --uid 1001 --gid appuser --create-home --home-dir /home/appuser appuser - -COPY --from=builder --chown=appuser:appuser /app /app - EXPOSE 8765 +RUN groupadd --system --gid 1001 appuser && \ + useradd --system --uid 1001 --gid appuser appuser + +COPY --from=ghcr.io/astral-sh/uv:latest --chown=appuser:appuser /uv /uvx /bin/ +COPY --from=builder --chown=appuser:appuser /app/.venv /app/.venv +COPY --from=builder --chown=appuser:appuser /app/site /app/site +COPY --chown=appuser:appuser ./docs /app/docs + +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 \ + --mount=type=bind,source=src/,target=src/ \ + uv sync --no-editable + USER appuser -CMD ["uvicorn", "personal_mcp.main:app", "--host", "0.0.0.0", "--port", "8765"] \ No newline at end of file +CMD ["uvicorn", "personal_mcp.main:create_app", "--factory", "--host", "0.0.0.0", "--port", "8765"] diff --git a/src/personal_mcp/main.py b/src/personal_mcp/main.py index e35d6b9..d075f2c 100644 --- a/src/personal_mcp/main.py +++ b/src/personal_mcp/main.py @@ -1,7 +1,14 @@ -from personal_mcp.mcp import mcp -from personal_mcp.web.app import app +from fastapi import FastAPI -__all__ = ["app", "main", "mcp"] +from personal_mcp.mcp import mcp +from personal_mcp.web.app import create_app as create_fastapi + +__all__ = ["create_app", "main", "mcp"] + + +def create_app() -> FastAPI: + """Create the HTTP application for ASGI servers using factory mode.""" + return create_fastapi() def main() -> None: diff --git a/src/personal_mcp/web/app.py b/src/personal_mcp/web/app.py index b1aecc0..2960a4f 100644 --- a/src/personal_mcp/web/app.py +++ b/src/personal_mcp/web/app.py @@ -1,14 +1,14 @@ from fastapi import FastAPI -from personal_mcp.config import Settings -from personal_mcp.config import get_settings -from personal_mcp.mcp import mcp -from personal_mcp.web.docs_mount import mount_docs_static -from personal_mcp.web.health import router as health_router +from ..config import Settings +from ..config import get_settings +from ..mcp import mcp +from .docs_mount import mount_docs_static +from .health import router as health_router def create_app(settings: Settings | None = None) -> FastAPI: - runtime_settings = settings or get_settings() + runtime_settings = settings if settings is not None else get_settings() mcp_app = mcp.http_app( path=runtime_settings.mounts.mcp, json_response=True, @@ -32,6 +32,3 @@ def create_app(settings: Settings | None = None) -> FastAPI: ) app.mount("/", mcp_app, name="mcp") return app - - -app = create_app()