Add alternative package provider to avoid GIT problems

This commit is contained in:
Federico Terzi 2020-03-16 21:18:33 +01:00
parent 82975bfbdc
commit 54f720eca1
6 changed files with 162 additions and 26 deletions

View File

@ -30,13 +30,11 @@ git2 = {version = "0.10.1", features = ["https"]}
tempfile = "3.1.0"
dialoguer = "0.4.0"
rand = "0.7.2"
zip = "0.5.3"
[target.'cfg(unix)'.dependencies]
libc = "0.2.62"
[target.'cfg(target_os = "macos")'.dependencies]
zip = "0.5.3"
[build-dependencies]
cmake = "0.1.31"

View File

@ -43,8 +43,10 @@ use crate::ui::UIManager;
use crate::protocol::*;
use std::io::{BufReader, BufRead};
use crate::package::default::DefaultPackageManager;
use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult};
use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult, PackageResolver};
use std::sync::atomic::AtomicBool;
use crate::package::git::GitPackageResolver;
use crate::package::zip::ZipPackageResolver;
mod ui;
mod edit;
@ -71,6 +73,12 @@ const LOG_FILE: &str = "espanso.log";
fn main() {
let install_subcommand = SubCommand::with_name("install")
.about("Install a package. Equivalent to 'espanso package install'")
.arg(Arg::with_name("no-git")
.short("g")
.long("no-git")
.required(false)
.takes_value(false)
.help("Install packages avoiding the GIT package provider. Try this flag if the default mode is not working."))
.arg(Arg::with_name("package_name")
.help("Package name"));
@ -742,7 +750,14 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
exit(1);
});
let mut package_manager = DefaultPackageManager::new_default();
let package_resolver: Box<dyn PackageResolver> = if matches.is_present("no-git") {
println!("Using alternative package provider");
Box::new(ZipPackageResolver::new())
}else{
Box::new(GitPackageResolver::new())
};
let mut package_manager = DefaultPackageManager::new_default(Some(package_resolver));
if package_manager.is_index_outdated() {
println!("Updating package index...");
@ -808,7 +823,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
exit(1);
});
let package_manager = DefaultPackageManager::new_default();
let package_manager = DefaultPackageManager::new_default(None);
let res = package_manager.remove_package(package_name);
@ -833,7 +848,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
}
fn update_index_main(_config_set: ConfigSet) {
let mut package_manager = DefaultPackageManager::new_default();
let mut package_manager = DefaultPackageManager::new_default(None);
let res = package_manager.update_index(true);
@ -856,7 +871,7 @@ fn update_index_main(_config_set: ConfigSet) {
}
fn list_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
let package_manager = DefaultPackageManager::new_default();
let package_manager = DefaultPackageManager::new_default(None);
let list = package_manager.list_local_packages();

View File

@ -18,7 +18,7 @@
*/
use std::path::{PathBuf, Path};
use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult};
use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult, PackageResolver};
use std::error::Error;
use std::fs::{File, create_dir};
use std::io::{BufReader, BufRead};
@ -31,6 +31,7 @@ use git2::Repository;
use regex::Regex;
use crate::package::RemoveResult::Removed;
use std::collections::HashMap;
use super::git::GitPackageResolver;
const DEFAULT_PACKAGE_INDEX_FILE : &str = "package_index.json";
@ -38,24 +39,28 @@ pub struct DefaultPackageManager {
package_dir: PathBuf,
data_dir: PathBuf,
package_resolver: Option<Box<dyn PackageResolver>>,
local_index: Option<PackageIndex>,
}
impl DefaultPackageManager {
pub fn new(package_dir: PathBuf, data_dir: PathBuf) -> DefaultPackageManager {
pub fn new(package_dir: PathBuf, data_dir: PathBuf, package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
let local_index = Self::load_local_index(&data_dir);
DefaultPackageManager{
package_dir,
data_dir,
package_resolver,
local_index
}
}
pub fn new_default() -> DefaultPackageManager {
pub fn new_default(package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
DefaultPackageManager::new(
crate::context::get_package_dir(),
crate::context::get_data_dir()
crate::context::get_data_dir(),
package_resolver,
)
}
@ -89,12 +94,6 @@ impl DefaultPackageManager {
Ok(index)
}
fn clone_repo_to_temp(repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
let temp_dir = TempDir::new()?;
let _repo = Repository::clone(repo_url, temp_dir.path())?;
Ok(temp_dir)
}
fn parse_package_from_readme(readme_path: &Path) -> Option<Package> {
lazy_static! {
static ref FIELD_REGEX: Regex = Regex::new(r###"^\s*(.*?)\s*:\s*"?(.*?)"?$"###).unwrap();
@ -248,7 +247,7 @@ impl super::PackageManager for DefaultPackageManager {
return Ok(AlreadyInstalled);
}
let temp_dir = Self::clone_repo_to_temp(repo_url)?;
let temp_dir = self.package_resolver.as_ref().unwrap().clone_repo_to_temp(repo_url)?;
let temp_package_dir = temp_dir.path().join(name);
if !temp_package_dir.exists() {
@ -337,7 +336,8 @@ mod tests {
let package_manager = DefaultPackageManager::new(
package_dir.path().clone().to_path_buf(),
data_dir.path().clone().to_path_buf()
data_dir.path().clone().to_path_buf(),
Some(Box::new(GitPackageResolver::new())),
);
TempPackageManager {
@ -465,12 +465,6 @@ mod tests {
assert_eq!(temp.package_manager.install_package("italian-accents").unwrap(), AlreadyInstalled);
}
#[test]
fn test_clone_temp_repository() {
let cloned_dir = DefaultPackageManager::clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
assert!(cloned_dir.path().join("LICENSE").exists());
}
#[test]
fn test_install_package() {
let mut temp = create_temp_package_manager(|_, data_dir| {

33
src/package/git.rs Normal file
View File

@ -0,0 +1,33 @@
use tempfile::TempDir;
use std::error::Error;
use git2::Repository;
use super::PackageResolver;
pub struct GitPackageResolver;
impl GitPackageResolver {
pub fn new() -> GitPackageResolver {
return GitPackageResolver{};
}
}
impl super::PackageResolver for GitPackageResolver {
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
let temp_dir = TempDir::new()?;
let _repo = Repository::clone(repo_url, temp_dir.path())?;
Ok(temp_dir)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::{TempDir, NamedTempFile};
#[test]
fn test_clone_temp_repository() {
let resolver = GitPackageResolver::new();
let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
assert!(cloned_dir.path().join("LICENSE").exists());
}
}

View File

@ -17,9 +17,13 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
pub(crate) mod git;
pub(crate) mod zip;
pub(crate) mod default;
use serde::{Serialize, Deserialize};
use std::error::Error;
use tempfile::TempDir;
pub trait PackageManager {
fn is_index_outdated(&self) -> bool;
@ -35,6 +39,10 @@ pub trait PackageManager {
fn list_local_packages(&self) -> Vec<Package>;
}
pub trait PackageResolver {
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>>;
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Package {
pub name: String,

88
src/package/zip.rs Normal file
View File

@ -0,0 +1,88 @@
use tempfile::TempDir;
use std::error::Error;
use super::PackageResolver;
use std::io::{Cursor, copy, Read};
use std::{fs, io};
use std::fs::File;
use log::debug;
pub struct ZipPackageResolver;
impl ZipPackageResolver {
pub fn new() -> ZipPackageResolver {
return ZipPackageResolver{};
}
}
impl super::PackageResolver for ZipPackageResolver {
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
let temp_dir = TempDir::new()?;
let zip_url = repo_url.to_owned() + "/archive/master.zip";
// Download the archive from GitHub
let mut response = reqwest::get(&zip_url)?;
// Extract zip file
let mut buffer = Vec::new();
copy(&mut response, &mut buffer)?;
let reader = Cursor::new(buffer);
let mut archive = zip::ZipArchive::new(reader).unwrap();
// Find the root folder name
let mut root_folder = {
let mut root_folder = archive.by_index(0).unwrap();
let root_folder = root_folder.sanitized_name();
root_folder.to_str().unwrap().to_owned()
};
root_folder.push(std::path::MAIN_SEPARATOR);
for i in 1..archive.len() {
let mut file = archive.by_index(i).unwrap();
let current_path = file.sanitized_name();
let current_filename = current_path.to_str().unwrap();
let trimmed_filename = current_filename.trim_start_matches(&root_folder);
let outpath = temp_dir.path().join(trimmed_filename);
{
let comment = file.comment();
if !comment.is_empty() {
debug!("File {} comment: {}", i, comment);
}
}
if (&*file.name()).ends_with('/') {
debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
fs::create_dir_all(&outpath).unwrap();
} else {
debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
if let Some(p) = outpath.parent() {
if !p.exists() {
fs::create_dir_all(&p).unwrap();
}
}
let mut outfile = fs::File::create(&outpath).unwrap();
io::copy(&mut file, &mut outfile).unwrap();
}
}
Ok(temp_dir)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::{TempDir, NamedTempFile};
#[test]
fn test_clone_temp_repository() {
let resolver = ZipPackageResolver::new();
let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
assert!(cloned_dir.path().join("LICENSE").exists());
}
}