Skip to content

Commit

Permalink
Merge branch 'main' into feat/ledger-signing
Browse files Browse the repository at this point in the history
  • Loading branch information
elizabethengelman authored May 1, 2024
2 parents e1631d7 + 1c08651 commit b44a9b4
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 272 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ jobs:
- run: rustup update
- run: cargo fmt --all --check

check-generated-examples-list:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make generate-examples-list
- name: Check no diffs exist
run: git add -N . && git diff HEAD --exit-code

build-and-test:
strategy:
fail-fast: false
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ build-test-wasms:

build-test: build-test-wasms install

generate-examples-list:
curl -sSL https://api.github.com/repos/stellar/soroban-examples/git/trees/main \
| jq -r '.tree[] | select(.type != "blob" and .path != "hello_world" and (.path | startswith(".") | not)) | .path' \
> cmd/soroban-cli/example_contracts.list

test: build-test
cargo test

Expand Down
14 changes: 7 additions & 7 deletions cmd/soroban-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ soroban-ledger-snapshot = { workspace = true }
stellar-strkey = { workspace = true }
soroban-sdk = { workspace = true }
soroban-rpc = { workspace = true }

cargo_toml = "0.20.1"
clap = { workspace = true, features = [
"derive",
"env",
"deprecated",
"string",
"derive",
"env",
"deprecated",
"string",
] }
clap_complete = { workspace = true }
async-trait = { workspace = true }
Expand Down Expand Up @@ -98,8 +98,8 @@ dotenvy = "0.15.7"
strum = "0.17.1"
strum_macros = "0.17.1"
gix = { version = "0.58.0", default-features = false, features = [
"blocking-http-transport-reqwest-rust-tls",
"worktree-mutation",
"blocking-http-transport-reqwest-rust-tls",
"worktree-mutation",
] }
ureq = { version = "2.9.1", features = ["json"] }

Expand Down
121 changes: 0 additions & 121 deletions cmd/soroban-cli/build.rs
Original file line number Diff line number Diff line change
@@ -1,124 +1,3 @@
fn main() {
crate_git_revision::init();
build_helper::set_example_contracts();
}

