Skip to content

Commit

Permalink
feat(templates): liquid standard templates
Browse files Browse the repository at this point in the history
  • Loading branch information
al3mart committed Jun 28, 2024
1 parent f0e2dfc commit cf68f9f
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 109 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ repository = "https://github.com/r0gue-io/pop-cli"
[workspace.dependencies]
anyhow = "1.0"
assert_cmd = "2.0.14"
cargo-generate = "0.21.1"
dirs = "5.0"
duct = "0.13"
env_logger = "0.11.1"
Expand Down Expand Up @@ -68,4 +69,4 @@ cliclack = "0.3.1"
console = "0.15"
os_info = { version = "3", default-features = false }
strum = "0.26"
strum_macros = "0.26"
strum_macros = "0.26"
12 changes: 2 additions & 10 deletions crates/pop-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,6 @@ predicates.workspace = true

[features]
default = ["contract", "parachain", "telemetry"]
contract = [
"dep:pop-contracts",
"dep:sp-core",
"dep:sp-weights",
"dep:dirs",
]
parachain = [
"dep:pop-parachains",
"dep:dirs",
]
contract = ["dep:pop-contracts", "dep:sp-core", "dep:sp-weights", "dep:dirs"]
parachain = ["dep:pop-parachains", "dep:dirs"]
telemetry = ["dep:pop-telemetry"]
33 changes: 23 additions & 10 deletions crates/pop-cli/src/commands/new/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ use cliclack::{
};
use pop_common::{Git, GitHub, Release};
use pop_parachains::{
instantiate_template_dir, is_initial_endowment_valid, Config, Provider, Template,
instantiate_template_dir, is_decimals_valid, is_initial_endowment_valid, Config, Provider,
Template,
};
use strum::VariantArray;

const DEFAULT_DECIMALS: &str = "12";
const DEFAULT_INITIAL_ENDOWMENT: &str = "1u64 << 60";

