mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-10 19:27:14 +01:00
Third app onto freedays (~/utils/freedays), the shared pool of pre-declared days when every gate app stands down. due_slots() returns nothing on a free day, so no slot is ever due, the lock never opens and nothing is said about the missing meals. Read from one local file, never the network, so this cannot hang the systemd-fired gate -- and an unreadable pool reads as "not free", which means the normal rules apply rather than the gate quietly switching itself off. Tested explicitly: fail closed, not open. The conftest redirect is load-bearing. The pool lives under ~/.local/share, outside every diet_guard path the suite already isolates, so without it a real free day on this machine would fail every "the gate is due" test here for a reason that looks nothing like the cause. gatelock moves v0.7.2 -> v0.7.3, and the pylint hook's own pins (which had drifted further, to gatelock v0.7.1 and crdt-sync v0.6.0) come with it. Not cosmetic: pip refuses two different direct URLs for one distribution, so any stale pin alongside freedays -- which requires v0.7.3 -- is an unresolvable environment rather than a warning. requirements.txt also picks up pylint 4.0.8, pytest-randomly 5.0.0 and ruff 0.16.6, which the freshness gate flagged once this file was touched. 1043 tests pass at 100% coverage across the pytest-randomly major bump. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
235 lines
9.1 KiB
YAML
235 lines
9.1 KiB
YAML
# Pre-commit Configuration for diet-guard
|
|
# Run `pre-commit install` after cloning: pre-commit hooks are not automatic.
|
|
# Install: pre-commit install && pre-commit install --hook-type pre-push
|
|
# Run: pre-commit run --all-files
|
|
# Update: pre-commit autoupdate
|
|
|
|
default_language_version:
|
|
python: python3
|
|
default_stages: [pre-commit]
|
|
fail_fast: false
|
|
|
|
repos:
|
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
rev: v4.6.0
|
|
hooks:
|
|
- id: trailing-whitespace
|
|
args: [--markdown-linebreak-ext=md]
|
|
- id: end-of-file-fixer
|
|
- id: check-yaml
|
|
- id: check-json
|
|
- id: check-toml
|
|
- id: check-added-large-files
|
|
args: [--maxkb=2000]
|
|
- id: check-merge-conflict
|
|
- id: detect-private-key
|
|
- id: debug-statements
|
|
- id: name-tests-test
|
|
args: [--pytest-test-first]
|
|
# Shared fakes and fixtures, not test modules. Split out of conftest.py
|
|
# and test_sync.py only because those files passed the 250-line cap.
|
|
exclude: ^diet_guard/tests/_(tk_fakes(_containers|_canvas)?|gate_layout_probe|gate_focus_probe|gate_measurement|gate_keyboard_probe|gate_fixtures|sync_fakes|kuchnia_fakes)\.py$
|
|
- id: check-ast
|
|
- id: mixed-line-ending
|
|
args: [--fix=lf]
|
|
- id: requirements-txt-fixer
|
|
|
|
- repo: local
|
|
hooks:
|
|
- id: no-noqa
|
|
name: Block noqa comments
|
|
entry: '(?i)#\s*(noqa|type:\s*ignore)'
|
|
language: pygrep
|
|
types: [python]
|
|
- id: no-ruff-noqa
|
|
name: Block ruff noqa file-level comments
|
|
entry: '(?i)#\s*ruff:\s*noqa'
|
|
language: pygrep
|
|
types: [python]
|
|
- id: requirements-sync
|
|
name: requirements.txt covers all package imports
|
|
entry: python3 scripts/check_requirements_sync.py
|
|
language: system
|
|
pass_filenames: false
|
|
files: ^(diet_guard/.*\.py|requirements\.txt)$
|
|
|
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
rev: v0.15.2
|
|
hooks:
|
|
- id: ruff
|
|
args: [--fix, --unsafe-fixes, --exit-non-zero-on-fix, --show-fixes]
|
|
types_or: [python, pyi]
|
|
- id: ruff-format
|
|
types_or: [python, pyi]
|
|
|
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
rev: v1.13.0
|
|
hooks:
|
|
- id: mypy
|
|
args:
|
|
- --ignore-missing-imports
|
|
- --no-error-summary
|
|
- --disable-error-code=no-untyped-def
|
|
- --disable-error-code=no-untyped-call
|
|
- --disable-error-code=var-annotated
|
|
- --disable-error-code=no-any-unimported
|
|
- --disable-error-code=type-arg
|
|
- --disable-error-code=no-any-return
|
|
- --disable-error-code=misc
|
|
- --disable-error-code=unused-ignore
|
|
- --disable-error-code=unreachable
|
|
- --disable-error-code=assignment
|
|
- --disable-error-code=no-redef
|
|
- --disable-error-code=attr-defined
|
|
- --disable-error-code=arg-type
|
|
- --disable-error-code=union-attr
|
|
- --disable-error-code=call-overload
|
|
- --disable-error-code=return-value
|
|
- --disable-error-code=redundant-cast
|
|
- --disable-error-code=empty-body
|
|
- --disable-error-code=list-item
|
|
additional_dependencies:
|
|
- types-requests
|
|
|
|
- repo: https://github.com/pylint-dev/pylint
|
|
rev: v3.3.2
|
|
hooks:
|
|
- id: pylint
|
|
args:
|
|
- --rcfile=pyproject.toml
|
|
- --fail-under=10
|
|
- --jobs=4
|
|
additional_dependencies:
|
|
- pytest
|
|
- requests
|
|
# Kept in step with pyproject.toml on purpose: pip refuses two
|
|
# different direct URLs for one distribution, so a stale pin here
|
|
# becomes an unresolvable hook environment the moment freedays --
|
|
# which requires gatelock v0.7.3 -- joins the list.
|
|
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.7.3#subdirectory=gatelock
|
|
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync
|
|
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.1.0#subdirectory=freedays
|
|
- mcp>=2,<3
|
|
- pydantic
|
|
# Test suites intentionally use patterns (protected-access, magic-value
|
|
# comparisons) that don't belong in the source-code 10/10 gate.
|
|
exclude: ^(\.venv/)|(^|/)(tests/|conftest\.py)
|
|
|
|
- repo: https://github.com/PyCQA/bandit
|
|
rev: 1.7.10
|
|
hooks:
|
|
- id: bandit
|
|
args:
|
|
- -c
|
|
- pyproject.toml
|
|
- --severity-level=high
|
|
- --confidence-level=medium
|
|
- --skip=B113
|
|
additional_dependencies: ["bandit[toml]"]
|
|
exclude: ^(tests/|.*test.*\.py$)
|
|
|
|
- repo: local
|
|
hooks:
|
|
- id: ci-mirror
|
|
name: CI mirror (clean-venv pytest + pre-commit --all-files)
|
|
entry: bash scripts/ci_mirror.sh
|
|
language: system
|
|
pass_filenames: false
|
|
always_run: true
|
|
require_serial: true
|
|
stages: [pre-push]
|
|
|
|
- repo: https://github.com/codespell-project/codespell
|
|
rev: v2.3.0
|
|
hooks:
|
|
- id: codespell
|
|
args:
|
|
- --skip=*.json,*.lock,.git,__pycache__,.venv
|
|
|
|
- repo: local
|
|
hooks:
|
|
# Fails a commit that still points crdt_sync at a local path. Local
|
|
# tests would go green against a library CI has never seen, silently.
|
|
- id: crdt-sync-override
|
|
name: no local crdt_sync dependency_override
|
|
entry: bash scripts/check_no_crdt_sync_override.sh app/pubspec.yaml
|
|
language: system
|
|
pass_filenames: false
|
|
always_run: true
|
|
# Every commit ships its own patch bump, so the release workflow's clean
|
|
# vX.Y.Z tag tracks HEAD instead of whatever version was last hand-edited.
|
|
# The Flutter app lives in app/, so point the script at its pubspec.
|
|
- id: bump-patch-version
|
|
name: Bump patch version in app/pubspec.yaml
|
|
entry: bash scripts/bump_patch_version.sh --pubspec app/pubspec.yaml
|
|
language: system
|
|
pass_filenames: false
|
|
always_run: true
|
|
require_serial: true
|
|
- id: shellcheck
|
|
name: shellcheck
|
|
entry: bash -c 'printf "%s\0" "$@" | xargs -0 -n 40 shellcheck --severity=warning' --
|
|
language: system
|
|
types: [shell]
|
|
# A PKGBUILD is a makepkg manifest, not a standalone script: it has no
|
|
# shebang (SC2148) and its variables are read by makepkg rather than by
|
|
# the file itself (SC2034). namcap is what actually lints these.
|
|
exclude: '^packaging/aur/PKGBUILD$'
|
|
# The shared 250-line cap. Checks only the files in the commit, so an
|
|
# unrelated commit never breaks; exits 1 rather than warning. No
|
|
# baseline and no allowlist -- those are suppressions. The exemption
|
|
# list (generated/vendored/data files) lives with the checker in
|
|
# ~/utils/file_length/config.py so the gate and the survey cannot
|
|
# disagree.
|
|
#
|
|
# Deliberately unfiltered: no types_or and no files. The cap applies to
|
|
# Dart and prose too, and a type filter here is what would silently
|
|
# exempt the whole Flutter app and every .md.
|
|
- id: file-length
|
|
name: file length <= 250 lines
|
|
entry: scripts/check_file_length.sh
|
|
language: system
|
|
|
|
# unittest.mock.patch fails OPEN: once a refactor moves a name, a patch
|
|
# target that still imports redirects nothing, the test passes, and the
|
|
# suite writes to the real ~/.local/share/diet_guard. This resolves every
|
|
# patch target statically so a stale path fails the commit instead.
|
|
- id: patch-targets
|
|
name: test patch targets resolve
|
|
entry: python3 scripts/check_patch_targets.py
|
|
language: system
|
|
pass_filenames: false
|
|
files: ^diet_guard/
|
|
|
|
# Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the
|
|
# rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may.
|
|
# Shared gate in ~/utils owns the namespaces and the exemption list, so
|
|
# one repo's idea of a valid name cannot drift from every other repo's.
|
|
- id: md-naming
|
|
name: markdown naming convention
|
|
entry: scripts/check_md_naming.sh
|
|
language: system
|
|
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)$
|