From c53acab0f2a95a3eda6a77289c19051cd86b462e Mon Sep 17 00:00:00 2001 From: Chris Chou Date: Sat, 30 Dec 2023 00:18:40 +1300 Subject: [PATCH] add shell specific command execution --- README.md | 20 ++++-- src/hooks/extract.rs | 40 +++++++---- src/hooks/init.rs | 55 ++++---------- src/hooks/install.rs | 45 +++--------- src/hooks/load.rs | 12 ++++ src/hooks/resource.rs | 74 ++++++++++++++----- src/hooks/src.rs | 42 ++++++++++- src/main.rs | 12 +++- src/utils.rs | 1 + src/utils/cli.rs | 7 +- src/utils/config.rs | 164 ++++++++++++++++++++++++++++-------------- src/utils/pipeline.rs | 52 ++++++++------ src/utils/script.rs | 81 +++++++++++++++++++-- src/utils/shells.rs | 48 +++++++++++++ src/utils/shim.rs | 34 ++++++--- src/utils/symlink.rs | 10 +-- 16 files changed, 485 insertions(+), 212 deletions(-) create mode 100644 src/utils/shells.rs diff --git a/README.md b/README.md index 85aefde..a0f3cbc 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,12 @@ repo: ajeetdsouza/zoxide from_release: true exec: '**/zoxide' - install: '**/zoxide init zsh > init-zoxide.zsh' - src: 'init-zoxide.zsh' + install: + zsh: '**/zoxide init zsh > init-zoxide.zsh' + bash: '**/zoxide init bash > init-zoxide.bash' + fish: '**/zoxide init fish > init-zoxide.fish' + src: + zsh: 'init-zoxide.zsh' load: 'alias cd=z' - id: fd @@ -106,8 +110,10 @@ resource: repo: direnv/direnv from_release: true - install: 'mv direnv* direnv; chmod +x ./direnv; ./direnv hook zsh > zhook.zsh' - src: zhook.zsh + install: + zsh: 'mv direnv* direnv; chmod +x ./direnv; ./direnv hook zsh > zhook.zsh' + src: + zsh: zhook.zsh load: export DIRENV_LOG_FORMAT="" exec: '**/direnv' @@ -145,12 +151,14 @@ - id: zsh-autosuggestions resource: repo: zsh-users/zsh-autosuggestions - src: zsh-autosuggestions.zsh + src: + zsh: zsh-autosuggestions.zsh - id: fast-syntax-highlighting resource: repo: z-shell/F-Sy-H - src: f-sy-h.plugin.zsh + src: + zsh: f-sy-h.plugin.zsh ``` ### Order of Execution diff --git a/src/hooks/extract.rs b/src/hooks/extract.rs index 7822f77..75044c9 100644 --- a/src/hooks/extract.rs +++ b/src/hooks/extract.rs @@ -1,30 +1,39 @@ use infer; use std::path::Path; -use crate::utils::script::*; +use crate::utils::{script::*, shells::SupportedShell}; -pub fn extract(cmd: &str) -> Result<(), Box> { - run_cmd(&cmd)?; +pub fn extract( + current_shell: &SupportedShell, + cmd: &str, +) -> Result<(), Box> { + run_cmd(current_shell, &cmd)?; Ok(()) } -pub fn extract_asset(asset_path: &Path) -> Result<(), Box> { +pub fn extract_asset( + current_shell: &SupportedShell, + asset_path: &Path, +) -> Result<(), Box> { let asset_path_string = &asset_path.display().to_string(); let infer_kind = infer::get_from_path(asset_path)?; if let Some(kind) = &infer_kind { match kind.extension() { "zip" => { - run_cmd(&format!("unzip {}", asset_path_string))?; + run_cmd(current_shell, &format!("unzip {}", asset_path_string))?; } "gz" => { - run_cmd(&format!("tar xvf {}", asset_path_string))?; + run_cmd(current_shell, &format!("tar xvf {}", asset_path_string))?; } "deb" => { - run_cmd(&format!( - "ar xv {}; ls *.tar.* | xargs -n 1 tar xvf", - asset_path_string - ))?; + run_cmd( + current_shell, + &format!( + "ar xv {}; ls *.tar.* | xargs -n 1 tar xvf", + asset_path_string + ), + )?; } _ => {} } @@ -32,8 +41,10 @@ pub fn extract_asset(asset_path: &Path) -> Result<(), Box println!("ext {:?}", ext); match ext.to_str().unwrap() { "dmg" => { - run_cmd(&format!( - r#" + run_cmd( + current_shell, + &format!( + r#" _extract_dmg() {{ local dmg_name="{}" echo "dmg_name $dmg_name" @@ -49,8 +60,9 @@ pub fn extract_asset(asset_path: &Path) -> Result<(), Box _extract_dmg "#, - asset_path_string - ))?; + asset_path_string + ), + )?; } _ => {} } diff --git a/src/hooks/init.rs b/src/hooks/init.rs index 60397f7..121642d 100644 --- a/src/hooks/init.rs +++ b/src/hooks/init.rs @@ -1,44 +1,17 @@ -use crate::utils::{config::AdaptiveInit, script::*}; -use log::debug; +use crate::utils::{ + config::{ShellSpecificCommand, SupportedShellSpecificCommand}, + script::*, + shells::SupportedShell, +}; -pub fn init(adaptive_init: &AdaptiveInit) -> Result> { - let init_result = match adaptive_init { - AdaptiveInit::Run(cmd) => run_cmd_with_output(&cmd)?, - AdaptiveInit::OSSpecific { - linux, - macos, - windows, - } => { - let os = std::env::consts::OS; - match os { - "linux" => { - if let Some(cmd) = &linux { - run_cmd_with_output(&cmd)? - } else { - "".to_owned() - } - } - "macos" => { - if let Some(cmd) = &macos { - run_cmd_with_output(&cmd)? - } else { - "".to_owned() - } - } - "windows" => { - if let Some(cmd) = &windows { - run_cmd_with_output(&cmd)? - } else { - "".to_owned() - } - } - _ => { - debug!("init hook: unsupported os os={}", os); - "".to_owned() - } - } +pub fn init( + current_shell: &SupportedShell, + init_cmd: &ShellSpecificCommand, +) -> Result> { + match init_cmd { + ShellSpecificCommand::Generic(generic) => run_cmd_with_output(current_shell, generic), + ShellSpecificCommand::ShellSpecific(shell_specific) => { + run_shell_cmd(current_shell, shell_specific) } - }; - - Ok(init_result) + } } diff --git a/src/hooks/install.rs b/src/hooks/install.rs index 34ebe10..44fd157 100644 --- a/src/hooks/install.rs +++ b/src/hooks/install.rs @@ -1,38 +1,13 @@ -use crate::utils::{config::AdaptiveInstall, script::*}; +use crate::utils::{config::ShellSpecificCommand, script::*, shells::SupportedShell}; -pub fn install(adaptive_install: &AdaptiveInstall) -> Result<(), Box> { - match adaptive_install { - AdaptiveInstall::Run(cmd) => { - run_cmd_with_output(&cmd)?; +pub fn install( + current_shell: &SupportedShell, + install_cmd: &ShellSpecificCommand, +) -> Result> { + match install_cmd { + ShellSpecificCommand::Generic(generic) => run_cmd_with_output(current_shell, generic), + ShellSpecificCommand::ShellSpecific(shell_specific) => { + run_shell_cmd(current_shell, shell_specific) } - AdaptiveInstall::OSSpecific { - linux, - macos, - windows, - } => { - let os = std::env::consts::OS; - match os { - "linux" => { - if let Some(cmd) = &linux { - run_cmd(&cmd)?; - } - } - "macos" => { - if let Some(cmd) = &macos { - run_cmd(&cmd)?; - } - } - "windows" => { - if let Some(cmd) = &windows { - run_cmd(&cmd)?; - } - } - _ => { - println!("install hook: unsupported os os={}", os); - } - } - } - }; - - Ok(()) + } } diff --git a/src/hooks/load.rs b/src/hooks/load.rs index 8b13789..d194020 100644 --- a/src/hooks/load.rs +++ b/src/hooks/load.rs @@ -1 +1,13 @@ +use crate::utils::{config::ShellSpecificCommand, script::*, shells::SupportedShell}; +pub fn load( + current_shell: &SupportedShell, + load_cmd: &ShellSpecificCommand, +) -> Result> { + match load_cmd { + ShellSpecificCommand::Generic(generic) => run_cmd_with_output(current_shell, generic), + ShellSpecificCommand::ShellSpecific(shell_specific) => { + run_shell_cmd(current_shell, shell_specific) + } + } +} diff --git a/src/hooks/resource.rs b/src/hooks/resource.rs index 5762f7c..4ebae80 100644 --- a/src/hooks/resource.rs +++ b/src/hooks/resource.rs @@ -1,6 +1,7 @@ use crate::utils::config::*; use crate::utils::paths::*; use crate::utils::script::*; +use crate::utils::shells::SupportedShell; use log::error; use regex::Regex; @@ -88,6 +89,7 @@ fn get_provider(repo_provider_type: &Option) -> Result Result> { @@ -99,7 +101,7 @@ fn clone_repo( }; assert!(std::env::set_current_dir(&payload_config_dir).is_ok()); - run_cmd(&format!("git clone {}", &url))?; + run_cmd(current_shell, &format!("git clone {}", &url))?; get_resource_name_from_url(&url) } @@ -241,24 +243,26 @@ fn get_asset( } fn clone_and_checkout_repo( + current_shell: &SupportedShell, repo: &Repo, payload_config_dir: &Path, current_install_dir: &Path, ) -> Result<(), Box> { - let repo_name = clone_repo(&payload_config_dir, &repo)?; + let repo_name = clone_repo(current_shell, &payload_config_dir, &repo)?; let resource_path = payload_config_dir.join(&repo_name); set_resource_as_current(&resource_path, ¤t_install_dir)?; // checkout branch/tag if let Some(ver) = &repo.ver { assert!(std::env::set_current_dir(¤t_install_dir).is_ok()); - run_cmd(&format!("git checkout -q {}", &ver))?; + run_cmd(current_shell, &format!("git checkout -q {}", &ver))?; }; Ok(()) } fn get_resource_repo( + current_shell: &SupportedShell, payload_config_dir: &Path, current_install_dir: &Path, repo: &Repo, @@ -269,11 +273,21 @@ fn get_resource_repo( let url = get_repo_release_asset_url(&repo)?; Some(get_asset(&payload_config_dir, ¤t_install_dir, &url)?) } else { - clone_and_checkout_repo(&repo, &payload_config_dir, ¤t_install_dir)?; + clone_and_checkout_repo( + current_shell, + &repo, + &payload_config_dir, + ¤t_install_dir, + )?; None } } else { - clone_and_checkout_repo(&repo, &payload_config_dir, ¤t_install_dir)?; + clone_and_checkout_repo( + current_shell, + &repo, + &payload_config_dir, + ¤t_install_dir, + )?; None }; @@ -301,13 +315,19 @@ fn get_resource_location( } pub fn get_resource( + current_shell: &SupportedShell, payload_config_dir: &Path, current_install_dir: &Path, resource: &Resource, init_result: Option<&str>, ) -> Result, Box> { match &resource { - Resource::Repo(repo) => get_resource_repo(&payload_config_dir, ¤t_install_dir, repo), + Resource::Repo(repo) => get_resource_repo( + current_shell, + &payload_config_dir, + ¤t_install_dir, + repo, + ), Resource::Location(url) => { get_resource_location(&payload_config_dir, ¤t_install_dir, &url, init_result) } @@ -315,6 +335,7 @@ pub fn get_resource( } fn get_os_specific_resource( + current_shell: &SupportedShell, payload_config_dir: &Path, current_install_dir: &Path, init_result: Option<&str>, @@ -334,10 +355,15 @@ fn get_os_specific_resource( let resource_path = if let Some(os_specific_resource) = supported_os_specific_resource { match os_specific_resource { - OSSpecificResource::Standard(res) => { - get_resource(payload_config_dir, current_install_dir, &res, init_result)? - } + OSSpecificResource::Standard(res) => get_resource( + current_shell, + payload_config_dir, + current_install_dir, + &res, + init_result, + )?, OSSpecificResource::ArchSpecific(res) => get_arch_specific_resource( + current_shell, &payload_config_dir, ¤t_install_dir, init_result, @@ -352,10 +378,11 @@ fn get_os_specific_resource( } fn get_arch_specific_resource( + current_shell: &SupportedShell, payload_config_dir: &Path, current_install_dir: &Path, init_result: Option<&str>, - resource: &ArchSpecificResource, + resource: &SupportedArchSpecificResource, ) -> Result, Box> { let machine_arch = env::consts::ARCH; let supported_arch_specific_resource = match machine_arch.as_ref() { @@ -368,7 +395,13 @@ fn get_arch_specific_resource( }; let resource_path = if let Some(res) = supported_arch_specific_resource { - get_resource(payload_config_dir, current_install_dir, &res, init_result)? + get_resource( + current_shell, + payload_config_dir, + current_install_dir, + &res, + init_result, + )? } else { None }; @@ -377,6 +410,7 @@ fn get_arch_specific_resource( } pub fn get_adaptive_resource( + current_shell: &SupportedShell, payload: &Payload, init_result: Option<&str>, ) -> Result, Box> { @@ -384,13 +418,19 @@ pub fn get_adaptive_resource( let current_install_dir = get_payload_current_install_dir_path(&payload)?; let asset_path = match &payload.resource { - AdaptiveResource::Repo(repo) => { - get_resource_repo(&payload_config_dir, ¤t_install_dir, repo)? - } - AdaptiveResource::Location(url) => { - get_resource_location(&payload_config_dir, ¤t_install_dir, url, init_result)? - } + AdaptiveResource::Standard(resource) => match resource { + Resource::Repo(repo) => get_resource_repo( + current_shell, + &payload_config_dir, + ¤t_install_dir, + repo, + )?, + Resource::Location(url) => { + get_resource_location(&payload_config_dir, ¤t_install_dir, url, init_result)? + } + }, AdaptiveResource::OSSpecific(os_specific_resource) => get_os_specific_resource( + current_shell, &payload_config_dir, ¤t_install_dir, init_result, diff --git a/src/hooks/src.rs b/src/hooks/src.rs index 9a4ec2b..f1c4fb8 100644 --- a/src/hooks/src.rs +++ b/src/hooks/src.rs @@ -2,7 +2,37 @@ use glob::glob; use std::fs; -pub fn src(files: &Vec) -> Result<(), Box> { +use crate::utils::{ + config::{ShellSpecificSourceTarget, SourceTarget, SupportedShellSpecificSourceTarget}, + shells::SupportedShell, +}; + +pub fn src( + current_shell: &SupportedShell, + specified_src_target: &ShellSpecificSourceTarget, +) -> Result<(), Box> { + match specified_src_target { + ShellSpecificSourceTarget::Generic(generic) => process_src_target(&generic), + ShellSpecificSourceTarget::ShellSpecific(shell_specific) => { + let op_shell_specific_target = match current_shell { + SupportedShell::Sh => &shell_specific.sh, + SupportedShell::Bash => &shell_specific.bash, + SupportedShell::Zsh => &shell_specific.zsh, + SupportedShell::Fish => &shell_specific.fish, + SupportedShell::PowerShell => &shell_specific.powershell, + SupportedShell::WinCmd => &shell_specific.wincmd, + }; + + if let Some(shell_specific_target) = op_shell_specific_target { + process_src_target(&shell_specific_target) + } else { + Ok(()) + } + } + } +} + +pub fn src_files(files: &Vec) -> Result<(), Box> { for f in files { if f.contains("*") { // handle globs @@ -24,3 +54,13 @@ fn print_src_path_canonical(path: &str) { println!(". {}", &canonical_path.display().to_string()); }; } + +fn process_src_target(target: &SourceTarget) -> Result<(), Box> { + match target { + SourceTarget::Single(target) => { + let src_target = vec![target.to_owned()]; + src_files(&src_target) + } + SourceTarget::Multiple(targets) => src_files(targets), + } +} diff --git a/src/main.rs b/src/main.rs index c14d017..502c94f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use clap::Parser; use log::error; +use orbiter::utils::shells::SupportedShell; use std::process; use orbiter::utils::cli; @@ -9,9 +10,14 @@ use orbiter::utils::pipeline; fn main() { let cmd = cli::Cli::parse(); + let mut current_shell: SupportedShell = SupportedShell::Sh; // You can check for the existence of subcommands, and if found use their // matches just as you would the top level cmd match &cmd.command { + Some(cli::Commands::Init { shell }) => { + current_shell = SupportedShell::from_str(shell); + println!("init shell={:?}", shell); + } Some(cli::Commands::Update { item }) => { if let Some(item_id) = item { println!("update item={:?}", &item_id); @@ -25,18 +31,18 @@ fn main() { None => {} } - if let Err(e) = run() { + if let Err(e) = run(¤t_shell) { println!("Orbiter has encountered an error: {e}"); process::exit(1); } } -fn run() -> Result<(), Box> { +fn run(current_shell: &SupportedShell) -> Result<(), Box> { env_logger::init(); let payloads = config::get_payloads()?; payloads.iter().for_each(|payload| { - let result = pipeline::process_payload(&payload); + let result = pipeline::process_payload(current_shell, &payload); match &result { Ok(_) => (), Err(err) => error!("error processing payload [{:#?}]: {}", &payload, &err), diff --git a/src/utils.rs b/src/utils.rs index bd38813..1bfec14 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,5 +3,6 @@ pub mod config; pub mod paths; pub mod pipeline; pub mod script; +pub mod shells; pub mod shim; pub mod symlink; diff --git a/src/utils/cli.rs b/src/utils/cli.rs index 2139892..11e694b 100644 --- a/src/utils/cli.rs +++ b/src/utils/cli.rs @@ -10,6 +10,11 @@ pub struct Cli { #[derive(Subcommand)] pub enum Commands { /// update item(s) - Update { item: Option }, + Init { + shell: String, + }, + Update { + item: Option, + }, // List, } diff --git a/src/utils/config.rs b/src/utils/config.rs index fb3acb5..b9f1cb3 100644 --- a/src/utils/config.rs +++ b/src/utils/config.rs @@ -37,8 +37,7 @@ pub enum Resource { #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum AdaptiveResource { - Location(String), - Repo(Repo), + Standard(Resource), OSSpecific(SupportedOSSpecificResource), } @@ -53,15 +52,46 @@ pub struct SupportedOSSpecificResource { #[serde(untagged)] pub enum OSSpecificResource { Standard(Resource), - ArchSpecific(ArchSpecificResource), + ArchSpecific(SupportedArchSpecificResource), } #[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct ArchSpecificResource { +pub struct SupportedArchSpecificResource { pub x86_64: Option, pub aarch64: Option, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct SupportedOSSpecificCommand { + pub linux: Option, + pub macos: Option, + pub windows: Option, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum OSSpecificCommand { + Generic(String), + OSSpecific(SupportedOSSpecificCommand), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct SupportedShellSpecificCommand { + pub sh: Option, + pub zsh: Option, + pub bash: Option, + pub fish: Option, + pub powershell: Option, + pub wincmd: Option, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ShellSpecificCommand { + Generic(String), + ShellSpecific(SupportedShellSpecificCommand), +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum Executable { @@ -89,27 +119,24 @@ pub enum SourceTarget { } #[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum AdaptiveInstall { - Run(String), - OSSpecific { - linux: Option, - macos: Option, - windows: Option, - }, +pub struct SupportedShellSpecificSourceTarget { + pub sh: Option, + pub zsh: Option, + pub bash: Option, + pub fish: Option, + pub powershell: Option, + pub wincmd: Option, } + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] -pub enum AdaptiveInit { - Run(String), - OSSpecific { - linux: Option, - macos: Option, - windows: Option, - }, +pub enum ShellSpecificSourceTarget { + Generic(SourceTarget), + ShellSpecific(SupportedShellSpecificSourceTarget), } + #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Payload { // The `string_or_struct` function delegates deserialization to a type's @@ -118,13 +145,13 @@ pub struct Payload { // `Build`) so it can be reused for any field that implements both `FromStr` // and `Deserialize`. pub id: Option, - pub init: Option, + pub init: Option, pub resource: AdaptiveResource, - pub extract: Option, - pub install: Option, - pub update: Option, - pub src: Option, - pub load: Option, + pub extract: Option, // path to the file to be extracted + pub install: Option, + pub update: Option, + pub src: Option, + pub load: Option, pub exec: Option, pub menu: Option, } @@ -149,7 +176,7 @@ mod parse_tests { Payload { id: None, init: None, - resource: AdaptiveResource::Location("https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US".to_string()), + resource: AdaptiveResource::Standard(Resource::Location("https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US".to_string())), install: None, update: None, src: None, @@ -169,7 +196,9 @@ mod parse_tests { - id: ff-dev resource: https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US exec: "**/firefox" - install: "./GitAhead*.sh --include-subdir" + install: + sh: + macos: "./GitAhead*.sh --include-subdir" menu: name: firefox run: "env GDK_BACKEND=wayland $(readlink -f firefox/firefox)" @@ -182,8 +211,15 @@ mod parse_tests { Payload { id: Some("ff-dev".to_string()), init: None, - resource: AdaptiveResource::Location("https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US".to_string()), - install: Some(AdaptiveInstall::Run("./GitAhead*.sh --include-subdir".to_string()) ), + resource: AdaptiveResource::Standard(Resource::Location("https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US".to_string())), + install: Some(ShellSpecificCommand::ShellSpecific(SupportedShellSpecificCommand{ + sh:Some( OSSpecificCommand::OSSpecific( SupportedOSSpecificCommand{ + macos: Some("./GitAhead*.sh --include-subdir".to_string()), + linux: None, windows: None}, + )), + zsh: None, bash: None, fish: None, powershell: None, wincmd: None} + ), + ), update: None, src: None, extract: None, @@ -208,7 +244,9 @@ mod parse_tests { - id: gitahead resource: repo: gitahead/gitahead - install: ./GitAhead*.sh --include-subdir + install: + sh: + macos: './GitAhead*.sh --include-subdir' exec: run: '**/GitAhead' alias: gitahead @@ -218,16 +256,20 @@ mod parse_tests { let expected = vec![Payload { id: Some("gitahead".to_string()), init: None, - resource: AdaptiveResource::Repo(Repo { + resource: AdaptiveResource::Standard(Resource::Repo(Repo { repo: "gitahead/gitahead".to_string(), provider: None, ver: None, from_release: None, binary_pattern: None, - }), - install: Some(AdaptiveInstall::Run( - "./GitAhead*.sh --include-subdir".to_string(), - )), + })), + install: Some(ShellSpecificCommand::ShellSpecific(SupportedShellSpecificCommand{ + sh: Some(OSSpecificCommand::OSSpecific( SupportedOSSpecificCommand{ + macos: Some("./GitAhead*.sh --include-subdir".to_string()), + linux: None, windows: None }) + ), + zsh: None, bash: None, fish: None, powershell: None, wincmd: None + })), update: None, src: None, extract: None, @@ -250,7 +292,7 @@ mod parse_tests { resource: repo: neovim/neovim binary_pattern: "*.tar.gz" - extract: "tar xvf *.tar.*" + extract: "*.tar.*" exec: "**/bin/nvim" "#; @@ -258,11 +300,13 @@ mod parse_tests { let expected = "*.tar.gz"; let actual_resource = &actual.first().unwrap().resource; - if let AdaptiveResource::Repo(rel) = actual_resource { - assert_eq!(rel.binary_pattern.as_ref().unwrap(), expected) - } else { - panic!("No binary_pattern") - } + if let AdaptiveResource::Standard(res) = actual_resource { + if let Resource::Repo(rel) = res { + return assert_eq!(rel.binary_pattern.as_ref().unwrap(), expected) + } + } + + panic!("No binary_pattern") } #[test] @@ -270,9 +314,10 @@ mod parse_tests { let config = r#" - id: ff-dev resource: https://download.mozilla.org/?product=firefox-devedition-latest-ssl&os=linux64&lang=en-US - extract: "tar xzf *.tar.gz" + extract: "*.tar.gz" exec: "**/firefox" - install: "./GitAhead*.sh --include-subdir" + install: + sh: "./GitAhead*.sh --include-subdir" menu: name: firefox run: "env GDK_BACKEND=wayland $(readlink -f firefox/firefox)" @@ -281,9 +326,17 @@ mod parse_tests { "#; let actual: Vec = parse(config).unwrap(); - let expected = "tar xzf *.tar.gz"; + let expected = "*.tar.gz"; - assert_eq!(actual.first().unwrap().extract.as_ref().unwrap(), expected) + assert_eq!( + actual + .first() + .unwrap() + .extract + .as_ref() + .unwrap(), + expected + ) } #[test] @@ -294,7 +347,8 @@ mod parse_tests { macos: aarch64: https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-arm64 linux: https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 - install: 'chmod +x ./minikube; ./minikube completion zsh > zsh_completion.zsh' + install: + sh: 'chmod +x ./minikube; ./minikube completion zsh > zsh_completion.zsh' src: zsh_completion.zsh exec: minikube "#; @@ -334,7 +388,9 @@ mod from_reader_tests { - id: gitahead resource: repo: gitahead/gitahead - install: ./GitAhead*.sh --include-subdir + install: + sh: + macos: './GitAhead*.sh --include-subdir' exec: run: '**/GitAhead' alias: gitahead @@ -347,16 +403,20 @@ mod from_reader_tests { let expected = vec![Payload { id: Some("gitahead".to_string()), init: None, - resource: AdaptiveResource::Repo(Repo { + resource: AdaptiveResource::Standard(Resource::Repo(Repo { repo: "gitahead/gitahead".to_string(), provider: None, ver: None, from_release: None, binary_pattern: None, - }), - install: Some(AdaptiveInstall::Run( - "./GitAhead*.sh --include-subdir".to_string(), - )), + })), + install:Some(ShellSpecificCommand::ShellSpecific( SupportedShellSpecificCommand { + sh: Some(OSSpecificCommand::OSSpecific( SupportedOSSpecificCommand { + macos: Some("./GitAhead*.sh --include-subdir".to_string()), + linux: None, windows: None, + })), + zsh: None, bash: None, fish: None, powershell: None, wincmd: None, + })), update: None, src: None, extract: None, diff --git a/src/utils/pipeline.rs b/src/utils/pipeline.rs index 32e5819..b6d5760 100644 --- a/src/utils/pipeline.rs +++ b/src/utils/pipeline.rs @@ -2,17 +2,22 @@ use std::fs; use super::config::*; use super::paths::*; +use super::shells::SupportedShell; use super::shim::*; use super::symlink::*; use crate::hooks::extract::*; use crate::hooks::init::*; use crate::hooks::install::*; +use crate::hooks::load::*; use crate::hooks::resource::*; use crate::hooks::src::*; use log::info; -pub fn process_payload(payload: &Payload) -> Result<(), Box> { +pub fn process_payload( + current_shell: &SupportedShell, + payload: &Payload, +) -> Result<(), Box> { // check if already worked on let payload_orbiter_dir_path = get_payload_config_dir_path(payload)?; if !payload_orbiter_dir_path.exists() { @@ -23,13 +28,13 @@ pub fn process_payload(payload: &Payload) -> Result<(), Box Result<(), Box { - create_shim(&cmd, &get_shim_content(&cmd, &cmd, None)?)?; + create_shim( + current_shell, + &cmd, + &get_shim_content(current_shell, &cmd, &cmd, None)?, + )?; } Executable::Command { @@ -61,13 +70,21 @@ pub fn process_payload(payload: &Payload) -> Result<(), Box { if let Some(is_use_symlink) = use_symlink { if is_use_symlink.to_owned() { - create_symlink(run, alias)?; + create_symlink(current_shell, run, alias)?; } } else { if let Some(alias) = alias.as_ref() { - create_shim(alias, &get_shim_content(run, alias, None)?)?; + create_shim( + current_shell, + alias, + &get_shim_content(current_shell, run, alias, None)?, + )?; } else { - create_shim(run, &get_shim_content(run, run, None)?)?; + create_shim( + current_shell, + run, + &get_shim_content(current_shell, run, run, None)?, + )?; }; }; } @@ -76,27 +93,21 @@ pub fn process_payload(payload: &Payload) -> Result<(), Box { - let src_target = vec![target.to_owned()]; - src(&src_target)?; - } - SourceTarget::Multiple(targets) => src(targets)?, - }; + src(current_shell, src_target)?; } // post load - if let Some(load_script) = &payload.load { + if let Some(load_cmd) = &payload.load { // set wd to payload config dir let current_install_dir = get_payload_current_install_dir_path(payload)?; assert!(std::env::set_current_dir(¤t_install_dir).is_ok()); - println!("{}", &load_script); + load(current_shell, load_cmd)?; } Ok(()) @@ -122,7 +133,6 @@ pub fn update_payload(payload: &Payload) -> Result<(), Box Result> { - let dflt_shell = env::var("SHELL")?; - let proc = Command::new(dflt_shell).arg("-c").arg(full_cmd).output()?; +use log::debug; - Ok(proc) +use super::config::{OSSpecificCommand, SupportedOSSpecificCommand, SupportedShellSpecificCommand}; +use super::shells::SupportedShell; + +pub fn run_cmd( + current_shell: &SupportedShell, + full_cmd: &str, +) -> Result> { + Ok(Command::new(current_shell.as_program_str()) + .arg(current_shell.as_dflt_arg_str()) + .arg(full_cmd) + .output()?) } #[allow(dead_code)] -pub fn run_cmd_with_output(full_cmd: &str) -> Result> { - let proc = run_cmd(full_cmd)?; +pub fn run_cmd_with_output( + current_shell: &SupportedShell, + full_cmd: &str, +) -> Result> { + let proc = run_cmd(current_shell, full_cmd)?; let stdout_content = str::from_utf8(&proc.stdout).unwrap_or(""); let stderr_content = str::from_utf8(&proc.stderr).unwrap_or(""); @@ -22,3 +33,59 @@ pub fn run_cmd_with_output(full_cmd: &str) -> Result, +) -> Result> { + Ok(if let Some(all_os_specific_cmd) = op_os_specific_cmd { + match all_os_specific_cmd { + OSSpecificCommand::Generic(cmd) => run_cmd_with_output(current_shell, &cmd)?, + OSSpecificCommand::OSSpecific(os_specific_cmd) => { + let os = std::env::consts::OS; + match os { + "linux" => { + if let Some(cmd) = &os_specific_cmd.linux { + run_cmd_with_output(current_shell, &cmd)? + } else { + "".to_owned() + } + } + "macos" => { + if let Some(cmd) = &os_specific_cmd.macos { + run_cmd_with_output(current_shell, &cmd)? + } else { + "".to_owned() + } + } + "windows" => { + if let Some(cmd) = &os_specific_cmd.windows { + run_cmd_with_output(current_shell, &cmd)? + } else { + "".to_owned() + } + } + _ => "".to_owned(), + } + } + } + } else { + "".to_owned() + }) +} + +pub fn run_shell_cmd( + current_shell: &SupportedShell, + shell_specific_cmd: &SupportedShellSpecificCommand, +) -> Result> { + let os_specific_cmd = match current_shell { + SupportedShell::Sh => &shell_specific_cmd.sh, + SupportedShell::Bash => &shell_specific_cmd.bash, + SupportedShell::Zsh => &shell_specific_cmd.zsh, + SupportedShell::Fish => &shell_specific_cmd.fish, + SupportedShell::PowerShell => &shell_specific_cmd.powershell, + SupportedShell::WinCmd => &shell_specific_cmd.wincmd, + }; + + Ok(run_os_specific_cmd(current_shell, os_specific_cmd)?) +} diff --git a/src/utils/shells.rs b/src/utils/shells.rs new file mode 100644 index 0000000..7c3af1d --- /dev/null +++ b/src/utils/shells.rs @@ -0,0 +1,48 @@ +use serde::Deserialize; +use serde::Serialize; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum SupportedShell { + Sh, + Zsh, + Bash, + Fish, + PowerShell, + WinCmd, +} + +impl SupportedShell { + pub fn as_program_str(&self) -> &'static str { + match self { + SupportedShell::Sh => "sh", + SupportedShell::Zsh => "zsh", + SupportedShell::Bash => "bash", + SupportedShell::Fish => "fish", + SupportedShell::PowerShell => "powershell", + SupportedShell::WinCmd => "cmd.exe", + } + } + + pub fn as_dflt_arg_str(&self) -> &'static str { + match self { + SupportedShell::Sh => "-c", + SupportedShell::Zsh => "-c", + SupportedShell::Bash => "-c", + SupportedShell::Fish => "-c", + SupportedShell::PowerShell => "-command", + SupportedShell::WinCmd => "/C", + } + } + + pub fn from_str(shell: &str) -> SupportedShell { + match shell { + "sh" => SupportedShell::Sh, + "zsh" => SupportedShell::Zsh, + "bash" => SupportedShell::Bash, + "fish" => SupportedShell::Fish, + "powershell" => SupportedShell::PowerShell, + "cmd" => SupportedShell::WinCmd, + _ => SupportedShell::Sh, + } + } +} diff --git a/src/utils/shim.rs b/src/utils/shim.rs index 5681a46..21cbd63 100644 --- a/src/utils/shim.rs +++ b/src/utils/shim.rs @@ -6,31 +6,38 @@ use std::path::PathBuf; use crate::utils::paths::*; use crate::utils::script::*; +use super::shells::SupportedShell; + pub fn get_func_name(func: &str) -> Result> { Ok(get_file_name(func)?) } pub fn get_shim_content( + current_shell: &SupportedShell, func: &str, bin_dir: &str, env: Option<&str>, ) -> Result> { match env { - None => get_basic_shim(func, bin_dir), - Some("base") => get_basic_shim(func, bin_dir), - Some(&_) => get_basic_shim(func, bin_dir), + None => get_basic_shim(current_shell, func, bin_dir), + Some("base") => get_basic_shim(current_shell, func, bin_dir), + Some(&_) => get_basic_shim(current_shell, func, bin_dir), } } -pub fn get_basic_shim(func: &str, bin_dir: &str) -> Result> { +pub fn get_basic_shim( + current_shell: &SupportedShell, + func: &str, + bin_dir: &str, +) -> Result> { let func_name = get_func_name(func)?; let resolved_bin_path = resolve_single_path(bin_dir)?; // set exec mode - run_cmd(&format!( - "chmod +x {}", - &resolved_bin_path.display().to_string() - ))?; + run_cmd( + current_shell, + &format!("chmod +x {}", &resolved_bin_path.display().to_string()), + )?; let bin_dir = get_dir(&resolved_bin_path)?.display().to_string(); @@ -59,14 +66,21 @@ fn get_shim_path(cmd: &str) -> Result> { Ok(get_bin_file_path(&get_func_name(&cmd)?)?) } -pub fn create_shim(cmd: &str, shim_content: &str) -> Result<(), Box> { +pub fn create_shim( + current_shell: &SupportedShell, + cmd: &str, + shim_content: &str, +) -> Result<(), Box> { fs::create_dir_all(&get_bin_dir_path()?)?; let shim_path = get_shim_path(&cmd)?; let mut dest = File::create(&shim_path)?; io::copy(&mut shim_content.as_bytes(), &mut dest)?; // set shim mode - run_cmd(&format!("chmod +x {}", &shim_path.display().to_string()))?; + run_cmd( + current_shell, + &format!("chmod +x {}", &shim_path.display().to_string()), + )?; Ok(()) } diff --git a/src/utils/symlink.rs b/src/utils/symlink.rs index 0ff3b69..122974c 100644 --- a/src/utils/symlink.rs +++ b/src/utils/symlink.rs @@ -1,7 +1,9 @@ use super::paths::*; use super::script::*; +use super::shells::SupportedShell; pub fn create_symlink( + current_shell: &SupportedShell, file_path: &str, alias: &Option, ) -> Result<(), Box> { @@ -13,10 +15,10 @@ pub fn create_symlink( let resolved_bin_path = resolve_single_path(file_path)?; // set exec mode - run_cmd(&format!( - "chmod +x {}", - &resolved_bin_path.display().to_string() - ))?; + run_cmd( + current_shell, + &format!("chmod +x {}", &resolved_bin_path.display().to_string()), + )?; println!( "ln -sf {} {}",