Fix pre-commit OOM: oom_score_adj + Node heap caps

- Set oom_score_adj=1000 in git hooks so OOM killer targets
  pre-commit first, never crashing the PC
- Cap Node.js heap to 512MB for eslint/prettier/vitest
- Remove broken systemd-run cgroup wrapper (didn't work)
- cppcheck: process files one-at-a-time, --check-level=normal
- pytest: run packages sequentially in separate subprocesses
- Remove --force from cppcheck (exponential memory on #ifdef combos)
This commit is contained in:
Krzysztof kuhy Rudnicki 2026-04-12 21:34:56 +02:00
parent dee307700e
commit 5fcecd6cf2
7 changed files with 67 additions and 22 deletions

View File

@ -307,6 +307,7 @@ repos:
types_or: [yaml, json, markdown]
exclude: ^(Bash/|\.venv/|.*\.lock$|C/compile_commands\.json)
stages: [pre-push]
args: [--no-cache]
# ===========================================================================
# SHELLCHECK - Shell script linting
@ -463,7 +464,7 @@ repos:
hooks:
- id: ts-battery-status-tests
name: TS battery-status vitest (100% coverage)
entry: bash -c 'cd TS/battery-status && npx vitest run --coverage'
entry: bash -c 'cd TS/battery-status && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
language: system
files: ^TS/battery-status/
pass_filenames: false
@ -471,7 +472,7 @@ repos:
- id: ts-champions-leauge-scores-tests
name: TS champions_leauge_scores vitest (100% coverage)
entry: bash -c 'cd TS/champions_leauge_scores && npx vitest run --coverage'
entry: bash -c 'cd TS/champions_leauge_scores && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
language: system
files: ^TS/champions_leauge_scores/
pass_filenames: false
@ -479,7 +480,7 @@ repos:
- id: ts-two-inputs-tests
name: TS two-inputs vitest (100% coverage)
entry: bash -c 'cd TS/two-inputs && npx vitest run --coverage'
entry: bash -c 'cd TS/two-inputs && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
language: system
files: ^TS/two-inputs/
pass_filenames: false

View File

@ -67,5 +67,6 @@
],
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["python_pkg"],
"python-envs.alwaysUseUv": true
"python-envs.alwaysUseUv": true,
"cmake.sourceDirectory": "/home/kuhy/testsAndMisc/pomodoro_app/linux"
}

View File

@ -267,7 +267,7 @@ class PhoneVerificationMixin:
Returns:
True if the latest finish time is within 24 hours of now.
"""
max_age_seconds = 24 * 3600
max_age_seconds = 24 * 3600 # accept same-day workouts
try:
conn = sqlite3.connect(str(db_path))
try:

View File

@ -304,7 +304,9 @@ class ScreenLocker(
"HMAC key unavailable — accepting unsigned entry",
)
return True
_logger.warning("HMAC verification failed for today's log entry")
_logger.warning(
"HMAC verification failed for today's log entry",
)
return False
def _load_existing_logs(self) -> dict:

View File

@ -795,7 +795,7 @@ class TestIsWorkoutFinishRecent:
mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test returns False for workout that finished >4 hours ago."""
"""Test returns False for workout that finished >24 hours ago."""
locker = create_locker(mock_tk, tmp_path)
db_file = tmp_path / "sl_test.db"
conn = sqlite3.connect(str(db_file))
@ -803,9 +803,9 @@ class TestIsWorkoutFinishRecent:
"CREATE TABLE workouts "
"(id TEXT PRIMARY KEY, start INTEGER, finish INTEGER)",
)
# Finished 5 hours ago (but still "today" in local time)
# Finished 25 hours ago (not "today" in local time either)
now_ms = int(time.time() * 1000)
old_finish = now_ms - 5 * 3600 * 1000
old_finish = now_ms - 25 * 3600 * 1000 # beyond 24h window
conn.execute(
"INSERT INTO workouts VALUES (?, ?, ?)",
("w1", old_finish - 3600000, old_finish),

View File

@ -154,6 +154,60 @@ class TestHasLoggedToday:
):
assert locker.has_logged_today() is False
def test_today_unsigned_entry_no_hmac_key(
self,
mock_tk: MagicMock,
mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Accept unsigned entry when HMAC key is unavailable."""
log_file = tmp_path / "workout_log.json"
today = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d")
log_file.write_text(
json.dumps({today: {"workout": "data"}}),
)
locker = create_locker(mock_tk, tmp_path)
locker.log_file = log_file
with (
patch(
"python_pkg.screen_locker.screen_lock.verify_entry_hmac",
return_value=False,
),
patch(
"python_pkg.screen_locker.screen_lock._load_hmac_key",
return_value=None,
),
):
assert locker.has_logged_today() is True
def test_today_unsigned_entry_with_hmac_key(
self,
mock_tk: MagicMock,
mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Reject unsigned entry when HMAC key IS available."""
log_file = tmp_path / "workout_log.json"
today = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d")
log_file.write_text(
json.dumps({today: {"workout": "data"}}),
)
locker = create_locker(mock_tk, tmp_path)
locker.log_file = log_file
with (
patch(
"python_pkg.screen_locker.screen_lock.verify_entry_hmac",
return_value=False,
),
patch(
"python_pkg.screen_locker.screen_lock._load_hmac_key",
return_value=b"secret-key",
),
):
assert locker.has_logged_today() is False
def test_other_day_logged(
self,
mock_tk: MagicMock,

View File

@ -12,12 +12,10 @@ all tests are run as a fallback.
from __future__ import annotations
from pathlib import Path, PurePosixPath
import shutil
import subprocess
import sys
_MIN_SUBPACKAGE_DEPTH = 2
_MEMORY_CAP = "3G"
def _affected_packages(files: list[str]) -> set[str] | None:
@ -90,19 +88,8 @@ def main() -> int:
return 0
# Run each package in its own subprocess so memory is freed between runs.
# Wrap in systemd-run cgroup to hard-cap memory per package.
use_cgroup = shutil.which("systemd-run") is not None
for pkg in sorted(packages):
cmd = _build_pytest_command({pkg})
if use_cgroup:
cmd = [
"systemd-run",
"--user",
"--scope",
"-p",
f"MemoryMax={_MEMORY_CAP}",
*cmd,
]
result = subprocess.run(cmd, check=False)
if result.returncode != 0:
return result.returncode