3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
8.2 KiB
8.2 KiB
WinPodX 0.6.0 — Consolidation & UX Release
Goal
Single-pass cleanup of accumulated structural debt: unify duplicated implementations, sharpen the command surface, and rethink the AppImage so it stops being fragile. After 0.6.0, the shells (install.sh, uninstall.sh, postinst, postrm) are thin wrappers and the Python CLI is the single source of truth.
This roadmap is the cut-list. Items move from "in scope" to "done" only when the corresponding PR has merged to main. Deferred items are recorded so we don't lose them; "won't do" items are decided no.
In scope (must land in 0.6.0)
Provisioning + AppImage
- A. Thin AppImage. Drop bundled podman / podman-compose / conmon / crun / netavark / pasta. Keep FreeRDP + Python + Qt + WinPodX. Require host podman/docker/libvirt (same model as
install.sh). Closes #357 and #363 by removing the root cause (bundled podman shadowing or poisoning the host stack) instead of patching around it. AppImage shrinks ~150 MB → ~50 MB._hostenvhelper collapses to anLD_LIBRARY_PATHstrip (no host-firstPATHgames needed once nothing is shadowing). Effort M / Risk M (recipe rebuild + reporter smoke). - B. P1 —
winpodx provisionsingle source of truth. Newcore/provisioner.finish_provisioning(cfg, *, wait_timeout, require_agent, with_reverse_open)consolidates thewait-ready → apply-fixes → discovery → reverse-openchain currently duplicated ininstall.sh,setup_cmd._run_full_provision,migrate, andpending.resume.install.shcollapses ~140 lines → ~5.setup --create-onlyis dropped. Effort L / Risk M (load-bearing; needs real-Windows smoke). Absorbs I. - I. Qt GUI bring-up →
winpodx provision.gui/_main_window_bringup.pyis the 5th copy of the chain. Folded into B. Effort S (inside B).
Hardcoding / single source
- C. P2 — port 8765 constant.
core/agent.AGENT_PORTreferenced by all host-side callers (compose generation, checks, guest_sync,install.shhealth-poll). The guest-sideagent.ps1keeps its own literal (can't import Python); the pairing is documented. 8 literals → 1 host SoT. Effort S / Risk Low. - D. P3 — dependency / health single detector. Extend
utils/deps.py:check_all(+ kvm, + podman-major gate).deps_quickcheck,doctor,setup_cmdall consume it. Drop the re-hardcoded freerdp lists.install.shkeeps a minimal pre-venv bash probe (genuinely shell-unique). 5 places → 1 Python SoT + 1 minimal shell probe. Effort M / Risk Low-M. - E. P4 — backend selection helper.
core/backend/select.py:choose_backend(prefer, present)implementingpodman → docker → libvirt+ the podman major-version gate.install.shandsetup_cmdboth consume it. 3 places → 1. Effort S-M / Risk Low. - F. P5 — version + edition single source.
__init__.__version__reads from package metadata. Windows-edition help/locale strings generate from_KNOWN_WIN_VERSIONS. Fix stalepackaging/rpm/winpodx.spec:8literal. Effort S / Risk Low.
Command surface
- G. Command taxonomy reorg.
- Diagnostics:
info+check+doctor→ singlewinpodx doctor(--jsonmachine-readable,--quickfast subset,--fixsee K). podkeeps lifecycle only:start | stop | restart | recreate | status | wait-ready.- New
winpodx guest:apply-fixes,sync,sync-password,multi-session,recover-oem. - New
winpodx install:status,resume,grow-disk,disk-usage. - Deprecation aliases: old
pod apply-fixesetc. keep working through 0.6.x with a deprecation note; removed in 0.7.0. install.sh/uninstall.sh/postinst/postrmcall only publicwinpodx <command>(no internal Python imports).- Effort M-L / Risk Low with aliases.
- Diagnostics:
- K.
winpodx doctor --fixauto-remediation. ✅ Implemented (PRfeat/doctor-fix); guest-touching fixers smoke-gated before merge. Common recoverable failures: dead agent → keepalive kick, stale lock files → purge, missing desktop entries → re-register, oem-version drift → trigger guest-sync. Each fix idempotent. Effort S-M / Risk Low.
Docs + ops
- H. Documentation refresh (mandatory for G). README, FEATURES, COMPARISON, ARCHITECTURE, LIFECYCLE, USAGE + their
docs/*.ko.mdmirrors. New-command table + old→new alias map. Install / upgrade flows reflect Thin AppImage +winpodx provision. Effort M / Risk Low. - J. Config schema versioning.
cfg.schema_version: int = 1field + load-time migration hook. 0.6.0 doesn't change config structure, but the marker lets 0.7.0+ migrate cleanly. Effort S / Risk Low. - L. Logging hygiene pass. Consistent levels across modules (some INFO too noisy, some WARNING missing). One-pass review. Effort S / Risk Low.
- M.
install.sh: drop discovery 6× retry. Agent keepalive (#359) proven; 6× is overkill. 1-2× max. Faster first run. Effort S / Risk Low. - N. Packaging single-source enforcement.
flake.nixalready readspyproject(good); fixwinpodx.spec:8stale literal + a lint that catches future drift. Subset of F. Effort S / Risk Low.
Execution order
- C — port constant (warm-up, fastest, low risk).
- J —
schema_version(cheap future-proofing before big changes). - F + N — version / edition / packaging single-source (quick).
- D — dep check (prerequisite for cleaner E and
install.sh). - E — backend select (prerequisite for cleaner A and
install.sh). - A — Thin AppImage (root-cause kill for #357 / #363).
- B + I — provision unify + GUI bring-up (biggest piece; real-Windows smoke gate).
- G — command taxonomy (depends on B; deprecation aliases for back-compat).
- K —
doctor --fix(pairs with G's doctor). - L — logging hygiene (pass once everything else has landed).
- M —
install.shretry trim (final tightening). - H — docs refresh (last; reflects final shapes; ko mirrors after EN approved).
- Cut
v0.6.0+REL-v0.6.0.
Real-Windows smoke gate applies to: A (rebuilt AppImage), B / I (provision chain), G (command renames touching guest paths), K (--fix actions). Other items don't need it.
Deferred to 0.7.0+ (tracked, not started)
- Agent authentication. The listener is localhost-only and only reachable from the host via dockur's port map → low risk today. Future: HMAC or shared-token on
/execto harden against host-local malice. - Real-Windows smoke CI lane. A cloud Windows VM in CI to gate guest-side install changes. Expensive (~$50/mo + setup); manual user smoke continues for now.
- libvirt backend feature-completeness. podman is primary; libvirt has rougher edges (storage growth, multi-session). Separate effort.
- Manual mode (no backend) testing. Niche path; document constraints rather than build a test surface.
- Performance pass. Discovery cache, FreeRDP launch latency, full-rescan vs. incremental. Current is adequate.
- Reverse-open edge cases. Special chars in paths, multi-monitor positioning, MIME priority conflicts. File as encountered.
- i18n drift detection. A lint that catches keys present in only some
locale/*.json. Small but needs CI infra. - Content-addressed
oem_bundle. Currently a counter (#359 bumped to 26). A hash would be drift-proof. Backwards-compat work to migrate existing stamps. - OBS publish race. Timing race on the publish workflow (intermittent re-runs needed). Workflow refactor.
- Lock-file lifecycle. Stale lock cleanup in
~/.local/share/winpodx/run/is hit-or-miss. Owned-pid check + boot-time sweep. - PowerShell window flash regression test. A test that asserts no host→guest op ends up on
run_in_windows(the FreeRDP path that flashes) outside of explicitly-flash-okay callers. Locks in the #355 win.
Won't do
- External telemetry. Decided. Local
FreeRDP-fallbacklog markers (already shipped) substitute for the only signal we needed. - New opt-in flags for behaviour. Decided. Features ship default-on; behaviour-changing flags are debt.
- Bundled-podman "actually works". Option A acknowledges the fat AppImage's "self-contained" promise was always limited by the host-systemd dependency. Thin makes that limit explicit instead of pretending.