mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-01 03:31:25 +02:00
dev
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0be6ccd090 |
fix(backup): stop Gitea's tree pager failing open on a missing total_count (#2656)
`if not isinstance(total, int) or seen >= total or not entries: return blobs, ""` — the first arm short-circuited the page loop into a **success** holding page 1 only. Gitea clamps `per_page` to `MAX_RESPONSE_ITEMS` (default 50), so that is 50 entries of an arbitrarily large tree returned as a complete listing. The restore then reports genuinely-present categories as "Not present in this backup commit". That silent skip is the exact failure this override exists to prevent, and the same class as E7 and G2 — G2 fixed the arithmetic here and left the shape. GitHub and GitLab both hard-fail in the equivalent spot; only Gitea guessed, and it guessed in the one direction that loses data quietly. Whether Gitea always sends `total_count` on this route is beside the point: the code was defending against a response shape it did not trust, and then trusting it. Now a missing or non-int `total_count` means "page until a short or empty page". A page shorter than the first one is the last one, floored at Gitea's default clamp so a genuinely small tree still costs exactly one request — the reason G2 rejected paging-until-short in the `total_count`-present case, which is unchanged and still stops on the count. The existing `page <= 50` ceiling gives the correct hard failure for a tree that really is over cap, so this cannot truncate. Residual, and deliberately not widened into a `return None` on the first ambiguous response — that would break single-page trees, the common case: an instance whose `MAX_RESPONSE_ITEMS` is set *below* 50 *and* which omits `total_count` would still stop at page 1. Both halves have to be true. Tests: +6 (paged to the end with no count, on both Gitea and Forgejo; a short page ends it; a non-int count is treated as no count; the page ceiling still fails). Fail-pre-fix 5, control that passes either way 1 (a small tree is one request). These don't match `-k github`, so 274 -> 280 across the three restore files but `-k github` is unmoved. |
||
|
|
582a6b18bc |
fix(backup): page Gitea's tree off what came back, not what we asked for (#2656)
The pager computed `seen = (page - 1) * 1000 + len(entries)`, taking the requested `per_page` as fact. Gitea clamps `per_page` to `MAX_RESPONSE_ITEMS`, which defaults to 50. So on a default install page 1 returns 50 entries and sets `seen` to 50, then page 2 sets it to 1050 — which clears any `total_count` under 1050. The loop returns `success: true` holding the first 100 entries of a much larger tree. The restore then reads every missing path as "category not present in this commit" and skips it silently, which is precisely the failure this override was written to prevent. Same class as the GitLab pager fix, in the one direction that got left behind. Fix: accumulate `seen += len(entries)`. A genuinely over-cap tree still hard-fails rather than truncating; the cap is a page count, not a file count, because the page size is the server's choice. Test: a 120-entry tree served 50 at a time reaches its last entry, in three requests. Confirmed failing against the pre-fix backend — it stopped after two pages and reported 100 entries as the whole tree. |
||
|
|
158301ac8a |
fix(backup): halve the provider round-trips, and stop losing commit metadata (#2656)
The three remaining review items, all in the read path. E1 — four provider calls where two would do. preview() called list_commits twice: once inside _resolve_ref to turn HEAD into a SHA, once more at limit=20 purely to find the entry describing that same SHA. And list_tree's recursive tree GET was thrown away, so fetch_files immediately fetched the identical tree again to map path -> blob SHA. _resolve_ref now returns the entry it already has, and list_tree returns its blob_shas map for fetch_files to take as an optional argument. GitLab reads files by path and ignores it. E2 — `commit: null` for a ref outside the 20 most recent. Two causes, and the second is the one that actually bit: REF_PATTERN accepts a 7-character ref while providers return the full 40, so the exact `==` in the scan never matched an abbreviated SHA *even when the commit was in the window*. Fixed by prefix comparison, plus a get_commit(ref) on the GitHub and GitLab backends for the genuinely-outside-the-window case. Gitea and Forgejo inherit GitHub's. Still best-effort: it is a subject line and a date, so a failed lookup renders the preview without them rather than failing it. E7 — the two tree readers disagreed, and each was wrong in the other's direction. GitHub's recursive trees endpoint is not paginated and signals overflow with truncated=true, which _blob_shas_at hard-fails on. Gitea and Forgejo *do* page that endpoint, and inherited that single GET unchanged — so a large backup repo returned only the first page and every category beyond it looked absent from the commit. GiteaBackend now has its own paging _blob_shas_at. GitLab had the mirror-image bug the review did not name: at its 50-page cap it exited through the while condition and returned success: True with a silently partial path list. Both now fail loudly, which is what the GitHub version was always doing. Both halves of E7 are the same failure the module already refuses to allow: a restore that skips categories and calls it "not present in this backup commit". 24 new or changed tests, all failing against this commit's parent. |
||
|
|
6a239314dc |
feat(backup): restore selected categories from a Git backup commit (#2656)
The Git backup feature was push-only: there was no equivalent of the local backup's Restore button, so recovering meant hand-downloading JSON files from the repository. This adds the read side. Providers gain list_commits / list_tree / fetch_files on the GitProviderBackend ABC. GitHub implements them against the Git Data API and Gitea/Forgejo inherit that unchanged; GitLab overrides for its own REST shape, including tree pagination and subgroup path encoding. fetch_files is batched so the path -> blob SHA lookup happens once per restore rather than once per file, and uses the blobs API rather than contents because contents silently inlines only the first 1 MB. The new GitHubRestoreService resolves HEAD to a concrete SHA up front, so a preview and the restore that follows act on the same commit even if a scheduled backup lands in between. Categories are applied archives -> spools -> settings -> kprofiles: archives first because spool usage history references archive_id, K-profiles last because they leave the database and publish over MQTT. Restores never reuse the backup's primary keys. spool.id and print_archives.id are bare autoincrement columns, so ids from an old backup very likely belong to unrelated rows today; rows are matched on natural keys (tag_uid, then tray_uuid, then a descriptive composite for spools; content_hash or filename plus started_at for archives), inserted without an explicit id, and an old_id -> new_id map rewrites the foreign keys in spool usage history. created_at is carried across on insert so restoring the same backup twice matches instead of duplicating. Dangling printer/project links are cleared and reported rather than failing the row. Settings restore re-applies the collector's credential denylist on the read side, plus a pattern guard, because a backup taken before that denylist existed can still contain secrets. Restored archives are metadata-only: the 3MF and thumbnail bytes are not in a Git backup and print_archives.file_path is NOT NULL, so inserted rows get an empty path and the UI says so. Backup and restore take a mutex against each other; both write the same tables and talk to the same printers. Restores are logged as GitHubBackupLog rows with trigger="restore", which needs no migration and surfaces them in the existing History card. Cloud profiles are deliberately not a restore category. The collector never actually writes cloud_profiles/*.json - it reads a "setting" list key the Bambu Cloud API does not return - and the preset list it would write carries no setting payload. Filed separately. Permission github:restore already existed and is granted to Administrators, so no permission changes were needed. Tests: 125 new backend tests (provider reads across all four providers, the per-category appliers, the API endpoints) and 13 frontend tests. Full suites pass with no regressions; the 35 backend failures on Windows are byte-identical with and without this branch. |