Latest stable dependencies, gatelock v0.7.2, and the gate

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-08-28 17:38:25 +02:00
parent 924630c029
commit 1cf567977f
7 changed files with 248 additions and 97 deletions

View File

@ -0,0 +1,62 @@
name: dependency freshness
# Every dependency on its ecosystem's newest STABLE release, enforced on push
# as well as at commit time. The local pre-commit hook only sees the manifests
# in a commit and can be skipped entirely by a fresh clone that never ran
# `pre-commit install`; this runs `--all` over the whole repo.
#
# `--strict` is CI-only. Offline the gate degrades to its cached answer and
# passes, because `--no-verify` is banned locally and a hook that hard-failed
# on a flaky connection would leave no way to commit. A runner is always
# online, so here an undeterminable answer is a real failure.
#
# Expect this to go red for reasons this repo's commit did not cause: an
# upstream release overnight is enough. That is the chosen behaviour -- the
# alternative is a gate that only notices staleness when someone remembers to
# look.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
dependency-freshness:
name: dependencies on newest stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The gate lives in the shared utils repo so every repo resolves the
# same "latest stable" the same way. Checking it out beats vendoring a
# copy, which is exactly how a gate and its survey start disagreeing.
- name: Check out the shared gate
uses: actions/checkout@v4
with:
repository: kuhyx/utils
path: .utils
# "3.x", never a pinned minor: the Python target IS the interpreter the
# gate runs under (registries/toolchain.python_installed), so a runner
# held a release behind this machine reports a repo's `requires-python`
# as excluding "latest" while the same check passes locally. flax-docs-mcp
# hit exactly that -- `>=3.14` green here, red on a 3.13 runner.
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install the gate's two runtime dependencies
run: python -m pip install --upgrade pyyaml packaging
- name: Cache registry lookups
uses: actions/cache@v4
with:
path: ~/.cache/dep-freshness
key: dep-freshness-${{ hashFiles('**/pubspec.yaml', '**/pyproject.toml',
'**/requirements*.txt', '**/package.json', '**/Cargo.toml', '**/go.mod') }}
restore-keys: dep-freshness-
- name: Every dependency is on newest stable
run: bash .utils/scripts/check_dependency_freshness.sh --all --strict

View File

@ -592,3 +592,25 @@ repos:
entry: scripts/check_md_naming.sh entry: scripts/check_md_naming.sh
language: system language: system
files: \.(md|markdown)$ files: \.(md|markdown)$
# Every dependency on its ecosystem's newest STABLE release, exact-pinned
# so the question is mechanically answerable at all.
#
# `files:` is what keeps this cheap: a commit touching only source makes
# ZERO network requests, and manifest commits mostly hit a 6h disk cache.
#
# Runs WITHOUT --strict on purpose. Offline it degrades to the cached
# answer and passes with a loud banner; `--no-verify` is banned here, so
# a hook that hard-failed on a flaky connection would leave no way to
# commit at all. CI, always online, is the strict authority.
- id: dependency-freshness
name: dependencies on newest stable
entry: scripts/check_dependency_freshness.sh
language: system
pass_filenames: true
files: >-
(pubspec\.(yaml|lock)|pyproject\.toml|requirements.*\.txt|uv\.lock
|package\.json|package-lock\.json|pnpm-lock\.yaml
|Cargo\.(toml|lock)|go\.(mod|sum)
|\.fvmrc|\.nvmrc|\.python-version
|\.github/workflows/.*\.ya?ml)$

View File

