From 1cc45744f3d27b5134d0b93a586f3c06094e73fc Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 01:00:34 +0530 Subject: [PATCH 01/11] add changes to support --include-global --- crates/cli/src/commands/install_all.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/commands/install_all.rs b/crates/cli/src/commands/install_all.rs index 034115e7..2d064490 100644 --- a/crates/cli/src/commands/install_all.rs +++ b/crates/cli/src/commands/install_all.rs @@ -4,24 +4,37 @@ use crate::{ commands::clean::{internal_clean, CleanArgs}, commands::install::{internal_install, InstallArgs}, }; +use clap::Args; use miette::IntoDiagnostic; use starbase::AppResult; use starbase_styles::color; use std::process; use tracing::debug; +#[derive(Args, Clone, Debug)] +pub struct InstallAllArgs { + #[arg(long, help = "Include versions from global ~/.proto/.prototools")] + include_global: bool, +} + #[tracing::instrument(skip_all)] pub async fn install_all(session: ProtoSession) -> AppResult { debug!("Loading tools and plugins from .prototools"); let tools = session.load_tools().await?; + let mut versions = session.load_local_versions().await?; + + let config_manager = session.env.load_config_manager()?; debug!("Detecting tool versions to install"); - let config = session - .env - .load_config_manager()? - .get_merged_config_without_global()?; + let env = session.env(); + let config = if args.include_global { + env.load_config_manager()?.get_merged_config()? // Including global configurations + } else { + env.load_config_manager()? + .get_merged_config_without_global()? // Excluding global configurations + }; let mut versions = config.versions.to_owned(); for tool in &tools { From 4acf116a839abe2c4ffdff46a3b88b04f9f0e19a Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 01:19:51 +0530 Subject: [PATCH 02/11] add argument --- crates/cli/src/commands/install_all.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/cli/src/commands/install_all.rs b/crates/cli/src/commands/install_all.rs index 2d064490..b529f094 100644 --- a/crates/cli/src/commands/install_all.rs +++ b/crates/cli/src/commands/install_all.rs @@ -18,21 +18,19 @@ pub struct InstallAllArgs { } #[tracing::instrument(skip_all)] -pub async fn install_all(session: ProtoSession) -> AppResult { +pub async fn install_all(session: ProtoSession, args: InstallAllArgs) -> AppResult { debug!("Loading tools and plugins from .prototools"); let tools = session.load_tools().await?; - let mut versions = session.load_local_versions().await?; - - let config_manager = session.env.load_config_manager()?; debug!("Detecting tool versions to install"); - let env = session.env(); let config = if args.include_global { - env.load_config_manager()?.get_merged_config()? // Including global configurations + session.env.load_config_manager()?.get_merged_config()? // Including global configurations } else { - env.load_config_manager()? + session + .env + .load_config_manager()? .get_merged_config_without_global()? // Excluding global configurations }; let mut versions = config.versions.to_owned(); From 77065486ab2ce774e0b9d231c15d456b95c1159f Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 01:39:22 +0530 Subject: [PATCH 03/11] fix command --- crates/cli/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 148cde07..87f22d7e 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -101,7 +101,7 @@ async fn main() -> MainResult { Commands::Uninstall(args) => commands::uninstall(session, args).await, Commands::Unpin(args) => commands::unpin(session, args).await, Commands::Upgrade => commands::upgrade(session).await, - Commands::Use => commands::install_all(session).await, + Commands::Use => commands::install_all(session, args).await, } }) .await?; From eb4c84b2790b4ac263f66bfe3ac17d821193bfe7 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 09:59:06 +0530 Subject: [PATCH 04/11] restruct Use variant --- crates/cli/src/app.rs | 3 ++- crates/cli/src/commands/mod.rs | 2 +- crates/cli/src/main.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/cli/src/app.rs b/crates/cli/src/app.rs index 0d01fca8..bdfb1ea1 100644 --- a/crates/cli/src/app.rs +++ b/crates/cli/src/app.rs @@ -1,5 +1,6 @@ use crate::commands::{ debug::DebugConfigArgs, + install_all::InstallAllArgs, plugin::{AddPluginArgs, InfoPluginArgs, ListPluginsArgs, RemovePluginArgs, SearchPluginArgs}, AliasArgs, BinArgs, CleanArgs, CompletionsArgs, DiagnoseArgs, InstallArgs, ListArgs, ListRemoteArgs, MigrateArgs, OutdatedArgs, PinArgs, RegenArgs, RunArgs, SetupArgs, StatusArgs, @@ -258,7 +259,7 @@ pub enum Commands { name = "use", about = "Download and install all tools from loaded .prototools." )] - Use, + Use(InstallAllArgs), } #[derive(Clone, Debug, Subcommand)] diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index 6fbbcfa7..5d32b532 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -5,7 +5,7 @@ mod completions; pub mod debug; mod diagnose; mod install; -mod install_all; +pub mod install_all; mod list; mod list_remote; mod migrate; diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 87f22d7e..180fefaf 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -101,7 +101,7 @@ async fn main() -> MainResult { Commands::Uninstall(args) => commands::uninstall(session, args).await, Commands::Unpin(args) => commands::unpin(session, args).await, Commands::Upgrade => commands::upgrade(session).await, - Commands::Use => commands::install_all(session, args).await, + Commands::Use(args) => commands::install_all(session, args).await, } }) .await?; From 19a70b195824fcbf635d3606b62bef39dd26d2c6 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 10:28:54 +0530 Subject: [PATCH 05/11] add intital tests --- crates/cli/src/commands/install_all.rs | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/cli/src/commands/install_all.rs b/crates/cli/src/commands/install_all.rs index b529f094..5fcdf581 100644 --- a/crates/cli/src/commands/install_all.rs +++ b/crates/cli/src/commands/install_all.rs @@ -107,3 +107,39 @@ pub async fn install_all(session: ProtoSession, args: InstallAllArgs) -> AppResu Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::app::App; + use crate::session::ProtoSession; + use starbase::AppResult; + + #[tokio::test] + async fn test_install_all_with_global() -> AppResult { + let app = App::new(); + let session = ProtoSession::new(app); + let args = InstallAllArgs { + include_global: true, + }; + + let result = install_all(session, args).await; + assert!(result.is_ok()); + + Ok(()) + } + + #[tokio::test] + async fn test_install_all_without_global() -> AppResult { + let app = App::new(); + let session = ProtoSession::new(app); + let args = InstallAllArgs { + include_global: false, + }; + + let result = install_all(session, args).await; + assert!(result.is_ok()); + + Ok(()) + } +} From c4b84a0a849c13daa3e4fbc931e51a8fd2c96914 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Wed, 3 Jul 2024 11:18:50 +0530 Subject: [PATCH 06/11] add --include-global integratation tests --- crates/cli/src/commands/install_all.rs | 36 -------------------------- crates/cli/tests/use_test.rs | 19 ++++++++++++++ 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/crates/cli/src/commands/install_all.rs b/crates/cli/src/commands/install_all.rs index 5fcdf581..b529f094 100644 --- a/crates/cli/src/commands/install_all.rs +++ b/crates/cli/src/commands/install_all.rs @@ -107,39 +107,3 @@ pub async fn install_all(session: ProtoSession, args: InstallAllArgs) -> AppResu Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::app::App; - use crate::session::ProtoSession; - use starbase::AppResult; - - #[tokio::test] - async fn test_install_all_with_global() -> AppResult { - let app = App::new(); - let session = ProtoSession::new(app); - let args = InstallAllArgs { - include_global: true, - }; - - let result = install_all(session, args).await; - assert!(result.is_ok()); - - Ok(()) - } - - #[tokio::test] - async fn test_install_all_without_global() -> AppResult { - let app = App::new(); - let session = ProtoSession::new(app); - let args = InstallAllArgs { - include_global: false, - }; - - let result = install_all(session, args).await; - assert!(result.is_ok()); - - Ok(()) - } -} diff --git a/crates/cli/tests/use_test.rs b/crates/cli/tests/use_test.rs index a3317e06..77ab831f 100644 --- a/crates/cli/tests/use_test.rs +++ b/crates/cli/tests/use_test.rs @@ -65,4 +65,23 @@ deno = "1.30.0" assert!(node_path.exists()); assert!(!deno_path.exists()); } + + #[test] + fn installs_global_tools_when_included() { + let sandbox = create_empty_sandbox(); + let node_path = sandbox.path().join(".proto/tools/node/19.0.0"); + let deno_path = sandbox.path().join(".proto/tools/deno/1.30.0"); + + sandbox.create_file(".prototools", r#"node = "19.0.0""#); + sandbox.create_file(".proto/.prototools", r#"deno = "1.30.0""#); + + assert!(!node_path.exists()); + assert!(!deno_path.exists()); + + let mut cmd = create_proto_command(sandbox.path()); + cmd.arg("use").arg("--include-global").assert().success(); + + assert!(node_path.exists()); + assert!(deno_path.exists()); + } } From e2836dfe3db60eaf17f5db9b613ad822e40a2b9c Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Tue, 9 Jul 2024 17:26:31 +0530 Subject: [PATCH 07/11] add initial configuration of wasm plugin to supportdynamic asdf plugin --- asdf-plugin/Cargo.toml | 25 ++++ asdf-plugin/src/asdf_plugin.rs | 241 +++++++++++++++++++++++++++++++++ asdf-plugin/src/lib.rs | 1 + 3 files changed, 267 insertions(+) create mode 100644 asdf-plugin/Cargo.toml create mode 100644 asdf-plugin/src/asdf_plugin.rs create mode 100644 asdf-plugin/src/lib.rs diff --git a/asdf-plugin/Cargo.toml b/asdf-plugin/Cargo.toml new file mode 100644 index 00000000..f7f66ba7 --- /dev/null +++ b/asdf-plugin/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "asdf-plugin" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ['cdylib'] + +[profile.release] +codegen-units = 1 +debug = false +lto = true +opt-level = "s" +panic = "abort" + +[dependencies] +proto_pdk = { path = "../../crates/pdk" } +extism-pdk = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } + +[dev-dependencies] +starbase_sandbox = { workspace = true } +tokio = { workspace = true } \ No newline at end of file diff --git a/asdf-plugin/src/asdf_plugin.rs b/asdf-plugin/src/asdf_plugin.rs new file mode 100644 index 00000000..0860a7ba --- /dev/null +++ b/asdf-plugin/src/asdf_plugin.rs @@ -0,0 +1,241 @@ +use extism_pdk::*; +use proto_core::Tool; +use proto_pdk::*; +use serde::Deserialize; +use std::fs; +use std::path::Path; + +#[derive(Debug, Default, Deserialize)] +#[serde(default, deny_unknown_fields, rename_all = "kebab-case")] +pub struct AsdfConfig { + pub asdf_plugin: Option, + pub asdf_repository: Option, +} + +pub struct AsdfPlugin { + pub tool: Tool, +} + +impl AsdfPlugin { + pub fn detect_version_files(&self) -> DetectVersionOutput { + DetectVersionOutput { + files: vec![".tool-versions".into()], + ignore: vec![], + } + } + + pub fn parse_version_file(&self, input: ParseVersionFileInput) -> ParseVersionFileOutput { + let mut version = None; + if input.file == ".tool-versions" { + for line in input.content.lines() { + if let Some((tool, version_str)) = line.split_once(' ') { + if tool == self.tool.name() { + version = Some(UnresolvedVersionSpec::parse(version_str.trim()).unwrap()); + break; + } + } + } + } + ParseVersionFileOutput { version } + } + + pub fn download_prebuilt(&self, input: DownloadPrebuiltInput) -> DownloadPrebuiltOutput { + let env = get_host_environment().unwrap(); + + check_supported_os_and_arch( + "ASDF Plugin", + &env, + permutations![ + HostOS::Linux => [HostArch::X64, HostArch::Arm64, HostArch::Arm, HostArch::Powerpc64, HostArch::S390x], + HostOS::MacOS => [HostArch::X64, HostArch::Arm64], + // Aren't supported for windows as asdf-plugin is unix-centric based + // HostOS::Windows => [HostArch::X64, HostArch::X86, HostArch::Arm64], + ], + ).unwrap(); + + let version = input.context.version; + let arch = env.arch; + let os = env.os; + + let prefix = match os { + HostOS::Linux => format!("asdf-plugin-v{version}-linux-{arch}"), + HostOS::MacOS => format!("asdf-plugin-v{version}-darwin-{arch}"), + HostOS::Windows => format!("asdf-plugin-v{version}-win-{arch}"), + other => { + return DownloadPrebuiltOutput { + download_url: format!("Unsupported platform: {}", other), + ..DownloadPrebuiltOutput::default() + }; + } + }; + + // let filename = if os == HostOS::Windows { + // format!("{prefix}.zip") + // } else { + // format!("{prefix}.tar.xz") + // }; + + let config: AsdfConfig = self.tool.config(); + let asdf_plugin = config + .asdf_plugin + .unwrap_or_else(|| self.tool.name().to_string()); + let repository = config + .asdf_repository + .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", asdf_plugin)); + + DownloadPrebuiltOutput { + archive_prefix: Some(prefix), + download_url: format!("{repository}/releases/download/v{version}/{filename}"), + download_name: Some(filename), + checksum_url: Some(format!( + "{repository}/releases/download/v{version}/SHA256SUMS" + )), + checksum_public_key: Some("public-key-string".into()), // Need to adjust if applicable + ..DownloadPrebuiltOutput::default() + } + } + + pub fn install_plugin(&self, repository: &str) { + let asdf_dir = match env::var("ASDF_DATA_DIR") { + Ok(val) => Path::new(&val).to_path_buf(), + Err(_) => dirs::home_dir().unwrap().join(".asdf"), + }; + + let plugin_dir = asdf_dir.join("plugins").join(self.tool.name()); + + if !plugin_dir.exists() { + std::fs::create_dir_all(&plugin_dir).unwrap(); + } + + std::process::Command::new("git") + .arg("clone") + .arg(repository) + .arg(&plugin_dir) + .output() + .expect("Failed to clone asdf plugin"); + } + + pub fn pre_install(&self, mut input: InstallHook) { + let config: AsdfConfig = self.tool.config(); + let repository = config + .asdf_repository + .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", self.tool.name())); + self.install_plugin(&repository); + input.context = self.prepare_context(input.context); + self.tool + .plugin + .call_func_without_output("pre_install", input) + .unwrap(); + } + + fn prepare_context(&self, context: ToolContext) -> ToolContext { + let dir = if context.tool_dir.any_path().components().count() == 0 { + self.tool.get_product_dir() + } else { + context.tool_dir.any_path().to_path_buf() + }; + ToolContext { + tool_dir: self.tool.to_virtual_path(&dir), + ..context + } + } +} + +// register_tool: Registers the plugin and provides metadata. +#[plugin_fn] +pub fn register_tool(Json(input): Json) -> FnResult> { + Ok(Json(ToolMetadataOutput { + name: "ASDF Plugin".into(), + type_of: PluginType::Language, + plugin_version: Some(env!("CARGO_PKG_VERSION").into()), + ..ToolMetadataOutput::default() + })) +} + +// download_prebuilt: Handles downloading the pre-built tool, with URL construction based on OS and architecture. +#[plugin_fn] +pub fn download_prebuilt( + Json(input): Json, +) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.download_prebuilt(input))) +} + +// unpack_archive: Unpacks downloaded archives based on their file extension. +#[plugin_fn] +pub fn unpack_archive(Json(input): Json) -> FnResult<()> { + let input_file = input.input_file; + let output_dir = input.output_dir; + + // Need to ensure file type and unpack accordingly + if input_file.ends_with(".tar.xz") { + untar(input_file, output_dir)?; + } else if input_file.ends_with(".zip") { + unzip(input_file, output_dir)?; + } else { + return Err( + PluginError::Other(format!("Unsupported archive format: {}", input_file)).into(), + ); + } + + Ok(()) +} + +// detect_version_files: Specifies which files to check for version information. +#[plugin_fn] +pub fn detect_version_files(_: ()) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.detect_version_files())) +} + +// parse_version_file: Parses version information from specified files +#[plugin_fn] +pub fn parse_version_file( + Json(input): Json, +) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.parse_version_file(input))) +} + +// locate_executables: Locates the installed tool's executable files. +#[plugin_fn] +pub fn locate_executables( + Json(_): Json, +) -> FnResult> { + let env = get_host_environment()?; + + Ok(Json(LocateExecutablesOutput { + primary: Some(ExecutableConfig::new( + env.os.for_native("bin/node", "node.exe"), + )), + globals_lookup_dirs: vec!["$DENO_INSTALL_ROOT/bin".into(), "$HOME/.deno/bin".into()], + ..LocateExecutablesOutput::default() + })) +} + +// #[test] +// fn registers_metadata() { +// let sandbox = create_empty_sandbox(); +// let plugin = create_plugin("asdf_tool", sandbox.path()); +// assert_eq!( +// plugin.register_tool(ToolMetadataInput::default()), +// ToolMetadataOutput { +// name: "Asdf Tool".into(), +// ..ToolMetadataOutput::default() +// } +// ); +// } + +// generate_download_install_tests!("asdf_tool", "1.2.3"); + +// generate_resolve_versions_tests!("asdf_tool", { +// "0.1" => "0.1.5", +// "1.0" => "1.0.1", +// "1.2.3" => "1.2.3", +// }); diff --git a/asdf-plugin/src/lib.rs b/asdf-plugin/src/lib.rs new file mode 100644 index 00000000..ca718018 --- /dev/null +++ b/asdf-plugin/src/lib.rs @@ -0,0 +1 @@ +// mod asdf_plugin; From 2cd736ca99e129c9cb5d24ce92d93ff1c312436f Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Tue, 9 Jul 2024 17:27:33 +0530 Subject: [PATCH 08/11] format --- asdf-plugin/src/asdf_plugin.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/asdf-plugin/src/asdf_plugin.rs b/asdf-plugin/src/asdf_plugin.rs index 0860a7ba..2d706b2d 100644 --- a/asdf-plugin/src/asdf_plugin.rs +++ b/asdf-plugin/src/asdf_plugin.rs @@ -218,24 +218,3 @@ pub fn locate_executables( ..LocateExecutablesOutput::default() })) } - -// #[test] -// fn registers_metadata() { -// let sandbox = create_empty_sandbox(); -// let plugin = create_plugin("asdf_tool", sandbox.path()); -// assert_eq!( -// plugin.register_tool(ToolMetadataInput::default()), -// ToolMetadataOutput { -// name: "Asdf Tool".into(), -// ..ToolMetadataOutput::default() -// } -// ); -// } - -// generate_download_install_tests!("asdf_tool", "1.2.3"); - -// generate_resolve_versions_tests!("asdf_tool", { -// "0.1" => "0.1.5", -// "1.0" => "1.0.1", -// "1.2.3" => "1.2.3", -// }); From 7c1923e4f85a78087755d9460968e225a3906db3 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Tue, 9 Jul 2024 17:47:34 +0530 Subject: [PATCH 09/11] move asdf-plugin to crates --- asdf-plugin/src/lib.rs | 1 - {asdf-plugin => crates/asdf-plugin}/Cargo.toml | 0 {asdf-plugin => crates/asdf-plugin}/src/asdf_plugin.rs | 0 crates/asdf-plugin/src/lib.rs | 1 + 4 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 asdf-plugin/src/lib.rs rename {asdf-plugin => crates/asdf-plugin}/Cargo.toml (100%) rename {asdf-plugin => crates/asdf-plugin}/src/asdf_plugin.rs (100%) create mode 100644 crates/asdf-plugin/src/lib.rs diff --git a/asdf-plugin/src/lib.rs b/asdf-plugin/src/lib.rs deleted file mode 100644 index ca718018..00000000 --- a/asdf-plugin/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// mod asdf_plugin; diff --git a/asdf-plugin/Cargo.toml b/crates/asdf-plugin/Cargo.toml similarity index 100% rename from asdf-plugin/Cargo.toml rename to crates/asdf-plugin/Cargo.toml diff --git a/asdf-plugin/src/asdf_plugin.rs b/crates/asdf-plugin/src/asdf_plugin.rs similarity index 100% rename from asdf-plugin/src/asdf_plugin.rs rename to crates/asdf-plugin/src/asdf_plugin.rs diff --git a/crates/asdf-plugin/src/lib.rs b/crates/asdf-plugin/src/lib.rs new file mode 100644 index 00000000..0450b1f7 --- /dev/null +++ b/crates/asdf-plugin/src/lib.rs @@ -0,0 +1 @@ +mod asdf_plugin; From 1c4ac9f6d3d02d765a63d7c349b7404426c90927 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Tue, 9 Jul 2024 18:47:14 +0530 Subject: [PATCH 10/11] refactor --- Cargo.lock | 15 ++ crates/asdf-plugin/Cargo.toml | 12 +- crates/asdf-plugin/src/asdf_plugin.rs | 220 ------------------------- crates/asdf-plugin/src/lib.rs | 222 +++++++++++++++++++++++++- crates/pdk-api/src/error.rs | 3 + 5 files changed, 243 insertions(+), 229 deletions(-) delete mode 100644 crates/asdf-plugin/src/asdf_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index 3f8baf2b..e54a6bae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,21 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "asdf-plugin" +version = "0.1.0" +dependencies = [ + "dirs 5.0.1", + "extism-pdk", + "proto_core", + "proto_pdk", + "proto_pdk_test_utils", + "serde", + "serde_json", + "starbase_sandbox", + "tokio", +] + [[package]] name = "assert_cmd" version = "2.0.14" diff --git a/crates/asdf-plugin/Cargo.toml b/crates/asdf-plugin/Cargo.toml index f7f66ba7..6d72d04d 100644 --- a/crates/asdf-plugin/Cargo.toml +++ b/crates/asdf-plugin/Cargo.toml @@ -7,19 +7,15 @@ publish = false [lib] crate-type = ['cdylib'] -[profile.release] -codegen-units = 1 -debug = false -lto = true -opt-level = "s" -panic = "abort" - [dependencies] proto_pdk = { path = "../../crates/pdk" } +proto_core = { version = "0.38.2", path = "../core" } extism-pdk = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +dirs = "5.0.1" [dev-dependencies] -starbase_sandbox = { workspace = true } +proto_pdk_test_utils = { path = "../../crates/pdk-test-utils" } +starbase_sandbox = "*" tokio = { workspace = true } \ No newline at end of file diff --git a/crates/asdf-plugin/src/asdf_plugin.rs b/crates/asdf-plugin/src/asdf_plugin.rs deleted file mode 100644 index 2d706b2d..00000000 --- a/crates/asdf-plugin/src/asdf_plugin.rs +++ /dev/null @@ -1,220 +0,0 @@ -use extism_pdk::*; -use proto_core::Tool; -use proto_pdk::*; -use serde::Deserialize; -use std::fs; -use std::path::Path; - -#[derive(Debug, Default, Deserialize)] -#[serde(default, deny_unknown_fields, rename_all = "kebab-case")] -pub struct AsdfConfig { - pub asdf_plugin: Option, - pub asdf_repository: Option, -} - -pub struct AsdfPlugin { - pub tool: Tool, -} - -impl AsdfPlugin { - pub fn detect_version_files(&self) -> DetectVersionOutput { - DetectVersionOutput { - files: vec![".tool-versions".into()], - ignore: vec![], - } - } - - pub fn parse_version_file(&self, input: ParseVersionFileInput) -> ParseVersionFileOutput { - let mut version = None; - if input.file == ".tool-versions" { - for line in input.content.lines() { - if let Some((tool, version_str)) = line.split_once(' ') { - if tool == self.tool.name() { - version = Some(UnresolvedVersionSpec::parse(version_str.trim()).unwrap()); - break; - } - } - } - } - ParseVersionFileOutput { version } - } - - pub fn download_prebuilt(&self, input: DownloadPrebuiltInput) -> DownloadPrebuiltOutput { - let env = get_host_environment().unwrap(); - - check_supported_os_and_arch( - "ASDF Plugin", - &env, - permutations![ - HostOS::Linux => [HostArch::X64, HostArch::Arm64, HostArch::Arm, HostArch::Powerpc64, HostArch::S390x], - HostOS::MacOS => [HostArch::X64, HostArch::Arm64], - // Aren't supported for windows as asdf-plugin is unix-centric based - // HostOS::Windows => [HostArch::X64, HostArch::X86, HostArch::Arm64], - ], - ).unwrap(); - - let version = input.context.version; - let arch = env.arch; - let os = env.os; - - let prefix = match os { - HostOS::Linux => format!("asdf-plugin-v{version}-linux-{arch}"), - HostOS::MacOS => format!("asdf-plugin-v{version}-darwin-{arch}"), - HostOS::Windows => format!("asdf-plugin-v{version}-win-{arch}"), - other => { - return DownloadPrebuiltOutput { - download_url: format!("Unsupported platform: {}", other), - ..DownloadPrebuiltOutput::default() - }; - } - }; - - // let filename = if os == HostOS::Windows { - // format!("{prefix}.zip") - // } else { - // format!("{prefix}.tar.xz") - // }; - - let config: AsdfConfig = self.tool.config(); - let asdf_plugin = config - .asdf_plugin - .unwrap_or_else(|| self.tool.name().to_string()); - let repository = config - .asdf_repository - .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", asdf_plugin)); - - DownloadPrebuiltOutput { - archive_prefix: Some(prefix), - download_url: format!("{repository}/releases/download/v{version}/{filename}"), - download_name: Some(filename), - checksum_url: Some(format!( - "{repository}/releases/download/v{version}/SHA256SUMS" - )), - checksum_public_key: Some("public-key-string".into()), // Need to adjust if applicable - ..DownloadPrebuiltOutput::default() - } - } - - pub fn install_plugin(&self, repository: &str) { - let asdf_dir = match env::var("ASDF_DATA_DIR") { - Ok(val) => Path::new(&val).to_path_buf(), - Err(_) => dirs::home_dir().unwrap().join(".asdf"), - }; - - let plugin_dir = asdf_dir.join("plugins").join(self.tool.name()); - - if !plugin_dir.exists() { - std::fs::create_dir_all(&plugin_dir).unwrap(); - } - - std::process::Command::new("git") - .arg("clone") - .arg(repository) - .arg(&plugin_dir) - .output() - .expect("Failed to clone asdf plugin"); - } - - pub fn pre_install(&self, mut input: InstallHook) { - let config: AsdfConfig = self.tool.config(); - let repository = config - .asdf_repository - .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", self.tool.name())); - self.install_plugin(&repository); - input.context = self.prepare_context(input.context); - self.tool - .plugin - .call_func_without_output("pre_install", input) - .unwrap(); - } - - fn prepare_context(&self, context: ToolContext) -> ToolContext { - let dir = if context.tool_dir.any_path().components().count() == 0 { - self.tool.get_product_dir() - } else { - context.tool_dir.any_path().to_path_buf() - }; - ToolContext { - tool_dir: self.tool.to_virtual_path(&dir), - ..context - } - } -} - -// register_tool: Registers the plugin and provides metadata. -#[plugin_fn] -pub fn register_tool(Json(input): Json) -> FnResult> { - Ok(Json(ToolMetadataOutput { - name: "ASDF Plugin".into(), - type_of: PluginType::Language, - plugin_version: Some(env!("CARGO_PKG_VERSION").into()), - ..ToolMetadataOutput::default() - })) -} - -// download_prebuilt: Handles downloading the pre-built tool, with URL construction based on OS and architecture. -#[plugin_fn] -pub fn download_prebuilt( - Json(input): Json, -) -> FnResult> { - let asdf_plugin = AsdfPlugin { - tool: Tool::default(), - }; - Ok(Json(asdf_plugin.download_prebuilt(input))) -} - -// unpack_archive: Unpacks downloaded archives based on their file extension. -#[plugin_fn] -pub fn unpack_archive(Json(input): Json) -> FnResult<()> { - let input_file = input.input_file; - let output_dir = input.output_dir; - - // Need to ensure file type and unpack accordingly - if input_file.ends_with(".tar.xz") { - untar(input_file, output_dir)?; - } else if input_file.ends_with(".zip") { - unzip(input_file, output_dir)?; - } else { - return Err( - PluginError::Other(format!("Unsupported archive format: {}", input_file)).into(), - ); - } - - Ok(()) -} - -// detect_version_files: Specifies which files to check for version information. -#[plugin_fn] -pub fn detect_version_files(_: ()) -> FnResult> { - let asdf_plugin = AsdfPlugin { - tool: Tool::default(), - }; - Ok(Json(asdf_plugin.detect_version_files())) -} - -// parse_version_file: Parses version information from specified files -#[plugin_fn] -pub fn parse_version_file( - Json(input): Json, -) -> FnResult> { - let asdf_plugin = AsdfPlugin { - tool: Tool::default(), - }; - Ok(Json(asdf_plugin.parse_version_file(input))) -} - -// locate_executables: Locates the installed tool's executable files. -#[plugin_fn] -pub fn locate_executables( - Json(_): Json, -) -> FnResult> { - let env = get_host_environment()?; - - Ok(Json(LocateExecutablesOutput { - primary: Some(ExecutableConfig::new( - env.os.for_native("bin/node", "node.exe"), - )), - globals_lookup_dirs: vec!["$DENO_INSTALL_ROOT/bin".into(), "$HOME/.deno/bin".into()], - ..LocateExecutablesOutput::default() - })) -} diff --git a/crates/asdf-plugin/src/lib.rs b/crates/asdf-plugin/src/lib.rs index 0450b1f7..95f658e6 100644 --- a/crates/asdf-plugin/src/lib.rs +++ b/crates/asdf-plugin/src/lib.rs @@ -1 +1,221 @@ -mod asdf_plugin; +use extism_pdk::*; +use proto_core::Tool; +use proto_pdk::*; +use serde::Deserialize; +use std::env; +use std::path::Path; +use dirs; + +#[derive(Debug, Default, Deserialize)] +#[serde(default, deny_unknown_fields, rename_all = "kebab-case")] +pub struct AsdfConfig { + pub asdf_plugin: Option, + pub asdf_repository: Option, +} + +pub struct AsdfPlugin { + pub tool: Tool, +} + +impl AsdfPlugin { + pub fn detect_version_files(&self) -> DetectVersionOutput { + DetectVersionOutput { + files: vec![".tool-versions".into()], + ignore: vec![], + } + } + + pub fn parse_version_file(&self, input: ParseVersionFileInput) -> ParseVersionFileOutput { + let mut version = None; + if input.file == ".tool-versions" { + for line in input.content.lines() { + if let Some((tool, version_str)) = line.split_once(' ') { + if tool == self.tool.get_name() { + version = Some(UnresolvedVersionSpec::parse(version_str.trim()).unwrap()); + break; + } + } + } + } + ParseVersionFileOutput { version } + } + + pub fn download_prebuilt(&self, input: DownloadPrebuiltInput) -> DownloadPrebuiltOutput { + let env = get_host_environment().unwrap(); + + check_supported_os_and_arch( + "ASDF Plugin", + &env, + permutations![ + HostOS::Linux => [HostArch::X64, HostArch::Arm64, HostArch::Arm, HostArch::Powerpc64, HostArch::S390x], + HostOS::MacOS => [HostArch::X64, HostArch::Arm64], + HostOS::Windows => [HostArch::X64, HostArch::X86, HostArch::Arm64], + ], + ).unwrap(); + + let version = input.context.version; + let arch = env.arch; + let os = env.os; + + let prefix = match os { + HostOS::Linux => format!("asdf-plugin-v{version}-linux-{arch}"), + HostOS::MacOS => format!("asdf-plugin-v{version}-darwin-{arch}"), + HostOS::Windows => format!("asdf-plugin-v{version}-win-{arch}"), + other => { + return DownloadPrebuiltOutput { + download_url: format!("Unsupported platform: {}", other), + ..DownloadPrebuiltOutput::default() + }; + } + }; + + let filename = if os == HostOS::Windows { + format!("{prefix}.zip") + } else { + format!("{prefix}.tar.xz") + }; + + let config: AsdfConfig = self.tool.config(); + let asdf_plugin = config + .asdf_plugin + .unwrap_or_else(|| self.tool.get_name().to_string()); + let repository = config + .asdf_repository + .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", asdf_plugin)); + + DownloadPrebuiltOutput { + archive_prefix: Some(prefix), + download_url: format!("{repository}/releases/download/v{version}/{filename}"), + download_name: Some(filename), + checksum_url: Some(format!( + "{repository}/releases/download/v{version}/SHA256SUMS" + )), + checksum_public_key: Some("public-key-string".into()), // Need to adjust if applicable + ..DownloadPrebuiltOutput::default() + } + } + + pub fn install_plugin(&self, repository: &str) { + let asdf_dir = match env::var("ASDF_DATA_DIR") { + Ok(val) => Path::new(&val).to_path_buf(), + Err(_) => dirs::home_dir().unwrap().join(".asdf"), + }; + + let plugin_dir = asdf_dir.join("plugins").join(self.tool.get_name()); + + if !plugin_dir.exists() { + std::fs::create_dir_all(&plugin_dir).unwrap(); + } + + std::process::Command::new("git") + .arg("clone") + .arg(repository) + .arg(&plugin_dir) + .output() + .expect("Failed to clone asdf plugin"); + } + + pub fn pre_install(&self, mut input: InstallHook) { + let config: AsdfConfig = self.tool.config(); + let repository = config + .asdf_repository + .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", self.tool.get_name())); + self.install_plugin(&repository); + input.context = self.prepare_context(input.context); + self.tool + .plugin + .call_func_without_output("pre_install", input) + .unwrap(); + } + + fn prepare_context(&self, context: ToolContext) -> ToolContext { + let dir = if context.tool_dir.any_path().components().count() == 0 { + self.tool.get_product_dir() + } else { + context.tool_dir.any_path().to_path_buf() + }; + ToolContext { + tool_dir: self.tool.to_virtual_path(&dir), + ..context + } + } +} + +// register_tool: Registers the plugin and provides metadata. +#[plugin_fn] +pub fn register_tool(Json(input): Json) -> FnResult> { + Ok(Json(ToolMetadataOutput { + name: "ASDF Plugin".into(), + type_of: PluginType::Language, + plugin_version: Some(env!("CARGO_PKG_VERSION").into()), + ..ToolMetadataOutput::default() + })) +} + +// download_prebuilt: Handles downloading the pre-built tool, with URL construction based on OS and architecture. +#[plugin_fn] +pub fn download_prebuilt( + Json(input): Json, +) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.download_prebuilt(input))) +} + +// unpack_archive: Unpacks downloaded archives based on their file extension. +#[plugin_fn] +pub fn unpack_archive(Json(input): Json) -> FnResult<()> { + let input_file = input.input_file; + let output_dir = input.output_dir; + + // Need to ensure file type and unpack accordingly + if input_file.ends_with(".tar.xz") { + //TODO: Implement the untar and unzip moments + // untar(input_file, output_dir)?; + } else if input_file.ends_with(".zip") { + // unzip(input_file, output_dir)?; + } else { + return Err( + PluginError::UnsupportedArchiveFormat(format!("Unsupported archive format: {}", input_file)).into(), + ); + } + + Ok(()) +} + +// detect_version_files: Specifies which files to check for version information. +#[plugin_fn] +pub fn detect_version_files(_: ()) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.detect_version_files())) +} + +// parse_version_file: Parses version information from specified files +#[plugin_fn] +pub fn parse_version_file( + Json(input): Json, +) -> FnResult> { + let asdf_plugin = AsdfPlugin { + tool: Tool::default(), + }; + Ok(Json(asdf_plugin.parse_version_file(input))) +} + +// locate_executables: Locates the installed tool's executable files. +#[plugin_fn] +pub fn locate_executables( + Json(_): Json, +) -> FnResult> { + let env = get_host_environment()?; + + Ok(Json(LocateExecutablesOutput { + primary: Some(ExecutableConfig::new( + env.os.for_native("bin/node", "node.exe"), + )), + globals_lookup_dirs: vec!["$DENO_INSTALL_ROOT/bin".into(), "$HOME/.deno/bin".into()], + ..LocateExecutablesOutput::default() + })) +} diff --git a/crates/pdk-api/src/error.rs b/crates/pdk-api/src/error.rs index 4e7f5146..a53a746b 100644 --- a/crates/pdk-api/src/error.rs +++ b/crates/pdk-api/src/error.rs @@ -21,4 +21,7 @@ pub enum PluginError { arch: String, os: String, }, + + #[error("Unsupported archive format: {0}")] + UnsupportedArchiveFormat(String), } From 27bfd7d29f6e9551d0d99d53e878fd49d1504157 Mon Sep 17 00:00:00 2001 From: M-Vamshi <21211a05f1@bvrit.ac.in> Date: Tue, 9 Jul 2024 19:06:26 +0530 Subject: [PATCH 11/11] fix format --- crates/asdf-plugin/src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/asdf-plugin/src/lib.rs b/crates/asdf-plugin/src/lib.rs index 95f658e6..c7cb8543 100644 --- a/crates/asdf-plugin/src/lib.rs +++ b/crates/asdf-plugin/src/lib.rs @@ -1,10 +1,10 @@ +use dirs; use extism_pdk::*; use proto_core::Tool; use proto_pdk::*; use serde::Deserialize; use std::env; use std::path::Path; -use dirs; #[derive(Debug, Default, Deserialize)] #[serde(default, deny_unknown_fields, rename_all = "kebab-case")] @@ -117,9 +117,12 @@ impl AsdfPlugin { pub fn pre_install(&self, mut input: InstallHook) { let config: AsdfConfig = self.tool.config(); - let repository = config - .asdf_repository - .unwrap_or_else(|| format!("https://github.com/asdf-vm/asdf-{}.git", self.tool.get_name())); + let repository = config.asdf_repository.unwrap_or_else(|| { + format!( + "https://github.com/asdf-vm/asdf-{}.git", + self.tool.get_name() + ) + }); self.install_plugin(&repository); input.context = self.prepare_context(input.context); self.tool @@ -176,9 +179,11 @@ pub fn unpack_archive(Json(input): Json) -> FnResult<()> { } else if input_file.ends_with(".zip") { // unzip(input_file, output_dir)?; } else { - return Err( - PluginError::UnsupportedArchiveFormat(format!("Unsupported archive format: {}", input_file)).into(), - ); + return Err(PluginError::UnsupportedArchiveFormat(format!( + "Unsupported archive format: {}", + input_file + )) + .into()); } Ok(())