diff --git a/Cargo.lock b/Cargo.lock index 59accbaca8c..538ee0fd468 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4552,7 +4552,7 @@ dependencies = [ [[package]] name = "wasmer-registry" -version = "3.1.0" +version = "4.0.0" dependencies = [ "anyhow", "console", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 0062558e3d9..be699380915 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -37,7 +37,7 @@ wasmer-wasi-experimental-io-devices = { version = "=3.1.0", path = "../wasi-expe wasmer-wast = { version = "=3.1.0", path = "../../tests/lib/wast", optional = true } wasmer-cache = { version = "=3.1.0", path = "../cache", optional = true } wasmer-types = { version = "=3.1.0", path = "../types" } -wasmer-registry = { version = "=3.1.0", path = "../registry" } +wasmer-registry = { version = "=4.0.0", path = "../registry" } wasmer-object = { version = "=3.1.0", path = "../object", optional = true } wasmer-vfs = { version = "=3.1.0", path = "../vfs", default-features = false, features = ["host-fs"] } wasmer-wasm-interface = { version = "3.1.0", path = "../wasm-interface" } diff --git a/lib/cli/src/commands/add.rs b/lib/cli/src/commands/add.rs index 443b966d41a..885dc1f9b38 100644 --- a/lib/cli/src/commands/add.rs +++ b/lib/cli/src/commands/add.rs @@ -2,7 +2,7 @@ use std::process::{Command, Stdio}; use anyhow::{Context, Error}; use clap::Parser; -use wasmer_registry::{Bindings, PartialWapmConfig, ProgrammingLanguage}; +use wasmer_registry::{Bindings, ProgrammingLanguage, WasmerConfig}; /// Add a WAPM package's bindings to your application. #[derive(Debug, Parser)] @@ -77,8 +77,8 @@ impl Add { Some(r) => Ok(r.clone()), None => { let wasmer_dir = - PartialWapmConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; - let cfg = PartialWapmConfig::from_file(&wasmer_dir) + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; + let cfg = WasmerConfig::from_file(&wasmer_dir) .map_err(Error::msg) .context("Unable to load WAPM's config file")?; Ok(cfg.registry.get_current_registry()) diff --git a/lib/cli/src/commands/config.rs b/lib/cli/src/commands/config.rs index 2137f6e0caa..3df09d409de 100644 --- a/lib/cli/src/commands/config.rs +++ b/lib/cli/src/commands/config.rs @@ -3,10 +3,22 @@ use anyhow::{Context, Result}; use clap::Parser; use std::env; use std::path::PathBuf; +use std::str::ParseBoolError; +use wasmer_registry::WasmerConfig; #[derive(Debug, Parser)] -/// The options for the `wasmer config` subcommand +/// The options for the `wasmer config` subcommand: `wasmer config get --OPTION` or `wasmer config set [FLAG]` pub struct Config { + #[clap(flatten)] + flags: Flags, + /// Subcommand for `wasmer config get | set` + #[clap(subcommand)] + set: Option, +} + +/// Normal configuration +#[derive(Debug, Parser)] +pub struct Flags { /// Print the installation prefix. #[clap(long, conflicts_with = "pkg-config")] prefix: bool, @@ -31,12 +43,118 @@ pub struct Config { #[clap(long, conflicts_with = "pkg-config")] cflags: bool, - /// It outputs the necessary details for compiling + /// Print the path to the wasmer configuration file where all settings are stored + #[clap(long, conflicts_with = "pkg-config")] + config_path: bool, + + /// Outputs the necessary details for compiling /// and linking a program to Wasmer, using the `pkg-config` format. #[clap(long)] pkg_config: bool, } +/// Subcommand for `wasmer config set` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum GetOrSet { + /// `wasmer config get $KEY` + #[clap(subcommand)] + Get(RetrievableConfigField), + /// `wasmer config set $KEY $VALUE` + #[clap(subcommand)] + Set(StorableConfigField), +} + +/// Subcommand for `wasmer config get` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum RetrievableConfigField { + /// Print the registry URL of the currently active registry + #[clap(name = "registry.url")] + RegistryUrl, + /// Print the token for the currently active registry or nothing if not logged in + #[clap(name = "registry.token")] + RegistryToken, + /// Print whether telemetry is currently enabled + #[clap(name = "telemetry.enabled")] + TelemetryEnabled, + /// Print whether update notifications are enabled + #[clap(name = "update-notifications.enabled")] + UpdateNotificationsEnabled, + /// Print the proxy URL + #[clap(name = "proxy.url")] + ProxyUrl, +} + +/// Setting that can be stored in the wasmer config +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum StorableConfigField { + /// Set the registry URL of the currently active registry + #[clap(name = "registry.url")] + RegistryUrl(SetRegistryUrl), + /// Set the token for the currently active registry or nothing if not logged in + #[clap(name = "registry.token")] + RegistryToken(SetRegistryToken), + /// Set whether telemetry is currently enabled + #[clap(name = "telemetry.enabled")] + TelemetryEnabled(SetTelemetryEnabled), + /// Set whether update notifications are enabled + #[clap(name = "update-notifications.enabled")] + UpdateNotificationsEnabled(SetUpdateNotificationsEnabled), + /// Set the active proxy URL + #[clap(name = "proxy.url")] + ProxyUrl(SetProxyUrl), +} + +/// Set the current active registry URL +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetRegistryUrl { + /// Url of the registry + #[clap(name = "URL")] + pub url: String, +} + +/// Set or change the token for the current active registry +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetRegistryToken { + /// Token to set + #[clap(name = "TOKEN")] + pub token: String, +} + +/// Set if update notifications are enabled +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetUpdateNotificationsEnabled { + /// Whether to enable update notifications + #[clap(name = "ENABLED", possible_values = ["true", "false"])] + pub enabled: BoolString, +} + +/// "true" or "false" for handling input in the CLI +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct BoolString(pub bool); + +impl std::str::FromStr for BoolString { + type Err = ParseBoolError; + fn from_str(s: &str) -> Result { + Ok(Self(bool::from_str(s)?)) + } +} + +/// Set if telemetry is enabled +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetTelemetryEnabled { + /// Whether to enable telemetry + #[clap(name = "ENABLED", possible_values = ["true", "false"])] + pub enabled: BoolString, +} + +/// Set if a proxy URL should be used +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetProxyUrl { + /// Set if a proxy URL should be used (empty = unset proxy) + #[clap(name = "URL")] + pub url: String, +} + impl Config { /// Runs logic for the `config` subcommand pub fn execute(&self) -> Result<()> { @@ -44,6 +162,12 @@ impl Config { .context("failed to retrieve the wasmer config".to_string()) } fn inner_execute(&self) -> Result<()> { + if let Some(s) = self.set.as_ref() { + return s.execute(); + } + + let flags = &self.flags; + let key = "WASMER_DIR"; let wasmer_dir = env::var(key) .or_else(|e| { @@ -65,7 +189,7 @@ impl Config { let cflags = format!("-I{}", includedir); let libs = format!("-L{} -lwasmer", libdir); - if self.pkg_config { + if flags.pkg_config { println!("prefix={}", prefixdir); println!("exec_prefix={}", bindir); println!("includedir={}", includedir); @@ -79,24 +203,114 @@ impl Config { return Ok(()); } - if self.prefix { + if flags.prefix { println!("{}", prefixdir); } - if self.bindir { + if flags.bindir { println!("{}", bindir); } - if self.includedir { + if flags.includedir { println!("{}", includedir); } - if self.libdir { + if flags.libdir { println!("{}", libdir); } - if self.libs { + if flags.libs { println!("{}", libs); } - if self.cflags { + if flags.cflags { println!("{}", cflags); } + + if flags.config_path { + let wasmer_dir = WasmerConfig::get_wasmer_dir() + .map_err(|e| anyhow::anyhow!("could not find wasmer dir: {e}"))?; + let path = WasmerConfig::get_file_location(&wasmer_dir); + println!("{}", path.display()); + } + + Ok(()) + } +} + +impl GetOrSet { + fn execute(&self) -> Result<()> { + let wasmer_dir = WasmerConfig::get_wasmer_dir() + .map_err(|e| anyhow::anyhow!("could not find wasmer dir: {e}"))?; + let config_file = WasmerConfig::get_file_location(&wasmer_dir); + let mut config = WasmerConfig::from_file(&wasmer_dir).map_err(|e| { + anyhow::anyhow!( + "could not find config file {e} at {}", + config_file.display() + ) + })?; + match self { + GetOrSet::Get(g) => match g { + RetrievableConfigField::RegistryUrl => { + println!("{}", config.registry.get_current_registry()); + } + RetrievableConfigField::RegistryToken => { + if let Some(s) = config + .registry + .get_login_token_for_registry(&config.registry.get_current_registry()) + { + println!("{s}"); + } + } + RetrievableConfigField::TelemetryEnabled => { + println!("{:?}", config.telemetry_enabled); + } + RetrievableConfigField::UpdateNotificationsEnabled => { + println!("{:?}", config.update_notifications_enabled); + } + RetrievableConfigField::ProxyUrl => { + if let Some(s) = config.proxy.url.as_ref() { + println!("{s}"); + } else { + println!("none"); + } + } + }, + GetOrSet::Set(s) => { + match s { + StorableConfigField::RegistryUrl(s) => { + config.registry.set_current_registry(&s.url); + let current_registry = config.registry.get_current_registry(); + if let Some(u) = wasmer_registry::utils::get_username(¤t_registry) + .ok() + .and_then(|o| o) + { + println!( + "Successfully logged into registry {current_registry:?} as user {u:?}" + ); + } + } + StorableConfigField::RegistryToken(t) => { + config.registry.set_login_token_for_registry( + &config.registry.get_current_registry(), + &t.token, + wasmer_registry::config::UpdateRegistry::LeaveAsIs, + ); + } + StorableConfigField::TelemetryEnabled(t) => { + config.telemetry_enabled = t.enabled.0; + } + StorableConfigField::ProxyUrl(p) => { + if p.url == "none" || p.url.is_empty() { + config.proxy.url = None; + } else { + config.proxy.url = Some(p.url.clone()); + } + } + StorableConfigField::UpdateNotificationsEnabled(u) => { + config.update_notifications_enabled = u.enabled.0; + } + } + config + .save(config_file) + .with_context(|| anyhow::anyhow!("could not save config file"))?; + } + } Ok(()) } } diff --git a/lib/cli/src/commands/init.rs b/lib/cli/src/commands/init.rs index d3e4e520313..ba641ec0ccb 100644 --- a/lib/cli/src/commands/init.rs +++ b/lib/cli/src/commands/init.rs @@ -4,7 +4,7 @@ use clap::Parser; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; -use wasmer_registry::PartialWapmConfig; +use wasmer_registry::WasmerConfig; static NOTE: &str = "# See more keys and definitions at https://docs.wasmer.io/ecosystem/wapm/manifest"; @@ -365,7 +365,7 @@ fn construct_manifest( .map(|p| &p.name) .unwrap_or(fallback_package_name) }); - let wasmer_dir = PartialWapmConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; + let wasmer_dir = WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; let namespace = namespace.or_else(|| { wasmer_registry::whoami(&wasmer_dir, None, None) .ok() diff --git a/lib/cli/src/commands/list.rs b/lib/cli/src/commands/list.rs index 34c494e889f..ae0ec3462f6 100644 --- a/lib/cli/src/commands/list.rs +++ b/lib/cli/src/commands/list.rs @@ -1,5 +1,5 @@ use clap::Parser; -use wasmer_registry::PartialWapmConfig; +use wasmer_registry::WasmerConfig; /// Subcommand for listing packages #[derive(Debug, Copy, Clone, Parser)] @@ -9,8 +9,8 @@ impl List { /// execute [List] pub fn execute(&self) -> Result<(), anyhow::Error> { use prettytable::{format, row, Table}; - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let wasmer_dir = + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; let rows = wasmer_registry::get_all_local_packages(&wasmer_dir) .into_iter() .filter_map(|pkg| { diff --git a/lib/cli/src/commands/login.rs b/lib/cli/src/commands/login.rs index f23694dc3da..e1d141db492 100644 --- a/lib/cli/src/commands/login.rs +++ b/lib/cli/src/commands/login.rs @@ -1,7 +1,7 @@ use clap::Parser; #[cfg(not(test))] use dialoguer::Input; -use wasmer_registry::PartialWapmConfig; +use wasmer_registry::WasmerConfig; /// Subcommand for listing packages #[derive(Debug, Clone, Parser)] @@ -52,8 +52,8 @@ impl Login { /// execute [List] pub fn execute(&self) -> Result<(), anyhow::Error> { let token = self.get_token_or_ask_user()?; - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let wasmer_dir = + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; match wasmer_registry::login::login_and_save_token(&wasmer_dir, &self.registry, &token)? { Some(s) => println!("Login for WAPM user {:?} saved", s), None => println!( diff --git a/lib/cli/src/commands/publish.rs b/lib/cli/src/commands/publish.rs index e3a825be79a..f4b038d3e0c 100644 --- a/lib/cli/src/commands/publish.rs +++ b/lib/cli/src/commands/publish.rs @@ -9,7 +9,7 @@ use tar::Builder; use thiserror::Error; use time::{self, OffsetDateTime}; use wasmer_registry::publish::SignArchiveResult; -use wasmer_registry::{PartialWapmConfig, PACKAGE_TOML_FALLBACK_NAME}; +use wasmer_registry::{WasmerConfig, PACKAGE_TOML_FALLBACK_NAME}; const MIGRATIONS: &[(i32, &str)] = &[ (0, include_str!("../../sql/migrations/0000.sql")), @@ -94,9 +94,9 @@ impl Publish { let registry = match self.registry.as_deref() { Some(s) => wasmer_registry::format_graphql(s), None => { - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() + let wasmer_dir = WasmerConfig::get_wasmer_dir() .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - let config = PartialWapmConfig::from_file(&wasmer_dir) + let config = WasmerConfig::from_file(&wasmer_dir) .map_err(|e| anyhow::anyhow!("could not load config {e}"))?; config.registry.get_current_registry() } @@ -325,8 +325,8 @@ pub fn sign_compressed_archive( /// Opens an exclusive read/write connection to the database, creating it if it does not exist pub fn open_db() -> anyhow::Result { let wasmer_dir = - PartialWapmConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - let db_path = PartialWapmConfig::get_database_file_path(&wasmer_dir); + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let db_path = WasmerConfig::get_database_file_path(&wasmer_dir); let mut conn = Connection::open_with_flags( db_path, OpenFlags::SQLITE_OPEN_CREATE diff --git a/lib/cli/src/commands/whoami.rs b/lib/cli/src/commands/whoami.rs index a0fbedd3ab1..8d5b27fedee 100644 --- a/lib/cli/src/commands/whoami.rs +++ b/lib/cli/src/commands/whoami.rs @@ -1,5 +1,5 @@ use clap::Parser; -use wasmer_registry::PartialWapmConfig; +use wasmer_registry::WasmerConfig; #[derive(Debug, Parser)] /// The options for the `wasmer whoami` subcommand @@ -12,8 +12,8 @@ pub struct Whoami { impl Whoami { /// Execute `wasmer whoami` pub fn execute(&self) -> Result<(), anyhow::Error> { - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let wasmer_dir = + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; let (registry, username) = wasmer_registry::whoami(&wasmer_dir, self.registry.as_deref(), None)?; println!("logged into registry {registry:?} as user {username:?}"); diff --git a/lib/cli/src/package_source.rs b/lib/cli/src/package_source.rs index d3ffc116615..3cb973d68e7 100644 --- a/lib/cli/src/package_source.rs +++ b/lib/cli/src/package_source.rs @@ -4,7 +4,7 @@ use anyhow::Context; use std::path::{Path, PathBuf}; use std::str::FromStr; use url::Url; -use wasmer_registry::PartialWapmConfig; +use wasmer_registry::WasmerConfig; /// Source of a package #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -62,7 +62,7 @@ impl PackageSource { }; } Self::Url(u) => { - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() + let wasmer_dir = WasmerConfig::get_wasmer_dir() .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; if let Some(path) = wasmer_registry::Package::is_url_already_installed(u, &wasmer_dir) @@ -73,7 +73,7 @@ impl PackageSource { } } Self::Package(p) => { - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() + let wasmer_dir = WasmerConfig::get_wasmer_dir() .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; let package_path = Path::new(&p.file()).to_path_buf(); if package_path.exists() { @@ -81,7 +81,7 @@ impl PackageSource { } else if let Some(path) = p.already_installed(&wasmer_dir) { return Ok(path); } else { - let config = PartialWapmConfig::from_file(&wasmer_dir) + let config = WasmerConfig::from_file(&wasmer_dir) .map_err(|e| anyhow::anyhow!("error loading wasmer config file: {e}"))?; p.url(&config.registry.get_current_registry())? } @@ -94,8 +94,8 @@ impl PackageSource { String::new() }; - let wasmer_dir = PartialWapmConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let wasmer_dir = + WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; let mut sp = start_spinner(format!("Installing package {url} ...")); let opt_path = wasmer_registry::install_package(&wasmer_dir, &url); if let Some(sp) = sp.take() { diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index af96584af6e..a1fb62d8a07 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-registry" -version = "3.1.0" +version = "4.0.0" edition = "2021" license = "MIT" description = "Crate to interact with the wasmer registry (wapm.io), download packages, etc." diff --git a/lib/registry/src/config.rs b/lib/registry/src/config.rs index ac7b26688c9..25dad16d9ec 100644 --- a/lib/registry/src/config.rs +++ b/lib/registry/src/config.rs @@ -1,28 +1,21 @@ use graphql_client::GraphQLQuery; -use serde::Deserialize; -use serde::Serialize; -use std::collections::BTreeMap; +use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; pub static GLOBAL_CONFIG_DATABASE_FILE_NAME: &str = "wasmer.sqlite"; #[derive(Deserialize, Default, Serialize, Debug, PartialEq, Eq)] -pub struct PartialWapmConfig { - /// The number of seconds to wait before checking the registry for a new - /// version of the package. - #[serde(default = "wax_default_cooldown")] - pub wax_cooldown: i32, - - /// The registry that wapm will connect to. - pub registry: Registries, - +pub struct WasmerConfig { /// Whether or not telemetry is enabled. #[serde(default)] - pub telemetry: Telemetry, + pub telemetry_enabled: bool, /// Whether or not updated notifications are enabled. #[serde(default)] - pub update_notifications: UpdateNotifications, + pub update_notifications_enabled: bool, + + /// The registry that wapm will connect to. + pub registry: MultiRegistry, /// The proxy to use when connecting to the Internet. #[serde(default)] @@ -38,38 +31,31 @@ pub struct Proxy { pub url: Option, } -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Default)] -pub struct UpdateNotifications { - pub enabled: String, -} - -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Default)] -pub struct Telemetry { - pub enabled: String, -} - -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] -#[serde(untagged)] -pub enum Registries { - Single(Registry), - Multi(MultiRegistry), -} - +/// Struct to store login tokens for multiple registry URLs +/// inside of the wasmer.toml configuration file #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] pub struct MultiRegistry { /// Currently active registry - pub current: String, + pub active_registry: String, /// Map from "RegistryUrl" to "LoginToken", in order to /// be able to be able to easily switch between registries - pub tokens: BTreeMap, + pub tokens: Vec, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] +pub struct RegistryLogin { + /// Registry URL to login to + pub registry: String, + /// Login token for the registry + pub token: String, } -impl Default for Registries { +impl Default for MultiRegistry { fn default() -> Self { - Registries::Single(Registry { - url: format_graphql("https://registry.wapm.io"), - token: None, - }) + MultiRegistry { + active_registry: format_graphql("wapm.io"), + tokens: Vec::new(), + } } } @@ -112,7 +98,7 @@ pub enum UpdateRegistry { #[test] fn test_registries_switch_token() { - let mut registries = Registries::default(); + let mut registries = MultiRegistry::default(); registries.set_current_registry("https://registry.wapm.dev"); assert_eq!( @@ -144,18 +130,12 @@ fn test_registries_switch_token() { ); } -impl Registries { +impl MultiRegistry { /// Gets the current (active) registry URL pub fn clear_current_registry_token(&mut self) { - match self { - Registries::Single(s) => { - s.token = None; - } - Registries::Multi(m) => { - m.tokens.remove(&m.current); - m.tokens.remove(&format_graphql(&m.current)); - } - } + self.tokens.retain(|i| i.registry != self.active_registry); + self.tokens + .retain(|i| i.registry != format_graphql(&self.active_registry)); } pub fn get_graphql_url(&self) -> String { @@ -165,10 +145,7 @@ impl Registries { /// Gets the current (active) registry URL pub fn get_current_registry(&self) -> String { - match self { - Registries::Single(s) => format_graphql(&s.url), - Registries::Multi(m) => format_graphql(&m.current), - } + format_graphql(&self.active_registry) } /// Sets the current (active) registry URL @@ -178,25 +155,17 @@ impl Registries { println!("Error when trying to ping registry {registry:?}: {e}"); println!("WARNING: Registry {registry:?} will be used, but commands may not succeed."); } - match self { - Registries::Single(s) => s.url = registry, - Registries::Multi(m) => m.current = registry, - } + self.active_registry = registry; } /// Returns the login token for the registry pub fn get_login_token_for_registry(&self, registry: &str) -> Option { - match self { - Registries::Single(s) if s.url == registry || format_graphql(registry) == s.url => { - s.token.clone() - } - Registries::Multi(m) => m - .tokens - .get(registry) - .or_else(|| m.tokens.get(&format_graphql(registry))) - .cloned(), - _ => None, - } + let registry_formatted = format_graphql(registry); + self.tokens + .iter() + .filter(|login| login.registry == registry || login.registry == registry_formatted) + .last() + .map(|login| login.token.clone()) } /// Sets the login token for the registry URL @@ -206,38 +175,20 @@ impl Registries { token: &str, update_current_registry: UpdateRegistry, ) { - let new_map = match self { - Registries::Single(s) => { - if s.url == registry { - Registries::Single(Registry { - url: format_graphql(registry), - token: Some(token.to_string()), - }) - } else { - let mut map = BTreeMap::new(); - if let Some(token) = s.token.clone() { - map.insert(format_graphql(&s.url), token); - } - map.insert(format_graphql(registry), token.to_string()); - Registries::Multi(MultiRegistry { - current: format_graphql(&s.url), - tokens: map, - }) - } - } - Registries::Multi(m) => { - m.tokens.insert(format_graphql(registry), token.to_string()); - if update_current_registry == UpdateRegistry::Update { - m.current = format_graphql(registry); - } - Registries::Multi(m.clone()) - } - }; - *self = new_map; + let registry_formatted = format_graphql(registry); + self.tokens + .retain(|login| !(login.registry == registry || login.registry == registry_formatted)); + self.tokens.push(RegistryLogin { + registry: format_graphql(registry), + token: token.to_string(), + }); + if update_current_registry == UpdateRegistry::Update { + self.active_registry = format_graphql(registry); + } } } -impl PartialWapmConfig { +impl WasmerConfig { /// Save the config to a file pub fn save>(&self, to: P) -> anyhow::Result<()> { use std::{fs::File, io::Write}; @@ -250,9 +201,7 @@ impl PartialWapmConfig { pub fn from_file(wasmer_dir: &Path) -> Result { let path = Self::get_file_location(wasmer_dir); match std::fs::read_to_string(&path) { - Ok(config_toml) => { - toml::from_str(&config_toml).map_err(|e| format!("could not parse {path:?}: {e}")) - } + Ok(config_toml) => Ok(toml::from_str(&config_toml).unwrap_or_else(|_| Self::default())), Err(_e) => Ok(Self::default()), } } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index a8bad0cecfa..bb6f6a162c8 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -8,7 +8,6 @@ //! curl -sSfL https://registry.wapm.io/graphql/schema.graphql > lib/registry/graphql/schema.graphql //! ``` -use crate::config::Registries; use anyhow::Context; use core::ops::Range; use reqwest::header::{ACCEPT, RANGE}; @@ -28,14 +27,13 @@ pub mod queries; pub mod utils; pub use crate::{ - config::{format_graphql, PartialWapmConfig}, + config::{format_graphql, WasmerConfig}, package::Package, queries::get_bindings_query::ProgrammingLanguage, }; pub static PACKAGE_TOML_FILE_NAME: &str = "wasmer.toml"; pub static PACKAGE_TOML_FALLBACK_NAME: &str = "wapm.toml"; - pub static GLOBAL_CONFIG_FILE_NAME: &str = "wasmer.toml"; #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] @@ -578,7 +576,7 @@ pub fn whoami( use crate::queries::{who_am_i_query, WhoAmIQuery}; use graphql_client::GraphQLQuery; - let config = PartialWapmConfig::from_file(wasmer_dir); + let config = WasmerConfig::from_file(wasmer_dir); let config = config .map_err(|e| anyhow::anyhow!("{e}")) @@ -627,18 +625,10 @@ pub fn test_if_registry_present(registry: &str) -> Result { } pub fn get_all_available_registries(wasmer_dir: &Path) -> Result, String> { - let config = PartialWapmConfig::from_file(wasmer_dir)?; - + let config = WasmerConfig::from_file(wasmer_dir)?; let mut registries = Vec::new(); - match config.registry { - Registries::Single(s) => { - registries.push(format_graphql(&s.url)); - } - Registries::Multi(m) => { - for key in m.tokens.keys() { - registries.push(format_graphql(key)); - } - } + for login in config.registry.tokens { + registries.push(format_graphql(&login.registry)); } Ok(registries) } diff --git a/lib/registry/src/login.rs b/lib/registry/src/login.rs index 86d1be712b2..d2a095a9b9a 100644 --- a/lib/registry/src/login.rs +++ b/lib/registry/src/login.rs @@ -1,5 +1,5 @@ use crate::config::{format_graphql, UpdateRegistry}; -use crate::PartialWapmConfig; +use crate::WasmerConfig; use std::path::Path; /// Login to a registry and save the token associated with it. @@ -11,7 +11,7 @@ pub fn login_and_save_token( token: &str, ) -> Result, anyhow::Error> { let registry = format_graphql(registry); - let mut config = PartialWapmConfig::from_file(wasmer_dir) + let mut config = WasmerConfig::from_file(wasmer_dir) .map_err(|e| anyhow::anyhow!("config from file: {e}"))?; config.registry.set_current_registry(®istry); config.registry.set_login_token_for_registry( @@ -19,7 +19,7 @@ pub fn login_and_save_token( token, UpdateRegistry::Update, ); - let path = PartialWapmConfig::get_file_location(wasmer_dir); + let path = WasmerConfig::get_file_location(wasmer_dir); config.save(&path)?; crate::utils::get_username_registry_token(®istry, token) } diff --git a/lib/registry/src/package.rs b/lib/registry/src/package.rs index 9662e2a8584..062800f7f92 100644 --- a/lib/registry/src/package.rs +++ b/lib/registry/src/package.rs @@ -1,4 +1,4 @@ -use crate::PartialWapmConfig; +use crate::WasmerConfig; use regex::Regex; use std::path::{Path, PathBuf}; use std::{fmt, str::FromStr}; @@ -28,7 +28,7 @@ impl Package { /// Checks whether the package is already installed, if yes, returns the path to the root dir pub fn already_installed(&self, wasmer_dir: &Path) -> Option { let checkouts_dir = crate::get_checkouts_dir(wasmer_dir); - let config = PartialWapmConfig::from_file(wasmer_dir).ok()?; + let config = WasmerConfig::from_file(wasmer_dir).ok()?; let current_registry = config.registry.get_current_registry(); let hash = self.get_hash(¤t_registry); @@ -134,7 +134,7 @@ impl Package { /// Does not check whether the installation directory already exists. pub fn get_path(&self, wasmer_dir: &Path) -> Result { let checkouts_dir = crate::get_checkouts_dir(wasmer_dir); - let config = PartialWapmConfig::from_file(wasmer_dir) + let config = WasmerConfig::from_file(wasmer_dir) .map_err(|e| anyhow::anyhow!("could not load config {e}"))?; let hash = self.get_hash(&config.registry.get_current_registry()); diff --git a/lib/registry/src/publish.rs b/lib/registry/src/publish.rs index 612cd2ca733..71a605ae0b0 100644 --- a/lib/registry/src/publish.rs +++ b/lib/registry/src/publish.rs @@ -1,6 +1,6 @@ use crate::graphql::{execute_query_modifier_inner, get_signed_url, GetSignedUrl}; use crate::graphql::{publish_package_mutation_chunked, PublishPackageMutationChunked}; -use crate::{format_graphql, PartialWapmConfig}; +use crate::{format_graphql, WasmerConfig}; use console::{style, Emoji}; use graphql_client::GraphQLQuery; use indicatif::{ProgressBar, ProgressState, ProgressStyle}; @@ -39,10 +39,9 @@ pub fn try_chunked_uploading( let registry = match registry.as_ref() { Some(s) => format_graphql(s), None => { - let wasmer_dir = - PartialWapmConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; + let wasmer_dir = WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; - let config = PartialWapmConfig::from_file(&wasmer_dir); + let config = WasmerConfig::from_file(&wasmer_dir); config .map_err(|e| anyhow::anyhow!("{e}"))? @@ -54,10 +53,9 @@ pub fn try_chunked_uploading( let token = match token.as_ref() { Some(s) => s.to_string(), None => { - let wasmer_dir = - PartialWapmConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; + let wasmer_dir = WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; - let config = PartialWapmConfig::from_file(&wasmer_dir); + let config = WasmerConfig::from_file(&wasmer_dir); config .map_err(|e| anyhow::anyhow!("{e}"))? diff --git a/tests/integration/cli/tests/config.rs b/tests/integration/cli/tests/config.rs new file mode 100644 index 00000000000..fa3ea57708c --- /dev/null +++ b/tests/integration/cli/tests/config.rs @@ -0,0 +1,365 @@ +use anyhow::bail; +use std::path::{Path, PathBuf}; +use std::process::Command; +use wasmer_integration_tests_cli::get_wasmer_path; + +#[test] +fn wasmer_config_multiget() -> anyhow::Result<()> { + let bin_path = Path::new(env!("WASMER_DIR")).join("bin"); + let include_path = Path::new(env!("WASMER_DIR")).join("include"); + + let bin = format!("{}", bin_path.display()); + let include = format!("-I{}", include_path.display()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("--bindir") + .arg("--cflags") + .output()?; + + let lines = String::from_utf8_lossy(&output.stdout) + .lines() + .map(|s| s.trim().to_string()) + .collect::>(); + + let expected = vec![bin, include]; + + assert_eq!(lines, expected); + + Ok(()) +} + +#[test] +fn wasmer_config_error() -> anyhow::Result<()> { + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("--bindir") + .arg("--cflags") + .arg("--pkg-config") + .output()?; + + let lines = String::from_utf8_lossy(&output.stderr) + .lines() + .map(|s| s.trim().to_string()) + .collect::>(); + let expected = vec![ + "error: The argument '--bindir' cannot be used with '--pkg-config'", + "", + "USAGE:", + "wasmer config --bindir --cflags", + "", + "For more information try --help", + ]; + + assert_eq!(lines, expected); + + Ok(()) +} +#[test] +fn config_works() -> anyhow::Result<()> { + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--bindir") + .output()?; + + let bin_path = Path::new(env!("WASMER_DIR")).join("bin"); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("{}\n", bin_path.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--cflags") + .output()?; + + let include_path = Path::new(env!("WASMER_DIR")).join("include"); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("-I{}\n", include_path.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--includedir") + .output()?; + + let include_path = Path::new(env!("WASMER_DIR")).join("include"); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("{}\n", include_path.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--libdir") + .output()?; + + let lib_path = Path::new(env!("WASMER_DIR")).join("lib"); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("{}\n", lib_path.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--libs") + .output()?; + + let lib_path = Path::new(env!("WASMER_DIR")).join("lib"); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("-L{} -lwasmer\n", lib_path.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--prefix") + .output()?; + + let wasmer_dir = Path::new(env!("WASMER_DIR")); + assert_eq!( + String::from_utf8(bindir.stdout).unwrap(), + format!("{}\n", wasmer_dir.display()) + ); + + let bindir = Command::new(get_wasmer_path()) + .arg("config") + .arg("--pkg-config") + .output()?; + + let bin_path = format!("{}", bin_path.display()); + let include_path = format!("{}", include_path.display()); + let lib_path = format!("{}", lib_path.display()); + let wasmer_dir = format!("{}", wasmer_dir.display()); + + let args = vec![ + format!("prefix={wasmer_dir}"), + format!("exec_prefix={bin_path}"), + format!("includedir={include_path}"), + format!("libdir={lib_path}"), + format!(""), + format!("Name: wasmer"), + format!("Description: The Wasmer library for running WebAssembly"), + format!("Version: {}", env!("CARGO_PKG_VERSION")), + format!("Cflags: -I{include_path}"), + format!("Libs: -L{lib_path} -lwasmer"), + ]; + + let lines = String::from_utf8(bindir.stdout) + .unwrap() + .lines() + .map(|s| s.trim().to_string()) + .collect::>(); + + assert_eq!(lines, args); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("--config-path") + .output()?; + + let config_path = Path::new(env!("WASMER_DIR")).join("wasmer.toml"); + assert_eq!( + String::from_utf8_lossy(&output.stdout), + format!("{}\n", config_path.display()) + ); + + // ---- config get + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.token") + .output()?; + + let original_token = String::from_utf8_lossy(&output.stdout); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("registry.token") + .arg("abc123") + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.token") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + "abc123\n".to_string() + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("registry.token") + .arg(original_token.to_string().trim()) + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.token") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + format!("{}\n", original_token.to_string().trim().to_string()) + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.url") + .output()?; + + let original_url = String::from_utf8_lossy(&output.stdout); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("registry.url") + .arg("wapm.dev") + .output()?; + + let output_str = String::from_utf8_lossy(&output.stdout); + + assert_eq!(output_str, "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.url") + .output()?; + + let output_str = String::from_utf8_lossy(&output.stdout); + assert_eq!( + output_str, + "https://registry.wapm.dev/graphql\n".to_string() + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("registry.url") + .arg(original_url.to_string().trim()) + .output()?; + + let output_str = String::from_utf8_lossy(&output.stdout); + assert_eq!(output_str, "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("registry.url") + .output()?; + + let output_str = String::from_utf8_lossy(&output.stdout); + assert_eq!(output_str, original_url.to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("telemetry.enabled") + .output()?; + + let original_output = String::from_utf8_lossy(&output.stdout); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("telemetry.enabled") + .arg("true") + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("telemetry.enabled") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + "true\n".to_string() + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("telemetry.enabled") + .arg(original_output.to_string().trim()) + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("telemetry.enabled") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + original_output.to_string() + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("update-notifications.enabled") + .output()?; + + let original_output = String::from_utf8_lossy(&output.stdout); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("update-notifications.enabled") + .arg("true") + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("update-notifications.enabled") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + "true\n".to_string() + ); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("set") + .arg("update-notifications.enabled") + .arg(original_output.to_string().trim()) + .output()?; + + assert_eq!(String::from_utf8_lossy(&output.stdout), "".to_string()); + + let output = Command::new(get_wasmer_path()) + .arg("config") + .arg("get") + .arg("update-notifications.enabled") + .output()?; + + assert_eq!( + String::from_utf8_lossy(&output.stdout), + original_output.to_string() + ); + + Ok(()) +}