Browse Source
- OpenSpec for declarative pacman package management - stdin and --state file inputs with merging - Full system upgrades, transitive deps, orphan cleanup - AUR support with pacman/makepkg fallback - Machine-readable output for scriptingmaster
14 changed files with 443 additions and 1 deletions
@ -0,0 +1,2 @@ |
|||||||
|
schema: spec-driven |
||||||
|
created: 2026-04-13 |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
## Context |
||||||
|
|
||||||
|
Pacman, Arch Linux's package manager, lacks declarative package management and |
||||||
|
doesn't support partial upgrades. Users managing system packages from scripts |
||||||
|
or configuration files need a way to ensure their system state matches a declared |
||||||
|
package list, with all packages at their latest available versions. |
||||||
|
|
||||||
|
## Goals / Non-Goals |
||||||
|
|
||||||
|
**Goals:** |
||||||
|
- Provide a declarative interface for pacman package management through |
||||||
|
command-line interface |
||||||
|
- Support flexible input sources (stdin and multiple state files) with |
||||||
|
additive merging |
||||||
|
- **Automatically resolve transitive dependencies (users specify only direct packages)** |
||||||
|
- Enforce full package upgrades (no partial upgrades) to prevent version |
||||||
|
mismatches |
||||||
|
- Generate machine-readable output suitable for automated scripting |
||||||
|
- Enable AI-driven implementation for new Go developers |
||||||
|
|
||||||
|
**Non-Goals:** |
||||||
|
- Package dependency resolution or smart upgrade scheduling |
||||||
|
- Transaction rollback capabilities |
||||||
|
- Pretty terminal output or interactive prompts |
||||||
|
- Support for partial upgrades or selective package versions |
||||||
|
- State persistence (tool only syncs, doesn't save state) |
||||||
|
|
||||||
|
## Decisions |
||||||
|
|
||||||
|
**Language: Go** |
||||||
|
- Rationale: User wants modern CLI tool with native package management |
||||||
|
capabilities |
||||||
|
- Go provides strong standard library, easy command-line parsing, and |
||||||
|
excellent for system-level operations |
||||||
|
- User unfamiliar with Go - implementation will be AI-driven with detailed |
||||||
|
documentation |
||||||
|
|
||||||
|
**Input Parsing Approach** |
||||||
|
- Parse whitespace, tabs, newlines for package names |
||||||
|
- Support multiple --state files: all additive including with stdin |
||||||
|
- Validate package names against pacman available packages (optional validation) |
||||||
|
- Empty state detection: print error to stderr and exit with code 1 (abort) |
||||||
|
|
||||||
|
**State Merging Logic** |
||||||
|
- All inputs are additive: combine all packages from stdin and all state files |
||||||
|
- No conflict resolution: missing packages are added, duplicates accumulate |
||||||
|
- Order-independent: inputs don't override each other |
||||||
|
|
||||||
|
**Pacman Interaction** |
||||||
|
- Use os/exec.Command to run pacman with -Sy (sync databases) and -S flag |
||||||
|
- Run pacman -Syu with all target packages to ensure full upgrade |
||||||
|
- Capture stderr/stdout for error reporting and machine output |
||||||
|
- Detect pacman exit codes and translate to tool exit codes |
||||||
|
|
||||||
|
**Error Handling Strategy** |
||||||
|
- Parse pacman error messages from stderr output |
||||||
|
- Distinguish between package-not-found (warning) vs. execution failures (errors) |
||||||
|
- Return appropriate exit codes: 0 for success, non-zero for errors |
||||||
|
- Include error details in output for scripting purposes |
||||||
|
|
||||||
|
**AUR Integration** |
||||||
|
- For packages not found in pacman repositories, check if available in AUR |
||||||
|
- Use makepkg directly to build and install AUR packages (no AUR helpers) |
||||||
|
- Clone AUR git repo to temp directory |
||||||
|
- Run `makepkg -si` in temp directory for installation |
||||||
|
- Capture stdout/stderr for output and error handling |
||||||
|
- Report error to stderr if package not found in pacman or AUR |
||||||
|
|
||||||
|
**Dependency Resolution** |
||||||
|
- Use pacman's dependency resolution by passing all declared packages to `pacman -Syu` |
||||||
|
- Pacman automatically resolves and includes transitive dependencies |
||||||
|
- For AUR packages, makepkg handles dependency resolution during build |
||||||
|
- No custom dependency resolution logic required - delegate to pacman/makepkg |
||||||
|
|
||||||
|
**Explicit Marking & Orphan Cleanup** |
||||||
|
- Before syncing, get list of all currently installed packages |
||||||
|
- Mark declared state packages as explicitly installed via `pacman -D --explicit <pkg>` |
||||||
|
- All other installed packages remain as non-explicit (dependencies) |
||||||
|
- After sync completes, run `pacman -Rns $(pacman -Qdtq)` to remove orphaned packages |
||||||
|
- Pacman -Rns $(pacman -Qdtq) only removes packages that are not explicitly |
||||||
|
installed and not required |
||||||
|
- Capture and report number of packages removed during cleanup |
||||||
|
|
||||||
|
**Package Version Management** |
||||||
|
- Always force full system upgrade via `pacman -Syu <packages>` |
||||||
|
- Pacman handles version selection automatically, ensuring latest versions |
||||||
|
- No semantic version comparison or pinning logic required |
||||||
|
|
||||||
|
## Risks / Trade-offs |
||||||
|
|
||||||
|
**Known Risks:** |
||||||
|
- [User unfamiliarity with Go] → Mitigation: Provide complete implementation |
||||||
|
with detailed comments; user can review and run without understanding deeply |
||||||
|
- [Pacman error message parsing complexity] → Mitigation: Use broad error |
||||||
|
pattern matching; include error output as-is for debugging |
||||||
|
- [Empty state triggers abort] → Mitigation: Print error to stderr and exit |
||||||
|
with code 1 instead of proceeding with empty state |
||||||
|
- [Additive merging could accumulate duplicates] → Mitigation: Accept as |
||||||
|
design choice; pacman handles duplicates gracefully |
||||||
|
- [Dependency conflicts could occur] → Mitigation: Let pacman handle standard |
||||||
|
conflicts; tool won't implement complex dependency resolution |
||||||
|
|
||||||
|
**Trade-offs:** |
||||||
|
- No conflict resolution: simpler merging but may include packages the system |
||||||
|
rejects |
||||||
|
- Additive vs replacement: safer but less predictable for users expecting |
||||||
|
replacements |
||||||
|
- No user prompts: fully automated for scripting but could be risky without |
||||||
|
warnings |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
## Why |
||||||
|
|
||||||
|
Pacman doesn't support declarative package management or partial upgrades, |
||||||
|
making it challenging to maintain consistent system states. This tool provides |
||||||
|
a clean, scriptable interface to synchronize the system with a declared package |
||||||
|
list, ensuring all packages are at the latest version. |
||||||
|
|
||||||
|
## What Changes |
||||||
|
|
||||||
|
- Add CLI tool for declarative pacman package management |
||||||
|
- Support stdin input for package lists |
||||||
|
- Support multiple --state file inputs |
||||||
|
- Merge all inputs (additive strategy with empty state warning) |
||||||
|
- Force full upgrade of all packages (no partial upgrades) |
||||||
|
- Handle transitive dependencies automatically (users only specify direct packages) |
||||||
|
- Mark non-state packages as non-explicit before sync |
||||||
|
- After sync, remove orphaned packages (cleanup) |
||||||
|
- Support AUR packages: try pacman first, then makepkg, then report errors |
||||||
|
- Machine-readable output (install/remove counts, exit codes) |
||||||
|
- No conflict resolution for missing packages (append-only) |
||||||
|
- Print error to stderr for empty state input and exit with code 1 |
||||||
|
|
||||||
|
## Capabilities |
||||||
|
|
||||||
|
### New Capabilities |
||||||
|
|
||||||
|
- **stdin-input**: Read package lists from standard input stream |
||||||
|
- **state-files**: Load declarative package states from files |
||||||
|
- **input-merging**: Combine multiple input sources (stdin + multiple --state files) |
||||||
|
- **state-validation**: Validate package names and empty states |
||||||
|
- **transitive-deps**: Automatically resolve all transitive dependencies |
||||||
|
- **pacman-sync**: Execute pacman operations to match declared state |
||||||
|
- **aur-sync**: Handle AUR packages: pacman first, then fall back to makepkg, |
||||||
|
then report errors |
||||||
|
- **machine-output**: Generate machine-readable sync results for scripting |
||||||
|
|
||||||
|
### Modified Capabilities |
||||||
|
|
||||||
|
None (new tool) |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can handle AUR packages with pacman fallback |
||||||
|
|
||||||
|
The system SHALL attempt to install AUR packages by first trying pacman |
||||||
|
repositories, then falling back to AUR when pacman fails, and finally reporting |
||||||
|
errors for packages still missing. |
||||||
|
|
||||||
|
#### Scenario: Install from pacman first |
||||||
|
- **WHEN** package is in pacman repositories |
||||||
|
- **THEN** system shall install via pacman |
||||||
|
|
||||||
|
#### Scenario: Fall back to AUR |
||||||
|
- **WHEN** package is not in pacman repositories but is in AUR |
||||||
|
- **THEN** system shall attempt installation via makepkg (direct AUR build) |
||||||
|
|
||||||
|
#### Scenario: Report error for missing packages |
||||||
|
- **WHEN** package is not in pacman repositories or AUR |
||||||
|
- **THEN** system shall report error and print to stderr |
||||||
|
|
||||||
|
#### Scenario: Continue for remaining packages |
||||||
|
- **WHEN** some packages are installed and some fail |
||||||
|
- **THEN** system shall continue installation process for successful packages |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can merge multiple input sources |
||||||
|
|
||||||
|
The system SHALL combine package lists from all inputs using an additive |
||||||
|
strategy without conflict resolution. |
||||||
|
|
||||||
|
#### Scenario: Additive merging of all inputs |
||||||
|
- **WHEN** stdin and multiple state files provide package lists |
||||||
|
- **THEN** system shall include all packages from all inputs in the final state |
||||||
|
|
||||||
|
#### Scenario: Input priority is last-writer-wins per file |
||||||
|
- **WHEN** multiple state files contain the same package name |
||||||
|
- **THEN** the package from the last provided file in command-line order takes |
||||||
|
precedence |
||||||
|
|
||||||
|
#### Scenario: Missing packages from stdin are added |
||||||
|
- **WHEN** stdin contains packages not in state files |
||||||
|
- **THEN** those packages shall be added to the final state |
||||||
|
|
||||||
|
#### Scenario: Duplicate packages accumulate |
||||||
|
- **WHEN** the same package appears in multiple inputs |
||||||
|
- **THEN** it shall be included multiple times in the final state (pacman |
||||||
|
handles duplicates) |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can generate machine-readable output |
||||||
|
|
||||||
|
The system SHALL produce output suitable for automated scripting. |
||||||
|
|
||||||
|
#### Scenario: Install/remove count output |
||||||
|
- **WHEN** sync completes successfully |
||||||
|
- **THEN** system shall output: "<installed_count> package(s) installed, <removed_count> package(s) removed" |
||||||
|
|
||||||
|
#### Scenario: Empty output format |
||||||
|
- **WHEN** no packages to sync |
||||||
|
- **THEN** system shall output nothing (or indicate no changes) |
||||||
|
|
||||||
|
#### Scenario: Error details in output |
||||||
|
- **WHEN** pacman operation fails |
||||||
|
- **THEN** system shall include error details in output (package names, pacman error messages) |
||||||
|
|
||||||
|
#### Scenario: Exit code for scripting |
||||||
|
- **WHEN** sync completes |
||||||
|
- **THEN** system shall return exit code 0 for success, non-zero for errors |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can mark non-state packages as non-explicit |
||||||
|
|
||||||
|
The system SHALL mark all packages not declared in the state as non-explicit |
||||||
|
(dependencies) before syncing, so they can be safely removed later. |
||||||
|
|
||||||
|
#### Scenario: Mark packages as non-explicit |
||||||
|
- **WHEN** packages are declared in state |
||||||
|
- **THEN** system shall mark those packages as explicitly installed via pacman -D --explicit |
||||||
|
- **AND** mark all other installed packages as non-explicit |
||||||
|
|
||||||
|
### Requirement: Can clean up orphaned packages |
||||||
|
|
||||||
|
After syncing, the system SHALL remove packages that are no longer required |
||||||
|
(as dependencies of removed packages). |
||||||
|
|
||||||
|
#### Scenario: Orphan cleanup after sync |
||||||
|
- **WHEN** sync operation completes successfully |
||||||
|
- **THEN** system shall run pacman -Rsu to remove unneeded dependencies |
||||||
|
- **AND** report the number of packages removed |
||||||
|
|
||||||
|
#### Scenario: Orphan cleanup respects explicitly installed |
||||||
|
- **WHEN** a package not in state is marked as explicitly installed by user |
||||||
|
- **THEN** system shall NOT remove it during orphan cleanup |
||||||
|
|
||||||
|
#### Scenario: No orphans to clean |
||||||
|
- **WHEN** there are no orphaned packages to remove |
||||||
|
- **THEN** system shall report "No packages to remove" in output |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can sync system with declared package state |
||||||
|
|
||||||
|
The system SHALL execute pacman operations to install and upgrade all declared packages. |
||||||
|
|
||||||
|
#### Scenario: Full system upgrade with all packages |
||||||
|
- **WHEN** system has declared package list |
||||||
|
- **THEN** system shall run pacman -Syu with all declared package names |
||||||
|
|
||||||
|
#### Scenario: No partial upgrades |
||||||
|
- **WHEN** running pacman commands |
||||||
|
- **THEN** system shall use -Syu flag (full system upgrade) ensuring all packages are latest |
||||||
|
|
||||||
|
#### Scenario: Package availability check |
||||||
|
- **WHEN** a package from input is not in pacman repositories |
||||||
|
- **THEN** system shall report the error and include package name |
||||||
|
|
||||||
|
#### Scenario: Pacman execution capture |
||||||
|
- **WHEN** pacman commands execute |
||||||
|
- **THEN** system shall capture both stdout and stderr for error reporting |
||||||
|
|
||||||
|
#### Scenario: Exit code propagation |
||||||
|
- **WHEN** pacman commands execute |
||||||
|
- **THEN** system shall exit with code equal to pacman's exit code for success/failure detection |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can load package states from state files |
||||||
|
|
||||||
|
The system SHALL load package lists from state files specified via --state flag. |
||||||
|
|
||||||
|
#### Scenario: Single state file loading |
||||||
|
- **WHEN** a state file is provided via --state flag |
||||||
|
- **THEN** system shall read package names from the file content |
||||||
|
|
||||||
|
#### Scenario: Multiple state file loading |
||||||
|
- **WHEN** multiple --state flags are provided |
||||||
|
- **THEN** system shall load package names from each file |
||||||
|
|
||||||
|
#### Scenario: State file format is text list |
||||||
|
- **WHEN** loading from state files |
||||||
|
- **THEN** system shall parse whitespace-delimited package names |
||||||
|
|
||||||
|
#### Scenario: File path validation |
||||||
|
- **WHEN** a state file path points to a non-existent file |
||||||
|
- **THEN** system shall report a file read error |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can detect empty state input |
||||||
|
|
||||||
|
The system SHALL detect when no packages are provided in any input source. |
||||||
|
|
||||||
|
#### Scenario: Empty state warning |
||||||
|
- **WHEN** no package names are found in stdin or state files |
||||||
|
- **THEN** system shall print a warning to stderr |
||||||
|
|
||||||
|
#### Scenario: Empty state warning message |
||||||
|
- **WHEN** warning is printed for empty state |
||||||
|
- **THEN** message shall say: "Called without state, aborting.." |
||||||
|
|
||||||
|
#### Scenario: Abort on empty state |
||||||
|
- **WHEN** empty state is detected |
||||||
|
- **THEN** system shall exit with code 1 (error: no packages to sync) |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can read package lists from stdin |
||||||
|
|
||||||
|
The system SHALL accept package names from standard input, where each line represents a package name. |
||||||
|
|
||||||
|
#### Scenario: Packages from stdin are read correctly |
||||||
|
- **WHEN** package names are passed via stdin separated by whitespace, tabs, or newlines |
||||||
|
- **THEN** system shall parse each unique package name from the input stream |
||||||
|
|
||||||
|
#### Scenario: Empty stdin input is handled |
||||||
|
- **WHEN** stdin contains no package names |
||||||
|
- **THEN** system shall skip stdin input processing |
||||||
|
|
||||||
|
#### Scenario: Whitespace normalization |
||||||
|
- **WHEN** packages are separated by multiple spaces, tabs, or newlines |
||||||
|
- **THEN** each package name shall have leading/trailing whitespace trimmed |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
## ADDED Requirements |
||||||
|
|
||||||
|
### Requirement: Can resolve transitive dependencies automatically |
||||||
|
|
||||||
|
The system SHALL automatically determine and install all transitive dependencies |
||||||
|
for declared packages, so users only need to specify direct packages. |
||||||
|
|
||||||
|
#### Scenario: Transitive dependencies are resolved |
||||||
|
- **WHEN** a package with dependencies is declared in state |
||||||
|
- **THEN** system shall identify all transitive dependencies via pacman |
||||||
|
- **AND** include them in the installation operation |
||||||
|
|
||||||
|
#### Scenario: Dependency resolution includes AUR dependencies |
||||||
|
- **WHEN** a package's transitive dependencies include AUR packages |
||||||
|
- **THEN** system shall resolve those AUR dependencies via makepkg |
||||||
|
|
||||||
|
#### Scenario: Shared dependencies are deduplicated |
||||||
|
- **WHEN** multiple declared packages share dependencies |
||||||
|
- **THEN** system shall install each shared dependency only once |
||||||
|
|
||||||
|
#### Scenario: Missing dependency handling |
||||||
|
- **WHEN** a declared package has a dependency not available in pacman or AUR |
||||||
|
- **THEN** system shall report error for the missing dependency |
||||||
@ -0,0 +1,81 @@ |
|||||||
|
## 1. Project Setup |
||||||
|
|
||||||
|
- [ ] 1.1 Initialize Go module with proper imports |
||||||
|
- [ ] 1.2 Add required dependencies (libalpm wrapper) |
||||||
|
- [ ] 1.3 Set up project structure (main.go, pkg/ subdirectory) |
||||||
|
|
||||||
|
## 2. Input Parsing |
||||||
|
|
||||||
|
- [ ] 2.1 Implement stdin reader to collect package names |
||||||
|
- [ ] 2.2 Implement state file reader for text-list format |
||||||
|
- [ ] 2.3 Add whitespace normalization for package names |
||||||
|
- [ ] 2.4 Create package name set data structure |
||||||
|
|
||||||
|
## 3. Input Merging |
||||||
|
|
||||||
|
- [ ] 3.1 Implement additive merging of stdin and state file packages |
||||||
|
- [ ] 3.2 Handle multiple --state flags with last-writer-wins per file |
||||||
|
- [ ] 3.3 Implement duplicate package handling (no deduplication) |
||||||
|
|
||||||
|
## 4. State Validation |
||||||
|
|
||||||
|
- [ ] 4.1 Implement empty state detection (no packages found) |
||||||
|
- [ ] 4.2 Add stderr error output for empty state |
||||||
|
- [ ] 4.3 Set exit code 1 for empty state case (abort, not proceed) |
||||||
|
|
||||||
|
## 5. Pacman Integration |
||||||
|
|
||||||
|
- [ ] 5.1 Implement pacman -Syu command execution wrapper |
||||||
|
- [ ] 5.2 Add command-line argument construction with package list |
||||||
|
- [ ] 5.3 Capture pacman stdout and stderr output |
||||||
|
- [ ] 5.4 Implement pacman error message parsing |
||||||
|
- [ ] 5.5 Handle pacman exit codes for success/failure detection |
||||||
|
- [ ] 5.6 Verify pacman automatically resolves transitive dependencies |
||||||
|
- [ ] 5.7 Verify shared dependencies are deduplicated by pacman |
||||||
|
|
||||||
|
## 6. Explicit Marking & Orphan Cleanup |
||||||
|
|
||||||
|
- [ ] 6.1 Get list of currently installed packages before sync |
||||||
|
- [ ] 6.2 Mark declared state packages as explicitly installed via pacman -D --explicit |
||||||
|
- [ ] 6.3 Run pacman sync operation (5.x series) |
||||||
|
- [ ] 6.4 Run pacman -Rsu to remove orphaned packages |
||||||
|
- [ ] 6.5 Capture and report number of packages removed |
||||||
|
- [ ] 6.6 Handle case where no orphans exist (no packages removed) |
||||||
|
|
||||||
|
## 7. AUR Integration |
||||||
|
|
||||||
|
- [ ] 7.1 Implement AUR package lookup via package query (Jguer/aur) |
||||||
|
- [ ] 7.2 Add pacman first attempt (package not in repos) |
||||||
|
- [ ] 7.3 Implement AUR fallback using makepkg (direct build, not AUR helper) |
||||||
|
- [ ] 7.4 Clone AUR package git repo to temp directory |
||||||
|
- [ ] 7.5 Run makepkg -si in temp directory for installation |
||||||
|
- [ ] 7.6 Add stderr error reporting for packages not in pacman or AUR |
||||||
|
- [ ] 7.7 Capture makepkg stdout and stderr for output parsing |
||||||
|
- [ ] 7.8 Handle makepkg exit codes for success/failure detection |
||||||
|
|
||||||
|
## 8. Output Generation |
||||||
|
|
||||||
|
- [ ] 8.1 Capture installed/removed package counts from pacman output |
||||||
|
- [ ] 8.2 Generate machine-readable output line in requested format |
||||||
|
- [ ] 8.3 Include error details in output on failure |
||||||
|
|
||||||
|
## 9. CLI Interface |
||||||
|
|
||||||
|
- [ ] 9.1 Implement --state flag argument parsing |
||||||
|
- [ ] 9.2 Implement stdin input handling from /dev/stdin |
||||||
|
- [ ] 9.3 Set up correct CLI usage/help message |
||||||
|
- [ ] 9.4 Implement flag order validation |
||||||
|
|
||||||
|
## 10. Integration & Testing |
||||||
|
|
||||||
|
- [ ] 10.1 Wire together stdin -> state files -> merging -> validation -> pacman sync -> orphan cleanup -> output |
||||||
|
- [ ] 10.2 Test empty state error output and exit code 1 |
||||||
|
- [ ] 10.3 Test single state file parsing |
||||||
|
- [ ] 10.4 Test multiple state file merging |
||||||
|
- [ ] 10.5 Test stdin input parsing |
||||||
|
- [ ] 10.6 Test explicit marking before sync |
||||||
|
- [ ] 10.7 Test pacman command execution with real packages |
||||||
|
- [ ] 10.8 Test orphan cleanup removes unneeded packages |
||||||
|
- [ ] 10.9 Test AUR fallback with makepkg for AUR package |
||||||
|
- [ ] 10.10 Test error handling for missing packages |
||||||
|
- [ ] 10.11 Generate final binary |
||||||
Loading…
Reference in new issue