Some checks failed
Comprehensive CI/CD Pipeline / Build and Test (push) Failing after 4m45s
Comprehensive CI/CD Pipeline / Security Audit (push) Failing after 7s
Comprehensive CI/CD Pipeline / Package Validation (push) Successful in 2m21s
Comprehensive CI/CD Pipeline / Status Report (push) Has been skipped
- Fix trailing spaces and blank lines in Forgejo workflows - Update system requirements from Ubuntu Jammy/Bookworm to Debian 13+ (Trixie) - Update test treefile to use Debian Trixie instead of Ubuntu Jammy - Update documentation to reflect modern system requirements - Fix yamllint errors for CI/CD functionality - Ensure compatibility with modern OSTree and libapt versions
15 KiB
15 KiB
apt-ostree Developer Guide
Table of Contents
- Architecture Overview
- Development Setup
- Code Organization
- API Documentation
- Development Workflow
- Testing and Debugging
- Contributing Guidelines
Architecture Overview
System Components
apt-ostree consists of several key components:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ CLI Client │ │ System Daemon │ │ OSTree Repo │
│ (apt-ostree) │◄──►│ (apt-ostreed) │◄──►│ (/ostree/) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ APT System │ │ D-Bus Layer │ │ File System │
│ (Package Mgmt)│ │ (IPC Interface) │ │ (Deployments) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
Component Responsibilities
CLI Client (apt-ostree)
- Command parsing: Uses
clapfor argument parsing - User interface: Provides user-friendly command interface
- Command dispatch: Routes commands to appropriate handlers
- Output formatting: Formats results for user consumption
System Daemon (apt-ostreed)
- Background processing: Handles long-running operations
- D-Bus interface: Provides IPC for system operations
- Transaction management: Manages atomic system changes
- Security: Implements Polkit authentication
OSTree Integration
- Repository management: Manages OSTree repositories
- Deployment handling: Handles system deployments
- Atomic operations: Ensures system consistency
- Rollback support: Provides system recovery
APT Integration
- Package resolution: Resolves package dependencies
- Repository management: Manages APT repositories
- Transaction handling: Handles package transactions
- Conflict resolution: Resolves package conflicts
Development Setup
Prerequisites
- Rust toolchain: Rust 1.75+ with Cargo
- System dependencies: See installation guide
- Development tools: Git, make, pkg-config
- Documentation tools: rustdoc, cargo-doc
Environment Setup
# Clone repository
git clone https://github.com/robojerk/apt-ostree.git
cd apt-ostree
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Install system dependencies
sudo apt-get install build-essential pkg-config \
libostree-dev libapt-pkg-dev libpolkit-gobject-1-dev \
libdbus-1-dev libsystemd-dev
# Install development tools
cargo install cargo-outdated cargo-audit cargo-tarpaulin
Build Configuration
# Development build with all features
cargo build --features development,dev-full
# Release build
cargo build --release
# Documentation
cargo doc --open --features development
# Run tests
cargo test --features development
Code Organization
Directory Structure
src/
├── main.rs # CLI entry point
├── cli.rs # Command-line interface definitions
├── commands/ # Command implementations
│ ├── mod.rs # Command registry
│ ├── package.rs # Package management commands
│ ├── system.rs # System management commands
│ ├── testutils.rs # Development utilities
│ ├── shlib_backend.rs # Shared library backend
│ └── internals.rs # Internal diagnostics
├── lib/ # Core library code
│ ├── mod.rs # Library entry point
│ ├── apt.rs # APT integration
│ ├── ostree.rs # OSTree integration
│ ├── security.rs # Security and authentication
│ ├── system.rs # System operations
│ ├── transaction.rs # Transaction management
│ └── logging.rs # Logging and metrics
├── daemon/ # Daemon implementation
│ ├── main.rs # Daemon entry point
│ ├── dbus.rs # D-Bus interface
│ ├── polkit.rs # Polkit integration
│ └── systemd.rs # systemd integration
└── tests/ # Test suite
├── integration_tests.rs
└── common/
Key Modules
Command System
The command system uses a trait-based approach for extensibility:
pub trait Command {
fn execute(&self, args: &[String]) -> Result<(), Box<dyn std::error::Error>>;
fn help(&self) -> String;
fn usage(&self) -> String;
}
pub struct CommandRegistry {
commands: HashMap<String, Box<dyn Command>>,
}
Library Interface
The library provides a clean API for external consumers:
pub struct AptOstree {
apt_manager: AptManager,
ostree_manager: OstreeManager,
security_manager: SecurityManager,
}
impl AptOstree {
pub fn new() -> Result<Self, Error> { /* ... */ }
pub fn install_packages(&self, packages: &[String]) -> Result<(), Error> { /* ... */ }
pub fn remove_packages(&self, packages: &[String]) -> Result<(), Error> { /* ... */ }
}
API Documentation
Core Types
Package Management
pub struct Package {
pub name: String,
pub version: String,
pub architecture: String,
pub dependencies: Vec<String>,
pub conflicts: Vec<String>,
}
pub struct PackageTransaction {
pub packages: Vec<Package>,
pub operation: TransactionOperation,
pub status: TransactionStatus,
}
OSTree Integration
pub struct Deployment {
pub id: String,
pub branch: String,
pub commit: String,
pub timestamp: DateTime<Utc>,
pub packages: Vec<String>,
}
pub struct OSTreeRepository {
pub path: PathBuf,
pub mode: RepositoryMode,
pub remotes: HashMap<String, Remote>,
}
Security
pub struct SecurityContext {
pub user: String,
pub groups: Vec<String>,
pub permissions: Permissions,
}
pub struct AuthenticationResult {
pub authenticated: bool,
pub user: Option<String>,
pub permissions: Permissions,
}
Public API
High-Level Operations
impl AptOstree {
/// Install packages atomically
pub fn install_packages(&self, packages: &[String]) -> Result<(), Error>
/// Remove packages atomically
pub fn remove_packages(&self, packages: &[String]) -> Result<(), Error>
/// Upgrade all packages
pub fn upgrade_system(&self) -> Result<(), Error>
/// Rollback to previous deployment
pub fn rollback(&self) -> Result<(), Error>
}
Transaction Management
impl TransactionManager {
/// Start a new transaction
pub fn start_transaction(&mut self, operation: TransactionOperation) -> Result<TransactionId, Error>
/// Add packages to transaction
pub fn add_packages(&mut self, transaction_id: TransactionId, packages: &[String]) -> Result<(), Error>
/// Commit transaction
pub fn commit_transaction(&mut self, transaction_id: TransactionId) -> Result<(), Error>
/// Rollback transaction
pub fn rollback_transaction(&mut self, transaction_id: TransactionId) -> Result<(), Error>
}
System Operations
impl SystemManager {
/// Get system status
pub fn get_system_status(&self) -> SystemStatus
/// Manage kernel arguments
pub fn set_kernel_args(&self, args: &[String]) -> Result<(), Error>
/// Regenerate initramfs
pub fn regenerate_initramfs(&self) -> Result<(), Error>
}
Development Workflow
Adding New Commands
1. Define Command Structure
// src/cli.rs
#[derive(Subcommand)]
pub enum Commands {
// ... existing commands ...
NewCommand(NewCommandArgs),
}
#[derive(Args)]
pub struct NewCommandArgs {
#[arg(long)]
option: Option<String>,
#[arg(value_name = "TARGET")]
target: String,
}
2. Implement Command Logic
// src/commands/new_command.rs
pub struct NewCommand;
impl Command for NewCommand {
fn execute(&self, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
// Implementation here
Ok(())
}
fn help(&self) -> String {
"Help text for new command".to_string()
}
fn usage(&self) -> String {
"new-command [OPTIONS] TARGET".to_string()
}
}
3. Register Command
// src/commands/mod.rs
pub mod new_command;
// In CommandRegistry::new()
self.commands.insert("new-command".to_string(), Box::new(NewCommand));
4. Add to Main Dispatch
// src/main.rs
match cli.command {
Commands::NewCommand(args) => {
// Handle new command
}
}
Adding New Features
1. Library Implementation
// src/lib/new_feature.rs
pub struct NewFeature {
// Feature implementation
}
impl NewFeature {
pub fn new() -> Self {
// Constructor
}
pub fn execute(&self) -> Result<(), Error> {
// Feature logic
}
}
2. Integration
// src/lib/mod.rs
pub mod new_feature;
// In main library struct
pub struct AptOstree {
// ... existing fields ...
new_feature: NewFeature,
}
3. Testing
// tests/new_feature_tests.rs
#[test]
fn test_new_feature() {
let feature = NewFeature::new();
assert!(feature.execute().is_ok());
}
Testing and Debugging
Unit Testing
# Run all tests
cargo test
# Run specific test module
cargo test commands::package
# Run tests with output
cargo test -- --nocapture
# Run tests with specific feature
cargo test --features development
Integration Testing
# Run integration tests
cargo test --test integration_tests
# Run specific integration test
cargo test --test integration_tests test_package_installation
Development Commands
# Run diagnostics
cargo run --features development -- internals diagnostics
# Validate system state
cargo run --features development -- internals validate-state
# Dump debug information
cargo run --features development -- internals debug-dump
Debugging Tools
Logging
use tracing::{info, warn, error, debug};
// Set log level
std::env::set_var("RUST_LOG", "debug");
// Use in code
debug!("Debug information");
info!("Information message");
warn!("Warning message");
error!("Error message");
Profiling
# Install profiling tools
cargo install flamegraph
# Generate flamegraph
cargo flamegraph --bin apt-ostree -- install nginx
# Memory profiling
cargo install cargo-valgrind
cargo valgrind test
Code Quality Tools
Clippy
# Run Clippy
cargo clippy
# Run with specific rules
cargo clippy -- -D warnings -A clippy::too_many_arguments
Formatting
# Check formatting
cargo fmt -- --check
# Format code
cargo fmt
Security Audit
# Check for vulnerabilities
cargo audit
# Update dependencies
cargo update
Contributing Guidelines
Code Style
- Rust conventions: Follow Rust style guide and idioms
- Documentation: Document all public APIs with doc comments
- Error handling: Use proper error types and Result handling
- Testing: Include tests for new functionality
- Logging: Use appropriate log levels for debugging
Pull Request Process
- Fork repository: Create your own fork
- Create feature branch:
git checkout -b feature/new-feature - Implement changes: Follow coding guidelines
- Add tests: Include unit and integration tests
- Update documentation: Update relevant documentation
- Submit PR: Create pull request with clear description
Commit Guidelines
type(scope): description
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore Scope: cli, daemon, lib, commands, etc.
Review Process
- Code review: All changes require review
- Testing: Ensure all tests pass
- Documentation: Verify documentation is updated
- Integration: Test integration with existing features
Issue Reporting
When reporting issues, include:
- Environment: OS, version, dependencies
- Steps to reproduce: Clear reproduction steps
- Expected behavior: What should happen
- Actual behavior: What actually happens
- Logs: Relevant error logs and output
Advanced Development
Custom Builds
# Build with specific features
cargo build --features development,dev-full
# Build for specific target
cargo build --target x86_64-unknown-linux-gnu
# Cross-compilation
cargo build --target aarch64-unknown-linux-gnu
Performance Optimization
// Use appropriate data structures
use std::collections::HashMap; // O(1) lookup
use std::collections::BTreeMap; // Ordered, O(log n) lookup
// Avoid unnecessary allocations
let result = if condition { "value" } else { "default" };
// Instead of
let result = if condition { "value".to_string() } else { "default".to_string() };
// Use iterators efficiently
let sum: u64 = items.iter().map(|x| x.value).sum();
Memory Management
// Use Arc for shared ownership
use std::sync::Arc;
let shared_data = Arc::new(SharedData::new());
let clone1 = Arc::clone(&shared_data);
let clone2 = Arc::clone(&shared_data);
// Use Box for trait objects
let commands: Vec<Box<dyn Command>> = vec![
Box::new(PackageCommand),
Box::new(SystemCommand),
];
Async Programming
use tokio::runtime::Runtime;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = async_operation().await?;
Ok(())
}
async fn async_operation() -> Result<String, Error> {
// Async implementation
Ok("result".to_string())
}
Troubleshooting Development Issues
Common Problems
Build Failures
# Clean and rebuild
cargo clean
cargo build
# Check Rust version
rustc --version
cargo --version
# Update Rust toolchain
rustup update
Dependency Issues
# Check dependency tree
cargo tree
# Update dependencies
cargo update
# Check for conflicts
cargo check
Test Failures
# Run specific failing test
cargo test test_name -- --nocapture
# Check test environment
cargo test --features development
# Debug test setup
RUST_LOG=debug cargo test
Getting Help
- Documentation: Check inline documentation and rustdoc
- Issues: Search existing GitHub issues
- Discussions: Use GitHub Discussions for questions
- Community: Join project community channels
This guide covers development aspects of apt-ostree. For user documentation, refer to the User Guide.