Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions crates/vfox/src/hooks/pre_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,38 @@ impl Plugin {

Ok(pre_install)
}

pub async fn pre_install_for_platform(
&self,
version: &str,
os: &str,
arch: &str,
) -> Result<PreInstall> {
debug!(
"[vfox:{}] pre_install_for_platform os={} arch={}",
&self.name, os, arch
);
let ctx = self.context(Some(version.to_string()))?;
let target_os = os.to_string();
let target_arch = arch.to_string();
let pre_install = self
.eval_async(chunk! {
require "hooks/pre_install"
-- Override globals with target platform for cross-platform URL generation
local saved_os = OS_TYPE
local saved_arch = ARCH_TYPE
OS_TYPE = $target_os
ARCH_TYPE = $target_arch
local result = PLUGIN:PreInstall($ctx)
-- Restore original values
OS_TYPE = saved_os
ARCH_TYPE = saved_arch
return result
Comment on lines +39 to +48
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global variable override pattern may cause issues in concurrent scenarios. If multiple calls to pre_install_for_platform() execute simultaneously, they could interfere with each other's OS_TYPE/ARCH_TYPE values. Consider using a more isolated approach, such as passing platform parameters directly to the plugin function if the vfox plugin API supports it, or ensuring this method is called under a lock.

Suggested change
-- Override globals with target platform for cross-platform URL generation
local saved_os = OS_TYPE
local saved_arch = ARCH_TYPE
OS_TYPE = $target_os
ARCH_TYPE = $target_arch
local result = PLUGIN:PreInstall($ctx)
-- Restore original values
OS_TYPE = saved_os
ARCH_TYPE = saved_arch
return result
-- Pass target_os and target_arch as arguments to PreInstall
return PLUGIN:PreInstall($ctx, $target_os, $target_arch)

Copilot uses AI. Check for mistakes.
})
.await?;

Ok(pre_install)
}
}

/// Optional attestation parameters provided by the return value of the preinstall hook.
Expand Down
11 changes: 11 additions & 0 deletions crates/vfox/src/vfox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ impl Vfox {
Ok(())
}

pub async fn pre_install_for_platform(
&self,
sdk: &str,
version: &str,
os: &str,
arch: &str,
) -> Result<PreInstall> {
let sdk = self.get_sdk(sdk)?;
sdk.pre_install_for_platform(version, os, arch).await
}

pub async fn metadata(&self, sdk: &str) -> Result<Metadata> {
self.get_sdk(sdk)?.get_metadata()
}
Expand Down
129 changes: 59 additions & 70 deletions src/backend/vfox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use tokio::sync::RwLock;

use crate::backend::Backend;
use crate::backend::backend_type::BackendType;
use crate::backend::platform_target::PlatformTarget;
use crate::cache::{CacheManager, CacheManagerBuilder};
use crate::cli::args::BackendArg;
use crate::config::{Config, Settings};
Expand Down Expand Up @@ -54,21 +55,14 @@ impl Backend for VfoxBackend {
this.ensure_plugin_installed(config).await?;

// Use backend methods if the plugin supports them
if matches!(&this.plugin_enum, PluginEnum::VfoxBackend(_)) {
if this.is_backend_plugin() {
Settings::get().ensure_experimental("custom backends")?;
debug!("Using backend method for plugin: {}", this.pathname);
let tool_name = this.tool_name.as_ref().ok_or_else(|| {
eyre::eyre!("VfoxBackend requires a tool name (plugin:tool format)")
})?;
match vfox.backend_list_versions(&this.pathname, tool_name).await {
Ok(versions) => {
return Ok(versions);
}
Err(e) => {
debug!("Backend method failed: {}", e);
return Err(e).wrap_err("Backend list versions method failed");
}
}
let tool_name = this.get_tool_name()?;
return vfox
.backend_list_versions(&this.pathname, tool_name)
.await
.wrap_err("Backend list versions method failed");
}

// Use default vfox behavior for traditional plugins
Expand Down Expand Up @@ -99,22 +93,13 @@ impl Backend for VfoxBackend {
});

// Use backend methods if the plugin supports them
if matches!(&self.plugin_enum, PluginEnum::VfoxBackend(_)) {
if self.is_backend_plugin() {
Settings::get().ensure_experimental("custom backends")?;
let tool_name = self.tool_name.as_ref().ok_or_else(|| {
eyre::eyre!("VfoxBackend requires a tool name (plugin:tool format)")
})?;
match vfox
.backend_install(&self.pathname, tool_name, &tv.version, tv.install_path())
let tool_name = self.get_tool_name()?;
vfox.backend_install(&self.pathname, tool_name, &tv.version, tv.install_path())
.await
{
Ok(_response) => {
return Ok(tv);
}
Err(e) => {
return Err(e).wrap_err("Backend install method failed");
}
}
.wrap_err("Backend install method failed")?;
return Ok(tv);
}

// Use default vfox behavior for traditional plugins
Expand Down Expand Up @@ -174,9 +159,45 @@ impl Backend for VfoxBackend {
)
})
}

