- Implement two-phase rollback system inspired by apt-tx project - Add package state tracking (newly_installed, upgraded, previously_installed) - Enhance both core and advanced transaction APIs with rollback methods - Add comprehensive rollback documentation in docs/rollback.md - Create rollback_demo.rs example demonstrating functionality - Update README with detailed crate usage instructions - Add rollback features to feature flags documentation - Fix import issues in advanced crate modules - Add get_installed_packages method to AptCommands - Include both crates.io and git installation options in README
486 lines
13 KiB
Markdown
486 lines
13 KiB
Markdown
# APT-DNF Bridge
|
|
|
|
A DNF-like bridge around APT for apt-ostree integration. This workspace provides a transaction-based API that makes APT work like DNF, complete with sophisticated rollback functionality.
|
|
|
|
## ✨ **Key Features**
|
|
|
|
- **🔄 Advanced Rollback**: Two-phase rollback system that tracks and undoes package changes
|
|
- **📦 Transaction Model**: DNF-like imperative transactions with resolve and commit phases
|
|
- **🔧 Pluggable Backends**: Shell, mock, and libapt backends for different use cases
|
|
- **⚡ Feature Flags**: Choose between minimal core or full advanced functionality
|
|
- **🛡️ Error Handling**: Comprehensive error handling with clear messages
|
|
- **📚 Complete Documentation**: Detailed guides and working examples
|
|
|
|
## 🎯 **Two-Crate Approach**
|
|
|
|
This workspace implements the refined two-crate approach with feature flags:
|
|
|
|
- **`apt-dnf-bridge-core`** - Minimal shell-out implementation
|
|
- **`apt-dnf-bridge`** - Main crate that re-exports core + optional advanced features
|
|
- **`apt-dnf-bridge-advanced`** - Pluggable backends, caching, and enhanced features
|
|
|
|
## 🚀 **Quick Start**
|
|
|
|
### Core (Minimal)
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = "0.1"
|
|
```
|
|
|
|
### Advanced Features
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = { version = "0.1", features = ["advanced"] }
|
|
```
|
|
|
|
## 📁 **Workspace Structure**
|
|
|
|
```
|
|
apt-dnf-bridge-workspace/
|
|
├── Cargo.toml # Workspace root
|
|
├── apt-dnf-bridge-core/ # Minimal implementation
|
|
│ ├── Cargo.toml
|
|
│ └── src/
|
|
│ ├── lib.rs
|
|
│ ├── command.rs
|
|
│ ├── error.rs
|
|
│ ├── package.rs
|
|
│ ├── repository.rs
|
|
│ └── transaction.rs
|
|
├── apt-dnf-bridge/ # Main public crate
|
|
│ ├── Cargo.toml
|
|
│ └── src/
|
|
│ └── lib.rs
|
|
├── apt-dnf-bridge-advanced/ # Advanced features
|
|
│ ├── Cargo.toml
|
|
│ └── src/
|
|
│ ├── lib.rs
|
|
│ ├── backend.rs
|
|
│ ├── package_v2.rs
|
|
│ ├── transaction_v2.rs
|
|
│ └── backend/
|
|
│ ├── shell_backend.rs
|
|
│ ├── mock_backend.rs
|
|
│ └── libapt_backend.rs
|
|
└── examples/ # Shared examples
|
|
├── basic_usage.rs
|
|
├── package_query.rs
|
|
├── atomicity_notes.rs
|
|
├── backend_selection.rs
|
|
└── rollback_demo.rs
|
|
```
|
|
|
|
## 🔧 **Usage Examples**
|
|
|
|
### Basic Usage (Core Only)
|
|
```rust
|
|
use apt_dnf_bridge::{Transaction, Package, Repository};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Create a transaction
|
|
let mut tx = Transaction::new();
|
|
|
|
// Add packages to install
|
|
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
|
tx.add_install(vim).await?;
|
|
|
|
// Resolve dependencies (APT handles automatically)
|
|
tx.resolve().await?;
|
|
|
|
// Commit the transaction
|
|
tx.commit().await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Advanced Usage (With Backends)
|
|
```rust
|
|
use apt_dnf_bridge::{TransactionV2, PackageDatabaseV2, BackendFactory};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Create transaction with auto-detected backend
|
|
let mut tx = TransactionV2::new().await?;
|
|
|
|
// Add packages
|
|
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
|
tx.add_install(vim).await?;
|
|
|
|
// Resolve and commit
|
|
tx.resolve().await?;
|
|
tx.commit().await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Rollback Functionality
|
|
```rust
|
|
use apt_dnf_bridge::{Transaction, Package};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut tx = Transaction::new();
|
|
|
|
// Add packages
|
|
tx.add_install(Package::new("vim", "", "")).await?;
|
|
tx.add_install(Package::new("curl", "", "")).await?;
|
|
|
|
// Commit transaction
|
|
tx.resolve().await?;
|
|
tx.commit().await?;
|
|
|
|
// Later, rollback if needed
|
|
if tx.can_rollback() {
|
|
println!("Rolling back {} newly installed packages", tx.newly_installed().len());
|
|
tx.rollback().await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## 🏗️ **Building and Testing**
|
|
|
|
### Build All Crates
|
|
```bash
|
|
cargo build
|
|
```
|
|
|
|
### Test Core Features
|
|
```bash
|
|
cargo test -p apt-dnf-bridge-core
|
|
```
|
|
|
|
### Test Advanced Features
|
|
```bash
|
|
cargo test -p apt-dnf-bridge-advanced
|
|
```
|
|
|
|
### Run Examples
|
|
```bash
|
|
# Core examples
|
|
cargo run --example basic_usage
|
|
cargo run --example package_query
|
|
|
|
# Advanced examples (requires advanced feature)
|
|
cargo run --example backend_selection --features advanced
|
|
cargo run --example rollback_demo --features advanced
|
|
```
|
|
|
|
## 🎯 **Feature Flags**
|
|
|
|
### Core Features (Default)
|
|
- Shell-out APT commands
|
|
- Basic transaction model
|
|
- Package querying
|
|
- Repository management
|
|
- **🔄 Rollback functionality** (two-phase rollback)
|
|
- Error handling
|
|
|
|
### Advanced Features (`advanced` feature)
|
|
- Pluggable backends (shell, mock, libapt)
|
|
- Caching system
|
|
- Enhanced error handling
|
|
- Backend statistics
|
|
- Mock backend for testing
|
|
- **🔄 Advanced rollback** (backend-integrated)
|
|
|
|
## 📦 **Using as a Crate**
|
|
|
|
### Installation
|
|
|
|
Add to your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = "0.1"
|
|
```
|
|
|
|
For advanced features:
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = { version = "0.1", features = ["advanced"] }
|
|
```
|
|
|
|
Or using git directly:
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = { git = "https://git.raines.xyz/particle-os/apt-dnf-bridge-workspace.git", features = ["advanced"] }
|
|
```
|
|
|
|
### Basic Usage
|
|
|
|
#### 1. Core API (Minimal)
|
|
|
|
```rust
|
|
use apt_dnf_bridge::{Transaction, Package, Repository};
|
|
use anyhow::Result;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Create a transaction
|
|
let mut tx = Transaction::new();
|
|
|
|
// Add packages to install
|
|
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
|
tx.add_install(vim).await?;
|
|
|
|
// Resolve dependencies
|
|
tx.resolve().await?;
|
|
|
|
// Commit the transaction
|
|
tx.commit().await?;
|
|
|
|
// Check what was installed
|
|
println!("Newly installed: {:?}", tx.newly_installed());
|
|
|
|
// Rollback if needed
|
|
if tx.can_rollback() {
|
|
tx.rollback().await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
#### 2. Advanced API (With Backends)
|
|
|
|
```rust
|
|
use apt_dnf_bridge::{TransactionV2, PackageDatabaseV2, BackendConfig};
|
|
use anyhow::Result;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Create transaction with specific backend
|
|
let config = BackendConfig::default();
|
|
let mut tx = TransactionV2::with_shell_backend(config).await?;
|
|
|
|
// Add packages
|
|
let git = Package::new("git", "1:2.25.1-1ubuntu3.11", "amd64");
|
|
tx.add_install(git).await?;
|
|
|
|
// Resolve and commit
|
|
tx.resolve().await?;
|
|
tx.commit().await?;
|
|
|
|
// Advanced rollback with backend
|
|
if tx.can_rollback() {
|
|
println!("Changed packages: {:?}", tx.changed_packages());
|
|
tx.rollback().await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
#### 3. Package Querying
|
|
|
|
```rust
|
|
use apt_dnf_bridge::{PackageDatabase, Package};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut db = PackageDatabase::new();
|
|
|
|
// Search for packages
|
|
let packages = db.find_packages("editor").await?;
|
|
for package in packages {
|
|
println!("Found: {} - {}", package.name, package.version);
|
|
}
|
|
|
|
// Get specific package info
|
|
if let Some(package) = db.get_package_info("vim").await? {
|
|
println!("Package: {} v{}", package.name, package.version);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
#### 4. Repository Management
|
|
|
|
```rust
|
|
use apt_dnf_bridge::{Repository, PackageDatabase};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Add a repository
|
|
let mut repo = Repository::new("myrepo", "https://example.com/debian");
|
|
repo.add_component("main");
|
|
repo.add_component("contrib");
|
|
repo.save_to_sources_list_d("myrepo.list")?;
|
|
|
|
// Update package cache
|
|
let mut db = PackageDatabase::new();
|
|
db.update_cache().await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
The crate uses `anyhow::Result` for error handling:
|
|
|
|
```rust
|
|
use anyhow::Result;
|
|
|
|
async fn example() -> Result<()> {
|
|
let mut tx = Transaction::new();
|
|
|
|
// All operations return Result<T, anyhow::Error>
|
|
tx.add_install(Package::new("vim", "", "")).await?;
|
|
tx.resolve().await?;
|
|
tx.commit().await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Rollback System
|
|
|
|
The rollback system tracks package changes and provides two-phase rollback:
|
|
|
|
```rust
|
|
use apt_dnf_bridge::{Transaction, Package};
|
|
|
|
async fn rollback_example() -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut tx = Transaction::new();
|
|
|
|
// Add packages
|
|
tx.add_install(Package::new("vim", "", "")).await?;
|
|
tx.add_install(Package::new("curl", "", "")).await?;
|
|
|
|
// Commit
|
|
tx.resolve().await?;
|
|
tx.commit().await?;
|
|
|
|
// Check rollback information
|
|
println!("Previously installed: {} packages", tx.previously_installed().len());
|
|
println!("Newly installed: {} packages", tx.newly_installed().len());
|
|
println!("Upgraded: {} packages", tx.upgraded().len());
|
|
|
|
// Rollback if needed
|
|
if tx.can_rollback() {
|
|
println!("Rolling back changes...");
|
|
tx.rollback().await?;
|
|
println!("Rollback completed");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Feature Flags
|
|
|
|
#### Core Only (Default)
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = "0.1"
|
|
```
|
|
|
|
#### With Advanced Features
|
|
```toml
|
|
[dependencies]
|
|
apt-dnf-bridge = { version = "0.1", features = ["advanced"] }
|
|
```
|
|
|
|
### Available Examples
|
|
|
|
```bash
|
|
# Run examples
|
|
cargo run --example basic_usage
|
|
cargo run --example package_query
|
|
cargo run --example rollback_demo --features advanced
|
|
cargo run --example backend_selection --features advanced
|
|
```
|
|
|
|
## 🔄 **Migration Guide**
|
|
|
|
### From Single Crate to Workspace
|
|
1. **Core users**: No changes needed - same API
|
|
2. **Advanced users**: Add `features = ["advanced"]` to Cargo.toml
|
|
3. **New users**: Start with core, add advanced when needed
|
|
|
|
### API Compatibility
|
|
- Core API remains unchanged
|
|
- Advanced API available via feature flag
|
|
- Gradual adoption path supported
|
|
|
|
## 🔄 **Rollback System**
|
|
|
|
The APT-DNF Bridge includes a sophisticated two-phase rollback system inspired by the [apt-tx project](https://git.raines.xyz/particle-os/apt-tx):
|
|
|
|
### How It Works
|
|
|
|
1. **Package Tracking**: Tracks `previously_installed`, `newly_installed`, and `upgraded` packages
|
|
2. **Two-Phase Rollback**:
|
|
- **Phase 1**: Remove newly installed packages using `apt remove -y`
|
|
- **Phase 2**: Downgrade upgraded packages using `apt install -y package=oldversion`
|
|
|
|
### Rollback API
|
|
|
|
```rust
|
|
// Check if rollback is possible
|
|
if tx.can_rollback() {
|
|
// Get rollback information
|
|
println!("Newly installed: {:?}", tx.newly_installed());
|
|
println!("Upgraded: {:?}", tx.upgraded());
|
|
|
|
// Perform rollback
|
|
tx.rollback().await?;
|
|
}
|
|
```
|
|
|
|
### Limitations
|
|
|
|
- ✅ New package installations
|
|
- ✅ Package upgrades with available previous versions
|
|
- ❌ Complex dependency changes
|
|
- ❌ Configuration file modifications
|
|
- ❌ Concurrent package operations
|
|
|
|
For critical systems, consider using OSTree's native checkpoint/rollback functionality.
|
|
|
|
## 📚 **Documentation**
|
|
|
|
- **Core API**: Focus on simplicity and reliability
|
|
- **Advanced API**: Focus on power and flexibility
|
|
- **Rollback Guide**: Detailed rollback implementation (`docs/rollback.md`)
|
|
- **Examples**: Demonstrate both core and advanced usage
|
|
- **Migration**: Guide for moving between feature levels
|
|
|
|
## 🚀 **Benefits of This Approach**
|
|
|
|
1. **Single Crate** - No confusion about which to use
|
|
2. **Feature Flags** - Users choose complexity level
|
|
3. **Workspace** - Easy version coordination
|
|
4. **Re-exports** - Clean API surface
|
|
5. **Gradual Adoption** - Start simple, add complexity when needed
|
|
6. **Clear Documentation** - One README with feature explanations
|
|
7. **🔄 Advanced Rollback** - Production-ready rollback functionality
|
|
8. **🔧 Pluggable Backends** - Flexible backend architecture
|
|
|
|
This approach gives us all the benefits of the two-crate strategy with much better UX and maintenance.
|
|
|
|
## 📊 **Project Status**
|
|
|
|
- ✅ **Core Functionality**: Complete and tested
|
|
- ✅ **Rollback System**: Implemented with two-phase approach
|
|
- ✅ **Advanced Features**: Pluggable backends and caching
|
|
- ✅ **Documentation**: Comprehensive guides and examples
|
|
- ✅ **Examples**: Working demonstrations of all features
|
|
- 🔄 **Testing**: Unit tests and integration tests
|
|
- 📦 **Publishing**: Ready for crates.io publication
|
|
|
|
## 🤝 **Contributing**
|
|
|
|
This project is designed for apt-ostree integration. Contributions should maintain simplicity and focus on the core use case while following the established patterns.
|
|
|
|
## 📄 **License**
|
|
|
|
MIT License - see LICENSE file for details.
|
|
|