Skip to content

feat: config-driven distro + DE manifests with a real OCI pull client#25

Open
DaRipper91 wants to merge 8 commits into
REFORKfrom
refork/distro-manifest-and-installer
Open

feat: config-driven distro + DE manifests with a real OCI pull client#25
DaRipper91 wants to merge 8 commits into
REFORKfrom
refork/distro-manifest-and-installer

Conversation

@DaRipper91

Copy link
Copy Markdown
Owner

Summary

Backend for the multi-distro (Arch/Fedora/Debian/Ubuntu) + tiered-DE (XFCE4/LXQt/MATE/i3wm stable, niri/COSMIC experimental) selector described in the task brief. No UI/on-device build wiring yet — that's the next PR — but the data model and install logic are complete and independently reviewable.

What's real vs. what needs a device to confirm

  • ✅ Verified live: Arch's static tarball URL, all three OCI images' single-arm64-layer shape (direct Docker Registry v2 API calls), every tier-1 package/group name per distro (Arch group pages, Fedora's real comps XML, Debian source packages + the upstream README's own literal apt command), first-boot steps against the real tiny-computer/images README.
  • ⚠️ Not verified — flagged in code, not hidden: the proot chroot invocation used for first-boot setup and DE package installs (ProotExec.kt) has no concrete reference to copy from (this app's real boot_command lives per-container in .tiny.yaml, authored upstream, never in this repo). Needs a real-device smoke test.
  • ⚠️ Scope correction from the brief: upstream proot-distro was rewritten in Python for its 2026 OCI-pull update; this app bundles no Python runtime, so "shell out to proot-distro install <alias>" isn't actually feasible without a much bigger, separate effort. Built a small purpose-specific Docker Registry v2 client instead (OciRegistryClient.kt) — same OCI-pull outcome the brief wanted, without the Python dependency.
  • ⚠️ Tier-2 (niri/COSMIC) is deliberately pacman-only — checked, not assumed, that Fedora lacks both in its official repos and Debian/Ubuntu's apt situation isn't verified to the same bar as tier 1 yet.
  • ⚠️ Could not build/run this repo locally to compile-check — jniLibs.zip isn't in a fresh clone (must come from upstream's releases page per this repo's own README).

Files

  • distros.yaml / DistroRegistry.kt — config-driven distro list
  • de_packages.yaml / DeRegistry.kt — config-driven, tiered DE list
  • OciRegistryClient.kt — minimal Docker Registry v2 client
  • DistroInstaller.kt / DeInstaller.kt — dry-run-capable install orchestration, reusing the existing proot+tar extraction pipeline
  • ProotExec.kt — shared, not-yet-validated proot chroot helper

🤖 Generated with Claude Code

Backend for the multi-distro/multi-DE selector: no on-device build/UI
wiring yet (next PR), but the data model, distro rootfs acquisition,
and DE package installation logic are complete and independently
reviewable.

- distros.yaml / DistroRegistry.kt: config-driven distro list (Arch,
  Fedora, Debian, Ubuntu) — adding a distro is a manifest edit, no
  Kotlin changes. Arch uses a verified static tarball URL
  (os.archlinuxarm.org, confirmed live). Fedora/Debian/Ubuntu use OCI
  pulls from Docker Hub's official images (verified live via the real
  Docker Registry v2 API: all three resolve to exactly one arm64 layer,
  so no union-merge logic is needed).

- OciRegistryClient.kt: a small, purpose-built Docker Registry v2 HTTP
  client (auth token -> manifest list -> arch-specific manifest ->
  single layer blob), NOT upstream proot-distro. Confirmed upstream
  proot-distro was rewritten in Python for its 2026 OCI-pull update,
  and this app bundles no Python runtime, so "shell out to proot-distro
  install <alias>" (the brief's original assumption) isn't actually
  feasible without a much larger, separate effort to cross-compile and
  bundle Python for Android/ARM64. Flagging this now rather than
  silently building on top of an unavailable dependency.

- DistroInstaller.kt: fetches a distro's rootfs (OCI or static tarball)
  and extracts it via the same proot+tar pipeline
  ContainerManageViewModel.performInstall() already uses for the
  bundled rootfs.tar.zst, then runs the distro's first_boot_steps
  (verified against the real, live tiny-computer/images upstream guide
  — user/password setup, locale, timezone, polkit rule). Supports a
  dry-run mode (real network calls to resolve size/digest, no download/
  extraction/execution).

- de_packages.yaml / DeRegistry.kt / DeInstaller.kt: config-driven,
  tiered (stable/experimental) DE list. All tier-1 (XFCE4/LXQt/MATE/
  i3wm) package/group names verified per-distro against real package
  databases (archlinux.org group pages, Fedora's real comps group IDs,
  Debian source package existence + the upstream README's own verified
  apt command) — not assumed identical across distros, per the brief.
  Tier-2 (niri/COSMIC) is deliberately pacman-only: checked, not
  assumed, that Fedora lacks both in its official repos and that
  Debian/Ubuntu's situation isn't verified to the same standard yet.
  DeInstaller refuses a tier-2 install on any other package manager
  rather than guessing.

