Adds 'leo clone' command

This commit is contained in:
howardwu 2021-02-25 09:29:50 -08:00
parent d5f787cd92
commit 4fc5f3f30c
5 changed files with 191 additions and 18 deletions

4
.circleci/leo-publish.sh Executable file
View File

@ -0,0 +1,4 @@
mkdir hello-world && cd hello-world || exit 1
$LEO init
ls -la
$LEO run

View File

@ -25,7 +25,7 @@ use std::{
use structopt::StructOpt;
use tracing::Span;
/// Add package from Aleo Package Manager
/// Add a package from Aleo Package Manager
#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Add {
@ -58,7 +58,7 @@ impl Add {
}
/// Try to parse author/package string from self.remote
pub fn try_read_arguments(&self) -> Result<(String, String)> {
fn try_read_arguments(&self) -> Result<(String, String)> {
if let Some(val) = &self.remote {
let v: Vec<&str> = val.split('/').collect();
if v.len() == 2 {
@ -91,42 +91,41 @@ impl Command for Add {
}
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
// checking that manifest exists...
// Check that a manifest exists for the current package.
if context.manifest().is_err() {
return Err(anyhow!("Package Manifest not found, try running leo init or leo new"));
return Err(anyhow!("Package manifest not found, try running `leo init`"));
};
let (author, package_name) = match self.try_read_arguments() {
Ok((author, package)) => (author, package),
Err(err) => return Err(err),
};
let version = self.version;
// build request body (Options are skipped when sealizing)
let fetch = Fetch {
author,
package_name: package_name.clone(),
version,
// Attempt to fetch the package.
let reader = {
let fetch = Fetch {
author,
package_name: package_name.clone(),
version: self.version,
};
let bytes = context.api.run_route(fetch)?.bytes()?;
std::io::Cursor::new(bytes)
};
let bytes = context.api.run_route(fetch)?.bytes()?;
// Construct the directory structure.
let mut path = context.dir()?;
{
// setup directory structure since request was success
ImportsDirectory::create(&path)?;
path.push(IMPORTS_DIRECTORY_NAME);
path.push(package_name);
create_dir_all(&path)?;
};
let reader = std::io::Cursor::new(bytes);
// Proceed to unzip and parse the fetched bytes.
let mut zip_archive = match zip::ZipArchive::new(reader) {
Ok(zip) => zip,
Err(error) => return Err(anyhow!(error)),
};
for i in 0..zip_archive.len() {
let file = match zip_archive.by_index(i) {
Ok(file) => file,

View File

@ -0,0 +1,160 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{api::Fetch, commands::Command, context::Context};
use anyhow::{anyhow, Result};
use std::{
borrow::Cow,
fs::{self, File},
io::{Read, Write},
path::Path,
};
use structopt::StructOpt;
use tracing::Span;
/// Clone a package from Aleo Package Manager
#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Clone {
#[structopt(name = "REMOTE")]
remote: Option<String>,
#[structopt(name = "author", help = "Specify a package author", long = "author", short = "a")]
author: Option<String>,
#[structopt(name = "package", help = "Specify a package name", long = "package", short = "p")]
package: Option<String>,
#[structopt(name = "version", help = "Specify a package version", long = "version", short = "v")]
version: Option<String>,
}
impl Clone {
pub fn new(
remote: Option<String>,
author: Option<String>,
package: Option<String>,
version: Option<String>,
) -> Self {
Self {
remote,
author,
package,
version,
}
}
/// Try to parse author/package string from self.remote
fn try_read_arguments(&self) -> Result<(String, String)> {
if let Some(val) = &self.remote {
let v: Vec<&str> = val.split('/').collect();
if v.len() == 2 {
Ok((v[0].to_string(), v[1].to_string()))
} else {
Err(anyhow!(
"Incorrect argument, please use --help for information on command use"
))
}
} else if let (Some(author), Some(package)) = (&self.author, &self.package) {
Ok((author.clone(), package.clone()))
} else {
Err(anyhow!(
"Incorrect argument, please use --help for information on command use"
))
}
}
/// Creates a directory at the provided path with the given directory name.
fn create_directory(path: &Path, directory_name: &str) -> Result<()> {
let mut path = Cow::from(path);
// Check that the path ends in the directory name.
// If it does not, proceed to append the directory name to the path.
if path.is_dir() && !path.ends_with(directory_name) {
path.to_mut().push(directory_name);
}
Ok(fs::create_dir_all(&path)?)
}
}
impl Command for Clone {
type Input = ();
type Output = ();
fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Cloning")
}
fn prelude(&self) -> Result<Self::Input> {
Ok(())
}
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let (author, package_name) = match self.try_read_arguments() {
Ok((author, package)) => (author, package),
Err(err) => return Err(err),
};
// Attempt to fetch the package.
let reader = {
let fetch = Fetch {
author,
package_name: package_name.clone(),
version: self.version,
};
let bytes = context.api.run_route(fetch)?.bytes()?;
std::io::Cursor::new(bytes)
};
// Construct the directory structure.
let mut path = context.dir()?;
path.push(package_name.clone());
Self::create_directory(&path, &package_name)?;
// Proceed to unzip and parse the fetched bytes.
let mut zip_archive = match zip::ZipArchive::new(reader) {
Ok(zip) => zip,
Err(error) => return Err(anyhow!(error)),
};
for i in 0..zip_archive.len() {
let file = match zip_archive.by_index(i) {
Ok(file) => file,
Err(error) => return Err(anyhow!(error)),
};
let file_name = file.name();
let mut file_path = path.clone();
file_path.push(file_name);
if file_name.ends_with('/') {
fs::create_dir_all(file_path)?;
} else {
if let Some(parent_directory) = path.parent() {
fs::create_dir_all(parent_directory)?;
}
File::create(file_path)?.write_all(&file.bytes().map(|e| e.unwrap()).collect::<Vec<u8>>())?;
}
}
tracing::info!("Successfully cloned {}", package_name);
Ok(())
}
}

View File

@ -17,6 +17,9 @@
pub mod add;
pub use add::Add;
pub mod clone;
pub use clone::Clone;
pub mod login;
pub use login::Login;

View File

@ -23,7 +23,7 @@ pub mod synthesizer;
pub mod updater;
use commands::{
package::{Add, Login, Logout, Publish, Remove},
package::{Add, Clone, Login, Logout, Publish, Remove},
Build,
Clean,
Command,
@ -121,12 +121,18 @@ enum CommandOpts {
command: Test,
},
#[structopt(about = "Install a package from the Aleo Package Manager")]
#[structopt(about = "Import a package from the Aleo Package Manager")]
Add {
#[structopt(flatten)]
command: Add,
},
#[structopt(about = "Clone a package from the Aleo Package Manager")]
Clone {
#[structopt(flatten)]
command: Clone,
},
#[structopt(about = "Login to the Aleo Package Manager")]
Login {
#[structopt(flatten)]
@ -189,6 +195,7 @@ fn main() {
CommandOpts::Update { command } => command.try_execute(),
CommandOpts::Add { command } => command.try_execute(),
CommandOpts::Clone { command } => command.try_execute(),
CommandOpts::Login { command } => command.try_execute(),
CommandOpts::Logout { command } => command.try_execute(),
CommandOpts::Publish { command } => command.try_execute(),