- 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
419 lines
14 KiB
Rust
419 lines
14 KiB
Rust
//! Package management commands for apt-ostree
|
|
|
|
use crate::commands::Command;
|
|
use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult};
|
|
use apt_ostree::lib::ostree::OstreeManager;
|
|
|
|
/// Install command - Overlay additional packages
|
|
pub struct InstallCommand;
|
|
|
|
impl InstallCommand {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
}
|
|
|
|
impl Command for InstallCommand {
|
|
fn execute(&self, args: &[String]) -> AptOstreeResult<()> {
|
|
if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) {
|
|
self.show_help();
|
|
return Ok(());
|
|
}
|
|
|
|
if args.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No packages specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
// Parse options
|
|
let mut opt_dry_run = false;
|
|
let mut opt_verbose = false;
|
|
let mut opt_no_deps = false;
|
|
let packages: Vec<String> = args.iter()
|
|
.filter(|arg| !arg.starts_with('-'))
|
|
.cloned()
|
|
.collect();
|
|
|
|
for arg in args {
|
|
match arg.as_str() {
|
|
"--dry-run" | "-n" => opt_dry_run = true,
|
|
"--verbose" | "-v" => opt_verbose = true,
|
|
"--no-deps" => opt_no_deps = true,
|
|
"--help" | "-h" => {
|
|
self.show_help();
|
|
return Ok(());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
if packages.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No packages specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
println!("📦 Install Packages");
|
|
println!("===================");
|
|
println!("Packages to install: {}", packages.join(", "));
|
|
|
|
if opt_dry_run {
|
|
println!("Mode: Dry run (no actual installation)");
|
|
}
|
|
if opt_verbose {
|
|
println!("Mode: Verbose output");
|
|
}
|
|
if opt_no_deps {
|
|
println!("Mode: No dependency installation");
|
|
}
|
|
println!();
|
|
|
|
// Use the real APT manager for installation
|
|
use apt_ostree::lib::apt::AptManager;
|
|
let apt_manager = AptManager::new();
|
|
|
|
// Check if APT is available
|
|
if !apt_manager.check_database_health()? {
|
|
return Err(AptOstreeError::System("APT database is not healthy".to_string()));
|
|
}
|
|
|
|
if opt_dry_run {
|
|
println!("Dry run mode - would install the following packages:");
|
|
for package in &packages {
|
|
if let Ok(Some(pkg_info)) = apt_manager.get_package_info(package) {
|
|
println!(" {} (version: {})", pkg_info.name, pkg_info.version);
|
|
println!(" Description: {}", pkg_info.description);
|
|
if !pkg_info.depends.is_empty() {
|
|
println!(" Dependencies: {}", pkg_info.depends.join(", "));
|
|
}
|
|
println!();
|
|
} else {
|
|
println!(" {} - Package not found", package);
|
|
}
|
|
}
|
|
println!("Dry run completed. No packages were actually installed.");
|
|
return Ok(());
|
|
}
|
|
|
|
// Install packages
|
|
for package in &packages {
|
|
println!("Installing package: {}", package);
|
|
|
|
// Since install_package is async, we'll use a simple approach for now
|
|
// TODO: Make the Command trait async or use a different approach
|
|
match apt_manager.install_package(package) {
|
|
Ok(_) => println!("Successfully installed: {}", package),
|
|
Err(e) => {
|
|
println!("Failed to install {}: {}", package, e);
|
|
return Err(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
println!();
|
|
println!("✅ All packages installed successfully!");
|
|
println!("Note: On OSTree systems, packages are installed as overlays");
|
|
println!(" and will persist across system updates.");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
"install"
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"Overlay additional packages"
|
|
}
|
|
|
|
fn show_help(&self) {
|
|
println!("apt-ostree install - Overlay additional packages");
|
|
println!();
|
|
println!("Usage: apt-ostree install <PACKAGES>... [OPTIONS]");
|
|
println!();
|
|
println!("Arguments:");
|
|
println!(" PACKAGES Package names to install");
|
|
println!();
|
|
println!("Options:");
|
|
println!(" --dry-run, -n Show what would be installed without actually installing");
|
|
println!(" --verbose, -v Show detailed output during installation");
|
|
println!(" --no-deps Skip dependency installation (not recommended)");
|
|
println!(" --help, -h Show this help message");
|
|
println!();
|
|
println!("Examples:");
|
|
println!(" apt-ostree install nginx");
|
|
println!(" apt-ostree install nginx vim htop");
|
|
println!(" apt-ostree install --dry-run nginx");
|
|
println!(" apt-ostree install --verbose nginx");
|
|
println!();
|
|
println!("Note: On OSTree systems, packages are installed as overlays");
|
|
println!(" and will persist across system updates.");
|
|
}
|
|
}
|
|
|
|
/// Uninstall command - Remove overlayed additional packages
|
|
pub struct UninstallCommand;
|
|
|
|
impl UninstallCommand {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
}
|
|
|
|
impl Command for UninstallCommand {
|
|
fn execute(&self, args: &[String]) -> AptOstreeResult<()> {
|
|
if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) {
|
|
self.show_help();
|
|
return Ok(());
|
|
}
|
|
|
|
if args.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No packages specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
let packages: Vec<String> = args.iter()
|
|
.filter(|arg| !arg.starts_with('-'))
|
|
.cloned()
|
|
.collect();
|
|
|
|
if packages.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No packages specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
println!("🗑️ Uninstall Packages");
|
|
println!("=====================");
|
|
println!("Packages to remove: {}", packages.join(", "));
|
|
println!();
|
|
|
|
// Check if we're on an OSTree system
|
|
let ostree_manager = OstreeManager::new();
|
|
let is_ostree_system = ostree_manager.is_available() && ostree_manager.is_ostree_booted();
|
|
|
|
if is_ostree_system {
|
|
println!("OSTree: System is booted from OSTree");
|
|
println!("Mode: Package overlay removal");
|
|
} else {
|
|
println!("OSTree: Traditional package management system");
|
|
println!("Mode: Standard package removal");
|
|
}
|
|
println!();
|
|
|
|
// Use the real APT manager for package removal
|
|
use apt_ostree::lib::apt::AptManager;
|
|
let apt_manager = AptManager::new();
|
|
|
|
// Check if APT is available
|
|
if !apt_manager.check_database_health()? {
|
|
return Err(AptOstreeError::System("APT database is not healthy".to_string()));
|
|
}
|
|
|
|
// Process each package
|
|
let mut success_count = 0;
|
|
let mut failure_count = 0;
|
|
|
|
for package in &packages {
|
|
println!("Removing package: {}", package);
|
|
|
|
// Check if package is installed
|
|
if !apt_manager.is_package_installed(package)? {
|
|
println!(" Warning: Package '{}' is not installed", package);
|
|
continue;
|
|
}
|
|
|
|
// Get package info before removal
|
|
if let Ok(Some(pkg_info)) = apt_manager.get_package_info(package) {
|
|
println!(" Package: {} (version: {})", pkg_info.name, pkg_info.version);
|
|
println!(" Description: {}", pkg_info.description);
|
|
|
|
// Check for reverse dependencies
|
|
// TODO: Implement reverse dependency checking
|
|
println!(" Checking dependencies...");
|
|
}
|
|
|
|
// Remove the package
|
|
match apt_manager.remove_package(package) {
|
|
Ok(_) => {
|
|
println!(" ✅ Successfully removed: {}", package);
|
|
success_count += 1;
|
|
}
|
|
Err(e) => {
|
|
println!(" ❌ Failed to remove {}: {}", package, e);
|
|
failure_count += 1;
|
|
}
|
|
}
|
|
println!();
|
|
}
|
|
|
|
// Summary
|
|
println!("Uninstall Summary:");
|
|
println!(" Successfully removed: {} packages", success_count);
|
|
if failure_count > 0 {
|
|
println!(" Failed to remove: {} packages", failure_count);
|
|
}
|
|
|
|
if is_ostree_system {
|
|
println!();
|
|
println!("Note: On OSTree systems, package overlays have been removed.");
|
|
println!(" The base system remains unchanged.");
|
|
}
|
|
|
|
if failure_count == 0 {
|
|
println!("✅ All packages removed successfully!");
|
|
} else {
|
|
println!("⚠️ Some packages could not be removed. Check the output above.");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
"uninstall"
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"Remove overlayed additional packages"
|
|
}
|
|
|
|
fn show_help(&self) {
|
|
println!("apt-ostree uninstall - Remove overlayed additional packages");
|
|
println!();
|
|
println!("Usage: apt-ostree uninstall <PACKAGES>... [OPTIONS]");
|
|
println!();
|
|
println!("Arguments:");
|
|
println!(" PACKAGES Package names to remove");
|
|
println!();
|
|
println!("Options:");
|
|
println!(" --help, -h Show this help message");
|
|
}
|
|
}
|
|
|
|
/// Search command - Search for packages
|
|
pub struct SearchCommand;
|
|
|
|
impl SearchCommand {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
}
|
|
|
|
impl Command for SearchCommand {
|
|
fn execute(&self, args: &[String]) -> AptOstreeResult<()> {
|
|
if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) {
|
|
self.show_help();
|
|
return Ok(());
|
|
}
|
|
|
|
if args.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No search query specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
// Parse options
|
|
let mut opt_exact = false;
|
|
let mut opt_regex = false;
|
|
let mut opt_verbose = false;
|
|
let mut search_query = String::new();
|
|
|
|
let mut i = 0;
|
|
while i < args.len() {
|
|
match args[i].as_str() {
|
|
"--exact" | "-e" => opt_exact = true,
|
|
"--regex" | "-r" => opt_regex = true,
|
|
"--verbose" | "-v" => opt_verbose = true,
|
|
"--help" | "-h" => {
|
|
self.show_help();
|
|
return Ok(());
|
|
}
|
|
arg if !arg.starts_with('-') => {
|
|
search_query = arg.to_string();
|
|
}
|
|
_ => {}
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
if search_query.is_empty() {
|
|
return Err(AptOstreeError::InvalidArgument(
|
|
"No search query specified. Use --help for usage information.".to_string()
|
|
));
|
|
}
|
|
|
|
println!("🔍 Package Search");
|
|
println!("=================");
|
|
println!("Query: {}", search_query);
|
|
println!("Mode: {}", if opt_exact { "Exact Match" } else if opt_regex { "Regex" } else { "Standard Search" });
|
|
println!();
|
|
|
|
// Use the real APT manager for search
|
|
use apt_ostree::lib::apt::AptManager;
|
|
let apt_manager = AptManager::new();
|
|
|
|
let packages = if opt_exact {
|
|
apt_manager.search_packages_exact(&search_query)?
|
|
} else if opt_regex {
|
|
apt_manager.search_packages_regex(&search_query)?
|
|
} else {
|
|
apt_manager.search_packages(&search_query)?
|
|
};
|
|
|
|
if packages.is_empty() {
|
|
println!("No packages found matching '{}'", search_query);
|
|
return Ok(());
|
|
}
|
|
|
|
println!("Found {} packages:", packages.len());
|
|
println!();
|
|
|
|
for package in packages {
|
|
let status = if package.installed { "✓" } else { " " };
|
|
println!("{} {} - {}", status, package.name, package.description);
|
|
|
|
if opt_verbose {
|
|
println!(" Version: {}", package.version);
|
|
println!(" Section: {}", package.section);
|
|
println!(" Priority: {}", package.priority);
|
|
if !package.depends.is_empty() {
|
|
println!(" Dependencies: {}", package.depends.join(", "));
|
|
}
|
|
println!();
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
"search"
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"Search for packages"
|
|
}
|
|
|
|
fn show_help(&self) {
|
|
println!("apt-ostree search - Search for packages");
|
|
println!();
|
|
println!("Usage: apt-ostree search <QUERY> [OPTIONS]");
|
|
println!();
|
|
println!("Arguments:");
|
|
println!(" QUERY Search query (package name or description)");
|
|
println!();
|
|
println!("Options:");
|
|
println!(" --exact, -e Exact package name match");
|
|
println!(" --regex, -r Regular expression search");
|
|
println!(" --verbose, -v Show detailed package information");
|
|
println!(" --help, -h Show this help message");
|
|
println!();
|
|
println!("Examples:");
|
|
println!(" apt-ostree search nginx");
|
|
println!(" apt-ostree search --exact nginx");
|
|
println!(" apt-ostree search --regex '^nginx.*'");
|
|
println!(" apt-ostree search --verbose nginx");
|
|
}
|
|
}
|