Repository: paritytech/substrate
File: test-utils/cli/src/lib.rs (Rust)

Code before:
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg(unix)]

use assert_cmd::cargo::cargo_bin;
use nix::{
	sys::signal::{kill, Signal, Signal::SIGINT},
	unistd::Pid,
};
use node_primitives::{Hash, Header};
use regex::Regex;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
use std::{
	io::{BufRead, BufReader, Read},
	ops::{Deref, DerefMut},
	path::{Path, PathBuf},
	process::{self, Child, Command},
	time::Duration,
};
use tokio::io::{AsyncBufReadExt, AsyncRead};

/// Similar to [`crate::start_node`] spawns a dev node on port `45789`, but works in environments
/// where the substarte binary is not accessible with `cargo_bin("substrate-node")`.
///
/// Helpful if you need a Substrate dev node running in the background of a project external to
/// `substrate`.
///
/// The downside compared to using [`crate::start_node`] is that this method is blocking rather than
/// returning a [`Child`]. Therefore, you may want to call this method inside a new thread.
///
/// # Example
/// ```ignore
/// // Spawn a dev node in the background.
/// let _ = std::thread::spawn(move || {
/// 	common::start_node_without_binary();
/// });
/// ```
pub fn start_node_without_binary() -> Result<(), sc_service::error::Error> {
	use sc_cli::SubstrateCli;

	let cli = node_template::cli::Cli::from_iter(vec![
		"node-template", // first arg is ignored, this could be anything.
		"--dev",
		"--tmp",
		"--rpc-port=45789",
	]);
	let runner = cli.create_runner(&cli.run).unwrap();
	runner.run_node_until_exit(|config| async move { node_template::service::new_full(config) })

Review comment (suggestion):
It might be worth having a look at the [substrate-runner](https://github.com/paritytech/subxt/blob/9cfac4eec7571094f8f6ea864f6e662cf9737304/testing/substrate-runner/src/lib.rs) crate from subxt, a zero dependency crate, it has a lovely builder pattern to spawn a substrate-binary from paths with custom arguments. Cargo-contract also uses the crate for testing purposes.

It can retrieve the ws-port (p2p id, address, and port), so there would be no need to hardcode it to `45789` such multiple instances can run in parallel. 


Code after:
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg(unix)]

use assert_cmd::cargo::cargo_bin;
use nix::{
	sys::signal::{kill, Signal, Signal::SIGINT},
	unistd::Pid,
};
use node_primitives::{Hash, Header};
use regex::Regex;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
use std::{
	io::{BufRead, BufReader, Read},
	ops::{Deref, DerefMut},
	path::{Path, PathBuf},
	process::{self, Child, Command},
	time::Duration,
};
use tokio::io::{AsyncBufReadExt, AsyncRead};

/// Similar to [`crate::start_node`] spawns a node, but works in environments where the substrate
/// binary is not accessible with `cargo_bin("substrate-node")`, and allows customising the args
/// passed in.
///
/// Helpful if you need a Substrate dev node running in the background of a project external to
/// `substrate`.
///
/// The downside compared to using [`crate::start_node`] is that this method is blocking rather than
/// returning a [`Child`]. Therefore, you may want to call this method inside a new thread.
///
/// # Example
/// ```ignore
/// // Spawn a dev node.
/// let _ = std::thread::spawn(move || {
///     match common::start_node_inline(vec!["--dev", "--rpc-port=12345"]) {
///         Ok(_) => {}
///         Err(e) => {
///             panic!("Node exited with error: {}", e);
///         }
///     }
/// });
/// ```
pub fn start_node_inline(args: Vec<&str>) -> Result<(), sc_service::error::Error> {
	use sc_cli::SubstrateCli;

	// Prepend the args with some dummy value because the first arg is skipped.
	let cli_call = std::iter::once("node-template").chain(args);