FROM python:3.12-slim WORKDIR /app ENV PYTHONUNBUFFERED=1 PYTHONPATH=/app/src RUN pip install --no-cache-dir \ "fastapi>=0.115" "uvicorn[standard]>=0.30" "sqlalchemy>=2.0" \ "psycopg[binary]>=3.2" "pgvector>=0.3.6" "alembic>=1.13" \ "argon2-cffi>=23.1" "itsdangerous>=2.2" \ "jinja2>=3.1" "python-multipart>=0.0.9" "httpx>=0.27" \ "numpy>=1.26" "pillow>=10.0" "pydantic>=2.7" \ "ijson>=3.2" # `ijson` is an optional extra for a pip install and a plain dependency here, # on purpose. `foilstack enrich` is Magic-only and most installs run it once or # never, so it should not weigh on `pip install foilstack` — but the compose # file is the install path the README describes, and discovering a missing # parser at the moment you reach for the command is a worse trade than 200 KB. COPY src/ /app/src/ COPY alembic.ini /app/alembic.ini COPY docker/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh # The package is on PYTHONPATH rather than pip-installed, so the console script # from pyproject.toml is never generated in the image. Without this shim, # `docker compose exec web foilstack ingest ...` — the form the README and the # app both suggest — answers "command not found". RUN printf '#!/bin/sh\nexec python -m foilstack.cli "$@"\n' > /usr/local/bin/foilstack \ && chmod +x /usr/local/bin/foilstack # Which commit this image was built from. # # A build argument rather than a runtime environment variable, and that is the # whole point: a runtime value can be changed by restarting a container, so it # would report the commit the *host* has checked out rather than the code # actually inside the image. Those differ exactly when it matters — when a # service was not rebuilt — which is the case this exists to catch. ARG GIT_SHA="" ENV FOILSTACK_GIT_SHA=$GIT_SHA EXPOSE 8000 ENTRYPOINT ["/app/entrypoint.sh"] CMD ["uvicorn", "foilstack.web.app:app", "--host", "0.0.0.0", "--port", "8000"]