use std::path::PathBuf;
use std::sync::Once;
use aube::embed::{AUBE, Host};
static MISE_HOST: Host = Host {
name: "mise",
display_name: "mise",
vendor: None,
version: env!("CARGO_PKG_VERSION"),
user_agent: concat!("mise/", env!("CARGO_PKG_VERSION")),
self_names: AUBE.self_names,
compatible_names: AUBE.compatible_names,
lockfile_basename: AUBE.lockfile_basename,
workspace_yaml: AUBE.workspace_yaml,
manifest_namespace: AUBE.manifest_namespace,
env_prefix: AUBE.env_prefix,
config_env_prefix: AUBE.config_env_prefix,
cache_namespace: AUBE.cache_namespace,
data_namespace: AUBE.data_namespace,
canonical_lockfile_always_wins: true,
runtime_switching: false,
self_engines_check: false,
self_update_enabled: false,
};
static INIT: Once = Once::new();
const NODE_GYP_BOOTSTRAP_CMD: &str = "__node-gyp-bootstrap";
enum EmbeddedCli {
NodeGypBootstrap,
AubeCli,
}
pub(crate) fn init() {
INIT.call_once(|| {
aube::embed::initialize(&MISE_HOST, vec![]);
});
}
pub(crate) fn try_run_embedded_cli(args: &[String]) -> Option<i32> {
match embedded_cli_command(args)? {
EmbeddedCli::NodeGypBootstrap => Some(run_node_gyp_bootstrap(args)),
EmbeddedCli::AubeCli => Some(run_aube_cli()),
}
}
fn run_aube_cli() -> i32 {
init();
aube::cli_main(&MISE_HOST)
}
fn run_node_gyp_bootstrap(args: &[String]) -> i32 {
let project_dir = args
.get(2)
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("."));
init();
let runtime = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
{
Ok(rt) => rt,
Err(err) => {
eprintln!("mise: failed to start runtime for node-gyp bootstrap: {err}");
return 1;
}
};
match runtime.block_on(aube::embed::bootstrap_node_gyp(&project_dir)) {
Ok(path) => {
println!("{}", path.display());
0
}
Err(err) => {
eprintln!("{err:?}");
1
}
}
}
fn embedded_cli_command(args: &[String]) -> Option<EmbeddedCli> {
match args.get(1).map(String::as_str)? {
NODE_GYP_BOOTSTRAP_CMD => Some(EmbeddedCli::NodeGypBootstrap),
aube::embed::CLI_TRAMPOLINE_ARG => Some(EmbeddedCli::AubeCli),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mise_host_keeps_aube_config_names() {
assert_eq!(MISE_HOST.cache_namespace, AUBE.cache_namespace);
assert_eq!(MISE_HOST.data_namespace, AUBE.data_namespace);
assert_eq!(MISE_HOST.manifest_namespace, AUBE.manifest_namespace);
assert_eq!(MISE_HOST.lockfile_basename, AUBE.lockfile_basename);
}
#[test]
fn mise_host_brands_as_mise() {
assert_eq!(MISE_HOST.display_name, "mise");
assert_eq!(MISE_HOST.vendor, None);
assert_eq!(MISE_HOST.version, env!("CARGO_PKG_VERSION"));
assert!(MISE_HOST.user_agent.starts_with("mise/"));
}
#[test]
fn mise_host_disables_aube_owned_behavior() {
assert!(!MISE_HOST.runtime_switching);
assert!(!MISE_HOST.self_engines_check);
assert!(!MISE_HOST.self_update_enabled);
}
#[test]
fn detects_aube_node_gyp_bootstrap_trampoline() {
assert!(matches!(
embedded_cli_command(&[
"mise".into(),
NODE_GYP_BOOTSTRAP_CMD.into(),
"/tmp/project".into(),
]),
Some(EmbeddedCli::NodeGypBootstrap)
));
assert!(embedded_cli_command(&["mise".into(), "install".into(), "node".into()]).is_none());
assert!(embedded_cli_command(&["mise".into()]).is_none());
}
#[test]
fn detects_aube_cli_trampoline() {
assert!(matches!(
embedded_cli_command(&[
"mise".into(),
aube::embed::CLI_TRAMPOLINE_ARG.into(),
"run".into(),
"verify-build".into(),
]),
Some(EmbeddedCli::AubeCli)
));
assert!(
embedded_cli_command(&[
"mise".into(),
"run".into(),
aube::embed::CLI_TRAMPOLINE_ARG.into(),
])
.is_none()
);
}
}