From bd0e8ea3ec559175a4fffc0fed6f78ed2bc50cb8 Mon Sep 17 00:00:00 2001 From: Jeff Dickey <216188+jdx@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:11:45 -0600 Subject: [PATCH] erlang core plugin --- .github/workflows/test-plugins.yml | 4 + e2e/test_erlang | 8 ++ src/plugins/core/erlang.rs | 122 +++++++++++++++++++++++++++++ src/plugins/core/mod.rs | 9 ++- 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100755 e2e/test_erlang create mode 100644 src/plugins/core/erlang.rs diff --git a/.github/workflows/test-plugins.yml b/.github/workflows/test-plugins.yml index aa39c5efdd..e938fb8fee 100644 --- a/.github/workflows/test-plugins.yml +++ b/.github/workflows/test-plugins.yml @@ -55,6 +55,8 @@ jobs: command: rtx exec direnv@latest -- direnv --version - plugin: erlang command: rtx exec erlang@latest -- erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell + env: + RTX_EXPERIMENTAL: 1 - plugin: elixir command: | rtx use --global erlang@latest @@ -62,6 +64,8 @@ jobs: rtx use --global elixir@latest eval "$(rtx env bash)" rtx exec -- elixir --version + env: + RTX_EXPERIMENTAL: 1 - plugin: golang command: rtx exec golang@latest -- go version - plugin: java diff --git a/e2e/test_erlang b/e2e/test_erlang new file mode 100755 index 0000000000..345ef35abe --- /dev/null +++ b/e2e/test_erlang @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +# shellcheck source-path=SCRIPTDIR +source "$(dirname "$0")/assert.sh" + +export RTX_EXPERIMENTAL=1 + +assert_contains "rtx x erlang@24.3.4.9 -- erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell" "24" diff --git a/src/plugins/core/erlang.rs b/src/plugins/core/erlang.rs new file mode 100644 index 0000000000..caa0cee7c0 --- /dev/null +++ b/src/plugins/core/erlang.rs @@ -0,0 +1,122 @@ +use std::path::PathBuf; + +use color_eyre::eyre::Result; + +use crate::config::Settings; + +use crate::file::display_path; +use crate::install_context::InstallContext; +use crate::lock_file::LockFile; +use crate::plugins::core::CorePlugin; +use crate::plugins::{Plugin, HTTP}; +use crate::toolset::ToolVersionRequest; +use crate::{cmd, file}; + +#[derive(Debug)] +pub struct ErlangPlugin { + core: CorePlugin, +} + +const KERL_VERSION: &str = "4.0.0"; + +impl ErlangPlugin { + pub fn new() -> Self { + Self { + core: CorePlugin::new("erlang"), + } + } + + fn kerl_path(&self) -> PathBuf { + self.core.cache_path.join(format!("kerl-{}", KERL_VERSION)) + } + + fn lock_build_tool(&self) -> Result { + LockFile::new(&self.kerl_path()) + .with_callback(|l| { + trace!("install_or_update_kerl {}", l.display()); + }) + .lock() + } + + fn update_kerl(&self) -> Result<()> { + let _lock = self.lock_build_tool(); + if self.kerl_path().exists() { + return Ok(()); + } + self.install_kerl()?; + cmd!(self.kerl_path(), "update", "releases") + .env("KERL_BASE_DIR", self.core.cache_path.join("kerl")) + .run()?; + Ok(()) + } + + fn install_kerl(&self) -> Result<()> { + debug!("Installing kerl to {}", display_path(&self.kerl_path())); + HTTP.download_file( + format!("https://raw.githubusercontent.com/kerl/kerl/{KERL_VERSION}/kerl"), + &self.kerl_path(), + )?; + file::make_executable(&self.kerl_path())?; + Ok(()) + } + + fn fetch_remote_versions(&self) -> Result> { + match self.core.fetch_remote_versions_from_rtx() { + Ok(Some(versions)) => return Ok(versions), + Ok(None) => {} + Err(e) => warn!("failed to fetch remote versions: {}", e), + } + self.update_kerl()?; + let versions = CorePlugin::run_fetch_task_with_timeout(move || { + let output = cmd!(self.kerl_path(), "list", "releases", "all") + .env("KERL_BASE_DIR", self.core.cache_path.join("kerl")) + .read()?; + let versions = output + .split('\n') + .filter(|s| regex!(r"^[0-9].+$").is_match(s)) + .map(|s| s.to_string()) + .collect(); + Ok(versions) + })?; + Ok(versions) + } +} + +impl Plugin for ErlangPlugin { + fn name(&self) -> &str { + self.core.name + } + + fn list_remote_versions(&self, _settings: &Settings) -> Result> { + self.core + .remote_version_cache + .get_or_try_init(|| self.fetch_remote_versions()) + .cloned() + } + + fn install_version_impl(&self, ctx: &InstallContext) -> Result<()> { + self.update_kerl()?; + + file::remove_all(ctx.tv.install_path())?; + let build_name = format!("rtx_{}", ctx.tv.version); + match &ctx.tv.request { + ToolVersionRequest::Ref(..) => { + unimplemented!("erlang does not yet support refs"); + } + ToolVersionRequest::Version(..) => { + cmd!( + self.kerl_path(), + "build-install", + &ctx.tv.version, + &build_name, + ctx.tv.install_path() + ) + .env("KERL_BASE_DIR", self.core.cache_path.join("kerl")) + .run()?; + } + _ => unimplemented!(), + } + + Ok(()) + } +} diff --git a/src/plugins/core/mod.rs b/src/plugins/core/mod.rs index 8d7a79d4be..f0d890bc1f 100644 --- a/src/plugins/core/mod.rs +++ b/src/plugins/core/mod.rs @@ -14,6 +14,7 @@ use crate::cache::CacheManager; use crate::env::RTX_NODE_BUILD; use crate::plugins::core::bun::BunPlugin; use crate::plugins::core::deno::DenoPlugin; +use crate::plugins::core::erlang::ErlangPlugin; use crate::plugins::core::go::GoPlugin; use crate::plugins::core::java::JavaPlugin; use crate::plugins::core::node::NodePlugin; @@ -26,6 +27,7 @@ use crate::{dirs, env}; mod bun; mod deno; +mod erlang; mod go; mod java; mod node; @@ -54,8 +56,11 @@ pub static CORE_PLUGINS: Lazy = Lazy::new(|| { }); pub static EXPERIMENTAL_CORE_PLUGINS: Lazy = Lazy::new(|| { - let plugins: Vec> = - vec![Arc::new(BunPlugin::new()), Arc::new(DenoPlugin::new())]; + let plugins: Vec> = vec![ + Arc::new(BunPlugin::new()), + Arc::new(DenoPlugin::new()), + Arc::new(ErlangPlugin::new()), + ]; plugins .into_iter() .map(|plugin| (plugin.name().to_string(), plugin))