Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
.env
__pycache__
__pycache__
.venv
.data
.pytest_cache
.ruff_cache
21 changes: 9 additions & 12 deletions .github/workflows/code-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,39 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5.1.1
with:
python-version: 3.x
- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Display Python version
run: |
python --version
uv run python --version

- name: Install dependencies
run: |
pip install poetry
poetry install --no-root --with dev
uv sync --frozen

- name: Check for outdated dependencies
if: env.skip-job != 'true'
run: |
poetry show --outdated --only main
uv tree --outdated

- name: Run tests with coverage in latest stable 3.x
run: |
poetry run pytest --cov=app --cov-report=term-missing
uv run pytest --cov=app --cov-report=term-missing
continue-on-error: false

- name: Run Bandit for security analysis
run: |
poetry run bandit -r app
uv run bandit -r app
continue-on-error: false

- name: Run Ruff for linting
run: |
poetry run ruff check .
uv run ruff check .
continue-on-error: false

- name: Run Vulture for dead code analysis
run: |
poetry run vulture . --min-confidence 70
uv run vulture . --min-confidence 70 --exclude .venv
continue-on-error: false

30 changes: 13 additions & 17 deletions .github/workflows/compatability-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,48 +20,44 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5.1.1
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}

- name: Get Python version
id: python_version
run: |
python --version

- name: Install Poetry
run: |
pip install poetry
uv run python --version

- name: Backup lock file
run: |
if [ -f poetry.lock ]; then
mv poetry.lock poetry.lock.backup
if [ -f uv.lock ]; then
mv uv.lock uv.lock.backup
fi

- name: Install and update dependencies
run: |
if [[ "${{ matrix.update-strategy }}" == "minor" ]]; then
echo "Updating to minor versions"
poetry install --no-root --with dev
poetry update
uv lock --upgrade
uv sync
elif [[ "${{ matrix.update-strategy }}" == "major" ]]; then
echo "Updating to major versions"
cp pyproject.toml pyproject.toml.backup
sed -i 's/\^/>=/g' pyproject.toml # Adjust constraints to allow major updates
poetry install --no-root --with dev
sed -i 's/~=/>=/g' pyproject.toml # Adjust constraints to allow major updates
uv lock --upgrade
uv sync
mv pyproject.toml.backup pyproject.toml
fi


- name: Restore lock file
run: |
if [ -f poetry.lock.backup ]; then
mv poetry.lock.backup poetry.lock
if [ -f uv.lock.backup ]; then
mv uv.lock.backup uv.lock
fi

- name: Run tests
run: |
poetry run pytest
uv run pytest
continue-on-error: false # Set to true if you want to continue on test failures
22 changes: 4 additions & 18 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,7 @@ target/
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock


# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
Expand All @@ -120,6 +103,9 @@ ENV/
env.bak/
venv.bak/

# Local Docker volume data
.data/

# Spyder project settings
.spyderproject
.spyproject
Expand Down
1 change: 0 additions & 1 deletion .python-version

This file was deleted.

112 changes: 52 additions & 60 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,76 +1,68 @@
ARG IMAGE_TAG=3.13-slim-bullseye

FROM python:${IMAGE_TAG} AS base
# Allowing the argumenets to be read into the dockerfile. Ex: .env > compose.yml > Dockerfile
ARG UID=1000
ARG GID=1000
#
# Python dependencies stage
#
FROM python:${IMAGE_TAG} AS deps

# Create the user and usergroup
RUN groupadd -g ${GID} -o app
RUN useradd -m -d /app -u ${UID} -g ${GID} -o -s /bin/bash app
# Copy uv binary for dependency management
COPY --from=ghcr.io/astral-sh/uv:0.8.4 /uv /usr/local/bin/uv

# Set the working directory to /app
WORKDIR /app

# Both build and development need poetry, so it is its own step.
FROM base AS poetry
RUN pip install poetry

# Use this page as a reference for python and poetry environment variables:
# https://docs.python.org/3/using/cmdline.html#envvar-PYTHONUNBUFFERED Ensure
# the stdout and stderr streams are sent straight to terminal, then you can see
# the output of your application
ENV PYTHONUNBUFFERED=1\
# Avoid the generation of .pyc files during package install
# Disable pip's cache, then reduce the size of the image
PIP_NO_CACHE_DIR=off \
# Save runtime because it is not look for updating pip version
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
# Disable poetry interaction
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_CREATE=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache

FROM poetry AS build
COPY pyproject.toml poetry.lock ./
RUN poetry install --no-root --without dev && rm -rf ${POETRY_CACHE_DIR};

# FROM poetry AS build-minor-update
# # Install minor version updates in the absence of poetry.lock file
# COPY pyproject.toml poetry.lock ./
# RUN poetry install --no-root --without dev && rm -rf ${POETRY_CACHE_DIR};

FROM build AS test
ENV PYTHONUNBUFFERED=1 \
# Compile Python bytecode for faster startup
UV_COMPILE_BYTECODE=1 \
# Use copy mode (required in Docker; hardlinks don't work across layers)
UV_LINK_MODE=copy

# Install production dependencies
COPY pyproject.toml uv.lock ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev

#
# Test stage
#
FROM deps AS test

# Install dev dependencies
RUN poetry install --only dev --no-root && rm -rf ${POETRY_CACHE_DIR};
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project

COPY . .
# Run tests
USER app
RUN poetry run pytest tests

FROM build AS test-minor-update
# Install dev dependencies
RUN poetry install --only dev --no-root && rm -rf ${POETRY_CACHE_DIR};
RUN poetry update
CMD ["uv", "run", "pytest", "tests"]

#
# Test stage (with minor version updates)
#
FROM deps AS test-minor-update

# Update to latest compatible versions and install dev dependencies
RUN --mount=type=cache,target=/root/.cache/uv \
uv lock --upgrade && uv sync --no-install-project

COPY . .
# Run tests
USER app
RUN poetry run pytest tests

CMD ["uv", "run", "pytest", "tests"]

#
# Production stage
#
FROM python:${IMAGE_TAG} AS production

# Copy uv binary for runtime
COPY --from=ghcr.io/astral-sh/uv:0.8.4 /uv /usr/local/bin/uv

WORKDIR /app

FROM base AS production
RUN mkdir -p /venv && chown ${UID}:${GID} /venv
ENV PYTHONUNBUFFERED=1

# By adding /venv/bin to the PATH, the dependencies in the virtual environment
# are used
ENV VIRTUAL_ENV=/venv \
PATH="/venv/bin:$PATH"
# Copy the virtualenv from the deps stage
COPY --from=deps /app/.venv /app/.venv

COPY --chown=${UID}:${GID} . /app
COPY --chown=${UID}:${GID} --from=build "/app/.venv" ${VIRTUAL_ENV}
# Copy application source
COPY . .

# Switch to the app user
USER app
CMD ["uv", "run", "python", "-m", "gunicorn", "-c", "gunicorn_config.py"]
Loading
Loading