#[derive(Args, Clone)]
Expand Down Expand Up @@ -46,8 +48,8 @@ pub struct NewParachainCommand {
pub(crate) release_tag: Option<String>,
#[arg(long, short, help = "Token Symbol", default_value = "UNIT")]
pub(crate) symbol: Option<String>,
#[arg(long, short, help = "Token Decimals", default_value = "12")]
pub(crate) decimals: Option<u8>,
#[arg(long, short, help = "Token Decimals", default_value = DEFAULT_DECIMALS)]
pub(crate) decimals: Option<String>,
#[arg(
long = "endowment",
short,
Expand Down Expand Up @@ -126,7 +128,7 @@ async fn guide_user_to_generate_parachain() -> Result<NewParachainCommand> {

let mut customizable_options = Config {
symbol: "UNIT".to_string(),
decimals: 12,
decimals: "12".to_string(),
initial_endowment: "1u64 << 60".to_string(),
};
if template.matches(&Provider::Pop) {
Expand Down Expand Up @@ -235,7 +237,7 @@ fn display_select_options(provider: &Provider) -> Result<&Template> {
fn get_customization_value(
template: &Template,
symbol: Option<String>,
decimals: Option<u8>,
decimals: Option<String>,
initial_endowment: Option<String>,
) -> Result<Config> {
if !matches!(template, Template::Standard)
Expand Down Expand Up @@ -344,11 +346,22 @@ fn prompt_customizable_options() -> Result<Config> {
.default_input("UNIT")
.interact()?;

let decimals_input: String = input("How many token decimals?")
let mut decimals: String = input("How many token decimals?")
.placeholder("12")
.default_input("12")
.interact()?;
let decimals: u8 = decimals_input.parse::<u8>().expect("input has to be a number");
if !is_decimals_valid(&decimals) {
outro_cancel("⚠️ The specified decimals is not valid, input has to be numeric")?;
//Prompt the user if want to use the one by default
if !confirm(format!("📦 Would you like to use the default {}?", DEFAULT_DECIMALS))
.initial_value(true)
.interact()?
{
outro_cancel("🚫 Cannot create a parachain with an incorrect decimals value.")?;
return Err(anyhow::anyhow!("incorrect decimals value"));
}
decimals = DEFAULT_DECIMALS.to_string();
}

let mut initial_endowment: String = input("And the initial endowment for dev accounts?")
.placeholder("1u64 << 60")
Expand Down Expand Up @@ -417,7 +430,7 @@ mod tests {
template: Some(Template::Standard),
release_tag: None,
symbol: Some("UNIT".to_string()),
decimals: Some(12),
decimals: Some("12".to_string()),
initial_endowment: Some("1u64 << 60".to_string()),
};
command.execute().await?;
Expand Down Expand Up @@ -446,14 +459,14 @@ mod tests {
let config = get_customization_value(
&Template::Standard,
Some("DOT".to_string()),
Some(6),
Some("6".to_string()),
Some("10000".to_string()),
)?;
assert_eq!(
config,
Config {
symbol: "DOT".to_string(),
decimals: 6,
decimals: "6".to_string(),
initial_endowment: "10000".to_string()
}
);
Expand Down
1 change: 1 addition & 0 deletions crates/pop-parachains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository.workspace = true

[dependencies]
anyhow.workspace = true
cargo-generate.workspace = true
duct.workspace = true
flate2.workspace = true
glob.workspace = true
Expand Down
1 change: 0 additions & 1 deletion crates/pop-parachains/src/generator/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-3.0

pub mod pallet;
pub mod parachain;
17 changes: 0 additions & 17 deletions crates/pop-parachains/src/generator/parachain.rs

This file was deleted.

2 changes: 1 addition & 1 deletion crates/pop-parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub use new_pallet::{create_pallet_template, TemplatePalletConfig};
pub use new_parachain::instantiate_template_dir;
pub use templates::{Config, Provider, Template};
pub use up::{Binary, Status, Zombienet};
pub use utils::helpers::is_initial_endowment_valid;
pub use utils::helpers::{is_decimals_valid, is_initial_endowment_valid};
pub use utils::pallet_helpers::resolve_pallet_path;
/// Information about the Node. External export from Zombienet-SDK.
pub use zombienet_sdk::NetworkNode;
Expand Down
95 changes: 54 additions & 41 deletions crates/pop-parachains/src/new_parachain.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// SPDX-License-Identifier: GPL-3.0

use crate::{
generator::parachain::{ChainSpec, Network},
utils::helpers::{sanitize, write_to_file},
Config, Provider, Template,
};
use crate::{utils::helpers::sanitize, Config, Provider, Template};
use anyhow::Result;
use cargo_generate::{GenerateArgs, TemplatePath};
use pop_common::git::Git;
use std::{fs, path::Path};
use walkdir::WalkDir;

/// Create a new parachain.
///
Expand Down Expand Up @@ -39,37 +35,50 @@ pub fn instantiate_standard_template(
config: Config,
tag_version: Option<String>,
) -> Result<Option<String>> {
let temp_dir = ::tempfile::TempDir::new_in(std::env::temp_dir())?;
let source = temp_dir.path();
// Template palceholder definitions
let mut token_symbol: String = "token-symbol=".to_string();
let mut token_decimals: String = "token-decimals=".to_string();
let mut initial_endowement: String = "initial-endowment=".to_string();

let tag = Git::clone_and_degit(template.repository_url()?, source, tag_version)?;
// Placeholder customization
token_symbol.push_str(&*config.symbol);
token_decimals.push_str(&*config.decimals);
initial_endowement.push_str(&*config.initial_endowment);

for entry in WalkDir::new(&source) {
let entry = entry?;
// Tempalte rendering arguments
let standard_template_path = TemplatePath {
git: Some(
template
.repository_url()
.map_or(String::from("https://github.com/r0gue.io/base-parachain"), |r| {
r.to_string()
}),
),
tag: tag_version.clone(),
..Default::default()
};

let template_generation_args = GenerateArgs {
template_path: standard_template_path,
name: Some(target.file_name().unwrap().to_str().unwrap().to_string()),
destination: Some(target.parent().unwrap().to_path_buf()),
define: vec![token_symbol, token_decimals, initial_endowement],
..Default::default()
};

let source_path = entry.path();
let destination_path = target.join(source_path.strip_prefix(&source)?);
// Template rendering
let target_template_path = cargo_generate::generate(template_generation_args)
.expect("Couldn't render liquid tempalte");

if entry.file_type().is_dir() {
fs::create_dir_all(&destination_path)?;
} else {
fs::copy(source_path, &destination_path)?;
}
// Degit
let target_dirs = vec![".git", ".github"];
let remove_target = Path::new(&target_template_path);
for dir in target_dirs {
let git_dir = Path::new(dir);
fs::remove_dir_all(&remove_target.join(git_dir))?;
}
let chainspec = ChainSpec {
token_symbol: config.symbol,
decimals: config.decimals,
initial_endowment: config.initial_endowment,
};
use askama::Template;
write_to_file(
&target.join("node/src/chain_spec.rs"),
chainspec.render().expect("infallible").as_ref(),
)?;
// Add network configuration
let network = Network { node: "parachain-template-node".into() };
write_to_file(&target.join("network.toml"), network.render().expect("infallible").as_ref())?;
Ok(tag)

Ok(tag_version)
}

#[cfg(test)]
Expand All @@ -80,12 +89,19 @@ mod tests {

fn setup_template_and_instantiate() -> Result<tempfile::TempDir> {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
println!("{:?}", temp_dir);
let config = Config {
symbol: "DOT".to_string(),
decimals: 18,
decimals: "18".to_string(),
initial_endowment: "1000000".to_string(),
};
instantiate_standard_template(&Template::Standard, temp_dir.path(), config, None)?;
let _ = sanitize(temp_dir.path());
instantiate_standard_template(
&Template::Standard,
temp_dir.path(),
config,
Some(String::from("liquid-template")),
)?;
Ok(temp_dir)
}

Expand All @@ -107,13 +123,10 @@ mod tests {
// Verify network.toml contains expected content
let generated_file_content =
fs::read_to_string(temp_dir.path().join("network.toml")).expect("Failed to read file");
let expected_file_content =
fs::read_to_string(current_dir()?.join("./templates/base/network.templ"))
.expect("Failed to read file");
assert_eq!(
generated_file_content,
expected_file_content.replace("^^node^^", "parachain-template-node")
);
let mut expected_file_content =
temp_dir.path().file_name().unwrap().to_str().unwrap().to_string().to_owned();
expected_file_content.push_str("-node");
assert!(generated_file_content.contains(&expected_file_content));

Ok(())
}
Expand Down
8 changes: 6 additions & 2 deletions crates/pop-parachains/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Provider {
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub symbol: String,
pub decimals: u8,
pub decimals: String,
pub initial_endowment: String,
}

Expand Down Expand Up @@ -100,7 +100,8 @@ pub enum Template {
props(
Provider = "Pop",
Repository = "https://github.com/r0gue-io/base-parachain",
Network = "./network.toml"
Network = "./network.toml",
SupportedVersions = "liquid-template"
)
)]
Standard,
Expand Down Expand Up @@ -236,15 +237,18 @@ impl Template {
self.get_str("Network")
}

/// Get a list of the template' supported versions.
pub fn supported_versions(&self) -> Option<Vec<&str>> {
self.get_str("SupportedVersions").map(|s| s.split(',').collect())
}

/// Check if the provided version is supported.
pub fn is_supported_version(&self, version: &str) -> bool {
// if `SupportedVersion` is None, then all versions are supported. Otherwise, ensure version is present.
self.supported_versions().map_or(true, |versions| versions.contains(&version))
}

/// Check if the template has been audited.
pub fn is_audited(&self) -> bool {
self.get_str("IsAudited").map_or(false, |s| s == "true")
}
Expand Down
36 changes: 10 additions & 26 deletions crates/pop-parachains/src/utils/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ pub(crate) fn sanitize(target: &Path) -> Result<(), Error> {
Ok(())
}

/// Check if the decimals input by the user is a valid numeric value.
///
/// # Arguments
///
/// * `decimals` - decimals input to be checked for validity.
pub fn is_decimals_valid(decimals: &str) -> bool {
decimals.chars().all(char::is_numeric)
}

/// Check if the initial endowment input by the user is a valid balance.
///
/// # Arguments
Expand All @@ -33,6 +42,7 @@ pub fn is_initial_endowment_valid(initial_endowment: &str) -> bool {
initial_endowment.parse::<u128>().is_ok()
|| is_valid_bitwise_left_shift(initial_endowment).is_ok()
}

// Auxiliar method to check if the endowment input with a shift left (1u64 << 60) format is valid.
// Parse the self << rhs format and check the shift left operation is valid.
fn is_valid_bitwise_left_shift(initial_endowment: &str) -> Result<u128, Error> {
Expand Down Expand Up @@ -85,32 +95,6 @@ pub(crate) fn write_to_file(path: &Path, contents: &str) -> Result<(), Error> {
#[cfg(test)]
mod tests {
use super::*;
use crate::generator::parachain::ChainSpec;
use askama::Template;
use tempfile::tempdir;

#[test]
fn test_write_to_file() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempdir()?;
let chainspec = ChainSpec {
token_symbol: "DOT".to_string(),
decimals: 6,
initial_endowment: "1000000".to_string(),
};
let file_path = temp_dir.path().join("file.rs");
let _ = fs::write(&file_path, "");
write_to_file(&file_path, chainspec.render().expect("infallible").as_ref())?;
let generated_file_content =
fs::read_to_string(temp_dir.path().join("file.rs")).expect("Failed to read file");

assert!(generated_file_content
.contains("properties.insert(\"tokenSymbol\".into(), \"DOT\".into());"));
assert!(generated_file_content
.contains("properties.insert(\"tokenDecimals\".into(), 6.into());"));
assert!(generated_file_content.contains("1000000"));

Ok(())
}

#[test]
fn test_is_initial_endowment_valid() {
Expand Down

0 comments on commit cf68f9f

Please sign in to comment.