From 15eca29ca821262cfd2e7154ba339af4400f82c8 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sat, 2 Oct 2021 17:46:57 -0400 Subject: [PATCH] cli: Add localnet command (#820) --- cli/src/lib.rs | 100 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 9cd4c504eb..d77b6384ed 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -199,6 +199,25 @@ pub enum Command { #[clap(subcommand)] subcmd: KeysCommand, }, + /// Localnet commands. + Localnet { + /// Flag to skip building the program in the workspace, + /// use this to save time when running test and the program code is not altered. + #[clap(long)] + skip_build: bool, + /// Use this flag if you want to run tests against previously deployed + /// programs. + #[clap(long)] + skip_deploy: bool, + /// Arguments to pass to the underlying `cargo build-bpf` command. + #[clap( + required = false, + takes_value = true, + multiple_values = true, + last = true + )] + cargo_args: Vec, + }, } #[derive(Debug, Clap)] @@ -350,6 +369,11 @@ pub fn entry(opts: Opts) -> Result<()> { cargo_args, } => publish(&opts.cfg_override, program, cargo_args), Command::Keys { subcmd } => keys(&opts.cfg_override, subcmd), + Command::Localnet { + skip_build, + skip_deploy, + cargo_args, + } => localnet(&opts.cfg_override, skip_build, skip_deploy, cargo_args), } } @@ -1411,7 +1435,7 @@ fn test( true => None, false => Some(genesis_flags(cfg)?), }; - validator_handle = Some(start_test_validator(cfg, flags)?); + validator_handle = Some(start_test_validator(cfg, flags, true)?); } // Setup log reader. @@ -1570,7 +1594,11 @@ pub struct IdlTestMetadata { address: String, } -fn start_test_validator(cfg: &Config, flags: Option>) -> Result { +fn start_test_validator( + cfg: &Config, + flags: Option>, + test_log_stdout: bool, +) -> Result { fs::create_dir_all(".anchor")?; let test_ledger_filename = ".anchor/test-ledger"; let test_ledger_log_filename = ".anchor/test-ledger-log.txt"; @@ -1583,16 +1611,25 @@ fn start_test_validator(cfg: &Config, flags: Option>) -> Result { + let test_validator_stdout_file = File::create(test_ledger_log_filename)?; + let test_validator_sterr_file = test_validator_stdout_file.try_clone()?; + ( + Stdio::from(test_validator_stdout_file), + Stdio::from(test_validator_sterr_file), + ) + } + false => (Stdio::inherit(), Stdio::inherit()), + }; let mut validator_handle = std::process::Command::new("solana-test-validator") .arg("--ledger") .arg(test_ledger_filename) .arg("--mint") .arg(cfg.wallet_kp()?.pubkey().to_string()) .args(flags.unwrap_or_default()) - .stdout(Stdio::from(test_validator_stdout)) - .stderr(Stdio::from(test_validator_stderr)) + .stdout(test_validator_stdout) + .stderr(test_validator_stderr) .spawn() .map_err(|e| anyhow::format_err!("{}", e.to_string()))?; @@ -2264,6 +2301,57 @@ fn keys_list(cfg_override: &ConfigOverride) -> Result<()> { Ok(()) } +fn localnet( + cfg_override: &ConfigOverride, + skip_build: bool, + skip_deploy: bool, + cargo_args: Vec, +) -> Result<()> { + with_workspace(cfg_override, |cfg| { + // Build if needed. + if !skip_build { + build( + cfg_override, + None, + false, + None, + None, + None, + None, + cargo_args, + )?; + } + + // Setup log reader. + let log_streams = stream_logs(cfg); + + let flags = match skip_deploy { + true => None, + false => Some(genesis_flags(cfg)?), + }; + let validator_handle = &mut start_test_validator(cfg, flags, false)?; + + std::io::stdin().lock().lines().next().unwrap().unwrap(); + + // Check all errors and shut down. + if let Err(err) = validator_handle.kill() { + println!( + "Failed to kill subprocess {}: {}", + validator_handle.id(), + err + ); + } + + for mut child in log_streams? { + if let Err(err) = child.kill() { + println!("Failed to kill subprocess {}: {}", child.id(), err); + } + } + + Ok(()) + }) +} + // with_workspace ensures the current working directory is always the top level // workspace directory, i.e., where the `Anchor.toml` file is located, before // and after the closure invocation.