add docker file
This commit is contained in:
35
.dockerignore
Normal file
35
.dockerignore
Normal file
@@ -0,0 +1,35 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Development tools
|
||||
.idea/
|
||||
.vscode/
|
||||
*.egg-info/
|
||||
|
||||
# uv cache
|
||||
.uv/
|
||||
|
||||
# Local output
|
||||
dist/
|
||||
|
||||
# Don't bake data into the image — mount it at runtime
|
||||
# data.csv is excluded here so it must be provided via docker-compose volume
|
||||
data.csv
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Docker itself
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.dockerignore
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.idea
|
||||
.venv
|
||||
|
||||
__pycache__/
|
||||
|
||||
40
Dockerfile
Normal file
40
Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM python:3.13-slim
|
||||
|
||||
# Pull uv binary from the official image (no pip overhead)
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
|
||||
|
||||
# Non-root user for security
|
||||
RUN adduser --disabled-password --no-create-home appuser
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies first (layer is cached unless lock file changes)
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN uv sync --frozen --no-dev --no-install-project
|
||||
|
||||
# Copy application source
|
||||
COPY src/ ./src/
|
||||
|
||||
# data.csv is NOT baked into the image — it is mounted at runtime via docker-compose
|
||||
# so updates to the CSV don't require a rebuild.
|
||||
|
||||
RUN chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# WORKDIR=/app → gunicorn CWD is /app → data.csv resolves to /app/data.csv (mounted volume)
|
||||
# PYTHONPATH=/app/src → 'app' module resolves to src/app.py
|
||||
ENV PYTHONPATH=/app/src
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000')"
|
||||
|
||||
CMD ["uv", "run", "--no-dev", "gunicorn", \
|
||||
"--workers", "2", \
|
||||
"--bind", "0.0.0.0:8000", \
|
||||
"--access-logfile", "-", \
|
||||
"--error-logfile", "-", \
|
||||
"app:app"]
|
||||
|
||||
506
dist/index.html
vendored
Normal file
506
dist/index.html
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"flask>=3.1.1",
|
||||
"gunicorn>=23.0.0",
|
||||
"pandas>=2.3.0",
|
||||
"plotly>=6.1.2",
|
||||
"scikit-learn>=1.7.0",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<header class="bg-blue-900 text-white shadow-lg">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-8 py-8 sm:py-10">
|
||||
<p class="text-blue-300 text-xs font-semibold tracking-widest uppercase mb-2">Performance Dashboard</p>
|
||||
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight">🍺 The Hope Pub Quiz</h1>
|
||||
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight">The Hope Pub Quiz</h1>
|
||||
<p class="text-blue-300 mt-2 text-sm">Trying to convince ourselves we're not stupid</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
14
uv.lock
generated
14
uv.lock
generated
@@ -49,6 +49,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "25.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/13/ef67f59f6a7896fdc2c1d62b5665c5219d6b0a9a1784938eb9a28e55e128/gunicorn-25.1.0.tar.gz", hash = "sha256:1426611d959fa77e7de89f8c0f32eed6aa03ee735f98c01efba3e281b1c47616", size = 594377, upload-time = "2026-02-13T11:09:58.989Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/73/4ad5b1f6a2e21cf1e85afdaad2b7b1a933985e2f5d679147a1953aaa192c/gunicorn-25.1.0-py3-none-any.whl", hash = "sha256:d0b1236ccf27f72cfe14bce7caadf467186f19e865094ca84221424e839b8b8b", size = 197067, upload-time = "2026-02-13T11:09:57.146Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.2.0"
|
||||
@@ -272,6 +284,7 @@ version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "flask" },
|
||||
{ name = "gunicorn" },
|
||||
{ name = "pandas" },
|
||||
{ name = "plotly" },
|
||||
{ name = "scikit-learn" },
|
||||
@@ -281,6 +294,7 @@ dependencies = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "flask", specifier = ">=3.1.1" },
|
||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||
{ name = "pandas", specifier = ">=2.3.0" },
|
||||
{ name = "plotly", specifier = ">=6.1.2" },
|
||||
{ name = "scikit-learn", specifier = ">=1.7.0" },
|
||||
|
||||
Reference in New Issue
Block a user