mod build_helper {
use std::{
fs::{metadata, File, Metadata},
io::{self, Write},
path::{Path, PathBuf},
};

const GITHUB_API_URL: &str =
"https://api.github.com/repos/stellar/soroban-examples/git/trees/main?recursive=1";

pub fn set_example_contracts() {
let example_contracts = get_example_contracts().unwrap();
let w = &mut std::io::stdout();
set_example_contracts_env_var(w, &example_contracts).unwrap();
}

#[derive(serde::Deserialize, Debug)]
struct RepoPath {
path: String,
#[serde(rename = "type")]
type_field: String,
}

#[derive(serde::Deserialize, Debug)]
struct ReqBody {
tree: Vec<RepoPath>,
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Failed to complete get request")]
UreqError(#[from] Box<ureq::Error>),

#[error("Io error: {0}")]
IoError(#[from] std::io::Error),
}

fn get_example_contracts() -> Result<String, Error> {
if file_exists(&cached_example_contracts_file_path()) {
let example_contracts = std::fs::read_to_string(cached_example_contracts_file_path())?;
return Ok(example_contracts);
}

Ok(fetch_and_cache_example_contracts())
}

fn fetch_and_cache_example_contracts() -> String {
let example_contracts = fetch_example_contracts().unwrap().join(",");
let cached_example_contracts = target_dir().join("example_contracts.txt");

if let Err(err) = write_cache(&cached_example_contracts, &example_contracts) {
eprintln!("Error writing cache: {err}");
}

example_contracts
}

fn fetch_example_contracts() -> Result<Vec<String>, Error> {
let body: ReqBody = ureq::get(GITHUB_API_URL)
.call()
.map_err(Box::new)?
.into_json()?;
let mut valid_examples = Vec::new();
for item in body.tree {
if item.type_field == "blob"
|| item.path.starts_with('.')
|| item.path.contains('/')
|| item.path == "hello_world"
{
continue;
}

valid_examples.push(item.path);
}

Ok(valid_examples)
}

fn set_example_contracts_env_var(
w: &mut impl std::io::Write,
example_contracts: &str,
) -> std::io::Result<()> {
writeln!(w, "cargo:rustc-env=EXAMPLE_CONTRACTS={example_contracts}")?;
Ok(())
}

fn cached_example_contracts_file_path() -> PathBuf {
target_dir().join("example_contracts.txt")
}

fn target_dir() -> PathBuf {
project_root().join("target")
}

fn project_root() -> PathBuf {
Path::new(&env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.unwrap()
.to_path_buf()
}

fn write_cache(cache_file_path: &Path, data: &str) -> io::Result<()> {
// Create or open the cache file
let mut file = File::create(cache_file_path)?;

// Write the data to the cache file
file.write_all(data.as_bytes())?;

Ok(())
}

fn file_exists(file_path: &Path) -> bool {
metadata(file_path)
.as_ref()
.map(Metadata::is_file)
.unwrap_or(false)
}
}
23 changes: 23 additions & 0 deletions cmd/soroban-cli/example_contracts.list
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
account
alloc
atomic_multiswap
atomic_swap
auth
cross_contract
custom_types
deep_contract_auth
deployer
errors
eth_abi
events
fuzzing
increment
liquidity_pool
logging
mint-lock
simple_account
single_offer
timelock
token
upgradeable_contract
workspace
117 changes: 90 additions & 27 deletions cmd/soroban-cli/src/commands/contract/init.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
use cargo_toml;
use clap::{
builder::{PossibleValue, PossibleValuesParser, ValueParser},
Parser, ValueEnum,
};
use gix::{clone, create, open, progress, remote};
use rust_embed::RustEmbed;
use serde_json::{from_str, json, to_string_pretty, Error as JsonError, Value as JsonValue};
use std::{
env,
ffi::OsStr,
Expand All @@ -11,15 +19,6 @@ use std::{
str,
sync::atomic::AtomicBool,
};

use clap::{
builder::{PossibleValue, PossibleValuesParser, ValueParser},
Parser, ValueEnum,
};
use gix::{clone, create, open, progress, remote};
use rust_embed::RustEmbed;
use serde_json::{from_str, json, to_string_pretty, Error as JsonError, Value as JsonValue};
use toml_edit::{Document, Formatted, InlineTable, Item, TomlError, Value as TomlValue};
use ureq::get;

const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git";
Expand Down Expand Up @@ -51,7 +50,9 @@ pub struct Cmd {
}

fn possible_example_values() -> ValueParser {
let example_contracts = env!("EXAMPLE_CONTRACTS").split(',').collect::<Vec<&str>>();
let example_contracts = include_str!("../../../example_contracts.list")
.lines()
.collect::<Vec<&str>>();
let parser = PossibleValuesParser::new(example_contracts.iter().map(PossibleValue::new));
parser.into()
}
Expand All @@ -73,7 +74,7 @@ pub enum Error {
CheckoutError(#[from] clone::checkout::main_worktree::Error),

#[error("Failed to parse toml file: {0}")]
TomlParseError(#[from] TomlError),
CargoTomlParseError(#[from] cargo_toml::Error),

#[error("Failed to parse package.json file: {0}")]
JsonParseError(#[from] JsonError),
Expand Down Expand Up @@ -314,27 +315,49 @@ fn copy_example_contracts(from: &Path, to: &Path, contracts: &[String]) -> Resul
Ok(())
}

fn inherit_sdk(mut deps: cargo_toml::DepsSet) -> cargo_toml::DepsSet {
if let Some(sdk_dep) = deps.get("soroban-sdk") {
match sdk_dep {
cargo_toml::Dependency::Simple(_) => {
deps.insert(
"soroban-sdk".to_string(),
cargo_toml::Dependency::Inherited(cargo_toml::InheritedDependencyDetail {
workspace: true,
..Default::default()
}),
);
}

cargo_toml::Dependency::Detailed(details) => {
deps.insert(
"soroban-sdk".to_string(),
cargo_toml::Dependency::Inherited(cargo_toml::InheritedDependencyDetail {
features: details.features.clone(),
optional: details.optional,
workspace: true,
}),
);
}

// we don't need to do anything, it already has `workspace = true`
cargo_toml::Dependency::Inherited(_) => (),
}
}
deps
}

fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> {
let cargo_path = contract_path.join("Cargo.toml");
let cargo_toml_str = read_to_string(&cargo_path).map_err(|e| {
eprint!("Error reading Cargo.toml file in: {contract_path:?}");
e
})?;
let mut doc = cargo_toml_str.parse::<Document>().map_err(|e| {
let mut doc = cargo_toml::Manifest::from_path(cargo_path.clone()).map_err(|e| {
eprintln!("Error parsing Cargo.toml file in: {contract_path:?}");
e
})?;

let mut workspace_table = InlineTable::new();
workspace_table.insert("workspace", TomlValue::Boolean(Formatted::new(true)));

doc["dependencies"]["soroban-sdk"] =
Item::Value(TomlValue::InlineTable(workspace_table.clone()));
doc["dev_dependencies"]["soroban-sdk"] = Item::Value(TomlValue::InlineTable(workspace_table));

doc.remove("profile");
doc.dependencies = inherit_sdk(doc.dependencies);
doc.dev_dependencies = inherit_sdk(doc.dev_dependencies);
doc.profile = cargo_toml::Profiles::default();

write(&cargo_path, doc.to_string()).map_err(|e| {
write(&cargo_path, toml::to_string(&doc).unwrap()).map_err(|e| {
eprintln!("Error writing to Cargo.toml file in: {contract_path:?}");
e
})?;
Expand Down Expand Up @@ -637,8 +660,48 @@ mod tests {
fn assert_contract_cargo_file_uses_workspace(project_dir: &Path, contract_name: &str) {
let contract_dir = project_dir.join("contracts").join(contract_name);
let cargo_toml_path = contract_dir.as_path().join("Cargo.toml");
let cargo_toml_str = read_to_string(cargo_toml_path).unwrap();
assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }"));
let cargo_toml_str = read_to_string(cargo_toml_path.clone()).unwrap();
let doc = cargo_toml_str.parse::<toml_edit::Document>().unwrap();
println!("{cargo_toml_path:?} contents:\n{cargo_toml_str}");
assert!(
doc.get("dependencies")
.unwrap()
.get("soroban-sdk")
.unwrap()
.get("workspace")
.unwrap()
.as_bool()
.unwrap(),
"expected [dependencies.soroban-sdk] to be a workspace dependency"
);
assert!(
doc.get("dev-dependencies")
.unwrap()
.get("soroban-sdk")
.unwrap()
.get("workspace")
.unwrap()
.as_bool()
.unwrap(),
"expected [dev-dependencies.soroban-sdk] to be a workspace dependency"
);
assert_ne!(
0,
doc.get("dev-dependencies")
.unwrap()
.get("soroban-sdk")
.unwrap()
.get("features")
.unwrap()
.as_array()
.unwrap()
.len(),
"expected [dev-dependencies.soroban-sdk] to have a features list"
);
assert!(
doc.get("dev_dependencies").is_none(),
"erroneous 'dev_dependencies' section"
);
}

fn assert_example_contract_excluded_files_do_not_exist(
Expand Down
Loading

0 comments on commit b44a9b4

Please sign in to comment.