>(
+ archive_path: P,
+ out_directory: Option,
+) -> crate::Result<()> {
+ let archive_path: PathBuf = archive_path.into();
+ let out_directory: PathBuf = if let Some(out_dir) = out_directory {
+ out_dir.into()
+ } else {
+ Path::new(".").into()
+ };
+
+ let cmd_result = match archive_path.to_str().unwrap_or_default() {
+ x if x.ends_with(".tar.gz") => {
+ log::debug!("extracting with `tar`");
+ Command::new("tar")
+ .args(&[
+ "x",
+ "f",
+ x,
+ "-C",
+ out_directory.to_str().unwrap_or_default(),
+ ])
+ .output()?
+ }
+ x if x.ends_with(".gz") => {
+ log::debug!("extracting with `gunzip`");
+ Command::new("gunzip").args(&["-c", x]).output()?
+ }
+ x if x.ends_with(".zip") => {
+ log::debug!("extracting with `zip`");
+ Command::new("unzip")
+ .args(&[
+ "-q",
+ "-o",
+ x,
+ "-d",
+ out_directory.to_str().unwrap_or_default(),
+ ])
+ .output()?
+ }
+ _ => eyre::bail!("{:?} is not a known archive type.", archive_path),
+ };
+
+ log::debug!("Result of extract command: {:?}", cmd_result);
+ Ok(())
+}
diff --git a/src/github.rs b/crates/vers/src/github.rs
similarity index 99%
rename from src/github.rs
rename to crates/vers/src/github.rs
index 2e533fa..172e7e1 100644
--- a/src/github.rs
+++ b/crates/vers/src/github.rs
@@ -58,7 +58,7 @@ pub async fn get_specific_release_for_repo(
{
Ok(tagged_release) => Ok(tagged_release),
Err(_) => {
- error!(
+ debug!(
"Unable to get release {} for {}/{}.",
version.as_tag(),
owner,
diff --git a/crates/vers/src/main.rs b/crates/vers/src/main.rs
new file mode 100644
index 0000000..3a2a718
--- /dev/null
+++ b/crates/vers/src/main.rs
@@ -0,0 +1,129 @@
+// Turn off common dev assertions only for debug builds, release builds will still work as normal
+#![warn(clippy::all)]
+#![cfg_attr(
+ debug_assertions,
+ allow(dead_code, unused_macros, unused_imports, unused_variables)
+)]
+
+mod cli;
+mod cli_actions;
+mod config;
+mod dirs;
+mod download;
+mod environment;
+mod executor;
+mod extractor;
+mod github;
+mod system;
+mod tool;
+mod version;
+
+use {
+ crate::{
+ cli::Actions,
+ cli_actions::{AddOpts, UpdateType},
+ environment::Environment,
+ executor::*,
+ system::System,
+ },
+ clap::Command,
+ clap_complete::Generator,
+ log::*,
+};
+
+pub type Result = eyre::Result;
+
+// #[tokio::main]
+fn main() -> Result<()> {
+ let opts = cli::new();
+ env_logger::builder()
+ .filter_level(opts.verbose.log_level_filter())
+ .init();
+
+ println!("------------------------------");
+ ["github", "golang"].iter().for_each(|plugin| {
+ execute_wasm_test(format!("target/wasm32-wasi/debug/vers_{}.wasm", plugin)).unwrap();
+ println!("------------------------------");
+ });
+
+ // std::process::exit(1);
+ // let config_dir = dirs::get_default_config_path();
+ //
+ // match opts.action {
+ // Actions::Add {
+ // name,
+ // alias,
+ // user_pattern,
+ // file_pattern,
+ // plugin,
+ // pre_release,
+ // show,
+ // } => {
+ // debug!("CLI: Name `{name}`, alias `{:?}`, pattern `{:?}`, filter `{:?}`, pre_release `{pre_release}`, show `{show}`, plugin: `{:?}`", alias, user_pattern, file_pattern, plugin);
+ // let system = System::new();
+ // let mut env = Environment::load(&config_dir, &opts.env).await?;
+ // cli_actions::add_new_tool(
+ // &mut env,
+ // &name,
+ // &system,
+ // &AddOpts {
+ // user_pattern,
+ // file_pattern,
+ // alias,
+ // show,
+ // pre_release,
+ // },
+ // )
+ // .await?;
+ // },
+ // Actions::Remove { name, all } => {
+ // let mut env = Environment::load(&config_dir, &opts.env).await?;
+ // cli_actions::remove_tool(&mut env, &name, all).await?;
+ // },
+ // Actions::List { installed } => {
+ // let env = Environment::load(&config_dir, &opts.env).await?;
+ // cli_actions::list_tools(&env, installed).await?;
+ // },
+ // Actions::Update { name } => {
+ // let system = System::new();
+ // let mut env = Environment::load(&config_dir, &opts.env).await?;
+ // cli_actions::update_tools(
+ // &mut env,
+ // &system,
+ // if let Some(name) = name {
+ // UpdateType::Specific(name)
+ // } else {
+ // UpdateType::All
+ // },
+ // )
+ // .await?;
+ // },
+ // Actions::Env { name, shell } => {
+ // let name = match name {
+ // Some(name) => name,
+ // None => opts.env,
+ // };
+ // let env = Environment::load(&config_dir, &name).await?;
+ // match &shell[..] {
+ // "fish" => println!("set -p PATH \"{}\"", env.base_dir),
+ // "bash" | "sh" | "zsh" => println!("export PATH=\"{}:$PATH\"", env.base_dir),
+ // _ => panic!("{} is not a known shell", shell),
+ // }
+ // },
+ // Actions::Sync => {
+ // let system = System::new();
+ // let mut env = Environment::load(&config_dir, &opts.env).await?;
+ // println!("Syncing versions with {} configuration.", env.name);
+ // cli_actions::sync_tools(&mut env, &system).await?;
+ // },
+ // Actions::Completions { shell } => {
+ // let mut cmd = cli::Opts::cmd();
+ // print_completions(shell, &mut cmd)
+ // },
+ // };
+ Ok(())
+}
+
+fn print_completions(gen: G, cmd: &mut Command) {
+ clap_complete::generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout())
+}
diff --git a/src/system.rs b/crates/vers/src/system.rs
similarity index 100%
rename from src/system.rs
rename to crates/vers/src/system.rs
diff --git a/src/tool.rs b/crates/vers/src/tool.rs
similarity index 100%
rename from src/tool.rs
rename to crates/vers/src/tool.rs
diff --git a/src/version.rs b/crates/vers/src/version.rs
similarity index 100%
rename from src/version.rs
rename to crates/vers/src/version.rs
diff --git a/plugins/github/.cargo/config.toml b/plugins/github/.cargo/config.toml
new file mode 100644
index 0000000..6b77899
--- /dev/null
+++ b/plugins/github/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "wasm32-wasi"
diff --git a/plugins/github/Cargo.toml b/plugins/github/Cargo.toml
new file mode 100644
index 0000000..e7cec00
--- /dev/null
+++ b/plugins/github/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["Nic Patterson "]
+edition = "2021"
+license = "MIT"
+name = "vers-github"
+version = "0.1.0"
+
+[dependencies]
+eyre = "0.6"
+vers-plugin = { path = "../../crates/vers-plugin" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/plugins/github/src/lib.rs b/plugins/github/src/lib.rs
new file mode 100644
index 0000000..185e451
--- /dev/null
+++ b/plugins/github/src/lib.rs
@@ -0,0 +1,22 @@
+use vers_plugin::VersPlugin;
+
+#[no_mangle]
+pub fn run() {
+ println!("Running GitHub");
+}
+
+struct GitHubPlugin;
+
+impl VersPlugin for GitHubPlugin {
+ fn get_versions(&self) -> Result, Box> {
+ Ok(Vec::new())
+ }
+
+ fn get_versions_from_source(&self) -> Result, Box> {
+ Ok(Vec::new())
+ }
+
+ fn get_assets_for_version(&self) -> Result, Box> {
+ Ok(Vec::new())
+ }
+}
diff --git a/plugins/golang/.cargo/config.toml b/plugins/golang/.cargo/config.toml
new file mode 100644
index 0000000..6b77899
--- /dev/null
+++ b/plugins/golang/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "wasm32-wasi"
diff --git a/plugins/golang/Cargo.toml b/plugins/golang/Cargo.toml
new file mode 100644
index 0000000..3b06b66
--- /dev/null
+++ b/plugins/golang/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["Nic Patterson "]
+edition = "2021"
+license = "MIT"
+name = "vers-golang"
+version = "0.1.0"
+
+[dependencies]
+eyre = "0.6"
+vers-plugin = { path = "../../crates/vers-plugin" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/plugins/golang/src/lib.rs b/plugins/golang/src/lib.rs
new file mode 100644
index 0000000..df5c468
--- /dev/null
+++ b/plugins/golang/src/lib.rs
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub fn run() {
+ println!("Running Golang");
+}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..c05a8c6
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,4 @@
+[toolchain]
+# channel = "1.61.0"
+components = ["rustfmt", "clippy", "rust-analysis"]
+targets = ["wasm32-wasi", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
diff --git a/src/archiver/mod.rs b/src/archiver/mod.rs
deleted file mode 100644
index c1244d3..0000000
--- a/src/archiver/mod.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use std::path::PathBuf;
-
-use {
- self::{tar::TarArchiver, zip::ZipArchiver},
- crate::Result,
- async_trait::async_trait,
- log::*,
- once_cell::sync::Lazy,
- regex::Regex,
- std::path::Path,
-};
-
-mod tar;
-mod zip;
-
-static ARCHIVE_TYPE_TAR_REGEX: Lazy =
- Lazy::new(|| Regex::new(r#"^.+\.tar\..*?"#).expect("Unable to compile regex for tar archiver"));
-static ARCHIVE_TYPE_ZIP_REGEX: Lazy =
- Lazy::new(|| Regex::new(r#"^.+\.zip"#).expect("Unable to compile regex for zip archiver"));
-
-#[async_trait]
-pub trait Archiver {
- async fn extract_to(&self, file_path: &'_ Path, out_dir: &'_ Path) -> crate::Result<()>;
- async fn extract(&self, file_path: &'_ Path) -> crate::Result<()>;
-}
-
-pub fn determine_extractor(file_path: &'_ Path) -> Option> {
- let file_path_str = file_path.to_str().unwrap_or_default();
- if ARCHIVE_TYPE_TAR_REGEX.is_match(file_path_str) {
- info!("Tar Extractor for {}", file_path_str);
- Some(Box::new(TarArchiver))
- } else if ARCHIVE_TYPE_ZIP_REGEX.is_match(file_path_str) {
- info!("Zip Extractor for {}", file_path_str);
- Some(Box::new(ZipArchiver))
- } else {
- info!("No extractor found for {}", file_path_str);
- None
- }
-}
-
-pub async fn handle_file_extraction(
- archiver: Box,
- input_file: &'_ Path,
- output_dir: Option,
-) -> Result<()> {
- if let Some(out_dir) = output_dir {
- archiver.extract_to(input_file, &out_dir).await
- } else {
- archiver.extract(input_file).await
- }
-}
diff --git a/src/archiver/tar.rs b/src/archiver/tar.rs
deleted file mode 100644
index 24cc864..0000000
--- a/src/archiver/tar.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use {
- super::Archiver,
- async_trait::async_trait,
- log::*,
- std::{path::Path, process::Stdio},
- tokio::process::Command,
-};
-
-pub struct TarArchiver;
-
-#[async_trait]
-impl Archiver for TarArchiver {
- async fn extract_to(&self, file_path: &'_ Path, out_dir: &'_ Path) -> crate::Result<()> {
- let mut cmd = Command::new("tar");
- cmd.stdout(Stdio::null());
- cmd.args(&[
- "-x",
- "-f",
- file_path.to_str().unwrap_or_default(),
- "-C",
- out_dir.to_str().unwrap_or_default(),
- ]);
-
- match cmd.output().await {
- Ok(output) => {
- debug!("`tar` command {:?}. output: {:?}", &cmd, output);
- Ok(())
- }
- Err(io_err) => eyre::bail!(io_err),
- }
- }
-
- async fn extract(&self, file_path: &'_ Path) -> crate::Result<()> {
- self.extract_to(file_path, Path::new(".")).await
- }
-}
diff --git a/src/archiver/zip.rs b/src/archiver/zip.rs
deleted file mode 100644
index 25e170b..0000000
--- a/src/archiver/zip.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use {
- super::Archiver,
- async_trait::async_trait,
- log::*,
- std::{path::Path, process::Stdio},
- tokio::process::Command,
-};
-
-pub struct ZipArchiver;
-
-#[async_trait]
-impl Archiver for ZipArchiver {
- async fn extract_to(&self, file_path: &'_ Path, out_dir: &'_ Path) -> crate::Result<()> {
- let mut cmd = Command::new("unzip");
- cmd.stdout(Stdio::null());
- cmd.args(&[
- "-oq",
- file_path.to_str().unwrap_or_default(),
- "-d",
- out_dir.to_str().unwrap_or_default(),
- ]);
-
- match cmd.output().await {
- Ok(output) => {
- debug!("`zip` command {:?}. output: {:?}", &cmd, output);
- Ok(())
- }
- Err(io_err) => eyre::bail!(io_err),
- }
- }
-
- async fn extract(&self, file_path: &'_ Path) -> crate::Result<()> {
- self.extract_to(file_path, Path::new(".")).await
- }
-}
diff --git a/src/cli.rs b/src/cli.rs
deleted file mode 100644
index ae8bf06..0000000
--- a/src/cli.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use bpaf::*;
-
-#[derive(Debug, Clone, Bpaf)]
-#[bpaf(options)]
-pub struct Opts {
- #[bpaf(external(verbose))]
- pub verbose: usize,
- /// Environment where the tool will be installed to
- #[bpaf(short, long, fallback("global".to_string()))]
- pub env: String,
- /// A GitHub API token to use authenticated requests to the API
- #[bpaf(long)]
- pub github_api_token: Option,
- /// Use a local environment
- ///
- /// Files will be stored in the current directory under a "hidden" folder
- #[bpaf(short, long, fallback(false))]
- pub local: bool,
- #[bpaf(external(actions))]
- pub action: Actions,
-}
-
-#[derive(Debug, Clone, Bpaf)]
-pub enum Actions {
- /// Add a tool to the designated environment
- #[bpaf(command("add"))]
- Add {
- /// name of the tool to install to the environment
- ///
- /// To install a specific version use name@version, for example:
- /// `cli/cli@v2.4.0` version should be a release tag
- #[bpaf(positional("NAME"))]
- name: String,
- /// Alias to use instead of the repository name
- ///
- /// This is how the tool will be called from the command line
- #[bpaf(short, long)]
- alias: Option,
- /// Pattern used to determine which file from the release to download
- ///
- /// This can be used to override the autodetect mechanism to determine which assets to
- /// download
- #[bpaf(short, long)]
- pattern: Option,
- /// Filter used to find the executable to link into the environment
- #[bpaf(short, long)]
- filter: Option,
- /// Allow install of pre-release versions of the tool
- #[bpaf(short('P'), long, fallback(false))]
- pre_release: bool,
- /// Show available versions
- #[bpaf(short('S'), long, fallback(false))]
- show: bool,
- },
- /// Remove a tool from the designated environment
- #[bpaf(command("remove"))]
- Remove {
- /// name of the tool to remove from the environment
- #[bpaf(positional("NAME"))]
- name: String,
- /// Remove all versions of a tool. Default is to delete the currently installed version
- #[bpaf(short, long, fallback(false))]
- all: bool,
- },
- /// List tools available in the designated environment
- #[bpaf(command("list"))]
- List {
- #[bpaf(short, long, fallback(false))]
- installed: bool,
- },
- /// sync all version information with listed in the env config file
- #[bpaf(command("sync"))]
- Sync,
- /// Update tools to the latest version
- #[bpaf(command("update"))]
- Update {
- #[bpaf(positional("NAME"))]
- name: Option,
- },
- /// show the exports required for setup
- #[bpaf(command("env"))]
- Env {
- #[bpaf(short, long)]
- name: Option,
- #[bpaf(short, long)]
- shell: String,
- },
-}
-
-fn verbose() -> Parser {
- short('v')
- .long("verbose")
- .help("Increase the verbosity of output\nSpecify no more than 3 times\n-v -v -v or -vvv")
- .req_flag(())
- .many()
- .map(|xs| xs.len())
- .guard(|&x| x <= 3, "Cannot have more than 3 levels of verbosity")
-}
diff --git a/src/download.rs b/src/download.rs
deleted file mode 100644
index f88c4db..0000000
--- a/src/download.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use {
- log::*,
- octocrab::models::repos::Asset,
- std::path::PathBuf,
- tokio::fs::{create_dir_all, write},
-};
-
-/// Download a file from a provided URL
-pub async fn download_asset>(
- asset: &'_ Asset,
- out_dir: P,
-) -> crate::Result {
- let out_file_name: PathBuf = out_dir.into();
- let out_file_name = out_file_name.join(&asset.name);
- info!("Downloading file to {:?}", &out_file_name);
- if let Some(out_parent) = &out_file_name.parent() {
- match create_dir_all(out_parent).await {
- Ok(_) => {}
- Err(create_dirs_err) => eyre::bail!(create_dirs_err),
- };
- };
- match reqwest::get(asset.browser_download_url.as_str()).await {
- Ok(cd) => match cd.bytes().await {
- Ok(bytes) => match write(&out_file_name, bytes).await {
- Ok(_) => Ok(out_file_name),
- Err(write_err) => eyre::bail!(write_err),
- },
- Err(bytes_err) => eyre::bail!(bytes_err),
- },
- Err(down_err) => eyre::bail!(down_err),
- }
-}
diff --git a/src/environment.rs b/src/environment.rs
deleted file mode 100644
index baf5d13..0000000
--- a/src/environment.rs
+++ /dev/null
@@ -1,312 +0,0 @@
-use {
- crate::{archiver, download, tool::Tool, version::Version},
- log::*,
- octocrab::models::repos::Asset,
- serde::{Deserialize, Serialize},
- serde_json::{from_str, to_string_pretty},
- std::{
- os::unix::prelude::PermissionsExt,
- path::{Path, PathBuf},
- },
- tokio::{fs::read_to_string, process::Command},
- walkdir::{DirEntry, WalkDir},
-};
-
-pub trait Env {
- fn add_tool(&self, name: &'_ str, version: &'_ Version, asset: &'_ Asset) -> crate::Result<()>;
- fn remove_tool(&self, name: &'_ str) -> crate::Result<()>;
- fn change_tool_version(&self, name: &'_ str, new_version: &'_ Version) -> crate::Result<()>;
-}
-
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct Environment {
- pub name: String,
- #[serde(skip)]
- pub base_dir: String,
- pub tools: Vec,
-}
-
-impl Drop for Environment {
- fn drop(&mut self) {
- let out_file = Path::new(&self.base_dir).parent().unwrap();
- let out_file = out_file.join(format!("{}.json", &self.name));
- info!("Writing {} environment file to {:?}", &self.name, out_file);
- let contents = to_string_pretty(&self).unwrap();
- std::fs::write(out_file, contents).unwrap();
- }
-}
-
-impl Environment {
- pub async fn load>(config_dir: P, name: &'_ str) -> super::Result {
- let config_dir: PathBuf = config_dir.into();
- let env_dir = config_dir.join("envs");
- if !env_dir.exists() {
- match std::fs::create_dir_all(&env_dir) {
- Ok(_) => {}
- Err(create_dir_err) => {
- eyre::bail!("Unable to create the envs directory: {}", create_dir_err)
- }
- }
- }
- let env_path = env_dir.join(format!("{}.json", name));
- match read_to_string(&env_path).await {
- Ok(file_contents) => match from_str::(&file_contents) {
- Ok(mut res) => {
- res.base_dir = env_dir
- .join(name)
- .to_str()
- .expect("Unable to convert path to a string")
- .to_string();
- Ok(res)
- }
- Err(serde_err) => eyre::bail!(
- "Failed to deserialize {:?} as an environment. {:?}",
- env_path,
- serde_err
- ),
- },
- Err(read_err) => match read_err.kind() {
- std::io::ErrorKind::NotFound => {
- debug!("Environment file does not exist");
- Ok(Environment {
- name: name.to_string(),
- base_dir: env_dir.join(name).to_str().unwrap_or_default().to_string(),
- tools: Vec::new(),
- })
- }
- _ => eyre::bail!(
- "Failed to read contents from file {:?}. {}",
- env_path,
- read_err
- ),
- },
- }
- }
-
- pub async fn add_tool(
- &mut self,
- name: &'_ str,
- alias: &'_ str,
- version: Version,
- asset: Asset,
- asset_pattern: &'_ str,
- file_pattern: &'_ str,
- ) -> crate::Result<()> {
- let tool_dir = Path::new(&self.base_dir)
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .join("tools")
- .join(name);
- let version_tag = version.as_tag();
- let tool_version_dir = tool_dir.clone().join(&version_tag);
-
- match download::download_asset(&asset, &tool_version_dir).await {
- Ok(asset_path) => {
- info!("Completed download: {}", asset.browser_download_url);
- let symlink_dest = Path::new(&self.base_dir).join(alias);
- let extractor = archiver::determine_extractor(&asset_path);
-
- match extractor {
- Some(extractor) => {
- match archiver::handle_file_extraction(
- extractor,
- &asset_path,
- Some(tool_version_dir.clone()),
- )
- .await
- {
- Ok(_) => {
- info!(
- "Extracted file {}",
- &asset_path.to_str().unwrap_or_default()
- );
-
- if let Some(bin_file) = find_binary(
- &tool_version_dir,
- if !file_pattern.is_empty() {
- file_pattern
- } else {
- alias
- },
- ) {
- create_symlink(&bin_file.into_path(), &symlink_dest);
- match self.tools.iter_mut().find(|t| t.name == name) {
- // add to the tools list
- Some(installed_tool) => {
- installed_tool.set_current_version(&version);
- let version_tag = &version.as_tag();
- if !installed_tool
- .installed_versions
- .iter()
- .any(|v| v[..] == version_tag[..])
- {
- installed_tool
- .installed_versions
- .push(version.as_tag());
- info!(
- "Added new version {} of {} in environment {}",
- version.as_tag(),
- name,
- self.name
- );
- }
- Ok(())
- }
- // create a new tool, and add to our list
- None => {
- self.tools.push(Tool::new(
- name,
- alias,
- &version,
- asset_pattern,
- file_pattern,
- ));
- info!(
- "Added new tool {} in environment {}",
- name, self.name
- );
- Ok(())
- }
- }
- } else {
- eyre::bail!(
- "Could not find a binary named '{}' in {:?}",
- alias,
- tool_version_dir
- )
- }
- }
- Err(extractor_err) => eyre::bail!(
- "Failed to extract the file {:?}. {:?}",
- asset_path,
- extractor_err
- ),
- }
- }
- None => {
- let cmd = Command::new("file")
- .arg(&asset_path.to_str().unwrap_or_default())
- .output();
- let executable_file: bool = match cmd.await {
- Ok(output) => {
- let is_exec =
- String::from_utf8(output.stdout)?.contains("executable");
-
- info!("Setting {:?} as executable", &asset_path);
- match std::fs::set_permissions(
- &asset_path,
- std::fs::Permissions::from_mode(0o0755),
- ) {
- Ok(_) => {
- debug!("Successfully set permissions for {:?}", asset_path)
- }
- Err(perm_err) => {
- error!(
- "Unable to set permissions on {:?}. {:?}",
- asset_path, perm_err
- )
- }
- }
-
- is_exec
- }
- Err(cmd_err) => {
- eyre::bail!(
- "Unable to run `file` on {:?}. {:?}",
- &asset_path,
- cmd_err
- )
- }
- };
-
- if executable_file {
- create_symlink(&asset_path, &symlink_dest);
- match self.tools.iter_mut().find(|t| t.name == name) {
- // add to the tools list
- Some(installed_tool) => {
- installed_tool.set_current_version(&version);
- let version_tag = &version.as_tag();
- if !installed_tool
- .installed_versions
- .iter()
- .any(|v| v[..] == version_tag[..])
- {
- installed_tool.installed_versions.push(version.as_tag());
- info!(
- "Added new version {} of {} to environment {}",
- version.as_tag(),
- name,
- self.name
- );
- }
- }
- // create a new tool, and add to our list
- None => {
- info!("Added new tool {} to environment {}", name, self.name);
- self.tools.push(Tool::new(
- name,
- alias,
- &version,
- asset_pattern,
- file_pattern,
- ));
- }
- };
- Ok(())
- } else {
- eyre::bail!("Failed to determine the file type for {:?}", asset_path)
- }
- }
- }
- }
- Err(download_err) => {
- eyre::bail!(
- "Failed to download file {} from {}. {:?}",
- asset.name,
- asset.browser_download_url,
- download_err
- )
- }
- }
- }
-}
-
-fn create_symlink(src: &'_ Path, dest: &'_ Path) {
- match std::env::consts::OS {
- "windows" => unimplemented!(),
- "linux" | "macos" => {
- if dest.exists() {
- match std::fs::read_link(dest) {
- Ok(read_link) => {
- if *read_link != *dest {
- // delete the symlink if it isn't pointing to the same file we are trying
- // to use
- info!("Removing existing symlink pointing at {:?}", read_link);
- std::fs::remove_file(dest).unwrap()
- }
- }
- Err(read_err) => panic!("Failed to read symlink. {:?}", read_err),
- };
- }
- std::fs::create_dir_all(dest.parent().unwrap()).unwrap();
- info!("Creating symlink from {:?} to {:?}", src, dest);
- match std::os::unix::fs::symlink(src, dest) {
- Ok(_) => (),
- Err(e) => error!(
- "Failed to create symlink from {:?} to {:?}. {:?}",
- src, dest, e
- ),
- }
- }
- _ => panic!("unknown operating system"),
- }
-}
-
-fn find_binary(folder: &'_ Path, bin_name: &'_ str) -> Option {
- WalkDir::new(folder)
- .into_iter()
- .filter_map(Result::ok)
- .find(|entry| entry.file_name() == bin_name)
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 51fe30c..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-// Turn off common dev assertions only for debug builds, release builds will still work as normal
-#![warn(clippy::all)]
-// #![cfg_attr(
-// debug_assertions,
-// allow(dead_code, unused_macros, unused_imports, unused_variables)
-// )]
-
-mod archiver;
-mod cli;
-mod cli_actions;
-mod dirs;
-mod download;
-mod environment;
-mod github;
-mod system;
-mod tool;
-mod version;
-
-use {
- crate::{cli::Actions, cli_actions::UpdateType, environment::Environment, system::System},
- log::*,
-};
-
-pub type Result = eyre::Result;
-
-#[tokio::main]
-async fn main() -> Result<()> {
- let opts = cli::opts().run();
- env_logger::builder()
- .filter_level(match opts.verbose {
- 3 => LevelFilter::Trace,
- 2 => LevelFilter::Debug,
- 1 => LevelFilter::Info,
- _ => LevelFilter::Warn,
- })
- .init();
-
- let config_dir = dirs::get_default_config_path();
-
- if let Some(api_token) = opts.github_api_token {
- info!("Initializing the GitHub client with token from CLI args");
- octocrab::initialise(octocrab::Octocrab::builder().personal_token(api_token))?;
- } else if let Some(env_api_token) = std::env::var_os("GITHUB_TOKEN") {
- info!("Initializing the GitHub client with token from environment");
- octocrab::initialise(
- octocrab::Octocrab::builder().personal_token(env_api_token.to_str().unwrap().into()),
- )?;
- };
-
- match opts.action {
- Actions::Add {
- name,
- alias,
- pattern,
- filter,
- pre_release,
- show,
- } => {
- debug!("CLI: Name `{name}`, alias `{:?}`, pattern `{:?}`, filter `{:?}`, pre_release `{pre_release}`, show `{show}`", alias, pattern, filter);
- let system = System::new();
- let mut env = Environment::load(&config_dir, &opts.env).await?;
- cli_actions::add_new_tool(
- &mut env,
- &name,
- &system,
- pattern,
- filter,
- alias,
- show,
- pre_release,
- )
- .await?;
- }
- Actions::Remove { name, all } => {
- let mut env = Environment::load(&config_dir, &opts.env).await?;
- cli_actions::remove_tool(&mut env, &name, all).await?;
- }
- Actions::List { installed } => {
- let env = Environment::load(&config_dir, &opts.env).await?;
- cli_actions::list_tools(&env, installed).await?;
- }
- Actions::Update { name } => {
- let system = System::new();
- let mut env = Environment::load(&config_dir, &opts.env).await?;
- cli_actions::update_tools(
- &mut env,
- &system,
- if let Some(name) = name {
- UpdateType::Specific(name)
- } else {
- UpdateType::All
- },
- )
- .await?;
- }
- Actions::Env { name, shell } => {
- let name = match name {
- Some(name) => name,
- None => opts.env,
- };
- let env = Environment::load(&config_dir, &name).await?;
- match &shell[..] {
- "fish" => println!("set -p PATH \"{}\"", env.base_dir),
- "bash" | "sh" | "zsh" => println!("export PATH=\"{}:$PATH\"", env.base_dir),
- _ => panic!("{} is not a known shell", shell),
- }
- }
- Actions::Sync => {
- let system = System::new();
- let mut env = Environment::load(&config_dir, &opts.env).await?;
- println!("Syncing versions with {} configuration.", env.name);
- cli_actions::sync_tools(&mut env, &system).await?;
- }
- };
- Ok(())
-}