sweep: parallel batch of bug/hardening/optimization fixes (#272 #177 #192 #276 #277 #274 #246 #262 #273 #275 #267) - #280
Merged
Conversation
…t, denylist default) Part of the #267 codebase-audit cleanup (doc/comment subset): - compose.yaml: the BEETROOT_DENYLIST_PACKAGES comment claimed the default is the GMS pair, but the compose default is EMPTY (`:-`) — the GMS pair is supplied only by the CLI's render_env, so a bare `docker compose` leaves root unhidden. Corrected the comment. - AGENTS.md / docs/reference/cli.md: `beetroot build` clones redroid-script into the per-user cache dir (~/.cache/beetroot/redroid-script), not /tmp/redroid (moved in v0.4). (The v0.3→v0.4 migration doc's /tmp/redroid reference is historical and left intact.) - AGENTS.md verb list: replaced the non-existent `frida` verb with the real `frida-addr` + `install-frida`, and added the missing `modes` verb. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WmChW67jv2BaPk1a8HreHU
…i/compose/downloads/vm/registry Landed via 7 file-disjoint clusters (each independently gated to 100%): cli / hostcheck (#272, #177, #192, #267): - guard every ports.well_known(...)['frida'] banner access so a Frida-less instance no longer KeyErrors in create/register/up/restore; - the `up` banner no longer advertises a Frida endpoint for binder: vm; - the strict binder: host block remedy now carries the binder: vm hint; - `ls` shows the instance dir (not "-") for directory-backed vm rows. compose (#276, #277): - ps_status skips non-dict JSON lines instead of raising AttributeError; - lifecycle ComposeError messages include a trimmed stderr tail. downloads / builder (#274, #246, #262, #267): - rootfs_download caps zstd output (64 GiB) and validates Content-Length; - kernel_download streams the bzImage to disk (no full-RAM buffer); - the default Docker static bundle sha256 is pinned (docker-27.5.1.tgz); - the kernel source tarball is hashed in 64 KiB chunks; - a malformed IMAGE_SIZE_MB is a preflight problem, not a crash. vm boot-cache (#273, #267): - the warm-boot checkpoint identity folds in the resolved accelerator (kvm vs tcg), so an accel flip invalidates the checkpoint; - a warm-resume failure copies the console log to a .warm-fail sidecar before the cold-boot retry truncates it. adb (#275): - add_module raises AdbNotInstalledError when adb is absent. registry / config / snapshot (#267): - add_allocating bounds the port index by the cap at registration; - mem_reservation/memswap_limit are validated against mem at load time; - snapshot restore caps zstd decompression output. Combined gate green: ruff, ruff format, mypy (src+tests), pytest 2052 passed at 100% line+branch coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WmChW67jv2BaPk1a8HreHU
…d_module guard doesn't depend on ambient adb The #275 guard makes AdbDevice.add_module raise AdbNotInstalledError when adb is absent from PATH. test_module_verb_dispatches_to_adb_device drove the real AdbDevice without stubbing shutil.which, so it passed only where adb happens to be installed (the dev sandbox) and failed on CI runners with no adb. Stub which present, mirroring the sibling test_shell_dispatches_to_adb_device. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WmChW67jv2BaPk1a8HreHU
…ams (CR fix) Adversarial CR caught that the initial #276 implementation used capture_output=True on the lifecycle verbs, which HIDES all output — a real UX regression: `beetroot logs <name>` (non-follow, the default) printed nothing because the logs go to stdout, and `docker compose build` ran silently. Corrected scope: - up / down / logs (non-follow): capture STDERR ONLY (stderr=PIPE, text=True) so its tail is still folded into the ComposeError, while stdout stays inherited and streams to the terminal (logs print; compose progress shows). - build: reverted to inherit stdio (BuildKit progress is on stderr and the build log is the deliverable — capturing would run it silently); its error is exit-code-only, as before. - logs -f: unchanged (already inherits stdio). Tests updated to assert stderr=PIPE (not capture_output) for up/down/logs and that build neither captures nor folds stderr. Full gate green at 100% coverage, verified both with adb present and with adb stripped from PATH (CI-like). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WmChW67jv2BaPk1a8HreHU
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A batch of independent, file-disjoint fixes implemented in parallel and integrated together. Each was gated to 100% line+branch coverage in isolation; the combined tree is green (ruff,
mypy --strictsrc+tests, pytest 2052 passed at 100% coverage).Closes #272
Closes #177
Closes #192
Closes #276
Closes #277
Closes #274
Closes #246
Closes #262
Closes #273
Closes #275
Closes #267
cli / hostcheck
create/register/up/restorebanners guard everyports.well_known(...)['frida']access, so a Frida-less instance (anadb-onlyports:list, nofrida:block) no longer crashes withKeyError: 'frida'.beetroot upsuccess banner advertisesFrida localhost:<port>for vm instances that every other surface reports as unsupported #177 — theupsuccess banner prints only the ADB endpoint + a "Frida unsupported on binder: vm" note for the vm backend (matchingstatus/ls/frida-addr); redroid still shows both.binder: hostblock on a binderless host omits thebinder: vmhint, steering users away from the only locally-working backend #192 — the strictbinder: hostblock remedy now carries thebinder: vmhint (built once, shared with thewarnbranch), so a binderless host is pointed at the locally-working vm path.lsshows the instance directory for directory-backedbinder: vmrows instead of-(table PATH + JSONpathkey), via aVmDeviceBackend-awareTypeGuard.compose
docker compose psraises an uncaught AttributeError #277 —ps_statusskips a non-dict JSON line instead of raising an uncaughtAttributeErrorthroughls/status/doctor.ComposeErrormessages (up/down/build, non-followlogs) fold a trimmed stderr tail in;logs -fstill streams live.downloads / builder
rootfs_downloadcaps zstd output at 64 GiB and validates the download againstContent-Length(mirrors the frida frida_download.download() decompresses the entire .xz with no max-output cap (unbounded-decompression / OOM hardening gap) #228 / snapshot snapshot.restore decompresses the entire archive twice (manifest-validation pass + extraction pass) #265 guards).bzImagedownload streams to disk (hashing incrementally) instead of buffering the whole file in RAM._DEFAULT_DOCKER_BUNDLE_SHA256= verified sha256 ofdocker-27.5.1.tgz), so the local bake verifies it before extraction (override stays unverified-with-warning).IMAGE_SIZE_MBbecomes a preflight problem instead of aborting--check.vm boot-cache
-cpu hostvs TCG-cpu max), so an accel flip invalidates the checkpoint instead of attempting an incompatible-loadvm..warm-failsidecar before the cold-boot retry truncates it.adb
AdbDevice.add_moduleraises the typedAdbNotInstalledErrorwhenadbis absent, not a rawFileNotFoundError.registry / config / snapshot
add_allocatingbounds the port index by the cap at registration, so an over-cap instance fails cleanly instead of crashing later port resolution.mem_reservation/memswap_limitare validated againstmemat load time (honoring the-1unlimited sentinel).restorecaps zstd decompression output.docs (#267)
compose.yamldenylist-default comment, theredroid-scriptclone path (~/.cache/beetroot/redroid-script, not/tmp/redroid), and theAGENTS.mdverb list (frida-addr/install-frida/modes).🤖 Generated with Claude Code
https://claude.ai/code/session_01WmChW67jv2BaPk1a8HreHU
Generated by Claude Code