@ -0,0 +1,33 @@
{
"intent": "Put every dependency this repo declares on its newest stable release, and install the shared dependency-freshness gate so it stays there.",
"scope": [
"meta/requirements.txt, meta/requirements-test.txt, python_pkg/random_jpg/requirements.txt",
".pre-commit-config.yaml, .github/workflows/dependency-freshness.yml, scripts/check_dependency_freshness.sh",
"Non-goal: no source changes; this is manifests and gate wiring only"
],
"changes": [
"Exact-pinned 95 previously range-specified dependencies to their newest stable release, via ~/.claude/scripts/apply_dep_pins.py driven by the gate's own --json findings.",
"Moved the gatelock git-tag dependency from gatelock-v0.1.0 to gatelock-v0.7.2 -- six minor versions of drift that a PEP 508 direct reference hid from every previous check.",
"Installed the shared gate's three pieces (delegate, pre-commit hook, CI workflow) from ~/utils/dep_freshness/templates."
],
"verification": [
{
"command": "REQUIREMENTS_FILE=meta/requirements.txt ./meta/scripts/ci_mirror.sh",
"result": "pass",
"evidence": "exit 0; clean venv rebuilt from the repinned meta/requirements.txt, pre-commit --all-files and the full suite green inside it"
},
{
"command": "~/utils/scripts/check_dependency_freshness.sh --all",
"result": "pass",
"evidence": "exit 0 -- 97 findings before, 0 after"
}
],
"risks": [
"gatelock v0.1.0 -> v0.7.2 is a six-version jump in a shared library; the clean-venv mirror is what exercises it here.",
"Exact pins mean CI goes red on any upstream release until the pin is bumped. That is the chosen behaviour, not a regression."
],
"rollback": [
"git revert the commit; the pins and the three gate files are the whole change.",
"After reverting, re-run ci_mirror.sh to confirm the older pins still resolve."
]
}

View File

@ -21,20 +21,20 @@
# suite itself fails loudly here on a missing import. # suite itself fails loudly here on a missing import.
# ============================================================================== # ==============================================================================
anthropic>=0.25.0 anthropic==1.2.0
beautifulsoup4>=4.0 beautifulsoup4==4.15.0
coverage>=7.4.0 coverage==7.15.4
defusedxml>=0.7.1 defusedxml==0.7.1
markdownify>=1.2.0 markdownify==1.2.3
numpy>=1.20 numpy==2.5.2
pillow>=10.0 pillow==12.3.0
pytest>=8.0.0 pytest==9.1.1
pytest-cov>=4.1.0 pytest-cov==7.1.0
pytest-randomly>=3.15.0 pytest-randomly==4.1.0
pytest-sugar>=1.0.0 pytest-sugar==1.1.1
pytest-timeout>=2.2.0 pytest-timeout==2.4.0
pytest-xdist>=3.5.0 pytest-xdist==3.8.0
requests>=2.0 requests==2.34.2
rich>=13.0 rich==15.0.0
typer>=0.12.0 typer==0.27.2
typing_extensions typing_extensions==4.16.0

View File