async fn get_tarball_url(
&self,
tv: &ToolVersion,
target: &PlatformTarget,
) -> eyre::Result<Option<String>> {
let config = Config::get().await?;
self.ensure_plugin_installed(&config).await?;

// Map mise platform names to vfox platform names
let os = match target.os_name() {
"macos" => "darwin",
os => os,
};
let arch = match target.arch_name() {
"x64" => "amd64",
arch => arch,
};

let (vfox, _log_rx) = self.plugin.vfox();
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The variable _log_rx is prefixed with underscore indicating it's intentionally unused, but this pattern suggests the log receiver is being ignored. Consider either removing the underscore if logs should be handled, or add a comment explaining why logs are intentionally discarded in this context.

Copilot uses AI. Check for mistakes.
let pre_install = vfox
.pre_install_for_platform(&self.pathname, &tv.version, os, arch)
.await?;

Ok(pre_install.url)
}
}

impl VfoxBackend {
fn is_backend_plugin(&self) -> bool {
matches!(&self.plugin_enum, PluginEnum::VfoxBackend(_))
}

fn get_tool_name(&self) -> eyre::Result<&str> {
self.tool_name
.as_deref()
.ok_or_else(|| eyre::eyre!("VfoxBackend requires a tool name (plugin:tool format)"))
}

pub fn from_arg(ba: BackendArg, backend_plugin_name: Option<String>) -> Self {
let pathname = match &backend_plugin_name {
Some(plugin_name) => plugin_name.clone(),
Expand Down Expand Up @@ -233,63 +254,31 @@ impl VfoxBackend {
let (vfox, _log_rx) = self.plugin.vfox();

// Use backend methods if the plugin supports them
if matches!(&self.plugin_enum, PluginEnum::VfoxBackend(_)) {
let tool_name = self.tool_name.as_ref().ok_or_else(|| {
eyre::eyre!("VfoxBackend requires a tool name (plugin:tool format)")
})?;
match vfox
.backend_exec_env(&self.pathname, tool_name, &tv.version, tv.install_path())
let env_keys = if self.is_backend_plugin() {
let tool_name = self.get_tool_name()?;
vfox.backend_exec_env(&self.pathname, tool_name, &tv.version, tv.install_path())
.await
{
Ok(response) => {
return Ok(response.into_iter().fold(
BTreeMap::new(),
|mut acc, env_key| {
let key = &env_key.key;
if let Some(val) = acc.get(key) {
let mut paths =
env::split_paths(val).collect::<Vec<PathBuf>>();
paths.push(PathBuf::from(env_key.value));
acc.insert(
env_key.key,
env::join_paths(paths)
.unwrap()
.to_string_lossy()
.to_string(),
);
} else {
acc.insert(key.clone(), env_key.value);
}
acc
},
));
}
Err(e) => {
debug!("Backend method failed: {}", e);
return Err(e).wrap_err("Backend exec env method failed");
}
}
}
.wrap_err("Backend exec env method failed")?
} else {
vfox.env_keys(&self.pathname, &tv.version).await?
};

// Use default vfox behavior for traditional plugins
Ok(vfox
.env_keys(&self.pathname, &tv.version)
.await?
Ok(env_keys
.into_iter()
.fold(BTreeMap::new(), |mut acc, env_key| {
let key = &env_key.key;
if let Some(val) = acc.get(key) {
let mut paths = env::split_paths(val).collect::<Vec<PathBuf>>();
paths.push(PathBuf::from(env_key.value));
paths.push(PathBuf::from(&env_key.value));
acc.insert(
env_key.key,
env_key.key.clone(),
env::join_paths(paths)
.unwrap()
.to_string_lossy()
.to_string(),
);
} else {
acc.insert(key.clone(), env_key.value);
acc.insert(key.clone(), env_key.value.clone());
}
acc
}))
Expand Down
Loading