Skip to content

fix(config): preflight /config writability and surface it clearly (#278)#279

Merged
stevezau merged 3 commits into
devfrom
feat/config-writability-preflight
Jul 11, 2026
Merged

fix(config): preflight /config writability and surface it clearly (#278)#279
stevezau merged 3 commits into
devfrom
feat/config-writability-preflight

Conversation

@stevezau

Copy link
Copy Markdown
Owner

Summary

Fixes #278. A read-only /config (host permission mismatch — common on TrueNAS where the dataset isn't owned by the container's PUID/PGID) surfaced as SQLite's opaque attempt to write a readonly database in two confusing ways:

  • Scans looked frozen — the scan ran, but every job-state persist to jobs.db failed, flooding the log once per item while the UI never updated.
  • Schedule creation failed writing scheduler.db, and the catch-all blamed "a malformed cron expression or a clash with an existing schedule" — the wrong cause entirely.

Changes

  • web/config_health.py (new)probe_config_health(): authoritative create+delete probe, plus /proc/mounts (tells ownership vs :ro mount apart), statvfs (low-space), and network-FS (NFS/SMB) detection — each with an actionable fix. Probe file is uniquely named per attempt so concurrent gthread probes (shared PID) can't race into a false "unwritable".
  • web/app.py — startup preflight logs one clear error at boot instead of a per-item flood.
  • routes/api_system.pyGET /api/system/config-health (cheap, no GPU) feeding the banner; folds in live media-mount health.
  • routes/api_jobs.py — 503 guard on create_job/create_manual_job: refuses to start a scan that can't persist, with the fix.
  • routes/api_schedules.py — detects the readonly-DB case → 503 + chown hint instead of the misleading cron 500.
  • templates/base.html + static/js/app.js — global banner on every page: red when /config isn't writable, yellow for network-FS / low-space / missing media mount.

Verification

  • New tests/test_config_health.py — 12 tests (probe matrix incl. :ro mount, low-space, network-fs, cleanup-race regression; the two schedule error cells; both 503 guards).
  • Full suite: 3227 passed, 83.6% coverage.
  • Live UI check (throwaway instance, real read-only /config): the red banner renders with the exact chown hint; POST /api/jobs and POST /api/schedules both return 503 with the fix; banner clears on the next poll once perms are fixed (no restart). Architecture Review run pre-commit (1 MED + 2 LOW, all fixed).

🤖 Generated with Claude Code

A read-only /config (host permission mismatch — common on TrueNAS where
the dataset isn't owned by the container's PUID/PGID) surfaced as SQLite's
opaque "attempt to write a readonly database" in two confusing ways:

- Scans ran but every job-state persist to jobs.db failed, flooding the
  log once per item while the UI looked frozen (job state never updated).
- Schedule creation failed writing scheduler.db, and the catch-all blamed
  a "malformed cron expression" — the wrong cause entirely.

Add a config-directory health preflight:
- config_health.probe_config_health(): authoritative create+delete probe
  plus /proc/mounts (ownership vs :ro mount), statvfs (low space), and
  network-fs (NFS/SMB) detection, each with an actionable fix. Probe file
  is uniquely named per attempt so concurrent gthread probes can't race.
- Startup logs ONE clear error instead of a per-item flood.
- GET /api/system/config-health (cheap, no GPU) feeds a global banner on
  every page; scan + schedule creation refuse with a 503 + chown hint
  instead of starting work that can't persist / a misleading 500.
- Media-mount health folded into the same banner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.23404% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.06%. Comparing base (f8a2933) to head (de8c72e).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
media_preview_generator/web/config_health.py 87.23% 12 Missing ⚠️
media_preview_generator/web/routes/api_system.py 77.27% 5 Missing ⚠️
media_preview_generator/web/routes/api_jobs.py 93.33% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##              dev     #279      +/-   ##
==========================================
+ Coverage   84.00%   84.06%   +0.06%     
==========================================
  Files          89       90       +1     
  Lines       19669    19807     +138     
==========================================
+ Hits        16523    16651     +128     
- Misses       3146     3156      +10     
Files with missing lines Coverage Δ
media_preview_generator/web/app.py 83.38% <100.00%> (+0.10%) ⬆️
...edia_preview_generator/web/routes/api_schedules.py 89.17% <100.00%> (+3.17%) ⬆️
media_preview_generator/web/routes/api_jobs.py 86.41% <93.33%> (+0.11%) ⬆️
media_preview_generator/web/routes/api_system.py 82.85% <77.27%> (-0.31%) ⬇️
media_preview_generator/web/config_health.py 87.23% <87.23%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

stevezau and others added 2 commits July 12, 2026 06:23
checkConfigHealth() used apiGet(), which redirects to /login on a 401.
That let the passive background health poll bounce any auth-gated page
to login — e.g. the setup wizard, whose /api/system/config-health call
401s when setup is already complete but the page isn't authenticated.
This broke the TestSetupWizardStep5 e2e suite (wizard redirected to
login before its steps could render).

Use a raw fetch and silently bail on any non-OK response. A health
check must never move the user; the banner just doesn't render when the
endpoint is unavailable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stevezau
stevezau merged commit 634592c into dev Jul 11, 2026
14 checks passed
@stevezau
stevezau deleted the feat/config-writability-preflight branch July 11, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: issues happening when starting a run and scheduling a task

1 participant