Skip to content

Commit

Permalink
Add tools command.
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj committed Sep 15, 2023
1 parent 25b9e4a commit 51dbd23
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 28 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ proto_core = { version = "0.17.4", path = "../core" }
proto_pdk_api = { version = "0.7.1", path = "../pdk-api" }
proto_schema_plugin = { version = "0.11.4", path = "../schema-plugin" }
proto_wasm_plugin = { version = "0.6.5", path = "../wasm-plugin" }
chrono = "0.4.31"
clap = { workspace = true, features = ["derive", "env"] }
clap_complete = { workspace = true }
convert_case = { workspace = true }
Expand Down
6 changes: 5 additions & 1 deletion crates/cli/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::commands::{
AddPluginArgs, AliasArgs, BinArgs, CleanArgs, CompletionsArgs, GlobalArgs, InstallArgs,
InstallGlobalArgs, ListArgs, ListGlobalArgs, ListRemoteArgs, LocalArgs, PluginsArgs,
RemovePluginArgs, RunArgs, SetupArgs, UnaliasArgs, UninstallArgs, UninstallGlobalArgs,
RemovePluginArgs, RunArgs, SetupArgs, ToolsArgs, UnaliasArgs, UninstallArgs,
UninstallGlobalArgs,
};
use clap::builder::styling::{Color, Style, Styles};
use clap::{Parser, Subcommand, ValueEnum};
Expand Down Expand Up @@ -191,6 +192,9 @@ pub enum Commands {
#[command(name = "setup", about = "Setup proto for your current shell.")]
Setup(SetupArgs),

#[command(name = "tools", about = "List all installed tools and their versions.")]
Tools(ToolsArgs),

#[command(alias = "ua", name = "unalias", about = "Remove an alias from a tool.")]
Unalias(UnaliasArgs),

Expand Down
2 changes: 2 additions & 0 deletions crates/cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod plugins;
mod remove_plugin;
mod run;
mod setup;
mod tools;
mod unalias;
mod uninstall;
mod uninstall_global;
Expand All @@ -37,6 +38,7 @@ pub use plugins::*;
pub use remove_plugin::*;
pub use run::*;
pub use setup::*;
pub use tools::*;
pub use unalias::*;
pub use uninstall::*;
pub use uninstall_global::*;
Expand Down
38 changes: 14 additions & 24 deletions crates/cli/src/commands/plugins.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::helpers::load_configured_tools;
use clap::Args;
use miette::IntoDiagnostic;
use proto_core::{
load_tool_from_locator, Id, PluginLocator, ProtoEnvironment, ToolsConfig, UserConfig,
};
use proto_core::{Id, PluginLocator};
use serde::Serialize;
use starbase::system;
use starbase_styles::color;
use starbase_styles::color::{self, OwoStyle};
use starbase_utils::json;
use std::collections::HashMap;
use tracing::debug;
use std::collections::HashSet;
use tracing::info;

fn render_entry<V: AsRef<str>>(label: &str, value: V) {
println!(
Expand All @@ -34,30 +33,21 @@ pub struct PluginsArgs {

#[system]
pub async fn plugins(args: ArgsRef<PluginsArgs>) {
let proto = ProtoEnvironment::new()?;
let user_config = UserConfig::load()?;

let mut tools_config = ToolsConfig::load_upwards()?;
tools_config.inherit_builtin_plugins();

let mut plugins = HashMap::new();
plugins.extend(&user_config.plugins);
plugins.extend(&tools_config.plugins);

debug!("Loading plugins");
if !args.json {
info!("Loading plugins...");
}

let mut items = vec![];

for (id, locator) in plugins {
let tool = load_tool_from_locator(&id, &proto, &locator, &user_config).await?;

load_configured_tools(HashSet::new(), |tool, locator| {
items.push(PluginItem {
id: id.to_owned(),
locator: locator.to_owned(),
id: tool.id.to_owned(),
locator,
name: tool.metadata.name,
version: tool.metadata.plugin_version,
});
}
})
.await?;

items.sort_by(|a, d| a.id.cmp(&d.id));

Expand All @@ -70,7 +60,7 @@ pub async fn plugins(args: ArgsRef<PluginsArgs>) {
for item in items {
println!(
"{} {} {} {}",
color::id(item.id),
OwoStyle::new().bold().style(color::id(item.id)),
color::muted("-"),
item.name,
color::muted_light(if let Some(version) = item.version {
Expand Down
128 changes: 128 additions & 0 deletions crates/cli/src/commands/tools.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use crate::helpers::load_configured_tools;
use chrono::{DateTime, NaiveDateTime};
use clap::Args;
use miette::IntoDiagnostic;
use proto_core::Id;
use starbase::system;
use starbase_styles::color::{self, OwoStyle};
use starbase_utils::json;
use std::collections::{HashMap, HashSet};
use tracing::info;

#[derive(Args, Clone, Debug)]
pub struct ToolsArgs {
#[arg(help = "IDs of tool to list")]
id: Vec<Id>,

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

#[system]
pub async fn tools(args: ArgsRef<ToolsArgs>) {
if !args.json {
info!("Loading tools...");
}

let mut tools = vec![];

load_configured_tools(HashSet::from_iter(&args.id), |tool, _| {
if !tool.manifest.installed_versions.is_empty() {
tools.push(tool);
}
})
.await?;

tools.sort_by(|a, d| a.id.cmp(&d.id));

if args.json {
let items = tools
.into_iter()
.map(|t| (t.id, t.manifest))
.collect::<HashMap<_, _>>();

println!("{}", json::to_string_pretty(&items).into_diagnostic()?);

return Ok(());
}

for tool in tools {
println!(
"{} {} {}",
OwoStyle::new().bold().style(color::id(&tool.id)),
color::muted("-"),
tool.metadata.name,
);

println!(" Store: {}", color::path(tool.get_inventory_dir()));

if !tool.manifest.aliases.is_empty() {
println!(" Aliases:");

for (alias, version) in &tool.manifest.aliases {
println!(
" {} {} {}",
color::hash(version.to_string()),
color::muted("="),
color::label(alias),
);
}
}

if !tool.manifest.installed_versions.is_empty() {
println!(" Versions:");

let mut versions = tool.manifest.installed_versions.iter().collect::<Vec<_>>();
versions.sort();

for version in versions {
let mut comments = vec![];
let mut is_default = false;

if let Some(meta) = &tool.manifest.versions.get(version) {
if let Some(at) = create_datetime(meta.installed_at) {
comments.push(format!("installed {}", at.format("%x")));
}

if let Some(last_used) = &meta.last_used_at {
if let Some(at) = create_datetime(*last_used) {
comments.push(format!("last used {}", at.format("%x")));
}
}
}

if tool
.manifest
.default_version
.as_ref()
.is_some_and(|dv| dv == &version.to_unresolved_spec())
{
comments.push("default version".into());
is_default = true;
}

if comments.is_empty() {
println!(" {}", color::hash(version.to_string()));
} else {
println!(
" {} {} {}",
if is_default {
color::symbol(version.to_string())
} else {
color::hash(version.to_string())
},
color::muted("-"),
color::muted_light(comments.join(", "))
);
}
}
}

println!();
}
}

fn create_datetime(millis: u128) -> Option<NaiveDateTime> {
DateTime::from_timestamp((millis / 1000) as i64, ((millis % 1000) * 1_000_000) as u32)
.map(|dt| dt.naive_local())
}
34 changes: 33 additions & 1 deletion crates/cli/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use futures::StreamExt;
use indicatif::{ProgressBar, ProgressStyle};
use proto_core::{get_temp_dir, ProtoError};
use proto_core::{
get_temp_dir, load_tool_from_locator, Id, PluginLocator, ProtoEnvironment, ProtoError, Tool,
ToolsConfig, UserConfig,
};
use starbase_utils::fs;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
use std::io::Write;
use std::path::PathBuf;
Expand Down Expand Up @@ -83,3 +87,31 @@ pub async fn download_to_temp_with_progress_bar(

Ok(temp_file)
}

pub async fn load_configured_tools(
filter: HashSet<&Id>,
mut op: impl FnMut(Tool, PluginLocator),
) -> miette::Result<()> {
let proto = ProtoEnvironment::new()?;
let mut user_config = UserConfig::load()?;

let mut tools_config = ToolsConfig::load_upwards()?;
tools_config.inherit_builtin_plugins();

let mut plugins = HashMap::new();
plugins.extend(std::mem::take(&mut user_config.plugins));
plugins.extend(tools_config.plugins);

for (id, locator) in plugins {
if !filter.is_empty() && !filter.contains(&id) {
continue;
}

op(
load_tool_from_locator(&id, &proto, &locator, &user_config).await?,
locator,
);
}

Ok(())
}
1 change: 1 addition & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ async fn main() -> MainResult {
Commands::RemovePlugin(args) => app.execute_with_args(commands::remove_plugin, args),
Commands::Run(args) => app.execute_with_args(commands::run, args),
Commands::Setup(args) => app.execute_with_args(commands::setup, args),
Commands::Tools(args) => app.execute_with_args(commands::tools, args),
Commands::Unalias(args) => app.execute_with_args(commands::unalias, args),
Commands::Uninstall(args) => app.execute_with_args(commands::uninstall, args),
Commands::UninstallGlobal(args) => app.execute_with_args(commands::uninstall_global, args),
Expand Down

0 comments on commit 51dbd23

Please sign in to comment.