The shipped config could not load on a machine without a .env
config/default.yaml reads its camera's host, username and password from
${ENV}. Unset placeholders parse as YAML null, and CameraConfig had no
_normalize_blanks - the guard ApiSection and EmailSection have had all
along - so loading it raised three pydantic errors and the engine would
not start at all. On a developer's checkout .env is right there, which is
why this survived: the failing machine is every machine the product is
actually installed on, and installer/build.ps1 runs this suite, so the
Windows build would have failed on a fresh clone.
The normalisation is field-by-field, never a blanket None -> "": `webcam`
is an Optional[int] whose None means "this is not a webcam", and `tuning`
is a nested model. Sweeping either trades one validation error for
another - which it did, on the first attempt.
Second bug behind the same line: that camera entry would then have been
SEEDED into a fresh install, giving a shop a camera called cam1 that
nobody added, retrying a connection to "" forever, with the first task on
a new PC being to work out what it was. CameraConfig.addressed() says
what a camera entry needs to be one, and seed() drops the rest.
Found by cloning the repository into a temp directory and running the
tests there. Nothing in a working tree can find this class of bug.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HViLj9gYNRtSr7YVZmW5sn
This commit is contained in:
@@ -182,13 +182,22 @@ class CameraStore:
|
||||
|
||||
After that the store is authoritative — otherwise a camera the user
|
||||
deleted in the UI would reappear on every restart.
|
||||
|
||||
Entries with no address are dropped rather than imported. The bundled
|
||||
config declares one whose host comes from the environment, which is
|
||||
right for a checkout with a .env and resolves to nothing on every
|
||||
machine the product is actually installed on. Seeding that would put a
|
||||
camera the shop never added into a brand new install, permanently
|
||||
failing to connect to "" — and the first thing they would have to do is
|
||||
work out what it was and delete it.
|
||||
"""
|
||||
with self._lock:
|
||||
if self.path.exists() or not cameras:
|
||||
usable = [c for c in cameras if c.addressed()]
|
||||
if self.path.exists() or not usable:
|
||||
return False
|
||||
for cam in cameras:
|
||||
for cam in usable:
|
||||
self._cameras[cam.id] = cam
|
||||
self._save()
|
||||
log.info("seeded %d camera(s) from YAML into %s",
|
||||
len(cameras), self.path)
|
||||
len(usable), self.path)
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user