From f4242567520444a4d7eba3e1e57103c7832d3914 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Mon, 25 Dec 2023 07:37:19 +0800 Subject: [PATCH] feat(gcli): embed programs in commands (#3589) --- Cargo.lock | 1 + examples/messager/Cargo.toml | 6 ++ .../messager/src/bin/demo_messager.rs | 52 ++++++------ gcli/Cargo.toml | 1 + gcli/examples/mycli.rs | 2 +- gcli/src/app.rs | 2 +- gcli/src/bin/gcli.rs | 2 +- gcli/src/cmd/claim.rs | 2 +- gcli/src/cmd/create.rs | 2 +- gcli/src/cmd/info.rs | 4 +- gcli/src/cmd/key.rs | 6 +- gcli/src/cmd/login.rs | 2 +- gcli/src/cmd/mod.rs | 50 ++++++++--- gcli/src/cmd/new.rs | 2 +- gcli/src/cmd/program.rs | 42 +++++++-- gcli/src/cmd/reply.rs | 2 +- gcli/src/cmd/send.rs | 2 +- gcli/src/cmd/transfer.rs | 2 +- gcli/src/cmd/update.rs | 2 +- gcli/src/cmd/upload.rs | 23 ++++- gcli/src/embed.rs | 85 +++++++++++++++++++ gcli/src/lib.rs | 7 +- gcli/tests/common/app.rs | 67 +++++++++++++++ gcli/tests/common/env.rs | 14 +-- gcli/tests/common/mod.rs | 1 + gcli/tests/gear.rs | 14 +-- utils/wasm-builder/src/lib.rs | 12 ++- 27 files changed, 329 insertions(+), 78 deletions(-) rename gcli/src/cmd/meta.rs => examples/messager/src/bin/demo_messager.rs (54%) create mode 100644 gcli/src/embed.rs create mode 100644 gcli/tests/common/app.rs diff --git a/Cargo.lock b/Cargo.lock index b013b289e91..12f87345efc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2191,6 +2191,7 @@ dependencies = [ name = "demo-messager" version = "0.1.0" dependencies = [ + "gcli", "gear-wasm-builder", "gstd", ] diff --git a/examples/messager/Cargo.toml b/examples/messager/Cargo.toml index 884b045bec8..678d4d6e0c0 100644 --- a/examples/messager/Cargo.toml +++ b/examples/messager/Cargo.toml @@ -5,8 +5,14 @@ authors.workspace = true license.workspace = true edition.workspace = true +[[bin]] +name = "demo_messager" +path = "src/bin/demo_messager.rs" +required-features = ["gcli"] + [dependencies] gstd.workspace = true +gcli = { workspace = true, optional = true, features = ["embed"] } [build-dependencies] gear-wasm-builder.workspace = true diff --git a/gcli/src/cmd/meta.rs b/examples/messager/src/bin/demo_messager.rs similarity index 54% rename from gcli/src/cmd/meta.rs rename to examples/messager/src/bin/demo_messager.rs index b3dddfed8b5..6a70ab3bdfc 100644 --- a/gcli/src/cmd/meta.rs +++ b/examples/messager/src/bin/demo_messager.rs @@ -15,38 +15,42 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#![cfg(feature = "gcli")] -//! command `meta` -use crate::{metadata::Metadata, result::Result}; -use clap::Parser; -use std::{fs, path::PathBuf}; +use gcli::{ + anyhow, async_trait, + clap::{self, Parser}, + cmd::Upload, + color_eyre, tokio, App, +}; -/// Show metadata structure, read types from registry, etc. #[derive(Debug, Parser)] -pub enum Action { - /// Display the structure of the metadata. - Display, +pub enum Command { + Upload(Upload), } -/// Show metadata structure, read types from registry, etc. #[derive(Debug, Parser)] -pub struct Meta { - /// Path of "*.meta.wasm". - pub metadata: PathBuf, - #[command(subcommand)] - pub action: Action, +pub struct Messager { + #[clap(subcommand)] + command: Command, } -impl Meta { - /// Run command meta. - pub fn exec(&self) -> Result<()> { - let wasm = fs::read(&self.metadata)?; - let meta = Metadata::of(&wasm)?; +#[async_trait] +impl App for Messager { + async fn exec(&self) -> anyhow::Result<()> { + let lookup = gcli::lookup!(); + let signer = self.signer().await?; - match self.action { - Action::Display => println!("{}", format!("{meta:#}").replace('"', "")), - } - - Ok(()) + let Command::Upload(upload) = &self.command; + upload + .clone_with_code_overridden(lookup.opt) + .exec(signer) + .await + .map_err(Into::into) } } + +#[tokio::main] +async fn main() -> color_eyre::Result<()> { + Messager::parse().run().await +} diff --git a/gcli/Cargo.toml b/gcli/Cargo.toml index 3bb0dcb1b35..2db1a7dec7d 100644 --- a/gcli/Cargo.toml +++ b/gcli/Cargo.toml @@ -57,6 +57,7 @@ gsdk = { workspace = true, features = ["testing"] } which.workspace = true [features] +embed = [] node-key = [ "libp2p" ] [package.metadata.docs.rs] diff --git a/gcli/examples/mycli.rs b/gcli/examples/mycli.rs index 9b049342ae2..39c45f6019b 100644 --- a/gcli/examples/mycli.rs +++ b/gcli/examples/mycli.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use gcli::{async_trait, App, Command, Parser}; +use gcli::{async_trait, clap::Parser, App, Command}; /// My customized sub commands. #[derive(Debug, Parser)] diff --git a/gcli/src/app.rs b/gcli/src/app.rs index 7075d13b86d..a269f9538e1 100644 --- a/gcli/src/app.rs +++ b/gcli/src/app.rs @@ -27,7 +27,7 @@ use gsdk::{signer::Signer, Api}; /// Command line gear program application abstraction. /// /// ```ignore -/// use gcli::{async_trait, App, Command, Parser}; +/// use gcli::{async_trait, App, Command, clap::Parser, color_eyre, anyhow}; /// /// /// My customized sub commands. /// #[derive(Debug, Parser)] diff --git a/gcli/src/bin/gcli.rs b/gcli/src/bin/gcli.rs index 1ed0e8eefb0..8c5b36996cd 100644 --- a/gcli/src/bin/gcli.rs +++ b/gcli/src/bin/gcli.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use gcli::{cmd::Opt, App, Parser}; +use gcli::{clap::Parser, cmd::Opt, App}; #[tokio::main] async fn main() -> color_eyre::Result<()> { diff --git a/gcli/src/cmd/claim.rs b/gcli/src/cmd/claim.rs index 730cbcff38d..75c55f887f7 100644 --- a/gcli/src/cmd/claim.rs +++ b/gcli/src/cmd/claim.rs @@ -22,7 +22,7 @@ use clap::Parser; use gsdk::signer::Signer; /// Claim value from mailbox. -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] pub struct Claim { /// Claim value from. message_id: String, diff --git a/gcli/src/cmd/create.rs b/gcli/src/cmd/create.rs index 0f30db2b585..72c1eabc5bf 100644 --- a/gcli/src/cmd/create.rs +++ b/gcli/src/cmd/create.rs @@ -22,7 +22,7 @@ use clap::Parser; use gsdk::signer::Signer; /// Deploy program to gear node -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] pub struct Create { /// gear program code id code_id: String, diff --git a/gcli/src/cmd/info.rs b/gcli/src/cmd/info.rs index 59776d958ba..ef8b707f508 100644 --- a/gcli/src/cmd/info.rs +++ b/gcli/src/cmd/info.rs @@ -31,7 +31,7 @@ use gsdk::{ }; use std::fmt; -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub enum Action { /// Get balance info of the current account Balance, @@ -44,7 +44,7 @@ pub enum Action { } /// Get account info from ss58address. -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct Info { /// Info of this address, if none, will use the logged in account. pub address: Option, diff --git a/gcli/src/cmd/key.rs b/gcli/src/cmd/key.rs index 68c1bf3bc00..f4ea4d5f715 100644 --- a/gcli/src/cmd/key.rs +++ b/gcli/src/cmd/key.rs @@ -26,7 +26,7 @@ use gsdk::ext::{ use std::{fmt::Display, result::Result as StdResult, str::FromStr}; /// Cryptography scheme -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub enum Scheme { Ecdsa, Ed25519, @@ -45,7 +45,7 @@ impl FromStr for Scheme { } } -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub enum Action { /// Generate a random account Generate, @@ -88,7 +88,7 @@ pub enum Action { } /// Keypair utils -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct Key { /// Cryptography scheme #[arg(short, long, default_value = "sr25519")] diff --git a/gcli/src/cmd/login.rs b/gcli/src/cmd/login.rs index f36c9c999d0..a5ab026810d 100644 --- a/gcli/src/cmd/login.rs +++ b/gcli/src/cmd/login.rs @@ -22,7 +22,7 @@ use clap::Parser; use std::path::PathBuf; /// Log in to account -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct Login { /// The default keystore path is ~/.gear/keystore and ~/.gear/keystore.json /// diff --git a/gcli/src/cmd/mod.rs b/gcli/src/cmd/mod.rs index 29ea7ef4c41..4815ae63735 100644 --- a/gcli/src/cmd/mod.rs +++ b/gcli/src/cmd/mod.rs @@ -33,22 +33,27 @@ pub mod transfer; pub mod update; pub mod upload; +pub use self::{ + claim::Claim, create::Create, info::Info, key::Key, login::Login, new::New, program::Program, + reply::Reply, send::Send, transfer::Transfer, update::Update, upload::Upload, +}; + /// All SubCommands of gear command line interface. -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub enum Command { - Claim(claim::Claim), - Create(create::Create), - Info(info::Info), - Key(key::Key), - Login(login::Login), - New(new::New), + Claim(Claim), + Create(Create), + Info(Info), + Key(Key), + Login(Login), + New(New), #[clap(subcommand)] - Program(program::Program), - Reply(reply::Reply), - Send(send::Send), - Upload(upload::Upload), - Transfer(transfer::Transfer), - Update(update::Update), + Program(Program), + Reply(Reply), + Send(Send), + Upload(Upload), + Transfer(Transfer), + Update(Update), } impl Command { @@ -71,6 +76,25 @@ impl Command { Ok(()) } + + #[cfg(feature = "embed")] + pub async fn exec_embedded( + &self, + app: &impl App, + artifact: crate::embed::Artifact, + ) -> anyhow::Result<()> { + let this = match self { + Command::Upload(upload) => { + Command::Upload(upload.clone_with_code_overridden(artifact.opt)) + } + Command::Program(program) => { + Command::Program(program.clone_with_meta_overridden(artifact.meta)) + } + _ => self.clone(), + }; + + Self::exec(&this, app).await + } } /// Gear command-line interface. diff --git a/gcli/src/cmd/new.rs b/gcli/src/cmd/new.rs index 3698b2d6a32..524e4724c62 100644 --- a/gcli/src/cmd/new.rs +++ b/gcli/src/cmd/new.rs @@ -21,7 +21,7 @@ use crate::{result::Result, template}; use clap::Parser; /// Create a new gear program -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct New { /// Create gear program from templates. #[arg(short, long, default_value = "dapp-template")] diff --git a/gcli/src/cmd/program.rs b/gcli/src/cmd/program.rs index e6c7bdaba78..f533d1f92dc 100644 --- a/gcli/src/cmd/program.rs +++ b/gcli/src/cmd/program.rs @@ -23,7 +23,7 @@ use gsdk::{ext::sp_core::H256, Api}; use std::{fs, path::PathBuf}; /// Read program state, etc. -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub enum Program { /// Display metadata of the program. /// @@ -33,7 +33,11 @@ pub enum Program { /// /// - "*.meta.txt" describes the metadata of the program /// - "*.meta.wasm" describes the wasm exports of the program + #[cfg_attr(feature = "embed", clap(skip))] meta: PathBuf, + /// Overridden metadata binary if feature embed is enabled. + #[clap(skip)] + meta_override: Vec, /// Derive the description of the specified type from registry. #[arg(short, long)] derive: Option, @@ -60,6 +64,15 @@ pub enum Program { } impl Program { + /// Clone self with metadata overridden. + pub fn clone_with_meta_overridden(&self, meta: Vec) -> Self { + let mut overridden = self.clone(); + if let Program::Meta { meta_override, .. } = &mut overridden { + *meta_override = meta; + }; + overridden + } + /// Run command program. pub async fn exec(&self, app: &impl App) -> Result<()> { match self { @@ -79,7 +92,19 @@ impl Program { Self::full_state(api, *pid, *at).await?; } } - Program::Meta { meta, derive } => Self::meta(meta, derive)?, + Program::Meta { + meta, + derive, + meta_override, + } => { + let meta = if meta_override.is_empty() { + Self::resolve_meta(meta) + } else { + Meta::decode_wasm(meta_override) + }?; + + Self::meta(meta, derive)? + } } Ok(()) @@ -96,17 +121,17 @@ impl Program { let state = api .read_state_using_wasm(pid, Default::default(), method, wasm, args, at) .await?; - println!("{}", state); + println!("{state}"); Ok(()) } async fn full_state(api: Api, pid: H256, at: Option) -> Result<()> { let state = api.read_state(pid, Default::default(), at).await?; - println!("{}", state); + println!("{state}"); Ok(()) } - fn meta(path: &PathBuf, name: &Option) -> Result<()> { + fn resolve_meta(path: &PathBuf) -> Result { let ext = path .extension() .ok_or_else(|| anyhow::anyhow!("Invalid file extension"))?; @@ -122,11 +147,14 @@ impl Program { return Err(anyhow::anyhow!(format!("Unsupported file extension {:?}", ext)).into()); }; - // Format types. + Ok(meta) + } + + fn meta(meta: Meta, name: &Option) -> Result<()> { let fmt = if let Some(name) = name { format!("{:#}", meta.derive(name)?) } else { - format!("{:#}", meta) + format!("{meta:#}") }; // println result. diff --git a/gcli/src/cmd/reply.rs b/gcli/src/cmd/reply.rs index ada227ba901..a20b08862e7 100644 --- a/gcli/src/cmd/reply.rs +++ b/gcli/src/cmd/reply.rs @@ -33,7 +33,7 @@ use gsdk::signer::Signer; /// - `value`: balance to be transferred to the program once it's been created. /// /// - `DispatchMessageEnqueued(H256)` when dispatch message is placed in the queue. -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] pub struct Reply { /// Reply to reply_to_id: String, diff --git a/gcli/src/cmd/send.rs b/gcli/src/cmd/send.rs index a72e3bb7375..7ead01046b7 100644 --- a/gcli/src/cmd/send.rs +++ b/gcli/src/cmd/send.rs @@ -42,7 +42,7 @@ use gsdk::{ /// /// Emits the following events: /// - `DispatchMessageEnqueued(MessageInfo)` when dispatch message is placed in the queue. -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] pub struct Send { /// Send to pub destination: String, diff --git a/gcli/src/cmd/transfer.rs b/gcli/src/cmd/transfer.rs index ff0f4feb956..baf802d2e1d 100644 --- a/gcli/src/cmd/transfer.rs +++ b/gcli/src/cmd/transfer.rs @@ -32,7 +32,7 @@ use gsdk::{ /// [the staging testnet][0], and the decimals of 1 UNIT is 12 by default. /// /// [0]: https://github.com/gear-tech/gear/blob/c01d0390cdf1031cb4eba940d0199d787ea480e0/node/src/chain_spec.rs#L218 -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct Transfer { /// Transfer to (ss58address). destination: String, diff --git a/gcli/src/cmd/update.rs b/gcli/src/cmd/update.rs index cf9af5835ab..e7b06f8bd2b 100644 --- a/gcli/src/cmd/update.rs +++ b/gcli/src/cmd/update.rs @@ -24,7 +24,7 @@ use std::process::{self, Command}; const REPO: &str = "https://github.com/gear-tech/gear-program"; /// Update self from crates.io or github -#[derive(Debug, Parser)] +#[derive(Clone, Debug, Parser)] pub struct Update { /// Force update self from #[arg(short, long)] diff --git a/gcli/src/cmd/upload.rs b/gcli/src/cmd/upload.rs index 27f8073e362..cd47fd5677b 100644 --- a/gcli/src/cmd/upload.rs +++ b/gcli/src/cmd/upload.rs @@ -28,10 +28,14 @@ use gsdk::{ use std::{fs, path::PathBuf}; /// Deploy program to gear node or save program `code` in storage. -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] pub struct Upload { - /// gear program code <*.wasm> + /// Gear program code <*.wasm>. + #[cfg_attr(feature = "embed", clap(skip))] code: PathBuf, + /// Overridden code if feature embed is enabled. + #[clap(skip)] + code_override: Vec, /// Save program `code` in storage only. #[arg(short, long)] code_only: bool, @@ -50,10 +54,21 @@ pub struct Upload { } impl Upload { + /// Clone self with code overridden. + pub fn clone_with_code_overridden(&self, code: Vec) -> Self { + let mut overridden = self.clone(); + overridden.code_override = code; + overridden + } + /// Exec command submit pub async fn exec(&self, signer: Signer) -> Result<()> { - let code = - fs::read(&self.code).map_err(|e| anyhow!("program {:?} not found, {e}", &self.code))?; + let code = if self.code_override.is_empty() { + fs::read(&self.code).map_err(|e| anyhow!("program {:?} not found, {e}", &self.code))? + } else { + self.code_override.clone() + }; + if self.code_only { signer.calls.upload_code(code).await?; return Ok(()); diff --git a/gcli/src/embed.rs b/gcli/src/embed.rs new file mode 100644 index 00000000000..17b9be6e8d5 --- /dev/null +++ b/gcli/src/embed.rs @@ -0,0 +1,85 @@ +// This file is part of Gear. +// +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// 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 . + +//! Utils for embedded commands. +#![cfg(feature = "embed")] + +use std::{fs, path::PathBuf}; + +/// This macro is used to lookup the artifact from the `OUT_DIR`, +/// it's just a wrapper around [`Artifact::from_out_dir`] for +/// avoiding importing [`Artifact`] in users' code. +/// +/// NOTE: This macro should only be used in external crates. +#[macro_export] +macro_rules! lookup { + () => {{ + if let Some(artifact) = ::gcli::embed::Artifact::from_out_dir(env!("OUT_DIR")) { + artifact + } else { + ::gcli::log::warn!("No artifact is found from the out_dir"); + Default::default() + } + }}; +} + +/// The length of the suffix of the output folder. +/// +/// Example: `[gcli]-1234567890abcdef` +const OUT_SUFFIX_LENGTH: usize = 17; + +/// Program binaries for embedded commands. +#[derive(Debug, Default)] +pub struct Artifact { + /// The optitmized WASM binary. + pub opt: Vec, + + /// The metadata WASM binary if any. + pub meta: Vec, +} + +impl Artifact { + /// Parse the artifact from the `OUT_DIR` + /// environment variable. + pub fn from_out_dir(out: &str) -> Option { + let out_dir = PathBuf::from(out); + let mut ancestors = out_dir.ancestors(); + + let [name, profile, target] = [ + ancestors + .nth(1)? + .file_name()? + .to_str() + .and_then(|name| name.get(..name.len().checked_sub(OUT_SUFFIX_LENGTH)?))?, + if ancestors.nth(1)?.file_name()?.to_str()? == "debug" { + "debug" + } else { + "release" + }, + ancestors.next()?.to_str()?, + ]; + + let bin = PathBuf::from(format!("{target}/wasm32-unknown-unknown/{profile}")); + let stem = PathBuf::from(name.replace('-', "_")); + + Some(Self { + opt: fs::read(bin.join(stem.with_extension("wasm"))).ok()?, + meta: fs::read(bin.join(stem.with_extension("meta.wasm"))).unwrap_or_default(), + }) + } +} diff --git a/gcli/src/lib.rs b/gcli/src/lib.rs index 364fe1e00a8..00901343496 100644 --- a/gcli/src/lib.rs +++ b/gcli/src/lib.rs @@ -124,6 +124,7 @@ mod app; pub mod cmd; +pub mod embed; pub mod keystore; pub mod meta; pub mod result; @@ -131,9 +132,13 @@ pub mod template; pub mod utils; pub use self::{app::App, cmd::Command}; +pub use anyhow; pub use async_trait::async_trait; -pub use clap::Parser; +pub use clap; +pub use color_eyre; pub use gsdk::signer::Signer; +pub use log; +pub use tokio; /// SS58 prefix for vara network. pub const VARA_SS58_PREFIX: u8 = 137; diff --git a/gcli/tests/common/app.rs b/gcli/tests/common/app.rs new file mode 100644 index 00000000000..2521ac26aa3 --- /dev/null +++ b/gcli/tests/common/app.rs @@ -0,0 +1,67 @@ +// This file is part of Gear. +// +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// 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 . + +use crate::common::{ + self, env, + node::{Convert, NodeExec}, + Args, +}; +use anyhow::{anyhow, Result}; +use std::{ + path::PathBuf, + process::{Command, Stdio}, +}; + +fn demo_messager() -> Result { + let path = PathBuf::from(env::bin("demo_messager")); + + if !path.exists() + && !Command::new("cargo") + .args([ + "build", + "-p", + "demo-messager", + &format!("--{}", *env::PROFILE), + "--features", + "gcli", + ]) + .status()? + .success() + { + return Err(anyhow!("Failed to build demo-messager with feature gcli")); + } + + Ok(path) +} + +#[test] +fn embedded_gcli() -> Result<()> { + let node = common::dev()?; + let demo = Command::new(demo_messager()?) + .args(Vec::::from(Args::new("upload").endpoint(node.ws()))) + .stderr(Stdio::piped()) + .output()?; + + let stderr = demo.stderr.convert(); + assert!( + !stderr.contains("Submitted Gear::upload_program"), + "code should be uploaded, but got: {stderr}" + ); + + Ok(()) +} diff --git a/gcli/tests/common/env.rs b/gcli/tests/common/env.rs index 745287a80ee..38455e2845b 100644 --- a/gcli/tests/common/env.rs +++ b/gcli/tests/common/env.rs @@ -25,23 +25,23 @@ const WASM_TARGET: &str = "target/wasm32-unknown-unknown"; lazy_static! { static ref ROOT: String = env!("CARGO_MANIFEST_DIR").to_owned() + "/../"; + pub static ref PROFILE: &'static str = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; } fn bin_path(name: &str, wasm: bool) -> String { ROOT.clone() - + [ + + &[ if wasm { WASM_TARGET } else { TARGET }, "/", - if cfg!(debug_assertions) { - "debug" - } else { - "release" - }, + *PROFILE, "/", name, ] .concat() - .as_str() } /// path of binaries diff --git a/gcli/tests/common/mod.rs b/gcli/tests/common/mod.rs index f387ecb27de..a1699a3b27c 100644 --- a/gcli/tests/common/mod.rs +++ b/gcli/tests/common/mod.rs @@ -33,6 +33,7 @@ use std::{ process::{Command, Output}, }; +mod app; mod args; pub mod env; pub mod node; diff --git a/gcli/tests/gear.rs b/gcli/tests/gear.rs index a815d2d96d5..52635755941 100644 --- a/gcli/tests/gear.rs +++ b/gcli/tests/gear.rs @@ -25,10 +25,14 @@ mod common; #[tokio::test] async fn api_timeout() { - assert!(matches!( - Api::new_with_timeout(None, Some(1)).await.err(), - Some(Error::SubxtRpc(jsonrpsee::core::Error::Transport(..))) - )); + let error = Api::new_with_timeout(None, Some(0)).await.err(); + assert!( + matches!( + error, + Some(Error::SubxtRpc(jsonrpsee::core::Error::Transport(..))) + ), + "Unexpected error occurred: {error:?}" + ); } #[test] @@ -42,7 +46,7 @@ fn paths() { .into_iter() .for_each(|path| { if !PathBuf::from(&path).exists() { - panic!("{} not found.", path) + panic!("{path} not found.") } }) } diff --git a/utils/wasm-builder/src/lib.rs b/utils/wasm-builder/src/lib.rs index 9e7e2e21c91..6562f1cc155 100644 --- a/utils/wasm-builder/src/lib.rs +++ b/utils/wasm-builder/src/lib.rs @@ -166,7 +166,17 @@ impl WasmBuilder { unmatched_features.join(", ") ); } - Ok(matched_features) + + // NOTE: Filter out feature `gcli`. + // + // dependency feature `gcli` could be captured here + // but it is not needed for the build. + // + // TODO: Filter dep features in this function (#3588) + Ok(matched_features + .into_iter() + .filter(|feature| feature != "gcli") + .collect()) } fn paths_to_remap(&self) -> Result> {