Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
666 changes: 618 additions & 48 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions language/tools/move-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ tempfile = "3.2.0"
walkdir = "2.3.1"
codespan-reporting = "0.11.1"
itertools = "0.10.0"
serde_json = "1.0"
toml_edit = { version = "0.14.3", features = ["easy"] }

bcs = "0.1.2"
move-bytecode-verifier = { path = "../../move-bytecode-verifier" }
Expand Down Expand Up @@ -49,9 +51,11 @@ move-errmapgen = { path = "../../move-prover/move-errmapgen" }
move-bytecode-source-map = { path = "../../move-ir-compiler/move-bytecode-source-map" }
move-bytecode-viewer = { path = "../move-bytecode-viewer" }
workspace-hack = { version = "0.1", path = "../../../crates/workspace-hack" }
reqwest = { version = "0.10", features = ["blocking", "json"] }

[dev-dependencies]
datatest-stable = "0.1.1"
home = "0.5.3"

[[bin]]
name = "move"
Expand Down
1 change: 1 addition & 0 deletions language/tools/move-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod base;
pub mod experimental;
pub mod package;
pub mod sandbox;
pub mod utils;

/// Default directory where saved Move resources live
pub const DEFAULT_STORAGE_DIR: &str = "storage";
Expand Down
97 changes: 96 additions & 1 deletion language/tools/move-cli/src/package/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::{
collections::HashMap,
fmt,
fs::{create_dir_all, read_to_string},
fs::{self, create_dir_all, read_to_string},
io::Write,
path::{Path, PathBuf},
process::ExitStatus,
Expand All @@ -16,6 +16,7 @@ use std::os::windows::process::ExitStatusExt;
// if unix
#[cfg(any(target_family = "unix"))]
use std::os::unix::prelude::ExitStatusExt;
use std::process::Command;
// if not windows nor unix
#[cfg(not(any(target_family = "windows", target_family = "unix")))]
compile_error!("Unsupported OS, currently we only support windows and unix family");
Expand Down Expand Up @@ -45,7 +46,9 @@ use move_package::{
};
use move_unit_test::UnitTestingConfig;

use crate::utils::credential;
use crate::{package::prover::run_move_prover, NativeFunctionRecord};
use reqwest::blocking::Client;

#[derive(Parser)]
pub enum CoverageSummaryOptions {
Expand Down Expand Up @@ -88,6 +91,7 @@ pub enum PackageCommand {
/// Print address information.
#[clap(name = "info")]
Info,
Upload,
/// Generate error map for the package and its dependencies at `path` for use by the Move
/// explanation tool.
#[clap(name = "errmap")]
Expand Down Expand Up @@ -214,6 +218,14 @@ impl From<UnitTestResult> for ExitStatus {
}
}

#[derive(serde::Serialize, Default)]
pub struct UploadRequest {
github_repo_url: String,
rev: String,
total_files: usize,
token: String,
}

impl CoverageSummaryOptions {
pub fn handle_command(&self, config: move_package::BuildConfig, path: &Path) -> Result<()> {
let coverage_map = CoverageMap::from_binary_file(path.join(".coverage_map.mvcov"))?;
Expand Down Expand Up @@ -315,6 +327,89 @@ pub fn handle_package_commands(
.resolution_graph_for_package(&rerooted_path)?
.print_info()?;
}
PackageCommand::Upload => {
let mut upload_request: UploadRequest = Default::default();
let mut output = Command::new("git")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean we should go with map_err instead of unwrap like in the code you linked, OR do you mean we should try to reuse the function download_and_update_if_repo, in which case not necessary because here we are querying info from the local (already fetched) repo.

.current_dir(".")
.args(&["remote", "-v"])
.output()
.unwrap();
if !output.status.success() || output.stdout.len() == 0 {
bail!("invalid git repository")
}

let lines = String::from_utf8_lossy(output.stdout.as_slice());
let lines = lines.split("\n");
for line in lines {
if line.contains("github.com") {
let tokens: Vec<&str> = line.split(&['\t', ' '][..]).collect();
if tokens.len() != 3 {
bail!("invalid remote url")
}
// convert ssh url to https
if tokens[1].starts_with("[email protected]") {
let https_url = tokens[1]
.replace(":", "/")
.replace("git@", "https://")
.replace(".git", "");
upload_request.github_repo_url = https_url;
break;
}
upload_request.github_repo_url = String::from(tokens[1]);
break;
}
}

output = Command::new("git")
.current_dir(".")
.args(&["rev-parse", "--short", "HEAD"])
.output()
.unwrap();
if !output.status.success() {
bail!("invalid HEAD commit id")
}
let revision_num = String::from_utf8_lossy(output.stdout.as_slice());
upload_request.rev = String::from(revision_num.trim());

output = Command::new("git")
.current_dir(".")
.args(&["ls-files"])
.output()
.unwrap();
let tracked_files = String::from_utf8_lossy(output.stdout.as_slice());
let tracked_files: Vec<&str> = tracked_files.split("\n").collect();
let mut total_files = tracked_files.len();
for file_path in tracked_files {
if file_path.is_empty() {
total_files -= 1;
continue;
}
}
upload_request.total_files = total_files;
upload_request.token = credential::get_registry_api_token(config.test_mode)?;

if config.test_mode {
fs::write(
"./request-body.txt",
serde_json::to_string(&upload_request).expect("invalid request body"),
)
.expect("unable to write file");
} else {
let url: String;
if cfg!(debug_assertions) {
url = String::from("http://staging.movey.net/api/v1/post_package/");
} else {
url = String::from("https://www.movey.net/api/v1/post_package/");
}
let client = Client::new();
let response = client.post(&url).json(&upload_request).send().unwrap();
if response.status().as_u16() == 200 {
println!("{}", "Your package has been successfully uploaded to Movey")
} else {
println!("{}", "Upload failed")
}
}
}
PackageCommand::BytecodeView {
interactive,
package_name,
Expand Down
132 changes: 132 additions & 0 deletions language/tools/move-cli/src/utils/credential.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::fs;
use anyhow::{Result, Context, bail};
use toml_edit::easy::{Value};

pub fn get_move_home_path(is_test_mode: bool) -> String {
let mut home = std::env::var("MOVE_HOME").unwrap_or_else(|_| {
format!(
"{}/.move",
std::env::var("HOME").expect("env var 'HOME' must be set")
)
});
if is_test_mode && !home.contains("/test") {
home.push_str("/test");
}
home
}

pub fn get_credential_path(is_test_mode: bool) -> String {
get_move_home_path(is_test_mode) + "/credential.toml"
}

pub fn get_registry_api_token(is_test_mode: bool) -> Result<String> {
if let Ok(content) = get_api_token(is_test_mode) {
Ok(content)
} else {
bail!("There seems to be an error with your Movey credential. \
Please run `move login` and follow the instructions.")
}
}

fn get_api_token(is_test_mode: bool) -> Result<String> {
let credential_path = get_credential_path(is_test_mode);

let contents = fs::read_to_string(&credential_path)?;
let mut toml: Value = contents.parse()?;
let registry = toml.as_table_mut()
.context("Error parsing credential.toml")?
.get_mut("registry")
.context("Error parsing credential.toml")?;
let token = registry.as_table_mut()
.context("Error parsing token")?
.get_mut("token")
.context("Error parsing token")?;
Ok(token.to_string().replace("\"", ""))
}

#[cfg(test)]
mod tests {
use super::*;
use home::home_dir;
use std::env;
use std::fs::File;

fn setup_move_home() -> (String, String) {
let mut move_home = env::var("MOVE_HOME").unwrap_or_else(|_| {
env::var("HOME").unwrap_or_else(|_| {
let home_dir = home_dir().unwrap().to_string_lossy().to_string();
env::set_var("HOME", &home_dir);
home_dir
})
});
move_home.push_str("/.move/test");
let credential_path = move_home.clone() + "/credential.toml";

return (move_home, credential_path);
}

fn clean_up() {
let (move_home, _) = setup_move_home();
let _ = fs::remove_dir_all(move_home);
}

#[test]
fn get_api_token_works() {
let (move_home, credential_path) = setup_move_home();

let _ = fs::create_dir_all(&move_home);
File::create(&credential_path).unwrap();

let content = "[registry]\ntoken = \"a sample token\"";
fs::write(&credential_path, content).unwrap();

let token = get_api_token(true).unwrap();
assert!(token.contains("a sample token"));

clean_up()
}

#[test]
fn get_api_token_fails_if_there_is_no_move_home_directory() {
let (move_home, _) = setup_move_home();
let _ = fs::remove_dir_all(&move_home);
let token = get_api_token(true);
assert!(token.is_err());

clean_up()
}

#[test]
fn get_api_token_fails_if_there_is_no_credential_file() {
let (move_home, _) = setup_move_home();
let _ = fs::remove_dir_all(&move_home);
fs::create_dir_all(&move_home).unwrap();
let token = get_api_token(true);
assert!(token.is_err());

clean_up()
}

#[test]
fn get_api_token_fails_if_credential_file_is_in_wrong_format() {
let (move_home, credential_path) = setup_move_home();

let _ = fs::remove_dir_all(&move_home);
fs::create_dir_all(&move_home).unwrap();
File::create(&credential_path).unwrap();

let content = "[registry]\ntoken = a sample token";
fs::write(&credential_path, content).unwrap();

let token = get_api_token(true);
assert!(token.is_err());

let content = "[registry]\ntokens = \"a sample token\"";
fs::write(&credential_path, content).unwrap();

let token = get_api_token(true);
assert!(token.is_err());

clean_up()
}
}
1 change: 1 addition & 0 deletions language/tools/move-cli/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod credential;
Loading