-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cli): setup skeleton for quartz tool #105
Changes from 2 commits
e528ace
9bc55fc
f894c15
6422099
84e15c8
a8c1457
9d1a955
aac78a7
1448713
c58802b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "quartz" | ||
version.workspace = true | ||
authors.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"] | ||
readme = "README.md" | ||
|
||
[dependencies] | ||
clap.workspace = true | ||
color-eyre.workspace = true | ||
displaydoc.workspace = true | ||
serde.workspace = true | ||
thiserror.workspace = true | ||
tracing.workspace = true | ||
tracing-subscriber = { workspace = true, features = ["env-filter"] } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# quartz CLI | ||
|
||
A CLI tool to manage Quartz applications. The `quartz` CLI tool is designed to streamline the development and deployment | ||
process of Quartz applications. | ||
|
||
It provides helpful information about each command and its options. To get a list of all available subcommands and their | ||
descriptions, use the `--help` flag: | ||
|
||
```shell | ||
$ quartz --help | ||
|
||
Quartz 0.1.0 | ||
A CLI tool to manage Quartz applications | ||
|
||
USAGE: | ||
quartz [SUBCOMMAND] | ||
|
||
OPTIONS: | ||
-h, --help Print help information | ||
-V, --version Print version information | ||
|
||
SUBCOMMANDS: | ||
init Create base Quartz app directory from template | ||
build Build the contract and enclave binaries | ||
start Configure Gramine, sign, and start the enclave binary | ||
deploy Deploy the WASM binary to the blockchain and call instantiate | ||
run Run the enclave handler, and expose a public query server for users | ||
handshake Run the handshake between the contract and enclave | ||
``` | ||
|
||
## Installation | ||
|
||
To install Quartz, ensure you have Rust and Cargo installed. Then run: | ||
|
||
```shell | ||
cargo install quartz | ||
``` | ||
|
||
## Usage of subcommands | ||
|
||
### Init | ||
|
||
Initialize a new Quartz app directory structure with optional name and path arguments. | ||
|
||
#### Usage | ||
|
||
```shell | ||
$ quartz init --help | ||
quartz-init | ||
Create base Quartz app directory from template | ||
|
||
USAGE: | ||
quartz init [OPTIONS] | ||
|
||
OPTIONS: | ||
-n, --name <NAME> Set the name of the Quartz app [default: <name of parent directory>] | ||
-p, --path <PATH> Set the path where the Quartz app will be created [default: .] | ||
-h, --help Print help information | ||
``` | ||
|
||
#### Example | ||
|
||
```shell | ||
quartz init --name <app_name> --path <path> | ||
``` | ||
|
||
This command will create the following directory structure at the specified path (or the current directory if no path is | ||
provided): | ||
|
||
```shell | ||
$ tree /<path>/<app-name> -L 1 | ||
apps/transfers/ | ||
├── contracts/ | ||
├── enclave/ | ||
├── frontend/ | ||
└── README.md | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use std::path::PathBuf; | ||
|
||
use clap::{Parser, Subcommand}; | ||
use tracing::metadata::LevelFilter; | ||
|
||
#[derive(clap::Args, Debug, Clone)] | ||
pub struct Verbosity { | ||
/// Increase verbosity, can be repeated up to 2 times | ||
#[arg(long, short, action = clap::ArgAction::Count)] | ||
pub verbose: u8, | ||
} | ||
|
||
impl Verbosity { | ||
pub fn to_level_filter(&self) -> LevelFilter { | ||
match self.verbose { | ||
0 => LevelFilter::INFO, | ||
1 => LevelFilter::DEBUG, | ||
_ => LevelFilter::TRACE, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Parser)] | ||
#[command(version, long_about = None)] | ||
pub struct Cli { | ||
/// Increase log verbosity | ||
#[clap(flatten)] | ||
pub verbose: Verbosity, | ||
|
||
/// Main command | ||
#[command(subcommand)] | ||
pub command: Command, | ||
} | ||
|
||
#[derive(Debug, Subcommand)] | ||
pub enum Command { | ||
Init { | ||
hu55a1n1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// path to create & init a quartz app, defaults to current path if unspecified | ||
#[clap(long)] | ||
path: Option<PathBuf>, | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
use displaydoc::Display; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Display, Error)] | ||
pub enum Error { | ||
/// specified path `{0}` is not a directory | ||
PathNotDir(String), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
use crate::{cli::Verbosity, error::Error, request::Request, response::Response}; | ||
|
||
pub mod init; | ||
|
||
pub trait Handler { | ||
type Error; | ||
type Response; | ||
|
||
fn handle(self, verbosity: Verbosity) -> Result<Self::Response, Self::Error>; | ||
} | ||
|
||
impl Handler for Request { | ||
type Error = Error; | ||
type Response = Response; | ||
|
||
fn handle(self, verbosity: Verbosity) -> Result<Self::Response, Self::Error> { | ||
match self { | ||
Request::Init(request) => request.handle(verbosity), | ||
} | ||
.map(Into::into) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use tracing::trace; | ||
|
||
use crate::{ | ||
cli::Verbosity, error::Error, handler::Handler, request::init::InitRequest, | ||
response::init::InitResponse, | ||
}; | ||
|
||
impl Handler for InitRequest { | ||
type Error = Error; | ||
type Response = InitResponse; | ||
|
||
fn handle(self, _verbosity: Verbosity) -> Result<Self::Response, Self::Error> { | ||
trace!("initializing directory structure..."); | ||
todo!() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At what point will this be filled out, if not in this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was hoping once we had the skeleton in place, we (the team) could work on the commands in parallel. (we could also copy the work that @dangush is doing on the cycles-protocol repo, e.g. https://github.com/informalsystems/cycles-protocol/pull/14) |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#![doc = include_str!("../README.md")] | ||
#![forbid(unsafe_code)] | ||
#![warn( | ||
clippy::checked_conversions, | ||
clippy::panic, | ||
clippy::panic_in_result_fn, | ||
clippy::unwrap_used, | ||
trivial_casts, | ||
trivial_numeric_casts, | ||
rust_2018_idioms, | ||
unused_lifetimes, | ||
unused_import_braces, | ||
unused_qualifications | ||
)] | ||
|
||
pub mod cli; | ||
pub mod error; | ||
pub mod handler; | ||
pub mod request; | ||
pub mod response; | ||
|
||
use clap::Parser; | ||
use color_eyre::eyre::Result; | ||
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; | ||
|
||
use crate::{cli::Cli, handler::Handler, request::Request}; | ||
|
||
fn main() -> Result<()> { | ||
color_eyre::install()?; | ||
|
||
let args = Cli::parse(); | ||
|
||
let env_filter = EnvFilter::builder() | ||
.with_default_directive(args.verbose.to_level_filter().into()) | ||
.from_env_lossy(); | ||
|
||
tracing_subscriber::fmt() | ||
.with_target(false) | ||
.with_writer(std::io::stderr) | ||
.with_env_filter(env_filter) | ||
.finish() | ||
.init(); | ||
|
||
let request = Request::try_from(args.command)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is to parse the input args and convert them into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth converting your GH issue comment into a comment in the code 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
request.handle(args.verbose)?; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use crate::{cli::Command, error::Error, request::init::InitRequest}; | ||
|
||
pub mod init; | ||
|
||
#[derive(Clone, Debug)] | ||
pub enum Request { | ||
Init(InitRequest), | ||
} | ||
|
||
impl TryFrom<Command> for Request { | ||
type Error = Error; | ||
|
||
fn try_from(cmd: Command) -> Result<Self, Self::Error> { | ||
match cmd { | ||
Command::Init { path } => InitRequest::try_from(path), | ||
} | ||
.map(Into::into) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use std::path::PathBuf; | ||
|
||
use crate::{error::Error, request::Request}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct InitRequest { | ||
// TODO(hu55a1n1): remove `allow(unused)` here once init handler is implemented | ||
#[allow(unused)] | ||
directory: PathBuf, | ||
} | ||
|
||
impl TryFrom<Option<PathBuf>> for InitRequest { | ||
type Error = Error; | ||
|
||
fn try_from(path: Option<PathBuf>) -> Result<Self, Self::Error> { | ||
if let Some(path) = path { | ||
if !path.is_dir() { | ||
return Err(Error::PathNotDir(format!("{}", path.display()))); | ||
} | ||
} | ||
|
||
todo!() | ||
} | ||
} | ||
|
||
impl From<InitRequest> for Request { | ||
fn from(request: InitRequest) -> Self { | ||
Self::Init(request) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
use serde::Serialize; | ||
|
||
use crate::response::init::InitResponse; | ||
|
||
pub mod init; | ||
|
||
#[derive(Clone, Debug, Serialize)] | ||
pub enum Response { | ||
Init(InitResponse), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use serde::Serialize; | ||
|
||
use crate::response::Response; | ||
|
||
#[derive(Clone, Debug, Serialize)] | ||
pub struct InitResponse; | ||
|
||
impl From<InitResponse> for Response { | ||
fn from(response: InitResponse) -> Self { | ||
Self::Init(response) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 3 commands would most likely be confusing for any newcomer to Quartz. I'd recommend having
enclave
andcontract
subcommands like so:Also, please remind me why we have a
start
andrun
command for the enclave? That would most likely be confusing for users.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just removed
run
(f894c15). 👍Regarding
enclave
/contract
commands, I think it would be better instead to renamestart
tostart-enclave
anddeploy
todeploy-contract
, because we only have one subcommand per command currently. WDYT?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we plan on only having a single command for each going forward? If so then your proposal makes sense to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I am not sure TBH. 😅 IMHO it's easier to start with single commands for now because I cannot think of concrete examples for multiple sub-commands. Actually we could have separate
build
commands (e.g.quartz {enclave,contract} build
) instead ofquartz build
in the future. 🤔 I guess what you proposed initially makes sense. Will update. 🙏There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
84e15c8