diff --git a/.github/README.md b/.github/README.md
new file mode 120000
index 00000000..e5dc7c0d
--- /dev/null
+++ b/.github/README.md
@@ -0,0 +1 @@
+../crates/pop-cli/README.md
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 20af21b2..b61ae280 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5518,6 +5518,8 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
+ "strum 0.26.2",
+ "strum_macros 0.26.2",
"symlink",
"tempfile",
"thiserror",
diff --git a/Cargo.toml b/Cargo.toml
index 5bc68de1..023946d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,7 +35,7 @@ askama = "0.12"
regex="1.5.4"
walkdir = "2.4"
indexmap = { version = "2.2"}
-toml_edit = { version = "0.22" }
+toml_edit = { version = "0.22", features = ["serde"] }
symlink = { version = "0.1" }
reqwest = { version = "0.11" }
serde_json = { version = "1.0"}
diff --git a/README.md b/README.md
index 4477a124..a0c543b9 100644
--- a/README.md
+++ b/README.md
@@ -18,32 +18,32 @@ cargo install --locked --git https://github.com/r0gue-io/pop-cli
### Parachains
-Use `pop` to create a new Parachain project:
+Use `pop` to create a new Parachain project.
+To be guided through the entire parachain creation process, simply execute
+```sh
+pop new parachain
+```
+If no guidance is needed, proceed with:
```sh
# Create a minimal parachain
pop new parachain my-app
-# Get a pallet-contracts enabled parachain
-pop new parachain my-app cpt
-# Get a evm compatible parachain
-pop new parachain my-app fpt
```
-Use `pop` to build your Parachain:
+We also integrate other provider templates in the tool, check them running:
```sh
-# Build your parachain
-pop build parachain -p ./my-app
+pop new parachain --help
```
-
-or
-
+Some examples are:
```sh
-cd my-app
-pop build parachain
+# Get Parity's pallet-contracts enabled parachain template
+pop new parachain my-app parity -t cpt
+# Get Parity's evm compatible parachain template
+pop new parachain my-app parity -t fpt
```
-You can also customize your parachain by providing config options for token symbol (as it appears in chain metadata),
+For Pop templates you can also customize your parachain by providing config options for token symbol (as it appears in chain metadata),
token decimals, and the initial endowment for developer accounts. Here's how:
```sh
@@ -57,6 +57,21 @@ There's also the shorter version:
pop new parachain my-app -s DOT -d 6 -i 1_000_000_000
```
+Use `pop` to build your Parachain:
+
+```sh
+# Build your parachain
+pop build parachain -p ./my-app
+```
+
+or
+
+```sh
+cd my-app
+pop build parachain
+```
+
+
Finally, to build your Parachain:
```sh
diff --git a/crates/pop-cli/README.md b/crates/pop-cli/README.md
deleted file mode 100644
index 2aa0cfa0..00000000
--- a/crates/pop-cli/README.md
+++ /dev/null
@@ -1,257 +0,0 @@
-# Pop CLI
-
-
-
-An all-in-one tool for Polkadot development.
-
-## Install
-
-You can install Pop CLI as follows:
-
-```shell
-cargo install --locked --git https://github.com/r0gue-io/pop-cli
-```
-
-> :information_source: A [crates.io](https://crates.io/crates/pop-cli) version will be available soon!
-
-## Getting Started
-
-### Parachains
-
-Use `pop` to create a new Parachain project:
-
-```sh
-# Create a minimal parachain
-pop new parachain my-app
-# Get a pallet-contracts enabled parachain
-pop new parachain my-app cpt
-# Get a evm compatible parachain
-pop new parachain my-app fpt
-```
-
-Use `pop` to build your Parachain:
-
-```sh
-# Build your parachain
-pop build parachain -p ./my-app
-```
-
-or
-
-```sh
-cd my-app
-pop build parachain
-```
-
-You can also customize your parachain by providing config options for token symbol (as it appears in chain metadata),
-token decimals, and the initial endowment for developer accounts. Here's how:
-
-```sh
-# Create a minimal parachain with "DOT" as token symbol, 6 token decimals and 1 billion tokens per dev account
-pop new parachain my-app --symbol DOT --decimals 6 --endowment 1_000_000_000
-```
-
-There's also the shorter version:
-
-```sh
-pop new parachain my-app -s DOT -d 6 -i 1_000_000_000
-```
-
-Finally, to build your Parachain:
-
-```sh
-cd my-app
-pop build parachain --release
-```
-
-## Spawn Network using Zombienet
-
-You can spawn a local network using [zombienet](https://github.com/paritytech/zombienet-sdk) as follows:
-
-```shell
-pop up parachain -f ./tests/zombienet.toml -p https://github.com/r0gue-io/pop-node
-```
-
-> :information_source: Pop CLI will automatically source the necessary polkadot binaries. Currently, these will be built
-> if on a non-linux system.
-
-
-### Contracts
-
-Use `pop` to create a new Smart Contract project:
-
-```sh
-# Create a minimal Smart Contract
-pop new contract my_contract
-```
-
-Test the Smart Contract:
-
-```sh
-# Test an existing Smart Contract
-pop test contract -p ./my_contract
-```
-
-Build the Smart Contract:
-
-```sh
-# Build an existing Smart Contract
-pop build contract -p ./my_contract
-```
-
-To deploy a Smart Contract you need a chain running. For testing purposes one option is to
-run [substrate-contracts-node](https://github.com/paritytech/substrate-contracts-node):
-
-```sh
-cargo install contracts-node
-substrate-contracts-node
-```
-
-> :information_source: We plan to automate this in the future.
-
-Deploy and instantiate the Smart Contract:
-
-```sh
-pop up contract -p ./my_contract --constructor new --args "false" --suri //Alice
-```
-
-Some of the options available are:
-
-- Specify the contract `constructor `to use, which in this example is `new()`.
-- Specify the argument (`args`) to the constructor, which in this example is `false`.
-- Specify the account uploading and instantiating the contract with `--suri`, which in this example is the default
- development account of `//Alice`.
- For other accounts, the actual secret key must be provided e.g. an 0x prefixed 64 bit hex string, or the seed phrase.
-
-> :warning: **Use only for development**: Use a safer method of signing here before using this feature with production
-> projects. We will be looking to provide alternative solutions in the future!
-
-- You also can specify the url of your node with `--url ws://your-endpoint`, by default it is
- using `ws://localhost:9944`.
-
-For more information about the options,
-check [cargo-contract documentation](https://github.com/paritytech/cargo-contract/blob/master/crates/extrinsics/README.md#instantiate)
-
-Interacting with the Smart Contract:
-
-1. Read-only Operations: For operations that only require reading from the blockchain state. This approach does not
- require to submit an extrinsic.
- Example using the get() message:
-
-```sh
-pop call contract -p ./my_contract --contract $INSTANTIATED_CONTRACT_ADDRESS --message get --suri //Alice
-```
-
-2. State-modifying Operations: For operations that change a storage value, thus altering the blockchain state. Include
- the `x / --execute` flag to submit an extrinsic on-chain.
-
-Example executing the `flip()` message:
-
-```sh
-pop call contract -p ./my_contract --contract $INSTANTIATED_CONTRACT_ADDRESS --message flip --suri //Alice -x
-```
-
-## E2E testing
-
-For end-to-end testing you will need to have a Substrate node with `pallet contracts`.
-You do not need to run it in the background since the node is started for each test independently.
-To install the latest version:
-
-```
-cargo install contracts-node --git https://github.com/paritytech/substrate-contracts-node.git
-```
-
-If you want to run any other node with `pallet-contracts` you need to change `CONTRACTS_NODE` environment variable:
-
-```
-export CONTRACTS_NODE="YOUR_CONTRACTS_NODE_PATH"
-```
-
-Run e2e testing on the Smart Contract:
-
-```sh
-# Run e2e tests for an existing smart contract
- pop test contract -p ./my_contract --features e2e-tests
-```
-
-### Pallets
-
-To create a new Pallet, simply run `pop new pallet`. You will have a new pallet ready for hacking.
-To customize the new Pallet you can follow these options:
-
-```sh
-# create a pallet with name `pallet-awesome` in the current working directory
-pop new pallet pallet-awesome
-# or with options
-pop new pallet pallet-awesome --authors Me --description "This pallet oozes awesomeness" --path my_app/pallets
-```
-
-## Building Pop CLI locally
-
-Build the tool locally with all the features:
-
-```sh
-cargo build --all-features
-```
-
-Build the tool only for Parachain functionality:
-
-```sh
-cargo build --features parachain
-```
-
-Build the tool only for Smart Contracts functionality:
-
-```sh
-cargo build --features contract
-```
-
-## Testing Pop CLI
-
-To test the tool locally.
-
-Run the unit tests:
-
-```sh
-cargo test
-```
-
-Due to the time it can take to build a Parachain or a Smart Contract, some tests have been separated from the normal testing flow.
-
-To run the unit tests that involves building a Smart Contract:
-
-```sh
-cargo test --features unit_contract
-```
-
-To run the unit tests that involves building a Parachain:
-
-```sh
-cargo test --features unit_parachain
-```
-
-Then we have some tests that check all the flows are correct:
-
-Run the e2e tests for Smart Contracts functionality:
-
-```sh
-cargo test --features e2e_contract
-```
-
-Run the e2e tests for Parachain functionality:
-
-```sh
-cargo test --features e2e_parachain
-```
-
-Run all tests:
-
-```sh
-cargo test --all-features
-```
-## Acknowledgements
-
-Pop CLI would not be possible without these awesome crates!
-
-- Local network deployment powered by [zombienet-sdk](https://github.com/paritytech/zombienet-sdk)
-- [cargo contract](https://github.com/paritytech/cargo-contract) a setup and deployment tool for developing Wasm based Smart Contracts via ink!
diff --git a/crates/pop-cli/README.md b/crates/pop-cli/README.md
new file mode 120000
index 00000000..fe840054
--- /dev/null
+++ b/crates/pop-cli/README.md
@@ -0,0 +1 @@
+../../README.md
\ No newline at end of file
diff --git a/crates/pop-cli/src/commands/new/contract.rs b/crates/pop-cli/src/commands/new/contract.rs
index 0e9b6770..b86a7530 100644
--- a/crates/pop-cli/src/commands/new/contract.rs
+++ b/crates/pop-cli/src/commands/new/contract.rs
@@ -18,7 +18,7 @@ pub struct NewContractCommand {
}
impl NewContractCommand {
- pub(crate) fn execute(self) -> anyhow::Result<()> {
+ pub(crate) async fn execute(&self) -> anyhow::Result<()> {
clear_screen()?;
intro(format!(
"{}: Generating new contract \"{}\"!",
@@ -49,7 +49,7 @@ impl NewContractCommand {
fs::create_dir_all(contract_path.as_path())?;
let mut spinner = cliclack::spinner();
spinner.start("Generating contract...");
- create_smart_contract(self.name, contract_path.as_path())?;
+ create_smart_contract(&self.name, contract_path.as_path())?;
spinner.stop("Smart contract created!");
outro(format!("cd into \"{}\" and enjoy hacking! 🚀", contract_path.display()))?;
Ok(())
@@ -61,14 +61,14 @@ mod tests {
use super::*;
use anyhow::Result;
- #[test]
- fn test_new_contract_command_execute_success() -> Result<()> {
+ #[tokio::test]
+ async fn test_new_contract_command_execute_success() -> Result<()> {
let temp_contract_dir = tempfile::tempdir().expect("Could not create temp dir");
let command = NewContractCommand {
name: "test_contract".to_string(),
path: Some(PathBuf::from(temp_contract_dir.path())),
};
- let result = command.execute();
+ let result = command.execute().await;
assert!(result.is_ok());
Ok(())
diff --git a/crates/pop-cli/src/commands/new/pallet.rs b/crates/pop-cli/src/commands/new/pallet.rs
index d5746c80..f729b5f9 100644
--- a/crates/pop-cli/src/commands/new/pallet.rs
+++ b/crates/pop-cli/src/commands/new/pallet.rs
@@ -19,7 +19,7 @@ pub struct NewPalletCommand {
}
impl NewPalletCommand {
- pub(crate) fn execute(&self) -> anyhow::Result<()> {
+ pub(crate) async fn execute(&self) -> anyhow::Result<()> {
clear_screen()?;
intro(format!(
"{}: Generating new pallet \"{}\"!",
diff --git a/crates/pop-cli/src/commands/new/parachain.rs b/crates/pop-cli/src/commands/new/parachain.rs
index d0785be1..737b0808 100644
--- a/crates/pop-cli/src/commands/new/parachain.rs
+++ b/crates/pop-cli/src/commands/new/parachain.rs
@@ -1,40 +1,30 @@
// SPDX-License-Identifier: GPL-3.0
use crate::style::{style, Theme};
-use clap::{Args, Parser};
-use std::{fs, path::PathBuf};
-use strum_macros::{Display, EnumString};
-
-use cliclack::{clear_screen, confirm, intro, log, outro, outro_cancel, set_theme};
-use pop_parachains::{instantiate_template_dir, Config, Git, Template as ParachainTemplate};
-
-#[derive(Clone, Parser, Debug, Display, EnumString, PartialEq)]
-pub enum Template {
- #[strum(serialize = "Contracts Node Template", serialize = "cpt")]
- Contracts,
- #[strum(serialize = "Frontier Parachain Template", serialize = "fpt")]
- FPT,
- #[strum(serialize = "Base Parachain Template", serialize = "base")]
- Base,
-}
-impl Template {
- pub fn into_parachain_template(&self) -> ParachainTemplate {
- match self {
- Template::Base => ParachainTemplate::Base,
- Template::Contracts => ParachainTemplate::Contracts,
- Template::FPT => ParachainTemplate::FPT,
- }
- }
-}
+use anyhow::Result;
+use clap::Args;
+use std::{
+ fs,
+ path::{Path, PathBuf},
+};
+
+use cliclack::{clear_screen, confirm, input, intro, log, outro, outro_cancel, set_theme};
+use pop_parachains::{instantiate_template_dir, Config, Git, GitHub, Provider, Release, Template};
#[derive(Args)]
pub struct NewParachainCommand {
- #[arg(help = "Name of the project")]
- pub(crate) name: String,
+ #[arg(help = "Name of the project. If empty assistance in the process will be provided.")]
+ pub(crate) name: Option,
#[arg(
- help = "Template to use; Options are 'cpt', 'fpt'. Leave empty for default parachain template"
+ help = "Template provider. Options are pop or parity (deprecated).",
+ default_value = "pop"
)]
- #[arg(default_value = "base")]
- pub(crate) template: Template,
+ pub(crate) provider: Option,
+ #[arg(
+ short = 't',
+ long,
+ help = "Template to use: 'base' for Pop and 'cpt' and 'fpt' for Parity templates"
+ )]
+ pub(crate) template: Option,
#[arg(long, short, help = "Token Symbol", default_value = "UNIT")]
pub(crate) symbol: Option,
#[arg(long, short, help = "Token Decimals", default_value = "12")]
@@ -55,58 +45,224 @@ pub struct NewParachainCommand {
}
impl NewParachainCommand {
- pub(crate) fn execute(&self) -> anyhow::Result<()> {
+ pub(crate) async fn execute(&self) -> Result<()> {
clear_screen()?;
- intro(format!(
- "{}: Generating \"{}\" using {}!",
- style(" Pop CLI ").black().on_magenta(),
- &self.name,
- &self.template
- ))?;
set_theme(Theme);
- let destination_path = if let Some(ref path) = self.path {
- path.join(&self.name)
- } else {
- PathBuf::from(&self.name)
+
+ return match &self.name {
+ // If user doesn't select the name guide them to generate a parachain.
+ None => guide_user_to_generate_parachain().await,
+ Some(name) => {
+ let provider = &self.provider.clone().unwrap_or_default();
+ let template = match &self.template {
+ Some(template) => template.clone(),
+ None => provider.default_template(), // Each provider has a template by default
+ };
+
+ is_template_supported(provider, &template)?;
+ let config = get_customization_value(
+ &template,
+ self.symbol.clone(),
+ self.decimals,
+ self.initial_endowment.clone(),
+ )?;
+
+ generate_parachain_from_template(name, provider, &template, None, config)
+ },
};
- if destination_path.exists() {
- if !confirm(format!(
- "\"{}\" directory already exists. Would you like to remove it?",
- destination_path.display()
- ))
- .interact()?
- {
- outro_cancel(format!(
- "Cannot generate parachain until \"{}\" directory is removed.",
- destination_path.display()
- ))?;
- return Ok(());
- }
- fs::remove_dir_all(destination_path.as_path())?;
+ }
+}
+
+async fn guide_user_to_generate_parachain() -> Result<()> {
+ intro(format!("{}: Generate a parachain", style(" Pop CLI ").black().on_magenta()))?;
+
+ let mut prompt = cliclack::select("Select a template provider: ".to_string());
+ for (i, provider) in Provider::providers().iter().enumerate() {
+ if i == 0 {
+ prompt = prompt.initial_value(provider);
}
- let mut spinner = cliclack::spinner();
- spinner.start("Generating parachain...");
- let tag = instantiate_template_dir(
- &self.template.into_parachain_template(),
- destination_path.as_path(),
- Config {
- symbol: self.symbol.clone().expect("default values"),
- decimals: self.decimals.clone().expect("default values"),
- initial_endowment: self.initial_endowment.clone().expect("default values"),
- },
- )?;
- if let Err(err) = Git::git_init(destination_path.as_path(), "initialized parachain") {
- if err.class() == git2::ErrorClass::Config && err.code() == git2::ErrorCode::NotFound {
- outro_cancel("git signature could not be found. Please configure your git config with your name and email")?;
- }
+ prompt = prompt.item(
+ provider,
+ provider.name(),
+ format!(
+ "{} {} available option(s) {}",
+ provider.description(),
+ provider.templates().len(),
+ if provider.name() == "Parity" { "[deprecated]" } else { "" }
+ ),
+ );
+ }
+ let provider = prompt.interact()?;
+ let template = display_select_options(provider)?;
+
+ let url = url::Url::parse(&template.repository_url()?).expect("valid repository url");
+ let latest_3_releases = GitHub::get_latest_n_releases(3, &url).await?;
+
+ let mut release_name = None;
+ if latest_3_releases.len() > 0 {
+ release_name = Some(display_release_versions_to_user(latest_3_releases)?);
+ }
+
+ let name: String = input("Where should your project be created?")
+ .placeholder("./my-parachain")
+ .default_input("./my-parachain")
+ .interact()?;
+
+ let mut customizable_options = Config {
+ symbol: "UNIT".to_string(),
+ decimals: 12,
+ initial_endowment: "1u64 << 60".to_string(),
+ };
+ if matches!(template, Template::Base) {
+ customizable_options = prompt_customizable_options()?;
+ }
+
+ clear_screen()?;
+
+ generate_parachain_from_template(
+ &name,
+ &provider,
+ &template,
+ release_name,
+ customizable_options,
+ )
+}
+
+fn generate_parachain_from_template(
+ name_template: &String,
+ provider: &Provider,
+ template: &Template,
+ tag_version: Option,
+ config: Config,
+) -> Result<()> {
+ intro(format!(
+ "{}: Generating \"{}\" using {:?} from {:?}!",
+ style(" Pop CLI ").black().on_magenta(),
+ name_template,
+ template,
+ provider
+ ))?;
+ let destination_path = check_destination_path(name_template)?;
+
+ let mut spinner = cliclack::spinner();
+ spinner.start("Generating parachain...");
+ let tag = instantiate_template_dir(template, destination_path, tag_version, config)?;
+ if let Err(err) = Git::git_init(destination_path, "initialized parachain") {
+ if err.class() == git2::ErrorClass::Config && err.code() == git2::ErrorCode::NotFound {
+ outro_cancel("git signature could not be found. Please configure your git config with your name and email")?;
}
- spinner.stop("Generation complete");
- if let Some(tag) = tag {
- log::info(format!("Version: {}", tag))?;
+ }
+ spinner.stop("Generation complete");
+ if let Some(tag) = tag {
+ log::info(format!("Version: {}", tag))?;
+ }
+
+ cliclack::note(
+ "NOTE: the resulting parachain is not guaranteed to be audited or reviewed for security vulnerabilities.",
+ format!("Please consult the source repository at {} to assess production suitability and licensing restrictions.", template.repository_url()?))?;
+
+ outro(format!("cd into \"{}\" and enjoy hacking! 🚀", name_template))?;
+
+ Ok(())
+}
+
+fn is_template_supported(provider: &Provider, template: &Template) -> Result<()> {
+ if !template.matches(provider) {
+ return Err(anyhow::anyhow!(format!(
+ "The provider \"{:?}\" doesn't support the {:?} template.",
+ provider, template
+ )));
+ };
+ return Ok(());
+}
+
+fn display_select_options(provider: &Provider) -> Result<&Template> {
+ let mut prompt = cliclack::select("Select the type of parachain:".to_string());
+ for (i, template) in provider.templates().into_iter().enumerate() {
+ if i == 0 {
+ prompt = prompt.initial_value(template);
}
- outro(format!("cd into \"{}\" and enjoy hacking! 🚀", destination_path.display()))?;
- Ok(())
+ prompt = prompt.item(template, template.name(), template.description());
+ }
+ Ok(prompt.interact()?)
+}
+
+fn get_customization_value(
+ template: &Template,
+ symbol: Option,
+ decimals: Option,
+ initial_endowment: Option,
+) -> Result {
+ if !matches!(template, Template::Base)
+ && (symbol.is_some() || decimals.is_some() || initial_endowment.is_some())
+ {
+ log::warning("Customization options are not available for this template")?;
}
+ return Ok(Config {
+ symbol: symbol.clone().expect("default values"),
+ decimals: decimals.clone().expect("default values"),
+ initial_endowment: initial_endowment.clone().expect("default values"),
+ });
+}
+
+fn check_destination_path(name_template: &String) -> Result<&Path> {
+ let destination_path = Path::new(name_template);
+ if destination_path.exists() {
+ if !confirm(format!(
+ "\"{}\" directory already exists. Would you like to remove it?",
+ destination_path.display()
+ ))
+ .interact()?
+ {
+ outro_cancel(format!(
+ "Cannot generate parachain until \"{}\" directory is removed.",
+ destination_path.display()
+ ))?;
+ return Err(anyhow::anyhow!(format!(
+ "\"{}\" directory already exists.",
+ destination_path.display()
+ )));
+ }
+ fs::remove_dir_all(destination_path)?;
+ }
+ Ok(destination_path)
+}
+
+fn display_release_versions_to_user(releases: Vec) -> Result {
+ let mut prompt = cliclack::select("Select a specific release:".to_string());
+ for (i, release) in releases.iter().enumerate() {
+ if i == 0 {
+ prompt = prompt.initial_value(&release.tag_name);
+ }
+ prompt = prompt.item(
+ &release.tag_name,
+ &release.name,
+ match &release.commit {
+ Some(commit) => format!("{} / {}", &release.tag_name, &commit[..=6]),
+ None => release.tag_name.to_string(),
+ },
+ )
+ }
+ Ok(prompt.interact()?.to_string())
+}
+
+fn prompt_customizable_options() -> Result {
+ let symbol: String = input("What is the symbol of your parachain token?")
+ .placeholder("UNIT")
+ .default_input("UNIT")
+ .interact()?;
+
+ let decimals_input: String = input("How many token decimals?")
+ .placeholder("12")
+ .default_input("12")
+ .interact()?;
+ let decimals: u8 = decimals_input.parse::().expect("input has to be a number");
+
+ let endowment: String = input("And the initial endowment for dev accounts?")
+ .placeholder("1u64 << 60")
+ .default_input("1u64 << 60")
+ .interact()?;
+ Ok(Config { symbol, decimals, initial_endowment: endowment })
}
#[cfg(test)]
@@ -117,21 +273,22 @@ mod tests {
use super::*;
use std::{fs, path::Path};
- #[test]
- fn test_new_parachain_command_execute() -> anyhow::Result<()> {
+ #[tokio::test]
+ async fn test_new_parachain_command_execute() -> anyhow::Result<()> {
let command = NewParachainCommand {
- name: "test_parachain".to_string(),
- template: Template::Base,
+ name: Some("test_parachain".to_string()),
+ provider: Some(Provider::Pop),
+ template: Some(Template::Base),
symbol: Some("UNIT".to_string()),
decimals: Some(12),
initial_endowment: Some("1u64 << 60".to_string()),
path: None,
};
- let result = command.execute();
+ let result = command.execute().await;
assert!(result.is_ok());
// check for git_init
- let repo = Repository::open(Path::new(&command.name))?;
+ let repo = Repository::open(Path::new(&command.name.unwrap()))?;
let reflog = repo.reflog("HEAD")?;
assert_eq!(reflog.len(), 1);
@@ -141,4 +298,34 @@ mod tests {
}
Ok(())
}
+
+ #[test]
+ fn test_is_template_supported() {
+ assert!(is_template_supported(&Provider::Pop, &Template::Base).is_ok());
+ assert!(is_template_supported(&Provider::Pop, &Template::ParityContracts).is_err());
+ assert!(is_template_supported(&Provider::Pop, &Template::ParityFPT).is_err());
+
+ assert!(is_template_supported(&Provider::Parity, &Template::Base).is_err());
+ assert!(is_template_supported(&Provider::Parity, &Template::ParityContracts).is_ok());
+ assert!(is_template_supported(&Provider::Parity, &Template::ParityFPT).is_ok());
+ }
+
+ #[test]
+ fn test_get_customization_values() {
+ let config = get_customization_value(
+ &Template::Base,
+ Some("DOT".to_string()),
+ Some(6),
+ Some("10000".to_string()),
+ );
+ assert!(config.is_ok());
+ assert_eq!(
+ config.unwrap(),
+ Config {
+ symbol: "DOT".to_string(),
+ decimals: 6,
+ initial_endowment: "10000".to_string()
+ }
+ );
+ }
}
diff --git a/crates/pop-cli/src/main.rs b/crates/pop-cli/src/main.rs
index e9821d95..af707305 100644
--- a/crates/pop-cli/src/main.rs
+++ b/crates/pop-cli/src/main.rs
@@ -42,14 +42,14 @@ enum Commands {
async fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
- Commands::New(args) => match args.command {
+ Commands::New(args) => Ok(match &args.command {
#[cfg(feature = "parachain")]
- commands::new::NewCommands::Parachain(cmd) => cmd.execute(),
+ commands::new::NewCommands::Parachain(cmd) => cmd.execute().await?,
#[cfg(feature = "parachain")]
- commands::new::NewCommands::Pallet(cmd) => cmd.execute(),
+ commands::new::NewCommands::Pallet(cmd) => cmd.execute().await?,
#[cfg(feature = "contract")]
- commands::new::NewCommands::Contract(cmd) => cmd.execute(),
- },
+ commands::new::NewCommands::Contract(cmd) => cmd.execute().await?,
+ }),
Commands::Build(args) => match &args.command {
#[cfg(feature = "parachain")]
commands::build::BuildCommands::Parachain(cmd) => cmd.execute(),
diff --git a/crates/pop-contracts/src/build.rs b/crates/pop-contracts/src/build.rs
index 09fc8175..564667ca 100644
--- a/crates/pop-contracts/src/build.rs
+++ b/crates/pop-contracts/src/build.rs
@@ -27,8 +27,7 @@ mod tests {
let temp_dir = tempfile::tempdir().expect("Could not create temp dir");
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
- let result =
- crate::create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path());
+ let result = crate::create_smart_contract("test_contract", temp_contract_dir.as_path());
assert!(result.is_ok(), "Contract test environment setup failed");
Ok(temp_dir)
diff --git a/crates/pop-contracts/src/call.rs b/crates/pop-contracts/src/call.rs
index 6a5d0be3..1ca380ac 100644
--- a/crates/pop-contracts/src/call.rs
+++ b/crates/pop-contracts/src/call.rs
@@ -147,8 +147,7 @@ mod tests {
let temp_dir = tempfile::tempdir().expect("Could not create temp dir");
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
- let result =
- create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path());
+ let result = create_smart_contract("test_contract", temp_contract_dir.as_path());
assert!(result.is_ok(), "Contract test environment setup failed");
Ok(temp_dir)
diff --git a/crates/pop-contracts/src/new.rs b/crates/pop-contracts/src/new.rs
index f85be699..978b26ef 100644
--- a/crates/pop-contracts/src/new.rs
+++ b/crates/pop-contracts/src/new.rs
@@ -3,7 +3,7 @@ use crate::errors::Error;
use contract_build::new_contract_project;
use std::path::Path;
-pub fn create_smart_contract(name: String, target: &Path) -> Result<(), Error> {
+pub fn create_smart_contract(name: &str, target: &Path) -> Result<(), Error> {
// Canonicalize the target path to ensure consistency and resolve any symbolic links.
let canonicalized_path = target
.canonicalize()
@@ -35,7 +35,7 @@ mod tests {
let temp_dir = tempfile::tempdir()?;
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
- create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path())?;
+ create_smart_contract("test_contract", temp_contract_dir.as_path())?;
Ok(temp_dir)
}
diff --git a/crates/pop-contracts/src/test.rs b/crates/pop-contracts/src/test.rs
index fb43bf6a..8280d48c 100644
--- a/crates/pop-contracts/src/test.rs
+++ b/crates/pop-contracts/src/test.rs
@@ -43,11 +43,10 @@ mod tests {
fs::create_dir(&temp_contract_dir).map_err(|e| {
Error::TestEnvironmentError(format!("Failed to create test contract directory: {}", e))
})?;
- let result =
- crate::create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path())
- .map_err(|e| {
- Error::TestEnvironmentError(format!("Failed to create smart contract: {}", e))
- });
+ let result = crate::create_smart_contract("test_contract", temp_contract_dir.as_path())
+ .map_err(|e| {
+ Error::TestEnvironmentError(format!("Failed to create smart contract: {}", e))
+ });
assert!(result.is_ok(), "Contract test environment setup failed");
Ok(temp_dir)
}
diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs
index aa0acbca..b26e7a51 100644
--- a/crates/pop-contracts/src/up.rs
+++ b/crates/pop-contracts/src/up.rs
@@ -113,8 +113,7 @@ mod tests {
let temp_dir = tempfile::tempdir().expect("Could not create temp dir");
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
- let result =
- create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path());
+ let result = create_smart_contract("test_contract", temp_contract_dir.as_path());
assert!(result.is_ok(), "Contract test environment setup failed");
Ok(temp_dir)
diff --git a/crates/pop-contracts/src/utils/helpers.rs b/crates/pop-contracts/src/utils/helpers.rs
index b47574c9..2c6f6915 100644
--- a/crates/pop-contracts/src/utils/helpers.rs
+++ b/crates/pop-contracts/src/utils/helpers.rs
@@ -38,8 +38,7 @@ mod tests {
let temp_dir = tempfile::tempdir().expect("Could not create temp dir");
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
- let result =
- crate::create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path());
+ let result = crate::create_smart_contract("test_contract", temp_contract_dir.as_path());
assert!(result.is_ok(), "Contract test environment setup failed");
Ok(temp_dir)
diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml
index 9c03c8ad..4d54674e 100644
--- a/crates/pop-parachains/Cargo.toml
+++ b/crates/pop-parachains/Cargo.toml
@@ -5,27 +5,27 @@ version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
anyhow.workspace = true
-thiserror.workspace = true
duct.workspace = true
git2.workspace = true
git2_credentials.workspace = true
+strum.workspace = true
+strum_macros.workspace = true
tempfile.workspace = true
-url.workspace = true
+thiserror.workspace = true
tokio.workspace = true
+url.workspace = true
askama.workspace = true
-regex.workspace = true
-walkdir.workspace = true
indexmap.workspace = true
-toml_edit.workspace = true
-symlink.workspace = true
+regex.workspace = true
reqwest.workspace = true
serde_json.workspace = true
serde.workspace = true
+symlink.workspace = true
+toml_edit.workspace = true
+walkdir.workspace = true
# Zombienet
zombienet-sdk.workspace = true
zombienet-support.workspace = true
diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs
index 3fc6edad..6e636099 100644
--- a/crates/pop-parachains/src/errors.rs
+++ b/crates/pop-parachains/src/errors.rs
@@ -1,3 +1,4 @@
+use crate::templates;
use thiserror::Error;
use zombienet_sdk::OrchestratorError;
@@ -47,4 +48,7 @@ pub enum Error {
#[error("Failed to execute rustfmt")]
RustfmtError(std::io::Error),
+
+ #[error("Template error: {0}")]
+ TemplateError(#[from] templates::Error),
}
diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs
index d5162fed..82355601 100644
--- a/crates/pop-parachains/src/lib.rs
+++ b/crates/pop-parachains/src/lib.rs
@@ -4,14 +4,16 @@ mod errors;
mod generator;
mod new_pallet;
mod new_parachain;
+mod templates;
mod up;
mod utils;
pub use build::build_parachain;
pub use new_pallet::{create_pallet_template, TemplatePalletConfig};
-pub use new_parachain::{instantiate_template_dir, Config, Template};
+pub use new_parachain::instantiate_template_dir;
+pub use templates::{Config, Provider, Template};
pub use up::Zombienet;
-pub use utils::git::Git;
+pub use utils::git::{Git, GitHub, Release};
pub use utils::pallet_helpers::resolve_pallet_path;
// External exports
pub use zombienet_sdk::NetworkNode;
diff --git a/crates/pop-parachains/src/new_parachain.rs b/crates/pop-parachains/src/new_parachain.rs
index b90f56ea..a341c18a 100644
--- a/crates/pop-parachains/src/new_parachain.rs
+++ b/crates/pop-parachains/src/new_parachain.rs
@@ -6,46 +6,38 @@ use crate::{
git::Git,
helpers::{sanitize, write_to_file},
},
+ Config, Template,
};
use anyhow::Result;
use std::{fs, path::Path};
use walkdir::WalkDir;
-#[derive(Debug, Clone)]
-pub struct Config {
- pub symbol: String,
- pub decimals: u8,
- pub initial_endowment: String,
-}
-pub enum Template {
- Contracts,
- FPT,
- Base,
-}
-
/// Creates a new template at `target` dir
pub fn instantiate_template_dir(
template: &Template,
target: &Path,
+ tag_version: Option,
config: Config,
) -> Result