diff --git a/.dockerignore b/.dockerignore index c37dcdd..ea7e447 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,10 @@ # Local reports, captures and scratch files .user/ +# Test sources are mounted into the Compose test container instead of shipped +# in the production image. +vectorizing/tests/ + ### Python template # Byte-compiled / optimized / DLL files __pycache__/ @@ -164,7 +168,6 @@ cython_debug/ .idea/ # Other files -heroku.yml docker-compose.yml .pre-commit-config.yaml .vscode/ diff --git a/Dockerfile b/Dockerfile index e3ffe29..fc9abe8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,10 @@ # Use an official Python runtime as a parent image FROM python:3.11-slim -# Install necessary packages -RUN apt-get update -y && apt-get install -y \ - wget \ - build-essential \ - python3-dev \ - libagg-dev \ - libpotrace-dev \ - pkg-config \ - libgl1 \ - libcairo2 \ - --no-install-recommends && \ +# Install system dependencies shared with the development container. +COPY scripts/install_system_dependencies.sh /tmp/install_system_dependencies.sh +RUN bash /tmp/install_system_dependencies.sh && \ + rm /tmp/install_system_dependencies.sh && \ rm -rf /var/lib/apt/lists/* # Create a non-root user and group with specific IDs for consistency @@ -21,8 +14,10 @@ RUN addgroup --gid 1001 appuser && \ # Set the working directory WORKDIR /app -# Copy only requirements to leverage Docker cache -COPY requirements/dev.txt /app/requirements.txt +# Copy only requirements to leverage Docker cache. The test Compose service +# overrides this with the development requirements. +ARG REQUIREMENTS_FILE=prod.txt +COPY requirements/${REQUIREMENTS_FILE} /app/requirements.txt # Install Python dependencies RUN pip install --no-cache-dir -r /app/requirements.txt diff --git a/README.md b/README.md index b2e2bc0..775da5d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The first time the execution can take few minutes, as it is pulling the dev cont This will compile dependencies and environments, ensuring a consistent development workflow and deployment. -4. If you want to add or remove **system** dependencies, update the script: [`scripts/install_system_dependencies.sh`](scripts/install_system_dependencies.sh). This is used both in CI and at dev container creation, to keep them consistent. +4. If you want to add or remove **system** dependencies, update the shared script: [`scripts/install_system_dependencies.sh`](scripts/install_system_dependencies.sh). It is used by the production Docker image and at dev container creation, to keep them consistent. The Compose test image uses development Python requirements and mounts `vectorizing/tests/` at runtime because test sources are excluded from the production build context. ## Linting and formatting @@ -74,12 +74,16 @@ python scripts/benchmark_vectorization.py --output .user/benchmark.json --case a - Add `--baseline-root /path/to/baseline` to benchmark two trusted checkouts in alternating order, with exact-output checks. `--repeats` defaults to 3 and `--warmups` to 1 per process. Output mismatches exit `1` but still save the report; timings have no pass/fail threshold. - Reports include runtime/source fingerprints and raw samples. `--label` can record commit IDs. Checkouts share the installed dependencies; this is not a dependency-isolated or production/concurrency benchmark. Use only trusted source roots. -For Docker, build the current scripts and mount only the output directory: +For Docker, build the current scripts with development dependencies and mount the +fixture images and output directory: ```bash mkdir -p .user -docker build -t vectorizing:tools . -docker run --rm --user "$(id -u):$(id -g)" -v "$PWD/.user:/results" vectorizing:tools \ +docker build --build-arg REQUIREMENTS_FILE=dev.txt -t vectorizing:tools . +docker run --rm --user "$(id -u):$(id -g)" \ + -v "$PWD/.user:/results" \ + -v "$PWD/vectorizing/tests/images:/app/vectorizing/tests/images:ro" \ + vectorizing:tools \ python scripts/vectorization_outputs.py capture /results/candidate ``` diff --git a/docker-compose.yml b/docker-compose.yml index 9389ea5..ba6e921 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,12 +48,16 @@ services: test: <<: *app + image: vectorizing:test + build: + context: . + args: + REQUIREMENTS_FILE: dev.txt profiles: [testing] environment: <<: *local-aws volumes: - - ./vectorizing/tests/results:/app/vectorizing/tests/results - - ./vectorizing/tests/diff_output:/app/vectorizing/tests/diff_output + - ./vectorizing/tests:/app/vectorizing/tests depends_on: moto-init: condition: service_completed_successfully diff --git a/envs/dev.yaml b/envs/dev.yaml index 9fdcb8f..880033d 100644 --- a/envs/dev.yaml +++ b/envs/dev.yaml @@ -44,7 +44,6 @@ dependencies: - faiss-cpu==1.7.4 - filelock==3.18.0 - flask==2.3.3 - - flask-cors==4.0.2 - gunicorn==21.2.0 - identify==2.6.12 - idna==3.10 @@ -54,11 +53,9 @@ dependencies: - jinja2==3.1.6 - jmespath==1.0.1 - lazy-loader==0.4 - - llvmlite==0.40.1 - markupsafe==3.0.2 - networkx==3.4.2 - nodeenv==1.9.1 - - numba==0.57.1 - numpy==1.24.4 - opencv-python-headless==4.8.0.76 - packaging==25.0 diff --git a/envs/prod.yaml b/envs/prod.yaml index f7eae44..c8d13ff 100644 --- a/envs/prod.yaml +++ b/envs/prod.yaml @@ -34,7 +34,6 @@ dependencies: - cuid==0.4 - faiss-cpu==1.7.4 - flask==2.3.3 - - flask-cors==4.0.2 - gunicorn==21.2.0 - idna==3.10 - imageio==2.37.0 @@ -42,10 +41,8 @@ dependencies: - jinja2==3.1.6 - jmespath==1.0.1 - lazy-loader==0.4 - - llvmlite==0.40.1 - markupsafe==3.0.2 - networkx==3.4.2 - - numba==0.57.1 - numpy==1.24.4 - opencv-python-headless==4.8.0.76 - packaging==25.0 diff --git a/heroku.yml b/heroku.yml deleted file mode 100644 index 8072c39..0000000 --- a/heroku.yml +++ /dev/null @@ -1,5 +0,0 @@ -build: - docker: - web: Dockerfile -run: - web: python /app/src/app.py \ No newline at end of file diff --git a/requirements/dev.txt b/requirements/dev.txt index d9c7b5f..2511dd3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -54,10 +54,6 @@ faiss-cpu==1.7.4 filelock==3.18.0 # via virtualenv flask==2.3.3 - # via - # -r /workspaces/vectorizing/requirements/prod.txt - # flask-cors -flask-cors==4.0.2 # via -r /workspaces/vectorizing/requirements/prod.txt gunicorn==21.2.0 # via -r /workspaces/vectorizing/requirements/prod.txt @@ -90,10 +86,6 @@ lazy-loader==0.4 # via # -r /workspaces/vectorizing/requirements/prod.txt # scikit-image -llvmlite==0.40.1 - # via - # -r /workspaces/vectorizing/requirements/prod.txt - # numba markupsafe==3.0.2 # via # -r /workspaces/vectorizing/requirements/prod.txt @@ -105,13 +97,10 @@ networkx==3.4.2 # scikit-image nodeenv==1.9.1 # via pre-commit -numba==0.57.1 - # via -r /workspaces/vectorizing/requirements/prod.txt numpy==1.24.4 # via # -r /workspaces/vectorizing/requirements/prod.txt # imageio - # numba # opencv-python-headless # pypotrace # pywavelets diff --git a/requirements/prod.in b/requirements/prod.in index d5b6003..80ac6a4 100644 --- a/requirements/prod.in +++ b/requirements/prod.in @@ -3,9 +3,7 @@ cuid~=0.4 boto3~=1.35.2 flask~=2.3.3 -flask-cors~=4.0.0 sentry-sdk~=1.30.0 -numba~=0.57.1 pillow~=10.0.0 faiss-cpu~=1.7.4 pypotrace~=0.3 diff --git a/requirements/prod.txt b/requirements/prod.txt index ce3be5e..3bbc909 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -25,10 +25,6 @@ cuid==0.4 faiss-cpu==1.7.4 # via -r /workspaces/vectorizing/scripts/../requirements/prod.in flask==2.3.3 - # via - # -r /workspaces/vectorizing/scripts/../requirements/prod.in - # flask-cors -flask-cors==4.0.2 # via -r /workspaces/vectorizing/scripts/../requirements/prod.in gunicorn==21.2.0 # via -r /workspaces/vectorizing/scripts/../requirements/prod.in @@ -46,20 +42,15 @@ jmespath==1.0.1 # botocore lazy-loader==0.4 # via scikit-image -llvmlite==0.40.1 - # via numba markupsafe==3.0.2 # via # jinja2 # werkzeug networkx==3.4.2 # via scikit-image -numba==0.57.1 - # via -r /workspaces/vectorizing/scripts/../requirements/prod.in numpy==1.24.4 # via # imageio - # numba # opencv-python-headless # pypotrace # pywavelets diff --git a/scripts/install_system_dependencies.sh b/scripts/install_system_dependencies.sh index 05551bf..b6d62a8 100644 --- a/scripts/install_system_dependencies.sh +++ b/scripts/install_system_dependencies.sh @@ -1,5 +1,23 @@ #!/bin/bash -# this script install system dependencies for vectorizing +# Install the system dependencies required by the production image and devcontainer. -sudo apt-get update -sudo apt-get install -y build-essential libagg-dev libpotrace-dev pkg-config libffi-dev libcairo2-dev +set -euo pipefail + +if [[ "$(id -u)" -eq 0 ]]; then + APT_GET=(apt-get) +else + APT_GET=(sudo apt-get) +fi + +"${APT_GET[@]}" update +"${APT_GET[@]}" install -y --no-install-recommends \ + wget \ + build-essential \ + python3-dev \ + libagg-dev \ + libpotrace-dev \ + pkg-config \ + libgl1 \ + libcairo2 \ + libffi-dev \ + libcairo2-dev diff --git a/vectorizing/__init__.py b/vectorizing/__init__.py index 1abf422..26d04e5 100644 --- a/vectorizing/__init__.py +++ b/vectorizing/__init__.py @@ -130,7 +130,7 @@ def index() -> str | Response | tuple[Response, int]: timer.end_timer() if raw: - return markup + return Response(markup, mimetype="image/svg+xml") timer.start_timer("Markup Upload") cuid_str = upload_markup(markup, S3_BUCKET) diff --git a/vectorizing/tests/test_api_contracts.py b/vectorizing/tests/test_api_contracts.py index 4c050c3..c46236a 100644 --- a/vectorizing/tests/test_api_contracts.py +++ b/vectorizing/tests/test_api_contracts.py @@ -84,6 +84,7 @@ def test_raw_requests_crop_before_vectorizing_and_do_not_upload( }, ) assert response.status_code == 200 + assert response.mimetype == "image/svg+xml" assert response.data.startswith(b"