From 520c5586264e76a73f3fd578ed3041aa84083957 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Thu, 17 Apr 2025 15:04:40 -0400 Subject: [PATCH 01/10] copy the frontend template files --- Cargo.lock | 1 + crates/loam-cli/Cargo.toml | 3 +- crates/loam-cli/src/commands/init.rs | 56 ++++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed68810c..21c9b082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2395,6 +2395,7 @@ dependencies = [ "stellar-xdr", "strsim", "symlink", + "tempfile", "thiserror", "tokio", "tokio-stream", diff --git a/crates/loam-cli/Cargo.toml b/crates/loam-cli/Cargo.toml index ccd9bd4d..c6cd702f 100644 --- a/crates/loam-cli/Cargo.toml +++ b/crates/loam-cli/Cargo.toml @@ -69,11 +69,12 @@ rust-embed = { version = "8.2.0", features = ["debug-embed"] } regex = "1.10.5" toml_edit = "0.22.16" indexmap = { version = "1.9", features = ["serde"] } +tempfile = "3.8" +fs_extra = "1.3" [dev-dependencies] assert_cmd = "2.0.4" assert_fs = "1.0.7" -fs_extra = "1.3.0" predicates = "3.1.0" walkdir = "2.3" diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index aff1a4db..24431ea7 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -2,13 +2,15 @@ use clap::Parser; use rust_embed::{EmbeddedFile, RustEmbed}; use soroban_cli::commands::contract::init as soroban_init; use std::{ - fs::{self, create_dir_all, metadata, read_to_string, remove_dir_all, write, Metadata}, + fs::{self, create_dir_all, metadata, read_to_string, write, Metadata}, io, - path::{Path, PathBuf}, + path::{Path, PathBuf}, process::Command, }; +use tempfile::TempDir; use toml_edit::{DocumentMut, TomlError}; -const FRONTEND_TEMPLATE: &str = "https://github.com/loambuild/frontend"; + +const FRONTEND_TEMPLATE: &str = "https://github.com/AhaLabs/scaffold-stellar-frontend"; #[derive(RustEmbed)] #[folder = "./src/examples/soroban/core"] @@ -58,16 +60,19 @@ impl Cmd { project_path: self.project_path.to_string_lossy().to_string(), name: self.name.clone(), with_example: None, - frontend_template: Some(FRONTEND_TEMPLATE.to_string()), overwrite: true, + frontend_template: None, } .run(&soroban_cli::commands::global::Args::default())?; - // remove soroban hello_world default contract - remove_dir_all(self.project_path.join("contracts/hello_world/")).map_err(|e| { - eprintln!("Error removing directory"); - e + // Clone frontend template + let fe_template_dir = tempfile::tempdir().map_err(|e| { + eprintln!("Error creating temp dir for frontend template"); + Error::IoError(e) })?; + + clone_repo(FRONTEND_TEMPLATE, fe_template_dir.path())?; + copy_frontend_files(&fe_template_dir, &self.project_path)?; copy_example_contracts(&self.project_path)?; rename_cargo_toml_remove(&self.project_path, "core")?; @@ -175,3 +180,38 @@ fn rename_cargo_toml_remove(project: &Path, name: &str) -> Result<(), Error> { fs::rename(from, to)?; Ok(()) } + + +fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { + let status = Command::new("git") + .args(["clone", repo_url, dest.to_str().unwrap()]) + .status() + .map_err(|e| { + eprintln!("Error executing git clone"); + Error::IoError(e) + })?; + + if !status.success() { + return Err(Error::IoError(io::Error::new( + io::ErrorKind::Other, + "Failed to clone repository", + ))); + } + Ok(()) +} + +fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Error> { + fs_extra::dir::copy( + temp_dir.path(), + &project_path, + &fs_extra::dir::CopyOptions::new() + .content_only(true) + .overwrite(true), + ) + .map_err(|e| { + eprintln!("Error copying frontend files"); + Error::IoError(io::Error::new(io::ErrorKind::Other, e.to_string())) + })?; + + Ok(()) +} \ No newline at end of file From 7b807bcbe55c7b49d9aa3f48ad55f68241c34e38 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Thu, 17 Apr 2025 15:07:46 -0400 Subject: [PATCH 02/10] loam frontend --- crates/loam-cli/src/commands/init.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index 24431ea7..06b3ec7e 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -10,7 +10,7 @@ use tempfile::TempDir; use toml_edit::{DocumentMut, TomlError}; -const FRONTEND_TEMPLATE: &str = "https://github.com/AhaLabs/scaffold-stellar-frontend"; +const FRONTEND_TEMPLATE: &str = "https://github.com/loambuild/frontend"; #[derive(RustEmbed)] #[folder = "./src/examples/soroban/core"] @@ -165,7 +165,6 @@ fn copy_file( Ok(()) } -// TODO: import from stellar-cli init (not currently pub there) fn file_exists(file_path: &Path) -> bool { metadata(file_path) .as_ref() From f6729c3cbac2a66108ba890ea87b5ebcdaf7a76d Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Thu, 17 Apr 2025 15:14:36 -0400 Subject: [PATCH 03/10] add unit test for init command --- crates/loam-cli/tests/it/unit.rs | 28 ++++++++++++++++++++++++++++ crates/loam-cli/tests/it/util.rs | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/crates/loam-cli/tests/it/unit.rs b/crates/loam-cli/tests/it/unit.rs index 01ba7d5a..b25e2c69 100644 --- a/crates/loam-cli/tests/it/unit.rs +++ b/crates/loam-cli/tests/it/unit.rs @@ -73,3 +73,31 @@ soroban_token_contract.client = false assert!(stderr.contains("🌐 using network at http://localhost:8000/rpc\n")); }); } + +#[test] +fn init_copies_contracts_and_frontend_template() { + let env = TestEnv::new_empty(); + + // Run loam init with project path + let project_path = env.cwd.join("my-project"); + env.loam("init") + .args([project_path.to_str().unwrap()]) + .assert() + .success(); + // Verify contract files exist + assert!(project_path.join("contracts/core/src/lib.rs").exists()); + assert!(project_path.join("contracts/status_message/src/lib.rs").exists()); + assert!(project_path.join("contracts/core/Cargo.toml").exists()); + assert!(project_path.join("contracts/status_message/Cargo.toml").exists()); + + // Verify frontend template files exist + assert!(project_path.join("package.json").exists()); + assert!(project_path.join("src").exists()); + assert!(project_path.join("tsconfig.json").exists()); + + // Verify Cargo.toml contains loam dependencies + let cargo_toml = std::fs::read_to_string(project_path.join("Cargo.toml")) + .expect("Should be able to read Cargo.toml"); + assert!(cargo_toml.contains("loam-sdk")); + assert!(cargo_toml.contains("loam-subcontract-core")); +} \ No newline at end of file diff --git a/crates/loam-cli/tests/it/util.rs b/crates/loam-cli/tests/it/util.rs index e6c97d47..6e39fda2 100644 --- a/crates/loam-cli/tests/it/util.rs +++ b/crates/loam-cli/tests/it/util.rs @@ -53,6 +53,14 @@ impl TestEnv { } } + pub fn new_empty() -> Self { + let temp_dir = TempDir::new().unwrap(); + Self { + cwd: temp_dir.path().to_path_buf(), + temp_dir, + } + } + pub fn from(template: &str, f: F) { let test_env = TestEnv::new(template); f(&test_env); From c0ebeef2e32ba0b649d6d2a4e385d0546ae5b708 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Thu, 17 Apr 2025 15:15:38 -0400 Subject: [PATCH 04/10] clippy + fmt --- crates/loam-cli/src/commands/init.rs | 11 +++++------ crates/loam-cli/tests/it/unit.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index 06b3ec7e..8122b403 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -4,12 +4,12 @@ use soroban_cli::commands::contract::init as soroban_init; use std::{ fs::{self, create_dir_all, metadata, read_to_string, write, Metadata}, io, - path::{Path, PathBuf}, process::Command, + path::{Path, PathBuf}, + process::Command, }; use tempfile::TempDir; use toml_edit::{DocumentMut, TomlError}; - const FRONTEND_TEMPLATE: &str = "https://github.com/loambuild/frontend"; #[derive(RustEmbed)] @@ -70,7 +70,7 @@ impl Cmd { eprintln!("Error creating temp dir for frontend template"); Error::IoError(e) })?; - + clone_repo(FRONTEND_TEMPLATE, fe_template_dir.path())?; copy_frontend_files(&fe_template_dir, &self.project_path)?; @@ -180,7 +180,6 @@ fn rename_cargo_toml_remove(project: &Path, name: &str) -> Result<(), Error> { Ok(()) } - fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { let status = Command::new("git") .args(["clone", repo_url, dest.to_str().unwrap()]) @@ -202,7 +201,7 @@ fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Error> { fs_extra::dir::copy( temp_dir.path(), - &project_path, + project_path, &fs_extra::dir::CopyOptions::new() .content_only(true) .overwrite(true), @@ -213,4 +212,4 @@ fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Er })?; Ok(()) -} \ No newline at end of file +} diff --git a/crates/loam-cli/tests/it/unit.rs b/crates/loam-cli/tests/it/unit.rs index b25e2c69..4bf569a9 100644 --- a/crates/loam-cli/tests/it/unit.rs +++ b/crates/loam-cli/tests/it/unit.rs @@ -77,7 +77,7 @@ soroban_token_contract.client = false #[test] fn init_copies_contracts_and_frontend_template() { let env = TestEnv::new_empty(); - + // Run loam init with project path let project_path = env.cwd.join("my-project"); env.loam("init") @@ -86,9 +86,13 @@ fn init_copies_contracts_and_frontend_template() { .success(); // Verify contract files exist assert!(project_path.join("contracts/core/src/lib.rs").exists()); - assert!(project_path.join("contracts/status_message/src/lib.rs").exists()); + assert!(project_path + .join("contracts/status_message/src/lib.rs") + .exists()); assert!(project_path.join("contracts/core/Cargo.toml").exists()); - assert!(project_path.join("contracts/status_message/Cargo.toml").exists()); + assert!(project_path + .join("contracts/status_message/Cargo.toml") + .exists()); // Verify frontend template files exist assert!(project_path.join("package.json").exists()); @@ -100,4 +104,4 @@ fn init_copies_contracts_and_frontend_template() { .expect("Should be able to read Cargo.toml"); assert!(cargo_toml.contains("loam-sdk")); assert!(cargo_toml.contains("loam-subcontract-core")); -} \ No newline at end of file +} From dbc4b1626a81dff537c530198e56c6b839a3ace6 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 11:51:49 -0400 Subject: [PATCH 05/10] specific error types --- crates/loam-cli/src/commands/init.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index 8122b403..312ac5d7 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -40,6 +40,10 @@ pub enum Error { ConverBytesToStringErr(#[from] std::str::Utf8Error), #[error("Failed to parse toml file: {0}")] TomlParseError(#[from] TomlError), + #[error("Failed to copy frontend files: {0}")] + FrontendCopyError(String), + #[error("Git clone failed: {0}")] + GitCloneError(String), } impl Cmd { @@ -186,14 +190,11 @@ fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { .status() .map_err(|e| { eprintln!("Error executing git clone"); - Error::IoError(e) + Error::GitCloneError(format!("Failed to execute git clone: {e}")) })?; if !status.success() { - return Err(Error::IoError(io::Error::new( - io::ErrorKind::Other, - "Failed to clone repository", - ))); + return Err(Error::GitCloneError("Git clone command failed".to_string())); } Ok(()) } @@ -208,7 +209,7 @@ fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Er ) .map_err(|e| { eprintln!("Error copying frontend files"); - Error::IoError(io::Error::new(io::ErrorKind::Other, e.to_string())) + Error::FrontendCopyError(e.to_string()) })?; Ok(()) From 84828a449adefa05f523368dac9556ab2039ec38 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 12:16:06 -0400 Subject: [PATCH 06/10] add libdbus dep --- .github/workflows/rust.yml | 4 ++++ .github/workflows/tests.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f765d0a4..323f59a4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,6 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libudev-dev libdbus-1-dev - uses: actions/cache@v3 with: path: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c424d391..a71f3312 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,10 @@ jobs: --health-retries 50 steps: - uses: actions/checkout@v3 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libudev-dev libdbus-1-dev - uses: actions/cache@v3 with: path: | From 7a332a472d44c7095a8d81270340d923630ba420 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 14:08:42 -0400 Subject: [PATCH 07/10] fix `just build` command --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index fb7d806b..d78299ac 100644 --- a/justfile +++ b/justfile @@ -11,7 +11,7 @@ path: just --list loam +args: - @cargo r -- {{args}} + @cargo r {{args}} s +args: @stellar {{args}} From 0590ad360d240bb1a0a9bbcf4943234783133a07 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 14:32:17 -0400 Subject: [PATCH 08/10] remove eprint statemetns and clippy fixes and just build fix --- crates/loam-cli/src/commands/build/env_toml.rs | 2 +- crates/loam-cli/src/commands/init.rs | 2 -- crates/loam-cli/src/commands/mod.rs | 2 +- justfile | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/loam-cli/src/commands/build/env_toml.rs b/crates/loam-cli/src/commands/build/env_toml.rs index 440db62d..1cf9ae0a 100644 --- a/crates/loam-cli/src/commands/build/env_toml.rs +++ b/crates/loam-cli/src/commands/build/env_toml.rs @@ -136,7 +136,7 @@ impl Environment { let current_env = parsed_toml.remove(loam_env); if current_env.is_none() { return Err(Error::NoSettingsForCurrentEnv(loam_env.to_string())); - }; + } Ok(current_env) } } diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index 312ac5d7..70b49933 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -189,7 +189,6 @@ fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { .args(["clone", repo_url, dest.to_str().unwrap()]) .status() .map_err(|e| { - eprintln!("Error executing git clone"); Error::GitCloneError(format!("Failed to execute git clone: {e}")) })?; @@ -208,7 +207,6 @@ fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Er .overwrite(true), ) .map_err(|e| { - eprintln!("Error copying frontend files"); Error::FrontendCopyError(e.to_string()) })?; diff --git a/crates/loam-cli/src/commands/mod.rs b/crates/loam-cli/src/commands/mod.rs index 72c664aa..63cb024c 100644 --- a/crates/loam-cli/src/commands/mod.rs +++ b/crates/loam-cli/src/commands/mod.rs @@ -43,7 +43,7 @@ impl Root { Cmd::Build(build_info) => build_info.run().await?, Cmd::UpdateEnv(e) => e.run()?, Cmd::Dev(dev_info) => dev_info.run().await?, - }; + } Ok(()) } } diff --git a/justfile b/justfile index d78299ac..0c1fc695 100644 --- a/justfile +++ b/justfile @@ -42,8 +42,8 @@ test-integration: build-cli-test-contracts create: build rm -rf .soroban - stellar keys generate default - just stellar contract deploy --wasm ./target/loam/example_core.wasm --alias core + -stellar keys generate default + just stellar contract deploy --wasm ./target/loam/example_core.wasm --alias core --source-account default -- --admin default # # Builds contracts. Deploys core subcontract and then redeploys to status message. From 897b0e32799b920e1a9bfab6f76905e3cbba8dca Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 14:34:46 -0400 Subject: [PATCH 09/10] fmt --- crates/loam-cli/src/commands/init.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/loam-cli/src/commands/init.rs b/crates/loam-cli/src/commands/init.rs index 70b49933..3b6b673e 100644 --- a/crates/loam-cli/src/commands/init.rs +++ b/crates/loam-cli/src/commands/init.rs @@ -188,9 +188,7 @@ fn clone_repo(repo_url: &str, dest: &Path) -> Result<(), Error> { let status = Command::new("git") .args(["clone", repo_url, dest.to_str().unwrap()]) .status() - .map_err(|e| { - Error::GitCloneError(format!("Failed to execute git clone: {e}")) - })?; + .map_err(|e| Error::GitCloneError(format!("Failed to execute git clone: {e}")))?; if !status.success() { return Err(Error::GitCloneError("Git clone command failed".to_string())); @@ -206,9 +204,7 @@ fn copy_frontend_files(temp_dir: &TempDir, project_path: &Path) -> Result<(), Er .content_only(true) .overwrite(true), ) - .map_err(|e| { - Error::FrontendCopyError(e.to_string()) - })?; + .map_err(|e| Error::FrontendCopyError(e.to_string()))?; Ok(()) } From 2aebbbffbecac00cc577b05cfe08d0d3cd058654 Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Fri, 18 Apr 2025 14:54:11 -0400 Subject: [PATCH 10/10] clippy --- examples/soroban/simple_account/src/subcontract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/soroban/simple_account/src/subcontract.rs b/examples/soroban/simple_account/src/subcontract.rs index fb3f8696..e2c466ae 100644 --- a/examples/soroban/simple_account/src/subcontract.rs +++ b/examples/soroban/simple_account/src/subcontract.rs @@ -26,7 +26,7 @@ impl IsSimpleAccount for SimpleAccountManager { fn init(&mut self, public_key: BytesN<32>) -> Result<(), Error> { if self.owner.get().is_some() { return Err(Error::OwnerAlreadySet); - }; + } self.owner.set(&public_key); Ok(()) }