|
| 1 | +# Install Module - Phase 4 Implementation Plan |
| 2 | + |
| 3 | +This document tracks the scope, progress, and explicit non-goals of the Install Module (`feature = "install"`) in `arch-toolkit`. This is Phase 4 of the extraction plan from Pacsea. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Executive Summary |
| 8 | + |
| 9 | +| Aspect | Details | |
| 10 | +|--------|---------| |
| 11 | +| **Module** | `install` (feature = "install") | |
| 12 | +| **Source** | `Pacsea/src/install/` (command-string helpers only) + `Pacsea/src/logic/privilege.rs` (already represented in `arch-toolkit` as `src/system/privilege.rs`) | |
| 13 | +| **Estimated Effort** | 8-12 hours | |
| 14 | +| **Complexity** | Low-medium (pure string algebra + strict shell-quoting discipline) | |
| 15 | +| **Dependencies** | `error`, `system::privilege` | |
| 16 | +| **Status** | ✅ Complete - Priorities 1-4 shipped, example and phase doc in place | |
| 17 | + |
| 18 | +Phase 4 is intentionally scoped to **framework-agnostic, pure-Rust command string builders** for `pacman`/AUR-helper operations. Password pipes, PTY spawning, TUI wiring, and filesystem scans stay in Pacsea. |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Scope |
| 23 | + |
| 24 | +### In scope (ported / aligned) |
| 25 | + |
| 26 | +- Shell-quoting helpers (`shell_single_quote`, `is_safe_package_name`, `validate_package_names`, `quote_and_join_package_names`) - [src/install/utils.rs](../src/install/utils.rs) |
| 27 | +- Install command builders hardened to quote every interpolated package name: |
| 28 | + - `build_pacman_install_command` (official `-S` installs) |
| 29 | + - `build_aur_install_command` (helper installs with an explicit `AurHelper`) |
| 30 | + - `build_batch_install_command` (mixed official + AUR planning with overlap preflight) |
| 31 | +- Stateless helpers for constructing AUR planning fragments: |
| 32 | + - `aur_install_helper_flags` (constant flag selection) |
| 33 | + - `aur_install_body` (paru-preferred / yay-fallback shell snippet) |
| 34 | + - `detect_aur_helper` (PATH-based preference) |
| 35 | + - `aur_pkgnames_also_in_official_catalog` (overlap detection) |
| 36 | +- Remove / downgrade / update command builders wired through [`system::privilege`](../src/system/privilege.rs): |
| 37 | + - `CascadeMode` (mirrors Pacsea's `-R` / `-Rs` / `-Rns` modes) |
| 38 | + - `build_remove_command(tool, names, cascade, dry_run)` |
| 39 | + - `build_downgrade_command(tool, names, dry_run)` (guards against missing `downgrade` helper) |
| 40 | + - `build_update_command(commands, dry_run)` (chain with `&&`, dry-run echoes each step) |
| 41 | + |
| 42 | +### Out of scope (remains in Pacsea) |
| 43 | + |
| 44 | +- `Pacsea/src/install/executor.rs` — PTY spawning, password pipes, `sudo -S` integration, hold-tail loop, credential warm-up. |
| 45 | +- `Pacsea/src/install/scan/` and `Pacsea/src/install/patterns.rs` — scanner + env wiring; better fit a future Sandbox phase or a dedicated `scan` feature. |
| 46 | +- `Pacsea/src/install/logging.rs`, terminal spawn helpers, UX-specific dry-run failure simulation (`PACSEA_TEST_SIMULATE_PACMAN_FAILURE`). |
| 47 | +- Consumers that need password pipes should compose them themselves using `crate::system::privilege::build_privilege_command` plus their own redacted logging strategy. The library deliberately does not construct password pipe commands to keep its security surface small. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## Priorities and progress |
| 52 | + |
| 53 | +### Priority 1 — Security parity on existing APIs ✅ |
| 54 | + |
| 55 | +- [x] Added `shell_single_quote`, `is_safe_package_name`, and `validate_package_names` to `src/install/utils.rs` (mirrors Pacsea's pattern). |
| 56 | +- [x] Re-exported the quoting helpers from the `install` module root. |
| 57 | +- [x] Updated `build_pacman_install_command`, `build_aur_install_command`, and the batch builder to single-quote **every** package token before interpolation. |
| 58 | +- [x] Regression tests cover metacharacters (`;`, `$(...)`, embedded single quotes) and dry-run wrapping. |
| 59 | + |
| 60 | +### Priority 2 — Align helpers with Pacsea's pure install layer ✅ |
| 61 | + |
| 62 | +- [x] Ported `aur_install_body(flags, quoted_names)` so consumers can compose `paru || yay` fallback snippets without running `detect_aur_helper` at planning time. |
| 63 | +- [x] Kept the existing `aur_install_helper_flags(reinstall)` constant function for shared flag strings. |
| 64 | + |
| 65 | +### Priority 3 — Remove / update / downgrade command strings ✅ |
| 66 | + |
| 67 | +- [x] Added `CascadeMode` (`Basic` / `Cascade` / `CascadeWithConfigs`) matching Pacsea. |
| 68 | +- [x] `build_remove_command(tool, names, cascade, dry_run)` uses `system::privilege::build_privilege_command`. |
| 69 | +- [x] `build_downgrade_command(tool, names, dry_run)` emits the defensive `if (command -v downgrade) || pacman -Qi downgrade; then ... else echo ...; fi` shell guard. |
| 70 | +- [x] `build_update_command(commands, dry_run)` chains pre-built steps with ` && ` and wraps each step in `echo DRY RUN: '...'` on dry-run. |
| 71 | +- [x] Unit tests cover empty inputs, both privilege tools, cascade variants, dry-run wrapping, and guard logic. |
| 72 | + |
| 73 | +### Priority 4 — Process / documentation ✅ |
| 74 | + |
| 75 | +- [x] Update [AUR_TOOLKIT_CRATE_PREPARATION.md](./AUR_TOOLKIT_CRATE_PREPARATION.md) Phase 4 bullet to reflect in-progress state. |
| 76 | +- [x] Reconcile [INDEX_MODULE_PHASE.md](./INDEX_MODULE_PHASE.md) with the current `src/index` + `src/repos` tree (Task 3.5.x are implemented). |
| 77 | +- [x] Author this `INSTALL_MODULE_PHASE.md`. |
| 78 | +- [x] Expand `examples/install_example.rs` to tutorial-style parity with `pkgbuild_example` / `deps_types_example` (see Examples below). |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## Public API surface (current) |
| 83 | + |
| 84 | +```text |
| 85 | +arch_toolkit::install |
| 86 | +├── utils::shell_single_quote |
| 87 | +├── utils::is_safe_package_name |
| 88 | +├── utils::validate_package_names |
| 89 | +├── utils::quote_and_join_package_names |
| 90 | +├── InstallSource { Official, Aur } |
| 91 | +├── InstallTarget { name, source } |
| 92 | +├── AurHelper { Paru, Yay } + binary_name() |
| 93 | +├── aur_install_helper_flags(reinstall) |
| 94 | +├── aur_install_body(flags, quoted_names) |
| 95 | +├── detect_aur_helper() |
| 96 | +├── build_pacman_install_command(packages, reinstall, dry_run) |
| 97 | +├── build_aur_install_command(helper, packages, reinstall, dry_run) |
| 98 | +├── build_batch_install_command(targets, official_has_reinstall, aur_has_reinstall, official_catalog, dry_run) -> Result |
| 99 | +├── aur_pkgnames_also_in_official_catalog(targets, catalog) -> Vec<String> |
| 100 | +├── CascadeMode { Basic, Cascade, CascadeWithConfigs } + flag() |
| 101 | +├── build_remove_command(tool, names, cascade, dry_run) |
| 102 | +├── build_downgrade_command(tool, names, dry_run) |
| 103 | +└── build_update_command(commands, dry_run) |
| 104 | +``` |
| 105 | + |
| 106 | +All package tokens interpolated into shell command strings are passed through `shell_single_quote`. Callers who need strict allowlist enforcement should invoke `validate_package_names` before building commands. |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## Acceptance criteria |
| 111 | + |
| 112 | +- [x] Every builder that interpolates package tokens quotes them individually. |
| 113 | +- [x] Unit tests exist for each builder, including empty-list and dry-run paths. |
| 114 | +- [x] Regression tests prove shell metacharacters are neutralised. |
| 115 | +- [x] Remove / update / downgrade builders delegate privilege-tool selection to `crate::system::privilege`. |
| 116 | +- [x] No new environment-variable test-overrides ship in release builds. |
| 117 | +- [x] `cargo test --all-features -- --test-threads=1` passes. |
| 118 | +- [x] `cargo clippy --all-targets --all-features -- -D warnings` is clean. |
| 119 | +- [x] `examples/install_example.rs` walks through each public API in sectioned output (parity with Phase 2/3 examples). |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +## Examples parity (pending) |
| 124 | + |
| 125 | +Bring `examples/install_example.rs` up to the same tutorial style as [`examples/pkgbuild_example.rs`](../examples/pkgbuild_example.rs) and [`examples/deps_types_example.rs`](../examples/deps_types_example.rs): |
| 126 | + |
| 127 | +- Sectioned stdout walkthrough ("# 1. Shell quoting", "# 2. Validation", "# 3. Pacman install", "# 4. AUR install", "# 5. Mixed batch", "# 6. Remove / downgrade / update"). |
| 128 | +- Scenarios to demonstrate: empty lists, `reinstall = true/false`, `dry_run = true/false`, mixed official + AUR batch, overlap error path, quoting of awkward-but-valid edge cases. |
| 129 | +- Include `cargo run --example install_example --features install` in the file header doc comment. |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +## Verification |
| 134 | + |
| 135 | +Run from the repository root (per [AGENTS.md](../AGENTS.md)): |
| 136 | + |
| 137 | +```bash |
| 138 | +cargo fmt --all |
| 139 | +cargo clippy --all-targets --all-features -- -D warnings |
| 140 | +cargo check |
| 141 | +cargo test --all-features -- --test-threads=1 |
| 142 | +``` |
| 143 | + |
| 144 | +--- |
| 145 | + |
| 146 | +## References |
| 147 | + |
| 148 | +- [AUR_TOOLKIT_CRATE_PREPARATION.md](./AUR_TOOLKIT_CRATE_PREPARATION.md) — overall extraction plan. |
| 149 | +- [DEPENDENCIES_MODULE_PHASE.md](./DEPENDENCIES_MODULE_PHASE.md) — Phase 2 reference for doc structure. |
| 150 | +- [INDEX_MODULE_PHASE.md](./INDEX_MODULE_PHASE.md) — Phase 3 reference. |
| 151 | +- Pacsea source: `src/install/` — source code for command-string helpers. |
| 152 | +- Pacsea source: `src/logic/privilege.rs` — already represented here as `src/system/privilege.rs`. |
0 commit comments