From 518bf89f38899d5261785fd23acdd49bdf786bf0 Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Fri, 6 Sep 2024 18:00:15 +0700 Subject: [PATCH 01/11] update comment in aptos_containers --- Cargo.toml | 6 +- src/aptos_container.rs | 507 +++++++++++++++++++++++++++++++---------- src/errors.rs | 4 + src/lib.rs | 1 + src/test_utils.rs | 27 +++ src/utils.rs | 23 ++ 6 files changed, 439 insertions(+), 129 deletions(-) create mode 100644 src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 3cc544c..93e2959 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,15 +12,15 @@ rand = "0.8.5" regex = "1.10.6" serde = { version = "1.0" } testcontainers = { version = "0.21.1" } +tiny-keccak = { version = "2.0.2", features = ["sha3"] } thiserror = { version = "1.0.63" } tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros"] } walkdir = "2.3.3" +hex = "0.4.3" +ed25519-dalek = { version = "2.1.1" } [dev-dependencies] -ed25519-dalek = { version = "2.1.1" } -hex = "0.4.3" test-log = { version = "0.2.16" } -tiny-keccak = { version = "2.0.2", features = ["sha3"] } [features] testing = [] diff --git a/src/aptos_container.rs b/src/aptos_container.rs index 159a11d..eed6767 100644 --- a/src/aptos_container.rs +++ b/src/aptos_container.rs @@ -26,18 +26,46 @@ use crate::errors::AptosContainerError::{CommandFailed, DockerExecFailed}; const MOVE_TOML: &[u8] = include_bytes!("../contract-samples/sample1/Move.toml"); +/// `AptosContainer` is a struct that encapsulates the configuration and runtime details +/// for managing an Aptos node and its associated resources within a Docker container. +/// +/// # Fields +/// +/// * `node_url` - URL for accessing the Aptos node from external systems. +/// +/// * `inner_url` - Internal URL for accessing the Aptos node from within the container +/// or local environment. +/// +/// * `chain_id` - Chain ID for the network. +/// +/// * `deploy_contract` - Flag indicating whether to deploy contracts to the Aptos node. +/// Optional list of account addresses to override default accounts. +/// +/// * `override_accounts` - If set to `true`, contracts will be deployed upon initialization. +/// +/// * `container` - The Docker container instance running the Aptos node or shell. +/// +/// * `contract_path` - Path to the directory where contract files are stored. +/// +/// * `contracts` - A mutex-protected set of contracts. +/// +/// * `accounts` - A read-write lock protecting a list of account addresses. +/// +/// * `accounts_channel_rx` - A mutex-protected optional receiver for account-related +/// communication channels. +/// +/// * `accounts_channel_tx` - A read-write lock protecting an optional sender for account-related +/// communication channels. pub struct AptosContainer { node_url: String, inner_url: String, chain_id: u8, deploy_contract: bool, override_accounts: Option>, - container: ContainerAsync, contract_path: String, contracts: Mutex>, accounts: RwLock>, - accounts_channel_rx: Mutex>>, accounts_channel_tx: RwLock>>, } @@ -50,9 +78,26 @@ const ACCOUNTS_ENV: &str = "ACCOUNTS"; const CONTENT_MAX_CHARS: usize = 120000; // 120 KB impl AptosContainer { + /// Initializes a `AptosContainer`. + /// + /// # Returns + /// A new `AptosContainer` instance. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// } + /// ``` pub async fn init() -> Result { + // Load configuration from environment let config = EnvConfig::new(); let enable_node = config.enable_node.unwrap_or(true); + + // Set up the container's entrypoint, command, and wait condition based on whether the node is enabled. let (entrypoint, cmd, wait_for) = if enable_node { ( "aptos", @@ -63,6 +108,7 @@ impl AptosContainer { ("/bin/sh", vec!["-c", "sleep infinity"], WaitFor::Nothing) }; + // Create and start a new Docker container with the specified image and settings. let container = GenericImage::new(APTOS_IMAGE, APTOS_IMAGE_TAG) .with_exposed_port(8080.tcp()) .with_wait_for(wait_for) @@ -72,6 +118,7 @@ impl AptosContainer { .start() .await?; + // Configure URLs and other parameters based on whether the node is enabled. let (node_url, inner_url, deploy_contract, override_accounts, chain_id) = if enable_node { let node_url = format!( "http://{}:{}", @@ -113,47 +160,16 @@ impl AptosContainer { } impl AptosContainer { + /// Get `node_url` from `AptosContainer` pub fn get_node_url(&self) -> String { self.node_url.clone() } - + /// Get `chain_id` from `AptosContainer` pub fn get_chain_id(&self) -> u8 { self.chain_id } - - pub async fn run( - &self, - number_of_accounts: usize, - callback: impl FnOnce(Vec) -> Pin>>>, - ) -> Result<()> { - self.lazy_init_accounts().await?; - - let accounts = match &self.override_accounts { - Some(accounts) => accounts.clone(), - None => { - // TODO: check received messages size - let mut result = vec![]; - self.accounts_channel_rx - .lock() - .await - .as_mut() - .unwrap() - .recv_many(&mut result, number_of_accounts) - .await; - result - } - }; - - let result = callback(accounts.clone()).await; - if self.override_accounts.is_none() { - let guard = self.accounts_channel_tx.read().await; - for account in accounts { - guard.as_ref().unwrap().send(account).await?; - } - } - result - } - + /// Get `accounts` from `override_accounts` in `AptosContainer` if override_accounts + /// is `Some`. If `None` call to `lazy_init_accounts` to init and return `accounts`. pub async fn get_initiated_accounts(&self) -> Result> { match &self.override_accounts { Some(accounts) => Ok(accounts.clone()), @@ -163,20 +179,154 @@ impl AptosContainer { } } } + /// Generates a random alphanumeric string of the specified length. + /// + /// # Arguments + /// + /// * `length` - The length of the random string to generate. + /// + /// # Returns + /// + /// * `String` - A string of random alphanumeric characters of the specified length. + /// + fn generate_random_string(length: usize) -> String { + // Initialize a random number generator. + let rng = rand::thread_rng(); + // Create an iterator that samples random characters from the Alphanumeric set. + let random_string: String = rng + .sample_iter(&Alphanumeric) + .take(length) + .map(char::from) + .collect(); + random_string + } + + /// Executes a shell command inside the Docker container. + /// + /// # Arguments + /// + /// * `command` - A string representing the shell command to execute inside the container. + /// + /// # Returns + /// + /// * `Result<(String, String)>` - A tuple containing the `stdout` and `stderr` outputs from + /// the command execution. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// let command = "bin/sh -c mkdir my_file".to_string(); + /// let (_, stderr) = aptos_container.run_command(&command).await.unwrap(); + /// println!("stderr: {:?}", stderr) + /// } + /// ``` + pub async fn run_command(&self, command: &str) -> Result<(String, String)> { + // Execute the command inside the container using `/bin/sh -c`. + let mut result = self + .container + .exec(ExecCommand::new(vec!["/bin/sh", "-c", command])) + .await?; + + // Check the exit code of the command. + result + .exit_code() + .await? + .map(|code| Err(Error::new(DockerExecFailed(code)))) + .unwrap_or(Ok(()))?; + // Initialize empty strings for capturing stdout and stderr. + let mut stdout = String::new(); + let mut stderr = String::new(); + + // Read the command's stdout into the `stdout` string. + result.stdout().read_to_string(&mut stdout).await?; + // Read the command's stderr into the `stderr` string. + result.stderr().read_to_string(&mut stderr).await?; + Ok((stdout, stderr)) + } + + /// Recursively retrieves a list of files from directory. + /// + /// # Arguments + /// + /// * `local_dir` - A string slice representing the path to the local directory to search for files. + /// + /// # Returns + /// + /// * `Vec` - A vector of `DirEntry` objects representing the files that match the + /// filtering criteria. + fn get_files(local_dir: &str) -> Vec { + WalkDir::new(local_dir) + .into_iter() + .filter_map(|e| e.ok()) + .filter_map(|entry| { + let source_path = entry.path(); + // Ignore files located in build folders. + if source_path.to_str().unwrap().contains("/build/") { + return None; + } + + // Only consider files, not directories. + if !source_path.is_file() { + return None; + } + // Determine the relative path from the source directory + let relative_path = source_path.strip_prefix(local_dir).unwrap(); + // Compile the regex pattern and check if the relative path matches the pattern. + let re = Regex::new(FILTER_PATTERN).unwrap(); + if re.is_match(relative_path.to_str().unwrap()) { + return None; + } + + // Check file size, excluding files larger than 1 MB. + let metadata = fs::metadata(source_path).unwrap(); + let file_size = metadata.len(); + let file_size_mb = file_size as f64 / (1024.0 * 1024.0); + if file_size_mb > 1_f64 { + return None; + } + // Include the entry if it passes all filters. + Some(entry) + }) + .collect() + } + /// Lazily initializes the accounts if it has been initialized yet. + /// This ensures that accounts are set up either from an external source or + /// from environment variables only once, and avoids redundant initialization. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_containe = AptosContainer::init().await.unwrap(); + /// let accounts = aptos_containe.lazy_init_accounts().await.unwrap(); + /// } + /// ``` pub async fn lazy_init_accounts(&self) -> Result<()> { + // If override accounts are provided, skip initialization and return early. if self.override_accounts.is_some() { return Ok(()); } + // Lock the accounts_channel_tx to check if it's already initialized. let mut guard = self.accounts_channel_tx.write().await; + // If accounts_channel_tx is already initialized, return early. if guard.is_some() { return Ok(()); } + // Prepare to fetch the accounts from the environment variable. let command = format!("echo ${}", ACCOUNTS_ENV); + // Run the command to retrieve the accounts and capture stdout and stderr. let (stdout, stderr) = self.run_command(&command).await?; + // Ensure that the command returned valid output; otherwise, raise an error. ensure!( !stdout.is_empty(), CommandFailed { @@ -184,35 +334,56 @@ impl AptosContainer { stderr: format!("stdout: {} \n\n stderr: {}", stdout, stderr) } ); + + // Parse the stdout into a list of account strings. let accounts = stdout .trim() .split(",") .map(|s| s.to_string()) .collect::>(); + // Create a new mpsc channel with a buffer size equal to the number of accounts. let (tx, rx) = mpsc::channel(accounts.len()); + // Send each account into the channel. for account in accounts.iter() { tx.send(account.to_string()).await? } - + // Lock the accounts field and write the parsed accounts into it. *self.accounts.write().await = accounts; + // Lock the accounts_channel_rx and assign the receiver. *self.accounts_channel_rx.lock().await = Some(rx); - + // Assign the sender to accounts_channel_tx to finalize the initialization. *guard = Some(tx); + // Return success. Ok(()) } + /// Copies contract files from a local directory into the container's filesystem. The files + /// are base64-encoded and transferred in chunks to avoid issues with large files. + /// + /// # Arguments + /// + /// * `local_dir` - A path that refers to the local directory containing the contract files + /// to be copied into the container. + /// + /// # Returns + /// + /// * `Result` - Returns the path where the contracts are copied in the container, + /// or an error if the copying process fails. async fn copy_contracts(&self, local_dir: impl AsRef) -> Result { + // Generate a random destination path by appending a random string to contract_path. let contract_path = Path::new(&self.contract_path).join(AptosContainer::generate_random_string(6)); let contract_path_str = contract_path.to_str().unwrap(); - // clear previous run + // Clear the previous run by removing any existing files at the target path. let command = format!("rm -rf {}", contract_path_str); let (_, stderr) = self.run_command(&command).await?; + // Ensure there are no errors when executing the removal command. ensure!(stderr.is_empty(), CommandFailed { command, stderr }); - // copy files into the container + // Copy files into the container let local_dir_str = local_dir.as_ref().to_str().unwrap(); + // Iterate over each file in the local directory. for entry in AptosContainer::get_files(local_dir_str) { let source_path = entry.path(); let relative_path = source_path.strip_prefix(local_dir_str)?; @@ -237,6 +408,119 @@ impl AptosContainer { Ok(contract_path) } + /// This async function handles account initialization and execution of a callback function with the provided or received accounts. + /// + /// # Parameters: + /// - `number_of_accounts`: The number of accounts required for the operation. + /// - `callback`: A closure that takes the accounts and returns a `Future` wrapped in a `Pin` and boxed as a dynamic trait `Future>`. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// use aptos_testcontainer::utils::get_account_address; + /// use std::collections::HashMap; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_containe = AptosContainer::init().await.unwrap(); + /// let _ = aptos_containe.run(2, |accounts| { + /// Box::pin(async move { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// let accounts = aptos_container.get_initiated_accounts().await.unwrap(); + /// let module_account_private_key = accounts.first().unwrap(); + /// let module_account_address = get_account_address(module_account_private_key); + /// let mut named_addresses = HashMap::new(); + /// named_addresses.insert("verifier_addr".to_string(), module_account_address); + /// aptos_container + /// .upload_contract( + /// "./contract-samples/sample1", + /// module_account_private_key, + /// &named_addresses, + /// None, + /// false, + /// ) + /// .await + /// .unwrap(); + /// Ok(()) + /// }) + /// }); + /// } + /// ``` + pub async fn run( + &self, + number_of_accounts: usize, + callback: impl FnOnce(Vec) -> Pin>>>, + ) -> Result<()> { + // Ensure that accounts are initialized, if not already done. + self.lazy_init_accounts().await?; + + // Determine whether to use overridden accounts or to receive them via the channel. + let accounts = match &self.override_accounts { + // If override_accounts is Some, clone the provided accounts. + Some(accounts) => accounts.clone(), + // Otherwise, receive the accounts from the accounts_channel_rx. + None => { + // TODO: check received messages size + let mut result = vec![]; + // Lock the accounts_channel_rx to ensure exclusive access and receive accounts. + self.accounts_channel_rx + .lock() + .await + .as_mut() + .unwrap() + .recv_many(&mut result, number_of_accounts) + .await; + result + } + }; + + // Invoke the provided callback with the received or overridden accounts. + let result = callback(accounts.clone()).await; + + if self.override_accounts.is_none() { + let guard = self.accounts_channel_tx.read().await; + for account in accounts { + guard.as_ref().unwrap().send(account).await?; + } + } + result + } + + /// Executes a script located within the specified directory. + /// + /// # Parameters + /// - `local_dir`: The directory path containing the scripts to be executed. + /// - `private_key`: The private key of the account that will sign and execute the scripts. + /// - `named_addresses`: A mapping of named addresses used for the script compilation. + /// - `script_paths`: A vector of sub-directory paths within the `local_dir` where the scripts are located. + /// + /// # Example + /// ```rust + /// use std::collections::HashMap; + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// use aptos_testcontainer::utils::get_account_address; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// let accounts = aptos_container.get_initiated_accounts().await.unwrap(); + /// let module_account_private_key = accounts.first().unwrap(); + /// let module_account_address = get_account_address(module_account_private_key); + /// + /// let mut named_addresses = HashMap::new(); + /// named_addresses.insert("verifier_addr".to_string(), module_account_address.clone()); + /// named_addresses.insert("lib_addr".to_string(), module_account_address); + /// aptos_container + /// .run_script( + /// "./contract-samples/sample2", + /// module_account_private_key, + /// &named_addresses, + /// &vec!["verifier"], + /// ) + /// .await + /// .unwrap(); + /// } + /// ``` pub async fn run_script( &self, local_dir: impl AsRef, @@ -244,11 +528,17 @@ impl AptosContainer { named_addresses: &HashMap, script_paths: &Vec<&str>, ) -> Result<()> { + // Start the timer for performance measurement let now = Instant::now(); + + // Copy contract files to the container and get the path let contract_path = self.copy_contracts(local_dir).await?; debug!("copy_contracts takes: {:.2?}", now.elapsed()); + // Convert contract path to a string let contract_path_str = contract_path.to_str().unwrap(); + + // Build named addresses as CLI parameters let named_address_params = named_addresses .iter() .map(|(k, v)| format!("{}={}", k, v)) @@ -256,8 +546,9 @@ impl AptosContainer { .map(|named_addresses| format!("--named-addresses {}", named_addresses)) .unwrap_or("".to_string()); + // Compile and run each script in the provided paths for script_path in script_paths { - // compile script + // Compile script let command = format!( "cd {}/{} && aptos move compile-script --skip-fetch-latest-git-deps {}", contract_path_str, @@ -273,7 +564,7 @@ impl AptosContainer { } ); - // run script + // Run script let command = format!( "cd {}/{} && aptos move run-script --compiled-script-path script.mv --private-key {} --url {} --assume-yes", contract_path_str, script_path, private_key, self.inner_url @@ -290,6 +581,44 @@ impl AptosContainer { Ok(()) } + /// Uploads smart contracts to the Aptos node, optionally overriding existing contracts and + /// handling sub-packages. + /// + /// # Arguments + /// + /// * `local_dir` - The local directory containing the contract files. + /// * `private_key` - The private key used for publishing the contract. + /// * `named_addresses` - A hash map of named addresses for the contracts. + /// * `sub_packages` - Optional list of sub-packages to handle separately. If `None`, the entire + /// contract directory is handled as a whole. + /// * `override_contract` - A boolean flag indicating whether to override existing contracts. + /// + /// # Example + /// ```rust + /// use std::collections::HashMap; + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// use aptos_testcontainer::utils::get_account_address; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// let accounts = aptos_container.get_initiated_accounts().await.unwrap(); + /// let module_account_private_key = accounts.first().unwrap(); + /// let module_account_address = get_account_address(module_account_private_key); + /// let mut named_addresses = HashMap::new(); + /// named_addresses.insert("verifier_addr".to_string(), module_account_address); + /// aptos_container + /// .upload_contract( + /// "./contract-samples/sample1", + /// module_account_private_key, + /// &named_addresses, + /// None, + /// false, + /// ) + /// .await + /// .unwrap(); + /// } + /// ``` pub async fn upload_contract( &self, local_dir: &str, @@ -298,24 +627,29 @@ impl AptosContainer { sub_packages: Option>, override_contract: bool, ) -> Result<()> { + // Skip the upload process if contracts should not be deployed. if !self.deploy_contract { return Ok(()); } + // Compute absolute path and contract key. let absolute = path::absolute(local_dir)?; let absolute_contract_path = absolute.to_str().unwrap(); let contract_key = format!("{}:{}", private_key, absolute_contract_path); + + // Check if the contract has already been uploaded and whether overriding is allowed. let mut inserted_contracts = self.contracts.lock().await; if !override_contract && inserted_contracts.contains(&contract_key) { return Ok(()); } - + // Copy contracts to a new location and log the time taken. let now = Instant::now(); let contract_path = self.copy_contracts(local_dir).await?; debug!("copy_contracts takes: {:.2?}", now.elapsed()); let contract_path_str = contract_path.to_str().unwrap(); + // Override `Move.toml` if no sub-packages are provided.- if sub_packages.is_none() { // Override Move.toml let dest_path = contract_path.join("Move.toml"); @@ -330,7 +664,7 @@ impl AptosContainer { ensure!(stderr.is_empty(), CommandFailed { command, stderr }); } - // run move publish + // Run move publish let named_address_params = named_addresses .iter() .map(|(k, v)| format!("{}={}", k, v)) @@ -370,67 +704,10 @@ impl AptosContainer { } } + // Add the contract key to the set of inserted contracts. inserted_contracts.insert(contract_key); Ok(()) } - - fn get_files(local_dir: &str) -> Vec { - WalkDir::new(local_dir) - .into_iter() - .filter_map(|e| e.ok()) - .filter_map(|entry| { - let source_path = entry.path(); - // ignore build folders - if source_path.to_str().unwrap().contains("/build/") { - return None; - } - if !source_path.is_file() { - return None; - } - // Determine the relative path from the source directory - let relative_path = source_path.strip_prefix(local_dir).unwrap(); - let re = Regex::new(FILTER_PATTERN).unwrap(); - if re.is_match(relative_path.to_str().unwrap()) { - return None; - } - - let metadata = fs::metadata(source_path).unwrap(); - let file_size = metadata.len(); - let file_size_mb = file_size as f64 / (1024.0 * 1024.0); - if file_size_mb > 1_f64 { - return None; - } - Some(entry) - }) - .collect() - } - - pub async fn run_command(&self, command: &str) -> Result<(String, String)> { - let mut result = self - .container - .exec(ExecCommand::new(vec!["/bin/sh", "-c", command])) - .await?; - result - .exit_code() - .await? - .map(|code| Err(Error::new(DockerExecFailed(code)))) - .unwrap_or(Ok(()))?; - let mut stdout = String::new(); - result.stdout().read_to_string(&mut stdout).await?; - let mut stderr = String::new(); - result.stderr().read_to_string(&mut stderr).await?; - Ok((stdout, stderr)) - } - - fn generate_random_string(length: usize) -> String { - let rng = rand::thread_rng(); - let random_string: String = rng - .sample_iter(&Alphanumeric) - .take(length) - .map(char::from) - .collect(); - random_string - } } #[cfg(test)] @@ -438,10 +715,10 @@ impl AptosContainer { mod tests { use log::info; use test_log::test; - use tiny_keccak::Hasher; use super::*; use crate::test_utils::aptos_container_test_utils::{lazy_aptos_container, run}; + use crate::utils::get_account_address; #[test(tokio::test)] async fn run_script_test() { @@ -559,26 +836,4 @@ mod tests { .await .unwrap(); } - - fn get_account_address(private_key: &String) -> String { - let signing_key = ed25519_dalek::SigningKey::try_from( - hex::decode(private_key.trim_start_matches("0x")) - .unwrap() - .as_slice(), - ) - .unwrap(); - let public_key = ed25519_dalek::VerifyingKey::from(&signing_key); - let mut public_key_bytes = public_key.to_bytes().to_vec(); - public_key_bytes.push(0); - let mut sha3 = tiny_keccak::Sha3::v256(); - sha3.update(&public_key_bytes); - - let mut out_bytes = [0; 32]; - sha3.finalize(&mut out_bytes); - let mut public_key = "".to_string(); - for byte in out_bytes.iter() { - public_key = format!("{}{:02x}", public_key, byte); - } - public_key - } } diff --git a/src/errors.rs b/src/errors.rs index 6abdcfe..ecae61a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,14 +1,18 @@ #[derive(thiserror::Error, Debug)] pub enum AptosContainerError { + /// Represents an IO error, wrapping a standard `std::io::Error`. #[error("io error {0}")] IOError(#[from] std::io::Error), + /// Indicates that the file size is too large, providing the file path and its size in MB. #[error("file {path}:{size}MB is too big (max: 1MB)")] FileSizeTooBig { path: String, size: f64 }, + /// Represents an error where a Docker exec command failed, providing the returned code. #[error("docker exec failed with returned code: {0}")] DockerExecFailed(i64), + /// Indicates that a command failed to execute, providing the command and the associated stderr output. #[error("run command {command} failed: {stderr}")] CommandFailed { command: String, stderr: String }, } diff --git a/src/lib.rs b/src/lib.rs index efeaeb1..0a9be54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod aptos_container; mod config; pub mod errors; pub mod test_utils; +pub mod utils; diff --git a/src/test_utils.rs b/src/test_utils.rs index 18c3cda..41467c9 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -11,16 +11,43 @@ pub mod aptos_container_test_utils { static APTOS_CONTAINER: OnceLock>> = OnceLock::new(); + /// Lazily initializes and retrieves an `Arc`. + /// + /// This function checks if an `AptosContainer` has already been initialized and stored in the + /// global `APTOS_CONTAINER`. If the container exists, it upgrades the weak reference to a strong + /// `Arc` and returns it. If not, it creates a new `AptosContainer`, stores a weak + /// reference to it, and returns the strong reference. + /// + /// # Returns + /// + /// * `Result>` - Returns an `Arc` pointing to the initialized `AptosContainer`. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::test_utils::aptos_container_test_utils::lazy_aptos_container; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = lazy_aptos_container().await?; + /// } + /// ``` pub async fn lazy_aptos_container() -> Result> { + // Access the global `APTOS_CONTAINER`, initialize it with a new `Mutex>` + // if it's not already initialized. Lock the `Mutex` to safely access the weak reference. let mut guard = APTOS_CONTAINER .get_or_init(|| Mutex::new(Weak::new())) .lock() .await; + + // Attempt to upgrade the weak reference to a strong `Arc`. let maybe_client = guard.upgrade(); + // If a valid container already exists, return the strong reference. if let Some(client) = maybe_client { Ok(client) } else { + // Otherwise, initialize a new `AptosContainer`, store a weak reference to it, + // and return the strong `Arc`. let client = Arc::new(AptosContainer::init().await?); *guard = Arc::downgrade(&client); diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f8fed35 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,23 @@ +use tiny_keccak::Hasher; + +pub fn get_account_address(private_key: &str) -> String { + let signing_key = ed25519_dalek::SigningKey::try_from( + hex::decode(private_key.trim_start_matches("0x")) + .unwrap() + .as_slice(), + ) + .unwrap(); + let public_key = ed25519_dalek::VerifyingKey::from(&signing_key); + let mut public_key_bytes = public_key.to_bytes().to_vec(); + public_key_bytes.push(0); + let mut sha3 = tiny_keccak::Sha3::v256(); + sha3.update(&public_key_bytes); + + let mut out_bytes = [0; 32]; + sha3.finalize(&mut out_bytes); + let mut public_key = "".to_string(); + for byte in out_bytes.iter() { + public_key = format!("{}{:02x}", public_key, byte); + } + public_key +} From 16f594d2f09e8ba5ea89bfda7a89b04cd388b68e Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:15:08 +0700 Subject: [PATCH 02/11] Update: update after reviewed and rewrite README.md . --- Cargo.toml | 6 +- README.md | 124 ++++++++++++++++++++++++++++++++++++++++- src/aptos_container.rs | 52 +++++++++-------- src/errors.rs | 15 ++++- src/lib.rs | 10 ++++ src/test_utils.rs | 50 +++++++++++++++-- src/utils.rs | 33 +++++++++++ 7 files changed, 254 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93e2959..4a24ee2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,17 +7,17 @@ version = "0.1.0" anyhow = { version = "1.0.71" } base64 = "0.22.1" config = { version = "0.14.0" } +ed25519-dalek = { version = "2.1.1" } +hex = "0.4.3" log = "0.4.22" rand = "0.8.5" regex = "1.10.6" serde = { version = "1.0" } testcontainers = { version = "0.21.1" } -tiny-keccak = { version = "2.0.2", features = ["sha3"] } thiserror = { version = "1.0.63" } +tiny-keccak = { version = "2.0.2", features = ["sha3"] } tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros"] } walkdir = "2.3.3" -hex = "0.4.3" -ed25519-dalek = { version = "2.1.1" } [dev-dependencies] test-log = { version = "0.2.16" } diff --git a/README.md b/README.md index 3c5ee2c..2049f61 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,131 @@ ## Introduction -Test container for Aptos Node +This module provides a simple and smart test container framework for testing interactions with an Aptos node. ## Install +Add this to your `Cargo.toml`: + +```toml +[dependencies] +aptos-testcontainer = { git = "https://github.com/sota-zk-labs/aptos-testcontainer.git" } +``` +## Usage +Here’s how to use the `aptos-testcontainer` in your tests: + +### 1. Initialize the Container + +To start an Aptos node in a container and run tests on it, you can use the `lazy_aptos_container` function. + +```rust +use aptos_testcontainer::test_utils::aptos_container_test_utils::lazy_aptos_container; + +#[tokio::main] +async fn main() { + let aptos_container = lazy_aptos_container.await?; +} +``` + +### 2. Run Tests with Aptos Accounts + +Use the `run` function to run tests with initialized `accounts`. + +And use the `get_account_address` function to convert a given private key to its corresponding account address. +```rust +use aptos_testcontainer::test_utils::aptos_container_test_utils::{lazy_aptos_container, run}; +use aptos_testcontainer::utils::get_account_address; + +#[tokio::main] +async fn main() { + run(2, |accounts| { + Box::pin(async move { + let aptos_container = lazy_aptos_container().await?; + let module_account_private_key = accounts.first().unwrap(); + let module_account_address = get_account_address(module_account_private_key); + Ok(()) + }) + }) + .await + .unwrap(); +} +``` + +### 3. Upload Contracts and Run Scripts + +#### Using to `upload_contract` +```rust +use aptos_testcontainer::test_utils::aptos_container_test_utils::{lazy_aptos_container, run}; +use aptos_testcontainer::utils::get_account_address; + +#[tokio::main] +async fn main() { + run(2, |accounts| { + Box::pin(async move { + let aptos_container = lazy_aptos_container().await?; + let module_account_private_key = accounts.first().unwrap(); + let module_account_address = get_account_address(module_account_private_key); + + // The local directory containing the contract files. + let local_dir = "./contract-samples/sample1"; + + let mut named_addresses = HashMap::new(); + named_addresses.insert("verifier_addr".to_string(), module_account_address); + aptos_container + .upload_contract( + local_dir, + module_account_private_key, + &named_addresses, + None, + false, + ) + .await + .unwrap(); + Ok(()) + }) + }) + .await + .unwrap(); +} +``` + +#### Using to `run_scripts` +```rust +use aptos_testcontainer::test_utils::aptos_container_test_utils::{lazy_aptos_container, run}; +use aptos_testcontainer::utils::get_account_address; + +#[tokio::main] +async fn main() { + run(2, |accounts| { + Box::pin(async move { + let aptos_container = lazy_aptos_container().await?; + let module_account_private_key = accounts.first().unwrap(); + let module_account_address = get_account_address(module_account_private_key); + + // The directory path containing contract code. + let local_dir = "./contract-samples/sample2"; + + let mut named_addresses = HashMap::new(); + named_addresses.insert("verifier_addr".to_string(), module_account_address.clone()); + named_addresses.insert("lib_addr".to_string(), module_account_address); + aptos_container + .run_script( + local_dir, + module_account_private_key, + &named_addresses, + &vec!["verifier"], + ) + .await + .unwrap(); + let node_url = aptos_container.get_node_url(); + info!("node_url = {:#?}", node_url); + Ok(()) + }) + }) + .await + .unwrap(); +} +``` -## How To Use ### Environment Variables diff --git a/src/aptos_container.rs b/src/aptos_container.rs index eed6767..5cf4084 100644 --- a/src/aptos_container.rs +++ b/src/aptos_container.rs @@ -38,10 +38,10 @@ const MOVE_TOML: &[u8] = include_bytes!("../contract-samples/sample1/Move.toml") /// /// * `chain_id` - Chain ID for the network. /// -/// * `deploy_contract` - Flag indicating whether to deploy contracts to the Aptos node. -/// Optional list of account addresses to override default accounts. +/// * `deploy_contract` - If set to `true`, contracts will be deployed upon initialization. /// -/// * `override_accounts` - If set to `true`, contracts will be deployed upon initialization. +/// * `override_accounts` - Flag indicating whether to deploy contracts to the Aptos node. +/// Optional list of account addresses to override default accounts. /// /// * `container` - The Docker container instance running the Aptos node or shell. /// @@ -168,8 +168,23 @@ impl AptosContainer { pub fn get_chain_id(&self) -> u8 { self.chain_id } - /// Get `accounts` from `override_accounts` in `AptosContainer` if override_accounts - /// is `Some`. If `None` call to `lazy_init_accounts` to init and return `accounts`. + + /// Retrieves the list of accounts, either from an overridden source or from an initialized state. + /// + /// # Returns + /// + /// * `Result>` - A vector containing the accounts as `String` if successful. + /// + /// # Example + /// ```rust + /// use aptos_testcontainer::aptos_container::AptosContainer; + /// + /// #[tokio::main] + /// async fn main() { + /// let aptos_container = AptosContainer::init().await.unwrap(); + /// let accounts = aptos_container.get_initiated_accounts().await.unwrap(); + /// } + /// ``` pub async fn get_initiated_accounts(&self) -> Result> { match &self.override_accounts { Some(accounts) => Ok(accounts.clone()), @@ -220,7 +235,8 @@ impl AptosContainer { /// async fn main() { /// let aptos_container = AptosContainer::init().await.unwrap(); /// let command = "bin/sh -c mkdir my_file".to_string(); - /// let (_, stderr) = aptos_container.run_command(&command).await.unwrap(); + /// let (stdout, stderr) = aptos_container.run_command(&command).await.unwrap(); + /// println!("stdout: {:?}", stdout); /// println!("stderr: {:?}", stderr) /// } /// ``` @@ -297,18 +313,7 @@ impl AptosContainer { /// Lazily initializes the accounts if it has been initialized yet. /// This ensures that accounts are set up either from an external source or /// from environment variables only once, and avoids redundant initialization. - /// - /// # Example - /// ```rust - /// use aptos_testcontainer::aptos_container::AptosContainer; - /// - /// #[tokio::main] - /// async fn main() { - /// let aptos_containe = AptosContainer::init().await.unwrap(); - /// let accounts = aptos_containe.lazy_init_accounts().await.unwrap(); - /// } - /// ``` - pub async fn lazy_init_accounts(&self) -> Result<()> { + async fn lazy_init_accounts(&self) -> Result<()> { // If override accounts are provided, skip initialization and return early. if self.override_accounts.is_some() { return Ok(()); @@ -357,8 +362,7 @@ impl AptosContainer { Ok(()) } - /// Copies contract files from a local directory into the container's filesystem. The files - /// are base64-encoded and transferred in chunks to avoid issues with large files. + /// Copies contract files from a local directory into the container's filesystem. /// /// # Arguments /// @@ -489,7 +493,7 @@ impl AptosContainer { /// Executes a script located within the specified directory. /// /// # Parameters - /// - `local_dir`: The directory path containing the scripts to be executed. + /// - `local_dir`: The directory path containing contract code. /// - `private_key`: The private key of the account that will sign and execute the scripts. /// - `named_addresses`: A mapping of named addresses used for the script compilation. /// - `script_paths`: A vector of sub-directory paths within the `local_dir` where the scripts are located. @@ -507,12 +511,14 @@ impl AptosContainer { /// let module_account_private_key = accounts.first().unwrap(); /// let module_account_address = get_account_address(module_account_private_key); /// + /// let local_dir = "./contract-samples/sample2"; + /// /// let mut named_addresses = HashMap::new(); /// named_addresses.insert("verifier_addr".to_string(), module_account_address.clone()); /// named_addresses.insert("lib_addr".to_string(), module_account_address); /// aptos_container /// .run_script( - /// "./contract-samples/sample2", + /// local_dir, /// module_account_private_key, /// &named_addresses, /// &vec!["verifier"], @@ -649,7 +655,7 @@ impl AptosContainer { let contract_path_str = contract_path.to_str().unwrap(); - // Override `Move.toml` if no sub-packages are provided.- + // Override `Move.toml` if no sub-packages are provided. if sub_packages.is_none() { // Override Move.toml let dest_path = contract_path.join("Move.toml"); diff --git a/src/errors.rs b/src/errors.rs index ecae61a..8f6fdea 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,3 +1,4 @@ +/// Enum to represent errors related to operations with the `AptosContainer`. #[derive(thiserror::Error, Debug)] pub enum AptosContainerError { /// Represents an IO error, wrapping a standard `std::io::Error`. @@ -6,7 +7,12 @@ pub enum AptosContainerError { /// Indicates that the file size is too large, providing the file path and its size in MB. #[error("file {path}:{size}MB is too big (max: 1MB)")] - FileSizeTooBig { path: String, size: f64 }, + FileSizeTooBig { + /// The path of the file that is too large. + path: String, + /// The size of the file in megabytes. + size: f64, + }, /// Represents an error where a Docker exec command failed, providing the returned code. #[error("docker exec failed with returned code: {0}")] @@ -14,5 +20,10 @@ pub enum AptosContainerError { /// Indicates that a command failed to execute, providing the command and the associated stderr output. #[error("run command {command} failed: {stderr}")] - CommandFailed { command: String, stderr: String }, + CommandFailed { + /// The command that was executed and failed. + command: String, + /// The standard error output from the command execution. + stderr: String, + }, } diff --git a/src/lib.rs b/src/lib.rs index 0a9be54..16385ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,15 @@ +//! # Overview +//! This module provides a simple and smart test container framework for testing interactions +//! with an Aptos node. The framework is designed to easily initialize and manage a containerized +//! Aptos environment, making it straightforward to write integration tests for Aptos-based +//! applications. + +/// Responsible for handling the logic of an Aptos container. pub mod aptos_container; mod config; +/// Defines error types and custom error handling related to the Aptos container operations. pub mod errors; +/// Provides utilities and helper functions for testing the Aptos container. pub mod test_utils; +/// A helper function used across the test container. pub mod utils; diff --git a/src/test_utils.rs b/src/test_utils.rs index 41467c9..dfe19a6 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,3 +1,4 @@ +/// This module provides utility functions for testing the `AptosContainer` by: #[cfg(feature = "testing")] pub mod aptos_container_test_utils { use std::future::Future; @@ -13,11 +14,6 @@ pub mod aptos_container_test_utils { /// Lazily initializes and retrieves an `Arc`. /// - /// This function checks if an `AptosContainer` has already been initialized and stored in the - /// global `APTOS_CONTAINER`. If the container exists, it upgrades the weak reference to a strong - /// `Arc` and returns it. If not, it creates a new `AptosContainer`, stores a weak - /// reference to it, and returns the strong reference. - /// /// # Returns /// /// * `Result>` - Returns an `Arc` pointing to the initialized `AptosContainer`. @@ -28,7 +24,7 @@ pub mod aptos_container_test_utils { /// /// #[tokio::main] /// async fn main() { - /// let aptos_container = lazy_aptos_container().await?; + /// let aptos_container = lazy_aptos_container().await.unwrap(); /// } /// ``` pub async fn lazy_aptos_container() -> Result> { @@ -55,6 +51,48 @@ pub mod aptos_container_test_utils { } } + /// Asynchronously runs a process that involves managing Aptos accounts using a `AptopsContainer`. + /// + /// # Arguments + /// + /// * `number_of_accounts` - The number of accounts required to run the process. + /// * `runner` - A callback function that takes a vector of accounts (`Vec`) and returns + /// a `Future`. This function defines the operations to be performed + /// once the accounts are initialized or retrieved. + /// + /// # Example + /// ```rust + /// use std::collections::HashMap; + /// use aptos_testcontainer::test_utils::aptos_container_test_utils::{lazy_aptos_container, run}; + /// use aptos_testcontainer::utils::get_account_address; + /// + /// #[tokio::main] + /// async fn main() { + /// run(2, |accounts| { + /// Box::pin(async move { + /// let aptos_container = lazy_aptos_container().await.unwrap(); + /// let module_account_private_key = accounts.first().unwrap(); + /// let module_account_address = get_account_address(module_account_private_key); + /// + /// let mut named_addresses = HashMap::new(); + /// named_addresses.insert("verifier_addr".to_string(), module_account_address.clone()); + /// aptos_container + /// .upload_contract( + /// "./contract-samples/sample1", + /// module_account_private_key, + /// &named_addresses, + /// None, + /// false, + /// ) + /// .await + /// .unwrap(); + /// Ok(()) + /// }) + /// }) + /// .await + /// .unwrap(); + /// } + /// ``` pub async fn run( number_of_accounts: usize, runner: impl FnOnce(Vec) -> Pin>>>, diff --git a/src/utils.rs b/src/utils.rs index f8fed35..ef73e9d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,20 +1,53 @@ use tiny_keccak::Hasher; +/// Converts a given private key to its corresponding account address. +/// +/// # Arguments +/// +/// * `private_key` - A string slice representing the private key in hexadecimal format. +/// +/// # Returns +/// +/// * `String` - The derived account address in hexadecimal format. +/// +/// # Example +/// ```rust +/// use aptos_testcontainer::aptos_container::AptosContainer; +/// use aptos_testcontainer::utils::get_account_address; +/// +/// #[tokio::main] +/// async fn main() { +/// let aptos_container = AptosContainer::init().await.unwrap(); +/// let accounts = aptos_container.get_initiated_accounts().await.unwrap(); +/// let module_account_private_key = accounts.first().unwrap(); +/// let module_account_address = get_account_address(module_account_private_key); +/// } +/// ``` pub fn get_account_address(private_key: &str) -> String { + // Convert the private key from hexadecimal format to bytes, removing the "0x" prefix if present. let signing_key = ed25519_dalek::SigningKey::try_from( hex::decode(private_key.trim_start_matches("0x")) .unwrap() .as_slice(), ) .unwrap(); + + // Derive the public key from the signing key. let public_key = ed25519_dalek::VerifyingKey::from(&signing_key); + + // Prepare public key bytes for hashing by appending a trailing zero. let mut public_key_bytes = public_key.to_bytes().to_vec(); public_key_bytes.push(0); + + // Initialize SHA3-256 hashing algorithm and update it with the public key bytes. let mut sha3 = tiny_keccak::Sha3::v256(); sha3.update(&public_key_bytes); + // Finalize the hash and store it in `out_bytes`. let mut out_bytes = [0; 32]; sha3.finalize(&mut out_bytes); + + // Convert the hashed bytes to a hexadecimal string. let mut public_key = "".to_string(); for byte in out_bytes.iter() { public_key = format!("{}{:02x}", public_key, byte); From 63530f18ef4bc5b633811fc1b5ce813310f59baa Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:58:56 +0700 Subject: [PATCH 03/11] Update: Test coverage ci --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 04d6722..3da6b32 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,6 +66,7 @@ jobs: - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: + threshold: 10 token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true fail_ci_if_error: true From ac83d3a8760459a02bebb75149957735e4c92aba Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:02:42 +0700 Subject: [PATCH 04/11] Update: Test coverage ci part 2 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3da6b32..46d631f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: - threshold: 10 + threshold: 5 token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true fail_ci_if_error: true From 8a52a2f8e733ef3ac6289c47c7a53391c4936581 Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:09:30 +0700 Subject: [PATCH 05/11] Update: Test coverage ci part 3 --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 46d631f..6e4fa28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,6 +66,7 @@ jobs: - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: + target: 80 threshold: 5 token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true From df7729e922bff533926070d5e7a0f1fb6d26ccd7 Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:26:02 +0700 Subject: [PATCH 06/11] Update: final commit --- Cargo.toml | 2 ++ README.md | 7 ------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4a24ee2..cf100b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [package] +description = "Test container for Apotos Node" edition = "2021" +license = "MIT" name = "aptos-testcontainer" version = "0.1.0" diff --git a/README.md b/README.md index 2049f61..e908d08 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,6 @@ This module provides a simple and smart test container framework for testing interactions with an Aptos node. -## Install -Add this to your `Cargo.toml`: - -```toml -[dependencies] -aptos-testcontainer = { git = "https://github.com/sota-zk-labs/aptos-testcontainer.git" } -``` ## Usage Here’s how to use the `aptos-testcontainer` in your tests: From c9caa7cd1fb3f0e72984a04b2879342ae067025e Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:49:34 +0700 Subject: [PATCH 07/11] Update: test ci publish crates.io --- .github/workflows/ci.yaml | 14 ++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6e4fa28..7c0274a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,3 +71,17 @@ jobs: token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true fail_ci_if_error: true + publish-crates: + name: Publish to Crates.io + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + + - name: cargo login + run: | + cargo login ${{secrets.CRATES_IO_TOKEN}} + + - name: cargo publish + run: | + cargo publish --allow-dirty \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index dcafb1b..1c1917a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,7 +98,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "aptos-testcontainer" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index cf100b7..9fe3009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ description = "Test container for Apotos Node" edition = "2021" license = "MIT" name = "aptos-testcontainer" -version = "0.1.0" +repository = "https://github.com/sota-zk-labs/aptos-testcontainer.git" +version = "0.1.1" [dependencies] anyhow = { version = "1.0.71" } From a2d3a4df2fbaba2f5fd44353326df9b14be203ff Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:01:49 +0700 Subject: [PATCH 08/11] Update: test ci publish crates.io part 2 --- .github/workflows/ci.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7c0274a..39400d0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,12 +71,21 @@ jobs: token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true fail_ci_if_error: true + publish-crates: name: Publish to Crates.io runs-on: ubuntu-latest + needs: [check, lints, test-and-coverage] steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - name: Checkout sources + uses: actions/checkout@v4 + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true - name: cargo login run: | From a7652bfe2ed49a4be85d50094c9423c144f33a3b Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:19:02 +0700 Subject: [PATCH 09/11] Update: test ci publish crates.io part 3 --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 39400d0..2cf7a83 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,8 @@ on: push: branches: - master + tags: + - '*' pull_request: jobs: From 29bba68a9b868c2b3dd485870f94b904d473d340 Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:30:06 +0700 Subject: [PATCH 10/11] Update: test ci publish crates.io part 3 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c1917a..c1a3b5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,7 +98,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "aptos-testcontainer" -version = "0.1.1" +version = "0.0.1" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 9fe3009..8baf997 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "aptos-testcontainer" repository = "https://github.com/sota-zk-labs/aptos-testcontainer.git" -version = "0.1.1" +version = "0.0.1" [dependencies] anyhow = { version = "1.0.71" } From ef6692a593d300c6e0a1e64d7d5b7358b9778dd5 Mon Sep 17 00:00:00 2001 From: Tran Anh Dung <103950416+trandung2801@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:34:25 +0700 Subject: [PATCH 11/11] fix: remove publish crates in ci.yaml --- .github/workflows/ci.yaml | 25 ------------------------- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2cf7a83..6e4fa28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,8 +4,6 @@ on: push: branches: - master - tags: - - '*' pull_request: jobs: @@ -73,26 +71,3 @@ jobs: token: ${{secrets.CODECOV_TOKEN}} # not required for public repos verbose: true fail_ci_if_error: true - - publish-crates: - name: Publish to Crates.io - runs-on: ubuntu-latest - needs: [check, lints, test-and-coverage] - steps: - - uses: actions/checkout@v4 - - name: Checkout sources - uses: actions/checkout@v4 - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - - - name: cargo login - run: | - cargo login ${{secrets.CRATES_IO_TOKEN}} - - - name: cargo publish - run: | - cargo publish --allow-dirty \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c1a3b5f..5fd741b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,7 +98,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "aptos-testcontainer" -version = "0.0.1" +version = "0.0.5" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 8baf997..3614e1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "aptos-testcontainer" repository = "https://github.com/sota-zk-labs/aptos-testcontainer.git" -version = "0.0.1" +version = "0.0.5" [dependencies] anyhow = { version = "1.0.71" }