Skip to content

Commit

Permalink
fix: allow plugins in config to override core plugins
Browse files Browse the repository at this point in the history
Fixes #3063
  • Loading branch information
jdx committed Nov 17, 2024
1 parent 5a048db commit 8ca22f3
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 70 deletions.
11 changes: 6 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ erlang = ['23.3', '24.0']
# supports everything you can do with .tool-versions currently
node = ['16', 'prefix:20', 'ref:master', 'path:~/.nodes/14']

[plugins]
# specify a custom repo url
# note this will only be used if the plugin does not already exist
python = 'https://github.com/asdf-community/asdf-python'

[alias.node.versions] # project-local aliases
# use vfox:version-fox/vfox-nodejs when running `mise i node@backend`
backend = "vfox:version-fox/vfox-nodejs"
Expand All @@ -52,6 +47,12 @@ my_custom_node = '20'

[tasks.build]
run = 'echo "running build tasks"'

[plugins]
# DEPRECATED: use `alias.<PLUGIN>` instead
# specify a custom repo url
# note this will only be used if the plugin does not already exist
python = 'https://github.com/asdf-community/asdf-python'
```

`mise.toml` files are hierarchical. The configuration in a file in the current directory will
Expand Down
4 changes: 4 additions & 0 deletions e2e/backend/test_asdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

assert "mise x asdf:jdx/mise-tiny -- mise-tiny" "mise-tiny: v3.1.0"
assert "mise x asdf:https://github.com/jdx/mise-tiny -- mise-tiny" "mise-tiny: v3.1.0"
3 changes: 3 additions & 0 deletions e2e/plugins/test_plugin_install
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ mise plugin uninstall tiny
mise plugin update
mise plugin update shfmt
mise i

assert_contains "mise plugin install mise-x 2>&1 || true" "No repository found for plugin mise-x"
assert_contains "mise plugin add node 2>&1 || true" "node is a core plugin and does not need to be installed"
15 changes: 11 additions & 4 deletions e2e/tools/test_tools_alias
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
tools.node = "100.0.0"
tools.node = "1.0.0-node"
tools.erlang = "1.0.0-erlang"
tools.python = "1.0.0-python"
tools.mytool = "2"
tools.mytool-lts = "lts"
alias.node = "asdf:tiny"
alias.mytool = "asdf:tiny"
[alias]
erlang = 'asdf:https://github.com/jdx/mise-tiny'
python = 'asdf:jdx/mise-tiny'
node = "asdf:tiny"
mytool = "asdf:tiny"
[alias.mytool-lts]
backend = "asdf:tiny"
versions = {lts = "1.0.1"}
EOF

assert_contains "mise x node -- rtx-tiny" "rtx-tiny: v100.0.0"
assert "mise x node -- rtx-tiny" "rtx-tiny: v1.0.0-node args:"
assert "mise x python -- mise-tiny" "mise-tiny: v1.0.0-python"
assert "mise x erlang -- mise-tiny" "mise-tiny: v1.0.0-erlang"
assert_contains "mise x mytool -- rtx-tiny" "rtx-tiny: v2.1.0"
assert_contains "mise x mytool-lts -- rtx-tiny" "rtx-tiny: v1.0.1"

Expand Down
19 changes: 7 additions & 12 deletions src/backend/asdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ use std::fs;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};

use color_eyre::eyre::{eyre, Result, WrapErr};
use console::style;

use crate::backend::backend_type::BackendType;
use crate::backend::external_plugin_cache::ExternalPluginCache;
use crate::backend::Backend;
Expand All @@ -22,7 +19,10 @@ use crate::plugins::Script::{Download, ExecEnv, Install, ParseLegacyFile};
use crate::plugins::{Plugin, PluginType, Script, ScriptManager};
use crate::toolset::{ToolRequest, ToolVersion, Toolset};
use crate::ui::progress_report::SingleReport;
use crate::{env, file};
use crate::{dirs, env, file};
use color_eyre::eyre::{eyre, Result, WrapErr};
use console::style;
use heck::ToKebabCase;

/// This represents a plugin installed to ~/.local/share/mise/plugins
pub struct AsdfBackend {
Expand All @@ -42,7 +42,8 @@ pub struct AsdfBackend {
impl AsdfBackend {
pub fn from_arg(ba: BackendArg) -> Self {
let name = ba.tool_name.clone();
let plugin_path = ba.plugin_path.clone();
let plugin_path = dirs::PLUGINS.join(ba.short.to_kebab_case());
let plugin = AsdfPlugin::new(name.clone(), plugin_path.clone());
let mut toml_path = plugin_path.join("mise.plugin.toml");
if plugin_path.join("rtx.plugin.toml").exists() {
toml_path = plugin_path.join("rtx.plugin.toml");
Expand Down Expand Up @@ -75,13 +76,7 @@ impl AsdfBackend {
.with_fresh_file(plugin_path.join("bin/list-legacy-filenames"))
.build(),
plugin_path,
plugin: Box::new(AsdfPlugin::new(
ba.plugin_path
.file_name()
.unwrap()
.to_string_lossy()
.to_string(),
)),
plugin: Box::new(plugin),
repo_url: None,
toml,
name,
Expand Down
2 changes: 1 addition & 1 deletion src/backend/vfox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl VfoxBackend {
pub fn from_arg(ba: BackendArg) -> Self {
let pathname = ba.short.to_kebab_case();
let plugin_path = dirs::PLUGINS.join(&pathname);
let mut plugin = VfoxPlugin::new(pathname.clone());
let mut plugin = VfoxPlugin::new(pathname.clone(), plugin_path.clone());
plugin.full = Some(ba.full());
Self {
remote_version_cache: CacheManagerBuilder::new(
Expand Down
3 changes: 0 additions & 3 deletions src/cli/args/backend_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ pub struct BackendArg {
pub installs_path: PathBuf,
/// ~/.local/share/mise/downloads/<THIS>
pub downloads_path: PathBuf,
/// ~/.local/share/mise/plugins/<THIS>
pub plugin_path: PathBuf,
pub opts: Option<ToolVersionOptions>,
// TODO: make this not a hash key anymore to use this
// backend: OnceCell<ABackend>,
Expand Down Expand Up @@ -80,7 +78,6 @@ impl BackendArg {
tool_name,
short,
full,
plugin_path: dirs::PLUGINS.join(&pathname),
cache_path: dirs::CACHE.join(&pathname),
installs_path: dirs::INSTALLS.join(&pathname),
downloads_path: dirs::DOWNLOADS.join(&pathname),
Expand Down
26 changes: 4 additions & 22 deletions src/cli/plugins/install.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use color_eyre::eyre::{bail, eyre, Result};
use contracts::ensures;
use heck::ToKebabCase;
use rayon::prelude::*;
use rayon::ThreadPoolBuilder;
use url::Url;

use crate::backend::unalias_backend;
use crate::config::{Config, Settings};
use crate::dirs;
use crate::plugins::asdf_plugin::AsdfPlugin;
use crate::plugins::core::CORE_PLUGINS;
use crate::plugins::Plugin;
Expand Down Expand Up @@ -112,7 +114,8 @@ impl PluginsInstall {
git_url: Option<String>,
mpr: &MultiProgressReport,
) -> Result<()> {
let mut plugin = AsdfPlugin::new(name.clone());
let path = dirs::PLUGINS.join(name.to_kebab_case());
let mut plugin = AsdfPlugin::new(name.clone(), path);
plugin.repo_url = git_url;
if !self.force && plugin.is_installed() {
warn!("Plugin {name} already installed");
Expand Down Expand Up @@ -170,24 +173,3 @@ static AFTER_LONG_HELP: &str = color_print::cstr!(
$ <bold>mise plugins install node https://github.com/mise-plugins/rtx-nodejs.git#v1.0.0</bold>
"#
);

#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use test_log::test;

use crate::test::reset;
#[test]
fn test_plugin_install_invalid_url() {
reset();
let err = assert_cli_err!("plugin", "add", "tiny*");
assert_snapshot!(err, @"No repository found for plugin tiny*");
}

#[test]
fn test_plugin_install_core_plugin() {
reset();
let err = assert_cli_err!("plugin", "add", "node");
assert_snapshot!(err, @"node is a core plugin and does not need to be installed");
}
}
4 changes: 3 additions & 1 deletion src/config/env_directive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;

use eyre::{eyre, Context};
use heck::ToKebabCase;
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer};

Expand Down Expand Up @@ -302,7 +303,8 @@ impl EnvResults {
}
}
EnvDirective::Module(name, value) => {
let plugin = VfoxPlugin::new(name);
let path = dirs::PLUGINS.join(name.to_kebab_case());
let plugin = VfoxPlugin::new(name, path);
if let Some(env) = plugin.mise_env(&value)? {
for (k, v) in env {
r.env.insert(k, (v, source.clone()));
Expand Down
11 changes: 9 additions & 2 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,15 @@ impl Config {
.get(plugin_name)
.map(|full| registry::full_to_url(&full[0]))
.or_else(|| {
if plugin_name.starts_with("https://") || plugin_name.split('/').count() == 2 {
Some(registry::full_to_url(plugin_name))
if plugin_name.starts_with("https://")
|| plugin_name.starts_with("http://")
|| plugin_name.starts_with("git@")
|| plugin_name.starts_with("ssh://")
|| plugin_name.starts_with("git://")
{
Some(plugin_name.to_string())
} else if plugin_name.split('/').count() == 2 {
Some(format!("https://github.com/{}.git", plugin_name))
} else {
None
}
Expand Down
2 changes: 1 addition & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ mod tests {
fn test_dir_subdirs() {
reset();
let subdirs = dir_subdirs(&dirs::HOME).unwrap();
assert!(subdirs.contains(&"cwd".to_string()));
assert!(subdirs.contains("cwd"));
}

#[test]
Expand Down
27 changes: 13 additions & 14 deletions src/plugins/asdf_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,22 @@ use xx::regex;
#[derive(Debug)]
pub struct AsdfPlugin {
pub name: String,
pub plugin_path: PathBuf,
pub path: PathBuf,
pub repo: Mutex<Git>,
pub repo_url: Option<String>,
pub script_man: ScriptManager,
}

impl AsdfPlugin {
#[requires(!name.is_empty())]
pub fn new(name: String) -> Self {
let plugin_path = dirs::PLUGINS.join(&name);
let repo = Git::new(&plugin_path);
pub fn new(name: String, path: PathBuf) -> Self {
let repo = Git::new(&path);
Self {
script_man: build_script_man(&name, &plugin_path),
script_man: build_script_man(&name, &path),
name,
repo_url: None,
repo: Mutex::new(repo),
plugin_path,
path,
}
}

Expand Down Expand Up @@ -185,7 +184,7 @@ impl Plugin for AsdfPlugin {
}

fn path(&self) -> PathBuf {
self.plugin_path.clone()
self.path.clone()
}

fn get_plugin_type(&self) -> PluginType {
Expand Down Expand Up @@ -216,7 +215,7 @@ impl Plugin for AsdfPlugin {
}

fn is_installed(&self) -> bool {
self.plugin_path.exists()
self.path.exists()
}

fn is_installed_err(&self) -> eyre::Result<()> {
Expand Down Expand Up @@ -256,12 +255,12 @@ impl Plugin for AsdfPlugin {
}
let prefix = format!("plugin:{}", style(&self.name).blue().for_stderr());
let pr = mpr.add(&prefix);
let _lock = lock_file::get(&self.plugin_path, force)?;
let _lock = lock_file::get(&self.path, force)?;
self.install(pr.as_ref())
}

fn update(&self, pr: &dyn SingleReport, gitref: Option<String>) -> Result<()> {
let plugin_path = self.plugin_path.to_path_buf();
let plugin_path = self.path.to_path_buf();
if plugin_path.is_symlink() {
warn!(
"plugin:{} is a symlink, not updating",
Expand Down Expand Up @@ -309,7 +308,7 @@ impl Plugin for AsdfPlugin {
})
};

rmdir(&self.plugin_path)?;
rmdir(&self.path)?;

Ok(())
}
Expand All @@ -331,7 +330,7 @@ If you are trying to link to a local directory, use `mise plugins link` instead.
Plugins could support local directories in the future but for now a symlink is required which `mise plugins link` will create for you."#
))?;
}
let git = Git::new(&self.plugin_path);
let git = Git::new(&self.path);
pr.set_message(format!("cloning {repo_url}"));
git.clone(&repo_url)?;
if let Some(ref_) = &repo_ref {
Expand All @@ -349,7 +348,7 @@ Plugins could support local directories in the future but for now a symlink is r
}

fn external_commands(&self) -> eyre::Result<Vec<Command>> {
let command_path = self.plugin_path.join("lib/commands");
let command_path = self.path.join("lib/commands");
if !self.is_installed() || !command_path.exists() || self.name == "direnv" {
// asdf-direnv is disabled since it conflicts with mise's built-in direnv functionality
return Ok(vec![]);
Expand Down Expand Up @@ -394,7 +393,7 @@ Plugins could support local directories in the future but for now a symlink is r
return Err(PluginNotInstalled(self.name.clone()).into());
}
let script = Script::RunExternalCommand(
self.plugin_path
self.path
.join("lib/commands")
.join(format!("command-{command}.bash")),
args,
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/core/erlang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl ErlangPlugin {
self.install_kerl()?;
cmd!(self.kerl_path(), "update", "releases")
.env("KERL_BASE_DIR", self.kerl_base_dir())
.stdout_to_stderr()
.run()?;
Ok(())
}
Expand Down Expand Up @@ -104,6 +105,7 @@ impl Backend for ErlangPlugin {
ctx.tv.install_path()
)
.env("KERL_BASE_DIR", self.ba.cache_path.join("kerl"))
.stdout_to_stderr()
.run()?;
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/plugins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::dirs;
use crate::errors::Error::PluginNotInstalled;
use crate::plugins::asdf_plugin::AsdfPlugin;
use crate::plugins::vfox_plugin::VfoxPlugin;
Expand All @@ -6,6 +7,7 @@ use crate::ui::multi_progress_report::MultiProgressReport;
use crate::ui::progress_report::SingleReport;
use clap::Command;
use eyre::{eyre, Result};
use heck::ToKebabCase;
use once_cell::sync::Lazy;
use regex::Regex;
pub use script_manager::{Script, ScriptManager};
Expand Down Expand Up @@ -35,9 +37,10 @@ impl PluginType {
}

pub fn plugin(&self, short: String) -> APlugin {
let path = dirs::PLUGINS.join(short.to_kebab_case());
match self {
PluginType::Asdf => Box::new(AsdfPlugin::new(short)),
PluginType::Vfox => Box::new(VfoxPlugin::new(short)),
PluginType::Asdf => Box::new(AsdfPlugin::new(short, path)),
PluginType::Vfox => Box::new(VfoxPlugin::new(short, path)),
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/plugins/vfox_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ pub struct VfoxPlugin {

impl VfoxPlugin {
#[requires(!name.is_empty())]
pub fn new(name: String) -> Self {
let plugin_path = dirs::PLUGINS.join(&name);
pub fn new(name: String, plugin_path: PathBuf) -> Self {
let repo = Git::new(&plugin_path);
Self {
name,
Expand Down
7 changes: 6 additions & 1 deletion src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ fn normalize_remote(remote: &str) -> eyre::Result<String> {

pub fn full_to_url(full: &str) -> String {
let (_backend, url) = full.split_once(':').unwrap_or(("", full));
if url.starts_with("https://") {
if url.starts_with("https://")
|| url.starts_with("http://")
|| url.starts_with("git@")
|| url.starts_with("ssh://")
|| url.starts_with("git://")
{
url.to_string()
} else {
format!("https://github.com/{url}.git")
Expand Down

0 comments on commit 8ca22f3

Please sign in to comment.