diff --git a/local/recipes/system/cub/source/cub-cli/src/main.rs b/local/recipes/system/cub/source/cub-cli/src/main.rs index 4f822a6b4..5ac95a8ec 100644 --- a/local/recipes/system/cub/source/cub-cli/src/main.rs +++ b/local/recipes/system/cub/source/cub-cli/src/main.rs @@ -78,9 +78,12 @@ impl PackageCreator { #[command(version)] #[command(about = "Red Bear OS Package Builder")] struct Cli { - /// Disable the default TUI launcher when no subcommand is provided - #[arg(short = 'T', long = "no-tui", global = true)] - no_tui: bool, + /// Force re-fetch from AUR even if already cached + #[arg(short = 'f', long = "force", global = true)] + force: bool, + /// Skip all interactive prompts (assume yes) + #[arg(long = "noconfirm", global = true, action = clap::ArgAction::SetTrue)] + noconfirm: bool, #[command(subcommand)] command: Option, @@ -90,7 +93,7 @@ struct Cli { enum Commands { /// Install a package from the official repo or BUR Install { package: String }, - /// Search packages in the official repo and cached BUR + /// Search packages in the official repo, cached BUR, and AUR Search { query: String }, /// Show AUR package details Info { package: String }, @@ -104,6 +107,8 @@ enum Commands { Get { package: String }, /// Import an AUR package into ~/.cub/recipes GetAur { package: String }, + /// Print raw PKGBUILD from AUR to stdout + GetPkgbuild { package: String }, /// Inspect an installed package or local RBPKGBUILD Inspect { target: String }, /// Convert an AUR PKGBUILD into an RBPKGBUILD tree @@ -118,8 +123,10 @@ enum Commands { QueryInfo { package: String }, /// List files installed by a package QueryList { package: String }, - /// Remove cub and pkg download caches + /// Clean cub and pkg download caches CleanCache, + /// Clean all caches including build artifacts (~/.cub/tmp/) + CleanAll, } struct AppContext { @@ -181,19 +188,18 @@ fn main() -> Result<(), Box> { let context = AppContext::new(); if let Some(command) = cli.command { - run_command(&context, command)?; - } else if cli.no_tui { - print_help_text()?; + run_command(&context, command, cli.force)?; } else { - launch_tui_or_help()?; + let mut cmd = Cli::command(); + cmd.print_help()?; } Ok(()) } -fn run_command(context: &AppContext, command: Commands) -> Result<(), Box> { +fn run_command(context: &AppContext, command: Commands, force: bool) -> Result<(), Box> { match command { - Commands::Install { package } => install_package(context, &package)?, + Commands::Install { package } => install_package(context, &package, force)?, Commands::Search { query } => search_packages(context, &query)?, Commands::Info { package } => show_aur_info(&package)?, Commands::Sync => sync_sources()?, @@ -201,6 +207,7 @@ fn run_command(context: &AppContext, command: Commands) -> Result<(), Box build_local_dir(context, Path::new(&dir))?, Commands::Get { package } => fetch_bur_recipe(&package)?, Commands::GetAur { package } => get_aur_recipe(&package)?, + Commands::GetPkgbuild { package } => get_pkgbuild_stdout(&package)?, Commands::Inspect { target } => inspect_target(context, &target)?, Commands::ImportAur { target } => import_aur_target(&target)?, Commands::UpdateAll => update_all(context)?, @@ -209,40 +216,12 @@ fn run_command(context: &AppContext, command: Commands) -> Result<(), Box query_local_info(context, &package)?, Commands::QueryList { package } => query_local_files(context, &package)?, Commands::CleanCache => clean_cache()?, + Commands::CleanAll => clean_all()?, } Ok(()) } -fn launch_tui_or_help() -> Result<(), Box> { - #[cfg(feature = "tui")] - { - use std::io::IsTerminal; - - if io::stdin().is_terminal() && io::stdout().is_terminal() { - if let Err(error) = cub_tui::run() { - eprintln!("Failed to launch cub TUI: {error}"); - print_help_text()?; - } - } else { - print_help_text()?; - } - return Ok(()); - } - - #[cfg(not(feature = "tui"))] - { - print_help_text() - } -} - -fn print_help_text() -> Result<(), Box> { - let mut command = Cli::command(); - command.print_help()?; - println!(); - Ok(()) -} - fn rewrite_shortcut_args( args: impl IntoIterator, ) -> Result, Box> { @@ -261,6 +240,34 @@ fn rewrite_shortcut_args( return Ok(collected); }; + let known = ["-S", "-B", "-G", "-R", "-Q", "-Ss", "-Si", "-Sy", "-Syu", "-Sf", + "-Sua", "-Sc", "-Scc", "-Pi", "-Qi", "-Ql", "-Gp", + "--import-aur"]; + + if flag.starts_with('-') && flag.len() > 2 && !flag.starts_with("--") && !known.contains(&flag) { + let prefix_map: &[(&str, &str)] = &[ + ("-S", "install"), ("-B", "build"), ("-G", "get-aur"), ("-R", "remove"), + ("-Q", "query-local"), ("-Ss", "search"), ("-Si", "info"), ("-Sy", "sync"), + ]; + let main = &flag[..2]; + let sub = prefix_map.iter().find(|(p, _)| *p == main).map(|(_, s)| *s).unwrap_or(main); + + let mut expanded = vec![binary.clone()]; + expanded.extend(prefix.iter().cloned()); + expanded.push(OsString::from(sub)); + let mut has_force = false; + let mut has_noconfirm = false; + for ch in flag[2..].chars() { + match ch { + 'f' if !has_force => { expanded.push(OsString::from("--force")); has_force = true; } + 'y' | 'd' if !has_noconfirm => { expanded.push(OsString::from("--noconfirm")); has_noconfirm = true; } + _ => {} + } + } + expanded.extend(tail.iter().skip(1).cloned()); + return Ok(expanded); + } + let rewrite_value = |subcommand: &str, value_name: &str| { if prefix.is_empty() { rewrite_value_command(binary.clone(), tail, subcommand, value_name) @@ -278,6 +285,11 @@ fn rewrite_shortcut_args( match flag { "-S" => rewrite_value("install", "package"), + "-Sf" => { + let mut rewritten = rewrite_value_command(binary.clone(), tail, "install", "package")?; + rewritten.insert(1, OsString::from("--force")); + Ok(rewritten) + } "-Ss" => rewrite_value("search", "query"), "-Si" => rewrite_value("info", "package"), "-Sy" => rewrite_flag("sync"), @@ -292,6 +304,8 @@ fn rewrite_shortcut_args( "--import-aur" => rewrite_value("import-aur", "target"), "-Sua" => rewrite_flag("update-all"), "-Sc" => rewrite_flag("clean-cache"), + "-Scc" => rewrite_flag("clean-all"), + "-Gp" => rewrite_value("get-pkgbuild", "package"), _ => Ok(collected), } } @@ -299,7 +313,7 @@ fn rewrite_shortcut_args( fn leading_global_flag_count(rest: &[OsString]) -> usize { let mut count = 0; while let Some(flag) = rest.get(count).and_then(|value| value.to_str()) { - if matches!(flag, "-T" | "--no-tui") { + if matches!(flag, "-f" | "--force" | "--noconfirm") { count += 1; } else { break; @@ -368,7 +382,7 @@ fn new_pkg_callback() -> Rc> { Rc::new(RefCell::new(callback)) } -fn install_package(context: &AppContext, package: &str) -> Result<(), Box> { +fn install_package(context: &AppContext, package: &str, force: bool) -> Result<(), Box> { if cfg!(not(target_os = "redox")) { println!("Searching AUR for {package}..."); let client = AurClient::new(); @@ -398,6 +412,24 @@ fn install_package(context: &AppContext, package: &str) -> Result<(), Box Result<(), Box> { Ok(()) } +fn clean_all() -> Result<(), Box> { + clean_cache()?; + if let Ok(store) = CubStore::new() { + let tmp = store.root_dir.join("tmp"); + if tmp.exists() { + fs::remove_dir_all(&tmp)?; + println!("Removed ~/.cub/tmp/ build artifacts."); + } + } + Ok(()) +} + +fn get_pkgbuild_stdout(package: &str) -> Result<(), Box> { + validate_git_target(package)?; + let repo_url = aur_repo_url(package); + let clone_dir = cub_temp_dir("aur-view")?; + + let status = Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("--") + .arg(&repo_url) + .arg(&clone_dir) + .status()?; + + if !status.success() { + return Err(Box::new(CubError::BuildFailed(format!( + "failed to clone AUR source from {repo_url}" + )))); + } + + let pkgbuild_path = clone_dir.join("PKGBUILD"); + if !pkgbuild_path.exists() { + return Err(Box::new(CubError::PackageNotFound(format!( + "PKGBUILD not found in {repo_url}" + )))); + } + + let content = fs::read_to_string(&pkgbuild_path)?; + println!("{content}"); + Ok(()) +} + fn apply_library_changes(library: &mut Library) -> Result> { match library.apply() { Ok(changes) => Ok(changes),