Skip to content

Commit

Permalink
new: Add config loading mode support. (#577)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj committed Aug 13, 2024
1 parent 53c324e commit c3444c6
Show file tree
Hide file tree
Showing 21 changed files with 191 additions and 95 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,23 @@

#### 💥 Breaking

- Removed `--include-global` and `--only-local` flags from all applicable commands. Use the new `--config-mode` instead.
- WASM API
- Removed the `is_musl` function. Use the host environment instead.
- Deprecated many functions.

#### 🚀 Updates

- Added a new `--config-mode` global option that controls how configuration are loaded.
- Supports the following values:
- `global` - Only load `~/.proto/.prototools`.
- `local` - Only load `./.prototools` in the current directory.
- `upwards` (default) - Load `.prototools` while traversing upwards, but do not load `~/.proto/.prototools`.
- `upwards-global` - Load `.prototools` while traversing upwards, and do load `~/.proto/.prototools`.
- When not provided, the default mode is dependent on the command being ran.
- For `activate`, `install`, `outdated`, `status` -> `upwards`
- Everything else -> `upwards-global`
- Updated HTTP requests that occur from WASM to utilize the same HTTP client that proto does. This allows for the `[settings.http]` settings to be used, which weren't previously.
- WASM API
- Added `ToolMetadataOutput.config_schema`, which can be used to define a JSON schema for the plugins configuration.
- Added a new `send_request` host function, that uses the same HTTP client as proto does.
Expand Down
10 changes: 10 additions & 0 deletions crates/cli/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::commands::{
};
use clap::builder::styling::{Color, Style, Styles};
use clap::{Parser, Subcommand, ValueEnum};
use proto_core::ConfigMode;
use starbase_styles::color::Color as ColorType;
use std::{
env,
Expand Down Expand Up @@ -70,6 +71,15 @@ fn create_styles() -> Styles {
styles = create_styles()
)]
pub struct App {
#[arg(
long,
short = 'c',
global = true,
env = "PROTO_CONFIG_MODE",
help = "Mode in which to load configuration"
)]
pub config_mode: Option<ConfigMode>,

#[arg(
long,
global = true,
Expand Down
36 changes: 19 additions & 17 deletions crates/cli/src/commands/activate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::session::ProtoSession;
use clap::Args;
use indexmap::IndexMap;
use miette::IntoDiagnostic;
use proto_core::{detect_version, Id, UnresolvedVersionSpec};
use proto_core::{detect_version, ConfigMode, Id, UnresolvedVersionSpec};
use serde::Serialize;
use starbase::AppResult;
use starbase_shell::{Hook, ShellType, Statement};
Expand Down Expand Up @@ -55,9 +55,6 @@ pub struct ActivateArgs {
)]
export: bool,

#[arg(long, help = "Include versions from global ~/.proto/.prototools")]
include_global: bool,

#[arg(long, help = "Print the activate instructions in JSON format")]
json: bool,