Known open risk, flagged rather than hidden: the proot chroot
invocation used for first-boot steps and DE package installs
(ProotExec.kt) mirrors this app's existing --link2symlink extraction
flags plus standard proot chroot conventions, but this app's actual
working boot_command lives per-container in .tiny.yaml (authored per
upstream image, never checked into this repo), so there was no
concrete example to copy exactly. Needs a real-device smoke test
before this can be considered validated. A real build+run of this repo
also isn't currently possible without jniLibs.zip, which must come from
upstream's releases page and isn't part of a fresh clone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 03:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Wires PR #25's backend into an actual on-device build flow (menu entry
-> distro pick -> DE pick -> confirm code -> build -> back to
container list), plus a significant correctness fix to the proot
invocation itself found while building this.

## UI (new)
- DistroSelectFragment / DeSelectFragment (+ DistroBuildViewModel,
  activity-scoped like ContainerManageViewModel): RecyclerView pickers
  following this repo's existing ListAdapter+DiffUtil+ViewBinding
  pattern (mirrors QuicksFragment/ContainerManageFragment). DE tier 2
  entries and any DE unavailable for the picked distro's package
  manager are visibly disabled, not hidden.
- Reuses two existing pieces as-is rather than duplicating them:
  ConfirmInstallDialogFragment for the container-code prompt (already
  decoupled via setFragmentResult, works fine from a different
  fragment) and ProgressDialogFragment for build progress.
- Entry point: new "Build New Container" menu item next to the
  existing Import action in ContainerManageFragment's menu.
- New Screen.DistroSelect / Screen.DeSelect(distroAlias) cases in
  MainViewModel/MainActivity, following the exact existing
  screen-routing pattern. Deliberately did NOT add fragment back-stack
  entries for these — the existing ContainerInstall/ContainerMain
  screens have no back-stack either (confirmed: this app's whole
  fragment_container navigation has none), so adding one only for the
  new screens would be inconsistent, not fixing anything.

## Real correctness fix found while wiring this up
ProotExec.kt's chroot flags (from PR #25) were a plausible-sounding
guess (-r/-w/-b, no --change-id, no proc/sys fakes) that turned out to
be meaningfully wrong. Found tiny-computer/images' actual, live
proot-distro.tiny.yaml (a real, presumably-working example this
repo's own README points people to for the .tiny.yaml format) and its
boot_command/export_command use a completely different, more complete
flag set: --change-id=1000:1000 (not -r/-w), --kernel-release spoofing,
--sysvipc, and — most importantly — bind mounts for /proc/loadavg,
/proc/stat, /proc/uptime, /proc/version, /proc/vmstat, and two sysctl
entries, all targeting placeholder files
($CONTAINER_DIR/proc/.loadavg etc.) that upstream's own export.sh
creates by snapshotting a LIVE session via `tar --transform`. A
freshly-pulled distro image has none of these, so DistroInstaller now
creates them with plausible static content after extraction and before
any first-boot step. ProotExec now has two entry points matching the
real file's two real commands: runAsRootCommand (mirrors
export_command, used for first-boot/DE-install) and
bootCommandTemplate (mirrors boot_command, written into the
synthesized .tiny.yaml — TinyYamlBuilder.kt, new).

de_packages.yaml gained a session_command field per tier-1 DE (what to
run inside the container once its boot_command's X11 socket is ready —
verified shape, real dbus-launch/DE-session commands) — tier-2 entries
deliberately have none, since how a Wayland-only compositor gets a
picture onto this app's Termux:X11 bridge is still the unprototyped
question flagged in PR #25, and TinyYamlBuilder refuses to build a
container for a DE with no session_command rather than guess one.

## Still not done / still flagged
- Not device-tested — this repo still can't build without jniLibs.zip
  from upstream's releases page.
- Tier-2 DE build path exists in DeInstaller (PR #25) but the UI here
  disables any DE lacking a session_command, so tier-2 is currently
  unselectable end-to-end until that's prototyped — matches the task
  brief's "don't block tier 1 on tier 2."
- No fragment back-stack / no way to go back a step in the wizard
  besides the system back button (which exits, matching existing
  screens) — a real gap, just not a regression.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DaRipper and others added 6 commits July 20, 2026 22:36
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…CI) to trigger CI on this PR

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI (#27) caught two things on this branch's original ProotExec.kt:

1. A genuine compile error: the KDoc comment's own text contained the
   literal sequence "PROOT_LOADER*/PROOT_TMP_DIR" — the `*/` inside
   that prose prematurely closed the doc comment, so everything after
   it (the rest of the comment, then the real code) got parsed as
   top-level Kotlin and failed with cascading "Expecting a top level
   declaration" syntax errors starting at that exact line/column.

2. The underlying proot flags this file guessed at (-r/-w/-b, no
   --change-id, no proc/sys placeholder files) were also substantively
   wrong, not just wrapped in a comment with a syntax bug. This was
   already found and fixed while building PR #26's UI (which needed to
   actually invoke this to synthesize a working boot_command) by
   locating tiny-computer/images' real, live proot-distro.tiny.yaml —
   but the fix only landed on #26's branch, never backported here,
   which is why CI still caught the old broken version on this one.

Pulling over the already-corrected files as-is from #26 rather than
re-deriving them: ProotExec.kt (runAsRootCommand/bootCommandTemplate
replacing the old runInContainerCommand, using the real verified
flags), DistroInstaller.kt (creates the proc/sys placeholder files
those real flags' bind mounts require, in a second shell session after
extraction completes), DeInstaller.kt (updated call site).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI (#27) caught this: showFragmentForScreen()'s new Screen.DistroSelect/
DeSelect cases referenced these classes without importing them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… keep the stack clean

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat: distro/DE selector UI and real, verified proot invocation flags
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.

2 participants