4 changed files with 139 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||||
|
schema: spec-driven |
||||||
|
created: 2026-04-15 |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
## Implementation |
||||||
|
|
||||||
|
### Log File Location |
||||||
|
- Path: `/var/log/declpac.log` |
||||||
|
- Single merged log file (stdout + stderr intermingled in order of arrival) |
||||||
|
|
||||||
|
### State-Modifying Functions (need logging) |
||||||
|
1. `SyncPackages()` - `pacman -S --needed <packages>` |
||||||
|
2. `InstallAUR()` - `git clone` + `makepkg -si --noconfirm` |
||||||
|
3. `MarkAllAsDeps()` - `pacman -D --asdeps` |
||||||
|
4. `MarkAsExplicit()` - `pacman -D --asexplicit <packages>` |
||||||
|
5. `CleanupOrphans()` - `pacman -Rns` |
||||||
|
|
||||||
|
### Functions to Skip (read-only) |
||||||
|
- `DryRun()` - queries only |
||||||
|
- `getInstalledCount()` - pacman -Qq |
||||||
|
|
||||||
|
### Execution Patterns |
||||||
|
|
||||||
|
| Pattern | Functions | How | |
||||||
|
|---------|------------|-----| |
||||||
|
| Streaming | MarkAllAsDeps, MarkAsExplicit, InstallAUR | `io.MultiWriter` to tee to both terminal and log | |
||||||
|
| Captured | SyncPackages, CleanupOrphans | capture with `CombinedOutput()`, write to log, write to terminal | |
||||||
|
|
||||||
|
### Error Handling |
||||||
|
- Write error to log BEFORE returning from function |
||||||
|
- Print error to stderr so user sees it |
||||||
|
|
||||||
|
### Dependencies |
||||||
|
- Add to imports: `io`, `os`, `path/filepath` |
||||||
|
|
||||||
|
### Structure |
||||||
|
|
||||||
|
```go |
||||||
|
// pkg/state/state.go |
||||||
|
var logFile *os.File |
||||||
|
|
||||||
|
func OpenLog() error { |
||||||
|
logPath := filepath.Join("/var/log", "declpac.log") |
||||||
|
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
logFile = f |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func GetLogWriter() io.Writer { |
||||||
|
return logFile |
||||||
|
} |
||||||
|
|
||||||
|
func Close() error { |
||||||
|
if logFile == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return logFile.Close() |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### Flow in Sync() or main entrypoint |
||||||
|
1. Call `OpenLog()` at program start, defer close |
||||||
|
2. Each state-modifying function uses `state.GetLogWriter()` via MultiWriter |
||||||
|
|
||||||
|
### Wire into main.go |
||||||
|
- Open log at start of `run()` |
||||||
|
- Pass log writer to pacman package (via exported function or global) |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
## Why |
||||||
|
|
||||||
|
The tool exits without saving the full pacman output, making debugging difficult |
||||||
|
when operations fail. Users need a persistent log of all pacman operations for |
||||||
|
debugging and audit. |
||||||
|
|
||||||
|
## What Changes |
||||||
|
|
||||||
|
- Add state directory initialization creating `~/.local/state/declpac` if not exists |
||||||
|
- Open/manage a single log file at `$XDG_STATE_HOME/declpac` (e.g., `~/.local/state/declpac/declpac`) |
||||||
|
- Instrument all state-modifying exec calls in `pkg/pacman/pacman.go` to tee or append output to this file |
||||||
|
- Skip debug messages (internal timing logs) |
||||||
|
- Capture and write errors before returning |
||||||
|
|
||||||
|
## Capabilities |
||||||
|
|
||||||
|
### New Capabilities |
||||||
|
- Operation logging: Persist stdout/stderr from all pacman operations |
||||||
|
|
||||||
|
### Modified Capabilities |
||||||
|
- None |
||||||
|
|
||||||
|
## Impact |
||||||
|
|
||||||
|
- `pkg/pacman/pacman.go`: Instrument all state-modifying functions to write to log file |
||||||
|
- New module: May create `pkg/state/state.go` or similar for log file management |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
## Tasks |
||||||
|
|
||||||
|
- [x] 1. Create state module |
||||||
|
|
||||||
|
Create `pkg/state/state.go`: |
||||||
|
- `OpenLog()` - opens `/var/log/declpac.log` in append mode |
||||||
|
- `GetLogWriter()` - returns the raw log file writer (for MultiWriter) |
||||||
|
- `Write(msg []byte)` - writes message with timestamp + dashes separator |
||||||
|
- `Close()` - closes the file |
||||||
|
|
||||||
|
- [x] 2. Wire into main.go |
||||||
|
|
||||||
|
In `cmd/declpac/main.go` `run()`: |
||||||
|
- Call `OpenLog()` at start |
||||||
|
- `defer Close()` log |
||||||
|
|
||||||
|
- [x] 3. Instrument pkg/pacman |
||||||
|
|
||||||
|
Modify `pkg/pacman/pacman.go`: |
||||||
|
|
||||||
|
All state-modifying functions use `state.Write()` instead of `state.GetLogWriter().Write()`: |
||||||
|
|
||||||
|
``` |
||||||
|
// OLD |
||||||
|
state.GetLogWriter().Write(output) |
||||||
|
|
||||||
|
// NEW |
||||||
|
state.Write(output) // auto-prepends timestamp |
||||||
|
``` |
||||||
|
|
||||||
|
**Functions updated:** |
||||||
|
- `SyncPackages()` - write output with timestamp |
||||||
|
- `CleanupOrphans()` - write output with timestamp |
||||||
|
- `MarkAllAsDeps()` - write operation name with timestamp before running |
||||||
|
- `MarkAsExplicit()` - write operation name with timestamp before running |
||||||
|
- `InstallAUR()` - write "Cloning ..." and "Building package..." with timestamps |
||||||
|
- Error handling - `state.Write([]byte("error: ..."))` for all error paths |
||||||
|
|
||||||
|
- [x] 4. Add io import |
||||||
|
|
||||||
|
Add `io` to imports in pacman.go |
||||||
|
|
||||||
|
- [x] 5. Test |
||||||
|
|
||||||
|
Run a sync operation and verify log file created at `/var/log/declpac.log` |
||||||
Loading…
Reference in new issue