diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..4af1ee75 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +# Ignore git related files +.github/ +.gitignore + +# Ignore folders not necessary for the docker image +docs/ +tests/ + +# Ignore venv if any +venv/ + +# Ignore files not necessary for the docker image +CONTRIBUTING.md +*.png +*.json +LICENSE +Makefile \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e916fb19..84b7a1e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,51 @@ ARG PY_VERSION=3.12 + +#--------------------------------------------------------------------------------------- +# Stage 1 → Builder image +#--------------------------------------------------------------------------------------- FROM python:$PY_VERSION-slim AS build-env ARG VERSION - WORKDIR /app # Install python deps -RUN python -m pip install --upgrade poetry wheel twine +RUN apt-get update && apt-get install -y --no-install-recommends build-essential \ + && rm -rf /var/lib/apt/lists/* +RUN python -m pip install --no-cache-dir --upgrade poetry wheel twine # Install project deps -COPY pyproject.toml . -RUN poetry install --with dev +COPY pyproject.toml poetry.lock ./ +RUN poetry install --with dev --no-root # Copy code *after* installing deps to avoid unnecessarily invalidating cache COPY . . +# Build the project RUN poetry build -RUN PROJECT_VERSION=$(poetry version -s) && cp /app/dist/boaviztapi-$PROJECT_VERSION.tar.gz ./boaviztapi-$VERSION.tar.gz -RUN pip install boaviztapi-$VERSION.tar.gz && cp $(which uvicorn) /app +RUN PROJECT_VERSION=$(poetry version -s) && \ + cp /app/dist/boaviztapi-$PROJECT_VERSION.tar.gz ./boaviztapi-$VERSION.tar.gz +#--------------------------------------------------------------------------------------- +# Stage 2 → Runtime image +#--------------------------------------------------------------------------------------- FROM python:$PY_VERSION-slim AS run-env # Python 3 surrogate unicode handling # @see https://click.palletsprojects.com/en/7.x/python3/ -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 - -COPY --from=build-env /app /app -COPY --from=build-env /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages -ENV PYTHONPATH=/usr/local/lib/python3.12/site-packages +ENV LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 +ARG VERSION WORKDIR /app +# Copy executable and dependencies +COPY --from=build-env /app/boaviztapi-$VERSION.tar.gz /app/ +RUN pip install --no-cache-dir /app/boaviztapi-$VERSION.tar.gz + +# Required in main.py +COPY --from=build-env /app/pyproject.toml /usr/local/lib/python3.12/site-packages/boaviztapi/ + +# Copy uvicorn executable +RUN pip install --no-cache-dir uvicorn + EXPOSE 5000 -CMD ["./uvicorn", "boaviztapi.main:app", "--host", "0.0.0.0", "--port", "5000"] +CMD ["uvicorn", "boaviztapi.main:app", "--host", "0.0.0.0", "--port", "5000"] diff --git a/boaviztapi/main.py b/boaviztapi/main.py index 6d0f7b2c..6c2cca9f 100644 --- a/boaviztapi/main.py +++ b/boaviztapi/main.py @@ -27,13 +27,27 @@ from fastapi.responses import HTMLResponse +def get_version_from_pyproject(): + # List of potential locations for the pyproject.toml file + potential_paths = [ + os.path.join(os.path.dirname(__file__), '../pyproject.toml'), + os.path.join(os.path.dirname(__file__), 'pyproject.toml'), + ] + + for path in potential_paths: + if os.path.exists(path): + with open(path, 'r') as f: + return toml.loads(f.read())['tool']['poetry']['version'] + + # Raise an error if the file is not found in any of the locations + raise FileNotFoundError("pyproject.toml not found in expected locations") + # Serverless frameworks adds a 'stage' prefix to the route used to serve applications # We have to manage it to expose openapi doc on aws and generate proper links. stage = os.environ.get('STAGE', None) openapi_prefix = f"/{stage}" if stage else "/" app = FastAPI(root_path=openapi_prefix) # Here is the magic -version = toml.loads(open(os.path.join(os.path.dirname(__file__), '../pyproject.toml'), 'r').read())['tool']['poetry'][ - 'version'] +version = get_version_from_pyproject() _logger = logging.getLogger(__name__) origins = json.loads(os.getenv("ALLOWED_ORIGINS", '["*"]'))