-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add two-step snap generation process
This commit splits the snap generation into two steps. The goal is to allow the user to edit the intermediate generated WASM library to adjust the type resolution to compile his runtime call. It will still attempt to provide an out-of-the-box correct implementation.
- Loading branch information
Showing
9 changed files
with
822 additions
and
605 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,64 @@ | ||
use std::path::PathBuf; | ||
|
||
use clap::Parser; | ||
use clap::{Parser, Subcommand}; | ||
|
||
#[derive(Parser)] | ||
pub struct Cli { | ||
/// Path to the directory that contains the manifest TOML file of the project. | ||
#[arg(short, long)] | ||
pub path: Option<PathBuf>, | ||
#[command(subcommand)] | ||
pub command: Commands, | ||
|
||
/// Target directory to output the generated project. | ||
#[arg(short, long)] | ||
pub target: Option<PathBuf>, | ||
#[arg( | ||
long, | ||
short = 'q', | ||
action = clap::ArgAction::Count, | ||
global = true, | ||
help = "Sets the verbosity level.", | ||
)] | ||
pub quiet: u8, | ||
|
||
/// Git remote to use when cloning the origin repository. | ||
#[arg(short, long)] | ||
pub origin: Option<String>, | ||
/// Defaults all inputs. | ||
#[arg(long)] | ||
pub defaults: bool, | ||
|
||
/// Branch to use when cloning the origin repository. | ||
/// Skips all confirmations. | ||
#[arg(short, long)] | ||
pub branch: Option<String>, | ||
pub force: bool, | ||
} | ||
|
||
/// Context definition of the runtime spec. | ||
#[arg(short, long)] | ||
pub context: Option<String>, | ||
pub struct InterfaceArgs { | ||
pub quiet: u8, | ||
pub defaults: bool, | ||
pub force: bool, | ||
} | ||
|
||
/// DA definition of the runtime. | ||
#[arg(short, long)] | ||
pub da_spec: Option<String>, | ||
impl Cli { | ||
pub fn split_interface(self) -> (Subcommands, InterfaceArgs) { | ||
let command = match self.command { | ||
Commands::SovSnapGenerator { command } => command, | ||
}; | ||
|
||
/// Runtime call definition. | ||
#[arg(short, long)] | ||
pub runtime: Option<String>, | ||
( | ||
command, | ||
InterfaceArgs { | ||
quiet: self.quiet, | ||
defaults: self.defaults, | ||
force: self.force, | ||
}, | ||
) | ||
} | ||
} | ||
|
||
/// Defaults all inputs. | ||
#[arg(long)] | ||
pub defaults: bool, | ||
#[derive(Debug, Clone, Subcommand)] | ||
pub enum Commands { | ||
SovSnapGenerator { | ||
#[command(subcommand)] | ||
command: Subcommands, | ||
}, | ||
} | ||
|
||
/// Skips all confirmations. | ||
#[arg(long)] | ||
pub force: bool, | ||
#[derive(Debug, Clone, Subcommand)] | ||
pub enum Subcommands { | ||
/// Initializes the project from the provided path. | ||
Init(super::init::Init), | ||
|
||
/// Builds a project initialized via `Init`. | ||
Build(super::build::Build), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use std::{env, path::PathBuf}; | ||
|
||
use anyhow::Context; | ||
use clap::Parser; | ||
|
||
use super::interface::Interface; | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
pub struct Build { | ||
/// Path of the generated WASM project. | ||
#[arg(short, long)] | ||
path: Option<PathBuf>, | ||
} | ||
|
||
pub fn build(args: Build, interface: &mut Interface) -> anyhow::Result<()> { | ||
let Build { path } = args; | ||
|
||
let cwd = env::current_dir()?; | ||
interface.prompt("Insert the path to the generated `Cargo.toml`"); | ||
let path = interface.path_or_read(Some(&cwd.display().to_string()), path); | ||
let path = path | ||
.is_dir() | ||
.then(|| path.join("Cargo.toml")) | ||
.unwrap_or(path); | ||
|
||
let path = path | ||
.canonicalize()? | ||
.parent() | ||
.and_then(|p| p.parent()) | ||
.and_then(|p| p.parent()) | ||
.with_context(|| format!("Failed to locate the root snap of `{}`", path.display()))? | ||
.canonicalize()?; | ||
|
||
interface.info(format!("Using target root `{}`...", path.display())); | ||
|
||
duct::cmd!("yarn", "install").dir(&path).run()?; | ||
|
||
interface.info(format!( | ||
"Yarn packages installed on `{}`...", | ||
path.display() | ||
)); | ||
|
||
duct::cmd!("yarn", "update-wasm").dir(&path).run()?; | ||
|
||
interface.info(format!("WASM file built on `{}`...", path.display())); | ||
|
||
duct::cmd!("yarn", "build").dir(&path).run()?; | ||
|
||
interface.info(format!("Yarn project built on `{}`...", path.display())); | ||
interface.info("To start the browser application, run `yarn start`."); | ||
|
||
Ok(()) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
use std::{env, fs, path::PathBuf}; | ||
|
||
use clap::Parser; | ||
|
||
use super::{ | ||
interface::Interface, | ||
manifest::{Dependency, Manifest}, | ||
}; | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
pub struct Init { | ||
/// Path of the runtime module cargo project. | ||
#[arg(short, long)] | ||
path: Option<PathBuf>, | ||
|
||
/// Target directory to output the generated project. | ||
#[arg(short, long)] | ||
target: Option<PathBuf>, | ||
|
||
/// Git remote to use when cloning the origin repository. | ||
#[arg(short, long)] | ||
origin: Option<String>, | ||
|
||
/// Branch to use when cloning the origin repository. | ||
#[arg(short, long)] | ||
branch: Option<String>, | ||
} | ||
|
||
pub fn init(args: Init, interface: &mut Interface) -> anyhow::Result<()> { | ||
let Init { | ||
path, | ||
target, | ||
origin, | ||
branch, | ||
} = args; | ||
|
||
let cwd = env::current_dir()?; | ||
interface.prompt("Insert the path to your `Cargo.toml`"); | ||
let path = interface.path_or_read(Some(&cwd.display().to_string()), path); | ||
let path = path | ||
.is_dir() | ||
.then(|| path.join("Cargo.toml")) | ||
.unwrap_or(path); | ||
|
||
if !path.exists() { | ||
anyhow::bail!( | ||
"Failed to locate `Cargo.toml`; {} does not exist", | ||
path.display() | ||
); | ||
} | ||
|
||
if !path.is_file() { | ||
anyhow::bail!( | ||
"Failed to locate `Cargo.toml`; {} is not a file", | ||
path.display() | ||
); | ||
} | ||
|
||
let path = path.canonicalize()?; | ||
|
||
interface.info(format!("Using manifest `{}`...", path.display())); | ||
|
||
let manifest = Manifest::read(&path, interface)?; | ||
|
||
interface.prompt("Insert the target directory of the project"); | ||
let target_default = cwd | ||
.parent() | ||
.unwrap_or_else(|| cwd.as_path()) | ||
.join(format!("{}-snap", manifest.project.name)) | ||
.display() | ||
.to_string(); | ||
|
||
let target = interface.path_or_read(Some(&target_default), target); | ||
if target.is_file() { | ||
interface.bail(format!( | ||
"The provided target `{}` is a file; use a directory", | ||
target.display() | ||
)); | ||
} | ||
|
||
if target.exists() { | ||
if fs::remove_dir(&target).is_err() { | ||
interface.prompt(format!( | ||
"The target directory `{}` already exists; overwrite? [y/n]", | ||
target.display() | ||
)); | ||
|
||
interface.read_confirmation(); | ||
|
||
fs::remove_dir_all(&target)?; | ||
} | ||
} | ||
|
||
interface.prompt("Insert the origin git repository of the snap template"); | ||
let origin_default = "https://github.com/Sovereign-Labs/sov-snap"; | ||
let origin = interface.line_or_read(Some(&origin_default), origin); | ||
|
||
interface.prompt("Insert the branch of the snap template"); | ||
let branch_default = "v0.1.2"; | ||
let branch = interface.line_or_read(Some(&branch_default), branch); | ||
|
||
interface.info(format!( | ||
"Cloning the snap template into `{}`...", | ||
target.display() | ||
)); | ||
|
||
duct::cmd!( | ||
"git", | ||
"clone", | ||
"--quiet", | ||
"--progress", | ||
"-c", | ||
"advice.detachedHead=false", | ||
"--branch", | ||
branch, | ||
"--single-branch", | ||
"--depth", | ||
"1", | ||
origin, | ||
&target, | ||
) | ||
.run()?; | ||
|
||
interface.info(format!( | ||
"Cloned the snap template into `{}`", | ||
target.display() | ||
)); | ||
|
||
let target_wasm = target.join("external").join("sov-wasm").canonicalize()?; | ||
let target_wasm_manifest = target_wasm.join("Cargo.toml"); | ||
let target_definitions = target_wasm.join("src").join("definitions.rs"); | ||
|
||
let borsh = manifest | ||
.dependencies | ||
.get(&Dependency::new("borsh")) | ||
.cloned() | ||
.unwrap_or_else(|| String::from("\"0.10.3\"")); | ||
let serde_json = manifest | ||
.dependencies | ||
.get(&Dependency::new("serde_json")) | ||
.cloned() | ||
.unwrap_or_else(|| String::from("\"1.0\"")); | ||
let sov_modules_api = manifest.dependencies.get(&Dependency::new("sov-modules-api")).cloned().unwrap_or_else(|| String::from(r#"{ git = "https://github.com/Sovereign-Labs/sovereign-sdk.git", rev = "df169be", features = ["serde"] }"#)); | ||
let sov_mock_da = manifest.dependencies.get(&Dependency::new("sov-mock-da")).cloned().unwrap_or_else(|| String::from(r#"{ git = "https://github.com/Sovereign-Labs/sovereign-sdk.git", rev = "df169be" }"#)); | ||
|
||
let project = path | ||
.parent() | ||
.unwrap_or_else(|| path.as_path()) | ||
.display() | ||
.to_string(); | ||
let wasm_manifest = format!( | ||
r#"[package] | ||
name = "sov-wasm" | ||
version = "0.1.0" | ||
edition = "2021" | ||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
[dependencies] | ||
{} = {{ path = "{}" }} | ||
borsh = {} | ||
serde_json = {} | ||
sov-modules-api = {} | ||
sov-mock-da = {} | ||
"#, | ||
manifest.project.name, project, borsh, serde_json, sov_modules_api, sov_mock_da | ||
); | ||
|
||
let definitions = format!( | ||
r#"pub type Context = sov_modules_api::default_context::ZkDefaultContext; | ||
pub type DaSpec = sov_mock_da::MockDaSpec; | ||
pub type RuntimeCall = {}::RuntimeCall<Context, DaSpec>; | ||
"#, | ||
manifest.project.formatted | ||
); | ||
|
||
fs::write(&target_wasm_manifest, wasm_manifest)?; | ||
fs::write(&target_definitions, definitions)?; | ||
|
||
interface.info(format!( | ||
"Generated the snap template into `{}`", | ||
target.display() | ||
)); | ||
|
||
interface.info("Edit the generated snap template and run `cargo sov-snap-generator build`"); | ||
|
||
Ok(()) | ||
} |
Oops, something went wrong.