From 617248ea6eb91ed4fbd833130433381985a7b549 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Sun, 2 Aug 2026 14:15:18 -0400 Subject: [PATCH] Installer prerequisites: REQ-D through REQ-G REQ-D: restore waitress and tzdata to requirements.in. They existed ONLY in the generated requirements.txt (hand-added in bf9e60e), so the next `uv pip compile` would have silently removed the WSGI server and the IANA timezone database from every Windows install. REQ-E: split production and development requirements. requirements.txt was installing pytest, pytest-cov, pytest-flask, coverage, iniconfig and pluggy onto production servers. Verified on a real Windows Server box before this change. CI, scripts/test-external-plugin.sh and the dev docs now use requirements-dev.txt. REQ-F: standardise on Python 3.14. The repo declared four different versions (Dockerfile 3.12, DEPLOY-WINDOWS-IIS 3.12, INSTALL-WINDOWS-IIS 3.13, CI 3.13, plus README, web.config and PLUGIN-EXTERNAL-REPO). 3.14 is in active bugfix support until ~Apr 2027 and supported to Oct 2030; 3.13 entered security-only in Apr 2026. All four compiled dependencies publish win_amd64 wheels for 3.14 (cryptography via an abi3 wheel), verified by building an offline wheelhouse and installing it on Windows Server 2025. REQ-G: state MySQL 8.0 as the standard for new installs; 5.7+/5.6 remain supported on an existing server. Lockfiles regenerated with uv pip compile. Production deps 44 -> 38. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 4 +- Dockerfile | 2 +- README.md | 4 +- deploy/windows/web.config | 2 +- docs/DEPLOY-WINDOWS-IIS.md | 4 +- docs/DEVELOPMENT-SETUP.md | 2 +- docs/INSTALL-WINDOWS-IIS.md | 4 +- docs/PLUGIN-EXTERNAL-REPO.md | 6 +- requirements-dev.in | 12 +++ requirements-dev.txt | 129 ++++++++++++++++++++++++++++++++ requirements.in | 14 ++-- requirements.txt | 36 +++------ scripts/test-external-plugin.sh | 2 +- 13 files changed, 175 insertions(+), 46 deletions(-) create mode 100644 requirements-dev.in create mode 100644 requirements-dev.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 239888a..425fdc5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.13' + python-version: '3.14' cache: pip - - run: pip install -r requirements.txt + - run: pip install -r requirements-dev.txt - run: python -m pytest -q naming: diff --git a/Dockerfile b/Dockerfile index 082620b..d6c04a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ RUN npm run build # Output lands in /build/dist (Vite default), copied into the final stage below. # ---- Stage 2: Python application image ---- -FROM python:3.12-slim AS base +FROM python:3.14-slim AS base ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ diff --git a/README.md b/README.md index 705500b..378ac33 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ShopDB tracks and manages: ## Tech Stack **Backend:** -- Python 3.12 with Flask +- Python 3.14 with Flask - SQLAlchemy ORM - MySQL 5.7+ database (5.6 works with extra utf8mb4 config; see docs/DEPLOY.md) - JWT authentication @@ -98,7 +98,7 @@ To maintain consistency with the legacy ShopDB database and codebase, the follow ### Prerequisites -- Python 3.12 +- Python 3.14 - Node.js 18+ - MySQL 5.7+ (5.6 works with extra utf8mb4 config; see docs/DEPLOY.md) diff --git a/deploy/windows/web.config b/deploy/windows/web.config index b5b0704..624ef4c 100644 --- a/deploy/windows/web.config +++ b/deploy/windows/web.config @@ -11,7 +11,7 @@ - HttpPlatformHandler IIS module installed (https://www.iis.net/downloads/microsoft/httpplatformhandler) - URL Rewrite module installed (only for the optional X-Forwarded-For rule) - - Python 3.12 + a venv at APP_ROOT\venv with requirements.txt + waitress + - Python 3.14 + a venv at APP_ROOT\venv with requirements.txt + waitress - Secrets live in APP_ROOT\.env (wsgi.py load_dotenv() reads it). Keep them OUT of this file. Lock .env ACLs to the app pool identity + admins. diff --git a/docs/DEPLOY-WINDOWS-IIS.md b/docs/DEPLOY-WINDOWS-IIS.md index 5e47b9e..3c36736 100644 --- a/docs/DEPLOY-WINDOWS-IIS.md +++ b/docs/DEPLOY-WINDOWS-IIS.md @@ -13,7 +13,7 @@ physical path must be `APP_ROOT` (where `wsgi.py` lives). ## 0. Prerequisites on the box -- Python 3.12 (same minor as dev). `py -3.12 --version` to confirm. +- Python 3.14 (same minor as dev and CI). `py -3.14 --version` to confirm. - IIS with the **HttpPlatformHandler** module: https://www.iis.net/downloads/microsoft/httpplatformhandler - **URL Rewrite** module (only for the optional real-client-IP rule). @@ -39,7 +39,7 @@ Ship `frontend/dist` with the code (Node is not needed on the prod box). ```powershell cd C:\shopdb-flask -py -3.12 -m venv venv +py -3.14 -m venv venv venv\Scripts\python -m pip install --upgrade pip venv\Scripts\pip install -r requirements.txt ``` diff --git a/docs/DEVELOPMENT-SETUP.md b/docs/DEVELOPMENT-SETUP.md index ec14a19..db1be6f 100644 --- a/docs/DEVELOPMENT-SETUP.md +++ b/docs/DEVELOPMENT-SETUP.md @@ -147,7 +147,7 @@ python -m venv venv venv\Scripts\Activate.ps1 # bash/mac: source venv/bin/activate # If PowerShell blocks the activate script (execution policy), run once: # Set-ExecutionPolicy -Scope CurrentUser RemoteSigned -pip install -r requirements.txt +pip install -r requirements-dev.txt copy .env.example .env # bash/mac: cp .env.example .env # Edit .env - set SECRET_KEY, JWT_SECRET_KEY, and diff --git a/docs/INSTALL-WINDOWS-IIS.md b/docs/INSTALL-WINDOWS-IIS.md index 685a054..15c78a0 100644 --- a/docs/INSTALL-WINDOWS-IIS.md +++ b/docs/INSTALL-WINDOWS-IIS.md @@ -13,9 +13,9 @@ lives). Run PowerShell as Administrator. | Need | Notes | | --- | --- | -| **Python 3.13** (64-bit) | `python --version` | +| **Python 3.14** (64-bit) | `python --version` | | **IIS** with **HttpPlatformHandler** | https://www.iis.net/downloads/microsoft/httpplatformhandler (direct MSI: `download.microsoft.com/download/8/1/3/813AC4E6-9203-4F7A-8DD5-F3D54D10C5CD/httpPlatformHandler_amd64.msi`) | -| **MySQL 5.7+/8.0** (or 5.6 with the flags in step 1) | reachable from the app host | +| **MySQL 8.0** (standard for new installs) | reachable from the app host. 5.7+ is supported on an existing server; 5.6 needs the flags in step 1. CI and the container image both target 8.0. | | URL Rewrite (optional) | only for the real-client-IP rule; skip it and the app still runs | The app itself pulls in `waitress` and `tzdata` from `requirements.txt` (step 4). diff --git a/docs/PLUGIN-EXTERNAL-REPO.md b/docs/PLUGIN-EXTERNAL-REPO.md index b1610f1..6711bd9 100644 --- a/docs/PLUGIN-EXTERNAL-REPO.md +++ b/docs/PLUGIN-EXTERNAL-REPO.md @@ -85,7 +85,7 @@ ln -s ../../wjsf-shipping plugins/shipping # 3. Set up the framework as usual. python3 -m venv venv -venv/bin/pip install -r requirements.txt +venv/bin/pip install -r requirements-dev.txt # 4. Install (enable) your plugin. venv/bin/flask plugin install shipping @@ -239,7 +239,7 @@ else python3 -m venv "$WORKDIR/venv" PYTHON="$WORKDIR/venv/bin/python" "$PYTHON" -m pip install --upgrade pip >/dev/null - "$PYTHON" -m pip install -r "$FRAMEWORK/requirements.txt" + "$PYTHON" -m pip install -r "$FRAMEWORK/requirements-dev.txt" fi echo "==> Linking plugin '$PLUGIN_NAME' into framework plugins/" @@ -330,7 +330,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.14' - name: Fetch the harness from the framework run: | git clone --depth 1 --branch "$FRAMEWORK_REF" "$FRAMEWORK_URL" /tmp/framework diff --git a/requirements-dev.in b/requirements-dev.in new file mode 100644 index 0000000..c62f4b9 --- /dev/null +++ b/requirements-dev.in @@ -0,0 +1,12 @@ +# Development and CI dependencies. +# Production installs use requirements.txt only; these never ship to a site. +# +# Compile with: +# uv pip compile requirements-dev.in -o requirements-dev.txt + +-r requirements.in + +# Testing +pytest>=7.0 +pytest-flask>=1.2 +pytest-cov>=4.0 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..179ab5f --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,129 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements-dev.in -o requirements-dev.txt +alembic==1.18.5 + # via flask-migrate +blinker==1.9.0 + # via flask +cachelib==0.15.0 + # via flask-caching +certifi==2026.7.22 + # via requests +cffi==2.1.0 + # via cryptography +charset-normalizer==3.4.9 + # via requests +click==8.4.2 + # via + # -r requirements.in + # flask +coverage==7.15.2 + # via pytest-cov +cryptography==50.0.0 + # via -r requirements.in +dnspython==2.8.0 + # via email-validator +email-validator==2.3.0 + # via -r requirements.in +flask==3.1.3 + # via + # -r requirements.in + # flask-caching + # flask-cors + # flask-jwt-extended + # flask-marshmallow + # flask-migrate + # flask-sqlalchemy + # pytest-flask +flask-caching==2.4.1 + # via -r requirements.in +flask-cors==6.0.5 + # via -r requirements.in +flask-jwt-extended==4.7.4 + # via -r requirements.in +flask-marshmallow==1.5.0 + # via -r requirements.in +flask-migrate==4.1.0 + # via -r requirements.in +flask-sqlalchemy==3.1.1 + # via + # -r requirements.in + # flask-migrate +greenlet==3.5.4 + # via sqlalchemy +idna==3.18 + # via + # email-validator + # requests +iniconfig==2.3.0 + # via pytest +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +mako==1.3.12 + # via alembic +markupsafe==3.0.3 + # via + # flask + # jinja2 + # mako + # werkzeug +marshmallow==4.3.0 + # via + # flask-marshmallow + # marshmallow-sqlalchemy +marshmallow-sqlalchemy==1.5.0 + # via -r requirements.in +mysql-connector-python==26.7.0 + # via -r requirements.in +packaging==26.2 + # via pytest +pluggy==1.6.0 + # via + # pytest + # pytest-cov +pycparser==3.0 + # via cffi +pygments==2.20.0 + # via pytest +pyjwt==2.13.0 + # via flask-jwt-extended +pymysql==1.2.0 + # via -r requirements.in +pytest==9.1.1 + # via + # -r requirements-dev.in + # pytest-cov + # pytest-flask +pytest-cov==7.1.0 + # via -r requirements-dev.in +pytest-flask==1.3.0 + # via -r requirements-dev.in +python-dotenv==1.2.2 + # via -r requirements.in +requests==2.34.2 + # via -r requirements.in +sqlalchemy==2.0.51 + # via + # alembic + # flask-sqlalchemy + # marshmallow-sqlalchemy +tabulate==0.10.0 + # via -r requirements.in +typing-extensions==4.16.0 + # via + # alembic + # sqlalchemy +tzdata==2026.3 + # via -r requirements.in +urllib3==2.7.0 + # via requests +waitress==3.0.2 + # via -r requirements.in +werkzeug==3.1.8 + # via + # -r requirements.in + # flask + # flask-cors + # flask-jwt-extended + # pytest-flask diff --git a/requirements.in b/requirements.in index 46c7c3b..2cb58a0 100644 --- a/requirements.in +++ b/requirements.in @@ -24,10 +24,14 @@ requests>=2.31 # Security werkzeug>=3.0 +# Windows/IIS runtime +# waitress is the WSGI server IIS launches via HttpPlatformHandler. +# tzdata ships the IANA timezone database, which Windows does not provide; +# without it the notifications plugin fails on zoneinfo lookups. +# Both were previously hand-added to requirements.txt only, so a recompile +# would have silently dropped them from every Windows install. +waitress>=3.0 +tzdata + # Validation email-validator>=2.0 - -# Testing -pytest>=7.0 -pytest-flask>=1.2 -pytest-cov>=4.0 diff --git a/requirements.txt b/requirements.txt index 78ec85c..2dba136 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,14 +8,16 @@ cachelib==0.13.0 # via flask-caching certifi==2026.4.22 # via requests +cffi==2.1.0 + # via cryptography charset-normalizer==3.4.7 # via requests click==8.3.3 # via # -r requirements.in # flask -coverage==7.13.5 - # via pytest-cov +cryptography==50.0.0 + # via -r requirements.in dnspython==2.8.0 # via email-validator email-validator==2.3.0 @@ -29,7 +31,6 @@ flask==3.1.3 # flask-marshmallow # flask-migrate # flask-sqlalchemy - # pytest-flask flask-caching==2.4.0 # via -r requirements.in flask-cors==6.0.2 @@ -50,8 +51,6 @@ idna==3.13 # via # email-validator # requests -iniconfig==2.3.0 - # via pytest itsdangerous==2.2.0 # via flask jinja2==3.1.6 @@ -72,27 +71,11 @@ marshmallow-sqlalchemy==1.5.0 # via -r requirements.in mysql-connector-python==9.7.0 # via -r requirements.in -packaging==26.2 - # via pytest -pluggy==1.6.0 - # via - # pytest - # pytest-cov -pygments==2.20.0 - # via pytest +pycparser==3.0 + # via cffi pyjwt==2.12.1 # via flask-jwt-extended pymysql==1.1.3 -cryptography>=42.0 - # via -r requirements.in -pytest==9.0.3 - # via - # -r requirements.in - # pytest-cov - # pytest-flask -pytest-cov==7.1.0 - # via -r requirements.in -pytest-flask==1.3.0 # via -r requirements.in python-dotenv==1.2.2 # via -r requirements.in @@ -109,14 +92,15 @@ typing-extensions==4.15.0 # via # alembic # sqlalchemy +tzdata==2026.3 + # via -r requirements.in urllib3==2.7.0 # via requests +waitress==3.0.2 + # via -r requirements.in werkzeug==3.1.8 # via # -r requirements.in # flask # flask-cors # flask-jwt-extended - # pytest-flask -waitress>=3.0 -tzdata diff --git a/scripts/test-external-plugin.sh b/scripts/test-external-plugin.sh index 396e764..ad9980c 100755 --- a/scripts/test-external-plugin.sh +++ b/scripts/test-external-plugin.sh @@ -90,7 +90,7 @@ else python3 -m venv "$WORKDIR/venv" PYTHON="$WORKDIR/venv/bin/python" "$PYTHON" -m pip install --upgrade pip >/dev/null - "$PYTHON" -m pip install -r "$FRAMEWORK/requirements.txt" + "$PYTHON" -m pip install -r "$FRAMEWORK/requirements-dev.txt" fi echo "==> Linking plugin '$PLUGIN_NAME' into framework plugins/"