From 5ad5608a1b6ea7660d164a3366a6c8b098132065 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sat, 9 Aug 2025 23:52:59 -0700 Subject: [PATCH 01/10] Updated boulder recipe update command with build and local flags --- boulder/src/cli/recipe.rs | 70 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index 587433555..2d96665a0 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -83,6 +83,18 @@ pub enum Subcommand { overwrite: bool, #[arg(long, default_value = "false", help = "Don't increment the release number")] no_bump: bool, + #[arg( + long, + default_value = "false", + help = "Build the package after a successful stone.yaml update." + )] + build: bool, + #[arg( + long, + default_value = "false", + help = "Move successfully built package to local repository for testing." + )] + local: bool, }, #[command(about = "Print macro definitions")] Macros { @@ -114,7 +126,9 @@ pub fn handle(command: Command, env: Env) -> Result<(), Error> { version, upstreams, no_bump, - } => update(recipe, overwrite, version, upstreams, no_bump), + build, + local, + } => update(recipe, overwrite, version, upstreams, no_bump, build, local), Subcommand::Macros { _macro } => macros(_macro, env), } } @@ -173,6 +187,8 @@ fn update( version: String, upstreams: Vec, no_bump: bool, + build: bool, + local: bool, ) -> Result<(), Error> { if overwrite && recipe.is_none() { return Err(Error::OverwriteRecipeRequired); @@ -284,6 +300,58 @@ fn update( print!("{updated}"); } + if build { + use std::io::{BufRead, BufReader}; + use std::process::{Command as Cmd, Stdio}; + + let mut just_build = Cmd::new("boulder") + .args(["build", "-u", "stone.yaml"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run boulder build command!"); + + let build_stdout = just_build.stdout.take().expect("Failed to open build process stdout"); + let build_reader = BufReader::new(build_stdout); + + // Stream the build process output + build_reader.lines().for_each(|line| { + if let Ok(line) = line { + println!("{line}"); + } + }); + + let build_status = just_build + .wait_with_output() + .expect("Failed to wait for build to complete"); + + if build_status.status.success() { + println!("Successfully built package!"); + + // If local was set, move the package to the local repository + if local { + let just_mv_local = std::process::Command::new("just").arg("mv-local").output(); + + match just_mv_local { + Ok(mv) => { + if mv.status.success() { + println!("Successfully moved packed to local repository!"); + } else { + println!( + "Moving package failed with error: {}", + String::from_utf8_lossy(&mv.stdout) + ) + } + } + Err(e) => eprintln!("Move command failed with error: {e}"), + } + } + } else { + let code = build_status.status.code().expect("Failed to get status code"); + eprintln!("Build failed with status code: {code}"); + } + } + Ok(()) } From 7fcec2a14ebf67de7cbcc36e40cc41fc2048759d Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sun, 10 Aug 2025 00:07:18 -0700 Subject: [PATCH 02/10] Added restriction on local flag to require build flag --- boulder/src/cli/recipe.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index 2d96665a0..be5f5746d 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -92,6 +92,7 @@ pub enum Subcommand { #[arg( long, default_value = "false", + requires = "build", help = "Move successfully built package to local repository for testing." )] local: bool, From 917a7566bc3fbd4a41fea89cf5a7cd0986721a70 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Fri, 15 Aug 2025 09:05:12 -0700 Subject: [PATCH 03/10] Replaced just mv-local command --- boulder/src/cli/recipe.rs | 99 +++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index be5f5746d..784eb4bcd 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -322,6 +322,8 @@ fn update( } }); + let build_stderr = just_build.stderr.take().expect("Failed to get boulder build stderr"); + let build_status = just_build .wait_with_output() .expect("Failed to wait for build to complete"); @@ -331,25 +333,94 @@ fn update( // If local was set, move the package to the local repository if local { - let just_mv_local = std::process::Command::new("just").arg("mv-local").output(); - - match just_mv_local { - Ok(mv) => { - if mv.status.success() { - println!("Successfully moved packed to local repository!"); - } else { - println!( - "Moving package failed with error: {}", - String::from_utf8_lossy(&mv.stdout) - ) + let local_repo = dirs::home_dir() + .expect("Failed to get home directory") + .join(".cache/local_repo/x86_64") + .to_string_lossy() + .to_string(); + + let cwd = PathBuf::from("."); + let stone_ext = "stone"; + + // Create local repo directory if it doesn't exist + fs::create_dir_all(&local_repo).expect("Failed to create local repo directories"); + match fs::read_dir(&cwd) { + Ok(dir) => { + for pkg_file in dir { + let pkg_file = pkg_file.expect("Failed to get package file to move"); + let path = pkg_file.path(); + + if path.is_file() && let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { + if ext == stone_ext { + let file_name = path.file_name().ok_or("Invalid package file name").expect("Failed to get package file name"); + let dest_path = PathBuf::from(&local_repo).join(file_name); + + println!("Moving {:?} to {:?}", &path, &dest_path); + match fs::rename(&path, &dest_path) { + Ok(_) => { + println!("Successfully moved {:?} to {:?}", &path, &dest_path); + + let mut moss_cmd = Cmd::new("moss") + .args(["index", &local_repo]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to index new packages in local repo"); + + let moss_cmd_output = moss_cmd + .stdout + .take() + .expect("Failed to get stdout from moss index command"); + let moss_stderr = moss_cmd + .stderr + .take() + .expect("Failed to get stderr from moss index command"); + + let moss_output = BufReader::new(moss_cmd_output); + + moss_output.lines().for_each(|line| { + if let Ok(output) = line { + println!("{output}"); + } + }); + + let moss_status = moss_cmd + .wait_with_output() + .expect("Failed to wait for moss index command to completed"); + + if moss_status.status.success() { + println!("Successfully indexed local repo!"); + } else { + let err_output = BufReader::new(moss_stderr); + err_output.lines().for_each(|line| { + if let Ok(err) = line { + eprintln!("{err}"); + } + }); + eprintln!("Failed to index built package files after move to local repo"); + } + }, + Err(e) => { + eprintln!("Failed to move {:?} to {:?}: {e}", &path, &dest_path); + } + } + } + } } + }, + Err(e) => { + eprintln!("Failed to read directory: {e}"); } - Err(e) => eprintln!("Move command failed with error: {e}"), } } } else { - let code = build_status.status.code().expect("Failed to get status code"); - eprintln!("Build failed with status code: {code}"); + let build_err_output = BufReader::new(build_stderr); + + build_err_output.lines().for_each(|err_line| { + if let Ok(err_output) = err_line { + eprintln!("{err_output}"); + } + }); } } From cf1eddda8dcfd4470a6b061edb90c7ed527b86d3 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Wed, 20 Aug 2025 21:10:01 -0700 Subject: [PATCH 04/10] Added build flag and successfully tested --- boulder/src/cli.rs | 1 + boulder/src/cli/recipe.rs | 59 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index 7b1a68833..07929cc3d 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -123,6 +123,7 @@ pub fn process() -> Result<(), Error> { Some(Subcommand::Build(command)) => build::handle(command, env)?, Some(Subcommand::Chroot(command)) => chroot::handle(command, env)?, Some(Subcommand::Profile(command)) => profile::handle(command, env)?, + // Recipe takes into account the global.build flag Some(Subcommand::Recipe(command)) => recipe::handle(command, env)?, Some(Subcommand::Version(command)) => version::handle(command), None => (), diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index 784eb4bcd..2bda449c1 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -29,6 +29,14 @@ use url::Url; #[derive(Debug, Parser)] #[command(about = "Utilities to create and manipulate stone recipe files")] pub struct Command { + #[arg( + long, + required = false, + default_value_t = false, + global = true, + help = "Build the recipe after successful completion of the subcommand" + )] + build: bool, #[command(subcommand)] subcommand: Subcommand, } @@ -118,7 +126,7 @@ fn parse_upstream(s: &str) -> Result { } pub fn handle(command: Command, env: Env) -> Result<(), Error> { - match command.subcommand { + let run_cmd = match command.subcommand { Subcommand::Bump { recipe, release } => bump(recipe, release), Subcommand::New { output, upstreams } => new(output, upstreams, env), Subcommand::Update { @@ -131,7 +139,54 @@ pub fn handle(command: Command, env: Env) -> Result<(), Error> { local, } => update(recipe, overwrite, version, upstreams, no_bump, build, local), Subcommand::Macros { _macro } => macros(_macro, env), + }; + + if command.build + && let Ok(_) = run_cmd + { + use std::io::{BufRead, BufReader}; + use std::process::{Command as Cmd, Stdio}; + + let mut boulder_build = Cmd::new("boulder") + .args(["build", "-u", "stone.yaml"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run boulder build command!"); + + let build_stdout = boulder_build.stdout.take().expect("Failed to get build process stdout"); + let build_reader = BufReader::new(build_stdout); + + let build_stderr = boulder_build.stderr.take().expect("Failed to get build process stderr"); + + // Stream the build process output + build_reader.lines().for_each(|line| { + if let Ok(stdout) = line { + println!("{stdout}"); + } + }); + + let status = boulder_build + .wait() + .expect("boulder build command failed to wait to complete"); + + if !status.success() { + let err_reader = BufReader::new(build_stderr); + let mut err_str = String::new(); + err_reader.lines().for_each(|err_line| { + if let Ok(err) = err_line { + eprintln!("{err}"); + err_str = format!("{err_str}\n{err}"); + } + }); + + return Err(Error::BuildErr(err_str)); + } else { + println!("Successfully built package!"); + } } + + run_cmd } fn bump(recipe: PathBuf, release: Option) -> Result<(), Error> { @@ -573,4 +628,6 @@ pub enum Error { Utf8(#[from] std::string::FromUtf8Error), #[error("draft")] Draft(#[from] draft::Error), + #[error("Recipe auto-build error")] + BuildErr(String), } From 2f4ed5c15fb6841145113f561c4103c2a510aeb7 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Wed, 20 Aug 2025 23:04:46 -0700 Subject: [PATCH 05/10] Added mv-to-repo flag --- boulder/src/cli.rs | 104 +++++++++++++++++++++++++++++++++++++- boulder/src/cli/recipe.rs | 2 +- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index 07929cc3d..4a5048c69 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -51,6 +51,14 @@ pub struct Global { pub generate_manpages: Option, #[arg(long, global = true, hide = true)] pub generate_completions: Option, + #[arg( + long, + require_equals = true, + value_parser = clap::builder::PossibleValuesParser::new(["local", "unstable", "volatile"] + ), + help = "Move newly built .stone package files to the given repo" + )] + pub mv_to_repo: Option, } #[derive(Debug, clap::Subcommand)] @@ -106,6 +114,16 @@ pub fn process() -> Result<(), Error> { return Ok(()); } + if global.mv_to_repo.is_some() { + match subcommand { + Some(Subcommand::Build(_)) | Some(Subcommand::Recipe(_)) => {} + _ => { + eprintln!("The `--mv-to-repo` flag must be used with either the build or recipe subcommand"); + std::process::exit(1); + } + } + } + let env = Env::new(global.cache_dir, global.config_dir, global.data_dir, global.moss_root)?; if global.verbose { @@ -120,11 +138,31 @@ pub fn process() -> Result<(), Error> { } match subcommand { - Some(Subcommand::Build(command)) => build::handle(command, env)?, + Some(Subcommand::Build(command)) => { + match build::handle(command, env) { + Ok(_) => { + if let Some(repo) = global.mv_to_repo { + if let Err(err) = mv_to_repo(&repo) { + eprintln!("Error: {err}"); + return Err(err); + } + } + } + Err(e) => return Err(Error::Build(e)), + }; + } Some(Subcommand::Chroot(command)) => chroot::handle(command, env)?, Some(Subcommand::Profile(command)) => profile::handle(command, env)?, // Recipe takes into account the global.build flag - Some(Subcommand::Recipe(command)) => recipe::handle(command, env)?, + Some(Subcommand::Recipe(command)) => { + // Give an error message and exit without running the command + // if the --mv-to-repo flag was give without the --build flag. + if global.mv_to_repo.is_some() && !command.build { + eprintln!("Error: Cannot use `--mv-to-repo` without the `--build` flag"); + std::process::exit(1); + } + recipe::handle(command, env)?; + } Some(Subcommand::Version(command)) => version::handle(command), None => (), } @@ -161,6 +199,68 @@ fn replace_aliases(args: std::env::Args) -> Vec { args } +fn mv_to_repo(repo: &String) -> Result<(), Error> { + let repo_path = if repo == "local" { + dirs::home_dir() + .expect("Failed to get home directory") + .join(".cache/local_repo/x86_64") + .to_string_lossy() + .to_string() + } else if repo == "volatile" { + println!("TODO: Move to volatile repo"); + String::new() + } else { + println!("TODO: Move to unstable repo"); + String::new() + }; + + if !repo_path.is_empty() { + let cwd = PathBuf::from("."); + let manifest_ext = "stone"; + + // Create repo directory if it doesn't exist + fs::create_dir_all(&repo_path).expect("Failed to create local repo directories"); + match fs::read_dir(&cwd) { + Ok(dir) => { + for pkg_file in dir { + let pkg_file = pkg_file.expect("Failed to get package file to move"); + let path = pkg_file.path(); + + if path.is_file() + && let Some(ext) = path.extension().and_then(|ext| ext.to_str()) + { + if ext == manifest_ext { + let file_name = path + .file_name() + .ok_or("Invalid package file name") + .expect("Failed to get package file name"); + let dest_path = PathBuf::from(&repo_path).join(file_name); + + println!("Moving {:?} to {:?}", &path, &dest_path); + match fs::rename(&path, &dest_path) { + Ok(_) => { + println!("Successfully move {:?} to {:?}", &path, &dest_path); + return Ok(()); + } + Err(e) => { + eprintln!("Failed to move {:?} to {:?}: {e}", &path, &dest_path); + return Err(Error::Io(e)); + } + } + } + } + } + } + Err(e) => { + eprintln!("Failed to read directory: {e}"); + return Err(Error::Io(e)); + } + } + } + + Ok(()) +} + #[derive(Debug, Error)] pub enum Error { #[error("build")] diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index 2bda449c1..54e27b066 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -36,7 +36,7 @@ pub struct Command { global = true, help = "Build the recipe after successful completion of the subcommand" )] - build: bool, + pub(crate) build: bool, #[command(subcommand)] subcommand: Subcommand, } From abdb4184e592f1e3eb489652b7ad2ee9629b4e54 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sat, 23 Aug 2025 23:13:40 -0700 Subject: [PATCH 06/10] Colorized success and error messages. --- boulder/src/cli.rs | 40 +++++++++++++++++++++++++++++++++------ boulder/src/cli/recipe.rs | 38 +++++++++---------------------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index 4a5048c69..d1499e56e 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -12,6 +12,7 @@ use clap_complete::{ use clap_mangen::Man; use fs_err::{self as fs, File}; use thiserror::Error; +use tui::Styled; mod build; mod chroot; @@ -118,7 +119,12 @@ pub fn process() -> Result<(), Error> { match subcommand { Some(Subcommand::Build(_)) | Some(Subcommand::Recipe(_)) => {} _ => { - eprintln!("The `--mv-to-repo` flag must be used with either the build or recipe subcommand"); + eprintln!( + "{}", + "The `--mv-to-repo` flag must be used with either the build or recipe subcommand" + .red() + .to_string() + ); std::process::exit(1); } } @@ -143,12 +149,16 @@ pub fn process() -> Result<(), Error> { Ok(_) => { if let Some(repo) = global.mv_to_repo { if let Err(err) = mv_to_repo(&repo) { - eprintln!("Error: {err}"); + eprintln!("{} {}", "Error:".red(), err.to_string().red()); return Err(err); } } } - Err(e) => return Err(Error::Build(e)), + Err(e) => { + let err_str = e.to_string().red(); + eprintln!("{err_str}"); + return Err(Error::Build(e)); + } }; } Some(Subcommand::Chroot(command)) => chroot::handle(command, env)?, @@ -162,6 +172,13 @@ pub fn process() -> Result<(), Error> { std::process::exit(1); } recipe::handle(command, env)?; + + if let Some(repo) = global.mv_to_repo { + if let Err(err) = mv_to_repo(&repo) { + eprintln!("{} {}", "Error:".red(), err.to_string().red()); + return Err(err); + } + } } Some(Subcommand::Version(command)) => version::handle(command), None => (), @@ -202,7 +219,7 @@ fn replace_aliases(args: std::env::Args) -> Vec { fn mv_to_repo(repo: &String) -> Result<(), Error> { let repo_path = if repo == "local" { dirs::home_dir() - .expect("Failed to get home directory") + .expect(&format!("{}", "Failed to get home directory".red())) .join(".cache/local_repo/x86_64") .to_string_lossy() .to_string() @@ -239,11 +256,22 @@ fn mv_to_repo(repo: &String) -> Result<(), Error> { println!("Moving {:?} to {:?}", &path, &dest_path); match fs::rename(&path, &dest_path) { Ok(_) => { - println!("Successfully move {:?} to {:?}", &path, &dest_path); + println!("Successfully moved {:?} to {:?}", &path, &dest_path); return Ok(()); } Err(e) => { - eprintln!("Failed to move {:?} to {:?}: {e}", &path, &dest_path); + eprintln!( + "{}", + &format!( + "{} {} {} {} {} {}", + "Failed to move".red(), + &path.to_string_lossy().to_string().red(), + "to".red(), + &dest_path.to_string_lossy().to_string().red(), + ":".red(), + e.to_string().red() + ) + ); return Err(Error::Io(e)); } } diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index 54e27b066..bbe049e5c 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -144,45 +144,25 @@ pub fn handle(command: Command, env: Env) -> Result<(), Error> { if command.build && let Ok(_) = run_cmd { - use std::io::{BufRead, BufReader}; use std::process::{Command as Cmd, Stdio}; let mut boulder_build = Cmd::new("boulder") .args(["build", "-u", "stone.yaml"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) .spawn() .expect("Failed to run boulder build command!"); - let build_stdout = boulder_build.stdout.take().expect("Failed to get build process stdout"); - let build_reader = BufReader::new(build_stdout); - - let build_stderr = boulder_build.stderr.take().expect("Failed to get build process stderr"); - - // Stream the build process output - build_reader.lines().for_each(|line| { - if let Ok(stdout) = line { - println!("{stdout}"); - } - }); - let status = boulder_build .wait() - .expect("boulder build command failed to wait to complete"); + .expect(&format!("{}", "boulder build command failed to wait to complete".red())); if !status.success() { - let err_reader = BufReader::new(build_stderr); - let mut err_str = String::new(); - err_reader.lines().for_each(|err_line| { - if let Ok(err) = err_line { - eprintln!("{err}"); - err_str = format!("{err_str}\n{err}"); - } - }); - + let err_str = "Failed to build package".red().to_string(); return Err(Error::BuildErr(err_str)); } else { - println!("Successfully built package!"); + let success_msg = "Successfully updated and built package!".green().to_string(); + println!("{success_msg}"); } } @@ -232,7 +212,7 @@ fn new(output: PathBuf, upstreams: Vec, env: Env) -> Result<(), Error> { fs::write(PathBuf::from(&output).join(RECIPE_FILE), draft.stone).map_err(Error::Write)?; fs::write(PathBuf::from(&output).join(MONITORING_FILE), draft.monitoring).map_err(Error::Write)?; - println!("Saved {RECIPE_FILE} & {MONITORING_FILE} to {output:?}"); + println!("{}", "Saved {RECIPE_FILE} & {MONITORING_FILE} to {output:?}".green()); Ok(()) } @@ -351,9 +331,9 @@ fn update( if overwrite { let recipe = recipe.expect("checked above"); fs::write(&recipe, updated.as_bytes()).map_err(Error::Write)?; - println!("{} updated", recipe.display()); + println!("{} {}", recipe.display().to_string().green(), "updated".green()); } else { - print!("{updated}"); + print!("{}", updated.green()); } if build { From 4a2d0329bc2df3ea5ddb7d3ab11470c6908e3e15 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sun, 24 Aug 2025 00:38:25 -0700 Subject: [PATCH 07/10] Added re-index flag --- boulder/src/cli.rs | 93 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index d1499e56e..cbbb15fce 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -55,11 +55,19 @@ pub struct Global { #[arg( long, require_equals = true, - value_parser = clap::builder::PossibleValuesParser::new(["local", "unstable", "volatile"] - ), + value_parser = clap::builder::PossibleValuesParser::new(["local", "unstable", "volatile"]), + global = true, help = "Move newly built .stone package files to the given repo" )] pub mv_to_repo: Option, + #[arg( + long, + default_value_t = false, + requires = "mv-to-repo", + global = true, + help = "Auto re-index the repo after a successful build and move" + )] + pub re_index: bool, } #[derive(Debug, clap::Subcommand)] @@ -148,9 +156,26 @@ pub fn process() -> Result<(), Error> { match build::handle(command, env) { Ok(_) => { if let Some(repo) = global.mv_to_repo { - if let Err(err) = mv_to_repo(&repo) { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); - return Err(err); + match mv_to_repo(&repo) { + Ok(repo) => { + if global.re_index && !repo.is_empty() { + if let Err(err) = re_index_repo(&repo) { + eprintln!("{} {}", "Error:".red(), err.to_string().red()); + return Err(err); + } + } else { + eprintln!("{}", "Error: Cannot re-index, returned repo name was empty!".red()); + return Err(Error::Reindex( + "Cannot re-index, move operation returned an invalid repo name" + .red() + .to_string(), + )); + } + } + Err(err) => { + eprintln!("{} {}", "Error:".red(), err.to_string().red()); + return Err(err); + } } } } @@ -174,9 +199,23 @@ pub fn process() -> Result<(), Error> { recipe::handle(command, env)?; if let Some(repo) = global.mv_to_repo { - if let Err(err) = mv_to_repo(&repo) { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); - return Err(err); + match mv_to_repo(&repo) { + Ok(repo) => { + if global.re_index { + if let Err(err) = re_index_repo(&repo) { + eprintln!("{} {}", "Error:".red(), err.to_string().red()); + return Err(Error::Reindex( + "Cannot re-index, move operation returned an invalid repo name" + .red() + .to_string(), + )); + } + } + } + Err(err) => { + eprintln!("{} {}", "Error:".red(), err.to_string().red()); + return Err(err); + } } } } @@ -216,7 +255,7 @@ fn replace_aliases(args: std::env::Args) -> Vec { args } -fn mv_to_repo(repo: &String) -> Result<(), Error> { +fn mv_to_repo(repo: &String) -> Result { let repo_path = if repo == "local" { dirs::home_dir() .expect(&format!("{}", "Failed to get home directory".red())) @@ -239,7 +278,7 @@ fn mv_to_repo(repo: &String) -> Result<(), Error> { fs::create_dir_all(&repo_path).expect("Failed to create local repo directories"); match fs::read_dir(&cwd) { Ok(dir) => { - for pkg_file in dir { + for (_, pkg_file) in dir.enumerate() { let pkg_file = pkg_file.expect("Failed to get package file to move"); let path = pkg_file.path(); @@ -253,11 +292,23 @@ fn mv_to_repo(repo: &String) -> Result<(), Error> { .expect("Failed to get package file name"); let dest_path = PathBuf::from(&repo_path).join(file_name); - println!("Moving {:?} to {:?}", &path, &dest_path); + println!( + "{} {} {} {}\n", + "Moving".blue(), + &path.to_string_lossy().to_string().blue().italic().bold(), + "to".blue(), + &dest_path.to_string_lossy().to_string().blue().italic().bold() + ); match fs::rename(&path, &dest_path) { Ok(_) => { - println!("Successfully moved {:?} to {:?}", &path, &dest_path); - return Ok(()); + println!( + "{} {} {} {}\n", + "Successfully moved".green(), + &path.to_string_lossy().to_string().green().italic().bold(), + "to".green(), + &dest_path.to_string_lossy().to_string().green().italic().bold() + ); + return Ok(repo_path.to_string()); } Err(e) => { eprintln!( @@ -286,6 +337,20 @@ fn mv_to_repo(repo: &String) -> Result<(), Error> { } } + Ok(String::new()) +} + +fn re_index_repo(repo: &str) -> Result<(), Error> { + use std::process::{Command as Cmd, Stdio}; + + let mut moss_cmd = Cmd::new("moss") + .args(["index", repo]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn()?; + + let _index_status = moss_cmd.wait()?; + Ok(()) } @@ -303,4 +368,6 @@ pub enum Error { Recipe(#[from] recipe::Error), #[error("io error")] Io(#[from] std::io::Error), + #[error("reindex")] + Reindex(String), } From 359c691816c2e27942c838215666d4c433472df1 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Wed, 3 Sep 2025 00:29:36 -0700 Subject: [PATCH 08/10] Final clean-up complete --- boulder/src/build.rs | 2 + boulder/src/cli.rs | 203 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 165 insertions(+), 40 deletions(-) diff --git a/boulder/src/build.rs b/boulder/src/build.rs index 4401bd41d..f55e6ea0d 100644 --- a/boulder/src/build.rs +++ b/boulder/src/build.rs @@ -462,4 +462,6 @@ pub enum Error { RecreateArtefactsDir(#[source] io::Error), #[error("git upstream processing")] Git(#[from] git::GitError), + #[error("invalid repo in mv-to-repo flag")] + InvalidRepo, } diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index cbbb15fce..73d1e95d1 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright © 2020-2025 Serpent OS Developers // // SPDX-License-Identifier: MPL-2.0 -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use boulder::{Env, env}; use clap::{Args, CommandFactory, Parser}; @@ -55,7 +55,6 @@ pub struct Global { #[arg( long, require_equals = true, - value_parser = clap::builder::PossibleValuesParser::new(["local", "unstable", "volatile"]), global = true, help = "Move newly built .stone package files to the given repo" )] @@ -123,19 +122,33 @@ pub fn process() -> Result<(), Error> { return Ok(()); } - if global.mv_to_repo.is_some() { - match subcommand { - Some(Subcommand::Build(_)) | Some(Subcommand::Recipe(_)) => {} - _ => { + match subcommand { + Some(Subcommand::Build(_)) | Some(Subcommand::Recipe(_)) => { /* do nothing, the flags were passed in appropriately. */ + } + _ => match (global.mv_to_repo.clone(), global.re_index) { + (Some(_), false) => { eprintln!( - "{}", - "The `--mv-to-repo` flag must be used with either the build or recipe subcommand" - .red() - .to_string() + "{}: The `--mv-to-repo` flag cannot be used with anything but the `build` or `recipe` subcommands", + "Error".red() ); std::process::exit(1); } - } + (None, true) => { + eprintln!( + "{}: The `--re-index` cannot be used with anything but the `build` or `recipe` subcommands and requires `--mv-to-repo`", + "Error".red() + ); + std::process::exit(1); + } + (Some(_), true) => { + eprintln!( + "{}: The ``--mv-to-repo` and ``--re-index` flags can only be used with the `build` or `recipe` subcommands", + "Error".red() + ); + std::process::exit(1); + } + (None, false) => { /* do nothing, the flags weren't passed in. */ } + }, } let env = Env::new(global.cache_dir, global.config_dir, global.data_dir, global.moss_root)?; @@ -156,14 +169,65 @@ pub fn process() -> Result<(), Error> { match build::handle(command, env) { Ok(_) => { if let Some(repo) = global.mv_to_repo { - match mv_to_repo(&repo) { + // Check to see if the repo is in moss + let moss_cmd = std::process::Command::new("moss") + .args(["repo", "list"]) + .stdout(std::process::Stdio::piped()) + .output() + .expect("Couldn't get a list of moss repos"); + + // Convert the output to a String + let repos = String::from_utf8(moss_cmd.stdout).expect("Could get the repo list from moss"); + + let mv_repo = repos + .lines() + .filter_map(|line| { + if line.contains(&repo) { + let mut ret_map = HashMap::new(); + println!("{}: {line}", "DEBUG".yellow()); + let uri = line + .split_whitespace() + .filter(|line| line.contains("//")) + .last() + .and_then(|uri| { + if uri.contains("file:///") { + Some(uri.to_string().replace("file://", "")) + } else { + Some(uri.to_string()) + } + }) + .expect("Couldn't get URI from repo string".red().to_string().as_str()); + + let _ = ret_map.insert(&repo, Some(uri.clone())); + + Some(ret_map) + } else { + None + } + }) + .last() + .unwrap_or_else(|| HashMap::new()); + + // Check to ensure that the repo has a URI; + // return Err if there isn't. + if mv_repo.get(&repo).is_none() { + eprintln!("{} {}", &repo, "is not a valid repo registered with moss"); + return Err(Error::Build(build::Error::Build(boulder::build::Error::InvalidRepo))); + } + + // Move the newly built .stone files + match mv_to_repo(&repo, &mv_repo) { Ok(repo) => { - if global.re_index && !repo.is_empty() { - if let Err(err) = re_index_repo(&repo) { + if global.re_index && repo.is_some() { + if let Err(err) = + re_index_repo(&repo.expect( + format!("{}: Repo was supposed to be Some", "Error".red()).as_str(), + )) + { eprintln!("{} {}", "Error:".red(), err.to_string().red()); return Err(err); } - } else { + } else if global.re_index && repo.is_none() { eprintln!("{}", "Error: Cannot re-index, returned repo name was empty!".red()); return Err(Error::Reindex( "Cannot re-index, move operation returned an invalid repo name" @@ -196,13 +260,58 @@ pub fn process() -> Result<(), Error> { eprintln!("Error: Cannot use `--mv-to-repo` without the `--build` flag"); std::process::exit(1); } - recipe::handle(command, env)?; - if let Some(repo) = global.mv_to_repo { - match mv_to_repo(&repo) { + // Check to see if the repo is in moss + let moss_cmd = std::process::Command::new("moss") + .args(["repo", "list"]) + .output() + .expect("Couldn't get a list of moss repos"); + + let repos = String::from_utf8(moss_cmd.stdout).expect("Could get the repo list from moss"); + + let mv_repo = repos + .lines() + .filter_map(|line| { + if line.contains(&repo) { + let mut ret_map = HashMap::new(); + + let uri = line + .split_whitespace() + .filter(|line| line.contains("//")) + .last() + .and_then(|uri| { + if uri.contains("file:///") { + Some(uri.to_string().replace("file://", "")) + } else { + Some(uri.to_string()) + } + }) + .expect("Couldn't get URI from repo string".red().to_string().as_str()); + + let _ = ret_map.insert(&repo, Some(uri.clone())); + + Some(ret_map) + } else { + None + } + }) + .last() + .unwrap_or_else(|| HashMap::new()); + + if mv_repo.get(&repo).is_none() { + eprintln!("{} {}", &repo, "is not a valid repo registered with moss"); + return Err(Error::Build(build::Error::Build(boulder::build::Error::InvalidRepo))); + } + + recipe::handle(command, env)?; + + match mv_to_repo(&repo, &mv_repo) { Ok(repo) => { - if global.re_index { - if let Err(err) = re_index_repo(&repo) { + // Ok to re-index as there is a value use + if global.re_index && repo.is_some() { + if let Err(err) = re_index_repo(&repo.clone().expect( + format!("{}: Returned repo should've been Some", "Error".red().to_string()).as_str(), + )) { eprintln!("{} {}", "Error:".red(), err.to_string().red()); return Err(Error::Reindex( "Cannot re-index, move operation returned an invalid repo name" @@ -210,6 +319,13 @@ pub fn process() -> Result<(), Error> { .to_string(), )); } + } else if global.re_index && repo.is_none() { + eprintln!("{}", "Error: Cannot re-index, returned repo name was empty!".red()); + return Err(Error::Reindex( + "Cannot re-index, move operation returned an invalid repo name" + .red() + .to_string(), + )); } } Err(err) => { @@ -217,6 +333,8 @@ pub fn process() -> Result<(), Error> { return Err(err); } } + } else { + recipe::handle(command, env)?; } } Some(Subcommand::Version(command)) => version::handle(command), @@ -255,27 +373,25 @@ fn replace_aliases(args: std::env::Args) -> Vec { args } -fn mv_to_repo(repo: &String) -> Result { - let repo_path = if repo == "local" { - dirs::home_dir() - .expect(&format!("{}", "Failed to get home directory".red())) - .join(".cache/local_repo/x86_64") - .to_string_lossy() - .to_string() - } else if repo == "volatile" { - println!("TODO: Move to volatile repo"); - String::new() - } else { - println!("TODO: Move to unstable repo"); - String::new() - }; - - if !repo_path.is_empty() { +fn mv_to_repo(repo_key: &String, repo_map: &HashMap<&String, Option>) -> Result, Error> { + let repo_path = repo_map.get(repo_key).unwrap_or_else(|| &None); + if let Some(repo_path) = repo_path { let cwd = PathBuf::from("."); let manifest_ext = "stone"; + let repo_path = PathBuf::from(if repo_path.contains("file://") { + repo_path.replacen("file://", "", 1).replacen("stone.index", "", 1) + } else { + repo_path.to_string().replacen("stone.index", "", 1) + }); + + println!("{}: {repo_path:?}", "Debug".yellow()); + // Create repo directory if it doesn't exist - fs::create_dir_all(&repo_path).expect("Failed to create local repo directories"); + if !repo_path.exists() { + fs::create_dir_all(&repo_path).expect("Failed to create {repo_key} repo directories"); + } + match fs::read_dir(&cwd) { Ok(dir) => { for (_, pkg_file) in dir.enumerate() { @@ -308,7 +424,6 @@ fn mv_to_repo(repo: &String) -> Result { "to".green(), &dest_path.to_string_lossy().to_string().green().italic().bold() ); - return Ok(repo_path.to_string()); } Err(e) => { eprintln!( @@ -323,12 +438,13 @@ fn mv_to_repo(repo: &String) -> Result { e.to_string().red() ) ); - return Err(Error::Io(e)); } } } } } + + return Ok(Some(repo_path.to_string_lossy().to_string())); } Err(e) => { eprintln!("Failed to read directory: {e}"); @@ -337,7 +453,14 @@ fn mv_to_repo(repo: &String) -> Result { } } - Ok(String::new()) + if let Some(repo_path) = repo_path { + Ok(Some(repo_path.clone())) + } else { + Err(Error::Io(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("{}: {repo_key} doesn't have a valid path", "Error".red()).as_str(), + ))) + } } fn re_index_repo(repo: &str) -> Result<(), Error> { From a127eecca2d14d7e3f62fdec5a3d2376b29c447a Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Wed, 3 Sep 2025 13:12:54 -0700 Subject: [PATCH 09/10] Final cleanup complete --- boulder/src/cli.rs | 79 ++++++++------------- boulder/src/cli/recipe.rs | 142 +------------------------------------- 2 files changed, 28 insertions(+), 193 deletions(-) diff --git a/boulder/src/cli.rs b/boulder/src/cli.rs index 73d1e95d1..6c1785cac 100644 --- a/boulder/src/cli.rs +++ b/boulder/src/cli.rs @@ -184,7 +184,6 @@ pub fn process() -> Result<(), Error> { .filter_map(|line| { if line.contains(&repo) { let mut ret_map = HashMap::new(); - println!("{}: {line}", "DEBUG".yellow()); let uri = line .split_whitespace() .filter(|line| line.contains("//")) @@ -219,33 +218,26 @@ pub fn process() -> Result<(), Error> { match mv_to_repo(&repo, &mv_repo) { Ok(repo) => { if global.re_index && repo.is_some() { - if let Err(err) = - re_index_repo(&repo.expect( - format!("{}: Repo was supposed to be Some", "Error".red()).as_str(), - )) - { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); + if let Err(err) = re_index_repo(&repo.expect("Repo was supposed to be Some")) { + eprintln!("Error: {err}"); return Err(err); } } else if global.re_index && repo.is_none() { - eprintln!("{}", "Error: Cannot re-index, returned repo name was empty!".red()); + eprintln!("Error: Cannot re-index, returned repo name was empty!"); return Err(Error::Reindex( - "Cannot re-index, move operation returned an invalid repo name" - .red() - .to_string(), + "Cannot re-index, move operation returned an invalid repo name".to_string(), )); } } Err(err) => { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); + eprintln!("Error: {err}"); return Err(err); } } } } Err(e) => { - let err_str = e.to_string().red(); - eprintln!("{err_str}"); + eprintln!("{e}"); return Err(Error::Build(e)); } }; @@ -267,7 +259,7 @@ pub fn process() -> Result<(), Error> { .output() .expect("Couldn't get a list of moss repos"); - let repos = String::from_utf8(moss_cmd.stdout).expect("Could get the repo list from moss"); + let repos = String::from_utf8(moss_cmd.stdout).expect("Could not get the repo list from moss"); let mv_repo = repos .lines() @@ -286,7 +278,7 @@ pub fn process() -> Result<(), Error> { Some(uri.to_string()) } }) - .expect("Couldn't get URI from repo string".red().to_string().as_str()); + .expect("Could not get URI from repo string"); let _ = ret_map.insert(&repo, Some(uri.clone())); @@ -299,7 +291,7 @@ pub fn process() -> Result<(), Error> { .unwrap_or_else(|| HashMap::new()); if mv_repo.get(&repo).is_none() { - eprintln!("{} {}", &repo, "is not a valid repo registered with moss"); + eprintln!("{} is not a valid repo registered with moss", &repo); return Err(Error::Build(build::Error::Build(boulder::build::Error::InvalidRepo))); } @@ -309,27 +301,23 @@ pub fn process() -> Result<(), Error> { Ok(repo) => { // Ok to re-index as there is a value use if global.re_index && repo.is_some() { - if let Err(err) = re_index_repo(&repo.clone().expect( - format!("{}: Returned repo should've been Some", "Error".red().to_string()).as_str(), - )) { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); + if let Err(err) = + re_index_repo(&repo.clone().expect("Error: Returned repo should've been Some")) + { + eprintln!("Error {err}"); return Err(Error::Reindex( - "Cannot re-index, move operation returned an invalid repo name" - .red() - .to_string(), + "Cannot re-index, move operation returned an invalid repo name".to_string(), )); } } else if global.re_index && repo.is_none() { - eprintln!("{}", "Error: Cannot re-index, returned repo name was empty!".red()); + eprintln!("Error: Cannot re-index, returned repo name was empty!"); return Err(Error::Reindex( - "Cannot re-index, move operation returned an invalid repo name" - .red() - .to_string(), + "Cannot re-index, move operation returned an invalid repo name".to_string(), )); } } Err(err) => { - eprintln!("{} {}", "Error:".red(), err.to_string().red()); + eprintln!("Error: {err}"); return Err(err); } } @@ -385,8 +373,6 @@ fn mv_to_repo(repo_key: &String, repo_map: &HashMap<&String, Option>) -> repo_path.to_string().replacen("stone.index", "", 1) }); - println!("{}: {repo_path:?}", "Debug".yellow()); - // Create repo directory if it doesn't exist if !repo_path.exists() { fs::create_dir_all(&repo_path).expect("Failed to create {repo_key} repo directories"); @@ -409,34 +395,23 @@ fn mv_to_repo(repo_key: &String, repo_map: &HashMap<&String, Option>) -> let dest_path = PathBuf::from(&repo_path).join(file_name); println!( - "{} {} {} {}\n", - "Moving".blue(), - &path.to_string_lossy().to_string().blue().italic().bold(), - "to".blue(), - &dest_path.to_string_lossy().to_string().blue().italic().bold() + "Moving {} to {}", + &path.to_string_lossy().to_string(), + &dest_path.to_string_lossy().to_string() ); match fs::rename(&path, &dest_path) { Ok(_) => { println!( - "{} {} {} {}\n", - "Successfully moved".green(), - &path.to_string_lossy().to_string().green().italic().bold(), - "to".green(), - &dest_path.to_string_lossy().to_string().green().italic().bold() + "Successfully moved {} to {}", + &path.to_string_lossy().to_string(), + &dest_path.to_string_lossy().to_string() ); } Err(e) => { eprintln!( - "{}", - &format!( - "{} {} {} {} {} {}", - "Failed to move".red(), - &path.to_string_lossy().to_string().red(), - "to".red(), - &dest_path.to_string_lossy().to_string().red(), - ":".red(), - e.to_string().red() - ) + "Failed to move {} to {}: {e}", + &path.to_string_lossy().to_string(), + &dest_path.to_string_lossy().to_string(), ); } } @@ -458,7 +433,7 @@ fn mv_to_repo(repo_key: &String, repo_map: &HashMap<&String, Option>) -> } else { Err(Error::Io(std::io::Error::new( std::io::ErrorKind::NotFound, - format!("{}: {repo_key} doesn't have a valid path", "Error".red()).as_str(), + format!("Error: {repo_key} doesn't have a valid path").as_str(), ))) } } diff --git a/boulder/src/cli/recipe.rs b/boulder/src/cli/recipe.rs index bbe049e5c..b3d1b853a 100644 --- a/boulder/src/cli/recipe.rs +++ b/boulder/src/cli/recipe.rs @@ -91,19 +91,6 @@ pub enum Subcommand { overwrite: bool, #[arg(long, default_value = "false", help = "Don't increment the release number")] no_bump: bool, - #[arg( - long, - default_value = "false", - help = "Build the package after a successful stone.yaml update." - )] - build: bool, - #[arg( - long, - default_value = "false", - requires = "build", - help = "Move successfully built package to local repository for testing." - )] - local: bool, }, #[command(about = "Print macro definitions")] Macros { @@ -135,9 +122,7 @@ pub fn handle(command: Command, env: Env) -> Result<(), Error> { version, upstreams, no_bump, - build, - local, - } => update(recipe, overwrite, version, upstreams, no_bump, build, local), + } => update(recipe, overwrite, version, upstreams, no_bump), Subcommand::Macros { _macro } => macros(_macro, env), }; @@ -223,8 +208,6 @@ fn update( version: String, upstreams: Vec, no_bump: bool, - build: bool, - local: bool, ) -> Result<(), Error> { if overwrite && recipe.is_none() { return Err(Error::OverwriteRecipeRequired); @@ -336,129 +319,6 @@ fn update( print!("{}", updated.green()); } - if build { - use std::io::{BufRead, BufReader}; - use std::process::{Command as Cmd, Stdio}; - - let mut just_build = Cmd::new("boulder") - .args(["build", "-u", "stone.yaml"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to run boulder build command!"); - - let build_stdout = just_build.stdout.take().expect("Failed to open build process stdout"); - let build_reader = BufReader::new(build_stdout); - - // Stream the build process output - build_reader.lines().for_each(|line| { - if let Ok(line) = line { - println!("{line}"); - } - }); - - let build_stderr = just_build.stderr.take().expect("Failed to get boulder build stderr"); - - let build_status = just_build - .wait_with_output() - .expect("Failed to wait for build to complete"); - - if build_status.status.success() { - println!("Successfully built package!"); - - // If local was set, move the package to the local repository - if local { - let local_repo = dirs::home_dir() - .expect("Failed to get home directory") - .join(".cache/local_repo/x86_64") - .to_string_lossy() - .to_string(); - - let cwd = PathBuf::from("."); - let stone_ext = "stone"; - - // Create local repo directory if it doesn't exist - fs::create_dir_all(&local_repo).expect("Failed to create local repo directories"); - match fs::read_dir(&cwd) { - Ok(dir) => { - for pkg_file in dir { - let pkg_file = pkg_file.expect("Failed to get package file to move"); - let path = pkg_file.path(); - - if path.is_file() && let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { - if ext == stone_ext { - let file_name = path.file_name().ok_or("Invalid package file name").expect("Failed to get package file name"); - let dest_path = PathBuf::from(&local_repo).join(file_name); - - println!("Moving {:?} to {:?}", &path, &dest_path); - match fs::rename(&path, &dest_path) { - Ok(_) => { - println!("Successfully moved {:?} to {:?}", &path, &dest_path); - - let mut moss_cmd = Cmd::new("moss") - .args(["index", &local_repo]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to index new packages in local repo"); - - let moss_cmd_output = moss_cmd - .stdout - .take() - .expect("Failed to get stdout from moss index command"); - let moss_stderr = moss_cmd - .stderr - .take() - .expect("Failed to get stderr from moss index command"); - - let moss_output = BufReader::new(moss_cmd_output); - - moss_output.lines().for_each(|line| { - if let Ok(output) = line { - println!("{output}"); - } - }); - - let moss_status = moss_cmd - .wait_with_output() - .expect("Failed to wait for moss index command to completed"); - - if moss_status.status.success() { - println!("Successfully indexed local repo!"); - } else { - let err_output = BufReader::new(moss_stderr); - err_output.lines().for_each(|line| { - if let Ok(err) = line { - eprintln!("{err}"); - } - }); - eprintln!("Failed to index built package files after move to local repo"); - } - }, - Err(e) => { - eprintln!("Failed to move {:?} to {:?}: {e}", &path, &dest_path); - } - } - } - } - } - }, - Err(e) => { - eprintln!("Failed to read directory: {e}"); - } - } - } - } else { - let build_err_output = BufReader::new(build_stderr); - - build_err_output.lines().for_each(|err_line| { - if let Ok(err_output) = err_line { - eprintln!("{err_output}"); - } - }); - } - } - Ok(()) } From adcd90a643d66e85f3417ed962e8dd533414f93b Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Wed, 3 Sep 2025 13:16:06 -0700 Subject: [PATCH 10/10] boulder: Add '--build', '--mv-to-repo=', and '--re-index' flags --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6f1f9bcd2..23f735229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["moss"] resolver = "2" [workspace.package] -version = "0.25.6" +version = "0.25.7" edition = "2024" rust-version = "1.85"