mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
v1.46.1: re-check of v1.46.0 — HP-1460-01, -02, -03
HP-1460-01: v1.46.0 stopped overwriting attachments, but picking a free name and taking it were two steps. Two uploads racing between them agreed on the same name, both answered 200, and one set of bytes replaced the other; files/migrate had the same check-then-copy gap. reserve_filename now claims the name with O_CREAT|O_EXCL as it picks it, and both paths use it. It also splits the extension off the RAW name and budgets the stem against MAX_FILENAME including the collision tag — a maximal name lost its '.pdf' and then grew past the limit, so the view sanitised the request back to a different name and the attachment 404'd for good. HP-1460-02: cleanup lived in an 'except Exception', which CancelledError walks past, only one tmp_path was tracked, promotion had no finally, and the collector only walks marker folders — an aborted transfer stranded a .upload-* that nothing would ever remove. An outer finally owns every temporary, a second 'file' part is refused, promotion failure cleans up, and sweep_upload_temps runs at setup, daily, and inside the commit-scoped collector. Chunks are batched to 1 MB per disk task instead of one per 64 KB. HP-1460-03: the layout event reached the static card and not the full one, so two full cards diverged until a reload. The full card subscribes now and re-reads ONLY the layout, keyed on its revision. Two hazards handled: it records revisions it produced itself, and the reaction is deferred ~200 ms because the event can beat the reply to our own write over the same socket; positions dragged but not yet sent are flushed and merged on top, so a fix for a stale UI cannot become a lost drag. Tests: smoke_layout_sync (fails on a v1.46.0 build), four pure tests for atomic reservation incl. 20-thread concurrency and the length boundary, a backend test walking every failing exit path of an upload, and — as the report asked — an HA-harness test that a repair issue disappears with its space. Docs: CHANGELOG.md + CHANGELOG.ru.md + ARCHITECTURE.md + TESTING.md + STATUS.md.
This commit is contained in:
+10
-3
@@ -190,9 +190,16 @@ and only with SVG: a CSP on a PDF response can break the browser's built-in
|
||||
viewer, and a raster image has no execution model to disable.
|
||||
|
||||
**Attachments follow the same commit-scoped lifecycle as plans** (HP-1454-02).
|
||||
An upload takes a free name (`unique_filename`) and never overwrites, because
|
||||
the bytes under an existing name may be referenced by the stored configuration
|
||||
and an upload is not part of that transaction. A new icon has no id yet, so its
|
||||
An upload takes a free name and never overwrites, because the bytes under an
|
||||
existing name may be referenced by the stored configuration and an upload is
|
||||
not part of that transaction. `reserve_filename` *claims* the name as it picks
|
||||
it (`O_CREAT | O_EXCL`) — asking `exists()` and returning a string let two
|
||||
uploads agree on one name and quietly overwrite each other. It also budgets the
|
||||
length so the result survives the sanitiser the content view applies to the
|
||||
request, since a name the view rewrites is a file written and never served.
|
||||
Streaming temporaries live in the files root under `.upload-`, are removed on
|
||||
every exit path of the request, and are swept at startup and daily for the
|
||||
cases a process death leaves behind. A new icon has no id yet, so its
|
||||
files go to a per-dialog staging folder and move to the real id once the config
|
||||
write is accepted — the same copy → save → cleanup order as a rebind.
|
||||
`config/set` collects what its commit superseded, and anything unreferenced and
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## v1.46.1 — 2026-07-28 (re-check of v1.46.0: HP-1460-01 … -03)
|
||||
- **Two uploads of the same file name can no longer collide (HP-1460-01).**
|
||||
v1.46.0 stopped overwriting attachments, but choosing a free name and taking
|
||||
it were two steps: two uploads racing between them agreed on the same name,
|
||||
both reported success, and one set of bytes replaced the other. The name is
|
||||
now claimed atomically as it is chosen — twenty simultaneous uploads of
|
||||
`manual.pdf` produce twenty files. The same helper is used when rebinding
|
||||
moves files, which had the same gap.
|
||||
Also fixed there: a name at the length limit lost its extension, and the
|
||||
collision suffix pushed it past the limit, so the attachment was stored under
|
||||
a name the server would not serve back — a permanent 404 on a file the UI
|
||||
reported as attached.
|
||||
- **An interrupted upload no longer leaves a temporary file forever
|
||||
(HP-1460-02).** Cleanup ran in an `except Exception`, which a cancelled
|
||||
request walks straight past, and the collector only ever looks inside marker
|
||||
folders — so an aborted transfer left a `.upload-*` in place with nothing able
|
||||
to remove it. Every exit path now cleans up, a request carrying two files is
|
||||
refused outright, and abandoned temporaries are swept at startup and daily.
|
||||
Uploads also write in 1 MB batches instead of one disk task per 64 KB.
|
||||
- **Two full cards side by side keep the same positions (HP-1460-03).** v1.46.0
|
||||
taught the static card to follow position changes and left the full one
|
||||
behind, so dragging an icon in one window did not move it in another until a
|
||||
reload. It follows now, without disturbing a drag of its own: a revision
|
||||
arriving mid-drag is merged rather than applied over the top, and a card does
|
||||
not re-read what it just wrote itself.
|
||||
|
||||
## v1.46.0 — 2026-07-28 (full external audit of v1.45.4: HP-1454-01 … -10)
|
||||
|
||||
**Security**
|
||||
|
||||
@@ -6,6 +6,32 @@
|
||||
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
||||
> изменением — как и остальная документация (см. docs/STATUS.md).
|
||||
|
||||
## v1.46.1 — 2026-07-28 (перепроверка v1.46.0: HP-1460-01 … -03)
|
||||
- **Две загрузки с одинаковым именем больше не сталкиваются (HP-1460-01).**
|
||||
v1.46.0 перестала затирать вложения, но выбор свободного имени и его занятие
|
||||
были двумя шагами: две загрузки, попавшие между ними, сходились на одном
|
||||
имени, обе рапортовали успех, и одни байты заменяли другие. Теперь имя
|
||||
занимается атомарно в момент выбора — двадцать одновременных загрузок
|
||||
`manual.pdf` дают двадцать файлов. Тот же механизм используется при переносе
|
||||
файлов на перепривязке, где был ровно такой же зазор.
|
||||
Заодно там же: имя предельной длины теряло расширение, а суффикс коллизии
|
||||
выталкивал его за предел, и вложение сохранялось под именем, которое сервер
|
||||
обратно не отдаёт — вечный 404 на файл, который интерфейс считал
|
||||
прикреплённым.
|
||||
- **Прерванная загрузка больше не оставляет временный файл навсегда
|
||||
(HP-1460-02).** Уборка стояла в `except Exception`, мимо которого отменённый
|
||||
запрос проходит насквозь, а сборщик заглядывает только в папки маркеров —
|
||||
поэтому оборванная передача оставляла `.upload-*`, и убрать его было некому.
|
||||
Теперь убирает любой путь выхода, запрос с двумя файлами отклоняется сразу, а
|
||||
брошенные временные подметаются при старте и раз в сутки. Запись идёт
|
||||
порциями по мегабайту, а не отдельной задачей на каждые 64 КБ.
|
||||
- **Две полные карточки рядом держат одинаковые позиции (HP-1460-03).** v1.46.0
|
||||
научила следить за перемещениями статическую карточку и оставила позади
|
||||
полную, поэтому перетаскивание иконки в одном окне не двигало её в другом до
|
||||
перезагрузки. Теперь следит — и не мешает собственному перетаскиванию: чужая
|
||||
ревизия, пришедшая в его разгар, сливается, а не накатывается сверху, и
|
||||
карточка не перечитывает то, что записала сама.
|
||||
|
||||
## v1.46.0 — 2026-07-28 (полный внешний аудит v1.45.4: HP-1454-01 … -10)
|
||||
|
||||
**Безопасность**
|
||||
|
||||
+2
-2
@@ -15,12 +15,12 @@
|
||||
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| Version | **v1.46.0** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance |
|
||||
| Version | **v1.46.1** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance |
|
||||
| Workflow | Since 2026-07-22: minor changes go to branch **`dev`** (build + smokes → deploy home → commit → push, NO release); releases are batched on the owner's command (merge dev→main, one tag, one release with a summary changelog, CI checked on dev beforehand) |
|
||||
| GitHub | https://github.com/Matysh/houseplan-card — **`main` carries every published release, the latest tag is the current version above**; `dev` is where work lands and is merged into `main` at release time (so `dev` is normally equal to or ahead of `main`, never behind). Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
|
||||
| CI | validate.yml (hacs + hassfest + frontend + backend) green; release.yml attaches the bundle on release publish |
|
||||
| HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled; ~864 older open PRs but merge rate ≈180/mo; realistic ETA 1–3 months (checked 2026-07-24) |
|
||||
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.46.0** via direct copy (HACS custom repo also installed) |
|
||||
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.46.1** via direct copy (HACS custom repo also installed) |
|
||||
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
|
||||
| Tests | Four layers: frontend unit (`npm test`, node:test over `test-build/`), pure backend (`pytest tests_backend`, runs anywhere), HA-harness backend (same folder, CI only — needs py3.13 + pytest-homeassistant-custom-component), and browser smokes (`demo/smoke_*.mjs`, headless chromium). **Counts are not written down here** — they went stale within two releases while the version line beside them was kept current, which reads as less coverage than exists (review R5-2). Run `npm run inventory` for the current numbers, or read them off the last CI run |
|
||||
| Community | **Telegram chat: https://t.me/ha_houseplan** (created 2026-07-27) — the primary user-facing support channel; GitHub issues stay for bugs/features. Link it from any new release notes and posts |
|
||||
|
||||
@@ -239,6 +239,18 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
on the plan after a reload. Same for each tap action and each fill mode
|
||||
[auto: backend test_every_display_mode_the_editor_offers_is_accepted and
|
||||
neighbours, test_a_marker_showing_its_value_can_be_saved]
|
||||
- [ ] Concurrent uploads of one name (v1.46.1, HP-1460-01): attach the same
|
||||
file from two browser tabs at once — two attachments, two sets of bytes,
|
||||
neither lost. A file whose name is at the length limit still downloads
|
||||
[auto: unit: test_reserve_filename_is_safe_under_concurrency and neighbours]
|
||||
- [ ] No temporary files survive (v1.46.1, HP-1460-02): abort a large upload
|
||||
mid-transfer, send two files in one request, make promotion fail — in each
|
||||
case the files folder holds no `.upload-*`. An old one is swept at startup
|
||||
[auto: backend test_upload_leaves_no_temporary_behind + unit: sweep_upload_temps]
|
||||
- [ ] Two full cards agree on positions (v1.46.1, HP-1460-03): open the plan in
|
||||
two windows, drag an icon in one — it moves in the other without a reload;
|
||||
a drag in progress in the second window is not thrown away
|
||||
[auto: smoke_layout_sync]
|
||||
- [ ] Uploaded SVG is inert as a document (v1.46.0, HP-1454-01): open a plan's
|
||||
signed url directly in a tab — a `<script>` inside it must not run and must
|
||||
not reach the HA session's localStorage; the same plan still renders in the
|
||||
|
||||
Reference in New Issue
Block a user