Expand All @@ -78,16 +75,11 @@ pub async fn activate(session: ProtoSession, args: ActivateArgs) -> AppResult {

// If not exporting data, just print the activation syntax immediately
if !args.export && !args.json {
return print_activation_hook(&shell_type);
return print_activation_hook(&shell_type, &args, session.cli.config_mode.as_ref());
}

// Pre-load configuration
let manager = session.env.load_config_manager()?;
let config = if args.include_global {
manager.get_merged_config()?
} else {
manager.get_merged_config_without_global()?
};
let config = session.env.load_config()?;

// Load necessary tools so that we can extract info
let tools = session
Expand Down Expand Up @@ -189,14 +181,24 @@ pub async fn activate(session: ProtoSession, args: ActivateArgs) -> AppResult {
Ok(())
}

fn print_activation_hook(shell_type: &ShellType) -> AppResult {
fn print_activation_hook(
shell_type: &ShellType,
args: &ActivateArgs,
config_mode: Option<&ConfigMode>,
) -> AppResult {
let mut command = format!("proto activate {}", shell_type);

for arg in env::args() {
if arg.starts_with("--") {
command.push(' ');
command.push_str(&arg);
}
if let Some(mode) = config_mode {
command.push_str(" --config-mode ");
command.push_str(&mode.to_string());
}

if args.no_bin {
command.push_str(" --no-bin");
}

if args.no_shim {
command.push_str(" --no-shim");
}

match shell_type {
Expand Down
5 changes: 3 additions & 2 deletions crates/cli/src/commands/debug/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ fn print_toml(value: impl Serialize) -> miette::Result<()> {

#[tracing::instrument(skip_all)]
pub async fn config(session: ProtoSession, args: DebugConfigArgs) -> AppResult {
let manager = session.env.load_config_manager()?;
let config = manager.get_merged_config()?;
let env = &session.env;
let manager = env.load_config_manager()?;
let config = env.load_config()?;

if args.json {
let result = DebugConfigResult {
Expand Down
22 changes: 3 additions & 19 deletions crates/cli/src/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub enum PinOption {

#[derive(Args, Clone, Debug, Default)]
pub struct InstallArgs {
// ONE
#[arg(help = "ID of a single tool to install")]
pub id: Option<Id>,

Expand All @@ -46,13 +45,6 @@ pub struct InstallArgs {
)]
pub pin: Option<Option<PinOption>>,

// ALL
#[arg(
long,
help = "When installing all, include versions configured in global ~/.proto/.prototools"
)]
pub include_global: bool,

// Passthrough args (after --)
#[arg(
last = true,
Expand Down Expand Up @@ -317,22 +309,14 @@ pub async fn install_one(
}

#[tracing::instrument(skip_all)]
pub async fn install_all(session: &ProtoSession, args: InstallArgs) -> AppResult {
pub async fn install_all(session: &ProtoSession) -> AppResult {
debug!("Loading all tools");

let tools = session.load_tools().await?;

debug!("Detecting tool versions to install");

let config = if args.include_global {
session.env.load_config_manager()?.get_merged_config()?
} else {
session
.env
.load_config_manager()?
.get_merged_config_without_global()?
};
let mut versions = config.versions.to_owned();
let mut versions = session.env.load_config()?.versions.to_owned();
versions.remove("proto");

for tool in &tools {
Expand Down Expand Up @@ -408,7 +392,7 @@ pub async fn install(session: ProtoSession, args: InstallArgs) -> AppResult {
install_one(&session, args, &id, None).await?;
}
None => {
install_all(&session, args).await?;
install_all(&session).await?;
}
};

Expand Down
13 changes: 4 additions & 9 deletions crates/cli/src/commands/outdated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ use tracing::debug;

#[derive(Args, Clone, Debug)]
pub struct OutdatedArgs {
#[arg(long, help = "Include versions from global ~/.proto/.prototools")]
include_global: bool,

#[arg(long, help = "Print the outdated tools in JSON format")]
json: bool,

Expand All @@ -33,9 +30,6 @@ pub struct OutdatedArgs {
)]
latest: bool,

#[arg(long, help = "Only check versions in local ./.prototools")]
only_local: bool,

#[arg(
long,
help = "Update and write the versions to their respective configuration"
Expand Down Expand Up @@ -68,16 +62,17 @@ fn get_in_major_range(spec: &UnresolvedVersionSpec) -> UnresolvedVersionSpec {

#[tracing::instrument(skip_all)]
pub async fn outdated(session: ProtoSession, args: OutdatedArgs) -> AppResult {
let manager = session.env.load_config_manager()?;
let env = &session.env;
let manager = env.load_config_manager()?;

debug!("Determining outdated tools based on config...");

let mut configured_tools = BTreeMap::default();

for file in manager.files.iter().rev() {
if !file.exists
|| !args.include_global && file.global
|| args.only_local && !file.path.parent().is_some_and(|p| p == session.env.cwd)
|| !env.config_mode.includes_global() && file.global
|| env.config_mode.only_local() && !file.path.parent().is_some_and(|p| p == env.cwd)
{
continue;
}
Expand Down
7 changes: 7 additions & 0 deletions crates/cli/src/commands/plugin/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::path::PathBuf;
pub struct PluginInfo {
bins: Vec<ExecutableLocation>,
config: ProtoToolConfig,
exes_dir: Option<PathBuf>,
exe_path: PathBuf,
globals_dirs: Vec<PathBuf>,
globals_prefix: Option<String>,
Expand Down Expand Up @@ -44,6 +45,7 @@ pub async fn info(session: ProtoSession, args: InfoPluginArgs) -> AppResult {

tool.resolve_version(&version, false).await?;
tool.create_executables(false, false).await?;
tool.locate_exes_dir().await?;
tool.locate_globals_dirs().await?;

let mut config = session.env.load_config()?.to_owned();
Expand All @@ -55,6 +57,7 @@ pub async fn info(session: ProtoSession, args: InfoPluginArgs) -> AppResult {
let info = PluginInfo {
bins,
config: tool_config,
exes_dir: tool.get_exes_dir().map(|dir| dir.to_path_buf()),
exe_path: tool.get_exe_path()?.to_path_buf(),
globals_dirs: tool.get_globals_dirs().to_owned(),
globals_prefix: tool.get_globals_prefix().map(|p| p.to_owned()),
Expand Down Expand Up @@ -101,6 +104,10 @@ pub async fn info(session: ProtoSession, args: InfoPluginArgs) -> AppResult {

p.entry("Executable", color::path(tool.get_exe_path()?));

if let Some(dir) = tool.get_exes_dir() {
p.entry("Executables directory", color::path(dir));
}

if let Some(prefix) = tool.get_globals_prefix() {
p.entry("Global packages prefix", color::property(prefix));
}
Expand Down
19 changes: 8 additions & 11 deletions crates/cli/src/commands/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@ use tracing::debug;

#[derive(Args, Clone, Debug)]
pub struct StatusArgs {
#[arg(long, help = "Include versions from global ~/.proto/.prototools")]
include_global: bool,

#[arg(long, help = "Print the active tools in JSON format")]
json: bool,

#[arg(long, help = "Only check versions in local ./.prototools")]
only_local: bool,
}

#[derive(Debug, Default, Serialize)]
Expand All @@ -39,13 +33,16 @@ pub struct StatusItem {

fn find_versions_in_configs(
session: &ProtoSession,
args: &StatusArgs,
items: &mut BTreeMap<Id, StatusItem>,
) -> AppResult {
for file in session.env.load_config_manager()?.files.iter().rev() {
let env = &session.env;
let manager = env.load_config_manager()?;

for file in manager.files.iter().rev() {
if !file.exists
|| !args.include_global && file.global
|| args.only_local && !file.path.parent().is_some_and(|p| p == session.env.cwd)
|| !env.config_mode.includes_global() && file.global
|| env.config_mode.only_local()
&& !file.path.parent().is_some_and(|p| p == session.env.cwd)
{
continue;
}
Expand Down Expand Up @@ -160,7 +157,7 @@ pub async fn status(session: ProtoSession, args: StatusArgs) -> AppResult {

let mut items = BTreeMap::default();

find_versions_in_configs(&session, &args, &mut items)?;
find_versions_in_configs(&session, &mut items)?;
find_versions_from_ecosystem(&session, &mut items).await?;

if items.is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl ProtoSession {
#[async_trait]
impl AppSession for ProtoSession {
async fn startup(&mut self) -> AppResult {
self.env = Arc::new(detect_proto_env()?);
self.env = Arc::new(detect_proto_env(&self.cli)?);

sync_current_proto_tool(&self.env, &self.cli_version)?;

Expand Down
38 changes: 30 additions & 8 deletions crates/cli/src/systems.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::app::{App as CLI, Commands};
use crate::helpers::fetch_latest_version;
use miette::IntoDiagnostic;
use proto_core::{is_offline, now, ProtoEnvironment, UnresolvedVersionSpec, PROTO_CONFIG_NAME};
use proto_core::{
is_offline, now, ConfigMode, ProtoEnvironment, UnresolvedVersionSpec, PROTO_CONFIG_NAME,
};
use proto_installer::*;
use proto_shim::get_exe_file_name;
use semver::Version;
Expand All @@ -15,22 +18,36 @@ use tracing::{debug, instrument, trace};
// STARTUP

#[instrument(skip_all)]
pub fn detect_proto_env() -> AppResult<ProtoEnvironment> {
ProtoEnvironment::new()
pub fn detect_proto_env(cli: &CLI) -> AppResult<ProtoEnvironment> {
let mut env = ProtoEnvironment::new()?;

env.config_mode = cli.config_mode.unwrap_or(match cli.command {
Commands::Activate(_)
| Commands::Install(_)
| Commands::Outdated(_)
| Commands::Status(_) => ConfigMode::Upwards,
_ => ConfigMode::UpwardsGlobal,
});

Ok(env)
}

#[instrument(skip_all)]
pub fn sync_current_proto_tool(env: &ProtoEnvironment, version: &str) -> AppResult {
let Ok(current_exe) = env::current_exe() else {
return Ok(());
};

let tool_dir = env.store.inventory_dir.join("proto").join(version);

if tool_dir.exists() {
if tool_dir.exists()
|| current_exe
.iter()
.any(|comp| comp == "node_modules" || comp == ".cargo")
{
return Ok(());
}

let Ok(current_exe) = env::current_exe() else {
return Ok(());
};

let exe_dir = current_exe.parent().unwrap_or(&env.store.bin_dir);

for exe_name in [get_exe_file_name("proto"), get_exe_file_name("proto-shim")] {
Expand All @@ -49,6 +66,11 @@ pub fn sync_current_proto_tool(env: &ProtoEnvironment, version: &str) -> AppResu

#[instrument(skip_all)]
pub fn load_proto_configs(env: &ProtoEnvironment) -> AppResult {
debug!(
"Loading configuration in {} mode",
env.config_mode.to_string()
);

env.load_config()?;

Ok(())
Expand Down
Loading

0 comments on commit c3444c6

Please sign in to comment.