Skip to content

Dependency Policy

zach115th edited this page Aug 5, 2026 · 2 revisions

Dependency Policy

Why some dependencies sit well behind their latest release, and what it would take to move them. Everything here is deliberate — if you are wondering why an automated bump keeps being closed, this is the page.

Held back on purpose

Dependency Held at Why
PostgreSQL 17 A major Postgres version is a data migration, not a dependency bump
jQuery 3.x jQuery 4 removes APIs used in ~86 places, and Bootstrap 4 forbids it
Node 24 LTS Odd-numbered Node releases never get an LTS phase
marshmallow 3.x marshmallow 4 is a schema-wide migration
docxcompose / python-docx / docxtpl pinned The vendored report generator hard-pins them

PostgreSQL

A PostgreSQL 18 server will not start on a PostgreSQL 17 data directory. Bumping the image tag means every existing installation comes up with a crash-looping database, not a warning. Moving majors requires a pg_dumpall, removing the volume, starting the new version clean, and restoring — the shape scripts/migrate_postgres_17.sh implements for the 12 → 17 move.

There is also no urgency: PostgreSQL 17 is supported until 2029. The 12 → 17 move was forced by end of life; this one is not.

jQuery

The interface is Bootstrap 4 and jQuery 3. Two independent blockers:

  • Bootstrap 4 declares jquery: "1.9.1 - 3". jQuery 4 is outside the range its dropdowns, modals, tabs, tooltips and collapses are built against.
  • jQuery 4 removes APIs this codebase uses in roughly 86 places.bind(), $.proxy, $.trim, $.isFunction, $.isArray, .unbind(), $.parseJSON and others, before counting the bundled plugins.

Nothing fails at build time. The bundle would build, ship, and break in the browser one interaction at a time. Migrating means moving to Bootstrap 5 first — which drops jQuery entirely — then replacing jQuery UI and sweeping the removed APIs.

Node

Node alternates: even-numbered releases become LTS, odd-numbered ones are "Current" for about six months and then reach end of life without ever becoming LTS. An automated bump sees a higher number, not the support policy — Node 25 was already past end of life when it was first proposed.

Node is build-time only here: it compiles the UI bundle, and the runtime image contains no Node binary. A major bump has also been verified to produce byte-identical bundles, so tracking Current buys nothing. The project moves to the next LTS deliberately.

marshmallow

flask-marshmallow 1.5.0 and later require marshmallow>=4.0.0, so flask-marshmallow is held at 1.4.0 — the last release that still accepts marshmallow 3. marshmallow 4 changes schema semantics across every serializer in datamgmt/, which is a project rather than an upgrade.

The report generator

docx_generator is vendored as a wheel and hard-pins docxcompose, python-docx and docxtpl. Bumping any of them breaks .docx report generation. This stack is deliberately frozen; see Development Guide.

Kept current

Everything else tracks upstream, including SQLAlchemy, alembic, celery, Flask and its extensions, cryptography, requests, and the nginx and Python base images.

setuptools is kept current despite a complication worth knowing about: setuptools 81 removed pkg_resources, which two dependencies still import at module load. A build-time patch (source/patches/depatch_pkg_resources.py) rewrites those imports to importlib equivalents. It is content-matched and fails the build loudly if a future version moves the code, rather than regressing silently.

Verifying a dependency change

A green build is not evidence that a Python dependency upgrade worked. The pkg_resources case is the reference example: pip install succeeds, docker build succeeds, the image is produced — and the application dies at startup, because the missing module is only imported when the app boots.

Check the things that actually break:

docker compose -f docker-compose.dev.yml build app
docker compose -f docker-compose.dev.yml up -d --force-recreate --no-deps app worker ai_worker
docker logs --tail 80 iriswebapp_app | grep -iE "traceback|failed to boot"
curl -sk -o /dev/null -w "%{http_code}\n" https://127.0.0.1/login

For specific areas: generate a .docx report after touching the reporter stack, query /graphql after touching graphene, confirm alembic_version advanced after touching alembic, and exercise a CSRF-protected POST after touching Flask-WTF.

Version comparison

The release string IRIS-NG-v<major>.<minor>.<patch> is not valid PEP 440, so packaging.version.parse() rejects it. Code comparing IRIS-NG versions must strip the product prefix first — parse_iris_version() in updater.py does this. Before IRIS-NG-v1.1.1 the comparison relied on LegacyVersion, which packaging removed in version 22.

See also: Development Guide · Changelog

Clone this wiki locally