Skip to content

chore: sync develop with main - #502

Merged
chodeus merged 8 commits into
developfrom
main
Aug 12, 2026
Merged

chore: sync develop with main#502
chodeus merged 8 commits into
developfrom
main

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai ignore

develop has drifted behind main (releases, fixes, dependency bumps). Do not merge this PR — it reports the drift, it does not fix it.

develop requires branches be up to date, and head:main can never satisfy that without pulling develop's extension files into main, which the branch invariant forbids. Squash or rebase would also leave main unreachable from develop, so this workflow would just open another PR next push.

Sync locally instead:

git checkout develop && git merge origin/main && git push

Then verify git diff main develop is added extension files plus deploy/docker/Dockerfile only. GitHub marks this PR merged on its own once develop contains main's tip. Opened by the sync-develop workflow.

…vilege perms (#501)

Branch filters '*'->'**', branch-isolation-guard rewrite (icase + poster_self_heal + fonts), new develop-invariant-guard, least-privilege permissions, Python 3.13, on-branch-delete injection + latent-skip-bug fix, on-branch-create timeout/concurrency, dep-audit Dockerfile-trigger drop, sync-develop @coderabbitai ignore, and a JSON-asset parse test.
…vilege perms (#503)

Re-land of #501 through a full CodeRabbit review. Branch filters '*'->'**', branch-isolation-guard rewrite (icase + poster_self_heal + fonts), develop-invariant-guard with a byte-prefix Dockerfile check, least-privilege permissions, Python 3.13, on-branch-delete injection/latent-skip/status-handling fixes with bounded curls, on-branch-create timeout + event.ref concurrency, dep-audit Dockerfile-trigger drop, sync-develop @coderabbitai ignore, JSON-asset parse test.
…ot byte-prefix (#505)

Restores the round-1 pure-insertion-hunks check; the byte-prefix variant false-failed legitimate develop (CL2K blocks are mid-file insertions by design), breaking sync PR #502's guard and develop image publishing.
Per-branch GHCR image feature fixed holistically: builds the CREATED branch on create events (github.ref resolves to the default branch there); collision-safe branch->tag encoding (clean names <=128 pass through, else truncate+digest) applied byte-identically in on-branch-create, on-branch-delete, and codeql-lint docker-push; deletion via the GitHub Packages API only when the target tag is the sole tag on its version, with explicit status handling and bounded curls.
…il-open guards, hot-path caching) (#506)

* fix(backend): correctness + perf batch

- /api/posters/list: resolve posters via the shared STATIC_DIR (get_static_dir
  in util/helper), not a hardcoded templates/ path that is empty in Docker
- LIKE escaping: one escape_like() helper in db_base; fix the 3 unescaped sites
  (poster prefix guard, poster search, media tags) + ESCAPE clause, and collapse
  the copy-pasted escape chains into it
- /api/media/duplicates: narrow the config fail-open to ConfigError + a
  filter_applied flag, so a config error no longer silently reports quality pairs
- module_is_disabled: fail CLOSED on a config read error (skip the run)
- schema sync: gate init_schema per db file (process-level), and pass the SSE
  poll's open db through get_module_status — ~43 ChubDB opens/tick -> 1
- load_config: cache the validated config keyed on mtime/size/inode (returns a
  deep copy; callers mutate before saving), cleared on save + watcher reload
- posters.py: make the 3 blocking PIL/db routes plain def so Starlette offloads
  them, and drop the "without blocking the event loop" claim that wasn't true
- move backup logic to util/backup.py so util/maintenance no longer imports up
  into the api layer

* test: single import style for backend.util.config (CodeQL py/import-and-import-from)

* fix(backend): address review findings

- /api/media/duplicates: deny on ConfigError (shared CONFIG_INVALID handler)
  instead of returning unfiltered results; drop the filter_applied field;
  validate exclusion-group members before building sets
- get_backup_dir: propagate ConfigError instead of silently falling back;
  backup routes re-raise so the shared handler answers
- save_backup: exclusive creation with a collision suffix, and resolve the
  destination before paying for the dump
- schema sync: hold the (now reentrant) registry lock across the DDL, and skip
  only non-empty known files so a recreated db re-syncs despite inode reuse
- prune_old_backups: re-confine the resolved root against the allowed roots and
  lstat through dir_fd before unlinking
- 1-line docstrings on every touched function still missing one

* test: single import style per module (CodeQL py/import-and-import-from)

* fix(backend): round-2 review findings on maintenance

- prune_old_backups: config is now required — the re-confinement check can no
  longer be skipped by omission
- maintenance loop: skip the pass when config won't load instead of running
  destructive pruning against the startup snapshot; drop the now-unused
  start_maintenance config param
- trim the prune-loop safety comment to two lines

* fix(backend): bind prune authorization to the opened descriptor

Authorize the resolved root before opening it, verify fstat(dir_fd) matches
stat(root), and enumerate through the descriptor with one nofollow stat per
entry (CWE-367).
…route gating, dead code sweep (#507)

* fix(frontend): dead Tailwind classes, dev-route gating, dead code sweep

- Define or replace every class used in src/ that the Tailwind build didn't
  emit (bg-canvas, max-h-modal-body, rounded-t-xl, focus:ring-error, dropdown
  sizes; Separator -> bg-border-light, ButtonBase -> text-on-color/
  bg-surface-inset, dead h-header/bg-*-hover dropped)
- Add scripts/check-tailwind-classes.mjs + a CI step: fail when a class used in
  src/ is missing from the built CSS, so this class of bug can't return
- Gate the 15 dev pages behind import.meta.env.DEV (routes AND chunks are
  dropped from the prod bundle) and remove the duplicate dev/toolbar path
- Delete dead code: interactions.css, legacy navigation.css block,
  posterPreview.js (shadowed the real API), RenderField, forms/index,
  NotificationCard, useArrayField
- vite.config: chunk react-router (the actual dependency), not react-router-dom

* fix(frontend): check clsx object keys in className expressions

A quoted key in a className region is a class (clsx conditionals), so the
variant-map key exclusion now applies only to *Classes assignments. Regression
tests run the checker against fixture trees.

* fix(frontend): exclude .test. files from the class checker walk

Fixture markup in test files (this checker's own regression tests included) is
not UI and was flagged as used-but-unemitted.
…ame, import styles (#508)

* chore: repo hygiene — docs corrections, requirements split, cache rename, import styles

- docs/architecture.md: correct 9 stale/false claims (Tailwind v4 present,
  react-router 8, real static mounts, table names, webhook events, cancellation
  wiring, build path) after verifying every claim against the tree
- requirements.txt: runtime-only (13 unimported pins removed; pytest and its
  deps + httpx move to requirements-dev.txt; CI and Makefile install it — one
  home for test deps; the image ships no test stack). python-multipart stays:
  FastAPI needs it for the UploadFile routes despite zero imports
- rename backend/api/media.py -> cache.py (it serves /api/cache); OpenAPI
  operation list proven byte-identical (192 ops)
- delete unreferenced 1.87MB favicon-colored.svg; ignore design_handoff*/
- one import style per module everywhere (clears the recurring CodeQL
  py/import-and-import-from mints); replace two tuple-index interceptor lambdas
  with named functions; fix the inverted rasterize_borders docstring

* fix: address review findings on the cache module

- never return raw exception text from the six cache handlers (stable public
  messages; details stay in the logs)
- validate the refresh body shape (non-object or non-list fields -> 400
  INVALID_BODY instead of a 500)
- remove the dead update_mappings flag end to end: no producer's value ever
  changed behavior (sync_all_databases always refreshes mappings) and the two
  producers disagreed on the default
- guard make install against a <3.10 venv; note the floor in requirements-dev
- shorten the cache module docstrings to single behavior/gotcha lines and add
  the missing docstrings on functions this branch touches (coverage 100%)

* fix: shared refresh-body reader — empty body means full refresh on both routes

read_request_json owns the single body read: no body/whitespace -> {} (full
refresh, preserving media/refresh's historical bodyless tolerance and fixing
cache/refresh's 500), unparseable -> 400 via the shared validator. Closes the
charset content-type hole on media/refresh.

* fix: cap the refresh-body read and validate list elements

read_request_json streams with a 1MiB ceiling (Content-Length fast-path plus a
per-chunk accumulator, so chunked encoding can't bypass it) and the routes map
the sentinel to 413 BODY_TOO_LARGE. build_cache_refresh_payload requires
non-empty string elements and strips them.
@chodeus
chodeus merged commit dfcd5b9 into develop Aug 12, 2026
30 of 31 checks passed
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.

1 participant