@ -4,82 +4,82 @@
# Sorted alphabetically (enforced by pre-commit `requirements-txt-fixer`). # Sorted alphabetically (enforced by pre-commit `requirements-txt-fixer`).
# ============================================================================== # ==============================================================================
add-trailing-comma>=3.1.0 add-trailing-comma==4.0.0
aiohttp>=3.9 aiohttp==3.14.3
anthropic>=0.25.0 anthropic==1.2.0
autoflake>=2.2.0 autoflake==2.4.0
autopep8>=2.0.0 autopep8==2.3.2
bandit>=1.7.0 bandit==1.9.4
beautifulsoup4>=4.0 beautifulsoup4==4.15.0
berserk>=0.13 berserk==0.14.0
black>=24.0.0 black==26.5.1
bottle>=0.12 bottle==0.13.4
codespell>=2.2.0 codespell==2.4.3
coverage>=7.4.0 coverage==7.15.4
darglint>=1.8.0 darglint==1.8.1
dead>=1.5.0 dead==2.1.0
defusedxml>=0.7.1 defusedxml==0.7.1
docformatter>=1.7.0 docformatter==1.7.8
fixit>=2.1.0 fixit==2.2.1
flake8>=7.0.0 flake8==7.3.0
flake8-annotations>=3.0.0 flake8-annotations==3.2.0
flake8-bandit>=4.1.0 flake8-bandit==4.1.1
flake8-bugbear>=24.0.0 flake8-bugbear==25.11.29
flake8-comprehensions>=3.14.0 flake8-comprehensions==3.17.0
flake8-docstrings>=1.7.0 flake8-docstrings==1.7.0
flake8-eradicate>=1.5.0 flake8-eradicate==1.5.0
flake8-pie>=0.16.0 flake8-pie==0.16.0
flake8-print>=5.0.0 flake8-print==5.0.0
flake8-pyi>=24.0.0 flake8-pyi==26.5.0
flake8-pytest-style>=2.0.0 flake8-pytest-style==2.2.0
flake8-return>=1.2.0 flake8-return==1.2.0
flake8-simplify>=0.21.0 flake8-simplify==0.30.0
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.1.0#subdirectory=gatelock gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.7.2#subdirectory=gatelock
genanki>=0.13 genanki==0.13.1
geopandas>=1.0 geopandas==1.1.4
howlongtobeatpy>=1.0 howlongtobeatpy==1.0.23
hypothesis>=6.98.0 hypothesis==6.165.10
importlib-metadata>=7.0.0 importlib-metadata==9.0.1
interrogate>=1.5.0 interrogate==1.7.0
isort>=5.13.0 isort==9.0.1
lxml>=5.0 lxml==6.1.2
markdownify>=1.2.0 markdownify==1.2.3
matplotlib>=3.0 matplotlib==3.11.1
mccabe>=0.7.0 mccabe==0.7.0
mypy>=1.8.0 mypy==2.3.1
numpy>=1.20 numpy==2.5.2
opencv-python>=4.0 opencv-python==5.0.0.93
pillow>=10.0 pillow==12.3.0
pip-audit>=2.6.0 pip-audit==2.10.1
pipdeptree>=2.14.0 pipdeptree==4.2.2
pre-commit>=3.6.0 pre-commit==4.6.2
prospector>=1.10.0 prospector==1.19.1
pycodestyle>=2.11.0 pycodestyle==2.14.0
pydocstyle>=6.3.0 pydocstyle==6.3.0
pyflakes>=3.2.0 pyflakes==3.4.0
pygame>=2.0 pygame==2.6.1
pylint>=3.0.0 pylint==4.0.7
pyright>=1.1.350 pyright==1.1.411
pytest>=8.0.0 pytest==9.1.1
pytest-cov>=4.1.0 pytest-cov==7.1.0
pytest-randomly>=3.15.0 pytest-randomly==4.1.0
pytest-sugar>=1.0.0 pytest-sugar==1.1.1
pytest-timeout>=2.2.0 pytest-timeout==2.4.0
pytest-xdist>=3.5.0 pytest-xdist==3.8.0
python-chess>=1.999 python-chess==1.999
python-kasa>=0.7 python-kasa==0.10.2
pyupgrade>=3.15.0 pyupgrade==3.21.2
radon>=6.0.0 radon==6.0.1
reorder-python-imports>=3.12.0 reorder-python-imports==3.17.0
requests>=2.0 requests==2.34.2
rich>=13.0 rich==15.0.0
ruff>=0.8.0 ruff==0.16.5
safety>=2.3.0 safety==3.8.1
selenium>=4.0 selenium==4.48.0
typer>=0.12.0 typer==0.27.2
types-python-dateutil>=2.8.0 types-python-dateutil==2.9.0.20260807
types-PyYAML>=6.0.0 types-PyYAML==6.0.12.20260815
types-requests>=2.31.0 types-requests==2.33.0.20260712
types-setuptools>=69.0.0 types-setuptools==84.0.0.20260812
typing_extensions # Self annotation on py310 target (session_autopsy store lock) typing_extensions==4.16.0# Self annotation on py310 target (session_autopsy store lock)
websockets>=13.0 websockets==17.1

View File

@ -1 +1 @@
pillow pillow==12.3.0

View File

@ -0,0 +1,34 @@
#!/bin/bash
# ============================================================================
# Fail if any dependency in the commit is behind its ecosystem's newest stable.
#
# Thin delegate to the shared gate in ~/utils, which owns the registry
# adapters, the pre-release rules and the allowlist semantics. Copying that
# logic here is what lets one repo's idea of "current" drift from every other
# repo's -- so this script only locates the shared checker and forwards its
# arguments.
#
# Usage:
# scripts/check_dependency_freshness.sh <file> [<file> ...] # pre-commit
# scripts/check_dependency_freshness.sh --all --strict # CI
#
# Exit: 0 current | 1 behind | 2 allowlist wrong | 3 undeterminable (offline)
# ============================================================================
set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_dependency_freshness.sh"
main() {
if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared dependency-freshness gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2
exit 1
fi
exec bash "$SHARED_GATE" "$@"
}
main "$@"