Replace the outdated-dependency shell filters with a configured script
Split out of #7019 so the tooling change lands on its own and the pin bumps rebase onto it.
The check lived as two inline shell blocks in check_outdated_dependencies.yml, so it could not be run outside CI, and the ignored package names were baked into grep patterns over the rendered report. scripts/check_outdated_deps.py {backend,frontend} now does the work and the workflow is two one-line steps, with the ignored names in [tool.check-outdated-deps] in the root pyproject.toml.
This is pure infrastructure: it carries over exactly the filters main already had and changes no pins.
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- New feature (non-breaking change which adds functionality)
Changes
Matching is on parsed package names, not a regex over the table. That removes the anchoring the grep needed to avoid swallowing siblings: a bare tailwindcss entry can no longer match tailwindcss-animated, and a plotly.js entry cannot match plotly.js-locales or react-plotly.js. Entries ending in * opt into prefix matching where it is actually wanted — @chakra-ui/* for the scope, ag-grid* for the three packages that release in lockstep (which the shell filter covered with both ag-grid and ag-grid-react).
Two config keys, because the two reasons a package is ignored want different treatment.
*_held— blocked on work we own. A held package already at its latest version means the blocker is gone and the entry should be deleted, so that errors. This keeps the list from accumulating entries that suppress nothing.*_pinned— pinned by policy or owned by another repo (pyright,ruff, the reflex-enterprise packages, theag-gridfamily). Reaching latest says nothing about whether the entry can go, so staleness is not checked.
Both lists start as the filters main already had, all of which are *_pinned. *_held is empty here and is where blocker-driven entries land — #7019 fills it in.
A side benefit of the staleness check: a typo'd entry reports matches no installed package instead of silently suppressing nothing.
Two parser details, verified against a real Bun 1.4.0 install rather than assumed
-
Bun right-pads the package column, so the name is trimmed out of the cell rather than matched as a fixed-width field.
-
Bun annotates rows outside
dependenciesin that cell —tailwindcss (dev),react (peer),clsx (optional):| Package | Current | Update | Latest | |----------------------------|---------|--------|--------| | @tailwindcss/postcss (dev) | 4.3.0 | 4.3.0 | 4.3.3 | | react (peer) | 19.2.0 | 19.2.0 | 19.2.8 | | clsx (optional) | 2.1.0 | 2.1.0 | 2.1.1 |The annotation is stripped before matching (npm names cannot contain spaces, so the suffix is unambiguous). This matters as soon as anything held is a devDependency — the docs app declares both Tailwind packages that way. Correspondingly, the declared-name set used for staleness covers all four sections Bun reports on, not just
dependenciesanddevDependencies.
Note on how the two jobs invoke the script
They differ deliberately, and both carry a comment saying why:
- backend runs in the project's synced environment, because
uv pip listinspects the active env. Using--scriptthere resolves an isolated env, and the check then reports "all up to date" while verifying nothing — a green check that checks nothing, worse than the shell it replaces. - frontend uses
--script, because that job only syncsdocs/appand the frontend path reads Bun pluspackage.jsonrather than the Python environment.
Testing
Note this workflow only triggers on release/** pushes and workflow_dispatch, so the job does not run on this PR. That is part of the point of making it runnable locally.
-
tests/units/test_check_outdated_deps.py— 23 cases: exact/prefix matching including the sibling-swallowing regressions, both staleness modes, pinned-never-stale, Bun's padded table, all three annotation kinds, and an end-to-end case asserting a(dev)row is suppressed by its exact hold. -
Both real invocations exercised against this checkout, not just the unit tests:
$ uv run python scripts/check_outdated_deps.py backend Held back (1), not reported: ruff Outdated backend dependencies found: alembic, bidict, charset-normalizer, ... $ uv run --script scripts/check_outdated_deps.py frontend --web-dir <app>/.web -
uv run ruff check ./ruff format --check .,uv run pyright, anduv lock --checkclean.
No news fragment: root_source_dirs is reflex/, and this touches only .github/, scripts/, tests/ and pyproject.toml.
🤖 Generated with Claude Code
https://claude.ai/code/session_01RKQ4ARC2vkE5iKoULyaczz
Generated by Claude Code