diff --git a/Cargo.lock b/Cargo.lock index c632b51e22a4..9d44cbc6cd55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2881,7 +2881,7 @@ dependencies = [ [[package]] name = "linera-indexer" -version = "0.4.0" +version = "0.4.1" dependencies = [ "async-graphql", "async-graphql-axum", @@ -2908,7 +2908,7 @@ dependencies = [ [[package]] name = "linera-indexer-example" -version = "0.4.0" +version = "0.4.1" dependencies = [ "anyhow", "async-graphql", @@ -2931,7 +2931,7 @@ dependencies = [ [[package]] name = "linera-indexer-graphql-client" -version = "0.4.0" +version = "0.4.1" dependencies = [ "graphql_client", "linera-base", @@ -2948,7 +2948,7 @@ dependencies = [ [[package]] name = "linera-indexer-plugins" -version = "0.4.0" +version = "0.4.1" dependencies = [ "async-graphql", "async-trait", diff --git a/linera-explorer/package.json b/linera-explorer/package.json index 057fbaabf47e..460cca037c56 100644 --- a/linera-explorer/package.json +++ b/linera-explorer/package.json @@ -1,7 +1,7 @@ { "author": "linera ", "name": "linera-explorer", - "version": "0.4.0", + "version": "0.4.1", "scripts": { "clean": "rm -rf pkg dist package-lock.json gql", "wipe": "npm run clean && rm -rf node_modules", diff --git a/linera-indexer/example/Cargo.toml b/linera-indexer/example/Cargo.toml index a294061c4b61..fd820cf5b046 100644 --- a/linera-indexer/example/Cargo.toml +++ b/linera-indexer/example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linera-indexer-example" -version = "0.4.0" +version = "0.4.1" description = "Indexer example." authors = ["Linera "] readme = "README.md" diff --git a/linera-indexer/example/tests/test.rs b/linera-indexer/example/tests/test.rs index 9982567d0c59..c393c2538060 100644 --- a/linera-indexer/example/tests/test.rs +++ b/linera-indexer/example/tests/test.rs @@ -8,7 +8,10 @@ use linera_indexer_graphql_client::{ indexer::{plugins, state, Plugins, State}, operations::{get_operation, GetOperation, OperationKey}, }; -use linera_service::cli_wrappers::{resolve_binary, Database, LocalNetwork, Network}; +use linera_service::{ + cli_wrappers::{Database, LocalNetwork, Network}, + util::resolve_binary, +}; use linera_service_graphql_client::{block, request, transfer, Block, Transfer}; use once_cell::sync::Lazy; use std::{rc::Rc, str::FromStr, time::Duration}; @@ -24,7 +27,7 @@ static INTEGRATION_TEST_GUARD: Lazy> = Lazy::new(|| Mutex::new(())); async fn run_indexer(tmp_dir: &Rc) -> Child { let port = 8081; - let path = resolve_binary("linera-indexer", Some("linera-indexer-example")) + let path = resolve_binary("linera-indexer", "linera-indexer-example") .await .unwrap(); let mut command = Command::new(path); diff --git a/linera-indexer/graphql-client/Cargo.toml b/linera-indexer/graphql-client/Cargo.toml index 6902151485b4..8976b4cff7c6 100644 --- a/linera-indexer/graphql-client/Cargo.toml +++ b/linera-indexer/graphql-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linera-indexer-graphql-client" -version = "0.4.0" +version = "0.4.1" edition = "2021" description = "GraphQL client for the indexer" readme = "README.md" diff --git a/linera-indexer/graphql-client/tests/test.rs b/linera-indexer/graphql-client/tests/test.rs index 5ea2e5cb63e2..27ceaf2daa19 100644 --- a/linera-indexer/graphql-client/tests/test.rs +++ b/linera-indexer/graphql-client/tests/test.rs @@ -1,7 +1,7 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_service::cli_wrappers::resolve_binary; +use linera_service::util::resolve_binary; use std::{io::Read, rc::Rc}; use tempfile::tempdir; use tokio::process::Command; @@ -9,7 +9,7 @@ use tokio::process::Command; #[test_log::test(tokio::test)] async fn test_check_indexer_schema() { let tmp_dir = Rc::new(tempdir().unwrap()); - let path = resolve_binary("linera-indexer", Some("linera-indexer-example")) + let path = resolve_binary("linera-indexer", "linera-indexer-example") .await .unwrap(); let mut command = Command::new(path); @@ -33,7 +33,7 @@ async fn test_check_indexer_schema() { #[test_log::test(tokio::test)] async fn test_check_indexer_operations_schema() { let tmp_dir = Rc::new(tempdir().unwrap()); - let path = resolve_binary("linera-indexer", Some("linera-indexer-example")) + let path = resolve_binary("linera-indexer", "linera-indexer-example") .await .unwrap(); let mut command = Command::new(path); diff --git a/linera-indexer/lib/Cargo.toml b/linera-indexer/lib/Cargo.toml index 45bbc170c514..badd465f82dc 100644 --- a/linera-indexer/lib/Cargo.toml +++ b/linera-indexer/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linera-indexer" -version = "0.4.0" +version = "0.4.1" description = "Indexer for Linera protocol." authors = ["Linera "] readme = "README.md" diff --git a/linera-indexer/plugins/Cargo.toml b/linera-indexer/plugins/Cargo.toml index 8c4e3ad7be7b..35d9fd03fcd8 100644 --- a/linera-indexer/plugins/Cargo.toml +++ b/linera-indexer/plugins/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linera-indexer-plugins" -version = "0.4.0" +version = "0.4.1" description = "Indexer plugins." authors = ["Linera "] readme = "README.md" diff --git a/linera-service-graphql-client/Cargo.toml b/linera-service-graphql-client/Cargo.toml index 15b9ea1345c8..f3643612b61d 100644 --- a/linera-service-graphql-client/Cargo.toml +++ b/linera-service-graphql-client/Cargo.toml @@ -9,6 +9,12 @@ homepage = "https://linera.dev" documentation = "https://docs.rs/linera-service-graphql-client/latest/linera_service_graphql_client/" license = "Apache-2.0" +[features] +default = ["rocksdb"] +rocksdb = ["linera-service/rocksdb"] +aws = ["linera-service/aws"] +scylladb = ["linera-service/scylladb"] + [dependencies] graphql_client = { version = "0.13", features = [ "reqwest" ] } linera-base = { workspace = true } @@ -24,10 +30,10 @@ linera-execution = { workspace = true } [dev-dependencies] fungible = { workspace = true } -linera-service = { workspace = true } +linera-service = { workspace = true, features = ["test"] } once_cell = { workspace = true } test-log = { workspace = true, features = ["trace"] } tempfile = {workspace = true } tokio = { workspace = true, features = ["full", "test-util"] } tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["fmt"] } \ No newline at end of file +tracing-subscriber = { workspace = true, features = ["fmt"] } diff --git a/linera-service-graphql-client/tests/test.rs b/linera-service-graphql-client/tests/test.rs index 56be2e30e3c6..fa0d243cd899 100644 --- a/linera-service-graphql-client/tests/test.rs +++ b/linera-service-graphql-client/tests/test.rs @@ -3,20 +3,20 @@ #![cfg(any(feature = "rocksdb", feature = "aws", feature = "scylladb"))] -use linera_service::cli_wrappers::resolve_binary; -use once_cell::sync::Lazy; -use std::{io::Read, rc::Rc}; -use tempfile::tempdir; -use tokio::{process::Command, sync::Mutex}; - use fungible::{FungibleTokenAbi, InitialState}; use linera_base::{data_types::Amount, identifiers::ChainId}; -use linera_service::cli_wrappers::{Database, LocalNetwork, Network}; +use linera_service::{ + cli_wrappers::{Database, LocalNetwork, Network}, + util::resolve_binary, +}; use linera_service_graphql_client::{ applications, block, blocks, chains, request, transfer, Applications, Block, Blocks, Chains, Transfer, }; -use std::{collections::BTreeMap, str::FromStr}; +use once_cell::sync::Lazy; +use std::{collections::BTreeMap, io::Read, rc::Rc, str::FromStr}; +use tempfile::tempdir; +use tokio::{process::Command, sync::Mutex}; /// A static lock to prevent integration tests from running in parallel. pub static INTEGRATION_TEST_GUARD: Lazy> = Lazy::new(|| Mutex::new(())); @@ -54,7 +54,7 @@ async fn run_end_to_end_queries(database: Database) { let _guard = INTEGRATION_TEST_GUARD.lock().await; let network = Network::Grpc; let mut local_net = LocalNetwork::new_for_testing(database, network).unwrap(); - let mut client = local_net.make_client(network); + let client = local_net.make_client(network); local_net.generate_initial_validator_config().await.unwrap(); client.create_genesis_config().await.unwrap(); @@ -141,7 +141,7 @@ async fn run_end_to_end_queries(database: Database) { #[test_log::test(tokio::test)] async fn test_check_service_schema() { let tmp_dir = Rc::new(tempdir().unwrap()); - let path = resolve_binary("linera-schema-export", Some("linera-service")) + let path = resolve_binary("linera-schema-export", "linera-service") .await .unwrap(); let mut command = Command::new(path); diff --git a/linera-service/src/cli_wrappers.rs b/linera-service/src/cli_wrappers.rs index 644aba527a68..74db8f309f62 100644 --- a/linera-service/src/cli_wrappers.rs +++ b/linera-service/src/cli_wrappers.rs @@ -1,7 +1,7 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::config::WalletState; +use crate::{config::WalletState, util}; use anyhow::{Context, Result}; use async_graphql::InputType; use linera_base::{ @@ -11,7 +11,6 @@ use linera_base::{ identifiers::{ChainId, MessageId, Owner}, }; use linera_execution::Bytecode; -use once_cell::sync::OnceCell; use serde::ser::Serialize; use serde_json::{json, value::Value}; use std::{ @@ -25,10 +24,7 @@ use std::{ time::Duration, }; use tempfile::{tempdir, TempDir}; -use tokio::{ - process::{Child, Command}, - sync::Mutex, -}; +use tokio::process::{Child, Command}; use tonic_health::proto::{ health_check_response::ServingStatus, health_client::HealthClient, HealthCheckRequest, }; @@ -37,17 +33,13 @@ use tracing::{info, warn}; #[cfg(any(test, feature = "test"))] use linera_views::common::get_table_name; -/// The name of the environment variable that allows specifying additional arguments to be passed -/// to `cargo` when starting client, server and proxy processes. -pub const CARGO_ENV: &str = "LOCAL_NET_CARGO_PARAMS"; - /// The name of the environment variable that allows specifying additional arguments to be passed /// to the binary when starting a server. -const SERVER_ENV: &str = "LOCAL_NET_SERVER_PARAMS"; +const SERVER_ENV: &str = "LINERA_SERVER_PARAMS"; /// The name of the environment variable that allows specifying additional arguments to be passed /// to the node-service command of the client. -const CLIENT_SERVICE_ENV: &str = "LOCAL_NET_CLIENT_SERVICE_PARAMS"; +const CLIENT_SERVICE_ENV: &str = "LINERA_CLIENT_SERVICE_PARAMS"; #[derive(Copy, Clone)] pub enum Network { @@ -170,7 +162,7 @@ impl ClientWrapper { } async fn run(&self) -> Result { - let path = resolve_binary("linera", None).await?; + let path = util::resolve_binary("linera", env!("CARGO_PKG_NAME")).await?; let mut command = Command::new(path); command .current_dir(&self.tmp_dir.path().canonicalize()?) @@ -626,7 +618,7 @@ impl LocalNetwork { } async fn command_for_binary(&self, name: &'static str) -> Result { - let path = resolve_binary(name, None).await?; + let path = util::resolve_binary(name, env!("CARGO_PKG_NAME")).await?; let mut command = Command::new(path); command .current_dir(&self.tmp_dir.path().canonicalize()?) @@ -1065,105 +1057,3 @@ impl NodeService { serde_json::from_value(data["requestApplication"].clone()).unwrap() } } - -#[allow(unused_mut)] -fn detect_current_features() -> Vec<&'static str> { - let mut features = vec![]; - #[cfg(feature = "benchmark")] - { - features.push("benchmark"); - } - #[cfg(feature = "wasmer")] - { - features.push("wasmer"); - } - #[cfg(feature = "wasmtime")] - { - features.push("wasmtime"); - } - #[cfg(feature = "rocksdb")] - { - features.push("rocksdb"); - } - #[cfg(feature = "scylladb")] - { - features.push("scylladb"); - } - #[cfg(feature = "aws")] - { - features.push("aws"); - } - features -} - -async fn cargo_force_build_binary(name: &'static str, package: Option<&'static str>) -> PathBuf { - let package = package.unwrap_or(env!("CARGO_PKG_NAME")); - let mut build_command = Command::new("cargo"); - build_command.args(["build", "-p", package]); - let is_release = if let Ok(var) = env::var(CARGO_ENV) { - let extra_args = var.split_whitespace(); - build_command.args(extra_args.clone()); - let extra_args: HashSet<_> = extra_args.into_iter().map(str::trim).collect(); - extra_args.contains("-r") || extra_args.contains("--release") - } else { - false - }; - // Use the same features as the current environment so that we don't rebuild as often. - let features = detect_current_features().join(","); - build_command - .arg("--no-default-features") - .arg("--features") - .arg(features); - build_command.args(["--bin", name]); - info!("Running compiler: {:?}", build_command); - assert!(build_command - .spawn() - .unwrap() - .wait() - .await - .unwrap() - .success()); - let mut cargo_locate_command = Command::new("cargo"); - cargo_locate_command.args(["locate-project", "--workspace", "--message-format", "plain"]); - let output = cargo_locate_command.output().await.unwrap().stdout; - let workspace_path = Path::new(std::str::from_utf8(&output).unwrap().trim()) - .parent() - .unwrap(); - if is_release { - workspace_path - .join("target/release") - .join(name) - .canonicalize() - .unwrap() - } else { - workspace_path - .join("target/debug") - .join(name) - .canonicalize() - .unwrap() - } -} - -#[cfg(debug_assertions)] -pub async fn resolve_binary(name: &'static str, package: Option<&'static str>) -> Result { - Ok(cargo_build_binary(name, package).await) -} - -#[cfg(not(debug_assertions))] -pub async fn resolve_binary(name: &'static str, _package: Option<&'static str>) -> Result { - crate::util::resolve_cargo_binary(name) -} - -pub async fn cargo_build_binary(name: &'static str, package: Option<&'static str>) -> PathBuf { - type Key = (&'static str, Option<&'static str>); - static COMPILED_BINARIES: OnceCell>> = OnceCell::new(); - let mut binaries = COMPILED_BINARIES.get_or_init(Default::default).lock().await; - match binaries.get(&(name, package)) { - Some(path) => path.clone(), - None => { - let path = cargo_force_build_binary(name, package).await; - binaries.insert((name, package), path.clone()); - path - } - } -} diff --git a/linera-service/src/linera.rs b/linera-service/src/linera.rs index d240a496e1bd..12fb9ceba53c 100644 --- a/linera-service/src/linera.rs +++ b/linera-service/src/linera.rs @@ -585,7 +585,7 @@ fn deserialize_response(response: RpcMessage) -> Option { #[derive(StructOpt)] #[structopt( - name = "Linera Client", + name = "Linera client tool", about = "A Byzantine-fault tolerant sidechain with low-latency finality and high throughput" )] struct ClientOptions { @@ -1796,7 +1796,7 @@ async fn main() -> Result<(), anyhow::Error> { ProjectCommand::Test { path } => { let path = path.clone().unwrap_or_else(|| env::current_dir().unwrap()); let project = Project::from_existing_project(path)?; - Ok(project.test()?) + Ok(project.test().await?) } ProjectCommand::PublishAndCreate { .. } => options.run_command_with_storage().await, }, diff --git a/linera-service/src/project.rs b/linera-service/src/project.rs index 37dc8d0822da..428015b28540 100644 --- a/linera-service/src/project.rs +++ b/linera-service/src/project.rs @@ -12,13 +12,14 @@ use std::{ path::{Path, PathBuf}, process::Command, }; -use tracing::{debug, info}; +use tracing::debug; pub struct Project { root: PathBuf, } const RUNNER_BIN_NAME: &str = "test-runner"; +const RUNNER_BIN_CRATE: &str = "linera-sdk"; impl Project { pub fn new(root: PathBuf) -> Result { @@ -74,15 +75,12 @@ impl Project { Ok(Self { root }) } - pub fn test(&self) -> Result<()> { - if !Self::runner_is_installed()? { - debug!("Linera test runner not found"); - Self::install_test_runner()?; - } + pub async fn test(&self) -> Result<()> { + let runner_path = util::resolve_binary(RUNNER_BIN_NAME, RUNNER_BIN_CRATE).await?; let unit_tests = Command::new("cargo") .env( "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", - Self::runner_path()?.display().to_string().as_str(), + runner_path.display().to_string().as_str(), ) .arg("test") .args(["--target", "wasm32-unknown-unknown"]) @@ -104,10 +102,6 @@ impl Project { Ok(()) } - pub fn runner_is_installed() -> Result { - Ok(Self::runner_path()?.exists()) - } - /// Finds the workspace for a given crate. If the workspace /// does not exist, returns the path of the crate. fn workspace_root(&self) -> Result<&Path> { @@ -130,23 +124,6 @@ impl Project { Ok(self.root.as_path()) } - fn install_test_runner() -> Result<()> { - info!("installing test runner..."); - let cargo_install = Command::new("cargo") - .args(["install", "linera-sdk"]) - .args(["--bin", RUNNER_BIN_NAME]) - .spawn()? - .wait()?; - if !cargo_install.success() { - bail!("failed to install {}", &RUNNER_BIN_NAME) - } - Ok(()) - } - - fn runner_path() -> Result { - util::resolve_cargo_binary(RUNNER_BIN_NAME) - } - fn create_source_directory(project_root: &Path) -> Result { let source_directory = project_root.join("src"); std::fs::create_dir(&source_directory)?; diff --git a/linera-service/src/schema_export.rs b/linera-service/src/schema_export.rs index 58778f032499..7a5bd4a6fb31 100644 --- a/linera-service/src/schema_export.rs +++ b/linera-service/src/schema_export.rs @@ -1,11 +1,9 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use async_trait::async_trait; use linera_base::identifiers::ChainId; use linera_chain::data_types::{BlockProposal, Certificate, HashedValue, LiteCertificate}; -use linera_views::memory::TEST_MEMORY_MAX_STREAM_QUERIES; - -use async_trait::async_trait; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse}, node::{NodeError, NotificationStream, ValidatorNode, ValidatorNodeProvider}, @@ -13,6 +11,8 @@ use linera_core::{ use linera_execution::committee::Committee; use linera_service::{chain_listener::ChainListenerConfig, node_service::NodeService}; use linera_storage::{MemoryStoreClient, WallClock}; +use linera_views::memory::TEST_MEMORY_MAX_STREAM_QUERIES; +use structopt::StructOpt; #[derive(Clone)] struct DummyValidatorNode; @@ -67,7 +67,16 @@ impl ValidatorNodeProvider for DummyValidatorNodeProvider { } } +#[derive(StructOpt)] +#[structopt( + name = "Linera GraphQL schema exporter", + about = "Export the GraphQL schema for the core data in a Linera chain" +)] +struct Options {} + fn main() -> std::io::Result<()> { + let _options = Options::from_args(); + let store = MemoryStoreClient::new(None, TEST_MEMORY_MAX_STREAM_QUERIES, WallClock); let config = ChainListenerConfig { delay_before_ms: 0, diff --git a/linera-service/src/util.rs b/linera-service/src/util.rs index e60c8a5c362d..4a0068d3489c 100644 --- a/linera-service/src/util.rs +++ b/linera-service/src/util.rs @@ -1,19 +1,71 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Result}; use std::path::PathBuf; +use tokio::process::Command; +use tracing::{debug, error}; -pub fn resolve_cargo_binary(bin_name: &'static str) -> Result { - cargo_home().map(|cargo_home| cargo_home.join("bin").join(bin_name)) -} +/// Attempts to resolve the path and test the version of the given binary against our +/// package version. This is meant for binaries of the Linera repository. +pub async fn resolve_binary(name: &'static str, package: &'static str) -> Result { + debug!("Resolving binary {name} based on the current binary path."); + let mut current_binary_path = PathBuf::from( + std::env::args() + .next() + .expect("args should start with the current binary path"), + ); + current_binary_path.pop(); + + #[cfg(any(test, feature = "test"))] + // Test binaries are typically in target/debug/deps while crate binaries are in target/debug + // (same thing for target/release). + if current_binary_path.ends_with("target/debug/deps") + || current_binary_path.ends_with("target/release/deps") + { + current_binary_path.pop(); + } -fn cargo_home() -> Result { - if let Ok(cargo_home) = std::env::var("CARGO_HOME") { - Ok(PathBuf::from(cargo_home)) - } else if let Some(home) = dirs::home_dir() { - Ok(home.join(".cargo")) - } else { - bail!("could not find CARGO_HOME directory, please specify it explicitly") + let path = current_binary_path.join(name); + let version = env!("CARGO_PKG_VERSION"); + if !path.exists() { + error!( + "Cannot find a binary {name} in the directory {}. \ + Consider using `cargo install {package}` or `cargo build -p {package}`", + current_binary_path.display() + ); + bail!("Failed to resolve binary {name}"); } + + // Quick version check. + let version_message = Command::new(&path) + .arg("--version") + .output() + .await + .map_err(|e| { + anyhow!( + "Failed to execute and retrieve version from the binary {name} in directory {}: {e}", + current_binary_path.display()) + })? + .stdout; + let found_version = String::from_utf8_lossy(&version_message) + .trim() + .split(' ') + .last() + .ok_or_else(|| { + anyhow!( + "Passing --version to the binary {name} in directory {} returned an empty result", + current_binary_path.display() + ) + })? + .to_string(); + if version != found_version { + error!("The binary {name} in directory {} should have version {version} (found {found_version}). \ + Consider using `cargo install {package} --version '{version}'` or `cargo build -p {package}`", + current_binary_path.display() + ); + bail!("Incorrect version for binary {name}"); + } + + Ok(path) } diff --git a/linera-service/tests/end_to_end_tests.rs b/linera-service/tests/end_to_end_tests.rs index 2298617605cf..1ba5ff4b93bd 100644 --- a/linera-service/tests/end_to_end_tests.rs +++ b/linera-service/tests/end_to_end_tests.rs @@ -8,10 +8,28 @@ mod common; use common::INTEGRATION_TEST_GUARD; use linera_base::identifiers::ChainId; -use linera_service::cli_wrappers::{Database, LocalNetwork, Network}; +use linera_service::{ + cli_wrappers::{Database, LocalNetwork, Network}, + util, +}; use linera_views::common::get_table_name; use std::time::Duration; +#[tokio::test] +async fn test_resolve_binary() { + util::resolve_binary("linera", env!("CARGO_PKG_NAME")) + .await + .unwrap(); + util::resolve_binary("linera-proxy", env!("CARGO_PKG_NAME")) + .await + .unwrap(); + assert!( + util::resolve_binary("linera-spaceship", env!("CARGO_PKG_NAME")) + .await + .is_err() + ); +} + #[cfg(feature = "rocksdb")] #[test_log::test(tokio::test)] async fn test_rocks_db_end_to_end_reconfiguration_grpc() {