├── .gitignore ├── src ├── controller │ ├── mod.rs │ ├── list.rs │ ├── info.rs │ ├── project.rs │ └── package.rs ├── inner │ ├── mod.rs │ ├── go.rs │ ├── logger.rs │ ├── list_helper.rs │ ├── json_helper.rs │ ├── git_helper.rs │ ├── helpers.rs │ └── vendor.rs ├── tests │ └── mod.rs └── main.rs ├── CONTRIBUTING.md ├── templates ├── rubigo.lock └── rubigo.json ├── Cargo.toml ├── LICENSE ├── appveyor.yml ├── .travis.yml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | -------------------------------------------------------------------------------- /src/controller/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod project; 2 | pub mod package; 3 | pub mod info; 4 | pub mod list; 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please feel free to open an issue to report a bug or ask a question, or open a pull request to debug or add more features to Rubigo 2 | -------------------------------------------------------------------------------- /src/inner/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod logger; 2 | pub mod vendor; 3 | pub mod json_helper; 4 | pub mod git_helper; 5 | pub mod helpers; 6 | pub mod go; 7 | pub mod list_helper; 8 | -------------------------------------------------------------------------------- /templates/rubigo.lock: -------------------------------------------------------------------------------- 1 | { 2 | "git": [ 3 | { 4 | "import": "<:string>", 5 | "repo": "<:string> [optional]", 6 | "version": "" 7 | } 8 | ], 9 | "local": [ 10 | "" 11 | ], 12 | "global": [ 13 | "" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/inner/go.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::ffi::OsStr; 3 | 4 | pub fn get(package_name: &str, should_update: bool) -> bool { 5 | match should_update { 6 | true => run(&["get", "-u", package_name]), 7 | false => run(&["get", package_name]) 8 | } 9 | } 10 | 11 | fn run>(args: &[S]) -> bool { 12 | match Command::new("go").args(args).status() { 13 | Ok(exit_status) => exit_status.success(), 14 | _ => false, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/rubigo.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "name": "<:string>", 4 | "import": "<:string>", 5 | "description": "<:string>", 6 | "homepage": "<:string>", 7 | "license": "<:string>", 8 | "authors": [ 9 | { 10 | "name": "<:string>", 11 | "email": "<:string>", 12 | "website": "<:string>" 13 | } 14 | ] 15 | }, 16 | "packages": { 17 | "git": [ 18 | { 19 | "import": "<:string>", 20 | "repo": "<:string> [optional]", 21 | "version": "" 22 | } 23 | ], 24 | "local": [ 25 | "" 26 | ], 27 | "global": [ 28 | "" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rubigo" 3 | version = "1.0.4" 4 | authors = ["Navid "] 5 | description = "Golang dependency tool and package manager" 6 | homepage = "https://github.com/yaa110/Rubigo" 7 | documentation = "https://github.com/yaa110/Rubigo" 8 | repository = "https://github.com/yaa110/Rubigo" 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [dependencies] 13 | git2 = "0.6" 14 | json = "0.11" 15 | clap = "2.27" 16 | time = "0.1" 17 | threadpool = "1.7" 18 | num_cpus = "1.7" 19 | futures = "0.1" 20 | futures-cpupool = "0.1" 21 | semver = "0.9.0" 22 | regex = "0.2" 23 | curl = "0.4" 24 | 25 | [dev-dependencies] 26 | tempdir = "0.3" 27 | 28 | [profile.release] 29 | debug = false 30 | lto = true 31 | debug-assertions = false 32 | panic = 'unwind' 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Navid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/inner/logger.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | 3 | use std::process; 4 | use std::fmt::Display; 5 | use std::io::{self, Write}; 6 | 7 | #[derive(PartialEq, Eq, Debug, Copy, Clone)] 8 | pub enum Verbosity { 9 | High, 10 | Low, 11 | None, 12 | } 13 | 14 | #[derive(Copy, Clone)] 15 | pub struct Logger { 16 | verbosity: Verbosity, 17 | } 18 | 19 | impl Logger { 20 | pub fn new(verbosity: Verbosity) -> Self { 21 | Logger { 22 | verbosity: verbosity, 23 | } 24 | } 25 | 26 | pub fn verbose(&self, title: &str, msg: T) { 27 | if self.verbosity == Verbosity::High { 28 | println!("[{}] {} {}", time::strftime("%T", &time::now()).unwrap_or(String::from("00:00:00")), title, msg); 29 | let _ = io::stdout().flush(); 30 | } 31 | } 32 | 33 | pub fn error(&self, err: T) { 34 | if self.verbosity != Verbosity::None { 35 | let _ = writeln!(&mut io::stderr(), "{} {}", "error:", err); 36 | } 37 | } 38 | 39 | pub fn fatal(&self, err: T) { 40 | self.error(err); 41 | process::exit(1); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/inner/list_helper.rs: -------------------------------------------------------------------------------- 1 | use json::JsonValue; 2 | use inner::json_helper; 3 | 4 | pub fn print_header(header: &str, length: usize) { 5 | println!("{} ({}):", header, length); 6 | } 7 | 8 | pub fn print_git_packages(pkgs: &JsonValue) { 9 | for i in 0..pkgs.len() { 10 | let pkg = &pkgs[i]; 11 | 12 | print!("[{}]", i + 1); 13 | 14 | match pkg[json_helper::IMPORT_KEY].as_str() { 15 | Some(text) => println!("\t{}: {}", "Import", text), 16 | None => (), 17 | } 18 | 19 | match pkg[json_helper::VERSION_KEY].as_str() { 20 | Some(text) => println!("\t{}: {}", "Version", text), 21 | None => (), 22 | } 23 | 24 | match pkg[json_helper::REPO_KEY].as_str() { 25 | Some(text) => println!("\t{}: {}\n", "Repository", text), 26 | None => println!(), 27 | } 28 | } 29 | } 30 | 31 | pub fn print_str_packages(pkgs: &JsonValue) { 32 | for i in 0..pkgs.len() { 33 | print!("[{}]", i + 1); 34 | match pkgs[i].as_str() { 35 | Some(text) => println!("\t{}: {}\n", "Import", text), 36 | None => (), 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | RUST_VERSION: stable 4 | 5 | matrix: 6 | - TARGET: i686-pc-windows-msvc 7 | - TARGET: x86_64-pc-windows-msvc 8 | 9 | build: false 10 | 11 | install: 12 | - ps: >- 13 | If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { 14 | $Env:PATH += ';C:\msys64\mingw64\bin' 15 | } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { 16 | $Env:PATH += ';C:\msys64\mingw32\bin' 17 | } 18 | - curl -sSf -o rustup-init.exe https://win.rustup.rs/ 19 | - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% 20 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 21 | - rustc -Vv 22 | - cargo -V 23 | 24 | test_script: 25 | - cargo build --release --target %TARGET% 26 | 27 | before_deploy: 28 | - 7z a rubigo-%TARGET%.zip %APPVEYOR_BUILD_FOLDER%\target\%TARGET%\release\*.exe 29 | - appveyor PushArtifact rubigo-%TARGET%.zip 30 | 31 | 32 | deploy: 33 | artifact: rubigo-%TARGET%.zip 34 | auth_token: 35 | secure: DihzHIGlS+25iO7vCfIFKrFvzpn0tSIxEckmtXbo41JeBVelRzKP85xGftwklmQ9 36 | description: '' 37 | on: 38 | appveyor_repo_tag: true 39 | provider: GitHub 40 | 41 | notifications: 42 | - provider: Email 43 | on_build_success: false 44 | -------------------------------------------------------------------------------- /src/controller/list.rs: -------------------------------------------------------------------------------- 1 | use inner::logger::Logger; 2 | use inner::json_helper; 3 | use std::path::Path; 4 | use json::JsonValue; 5 | use inner::list_helper::{print_header, print_git_packages, print_str_packages}; 6 | 7 | pub fn list(is_local: bool, is_remote: bool, is_global: bool, logger: &Logger) { 8 | let lock_content = match json_helper::read(Path::new("rubigo.lock")) { 9 | Ok(content) => content, 10 | Err(e) => { 11 | logger.fatal(format!("unable to read `rubigo.lock`: {}", e)); 12 | return 13 | } 14 | }; 15 | 16 | let is_all = !(is_local || is_remote || is_global); 17 | 18 | if is_remote || is_all { 19 | list_remote(&lock_content[json_helper::GIT_KEY]); 20 | } 21 | 22 | if is_local || is_all { 23 | list_local(&lock_content[json_helper::LOCAL_KEY]); 24 | } 25 | 26 | if is_global || is_all { 27 | list_global(&lock_content[json_helper::GLOBAL_KEY]); 28 | } 29 | } 30 | 31 | fn list_global(content: &JsonValue) { 32 | if content.len() == 0 { 33 | return 34 | } 35 | print_header("Global packages", content.len()); 36 | print_str_packages(content); 37 | } 38 | 39 | fn list_local(content: &JsonValue) { 40 | if content.len() == 0 { 41 | return 42 | } 43 | print_header("Local packages", content.len()); 44 | print_str_packages(content); 45 | } 46 | 47 | fn list_remote(content: &JsonValue) { 48 | if content.len() == 0 { 49 | return 50 | } 51 | print_header("Remote packages", content.len()); 52 | print_git_packages(content); 53 | } 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: stable 4 | 5 | env: 6 | global: 7 | - PROJECT_NAME=rubigo 8 | - FILE_NAME=${PROJECT_NAME}-${TRAVIS_OS_NAME}-64bit.tar.gz 9 | 10 | script: 11 | - cargo test -- --nocapture 12 | - cargo build --release 13 | 14 | os: 15 | - linux 16 | - osx 17 | 18 | before_install: 19 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export OPENSSL_INCLUDE_DIR=`brew --prefix openssl`/include; fi 20 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export OPENSSL_LIB_DIR=`brew --prefix openssl`/lib; fi 21 | 22 | addons: 23 | apt: 24 | sources: 25 | - kalakris-cmake 26 | packages: 27 | - cmake 28 | - libcurl4-openssl-dev 29 | - libelf-dev 30 | - libdw-dev 31 | 32 | branches: 33 | only: 34 | - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ 35 | - master 36 | 37 | before_deploy: 38 | - strip "target/release/${PROJECT_NAME}" 39 | - tar czf "${FILE_NAME}" -C target/release "${PROJECT_NAME}" 40 | 41 | deploy: 42 | provider: releases 43 | api_key: 44 | secure: L07pFDLCtsmv5/RNkSmqeMoR2DbCB2h+7QsNrRAllw3gSmcW2stXG1w2tuUTRKE0+pOE0fau2hiVReBagYS3yof1k2ksxCJhUad99LUzFJmxYkTvVwHfmkeoqUi2WD10+DEsI4z5UZ69/F7cnBNlsJ6k8PKq6q0JS7Ob477Ga4Dd5QMmKEZVt2k0eSyn99TLzSas/DLNttwlGSkubkxL1oTdDyq2NVlX7kCuYAfKBbCMDpmkCuq4BQUOaTACHcYP3YaDiI/NOUeMMNIb8EV9R50PPXb2k9aV37GreSpw/9hDKBQ/plgTmHXf0cNKOSBoQ2qsVgrPBxfzUNAJXwlWRDgiqac2ct+0EcEA6DCoO7E7RzRD7f2VHl0vYuW1wPoR8aTq0IXVMq2+w8rfuH7FXwg9mgDfeSDrGANUZslSWdTzJRmKQMgtmTAQIJeJZGRGidOx73NoOUMpWeqWdAEM74jATHVzqghYX/Homp4n1PDpZ5M8jG0OffUBvj/Zop2hwnFxE7KtTsO5+ZTNmd8UEk7DlNN4QEWeea3ae/+95IzBjwR8UlIi60NMK5cJPSNm1+U2zbDR8rteAonm4xKSHS1v5BxPYQDsb4jS97Uux6jDXhKLf87Pl2dHtZHdhEFwcF64nMhDiaGrVZKii59U6oGlYs5179jQu9zxbB7ZXYo= 45 | file: "${FILE_NAME}" 46 | skip_cleanup: true 47 | on: 48 | tags: true 49 | 50 | notifications: 51 | email: 52 | on_success: never 53 | -------------------------------------------------------------------------------- /src/controller/info.rs: -------------------------------------------------------------------------------- 1 | use inner::logger::Logger; 2 | use inner::json_helper; 3 | use std::path::Path; 4 | 5 | pub fn display(logger: &Logger) { 6 | let content = match json_helper::read(Path::new("rubigo.json")) { 7 | Ok(content) => content, 8 | Err(e) => { 9 | logger.fatal(format!("unable to read `rubigo.json`: {}", e)); 10 | return 11 | } 12 | }; 13 | 14 | let info = &content[json_helper::INFO_KEY]; 15 | if info.is_null() { 16 | return 17 | } 18 | 19 | println!("{}:", "Project info"); 20 | 21 | match info[json_helper::NAME_KEY].as_str() { 22 | Some(text) => println!("\t{}: {}", "Project name", text), 23 | None => (), 24 | } 25 | 26 | match info[json_helper::IMPORT_KEY].as_str() { 27 | Some(text) => println!("\t{}: {}", "Import", text), 28 | None => (), 29 | } 30 | 31 | match info[json_helper::DESCRIPTION_KEY].as_str() { 32 | Some(text) => println!("\t{}: {}", "Description", text), 33 | None => (), 34 | } 35 | 36 | match info[json_helper::HOMEPAGE_KEY].as_str() { 37 | Some(text) => println!("\t{}: {}", "Homepage", text), 38 | None => (), 39 | } 40 | 41 | match info[json_helper::LICENSE_KEY].as_str() { 42 | Some(text) => println!("\t{}: {}", "License", text), 43 | None => (), 44 | } 45 | 46 | let authors = &info[json_helper::AUTHORS_KEY]; 47 | if authors.len() == 0 { 48 | return 49 | } 50 | 51 | println!("\n{} ({}):", "Authors", authors.len()); 52 | 53 | for i in 0..authors.len() { 54 | let author = &authors[i]; 55 | print!("[{}]", i + 1); 56 | 57 | match author[json_helper::NAME_KEY].as_str() { 58 | Some(text) => println!("\t{}: {}", "Name", text), 59 | None => (), 60 | } 61 | 62 | match author[json_helper::EMAIL_KEY].as_str() { 63 | Some(text) => println!("\t{}: {}", "Email", text), 64 | None => (), 65 | } 66 | 67 | match author[json_helper::WEBSITE_KEY].as_str() { 68 | Some(text) => println!("\t{}: {}\n", "Website", text), 69 | None => println!(), 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/inner/json_helper.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Write; 3 | use json::{self, JsonValue}; 4 | use std::path::Path; 5 | use std::io::{self, Read}; 6 | 7 | pub const IMPORT_KEY: &'static str = "import"; 8 | pub const VERSION_KEY: &'static str = "version"; 9 | pub const REPO_KEY: &'static str = "repo"; 10 | 11 | pub const INFO_KEY: &'static str = "info"; 12 | pub const NAME_KEY: &'static str = "name"; 13 | pub const LICENSE_KEY: &'static str = "license"; 14 | pub const HOMEPAGE_KEY: &'static str = "homepage"; 15 | pub const AUTHORS_KEY: &'static str = "authors"; 16 | pub const DESCRIPTION_KEY: &'static str = "description"; 17 | pub const WEBSITE_KEY: &'static str = "website"; 18 | pub const EMAIL_KEY: &'static str = "email"; 19 | 20 | pub const PACKAGES_KEY: &'static str = "packages"; 21 | pub const GIT_KEY: &'static str = "git"; 22 | pub const LOCAL_KEY: &'static str = "local"; 23 | pub const GLOBAL_KEY: &'static str = "global"; 24 | 25 | pub fn write>(json_path: P, project_name: &str, data: Option) -> io::Result<()> { 26 | match File::create(json_path) { 27 | Ok(mut file) => { 28 | match file.write_all(format!("{:#}", if data.is_none() { 29 | object!{ 30 | INFO_KEY => object!{ 31 | NAME_KEY => project_name 32 | }, 33 | PACKAGES_KEY => object!{ 34 | GIT_KEY => array![], 35 | LOCAL_KEY => array![], 36 | GLOBAL_KEY => array![] 37 | } 38 | } 39 | } else { 40 | data.unwrap() 41 | }).as_bytes()) { 42 | Ok(_) => (), 43 | Err(e) => return Err(e), 44 | } 45 | }, 46 | Err(e) => return Err(e), 47 | } 48 | 49 | Ok(()) 50 | } 51 | 52 | pub fn read(json_path: &Path) -> io::Result { 53 | let mut file = File::open(json_path)?; 54 | let mut contents = String::new(); 55 | file.read_to_string(&mut contents)?; 56 | match json::parse(contents.as_str()) { 57 | Ok(content) => Ok(content), 58 | Err(_) => Err(io::Error::new(io::ErrorKind::Other, format!("unable to parse json file: {:?}", json_path.to_str().unwrap_or("unknown")).as_str())), 59 | } 60 | } 61 | 62 | pub fn remove_package_from_array(pkg_import: &str, json_array: &JsonValue, is_str_array: bool) -> JsonValue { 63 | let mut result_array = json_array.clone(); 64 | for i in 0..json_array.len() { 65 | let pkg_name = if is_str_array { 66 | match json_array[i].as_str() { 67 | Some(name) => name, 68 | None => continue, 69 | } 70 | } else { 71 | match json_array[i][IMPORT_KEY].as_str() { 72 | Some(name) => name, 73 | None => continue, 74 | } 75 | }; 76 | if pkg_import == pkg_name { 77 | let _ = result_array.array_remove(i); 78 | break 79 | } 80 | } 81 | result_array 82 | } 83 | -------------------------------------------------------------------------------- /src/inner/git_helper.rs: -------------------------------------------------------------------------------- 1 | use git2::{Repository, Object, BranchType}; 2 | use semver::{Version, VersionReq}; 3 | use regex::Regex; 4 | use inner::logger::Logger; 5 | 6 | pub fn get_latest_commit(repo: &Repository) -> Option { 7 | match repo.head() { 8 | Ok(r) => match r.resolve() { 9 | Ok(ref r) => match r.target() { 10 | Some(id) => Some(format!("{}", id)), 11 | None => None, 12 | }, 13 | _ => None, 14 | }, 15 | _ => None, 16 | } 17 | } 18 | 19 | pub fn get_current_branch(repo: &Repository) -> Option { 20 | match repo.branches(Some(BranchType::Local)) { 21 | Ok(branches) => { 22 | for b in branches { 23 | let branch = match b { 24 | Ok(b) => b.0, 25 | _ => continue, 26 | }; 27 | if branch.is_head() { 28 | match branch.name() { 29 | Ok(name) => match name { 30 | Some(name_str) => return Some(name_str.to_owned()), 31 | None => return None, 32 | }, 33 | _ => return None, 34 | } 35 | } 36 | } 37 | None 38 | }, 39 | _ => None, 40 | } 41 | } 42 | 43 | pub fn get_latest_version(repo: &Repository, version_rule: Option<&VersionReq>) -> Option<(String, Version)> { 44 | let mut version = None; 45 | match repo.tag_names(None) { 46 | Ok(tag_names) => { 47 | let mut selected_tag = None; 48 | let re = match Regex::new(r"^v?([0-9]+)[.]?([0-9]*)[.]?([0-9]*)([-]?.*)") { 49 | Ok(re) => re, 50 | _ => return version, 51 | }; 52 | for t in tag_names.iter() { 53 | let tag_name = match t { 54 | Some(name) => name, 55 | None => continue, 56 | }; 57 | let tag_version_str = match re.captures(t.unwrap()) { 58 | Some(caps) => format!("{}.{}.{}{}", 59 | match caps.get(1) { 60 | Some(c) => { 61 | let n = c.as_str(); 62 | if n.is_empty() { 63 | continue 64 | } else { 65 | n 66 | } 67 | }, 68 | _ => continue, 69 | }, 70 | match caps.get(2) { 71 | Some(c) => { 72 | let n = c.as_str(); 73 | if n.is_empty() { 74 | "0" 75 | } else { 76 | n 77 | } 78 | }, 79 | _ => "0", 80 | }, 81 | match caps.get(3) { 82 | Some(c) => { 83 | let n = c.as_str(); 84 | if n.is_empty() { 85 | "0" 86 | } else { 87 | n 88 | } 89 | }, 90 | _ => "0", 91 | }, 92 | match caps.get(4) { 93 | Some(c) => c.as_str(), 94 | _ => "", 95 | }), 96 | None => continue, 97 | }; 98 | let tag_version = match Version::parse(tag_version_str.as_str()) { 99 | Ok(ver) => ver, 100 | _ => continue, 101 | }; 102 | if (version_rule.is_none() || version_rule.unwrap().matches(&tag_version)) && (selected_tag.is_none() || tag_version > selected_tag.clone().unwrap()) { 103 | version = Some((tag_name.to_owned(), tag_version.clone())); 104 | selected_tag = Some(tag_version); 105 | } 106 | } 107 | }, 108 | _ => (), 109 | } 110 | version 111 | } 112 | 113 | pub fn get_latest_compat_version(repo: &Repository, rule_tag_name: String) -> String { 114 | match VersionReq::parse(rule_tag_name.as_str()) { 115 | Ok(version_rule) => match get_latest_version(repo, Some(&version_rule)) { 116 | Some((tag_name, _)) => tag_name, 117 | None => rule_tag_name, 118 | }, 119 | _ => rule_tag_name, 120 | } 121 | } 122 | 123 | pub fn get_revision_object(repo: &Repository, pkg_import: String, version: String, should_retry: bool, logger: Logger) -> Option<(Object, String)> { 124 | match repo.revparse_single(version.as_str()) { 125 | Ok(obj) => return Some((obj, version)), 126 | Err(e) => { 127 | if !should_retry { 128 | return None 129 | } 130 | match get_latest_commit(repo) { 131 | Some(ver) => { 132 | logger.error(format!("the version of `{}` changed to `{}` due to {}", pkg_import, ver, e)); 133 | return get_revision_object(repo, pkg_import, ver, false, logger) 134 | }, 135 | None => return None, 136 | } 137 | }, 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rubigo 2 | ====== 3 | [![Build Status](https://travis-ci.org/yaa110/rubigo.svg?branch=master)](https://travis-ci.org/yaa110/rubigo) [![Build status](https://ci.appveyor.com/api/projects/status/gaj2qh18963d0hp1?svg=true)](https://ci.appveyor.com/project/yaa110/rubigo) [![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/yaa110/rubigo/blob/master/LICENSE) [![Version](https://img.shields.io/badge/version-1.0.4-blue.svg)](https://github.com/yaa110/rubigo/releases) 4 | 5 | **Rubigo** is a *DEPRECATED* dependency tool and package manager for [Golang](https://golang.org/), written in [Rust](https://www.rust-lang.org/en-US/). Rubigo uses `vendor` directory (starting from Go 1.5) to install packages, however it is possible to add packages globally (in `GOPATH/src` directory) or make a local package in `vendor` directory. Rubigo respects to manual changes in `vendor` directory and does not delete custom packages. Currently, Rubigo only supports `git` repositories. This source code is licensed under MIT license that can be found in the LICENSE file. 6 | 7 | ## Deprecation 8 | Consider using [Go versioned modules](https://github.com/golang/go/wiki/Modules): 9 | 10 | - `rm -r vendor rubigo.json rubigo.lock` 11 | - `export GO111MODULE=on` 12 | - `go mod init` 13 | 14 | ## Features 15 | - Manage `vendor`, `global` and `local` packages 16 | - Use a custom repository to clone a package 17 | - Support [semantic versioning](http://semver.org/) 18 | - Define package information 19 | - Start a new project (binary or library) 20 | 21 | ## How it works 22 | Rubigo creates two JSON (manifest) files (`rubigo.json` and `rubigo.lock`) inside the directory of Golang project. The `rubigo.json` contains the information of the project and packages which should be installed and maintained, and `rubigo.lock` contains the information of packages which have already been installed in `vendor` directory or globally in `GOPATH/src`. You could edit both files manually or using Rubigo sub-commands, then you can apply them to project's dependencies. Also, it is feasible to start Rubigo in an existing project. 23 | 24 | ## How to install 25 | You can download a pre-built binary from [releases](https://github.com/yaa110/rubigo/releases) page or you can build it manually as following: 26 | 1. Install [Rust](https://www.rust-lang.org/en-US/) programming language. 27 | * On Linux and Mac OS: install `cmake`, `libcurl4-openssl-dev`, `libelf-dev`, `libssl-dev` and `libdw-dev`. 28 | * On Windows: install `cmake`, `Visual Studio C++`. 29 | 2. Use Rust's package manager `cargo` to install the application: `cargo install --git https://github.com/yaa110/rubigo.git` 30 | 31 | ## Sub-commands 32 | - **init, start**: Initializes Rubigo project in an existing directory, e.g. `rubigo init`. This sub-command searches the `vendor` directory for packages which has already been installed. 33 | - **new, create**: Creates a new Golang project, e.g. `rubigo new my-project` or `rubigo new --lib my-library`. This sub-command creates a new directory with the name provided to it containing a new `.go` file and manifest files. 34 | - **get, add**: Adds a package to dependencies and clones it into `vendor` directory, e.g. `rubigo get github.com/blah/blah --repo=github.com/my/custom/repo` (the `--repo` argument is optional). This sub-command could also install packages globally to `GOPATH/src` directory using `--global` flag or create a local package using `--local` flag. 35 | - **update, up**: Updates one or all packages and applies the changes of `rubigo.json` to `rubigo.lock` and packages in `vendor` directory, e.g. `rubigo update github.com/blah/blah`. This sub-command could also delete the package's directory and clone it again using `--clean` flag. If no package name is provided, it updates all the packages. 36 | - **remove, rm**: Removes a package from manifest files and `vendor` directory, e.g. `rubigo remove github.com/blah/blah`. 37 | - **apply, install**: Applies the changes of `rubigo.lock` to packages in `vendor` directory, e.g. `rubigo apply`. This sub-command could also delete the package's directory and clone it again using `--clean` flag. Most of the time, it is used when you have cloned a project and wanted to install missing packages. 38 | - **reset, sync**: Updates manifest files to the list of packages which have already been installed in `vendor` directory, e.g. `rubigo reset`. It is used when you have manually changed the `vendor` directory and wanted to update manifest files. Please note that this subcommand only collects git packages and ignores local packages. 39 | - **list, ls**: Displays a list of packages from `rubigo.lock` file, e.g. `rubigo list`. This sub-command could only list git, local or global packages (or a combination of them) using `--remote`, `--local` or `--global` flags, respectively. 40 | - **info, about**: Displays the information about the project from `rubigo.json` file, e.g. `rubigo info`. 41 | - **help**: Displays the help message, e.g. `rubigo help`. It is also possible to get the information of a sub-command, e.g. `rubigo help get`. 42 | 43 | ## Flags 44 | - **--verbose, -v**: Uses verbose output. 45 | - **--quiet, -q**: Prints no output. 46 | - **--yes, -y**: Continues without prompt for a confirmation. 47 | - **--help, -h**: Displays the help message. 48 | - **--version, -V**: Displays the version of Rubigo. 49 | 50 | ## The manifest format 51 | You can find the template of [rubigo.json](https://github.com/yaa110/rubigo/blob/master/templates/rubigo.json) and [rubigo.lock](https://github.com/yaa110/rubigo/blob/master/templates/rubigo.lock) files in `templates` directory. Both files have a JSON format with the following objects: 52 | 53 | - **info**: Contains the (optional) information about the project. Only `rubigo.json` contains this object. 54 | * **name**: The name of project 55 | * **import**: The import path of project 56 | * **description**: Short description about the project 57 | * **homepage**: Url to the project homepage (should contain the protocol scheme, such as `http://`) 58 | * **license**: The license of the project 59 | * **authors**: An array of project's authors 60 | * **name**: The name of author 61 | * **email**: The email address of author 62 | * **website**: The website url of author (should contain the protocol scheme, such as `http://`) 63 | - **packages**: Containg the information about packages. 64 | * **git**: An array of dependencies cloned from a git repository 65 | * **import**: The import path of package 66 | * **repo**: A custom url to clone the repository 67 | * **version**: The version (a git revision or semantic version) of the project. For more information about the semantic rules, please check [semver](https://github.com/steveklabnik/semver) documentation. 68 | * **local**: An array of local packages in `vendor` directory. 69 | * **global**: An array of global packages in `GOPATH/src` directory. 70 | 71 | ## Contribution 72 | Please feel free to open an issue to report a bug or ask a question, or open a pull request to debug or add more features to Rubigo. 73 | -------------------------------------------------------------------------------- /src/inner/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::path::{Component, Path, PathBuf}; 3 | use std::io::{self, Write}; 4 | use threadpool::ThreadPool; 5 | use num_cpus; 6 | use regex::Regex; 7 | use inner::{git_helper, json_helper}; 8 | use inner::vendor::VENDOR_DIR; 9 | use git2::Repository; 10 | use json::JsonValue; 11 | use inner::logger::Logger; 12 | use curl::easy::Easy; 13 | use std::str; 14 | 15 | pub fn get_current_dir() -> String { 16 | match fs::canonicalize(Path::new(Component::CurDir.as_os_str())) { 17 | Ok(p_buf) => match p_buf.as_path().components().last() { 18 | Some(Component::Normal(name_os_str)) => match name_os_str.to_str() { 19 | Some(name_str) => name_str.to_string(), 20 | None => return "unknown".to_string(), 21 | }, 22 | _ => return "unknown".to_string(), 23 | }, 24 | _ => return "unknown".to_string(), 25 | } 26 | } 27 | 28 | pub fn get_input(msg: &str) -> io::Result { 29 | let mut input = String::new(); 30 | print!("{} ", msg); 31 | let _ = io::stdout().flush(); 32 | match io::stdin().read_line(&mut input) { 33 | Ok(_) => Ok(input), 34 | Err(e) => Err(e), 35 | } 36 | } 37 | 38 | pub fn confirmation_prompt(msg: &str) -> io::Result { 39 | match get_input(msg) { 40 | Ok(input) => match input.to_lowercase().as_str().trim() { 41 | "y" | "yes" | "yea" | "yeah" | "yep" | "yup" => Ok(true), 42 | _ => Ok(false), 43 | }, 44 | Err(e) => Err(e), 45 | } 46 | } 47 | 48 | pub fn version_prompt(repo: &Repository) -> Option<(String, String)> { 49 | let latest_commit = match git_helper::get_latest_commit(repo) { 50 | Some(ver) => ver, 51 | None => return None, 52 | }; 53 | let tag_version = git_helper::get_latest_version(repo, None); 54 | let current_branch = git_helper::get_current_branch(repo); 55 | 56 | if tag_version.is_none() && current_branch.is_none() { 57 | return Some((latest_commit.clone(), latest_commit)); 58 | } 59 | 60 | let mut versions = vec![]; 61 | 62 | if !tag_version.is_none() { 63 | let (tag, ver) = tag_version.unwrap(); 64 | versions.push(("Tilde (Patch)", format!("~{}", ver), tag.clone())); 65 | versions.push(("Caret (Minor)", format!("^{}", ver), tag.clone())); 66 | versions.push(("Exact (Fixed)", format!("={}", ver), tag)); 67 | } 68 | 69 | if !current_branch.is_none() { 70 | let branch_name = current_branch.unwrap(); 71 | versions.push(("Branch (HEAD)", branch_name.clone(), branch_name)); 72 | } 73 | 74 | versions.push(("Latest commit", latest_commit.clone(), latest_commit)); 75 | 76 | let mut msg = String::from("\nVersions:"); 77 | 78 | for i in 0..versions.len() { 79 | msg.push_str(format!("\n[{}] {}: {}", i + 1, versions[i].0, versions[i].1).as_str()) 80 | } 81 | 82 | msg.push_str(format!(" (Default)\nType `q` to cancel.\n\nPlease choose one of the following versions: [1-{}]", versions.len()).as_str()); 83 | 84 | match get_input(msg.as_str()) { 85 | Ok(input) => match input.to_lowercase().as_str().trim() { 86 | "q" | "quit" => { 87 | None 88 | }, 89 | input_str => match input_str.parse::() { 90 | Ok(index) => if index <= versions.len() && index > 0 { 91 | Some((versions[index - 1].2.clone(), versions[index - 1].1.clone())) 92 | } else { 93 | Some((versions[versions.len() - 1].2.clone(), versions[versions.len() - 1].1.clone())) 94 | }, 95 | _ => Some((versions[versions.len() - 1].2.clone(), versions[versions.len() - 1].1.clone())) 96 | }, 97 | }, 98 | _ => None, 99 | } 100 | } 101 | 102 | pub fn new_thread_pool() -> ThreadPool { 103 | let threads_num = num_cpus::get(); 104 | ThreadPool::new(if threads_num > 1 { 105 | threads_num 106 | } else { 107 | 2 108 | }) 109 | } 110 | 111 | pub fn strip_url_scheme(pkg_import: &str) -> String { 112 | let re = match Regex::new(r"https?://") { 113 | Ok(re) => re, 114 | _ => return pkg_import.to_owned(), 115 | }; 116 | re.replace_all(pkg_import, "").into_owned() 117 | } 118 | 119 | pub fn get_path_from_url(pkg_import: &str) -> PathBuf { 120 | let mut pkg_path_buf = PathBuf::from(VENDOR_DIR); 121 | let path_segments = pkg_import.split("/"); 122 | for segment in path_segments { 123 | pkg_path_buf.push(segment) 124 | } 125 | pkg_path_buf 126 | } 127 | 128 | pub fn remove_diff_packages(old_lock: &JsonValue, new_lock: &JsonValue, logger: Logger) { 129 | if old_lock.is_null() { 130 | return 131 | } 132 | 133 | let old_git = &old_lock[json_helper::GIT_KEY]; 134 | if !old_git.is_null() { 135 | let new_git = &new_lock[json_helper::GIT_KEY]; 136 | 'outer: for i in 0..old_git.len() { 137 | let old_pkg_name = match old_git[i][json_helper::IMPORT_KEY].as_str() { 138 | Some(name) => name, 139 | None => continue 'outer, 140 | }; 141 | 'inner: for j in 0..new_git.len() { 142 | let new_pkg_name = match new_git[j][json_helper::IMPORT_KEY].as_str() { 143 | Some(name) => name, 144 | None => continue 'inner, 145 | }; 146 | if old_pkg_name == new_pkg_name { 147 | continue 'outer; 148 | } 149 | } 150 | let _ = remove_package(old_pkg_name, logger); 151 | } 152 | } 153 | 154 | let old_local = &old_lock[json_helper::LOCAL_KEY]; 155 | if !old_local.is_null() { 156 | let new_local = &new_lock[json_helper::LOCAL_KEY]; 157 | 'outer2: for i in 0..old_local.len() { 158 | let old_pkg_name = match old_local[i].as_str() { 159 | Some(name) => name, 160 | None => continue 'outer2, 161 | }; 162 | 'inner2: for j in 0..new_local.len() { 163 | let new_pkg_name = match new_local[j].as_str() { 164 | Some(name) => name, 165 | None => continue 'inner2, 166 | }; 167 | if old_pkg_name == new_pkg_name { 168 | continue 'outer2; 169 | } 170 | } 171 | let _ = remove_package(old_pkg_name, logger); 172 | } 173 | } 174 | } 175 | 176 | pub fn remove_package(dir_path: &str, logger: Logger) -> bool { 177 | let pkg_path_buf: PathBuf = get_path_from_url(dir_path); 178 | let pkg_path = pkg_path_buf.as_path(); 179 | match fs::remove_dir_all(pkg_path) { 180 | Ok(_) => { 181 | logger.verbose("Remove package", dir_path); 182 | let mut parent = pkg_path.parent(); 183 | while parent.is_some() { 184 | match fs::remove_dir(parent.unwrap()) { 185 | Ok(_) => parent = parent.unwrap().parent(), 186 | _ => parent = None, 187 | } 188 | } 189 | }, 190 | Err(e) => { 191 | logger.error(format!("unable to delete `{}` directory: {}", dir_path, e)); 192 | return false 193 | }, 194 | } 195 | true 196 | } 197 | 198 | pub fn modify_golang_org(repo_url: &str) -> (String, Option) { 199 | if repo_url.starts_with("golang.org/x") { 200 | let mut buf = String::new(); 201 | { 202 | let mut handle = Easy::new(); 203 | match handle.url(repo_url) { 204 | Ok(_) => (), 205 | _ => return (format!("http://{}", repo_url), None), 206 | }; 207 | let mut transfer = handle.transfer(); 208 | match transfer.write_function(|data| { 209 | match str::from_utf8(data) { 210 | Ok(s) => { 211 | buf.push_str(s); 212 | Ok(data.len()) 213 | }, 214 | _ => Ok(0), 215 | } 216 | }) { 217 | Ok(_) => (), 218 | _ => return (format!("http://{}", repo_url), None), 219 | }; 220 | match transfer.perform() { 221 | Ok(_) => (), 222 | _ => return (format!("http://{}", repo_url), None), 223 | }; 224 | } 225 | let re = match Regex::new(r#".*go-import.* git ([^'"]*)"?'?>"#) { 226 | Ok(r) => r, 227 | _ => return (format!("http://{}", repo_url), None), 228 | }; 229 | let cap = match re.captures(buf.as_str()) { 230 | Some(c) => c, 231 | None => return (format!("http://{}", repo_url), None), 232 | }; 233 | return match cap.get(1) { 234 | Some(s) => { 235 | let url = s.as_str(); 236 | let re = match Regex::new(r#"[^/]*//[^/]*/(.*)"#) { 237 | Ok(r) => r, 238 | _ => return (url.to_owned(), None), 239 | }; 240 | let cap = match re.captures(url) { 241 | Some(c) => c, 242 | None => return (url.to_owned(), None), 243 | }; 244 | match cap.get(1) { 245 | Some(p) => return (url.to_owned(), Some(format!("golang.org/x/{}", p.as_str()))), 246 | None => (), 247 | }; 248 | (url.to_owned(), None) 249 | }, 250 | _ => (format!("http://{}", repo_url), None), 251 | } 252 | } 253 | (format!("http://{}", repo_url), None) 254 | } 255 | -------------------------------------------------------------------------------- /src/controller/project.rs: -------------------------------------------------------------------------------- 1 | use inner::logger::Logger; 2 | use std::path::Path; 3 | use std::fs::{File, create_dir, create_dir_all, remove_dir_all, remove_file}; 4 | use std::env::current_dir; 5 | use std::fmt::Display; 6 | use git2::Repository; 7 | use std::io::Write; 8 | use inner::{vendor, json_helper, helpers}; 9 | use futures::Future; 10 | use futures_cpupool::CpuPool; 11 | use std::thread; 12 | 13 | pub fn new(name: &str, is_lib: bool, logger: &Logger) { 14 | fn delete_new_project(err: T, path: &Path, current_dir: &Path, logger: &Logger) { 15 | match remove_dir_all(path) { 16 | Ok(_) => logger.verbose("Delete project", current_dir.to_str().unwrap_or("unknown")), 17 | Err(e) => logger.error(format!("unable to delete `{}` directory: {}", path.to_str().unwrap_or("unknown"), e)), 18 | } 19 | logger.fatal(err) 20 | } 21 | 22 | let path = Path::new(name); 23 | let current_dir = match current_dir() { 24 | Ok(path_buf) => path_buf, 25 | Err(e) => { 26 | logger.fatal(e); 27 | return 28 | }, 29 | }; 30 | 31 | if path.exists() { 32 | logger.fatal(format!("the directory `{}` already exists in {:?}", name, current_dir)); 33 | return 34 | } 35 | 36 | match create_dir_all(path.join(vendor::VENDOR_DIR)) { 37 | Ok(_) => { 38 | logger.verbose("Create project", name) 39 | }, 40 | Err(e) => { 41 | logger.fatal(e); 42 | return 43 | }, 44 | } 45 | 46 | let content; 47 | let go_file; 48 | if is_lib { 49 | content = format!("package {}\n\n", name); 50 | go_file = format!("{}.go", name); 51 | } else { 52 | content = String::from("package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, World!\")\n}\n\n"); 53 | go_file = String::from("main.go"); 54 | } 55 | 56 | match File::create(path.join(go_file.as_str())) { 57 | Ok(mut file) => { 58 | match file.write_all(content.as_bytes()) { 59 | Ok(_) => logger.verbose("Create file", go_file), 60 | Err(e) => delete_new_project(e, path, current_dir.as_path(), logger), 61 | }; 62 | }, 63 | Err(e) => delete_new_project(e, path, current_dir.as_path(), logger), 64 | } 65 | 66 | match json_helper::write(path.join("rubigo.json"), name, None) { 67 | Ok(_) => logger.verbose("Create file", "rubigo.json"), 68 | Err(e) => delete_new_project(e, path, current_dir.as_path(), logger), 69 | } 70 | 71 | match Repository::init(path) { 72 | Ok(repo) => logger.verbose("Initialize git", match repo.workdir() { 73 | Some(repo_path) => match repo_path.to_str() { 74 | Some(repo_path_str) => repo_path_str, 75 | None => "unknown", 76 | }, 77 | None => "unknown", 78 | }), 79 | Err(e) => delete_new_project(e, path, current_dir.as_path(), logger), 80 | } 81 | } 82 | 83 | pub fn init(logger: Logger) { 84 | fn delete_init_project(err: T, path: &Path, logger: &Logger) { 85 | match remove_file(path) { 86 | Ok(_) => logger.verbose("Delete file", "rubigo.json"), 87 | _ => (), 88 | } 89 | logger.fatal(err) 90 | } 91 | 92 | let json_path = Path::new("rubigo.json"); 93 | if json_path.exists() { 94 | logger.fatal("Rubigo project has already been initialized"); 95 | return 96 | } 97 | 98 | let lock_path = Path::new("rubigo.lock"); 99 | if lock_path.exists() { 100 | match remove_file(lock_path) { 101 | Ok(_) => logger.verbose("Delete file", "rubigo.lock"), 102 | Err(e) => delete_init_project(e, json_path, &logger), 103 | } 104 | } 105 | let parent_name = helpers::get_current_dir(); 106 | let vendor_path = Path::new(vendor::VENDOR_DIR); 107 | if !vendor_path.exists() { 108 | match json_helper::write(json_path, parent_name.as_str(), None) { 109 | Ok(_) => logger.verbose("Create file", "rubigo.json"), 110 | Err(e) => delete_init_project(e, json_path, &logger), 111 | } 112 | 113 | match create_dir(vendor_path) { 114 | Ok(_) => logger.verbose("Create directory", vendor::VENDOR_DIR), 115 | Err(e) => delete_init_project(e, json_path, &logger), 116 | } 117 | } else { 118 | logger.verbose("Synchronize", "vendor directory"); 119 | let git_packages = vendor::find_packages(logger); 120 | match json_helper::write(json_path, "", Some(object!{ 121 | json_helper::INFO_KEY => object!{ 122 | json_helper::NAME_KEY => parent_name.as_str() 123 | }, 124 | json_helper::PACKAGES_KEY => object!{ 125 | json_helper::GIT_KEY => git_packages.clone(), 126 | json_helper::LOCAL_KEY => array![], 127 | json_helper::GLOBAL_KEY => array![] 128 | } 129 | })) { 130 | Ok(_) => logger.verbose("Create file", "rubigo.json"), 131 | Err(e) => delete_init_project(e, json_path, &logger), 132 | } 133 | 134 | match json_helper::write(Path::new("rubigo.lock"), "", Some(object!{ 135 | json_helper::GIT_KEY => git_packages, 136 | json_helper::LOCAL_KEY => array![], 137 | json_helper::GLOBAL_KEY => array![] 138 | })) { 139 | Ok(_) => logger.verbose("Create file", "rubigo.lock"), 140 | Err(e) => { 141 | match remove_file("rubigo.lock") { 142 | Ok(_) => logger.verbose("Delete file", "rubigo.lock"), 143 | _ => (), 144 | } 145 | delete_init_project(e, json_path, &logger) 146 | }, 147 | } 148 | } 149 | } 150 | 151 | pub fn reset(no_prompt: bool, logger: Logger) { 152 | if no_prompt { 153 | inner_reset(logger); 154 | } else { 155 | match helpers::confirmation_prompt("This sub command might cause unexpected changes in `rubigo.json` and `rubigo.lock` files.\nDo you want to continue? [Y/n]") { 156 | Ok(accepted) => if accepted { 157 | inner_reset(logger); 158 | } else { 159 | logger.error("aborted"); 160 | }, 161 | Err(e) => { 162 | logger.fatal(e); 163 | return 164 | }, 165 | } 166 | } 167 | 168 | fn inner_reset(logger: Logger) { 169 | if !Path::new(vendor::VENDOR_DIR).is_dir() { 170 | logger.fatal("vendor directory not found."); 171 | return 172 | } 173 | 174 | let pool = CpuPool::new(2); 175 | let rubigo_json_future = pool.spawn_fn(|| { 176 | match json_helper::read(Path::new("rubigo.json")) { 177 | Ok(content_json) => Ok(content_json), 178 | Err(e) => Err(e), 179 | } 180 | }); 181 | let rubigo_lock_future = pool.spawn_fn(|| { 182 | match json_helper::read(Path::new("rubigo.lock")) { 183 | Ok(content_json) => Ok(content_json), 184 | Err(e) => Err(e), 185 | } 186 | }); 187 | 188 | logger.verbose("Synchronize", "vendor directory"); 189 | let git_packages = vendor::find_packages(logger); 190 | 191 | let rubigo_json = rubigo_json_future.wait().unwrap_or(object!{}); 192 | let rubigo_lock = rubigo_lock_future.wait().unwrap_or(object!{}); 193 | let mut global_packages = rubigo_lock[json_helper::GLOBAL_KEY].clone(); 194 | if global_packages.is_null() { 195 | global_packages = array![]; 196 | } 197 | let local_packages = &rubigo_lock[json_helper::LOCAL_KEY]; 198 | let mut local_packages_result = array![]; 199 | if !local_packages.is_null() { 200 | for i in 0..local_packages.len() { 201 | let local_pkg = local_packages[i].clone(); 202 | if Path::new(vendor::VENDOR_DIR).join(match local_pkg.as_str() { 203 | Some(val_str) => val_str, 204 | None => continue, 205 | }).is_dir() { 206 | match local_packages_result.push(local_pkg) { 207 | _ => (), 208 | } 209 | } 210 | } 211 | } 212 | 213 | let mut info_obj = rubigo_json[json_helper::INFO_KEY].clone(); 214 | if info_obj.is_null() { 215 | info_obj = object!{}; 216 | } 217 | match json_helper::write(Path::new("rubigo.json"), "", Some(object!{ 218 | json_helper::INFO_KEY => info_obj, 219 | json_helper::PACKAGES_KEY => object!{ 220 | json_helper::GIT_KEY => git_packages.clone(), 221 | json_helper::LOCAL_KEY => local_packages_result.clone(), 222 | json_helper::GLOBAL_KEY => global_packages.clone() 223 | } 224 | })) { 225 | Ok(_) => logger.verbose("Replace file", "rubigo.json"), 226 | Err(e) => { 227 | logger.fatal(format!("unable to write to `rubigo.json`: {}", e)); 228 | return 229 | }, 230 | } 231 | 232 | match json_helper::write(Path::new("rubigo.lock"), "", Some(object!{ 233 | json_helper::GIT_KEY => git_packages, 234 | json_helper::LOCAL_KEY => local_packages_result, 235 | json_helper::GLOBAL_KEY => global_packages 236 | })) { 237 | Ok(_) => logger.verbose("Replace file", "rubigo.lock"), 238 | Err(e) => { 239 | match json_helper::write("rubigo.json", "", Some(rubigo_json)) { 240 | Ok(_) => logger.verbose("Revert file", "rubigo.json"), 241 | Err(e) => logger.error(format!("unable to revert `rubigo.json`: {}", e)), 242 | } 243 | logger.fatal(format!("unable to write to `rubigo.lock`: {}", e)) 244 | }, 245 | } 246 | } 247 | } 248 | 249 | pub fn apply(should_clean: bool, logger: Logger) { 250 | let lock_content = match json_helper::read(Path::new("rubigo.lock")) { 251 | Ok(content) => content, 252 | Err(e) => { 253 | logger.fatal(format!("unable to read `rubigo.lock`: {}", e)); 254 | return 255 | } 256 | }; 257 | 258 | let c_lock = lock_content.clone(); 259 | let local_thread = thread::spawn(move || { 260 | let _ = vendor::install_local_packages(&c_lock[json_helper::LOCAL_KEY], logger); 261 | }); 262 | 263 | let c_lock2 = lock_content.clone(); 264 | let global_thread = thread::spawn(move || { 265 | let _ = vendor::install_global_packages(&c_lock2[json_helper::GLOBAL_KEY], false, logger); 266 | }); 267 | 268 | let _ = vendor::install_git_packages(&lock_content[json_helper::GIT_KEY], "Check package", should_clean, true, logger); 269 | 270 | match local_thread.join() { 271 | Ok(_) => (), 272 | _ => logger.error("unable to join local thread"), 273 | } 274 | 275 | match global_thread.join() { 276 | Ok(_) => (), 277 | _ => logger.error("unable to join global thread"), 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate tempdir; 2 | 3 | use controller::*; 4 | use self::tempdir::TempDir; 5 | use inner::logger::{Logger, Verbosity}; 6 | use std::env; 7 | use inner::json_helper; 8 | use std::fs::{File, remove_dir_all, remove_file}; 9 | use std::io::Read; 10 | 11 | #[test] 12 | fn test_main() { 13 | // Note: Due to setting current working directory for each test, they must not run parallel. 14 | 15 | println!("running test_new_bin:"); 16 | test_new_bin(); 17 | 18 | println!("\nrunning test_new_lib:"); 19 | test_new_lib(); 20 | 21 | println!("\nrunning test_init:"); 22 | test_init(); 23 | 24 | println!("\nrunning test_get_git:"); 25 | test_get_git(); 26 | 27 | println!("\nrunning test_get_git_repo:"); 28 | test_get_git_repo(); 29 | 30 | println!("\nrunning test_get_local:"); 31 | test_get_local(); 32 | 33 | println!("\nrunning test_apply:"); 34 | test_apply(); 35 | 36 | println!("\nrunning test_reset:"); 37 | test_reset(); 38 | 39 | println!("\nrunning test_remove:"); 40 | test_remove(); 41 | 42 | println!("\nrunning test_update_one:"); 43 | test_update_one(); 44 | 45 | println!("\nrunning test_update_all:"); 46 | test_update_all(); 47 | } 48 | 49 | fn test_new_bin() { 50 | let project_name = "test-bin-project"; 51 | 52 | let tmp_dir = TempDir::new("rubigo-new-bin").unwrap(); 53 | env::set_current_dir(tmp_dir.path()).unwrap(); 54 | 55 | let logger = Logger::new(Verbosity::High); 56 | 57 | project::new(project_name, false, &logger); 58 | let project_path_buf = tmp_dir.path().join(project_name); 59 | let project_path = project_path_buf.as_path(); 60 | 61 | let json_content = json_helper::read(project_path.join("rubigo.json").as_path()).unwrap(); 62 | let project_name_json = json_content[json_helper::INFO_KEY][json_helper::NAME_KEY].as_str().unwrap(); 63 | assert_eq!(project_name, project_name_json); 64 | 65 | let mut file = File::open(project_path.join("main.go").as_path()).unwrap(); 66 | let mut contents = String::new(); 67 | file.read_to_string(&mut contents).unwrap(); 68 | assert_eq!(contents.as_str(), "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, World!\")\n}\n\n"); 69 | } 70 | 71 | fn test_new_lib() { 72 | let project_name = "test-lib-project"; 73 | 74 | let tmp_dir = TempDir::new("rubigo-new-lib").unwrap(); 75 | env::set_current_dir(tmp_dir.path()).unwrap(); 76 | 77 | let logger = Logger::new(Verbosity::High); 78 | 79 | project::new(project_name, true, &logger); 80 | let project_path_buf = tmp_dir.path().join(project_name); 81 | let project_path = project_path_buf.as_path(); 82 | 83 | let json_content = json_helper::read(project_path.join("rubigo.json").as_path()).unwrap(); 84 | let project_name_json = json_content[json_helper::INFO_KEY][json_helper::NAME_KEY].as_str().unwrap(); 85 | assert_eq!(project_name, project_name_json); 86 | 87 | let mut file = File::open(project_path.join(format!("{}.go", project_name)).as_path()).unwrap(); 88 | let mut contents = String::new(); 89 | file.read_to_string(&mut contents).unwrap(); 90 | assert_eq!(contents.as_str(), format!("package {}\n\n", project_name).as_str()); 91 | } 92 | 93 | fn test_init() { 94 | let tmp_dir = TempDir::new("rubigo-init").unwrap(); 95 | env::set_current_dir(tmp_dir.path()).unwrap(); 96 | 97 | let logger = Logger::new(Verbosity::High); 98 | 99 | project::init(logger); 100 | 101 | let json_content = json_helper::read(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 102 | let project_name_json = json_content[json_helper::INFO_KEY][json_helper::NAME_KEY].as_str().unwrap(); 103 | assert_eq!(tmp_dir.path().file_name().unwrap().to_str().unwrap(), project_name_json); 104 | } 105 | 106 | fn test_get_git() { 107 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 108 | env::set_current_dir(tmp_dir.path()).unwrap(); 109 | 110 | let logger = Logger::new(Verbosity::High); 111 | 112 | project::init(logger); 113 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 114 | 115 | let mut file = File::open(tmp_dir.path().join("vendor").as_path().join("github.com").as_path().join("yaa110").as_path().join("test-repo-for-rubigo").as_path().join("file-to-read")).unwrap(); 116 | let mut contents = String::new(); 117 | file.read_to_string(&mut contents).unwrap(); 118 | assert_eq!(contents.as_str(), "rubigo\n"); 119 | 120 | let json_content = json_helper::read(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 121 | let import_json = json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY][0][json_helper::IMPORT_KEY].as_str().unwrap(); 122 | assert_eq!("github.com/yaa110/test-repo-for-rubigo", import_json); 123 | 124 | let lock_content = json_helper::read(tmp_dir.path().join("rubigo.lock").as_path()).unwrap(); 125 | let import_lock = lock_content[json_helper::GIT_KEY][0][json_helper::IMPORT_KEY].as_str().unwrap(); 126 | assert_eq!("github.com/yaa110/test-repo-for-rubigo", import_lock); 127 | } 128 | 129 | fn test_get_git_repo() { 130 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 131 | env::set_current_dir(tmp_dir.path()).unwrap(); 132 | 133 | let logger = Logger::new(Verbosity::High); 134 | 135 | project::init(logger); 136 | package::get("a/b/c", Some("https://github.com/yaa110/test-repo-for-rubigo"), true, false, false, logger); 137 | 138 | let mut file = File::open(tmp_dir.path().join("vendor").as_path().join("a").as_path().join("b").as_path().join("c").as_path().join("file-to-read")).unwrap(); 139 | let mut contents = String::new(); 140 | file.read_to_string(&mut contents).unwrap(); 141 | assert_eq!(contents.as_str(), "rubigo\n"); 142 | 143 | let json_content = json_helper::read(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 144 | let repo_json = json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY][0][json_helper::REPO_KEY].as_str().unwrap(); 145 | assert_eq!("https://github.com/yaa110/test-repo-for-rubigo", repo_json); 146 | 147 | let lock_content = json_helper::read(tmp_dir.path().join("rubigo.lock").as_path()).unwrap(); 148 | let repo_lock = lock_content[json_helper::GIT_KEY][0][json_helper::REPO_KEY].as_str().unwrap(); 149 | assert_eq!("https://github.com/yaa110/test-repo-for-rubigo", repo_lock); 150 | } 151 | 152 | fn test_get_local() { 153 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 154 | env::set_current_dir(tmp_dir.path()).unwrap(); 155 | 156 | let logger = Logger::new(Verbosity::High); 157 | 158 | project::init(logger); 159 | package::get("new-dir", None, true, false, true, logger); 160 | 161 | assert!(tmp_dir.path().join("vendor").as_path().join("new-dir").as_path().exists()) 162 | } 163 | 164 | fn test_apply() { 165 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 166 | env::set_current_dir(tmp_dir.path()).unwrap(); 167 | 168 | let logger = Logger::new(Verbosity::High); 169 | 170 | project::init(logger); 171 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 172 | remove_dir_all(tmp_dir.path().join("vendor").as_path()).unwrap(); 173 | 174 | project::apply(false, logger); 175 | 176 | let mut file = File::open(tmp_dir.path().join("vendor").as_path().join("github.com").as_path().join("yaa110").as_path().join("test-repo-for-rubigo").as_path().join("file-to-read")).unwrap(); 177 | let mut contents = String::new(); 178 | file.read_to_string(&mut contents).unwrap(); 179 | assert_eq!(contents.as_str(), "rubigo\n"); 180 | } 181 | 182 | fn test_reset() { 183 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 184 | env::set_current_dir(tmp_dir.path()).unwrap(); 185 | 186 | let logger = Logger::new(Verbosity::High); 187 | 188 | project::init(logger); 189 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 190 | remove_file(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 191 | remove_file(tmp_dir.path().join("rubigo.lock").as_path()).unwrap(); 192 | 193 | project::reset(true, logger); 194 | 195 | let json_content = json_helper::read(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 196 | let import_json = json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY][0][json_helper::IMPORT_KEY].as_str().unwrap(); 197 | assert_eq!("github.com/yaa110/test-repo-for-rubigo", import_json); 198 | 199 | let lock_content = json_helper::read(tmp_dir.path().join("rubigo.lock").as_path()).unwrap(); 200 | let import_lock = lock_content[json_helper::GIT_KEY][0][json_helper::IMPORT_KEY].as_str().unwrap(); 201 | assert_eq!("github.com/yaa110/test-repo-for-rubigo", import_lock); 202 | } 203 | 204 | fn test_remove() { 205 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 206 | env::set_current_dir(tmp_dir.path()).unwrap(); 207 | 208 | let logger = Logger::new(Verbosity::High); 209 | 210 | project::init(logger); 211 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 212 | 213 | package::remove("github.com/yaa110/test-repo-for-rubigo", logger); 214 | 215 | let json_content = json_helper::read(tmp_dir.path().join("rubigo.json").as_path()).unwrap(); 216 | assert_eq!(json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].len(), 0); 217 | 218 | let lock_content = json_helper::read(tmp_dir.path().join("rubigo.lock").as_path()).unwrap(); 219 | assert_eq!(lock_content[json_helper::GIT_KEY].len(), 0); 220 | 221 | assert!(!tmp_dir.path().join("vendor").as_path().exists()) 222 | } 223 | 224 | fn test_update_one() { 225 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 226 | env::set_current_dir(tmp_dir.path()).unwrap(); 227 | 228 | let logger = Logger::new(Verbosity::High); 229 | 230 | project::init(logger); 231 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 232 | remove_dir_all(tmp_dir.path().join("vendor").as_path()).unwrap(); 233 | 234 | package::update(Some("github.com/yaa110/test-repo-for-rubigo"), false, logger); 235 | 236 | let mut file = File::open(tmp_dir.path().join("vendor").as_path().join("github.com").as_path().join("yaa110").as_path().join("test-repo-for-rubigo").as_path().join("file-to-read")).unwrap(); 237 | let mut contents = String::new(); 238 | file.read_to_string(&mut contents).unwrap(); 239 | assert_eq!(contents.as_str(), "rubigo\n"); 240 | } 241 | 242 | fn test_update_all() { 243 | let tmp_dir = TempDir::new("rubigo-get-git").unwrap(); 244 | env::set_current_dir(tmp_dir.path()).unwrap(); 245 | 246 | let logger = Logger::new(Verbosity::High); 247 | 248 | project::init(logger); 249 | package::get("github.com/yaa110/test-repo-for-rubigo", None, true, false, false, logger); 250 | remove_dir_all(tmp_dir.path().join("vendor").as_path()).unwrap(); 251 | 252 | package::update(None, false, logger); 253 | 254 | let mut file = File::open(tmp_dir.path().join("vendor").as_path().join("github.com").as_path().join("yaa110").as_path().join("test-repo-for-rubigo").as_path().join("file-to-read")).unwrap(); 255 | let mut contents = String::new(); 256 | file.read_to_string(&mut contents).unwrap(); 257 | assert_eq!(contents.as_str(), "rubigo\n"); 258 | } 259 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // In the name of Allah 2 | // -------------------------------- 3 | 4 | extern crate clap; 5 | extern crate git2; 6 | #[macro_use] 7 | extern crate json; 8 | extern crate threadpool; 9 | extern crate num_cpus; 10 | extern crate futures; 11 | extern crate futures_cpupool; 12 | extern crate semver; 13 | extern crate regex; 14 | extern crate curl; 15 | 16 | mod inner; 17 | mod controller; 18 | #[cfg(test)] 19 | mod tests; 20 | 21 | use clap::{Arg, App, SubCommand, AppSettings}; 22 | use std::process; 23 | use controller::*; 24 | use inner::logger::{Logger, Verbosity}; 25 | 26 | const VERSION: &'static str = "1.0.4"; 27 | 28 | fn main() { 29 | let matches = App::new("Rubigo") 30 | .version(VERSION) 31 | .name("Rubigo") 32 | .setting(AppSettings::VersionlessSubcommands) 33 | .about("Golang dependency tool and package manager\nFor more information, please visit https://github.com/yaa110/rubigo") 34 | .arg(Arg::with_name("verbose") 35 | .short("v") 36 | .long("verbose") 37 | .help("Use verbose output") 38 | .takes_value(false)) 39 | .arg(Arg::with_name("no-prompt") 40 | .short("y") 41 | .long("yes") 42 | .help("Continue without prompt for a confirmation") 43 | .takes_value(false)) 44 | .arg(Arg::with_name("quiet") 45 | .short("q") 46 | .long("quiet") 47 | .conflicts_with("verbose") 48 | .help("Print no output") 49 | .takes_value(false)) 50 | .subcommand(SubCommand::with_name("new") 51 | .visible_alias("create") 52 | .arg(Arg::with_name("name") 53 | .help("The name of project") 54 | .required(true)) 55 | .arg(Arg::with_name("library") 56 | .short("l") 57 | .long("lib") 58 | .help("Create a new library project") 59 | .conflicts_with("binary") 60 | .takes_value(false)) 61 | .arg(Arg::with_name("binary") 62 | .short("b") 63 | .long("bin") 64 | .help("Create a new executable project (Default)") 65 | .takes_value(false)) 66 | .about("Create a new Golang project")) 67 | .subcommand(SubCommand::with_name("init") 68 | .visible_alias("start") 69 | .about("Initialize Rubigo project in an existing directory")) 70 | .subcommand(SubCommand::with_name("reset") 71 | .visible_alias("sync") 72 | .about("Update `rubigo.json` and `rubigo.lock` to the list of packages in `vendor` directory")) 73 | .subcommand(SubCommand::with_name("get") 74 | .visible_alias("add") 75 | .arg(Arg::with_name("package") 76 | .help("The import path of package") 77 | .required(true)) 78 | .arg(Arg::with_name("repository") 79 | .short("r") 80 | .long("repo") 81 | .value_name("repository") 82 | .help("Clone the package from the provided `repository` rather than its main url") 83 | .require_equals(true) 84 | .required(false) 85 | .conflicts_with_all(&["local", "global"]) 86 | .takes_value(true)) 87 | .arg(Arg::with_name("global") 88 | .short("g") 89 | .long("global") 90 | .help("Install the package in `GOPATH/src` directory") 91 | .required(false)) 92 | .arg(Arg::with_name("local") 93 | .short("l") 94 | .long("local") 95 | .conflicts_with("global") 96 | .help("Create a new local package in `vendor` directory") 97 | .required(false)) 98 | .about("Add a package to dependencies and clone it into `vendor` directory")) 99 | .subcommand(SubCommand::with_name("remove") 100 | .visible_alias("rm") 101 | .arg(Arg::with_name("package") 102 | .help("The import path of package") 103 | .required(true)) 104 | .about("Remove a package from manifest and `vendor` directory")) 105 | .subcommand(SubCommand::with_name("update") 106 | .visible_alias("up") 107 | .arg(Arg::with_name("clean") 108 | .short("c") 109 | .long("clean") 110 | .help("Remove the package directory and clone from the repository") 111 | .takes_value(false)) 112 | .arg(Arg::with_name("package") 113 | .help("The import path of package")) 114 | .arg(Arg::with_name("all") 115 | .short("a") 116 | .long("all") 117 | .help("Update all packages (Default)") 118 | .conflicts_with("package") 119 | .takes_value(false)) 120 | .about("Update one or all packages and apply the changes of `rubigo.json` to `rubigo.lock` and packages in `vendor` directory")) 121 | .subcommand(SubCommand::with_name("list") 122 | .visible_alias("ls") 123 | .arg(Arg::with_name("all") 124 | .short("a") 125 | .long("all") 126 | .conflicts_with_all(&["global", "remote", "local"]) 127 | .help("List all packages from `rubigo.lock` file (Default)") 128 | .takes_value(false)) 129 | .arg(Arg::with_name("local") 130 | .short("l") 131 | .long("local") 132 | .help("List local packages from `rubigo.lock` file") 133 | .takes_value(false)) 134 | .arg(Arg::with_name("global") 135 | .short("g") 136 | .long("global") 137 | .help("List global packages from `rubigo.lock` file") 138 | .takes_value(false)) 139 | .arg(Arg::with_name("remote") 140 | .short("r") 141 | .long("remote") 142 | .help("List remote (git) packages from `rubigo.lock` file") 143 | .takes_value(false)) 144 | .about("Display a list of dependencies from `rubigo.lock` file")) 145 | .subcommand(SubCommand::with_name("apply") 146 | .visible_alias("install") 147 | .arg(Arg::with_name("clean") 148 | .short("c") 149 | .long("clean") 150 | .help("Remove the package directory and clone from the repository") 151 | .takes_value(false)) 152 | .about("Apply the changes of `rubigo.lock` to packages in `vendor` directory")) 153 | .subcommand(SubCommand::with_name("info") 154 | .visible_alias("about") 155 | .about("Display the information about this Rubigo project")) 156 | .get_matches(); 157 | 158 | let logger = Logger::new(if matches.is_present("verbose") { 159 | Verbosity::High 160 | } else if matches.is_present("quiet") { 161 | Verbosity::None 162 | } else { 163 | Verbosity::Low 164 | }); 165 | 166 | match matches.subcommand_name() { 167 | Some("apply") => { 168 | let apply_matches = match matches.subcommand_matches("apply") { 169 | Some(args) => args, 170 | None => { 171 | logger.fatal("unable to get argument of `apply` sub command"); 172 | return 173 | }, 174 | }; 175 | project::apply(apply_matches.is_present("clean"), logger) 176 | }, 177 | Some("get") => { 178 | let get_matches = match matches.subcommand_matches("get") { 179 | Some(args) => args, 180 | None => { 181 | logger.fatal("unable to get argument of `get` sub command"); 182 | return 183 | }, 184 | }; 185 | package::get(match get_matches.value_of("package") { 186 | Some(pkg) => pkg, 187 | None => { 188 | logger.fatal("unable to get `package` argument of `get` sub command"); 189 | return 190 | } 191 | }, get_matches.value_of("repository"), matches.is_present("no-prompt"), get_matches.is_present("global"), get_matches.is_present("local"), logger); 192 | }, 193 | Some("info") => info::display(&logger), 194 | Some("init") => project::init(logger), 195 | Some("reset") => project::reset(matches.is_present("no-prompt"), logger), 196 | Some("list") => { 197 | let list_matches = match matches.subcommand_matches("list") { 198 | Some(args) => args, 199 | None => { 200 | logger.fatal("unable to get argument of `list` sub command"); 201 | return 202 | }, 203 | }; 204 | list::list(list_matches.is_present("local"), list_matches.is_present("remote"), list_matches.is_present("global"), &logger); 205 | }, 206 | Some("new") => { 207 | let new_matches = match matches.subcommand_matches("new") { 208 | Some(args) => args, 209 | None => { 210 | logger.fatal("unable to get argument of `new` sub command"); 211 | return 212 | }, 213 | }; 214 | project::new(match new_matches.value_of("name") { 215 | Some(value) => value, 216 | None => { 217 | logger.fatal("unable to get `name` argument of `new` sub command"); 218 | return 219 | }, 220 | }, new_matches.is_present("library"), &logger) 221 | }, 222 | Some("remove") => package::remove(match matches.subcommand_matches("remove") { 223 | Some(args) => match args.value_of("package") { 224 | Some(value) => value, 225 | None => { 226 | logger.fatal("unable to get argument of `remove` sub command"); 227 | return 228 | }, 229 | }, 230 | None => { 231 | logger.fatal("unable to get argument of `remove` sub command"); 232 | return 233 | }, 234 | }, logger), 235 | Some("update") => { 236 | let update_matches = match matches.subcommand_matches("update") { 237 | Some(args) => args, 238 | None => { 239 | logger.fatal("unable to get argument of `update` sub command"); 240 | return 241 | }, 242 | }; 243 | if update_matches.is_present("package") { 244 | package::update(Some(match update_matches.value_of("package") { 245 | Some(value) => value, 246 | None => { 247 | logger.fatal("unable to get `package` argument of `update` sub command"); 248 | return 249 | }, 250 | }), update_matches.is_present("clean"), logger) 251 | } else { 252 | package::update(None, update_matches.is_present("clean"), logger) 253 | } 254 | }, 255 | _ => { 256 | logger.error("No sub command has been provided. Please run `rubigo --help` for more information"); 257 | process::exit(1) 258 | }, 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/inner/vendor.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, Component}; 2 | use std::fs::{read_dir, remove_dir_all, create_dir_all}; 3 | use git2::{Repository, BranchType, ResetType}; 4 | use std::ffi::OsStr; 5 | use json::JsonValue; 6 | use std::sync::mpsc::{channel, Sender}; 7 | use std::sync::{Arc, Mutex}; 8 | use inner::logger::Logger; 9 | use inner::{git_helper, go, helpers, json_helper}; 10 | 11 | pub const VENDOR_DIR: &'static str = "vendor"; 12 | 13 | pub fn find_packages(logger: Logger) -> JsonValue { 14 | let packages = Arc::new(Mutex::new(array![])); 15 | let pool = helpers::new_thread_pool(); 16 | let (tx, rx) = channel(); 17 | let counter = Arc::new(Mutex::new(0)); 18 | 19 | match counter.lock() { 20 | Ok(mut ptr) => *ptr += 1, 21 | _ => (), 22 | } 23 | let cp_tx = tx.clone(); 24 | let cp_counter = counter.clone(); 25 | let cp_pkgs = packages.clone(); 26 | pool.execute(move || { 27 | parse_dir(String::from(VENDOR_DIR), cp_pkgs, cp_tx, cp_counter, logger); 28 | }); 29 | 30 | while match counter.lock() { 31 | Ok(ptr) => *ptr > 0, 32 | _ => false, 33 | } { 34 | match counter.lock() { 35 | Ok(mut ptr) => *ptr -= 1, 36 | _ => (), 37 | } 38 | match rx.recv() { 39 | Ok(path_opt) => match path_opt { 40 | Some(p) => { 41 | match counter.lock() { 42 | Ok(mut ptr) => *ptr += 1, 43 | _ => (), 44 | } 45 | let cp_tx = tx.clone(); 46 | let cp_counter = counter.clone(); 47 | let cp_pkgs = packages.clone(); 48 | pool.execute(move || { 49 | parse_dir(p, cp_pkgs, cp_tx, cp_counter, logger); 50 | }); 51 | }, 52 | None => (), 53 | }, 54 | _ => (), 55 | } 56 | } 57 | match Arc::try_unwrap(packages) { 58 | Ok(pkgs_mut) => match pkgs_mut.into_inner() { 59 | Ok(pkgs_array) => pkgs_array, 60 | _ => array![], 61 | }, 62 | _ => array![], 63 | } 64 | } 65 | 66 | pub fn install_local_packages(local_packages: &JsonValue, logger: Logger) -> JsonValue { 67 | let mut installed_packages = array![]; 68 | if !local_packages.is_null() { 69 | for i in 0..local_packages.len() { 70 | let local_pkg = match local_packages[i].as_str() { 71 | Some(val_str) => val_str, 72 | None => continue, 73 | }; 74 | let dir_path = Path::new(VENDOR_DIR).join(local_pkg); 75 | if !dir_path.is_dir() { 76 | match create_dir_all(dir_path) { 77 | Ok(_) => { 78 | let _ = installed_packages.push(local_pkg); 79 | logger.verbose("Create directory", local_pkg) 80 | }, 81 | Err(e) => logger.error(e), 82 | } 83 | } else { 84 | let _ = installed_packages.push(local_pkg); 85 | } 86 | } 87 | } 88 | installed_packages 89 | } 90 | 91 | pub fn install_global_packages(global_packages: &JsonValue, should_update: bool, logger: Logger) -> JsonValue { 92 | let mut installed_packages = array![]; 93 | if !global_packages.is_null() { 94 | for i in 0..global_packages.len() { 95 | let global_pkg = match global_packages[i].as_str() { 96 | Some(val_str) => val_str, 97 | None => continue, 98 | }; 99 | match go::get(global_pkg, should_update) { 100 | true => { 101 | let _ = installed_packages.push(global_pkg); 102 | logger.verbose("Global package", global_pkg) 103 | }, 104 | false => logger.error(format!("Unable to install global package `{}`", global_pkg)), 105 | } 106 | } 107 | } 108 | installed_packages 109 | } 110 | 111 | pub fn install_git_packages(packages: &JsonValue, msg_title: &str, should_clean: bool, is_apply: bool, logger: Logger) -> JsonValue { 112 | if packages.is_null() { 113 | return array![] 114 | } 115 | 116 | let length = packages.len(); 117 | if length == 0 { 118 | return array![] 119 | } 120 | 121 | let pool = helpers::new_thread_pool(); 122 | let (tx, rx) = channel(); 123 | 124 | for i in 0..length { 125 | let package = packages[i].clone(); 126 | let c_tx = tx.clone(); 127 | pool.execute(move || { 128 | update_package(package, should_clean, is_apply, c_tx, logger); 129 | }); 130 | } 131 | 132 | let mut git_packages = array![]; 133 | for pkg in rx.iter().take(length) { 134 | logger.verbose(msg_title, match pkg[json_helper::IMPORT_KEY].as_str() { 135 | Some(import_str) => import_str, 136 | None => continue, 137 | }); 138 | if !is_apply { 139 | let _ = git_packages.push(pkg); 140 | } 141 | } 142 | git_packages 143 | } 144 | 145 | pub fn update_package(package: JsonValue, should_clean: bool, is_apply: bool, tx: Sender, logger: Logger) { 146 | let mut mut_pkg = package.clone(); 147 | let pkg_import_raw = helpers::strip_url_scheme(match package[json_helper::IMPORT_KEY].as_str() { 148 | Some(import_str) => import_str, 149 | None => { 150 | logger.error("unable to get `import` value"); 151 | let _ = tx.send(mut_pkg); 152 | return 153 | }, 154 | }); 155 | let (http_import, modified_pkg_path) = helpers::modify_golang_org(pkg_import_raw.as_str()); 156 | let pkg_import = pkg_import_raw.as_str(); 157 | let repo_url = match package[json_helper::REPO_KEY].as_str() { 158 | Some(repo_str) => repo_str, 159 | None => http_import.as_str(), 160 | }; 161 | 162 | let modified_pkg_import_path = if modified_pkg_path.is_some() { 163 | modified_pkg_path.unwrap() 164 | } else { 165 | pkg_import_raw.clone() 166 | }; 167 | let pkg_path_buf = helpers::get_path_from_url(modified_pkg_import_path.as_str()); 168 | let pkg_path = pkg_path_buf.as_path(); 169 | if should_clean && pkg_path.exists() { 170 | match remove_dir_all(pkg_path) { 171 | Ok(_) => logger.verbose("Clean package", pkg_path.to_str().unwrap_or("unknown")), 172 | Err(e) => { 173 | logger.error(format!("{} {}", pkg_import, e)); 174 | let _ = tx.send(mut_pkg); 175 | return 176 | } 177 | } 178 | } 179 | 180 | let repo = if should_clean || !pkg_path.is_dir() { 181 | match create_dir_all(pkg_path) { 182 | Ok(_) => logger.verbose("Create directory", &pkg_import), 183 | Err(e) => { 184 | logger.fatal(e); 185 | return 186 | } 187 | } 188 | 189 | match Repository::clone(repo_url, pkg_path) { 190 | Ok(repo) => { 191 | logger.verbose("Clone repository", pkg_import); 192 | repo 193 | }, 194 | Err(e) => { 195 | logger.error(format!("{} {}", pkg_import, e)); 196 | let _ = tx.send(mut_pkg); 197 | return 198 | }, 199 | } 200 | } else { 201 | match Repository::open(pkg_path) { 202 | Ok(repo) => { 203 | logger.verbose("Open repository", pkg_import); 204 | if !is_apply { 205 | match repo.remotes() { 206 | Ok(remotes) => match remotes.get(0) { 207 | Some(remote_name) => match repo.find_remote(remote_name) { 208 | Ok(mut remote) => match remote.fetch(&[], None, None) { 209 | Ok(_) => { 210 | logger.verbose("Fetch repository", pkg_import); 211 | match repo.branches(Some(BranchType::Local)) { 212 | Ok(branches) => for branch in branches { 213 | match branch { 214 | Ok(br) => { 215 | let branch = br.0; 216 | let branch_ref_name = match branch.get().name() { 217 | Some(name) => name.to_owned(), 218 | None => continue, 219 | }; 220 | let remote_name = match branch.upstream() { 221 | Ok(remote_branch) => match remote_branch.name() { 222 | Ok(remote_branch_name) => match remote_branch_name { 223 | Some(name) => name.to_owned(), 224 | None => continue, 225 | }, 226 | _ => continue, 227 | }, 228 | _ => continue, 229 | }; 230 | let remote_object = match repo.revparse_single(remote_name.as_str()) { 231 | Ok(obj) => obj, 232 | _ => continue, 233 | }; 234 | match repo.set_head(branch_ref_name.as_str()) { 235 | Ok(_) => (), 236 | _ => continue, 237 | } 238 | match repo.reset(&remote_object, ResetType::Hard, None) { 239 | Ok(_) => logger.verbose("Update branch", format!("{} {}", pkg_import, branch.name().unwrap_or(None).unwrap_or("unknown"))), 240 | _ => continue, 241 | } 242 | }, 243 | _ => (), 244 | } 245 | }, 246 | _ => (), 247 | } 248 | }, 249 | Err(e) => { 250 | logger.error(format!("{} {}", pkg_import, e)); 251 | let _ = tx.send(mut_pkg); 252 | return 253 | }, 254 | }, 255 | Err(e) => { 256 | logger.error(format!("{} {}", pkg_import, e)); 257 | let _ = tx.send(mut_pkg); 258 | return 259 | }, 260 | }, 261 | None => { 262 | logger.error(format!("{} unable to get remote name of", pkg_import)); 263 | let _ = tx.send(mut_pkg); 264 | return 265 | }, 266 | }, 267 | Err(e) => { 268 | logger.error(format!("{} {}", pkg_import, e)); 269 | let _ = tx.send(mut_pkg); 270 | return 271 | }, 272 | } 273 | } 274 | repo 275 | }, 276 | Err(e) => { 277 | logger.error(format!("{} {}", pkg_import, e)); 278 | let _ = tx.send(mut_pkg); 279 | return 280 | } 281 | } 282 | }; 283 | 284 | let mut version = match package[json_helper::VERSION_KEY].as_str() { 285 | Some(version_str) => version_str.to_owned(), 286 | None => { 287 | logger.error(format!("{} unable to get `version` value", pkg_import)); 288 | let _ = tx.send(mut_pkg); 289 | return 290 | }, 291 | }; 292 | 293 | if !is_apply { 294 | version = git_helper::get_latest_compat_version(&repo, version); 295 | } 296 | 297 | let version_object = match git_helper::get_revision_object(&repo, pkg_import.to_owned(), version, true, logger) { 298 | Some(tup) => { 299 | mut_pkg[json_helper::VERSION_KEY] = tup.1.clone().into(); 300 | tup.0 301 | }, 302 | None => { 303 | logger.error(format!("unable to parse the version of `{}`", pkg_import)); 304 | let _ = tx.send(mut_pkg); 305 | return 306 | } 307 | }; 308 | 309 | match repo.set_head_detached(version_object.id()) { 310 | Ok(_) => (), 311 | Err(e) => { 312 | logger.error(format!("{} {}", pkg_import, e)); 313 | let _ = tx.send(mut_pkg); 314 | return 315 | }, 316 | } 317 | 318 | match repo.reset(&version_object, ResetType::Hard, None){ 319 | Ok(_) => (), 320 | Err(e) => { 321 | logger.error(format!("{} {}", pkg_import, e)); 322 | let _ = tx.send(mut_pkg); 323 | return 324 | }, 325 | } 326 | 327 | let _ = tx.send(mut_pkg); 328 | } 329 | 330 | fn parse_dir(dir_path: String, packages: Arc>, tx: Sender>, counter: Arc>, logger: Logger) { 331 | match read_dir(Path::new(dir_path.as_str())) { 332 | Ok(paths) => { 333 | for entry in paths { 334 | match entry { 335 | Ok(p) => { 336 | let path_buf = p.path(); 337 | let path: &Path = path_buf.as_path(); 338 | if path.is_dir() { 339 | match counter.lock() { 340 | Ok(mut ptr) => *ptr += 1, 341 | _ => (), 342 | } 343 | if path.join(".git").as_path().is_dir() { 344 | match Repository::open(path) { 345 | Ok(repo) => { 346 | match parse_repository(repo, &logger) { 347 | Some(pkg) => match packages.lock() { 348 | Ok(mut ptr) => { 349 | let _ = ptr.push(pkg); 350 | }, 351 | _ => (), 352 | }, 353 | None => (), 354 | } 355 | }, 356 | _ => (), 357 | } 358 | tx.send(None).unwrap(); 359 | } else { 360 | match path.to_str() { 361 | Some(path_str) => { 362 | let _ = tx.send(Some(path_str.to_owned())); 363 | }, 364 | None => { 365 | let _ = tx.send(None); 366 | }, 367 | } 368 | } 369 | } 370 | }, 371 | _ => (), 372 | } 373 | } 374 | }, 375 | _ => (), 376 | } 377 | let _ = tx.send(None); 378 | } 379 | 380 | fn parse_import(path: &Path) -> String { 381 | let mut parts = Vec::new(); 382 | let mut is_vendor_found = false; 383 | let vendor_os_str = OsStr::new(VENDOR_DIR); 384 | let git_os_str = OsStr::new(".git"); 385 | for comp in path.components() { 386 | match comp { 387 | Component::Normal(c) => { 388 | if is_vendor_found && c != git_os_str { 389 | let c_str = match c.to_str() { 390 | Some(s) => s, 391 | None => continue, 392 | }; 393 | parts.push(c_str); 394 | } else if c == vendor_os_str { 395 | is_vendor_found = true; 396 | } 397 | }, 398 | _ => (), 399 | } 400 | } 401 | parts.join("/") 402 | } 403 | 404 | fn parse_repository(repo: Repository, logger: &Logger) -> Option { 405 | let mut pkg = object!{}; 406 | match repo.head() { 407 | Ok(r) => match r.resolve() { 408 | Ok (ref reference) => match reference.target() { 409 | Some(o_id) => { 410 | pkg[json_helper::VERSION_KEY] = if reference.is_tag() { 411 | match repo.find_tag(o_id) { 412 | Ok(tag) => tag.name().unwrap_or(format!("{}", o_id).as_str()).into(), 413 | _ => format!("{}", o_id).into(), 414 | } 415 | } else if reference.is_branch() { 416 | reference.shorthand().unwrap_or(format!("{}", o_id).as_str()).into() 417 | } else { 418 | format!("{}", o_id).into() 419 | }; 420 | }, 421 | None => return None, 422 | }, 423 | _ => return None, 424 | }, 425 | _ => return None, 426 | } 427 | let pkg_import = parse_import(repo.path()); 428 | logger.verbose("Find package", &pkg_import); 429 | pkg[json_helper::IMPORT_KEY] = pkg_import.into(); 430 | Some(pkg) 431 | } 432 | -------------------------------------------------------------------------------- /src/controller/package.rs: -------------------------------------------------------------------------------- 1 | use inner::logger::Logger; 2 | use futures::Future; 3 | use futures_cpupool::CpuPool; 4 | use inner::{json_helper, vendor, go, helpers, git_helper}; 5 | use std::path::Path; 6 | use json::JsonValue; 7 | use std::sync::mpsc::channel; 8 | use std::thread; 9 | use std::fs::{create_dir_all, remove_dir_all}; 10 | use controller::project; 11 | use git2::{Repository, ResetType}; 12 | use std::process; 13 | 14 | pub fn get(mut package_url: &str, repo_url: Option<&str>, no_prompt: bool, is_global: bool, is_local: bool, logger: Logger) { 15 | if package_url.ends_with("/") || package_url.ends_with("\\") { 16 | package_url = &package_url[..package_url.len() - 1]; 17 | } 18 | 19 | if !Path::new("rubigo.json").exists() { 20 | if no_prompt { 21 | project::init(logger); 22 | } else { 23 | match helpers::confirmation_prompt("The `rubigo.json` file was not found in this directory, it seems that Rubigo project has not been initialized.\nDo you want to initialize it? [Y/n]") { 24 | Ok(state) => if state { 25 | project::init(logger); 26 | } else { 27 | logger.fatal("Rubigo project has not been initialized"); 28 | return 29 | }, 30 | Err(e) => { 31 | logger.fatal(e); 32 | return 33 | }, 34 | } 35 | } 36 | } 37 | 38 | let rubigo_json = match json_helper::read(Path::new("rubigo.json")) { 39 | Ok(content_json) => content_json, 40 | Err(e) => { 41 | logger.fatal(format!("unable to read `rubigo.json`: {}", e)); 42 | return 43 | }, 44 | }; 45 | 46 | let pool = CpuPool::new(1); 47 | let rubigo_lock_future = pool.spawn_fn(|| { 48 | match json_helper::read(Path::new("rubigo.lock")) { 49 | Ok(content_json) => Ok(content_json), 50 | Err(e) => Err(e), 51 | } 52 | }); 53 | 54 | let pkg_import = helpers::strip_url_scheme(package_url); 55 | let mut pkg_path_buf = helpers::get_path_from_url(&pkg_import); 56 | 57 | let json_packages_object; 58 | let lock_packages_object; 59 | let rubigo_lock; 60 | 61 | if is_global { 62 | let global_ps = &rubigo_json[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY]; 63 | if !global_ps.is_null() { 64 | for i in 0..global_ps.len() { 65 | if match global_ps[i].as_str() { 66 | Some(name) => name, 67 | None => continue, 68 | } == pkg_import.as_str() { 69 | logger.fatal(format!("the package `{}` already exists in `rubigo.json` file", pkg_import)); 70 | return; 71 | } 72 | } 73 | } 74 | 75 | match go::get(pkg_import.as_str(), false) { 76 | true => logger.verbose("Global package", &pkg_import), 77 | false => { 78 | logger.fatal(format!("unable to get package `{}`", pkg_import)); 79 | return 80 | } 81 | } 82 | 83 | rubigo_lock = rubigo_lock_future.wait().unwrap_or(object!{}); 84 | 85 | let mut global_pkgs = rubigo_json[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone(); 86 | if global_pkgs.is_null() { 87 | global_pkgs = array![pkg_import.clone()]; 88 | } else { 89 | let _ = global_pkgs.push(pkg_import.clone()); 90 | } 91 | 92 | let mut lock_global_pkgs = rubigo_lock[json_helper::GLOBAL_KEY].clone(); 93 | if lock_global_pkgs.is_null() { 94 | lock_global_pkgs = array![pkg_import]; 95 | } else { 96 | let _ = lock_global_pkgs.push(pkg_import); 97 | } 98 | 99 | json_packages_object = object!{ 100 | json_helper::GIT_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].clone(), 101 | json_helper::LOCAL_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(), 102 | json_helper::GLOBAL_KEY => global_pkgs 103 | }; 104 | 105 | lock_packages_object = object!{ 106 | json_helper::GIT_KEY => rubigo_lock[json_helper::GIT_KEY].clone(), 107 | json_helper::LOCAL_KEY => rubigo_lock[json_helper::LOCAL_KEY].clone(), 108 | json_helper::GLOBAL_KEY => lock_global_pkgs 109 | }; 110 | } else if is_local { 111 | let local_ps = &rubigo_json[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY]; 112 | if !local_ps.is_null() { 113 | for i in 0..local_ps.len() { 114 | if match local_ps[i].as_str() { 115 | Some(name) => name, 116 | None => continue, 117 | } == pkg_import.as_str() { 118 | logger.fatal(format!("the package `{}` already exists in `rubigo.json` file", pkg_import)); 119 | return; 120 | } 121 | } 122 | } 123 | 124 | let pkg_path = pkg_path_buf.as_path(); 125 | if pkg_path.exists() { 126 | logger.fatal(format!("the package `{}` already exists", pkg_import)); 127 | return 128 | } 129 | 130 | match create_dir_all(pkg_path) { 131 | Ok(_) => logger.verbose("Local package", &pkg_import), 132 | Err(e) => { 133 | logger.fatal(e); 134 | return 135 | } 136 | } 137 | 138 | rubigo_lock = rubigo_lock_future.wait().unwrap_or(object!{}); 139 | 140 | let mut local_pkgs = rubigo_json[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(); 141 | if local_pkgs.is_null() { 142 | local_pkgs = array![pkg_import.clone()]; 143 | } else { 144 | let _ = local_pkgs.push(pkg_import.clone()); 145 | } 146 | 147 | let mut lock_local_pkgs = rubigo_lock[json_helper::LOCAL_KEY].clone(); 148 | if lock_local_pkgs.is_null() { 149 | lock_local_pkgs = array![pkg_import]; 150 | } else { 151 | let _ = lock_local_pkgs.push(pkg_import); 152 | } 153 | 154 | json_packages_object = object!{ 155 | json_helper::GIT_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].clone(), 156 | json_helper::GLOBAL_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone(), 157 | json_helper::LOCAL_KEY => local_pkgs 158 | }; 159 | 160 | lock_packages_object = object!{ 161 | json_helper::GIT_KEY => rubigo_lock[json_helper::GIT_KEY].clone(), 162 | json_helper::GLOBAL_KEY => rubigo_lock[json_helper::GLOBAL_KEY].clone(), 163 | json_helper::LOCAL_KEY => lock_local_pkgs 164 | }; 165 | } else { 166 | let git_ps = &rubigo_json[json_helper::PACKAGES_KEY][json_helper::GIT_KEY]; 167 | if !git_ps.is_null() { 168 | for i in 0..git_ps.len() { 169 | if match git_ps[i][json_helper::IMPORT_KEY].as_str() { 170 | Some(name) => name, 171 | None => continue, 172 | } == pkg_import.as_str() { 173 | logger.fatal(format!("the package `{}` already exists in `rubigo.json` file", pkg_import)); 174 | return; 175 | } 176 | } 177 | } 178 | 179 | let mut pkg_json = object!{ 180 | json_helper::IMPORT_KEY => pkg_import.clone() 181 | }; 182 | 183 | let (pkg_import_url, modified_pkg_path) = helpers::modify_golang_org(pkg_import.as_str()); 184 | let modified_pkg_import = if modified_pkg_path.is_some() { 185 | modified_pkg_path.unwrap() 186 | } else { 187 | pkg_import.clone() 188 | }; 189 | pkg_path_buf = helpers::get_path_from_url(&modified_pkg_import); 190 | let pkg_path = pkg_path_buf.as_path(); 191 | if pkg_path.exists() { 192 | logger.error(format!("the package `{}` already exists in `vendor` directory", pkg_import)); 193 | match remove_dir_all(pkg_path) { 194 | Ok(_) => logger.verbose("Delete directory", &pkg_import), 195 | Err(e) => { 196 | logger.fatal(e); 197 | return 198 | }, 199 | } 200 | } 201 | match create_dir_all(pkg_path) { 202 | Ok(_) => logger.verbose("Create directory", &pkg_import), 203 | Err(e) => { 204 | logger.fatal(e); 205 | return 206 | }, 207 | } 208 | 209 | let repo = match Repository::clone(match repo_url { 210 | Some(url) => { 211 | pkg_json[json_helper::REPO_KEY] = url.into(); 212 | url 213 | }, 214 | None => &pkg_import_url, 215 | }, pkg_path) { 216 | Ok(repo) => { 217 | logger.verbose("Clone repository", &pkg_import); 218 | repo 219 | }, 220 | Err(e) => { 221 | let _ = remove_dir_all(pkg_path); 222 | logger.fatal(e); 223 | return 224 | }, 225 | }; 226 | 227 | let mut lock_pkg_json = pkg_json.clone(); 228 | 229 | let version; 230 | if !no_prompt { 231 | let (ver, rule) = match helpers::version_prompt(&repo) { 232 | Some(ver) => ver, 233 | None => { 234 | let _ = remove_dir_all(pkg_path); 235 | logger.fatal("unable to get latest commit of package"); 236 | return 237 | }, 238 | }; 239 | version = ver; 240 | pkg_json[json_helper::VERSION_KEY] = rule.into(); 241 | } else { 242 | version = match git_helper::get_latest_commit(&repo) { 243 | Some(ver) => ver, 244 | None => { 245 | let _ = remove_dir_all(pkg_path); 246 | logger.fatal("unable to get latest commit of package"); 247 | return 248 | }, 249 | }; 250 | 251 | pkg_json[json_helper::VERSION_KEY] = version.clone().into(); 252 | } 253 | 254 | let version_object = match git_helper::get_revision_object(&repo, pkg_import.clone(), version, true, logger) { 255 | Some(tup) => { 256 | lock_pkg_json[json_helper::VERSION_KEY] = tup.1.into(); 257 | tup.0 258 | }, 259 | None => { 260 | let _ = remove_dir_all(pkg_path); 261 | logger.fatal("unable to parse the version of package"); 262 | return 263 | } 264 | }; 265 | 266 | match repo.set_head_detached(version_object.id()) { 267 | Ok(_) => (), 268 | Err(e) => { 269 | let _ = remove_dir_all(pkg_path); 270 | logger.fatal(e); 271 | return 272 | }, 273 | } 274 | 275 | match repo.reset(&version_object, ResetType::Hard, None){ 276 | Ok(_) => (), 277 | Err(e) => { 278 | let _ = remove_dir_all(pkg_path); 279 | logger.fatal(e); 280 | return 281 | }, 282 | } 283 | 284 | let mut git_pkgs = rubigo_json[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].clone(); 285 | if git_pkgs.is_null() { 286 | git_pkgs = array![pkg_json]; 287 | } else { 288 | let _ = git_pkgs.push(pkg_json); 289 | } 290 | 291 | rubigo_lock = rubigo_lock_future.wait().unwrap_or(object!{}); 292 | let mut lock_git_pkgs = rubigo_lock[json_helper::GIT_KEY].clone(); 293 | if lock_git_pkgs.is_null() { 294 | lock_git_pkgs = array![lock_pkg_json]; 295 | } else { 296 | let _ = lock_git_pkgs.push(lock_pkg_json); 297 | } 298 | 299 | json_packages_object = object!{ 300 | json_helper::GIT_KEY => git_pkgs, 301 | json_helper::LOCAL_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(), 302 | json_helper::GLOBAL_KEY => rubigo_json[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone() 303 | }; 304 | 305 | lock_packages_object = object!{ 306 | json_helper::GIT_KEY => lock_git_pkgs, 307 | json_helper::LOCAL_KEY => rubigo_lock[json_helper::LOCAL_KEY].clone(), 308 | json_helper::GLOBAL_KEY => rubigo_lock[json_helper::GLOBAL_KEY].clone() 309 | }; 310 | } 311 | 312 | match json_helper::write("rubigo.json", "", Some(object!{ 313 | json_helper::INFO_KEY => rubigo_json[json_helper::INFO_KEY].clone(), 314 | json_helper::PACKAGES_KEY => json_packages_object 315 | })) { 316 | Ok(_) => logger.verbose("Update file", "rubigo.json"), 317 | Err(e) => { 318 | if !is_global { 319 | let _ = remove_dir_all(pkg_path_buf.as_path()); 320 | } 321 | logger.fatal(format!("unable to write to `rubigo.json`: {}", e)); 322 | return 323 | }, 324 | } 325 | 326 | match json_helper::write("rubigo.lock", "", Some(lock_packages_object)) { 327 | Ok(_) => logger.verbose("Update file", "rubigo.lock"), 328 | Err(e) => { 329 | let _ = json_helper::write("rubigo.json", "", Some(rubigo_json)); 330 | let _ = remove_dir_all(pkg_path_buf.as_path()); 331 | logger.fatal(format!("unable to write to `rubigo.lock`: {}", e)) 332 | }, 333 | } 334 | } 335 | 336 | pub fn remove(package_dir: &str, logger: Logger) { 337 | let json_content = match json_helper::read(Path::new("rubigo.json")) { 338 | Ok(content) => content, 339 | Err(e) => { 340 | logger.fatal(format!("unable to read `rubigo.json`: {}", e)); 341 | return 342 | } 343 | }; 344 | 345 | let lock_content = match json_helper::read(Path::new("rubigo.lock")) { 346 | Ok(content) => content, 347 | Err(e) => { 348 | logger.fatal(format!("unable to read `rubigo.lock`: {}", e)); 349 | return 350 | } 351 | }; 352 | 353 | let new_json_git = json_helper::remove_package_from_array(package_dir, &json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY], false); 354 | let new_lock_git = json_helper::remove_package_from_array(package_dir, &lock_content[json_helper::GIT_KEY], false); 355 | let new_json_local = json_helper::remove_package_from_array(package_dir, &json_content[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY], true); 356 | let new_lock_local = json_helper::remove_package_from_array(package_dir, &lock_content[json_helper::LOCAL_KEY], true); 357 | let new_json_global = json_helper::remove_package_from_array(package_dir, &json_content[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY], true); 358 | let new_lock_global = json_helper::remove_package_from_array(package_dir, &lock_content[json_helper::GLOBAL_KEY], true); 359 | 360 | match json_helper::write("rubigo.json", "", Some(object!{ 361 | json_helper::INFO_KEY => json_content[json_helper::INFO_KEY].clone(), 362 | json_helper::PACKAGES_KEY => object!{ 363 | json_helper::GIT_KEY => new_json_git, 364 | json_helper::LOCAL_KEY => new_json_local, 365 | json_helper::GLOBAL_KEY => new_json_global 366 | } 367 | })) { 368 | Ok(_) => logger.verbose("Update file", "rubigo.json"), 369 | Err(e) => { 370 | logger.fatal(e); 371 | return 372 | }, 373 | } 374 | 375 | match json_helper::write("rubigo.lock", "", Some(object!{ 376 | json_helper::GIT_KEY => new_lock_git, 377 | json_helper::LOCAL_KEY => new_lock_local, 378 | json_helper::GLOBAL_KEY => new_lock_global 379 | })) { 380 | Ok(_) => logger.verbose("Update file", "rubigo.lock"), 381 | Err(e) => { 382 | match json_helper::write("rubigo.json", "", Some(json_content)) { 383 | Ok(_) => logger.verbose("Revert file", "rubigo.json"), 384 | Err(e) => logger.error(format!("unable to revert `rubigo.json`: {}", e)), 385 | } 386 | logger.fatal(e); 387 | return 388 | }, 389 | } 390 | 391 | let pkg_path_buf = helpers::get_path_from_url(package_dir); 392 | let pkg_path = pkg_path_buf.as_path(); 393 | if pkg_path.exists() { 394 | if !helpers::remove_package(package_dir, logger) { 395 | match json_helper::write("rubigo.json", "", Some(json_content)) { 396 | Ok(_) => logger.verbose("Revert file", "rubigo.json"), 397 | Err(e) => logger.error(format!("unable to revert `rubigo.json`: {}", e)), 398 | } 399 | match json_helper::write("rubigo.lock", "", Some(lock_content)) { 400 | Ok(_) => logger.verbose("Revert file", "rubigo.lock"), 401 | Err(e) => logger.error(format!("unable to revert `rubigo.lock`: {}", e)), 402 | } 403 | process::exit(1); 404 | } 405 | } 406 | } 407 | 408 | pub fn update(package_url: Option<&str>, should_clean: bool, logger: Logger) { 409 | let json_content = match json_helper::read(Path::new("rubigo.json")) { 410 | Ok(content) => content, 411 | Err(e) => { 412 | logger.fatal(format!("unable to read `rubigo.json`: {}", e)); 413 | return 414 | } 415 | }; 416 | 417 | if package_url.is_some() { 418 | let mut git_pkgs = json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].clone(); 419 | let mut pkg = None; 420 | if !git_pkgs.is_null() { 421 | for i in 0..git_pkgs.len() { 422 | if match git_pkgs[i][json_helper::IMPORT_KEY].as_str() { 423 | Some(name) => name, 424 | None => continue, 425 | } == package_url.unwrap() { 426 | pkg = Some(git_pkgs.array_remove(i)); 427 | break; 428 | } 429 | } 430 | } 431 | if pkg.is_none() { 432 | let mut global_pkgs = json_content[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone(); 433 | if global_pkgs.is_null() { 434 | logger.fatal(format!("the package `{0}` did not find in `rubigo.json` file", package_url.unwrap())); 435 | return 436 | } 437 | let mut global_pkg = None; 438 | for i in 0..global_pkgs.len() { 439 | if match global_pkgs[i].as_str() { 440 | Some(name) => name, 441 | None => continue, 442 | } == package_url.unwrap() { 443 | global_pkg = Some(match global_pkgs.array_remove(i).as_str() { 444 | Some(name) => name.to_owned(), 445 | None => { 446 | logger.fatal(format!("the package `{0}` is not installed, it could be installed using `rubigo get {0}`", package_url.unwrap())); 447 | return 448 | } 449 | }); 450 | break; 451 | } 452 | } 453 | 454 | if global_pkg.is_none() { 455 | logger.fatal(format!("the package `{0}` is not installed, it could be installed using `rubigo get {0}`", package_url.unwrap())); 456 | return 457 | } 458 | 459 | let g_pkg = global_pkg.unwrap(); 460 | match go::get(&g_pkg, true) { 461 | true => { 462 | logger.verbose("Global package", &g_pkg); 463 | let _ = global_pkgs.push(g_pkg); 464 | }, 465 | false => { 466 | logger.fatal(format!("unable to update global package of `{}`", &g_pkg)); 467 | return 468 | }, 469 | } 470 | 471 | match json_helper::write("rubigo.lock", "", Some(object!{ 472 | json_helper::GIT_KEY => json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY].clone(), 473 | json_helper::LOCAL_KEY => json_content[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(), 474 | json_helper::GLOBAL_KEY => global_pkgs 475 | })) { 476 | Ok(_) => logger.verbose("Update file", "rubigo.lock"), 477 | Err(e) => logger.error(e), 478 | } 479 | 480 | return 481 | } 482 | 483 | let (tx, rx) = channel(); 484 | thread::spawn(move|| { 485 | vendor::update_package(pkg.unwrap(), should_clean, false, tx, logger); 486 | }); 487 | 488 | match rx.recv() { 489 | Ok(p) => { 490 | logger.verbose("Update package", match p[json_helper::IMPORT_KEY].as_str() { 491 | Some(import_str) => import_str, 492 | None => "unknown", 493 | }); 494 | let _ = git_pkgs.push(p); 495 | }, 496 | Err(e) => logger.fatal(e), 497 | } 498 | 499 | match json_helper::write("rubigo.lock", "", Some(object!{ 500 | json_helper::GIT_KEY => git_pkgs, 501 | json_helper::LOCAL_KEY => json_content[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(), 502 | json_helper::GLOBAL_KEY => json_content[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone() 503 | })) { 504 | Ok(_) => logger.verbose("Update file", "rubigo.lock"), 505 | Err(e) => logger.error(e), 506 | } 507 | 508 | return; 509 | } 510 | 511 | let pool = CpuPool::new(2); 512 | 513 | let old_lock_future = pool.spawn_fn(|| { 514 | match json_helper::read(Path::new("rubigo.lock")) { 515 | Ok(content_json) => Ok(content_json), 516 | Err(e) => Err(e), 517 | } 518 | }); 519 | 520 | let c_json = json_content[json_helper::PACKAGES_KEY][json_helper::LOCAL_KEY].clone(); 521 | let local_packages = pool.spawn_fn(move || { 522 | Ok::(vendor::install_local_packages(&c_json, logger)) 523 | }); 524 | 525 | let c_json2 = json_content[json_helper::PACKAGES_KEY][json_helper::GLOBAL_KEY].clone(); 526 | let global_packages = pool.spawn_fn(move || { 527 | Ok::(vendor::install_global_packages(&c_json2, true, logger)) 528 | }); 529 | 530 | let git_packages = vendor::install_git_packages(&json_content[json_helper::PACKAGES_KEY][json_helper::GIT_KEY], "Update package", should_clean, false, logger); 531 | 532 | let new_lock = object!{ 533 | json_helper::GIT_KEY => git_packages, 534 | json_helper::LOCAL_KEY => local_packages.wait().unwrap_or(array![]), 535 | json_helper::GLOBAL_KEY => global_packages.wait().unwrap_or(array![]) 536 | }; 537 | 538 | helpers::remove_diff_packages(&old_lock_future.wait().unwrap_or(object![]), &new_lock, logger); 539 | 540 | match json_helper::write("rubigo.lock", "", Some(new_lock)) { 541 | Ok(_) => logger.verbose("Update file", "rubigo.lock"), 542 | Err(e) => logger.error(e), 543 | } 544 | } 545 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "rubigo" 3 | version = "1.0.3" 4 | dependencies = [ 5 | "clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "json 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 13 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 14 | "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "aho-corasick" 21 | version = "0.6.3" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "ansi_term" 29 | version = "0.9.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "atty" 34 | version = "0.2.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | dependencies = [ 37 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 41 | ] 42 | 43 | [[package]] 44 | name = "bitflags" 45 | version = "0.7.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | 48 | [[package]] 49 | name = "bitflags" 50 | version = "0.9.1" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | 53 | [[package]] 54 | name = "cc" 55 | version = "1.0.3" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | 58 | [[package]] 59 | name = "cfg-if" 60 | version = "0.1.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | 63 | [[package]] 64 | name = "clap" 65 | version = "2.27.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | dependencies = [ 68 | "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 75 | ] 76 | 77 | [[package]] 78 | name = "cmake" 79 | version = "0.1.26" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | dependencies = [ 82 | "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "curl" 87 | version = "0.4.8" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | dependencies = [ 90 | "curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 92 | "openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "socket2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 96 | ] 97 | 98 | [[package]] 99 | name = "curl-sys" 100 | version = "0.3.15" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", 107 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 110 | ] 111 | 112 | [[package]] 113 | name = "fuchsia-zircon" 114 | version = "0.2.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 118 | ] 119 | 120 | [[package]] 121 | name = "fuchsia-zircon-sys" 122 | version = "0.2.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "futures" 130 | version = "0.1.17" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | 133 | [[package]] 134 | name = "futures-cpupool" 135 | version = "0.1.7" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | dependencies = [ 138 | "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 140 | ] 141 | 142 | [[package]] 143 | name = "git2" 144 | version = "0.6.8" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 149 | "libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "idna" 157 | version = "0.1.4" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 163 | ] 164 | 165 | [[package]] 166 | name = "json" 167 | version = "0.11.10" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | 170 | [[package]] 171 | name = "kernel32-sys" 172 | version = "0.2.2" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | dependencies = [ 175 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "lazy_static" 181 | version = "0.2.9" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | 184 | [[package]] 185 | name = "libc" 186 | version = "0.2.33" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | 189 | [[package]] 190 | name = "libgit2-sys" 191 | version = "0.6.16" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | dependencies = [ 194 | "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "libssh2-sys" 206 | version = "0.2.6" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | dependencies = [ 209 | "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 214 | ] 215 | 216 | [[package]] 217 | name = "libz-sys" 218 | version = "1.0.18" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | dependencies = [ 221 | "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 224 | "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 225 | ] 226 | 227 | [[package]] 228 | name = "matches" 229 | version = "0.1.6" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | 232 | [[package]] 233 | name = "memchr" 234 | version = "1.0.2" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | dependencies = [ 237 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 238 | ] 239 | 240 | [[package]] 241 | name = "num_cpus" 242 | version = "1.7.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | dependencies = [ 245 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 246 | ] 247 | 248 | [[package]] 249 | name = "openssl-probe" 250 | version = "0.1.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | 253 | [[package]] 254 | name = "openssl-sys" 255 | version = "0.9.20" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | dependencies = [ 258 | "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "percent-encoding" 266 | version = "1.0.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | 269 | [[package]] 270 | name = "pkg-config" 271 | version = "0.3.9" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | 274 | [[package]] 275 | name = "rand" 276 | version = "0.3.17" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | dependencies = [ 279 | "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 280 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 281 | ] 282 | 283 | [[package]] 284 | name = "redox_syscall" 285 | version = "0.1.31" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | 288 | [[package]] 289 | name = "redox_termios" 290 | version = "0.1.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | dependencies = [ 293 | "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 294 | ] 295 | 296 | [[package]] 297 | name = "regex" 298 | version = "0.2.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | dependencies = [ 301 | "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 306 | ] 307 | 308 | [[package]] 309 | name = "regex-syntax" 310 | version = "0.4.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | 313 | [[package]] 314 | name = "semver" 315 | version = "0.9.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | dependencies = [ 318 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 319 | ] 320 | 321 | [[package]] 322 | name = "semver-parser" 323 | version = "0.7.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | 326 | [[package]] 327 | name = "socket2" 328 | version = "0.2.4" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | dependencies = [ 331 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 332 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 336 | ] 337 | 338 | [[package]] 339 | name = "strsim" 340 | version = "0.6.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | 343 | [[package]] 344 | name = "tempdir" 345 | version = "0.3.5" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | dependencies = [ 348 | "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", 349 | ] 350 | 351 | [[package]] 352 | name = "termion" 353 | version = "1.5.1" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 358 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 359 | ] 360 | 361 | [[package]] 362 | name = "textwrap" 363 | version = "0.9.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | dependencies = [ 366 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 367 | ] 368 | 369 | [[package]] 370 | name = "thread_local" 371 | version = "0.3.4" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | dependencies = [ 374 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 375 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 376 | ] 377 | 378 | [[package]] 379 | name = "threadpool" 380 | version = "1.7.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | dependencies = [ 383 | "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "time" 388 | version = "0.1.38" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | dependencies = [ 391 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 392 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 395 | ] 396 | 397 | [[package]] 398 | name = "unicode-bidi" 399 | version = "0.3.4" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | dependencies = [ 402 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 403 | ] 404 | 405 | [[package]] 406 | name = "unicode-normalization" 407 | version = "0.1.5" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | 410 | [[package]] 411 | name = "unicode-width" 412 | version = "0.1.4" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | 415 | [[package]] 416 | name = "unreachable" 417 | version = "1.0.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | dependencies = [ 420 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 421 | ] 422 | 423 | [[package]] 424 | name = "url" 425 | version = "1.6.0" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | dependencies = [ 428 | "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 429 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 430 | "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 431 | ] 432 | 433 | [[package]] 434 | name = "utf8-ranges" 435 | version = "1.0.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | 438 | [[package]] 439 | name = "vcpkg" 440 | version = "0.2.2" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | 443 | [[package]] 444 | name = "vec_map" 445 | version = "0.8.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | 448 | [[package]] 449 | name = "void" 450 | version = "1.0.2" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | 453 | [[package]] 454 | name = "winapi" 455 | version = "0.2.8" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | 458 | [[package]] 459 | name = "winapi-build" 460 | version = "0.1.1" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | 463 | [[package]] 464 | name = "ws2_32-sys" 465 | version = "0.2.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | dependencies = [ 468 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 469 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 470 | ] 471 | 472 | [metadata] 473 | "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" 474 | "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" 475 | "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" 476 | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" 477 | "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" 478 | "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" 479 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 480 | "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" 481 | "checksum cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "357c07e7a1fc95732793c1edb5901e1a1f305cfcf63a90eb12dbd22bdb6b789d" 482 | "checksum curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7034c534a1d7d22f7971d6088aa9d281d219ef724026c3428092500f41ae9c2c" 483 | "checksum curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4bee31aa3a079d5f3ff9579ea4dcfb1b1a17a40886f5f467436d383e78134b55" 484 | "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" 485 | "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" 486 | "checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" 487 | "checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf" 488 | "checksum git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1c0203d653f4140241da0c1375a404f0a397249ec818cd2076c6280c50f6fa" 489 | "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" 490 | "checksum json 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d43dc02ce7e4618209b8912b61e0b373fd5c526fcc50a2907b562f2a47c0f9b1" 491 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 492 | "checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5" 493 | "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" 494 | "checksum libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "6f74b4959cef96898f5123148724fc7dee043b9a6b99f219d948851bfbe53cb2" 495 | "checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" 496 | "checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16" 497 | "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" 498 | "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" 499 | "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" 500 | "checksum openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98df0270d404ccd3c050a41d579c52d1db15375168bb3471e04ec0f5f378daf" 501 | "checksum openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)" = "0ad395f1cee51b64a8d07cc8063498dc7554db62d5f3ca87a67f4eed2791d0c8" 502 | "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" 503 | "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" 504 | "checksum rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "61efcbcd9fa8d8fbb07c84e34a8af18a1ff177b449689ad38a6e9457ecc7b2ae" 505 | "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" 506 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 507 | "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" 508 | "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" 509 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 510 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 511 | "checksum socket2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b4896961171cd3317c7e9603d88f379f8c6e45342212235d356496680c68fd" 512 | "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" 513 | "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" 514 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 515 | "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" 516 | "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" 517 | "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" 518 | "checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" 519 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 520 | "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" 521 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 522 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 523 | "checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" 524 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 525 | "checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" 526 | "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" 527 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 528 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 529 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 530 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 531 | --------------------------------------------------------------------------------