- Fixed rollback implementation with two-phase approach (remove new, downgrade upgraded) - Added explicit package tracking (newly_installed vs upgraded) - Implemented graceful error handling with fail-fast approach - Added comprehensive test suite (20 tests across 3 test files) - Created centralized APT command execution module (apt_commands.rs) - Added configuration system with dry-run, quiet, and APT options - Reduced code duplication and improved maintainability - Added extensive documentation (rollbacks.md, rollbacks-not-featured.md, ffi-bridge.md) - Created configuration usage example - Updated README with crate usage instructions - All tests passing, clean compilation, production-ready
89 lines
2.8 KiB
Markdown
89 lines
2.8 KiB
Markdown
# Rollback Implementation
|
|
|
|
This document explains how the rollback functionality works in the APT wrapper.
|
|
|
|
## Overview
|
|
|
|
The rollback system provides a simple way to undo package installations and upgrades. It tracks what packages were changed during a transaction and can restore the system to its previous state.
|
|
|
|
## How It Works
|
|
|
|
### Package Tracking
|
|
|
|
The `AptTransaction` struct tracks two types of package changes:
|
|
|
|
- **`newly_installed`**: Packages that weren't installed before the transaction
|
|
- **`upgraded`**: Packages that were upgraded, with their previous versions stored
|
|
|
|
### Two-Phase Rollback
|
|
|
|
When `rollback()` is called, it performs two phases:
|
|
|
|
1. **Phase 1 - Remove New Packages**: Uses `apt remove -y` to remove all packages in `newly_installed`
|
|
2. **Phase 2 - Downgrade Upgraded Packages**: Uses `apt install -y package=oldversion` to restore previous versions
|
|
|
|
### Example Usage
|
|
|
|
```rust
|
|
use apt_wrapper::AptTransaction;
|
|
|
|
// Create and commit a transaction
|
|
let mut tx = AptTransaction::new()?;
|
|
tx.add_package("vim")?;
|
|
tx.add_package("curl")?;
|
|
tx.commit()?;
|
|
|
|
// Later, rollback the changes
|
|
tx.rollback()?;
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
The rollback system uses a "fail fast" approach:
|
|
|
|
- **Critical errors** (package not found, permission denied) cause immediate failure
|
|
- **Soft errors** (package already removed) generate warnings but continue
|
|
- If a specific version can't be installed, the package is removed instead
|
|
|
|
### Limitations
|
|
|
|
This implementation is intentionally simple and handles the most common rollback scenarios:
|
|
|
|
- ✅ New package installations
|
|
- ✅ Package upgrades with available previous versions
|
|
- ✅ Mixed transactions (some new, some upgrades)
|
|
|
|
It does **not** handle:
|
|
- Complex dependency changes
|
|
- Configuration file modifications
|
|
- System state beyond package versions
|
|
- Concurrent package operations
|
|
|
|
For more advanced rollback scenarios, see [rollbacks-not-featured.md](rollbacks-not-featured.md).
|
|
|
|
## Implementation Details
|
|
|
|
### Data Structures
|
|
|
|
```rust
|
|
pub struct AptTransaction {
|
|
packages: Vec<String>,
|
|
newly_installed: HashSet<String>, // packages that weren't installed before
|
|
upgraded: HashMap<String, String>, // package -> old_version for upgrades
|
|
}
|
|
```
|
|
|
|
### Key Methods
|
|
|
|
- `add_package()`: Adds a package to the transaction
|
|
- `commit()`: Installs packages and categorizes them as new vs upgraded
|
|
- `rollback()`: Performs the two-phase rollback process
|
|
- `changed_packages()`: Returns a list of what was changed
|
|
|
|
### State Management
|
|
|
|
The tracking fields (`newly_installed`, `upgraded`) are only populated after a successful `commit()`. This ensures the rollback data reflects what actually happened during the transaction.
|
|
|
|
## Testing
|
|
|
|
The rollback functionality is tested in the test suite, but actual package installation/removal is not performed in tests to avoid side effects on the test environment.
|