mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 14:18:20 +00:00
Implement wasmer login
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@@ -467,6 +467,20 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode 0.3.6",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"terminal_size",
|
||||||
|
"unicode-width",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -830,6 +844,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dialoguer"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"tempfile",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff"
|
name = "diff"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -969,6 +994,12 @@ version = "1.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -2250,7 +2281,7 @@ checksum = "5f375cb74c23b51d23937ffdeb48b1fbf5b6409d4b9979c1418c1de58bc8f801"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"csv",
|
"csv",
|
||||||
"encode_unicode",
|
"encode_unicode 1.0.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"term 0.7.0",
|
"term 0.7.0",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
@@ -3214,6 +3245,16 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termtree"
|
name = "termtree"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -3937,6 +3978,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"clap 3.2.23",
|
"clap 3.2.23",
|
||||||
"colored 2.0.0",
|
"colored 2.0.0",
|
||||||
|
"dialoguer",
|
||||||
"dirs 4.0.0",
|
"dirs 4.0.0",
|
||||||
"distance",
|
"distance",
|
||||||
"fern",
|
"fern",
|
||||||
@@ -4856,3 +4898,9 @@ name = "yansi"
|
|||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ libc = { version = "^0.2", default-features = false }
|
|||||||
nuke-dir = { version = "0.1.0", optional = true }
|
nuke-dir = { version = "0.1.0", optional = true }
|
||||||
webc = { version = "3.0.1", optional = true }
|
webc = { version = "3.0.1", optional = true }
|
||||||
isatty = "0.1.9"
|
isatty = "0.1.9"
|
||||||
|
dialoguer = "0.10.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] }
|
chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] }
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::commands::CreateExe;
|
|||||||
use crate::commands::CreateObj;
|
use crate::commands::CreateObj;
|
||||||
#[cfg(feature = "wast")]
|
#[cfg(feature = "wast")]
|
||||||
use crate::commands::Wast;
|
use crate::commands::Wast;
|
||||||
use crate::commands::{Cache, Config, Inspect, List, Run, SelfUpdate, Validate};
|
use crate::commands::{Cache, Config, Inspect, List, Login, Run, SelfUpdate, Validate};
|
||||||
use crate::error::PrettyError;
|
use crate::error::PrettyError;
|
||||||
use clap::{CommandFactory, ErrorKind, Parser};
|
use clap::{CommandFactory, ErrorKind, Parser};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -44,6 +44,10 @@ enum WasmerCLIOptions {
|
|||||||
#[clap(name = "run")]
|
#[clap(name = "run")]
|
||||||
Run(Run),
|
Run(Run),
|
||||||
|
|
||||||
|
/// Login into a wapm.io-like registry
|
||||||
|
#[clap(name = "login")]
|
||||||
|
Login(Login),
|
||||||
|
|
||||||
/// Wasmer cache
|
/// Wasmer cache
|
||||||
#[clap(subcommand, name = "cache")]
|
#[clap(subcommand, name = "cache")]
|
||||||
Cache(Cache),
|
Cache(Cache),
|
||||||
@@ -164,6 +168,7 @@ impl WasmerCLIOptions {
|
|||||||
Self::Config(config) => config.execute(),
|
Self::Config(config) => config.execute(),
|
||||||
Self::Inspect(inspect) => inspect.execute(),
|
Self::Inspect(inspect) => inspect.execute(),
|
||||||
Self::List(list) => list.execute(),
|
Self::List(list) => list.execute(),
|
||||||
|
Self::Login(login) => login.execute(),
|
||||||
#[cfg(feature = "wast")]
|
#[cfg(feature = "wast")]
|
||||||
Self::Wast(wast) => wast.execute(),
|
Self::Wast(wast) => wast.execute(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@@ -220,7 +225,9 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> {
|
|||||||
} else {
|
} else {
|
||||||
match command.unwrap_or(&"".to_string()).as_ref() {
|
match command.unwrap_or(&"".to_string()).as_ref() {
|
||||||
"cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run"
|
"cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run"
|
||||||
| "self-update" | "validate" | "wast" | "binfmt" | "list" => WasmerCLIOptions::parse(),
|
| "self-update" | "validate" | "wast" | "binfmt" | "list" | "login" => {
|
||||||
|
WasmerCLIOptions::parse()
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| {
|
WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| {
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ mod create_exe;
|
|||||||
mod create_obj;
|
mod create_obj;
|
||||||
mod inspect;
|
mod inspect;
|
||||||
mod list;
|
mod list;
|
||||||
|
mod login;
|
||||||
mod run;
|
mod run;
|
||||||
mod self_update;
|
mod self_update;
|
||||||
mod validate;
|
mod validate;
|
||||||
@@ -27,7 +28,7 @@ pub use create_exe::*;
|
|||||||
pub use create_obj::*;
|
pub use create_obj::*;
|
||||||
#[cfg(feature = "wast")]
|
#[cfg(feature = "wast")]
|
||||||
pub use wast::*;
|
pub use wast::*;
|
||||||
pub use {cache::*, config::*, inspect::*, list::*, run::*, self_update::*, validate::*};
|
pub use {cache::*, config::*, inspect::*, list::*, login::*, run::*, self_update::*, validate::*};
|
||||||
|
|
||||||
/// The kind of object format to emit.
|
/// The kind of object format to emit.
|
||||||
#[derive(Debug, Copy, Clone, clap::Parser)]
|
#[derive(Debug, Copy, Clone, clap::Parser)]
|
||||||
|
|||||||
35
lib/cli/src/commands/login.rs
Normal file
35
lib/cli/src/commands/login.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use dialoguer::Input;
|
||||||
|
|
||||||
|
/// Subcommand for listing packages
|
||||||
|
#[derive(Debug, Clone, Parser)]
|
||||||
|
pub struct Login {
|
||||||
|
/// Registry to log into (default: wapm.io)
|
||||||
|
#[clap(name = "REGISTRY")]
|
||||||
|
pub registry: Option<String>,
|
||||||
|
/// Login token
|
||||||
|
#[clap(name = "TOKEN")]
|
||||||
|
pub token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Login {
|
||||||
|
fn get_token_or_ask_user(&self) -> Result<String, std::io::Error> {
|
||||||
|
match self.token.as_ref() {
|
||||||
|
Some(s) => Ok(s.clone()),
|
||||||
|
None => Input::new()
|
||||||
|
.with_prompt("Please paste the login token from https://wapm.io/me:\"")
|
||||||
|
.interact_text(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// execute [List]
|
||||||
|
pub fn execute(&self) -> Result<(), anyhow::Error> {
|
||||||
|
let token = self.get_token_or_ask_user()?;
|
||||||
|
let registry = self
|
||||||
|
.registry
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("https://registry.wapm.io");
|
||||||
|
wasmer_registry::login::login_and_save_token(registry, &token)
|
||||||
|
.map_err(|e| anyhow::anyhow!("{e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lib/registry/graphql/queries/whoami.graphql
Normal file
5
lib/registry/graphql/queries/whoami.graphql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
query WhoAmIQuery {
|
||||||
|
viewer {
|
||||||
|
username
|
||||||
|
}
|
||||||
|
}
|
||||||
332
lib/registry/src/config.rs
Normal file
332
lib/registry/src/config.rs
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
use graphql_client::GraphQLQuery;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") {
|
||||||
|
"/.private/wapm.toml"
|
||||||
|
} else {
|
||||||
|
"wapm.toml"
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Default, Serialize, Debug, PartialEq)]
|
||||||
|
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,
|
||||||
|
|
||||||
|
/// Whether or not telemetry is enabled.
|
||||||
|
#[cfg(feature = "telemetry")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub telemetry: Telemetry,
|
||||||
|
|
||||||
|
/// Whether or not updated notifications are enabled.
|
||||||
|
#[cfg(feature = "update-notifications")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub update_notifications: UpdateNotifications,
|
||||||
|
|
||||||
|
/// The proxy to use when connecting to the Internet.
|
||||||
|
#[serde(default)]
|
||||||
|
pub proxy: Proxy,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn wax_default_cooldown() -> i32 {
|
||||||
|
5 * 60
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Default)]
|
||||||
|
pub struct Proxy {
|
||||||
|
pub url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Default)]
|
||||||
|
pub struct UpdateNotifications {
|
||||||
|
pub enabled: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "telemetry")]
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
|
pub struct Telemetry {
|
||||||
|
pub enabled: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Registries {
|
||||||
|
Single(Registry),
|
||||||
|
Multi(MultiRegistry),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct MultiRegistry {
|
||||||
|
/// Currently active registry
|
||||||
|
pub current: String,
|
||||||
|
/// Map from "RegistryUrl" to "LoginToken", in order to
|
||||||
|
/// be able to be able to easily switch between registries
|
||||||
|
pub tokens: BTreeMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Registries {
|
||||||
|
fn default() -> Self {
|
||||||
|
Registries::Single(Registry {
|
||||||
|
url: format_graphql("https://registry.wapm.io"),
|
||||||
|
token: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct Registry {
|
||||||
|
pub url: String,
|
||||||
|
pub token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_graphql(registry: &str) -> String {
|
||||||
|
let mut registry = registry.to_string();
|
||||||
|
if !registry.starts_with("https://") {
|
||||||
|
registry = format!("https://{registry}");
|
||||||
|
}
|
||||||
|
if registry.ends_with("/graphql") {
|
||||||
|
registry.to_string()
|
||||||
|
} else if registry.ends_with('/') {
|
||||||
|
format!("{}graphql", registry)
|
||||||
|
} else {
|
||||||
|
format!("{}/graphql", registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_if_registry_present(registry: &str) -> Result<(), String> {
|
||||||
|
let q = TestIfRegistryPresent::build_query(test_if_registry_present::Variables {});
|
||||||
|
let _: test_if_registry_present::ResponseData =
|
||||||
|
crate::graphql::execute_query(registry, "", &q).map_err(|e| format!("{e}"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum UpdateRegistry {
|
||||||
|
Update,
|
||||||
|
LeaveAsIs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_registries_switch_token() {
|
||||||
|
let mut registries = Registries::default();
|
||||||
|
|
||||||
|
registries.set_current_registry("https://registry.wapm.dev");
|
||||||
|
assert_eq!(
|
||||||
|
registries.get_current_registry(),
|
||||||
|
"https://registry.wapm.dev/graphql".to_string()
|
||||||
|
);
|
||||||
|
registries.set_login_token_for_registry(
|
||||||
|
"https://registry.wapm.io",
|
||||||
|
"token1",
|
||||||
|
UpdateRegistry::LeaveAsIs,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
registries.get_current_registry(),
|
||||||
|
"https://registry.wapm.dev/graphql".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
registries.get_login_token_for_registry(®istries.get_current_registry()),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
registries.set_current_registry("https://registry.wapm.io");
|
||||||
|
assert_eq!(
|
||||||
|
registries.get_login_token_for_registry(®istries.get_current_registry()),
|
||||||
|
Some("token1".to_string())
|
||||||
|
);
|
||||||
|
registries.clear_current_registry_token();
|
||||||
|
assert_eq!(
|
||||||
|
registries.get_login_token_for_registry(®istries.get_current_registry()),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registries {
|
||||||
|
/// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_graphql_url(&self) -> String {
|
||||||
|
let registry = self.get_current_registry();
|
||||||
|
format_graphql(®istry)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the current (active) registry URL
|
||||||
|
pub fn set_current_registry(&mut self, registry: &str) {
|
||||||
|
let registry = format_graphql(registry);
|
||||||
|
if let Err(e) = test_if_registry_present(®istry) {
|
||||||
|
println!("Error when trying to ping registry {registry:?}: {e}");
|
||||||
|
if registry.contains("wapm.dev") {
|
||||||
|
println!("Note: The correct URL for wapm.dev is https://registry.wapm.dev, not {registry}");
|
||||||
|
} else if registry.contains("wapm.io") {
|
||||||
|
println!(
|
||||||
|
"Note: The correct URL for wapm.io is https://registry.wapm.io, not {registry}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the login token for the registry
|
||||||
|
pub fn get_login_token_for_registry(&self, registry: &str) -> Option<String> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the login token for the registry URL
|
||||||
|
pub fn set_login_token_for_registry(
|
||||||
|
&mut self,
|
||||||
|
registry: &str,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialWapmConfig {
|
||||||
|
/// Save the config to a file
|
||||||
|
#[cfg(not(feature = "integration_tests"))]
|
||||||
|
pub fn save(&self) -> anyhow::Result<()> {
|
||||||
|
use std::{fs::File, io::Write};
|
||||||
|
let path = Self::get_file_location().map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||||
|
let config_serialized = toml::to_string(&self)?;
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
file.write_all(config_serialized.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mocked version of the standard function for integration tests
|
||||||
|
#[cfg(feature = "integration_tests")]
|
||||||
|
pub fn save(&self) -> anyhow::Result<()> {
|
||||||
|
let config_serialized = toml::to_string(&self)?;
|
||||||
|
crate::integration_tests::data::RAW_CONFIG_DATA.with(|rcd| {
|
||||||
|
*rcd.borrow_mut() = Some(config_serialized);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file() -> Result<Self, String> {
|
||||||
|
let path = Self::get_file_location()?;
|
||||||
|
|
||||||
|
match std::fs::read_to_string(&path) {
|
||||||
|
Ok(config_toml) => {
|
||||||
|
toml::from_str(&config_toml).map_err(|e| format!("could not parse {path:?}: {e}"))
|
||||||
|
}
|
||||||
|
Err(_e) => Ok(Self::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_dir() -> std::io::Result<PathBuf> {
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
if let Some(pwd) = std::env::var("PWD").ok() {
|
||||||
|
return Ok(PathBuf::from(pwd));
|
||||||
|
}
|
||||||
|
std::env::current_dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_folder() -> Result<PathBuf, String> {
|
||||||
|
Ok(
|
||||||
|
if let Some(folder_str) = env::var("WASMER_DIR").ok().filter(|s| !s.is_empty()) {
|
||||||
|
let folder = PathBuf::from(folder_str);
|
||||||
|
std::fs::create_dir_all(folder.clone())
|
||||||
|
.map_err(|e| format!("cannot create config directory: {e}"))?;
|
||||||
|
folder
|
||||||
|
} else {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let default_dir = Self::get_current_dir()
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("/".to_string()));
|
||||||
|
#[cfg(feature = "dirs")]
|
||||||
|
let home_dir =
|
||||||
|
dirs::home_dir().ok_or(GlobalConfigError::CannotFindHomeDirectory)?;
|
||||||
|
#[cfg(not(feature = "dirs"))]
|
||||||
|
let home_dir = std::env::var("HOME")
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_else(|| default_dir.to_string_lossy().to_string());
|
||||||
|
let mut folder = PathBuf::from(home_dir);
|
||||||
|
folder.push(".wasmer");
|
||||||
|
std::fs::create_dir_all(folder.clone())
|
||||||
|
.map_err(|e| format!("cannot create config directory: {e}"))?;
|
||||||
|
folder
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_location() -> Result<PathBuf, String> {
|
||||||
|
Ok(Self::get_folder()?.join(GLOBAL_CONFIG_FILE_NAME))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/test_if_registry_present.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
struct TestIfRegistryPresent;
|
||||||
251
lib/registry/src/graphql.rs
Normal file
251
lib/registry/src/graphql.rs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
use graphql_client::*;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
use reqwest::{
|
||||||
|
blocking::{multipart::Form, Client},
|
||||||
|
header::USER_AGENT,
|
||||||
|
};
|
||||||
|
use std::env;
|
||||||
|
use std::time::Duration;
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
use {wasm_bus_reqwest::prelude::header::*, wasm_bus_reqwest::prelude::*};
|
||||||
|
|
||||||
|
mod proxy {
|
||||||
|
//! Code for dealing with setting things up to proxy network requests
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ProxyError {
|
||||||
|
#[error("Failed to parse URL from {}: {}", url_location, error_message)]
|
||||||
|
UrlParseError {
|
||||||
|
url_location: String,
|
||||||
|
error_message: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Could not connect to proxy: {0}")]
|
||||||
|
ConnectionError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to set up a proxy
|
||||||
|
///
|
||||||
|
/// This function reads from wapm config's `proxy.url` first, then checks
|
||||||
|
/// `ALL_PROXY`, `HTTPS_PROXY`, and `HTTP_PROXY` environment variables, in both
|
||||||
|
/// upper case and lower case, in that order.
|
||||||
|
///
|
||||||
|
/// If a proxy is specified in wapm config's `proxy.url`, it is assumed
|
||||||
|
/// to be a general proxy
|
||||||
|
///
|
||||||
|
/// A return value of `Ok(None)` means that there was no attempt to set up a proxy,
|
||||||
|
/// `Ok(Some(proxy))` means that the proxy was set up successfully, and `Err(e)` that
|
||||||
|
/// there was a failure while attempting to set up the proxy.
|
||||||
|
pub fn maybe_set_up_proxy() -> anyhow::Result<Option<reqwest::Proxy>> {
|
||||||
|
use std::env;
|
||||||
|
let proxy = if let Ok(proxy_url) = env::var("ALL_PROXY").or_else(|_| env::var("all_proxy"))
|
||||||
|
{
|
||||||
|
reqwest::Proxy::all(&proxy_url).map(|proxy| (proxy_url, proxy, "ALL_PROXY"))
|
||||||
|
} else if let Ok(https_proxy_url) =
|
||||||
|
env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy"))
|
||||||
|
{
|
||||||
|
reqwest::Proxy::https(&https_proxy_url)
|
||||||
|
.map(|proxy| (https_proxy_url, proxy, "HTTPS_PROXY"))
|
||||||
|
} else if let Ok(http_proxy_url) =
|
||||||
|
env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy"))
|
||||||
|
{
|
||||||
|
reqwest::Proxy::http(&http_proxy_url).map(|proxy| (http_proxy_url, proxy, "http_proxy"))
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
.map_err(|e| ProxyError::ConnectionError(e.to_string()))
|
||||||
|
.and_then(
|
||||||
|
|(proxy_url_str, proxy, url_location): (String, _, &'static str)| {
|
||||||
|
url::Url::parse(&proxy_url_str)
|
||||||
|
.map_err(|e| ProxyError::UrlParseError {
|
||||||
|
url_location: url_location.to_string(),
|
||||||
|
error_message: e.to_string(),
|
||||||
|
})
|
||||||
|
.map(|url| {
|
||||||
|
if !(url.username().is_empty()) && url.password().is_some() {
|
||||||
|
proxy.basic_auth(url.username(), url.password().unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
proxy
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Some(proxy))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/get_package_version.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
pub(crate) struct GetPackageVersionQuery;
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/get_package_by_command.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
pub(crate) struct GetPackageByCommandQuery;
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/test_if_registry_present.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
pub(crate) struct TestIfRegistryPresent;
|
||||||
|
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
pub fn whoami_distro() -> String {
|
||||||
|
whoami::os().to_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
pub fn whoami_distro() -> String {
|
||||||
|
whoami::distro().to_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_query_modifier_inner_check_json<V, F>(
|
||||||
|
registry_url: &str,
|
||||||
|
login_token: &str,
|
||||||
|
query: &QueryBody<V>,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
form_modifier: F,
|
||||||
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
V: serde::Serialize,
|
||||||
|
F: FnOnce(Form) -> Form,
|
||||||
|
{
|
||||||
|
let client = {
|
||||||
|
let builder = Client::builder();
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? {
|
||||||
|
builder.proxy(proxy)
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
};
|
||||||
|
builder.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let vars = serde_json::to_string(&query.variables).unwrap();
|
||||||
|
|
||||||
|
let form = Form::new()
|
||||||
|
.text("query", query.query.to_string())
|
||||||
|
.text("operationName", query.operation_name.to_string())
|
||||||
|
.text("variables", vars);
|
||||||
|
|
||||||
|
let form = form_modifier(form);
|
||||||
|
|
||||||
|
let user_agent = format!(
|
||||||
|
"wapm/{} {} {}",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
whoami::platform(),
|
||||||
|
whoami_distro(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut res = client
|
||||||
|
.post(registry_url)
|
||||||
|
.multipart(form)
|
||||||
|
.bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()))
|
||||||
|
.header(USER_AGENT, user_agent);
|
||||||
|
|
||||||
|
if let Some(t) = timeout {
|
||||||
|
res = res.timeout(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = res.send()?;
|
||||||
|
|
||||||
|
let _: Response<serde_json::Value> = res.json()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_query_modifier_inner<R, V, F>(
|
||||||
|
registry_url: &str,
|
||||||
|
login_token: &str,
|
||||||
|
query: &QueryBody<V>,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
form_modifier: F,
|
||||||
|
) -> anyhow::Result<R>
|
||||||
|
where
|
||||||
|
for<'de> R: serde::Deserialize<'de>,
|
||||||
|
V: serde::Serialize,
|
||||||
|
F: FnOnce(Form) -> Form,
|
||||||
|
{
|
||||||
|
let client = {
|
||||||
|
let builder = Client::builder();
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? {
|
||||||
|
builder.proxy(proxy)
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
};
|
||||||
|
builder.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let vars = serde_json::to_string(&query.variables).unwrap();
|
||||||
|
|
||||||
|
let form = Form::new()
|
||||||
|
.text("query", query.query.to_string())
|
||||||
|
.text("operationName", query.operation_name.to_string())
|
||||||
|
.text("variables", vars);
|
||||||
|
|
||||||
|
let form = form_modifier(form);
|
||||||
|
|
||||||
|
let user_agent = format!(
|
||||||
|
"wapm/{} {} {}",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
whoami::platform(),
|
||||||
|
whoami_distro(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut res = client
|
||||||
|
.post(registry_url)
|
||||||
|
.multipart(form)
|
||||||
|
.bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()))
|
||||||
|
.header(USER_AGENT, user_agent);
|
||||||
|
|
||||||
|
if let Some(t) = timeout {
|
||||||
|
res = res.timeout(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = res.send()?;
|
||||||
|
let response_body: Response<R> = res.json()?;
|
||||||
|
if let Some(errors) = response_body.errors {
|
||||||
|
let error_messages: Vec<String> = errors.into_iter().map(|err| err.message).collect();
|
||||||
|
return Err(anyhow::anyhow!("{}", error_messages.join(", ")));
|
||||||
|
}
|
||||||
|
Ok(response_body.data.expect("missing response data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_query<R, V>(
|
||||||
|
registry_url: &str,
|
||||||
|
login_token: &str,
|
||||||
|
query: &QueryBody<V>,
|
||||||
|
) -> anyhow::Result<R>
|
||||||
|
where
|
||||||
|
for<'de> R: serde::Deserialize<'de>,
|
||||||
|
V: serde::Serialize,
|
||||||
|
{
|
||||||
|
execute_query_modifier_inner(registry_url, login_token, query, None, |f| f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_query_with_timeout<R, V>(
|
||||||
|
registry_url: &str,
|
||||||
|
login_token: &str,
|
||||||
|
timeout: Duration,
|
||||||
|
query: &QueryBody<V>,
|
||||||
|
) -> anyhow::Result<R>
|
||||||
|
where
|
||||||
|
for<'de> R: serde::Deserialize<'de>,
|
||||||
|
V: serde::Serialize,
|
||||||
|
{
|
||||||
|
execute_query_modifier_inner(registry_url, login_token, query, Some(timeout), |f| f)
|
||||||
|
}
|
||||||
@@ -1,272 +1,15 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::env;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use serde::Deserialize;
|
pub mod config;
|
||||||
use serde::Serialize;
|
pub mod graphql;
|
||||||
|
pub mod login;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
pub mod graphql {
|
use crate::config::{format_graphql, Registries};
|
||||||
|
pub use config::PartialWapmConfig;
|
||||||
use graphql_client::*;
|
|
||||||
#[cfg(not(target_os = "wasi"))]
|
|
||||||
use reqwest::{
|
|
||||||
blocking::{multipart::Form, Client},
|
|
||||||
header::USER_AGENT,
|
|
||||||
};
|
|
||||||
use std::env;
|
|
||||||
use std::time::Duration;
|
|
||||||
#[cfg(target_os = "wasi")]
|
|
||||||
use {wasm_bus_reqwest::prelude::header::*, wasm_bus_reqwest::prelude::*};
|
|
||||||
|
|
||||||
mod proxy {
|
|
||||||
//! Code for dealing with setting things up to proxy network requests
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ProxyError {
|
|
||||||
#[error("Failed to parse URL from {}: {}", url_location, error_message)]
|
|
||||||
UrlParseError {
|
|
||||||
url_location: String,
|
|
||||||
error_message: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Could not connect to proxy: {0}")]
|
|
||||||
ConnectionError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to set up a proxy
|
|
||||||
///
|
|
||||||
/// This function reads from wapm config's `proxy.url` first, then checks
|
|
||||||
/// `ALL_PROXY`, `HTTPS_PROXY`, and `HTTP_PROXY` environment variables, in both
|
|
||||||
/// upper case and lower case, in that order.
|
|
||||||
///
|
|
||||||
/// If a proxy is specified in wapm config's `proxy.url`, it is assumed
|
|
||||||
/// to be a general proxy
|
|
||||||
///
|
|
||||||
/// A return value of `Ok(None)` means that there was no attempt to set up a proxy,
|
|
||||||
/// `Ok(Some(proxy))` means that the proxy was set up successfully, and `Err(e)` that
|
|
||||||
/// there was a failure while attempting to set up the proxy.
|
|
||||||
pub fn maybe_set_up_proxy() -> anyhow::Result<Option<reqwest::Proxy>> {
|
|
||||||
use std::env;
|
|
||||||
let proxy = if let Ok(proxy_url) =
|
|
||||||
env::var("ALL_PROXY").or_else(|_| env::var("all_proxy"))
|
|
||||||
{
|
|
||||||
reqwest::Proxy::all(&proxy_url).map(|proxy| (proxy_url, proxy, "ALL_PROXY"))
|
|
||||||
} else if let Ok(https_proxy_url) =
|
|
||||||
env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy"))
|
|
||||||
{
|
|
||||||
reqwest::Proxy::https(&https_proxy_url)
|
|
||||||
.map(|proxy| (https_proxy_url, proxy, "HTTPS_PROXY"))
|
|
||||||
} else if let Ok(http_proxy_url) =
|
|
||||||
env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy"))
|
|
||||||
{
|
|
||||||
reqwest::Proxy::http(&http_proxy_url)
|
|
||||||
.map(|proxy| (http_proxy_url, proxy, "http_proxy"))
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
.map_err(|e| ProxyError::ConnectionError(e.to_string()))
|
|
||||||
.and_then(
|
|
||||||
|(proxy_url_str, proxy, url_location): (String, _, &'static str)| {
|
|
||||||
url::Url::parse(&proxy_url_str)
|
|
||||||
.map_err(|e| ProxyError::UrlParseError {
|
|
||||||
url_location: url_location.to_string(),
|
|
||||||
error_message: e.to_string(),
|
|
||||||
})
|
|
||||||
.map(|url| {
|
|
||||||
if !(url.username().is_empty()) && url.password().is_some() {
|
|
||||||
proxy.basic_auth(url.username(), url.password().unwrap_or_default())
|
|
||||||
} else {
|
|
||||||
proxy
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Some(proxy))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(GraphQLQuery)]
|
|
||||||
#[graphql(
|
|
||||||
schema_path = "graphql/schema.graphql",
|
|
||||||
query_path = "graphql/queries/get_package_version.graphql",
|
|
||||||
response_derives = "Debug"
|
|
||||||
)]
|
|
||||||
pub(crate) struct GetPackageVersionQuery;
|
|
||||||
|
|
||||||
#[derive(GraphQLQuery)]
|
|
||||||
#[graphql(
|
|
||||||
schema_path = "graphql/schema.graphql",
|
|
||||||
query_path = "graphql/queries/get_package_by_command.graphql",
|
|
||||||
response_derives = "Debug"
|
|
||||||
)]
|
|
||||||
pub(crate) struct GetPackageByCommandQuery;
|
|
||||||
|
|
||||||
#[derive(GraphQLQuery)]
|
|
||||||
#[graphql(
|
|
||||||
schema_path = "graphql/schema.graphql",
|
|
||||||
query_path = "graphql/queries/test_if_registry_present.graphql",
|
|
||||||
response_derives = "Debug"
|
|
||||||
)]
|
|
||||||
pub(crate) struct TestIfRegistryPresent;
|
|
||||||
|
|
||||||
#[cfg(target_os = "wasi")]
|
|
||||||
pub fn whoami_distro() -> String {
|
|
||||||
whoami::os().to_lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "wasi"))]
|
|
||||||
pub fn whoami_distro() -> String {
|
|
||||||
whoami::distro().to_lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_query_modifier_inner_check_json<V, F>(
|
|
||||||
registry_url: &str,
|
|
||||||
login_token: &str,
|
|
||||||
query: &QueryBody<V>,
|
|
||||||
timeout: Option<Duration>,
|
|
||||||
form_modifier: F,
|
|
||||||
) -> anyhow::Result<()>
|
|
||||||
where
|
|
||||||
V: serde::Serialize,
|
|
||||||
F: FnOnce(Form) -> Form,
|
|
||||||
{
|
|
||||||
let client = {
|
|
||||||
let builder = Client::builder();
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "wasi"))]
|
|
||||||
let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? {
|
|
||||||
builder.proxy(proxy)
|
|
||||||
} else {
|
|
||||||
builder
|
|
||||||
};
|
|
||||||
builder.build()?
|
|
||||||
};
|
|
||||||
|
|
||||||
let vars = serde_json::to_string(&query.variables).unwrap();
|
|
||||||
|
|
||||||
let form = Form::new()
|
|
||||||
.text("query", query.query.to_string())
|
|
||||||
.text("operationName", query.operation_name.to_string())
|
|
||||||
.text("variables", vars);
|
|
||||||
|
|
||||||
let form = form_modifier(form);
|
|
||||||
|
|
||||||
let user_agent = format!(
|
|
||||||
"wapm/{} {} {}",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
whoami::platform(),
|
|
||||||
whoami_distro(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut res = client
|
|
||||||
.post(registry_url)
|
|
||||||
.multipart(form)
|
|
||||||
.bearer_auth(
|
|
||||||
env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()),
|
|
||||||
)
|
|
||||||
.header(USER_AGENT, user_agent);
|
|
||||||
|
|
||||||
if let Some(t) = timeout {
|
|
||||||
res = res.timeout(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = res.send()?;
|
|
||||||
|
|
||||||
let _: Response<serde_json::Value> = res.json()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_query_modifier_inner<R, V, F>(
|
|
||||||
registry_url: &str,
|
|
||||||
login_token: &str,
|
|
||||||
query: &QueryBody<V>,
|
|
||||||
timeout: Option<Duration>,
|
|
||||||
form_modifier: F,
|
|
||||||
) -> anyhow::Result<R>
|
|
||||||
where
|
|
||||||
for<'de> R: serde::Deserialize<'de>,
|
|
||||||
V: serde::Serialize,
|
|
||||||
F: FnOnce(Form) -> Form,
|
|
||||||
{
|
|
||||||
let client = {
|
|
||||||
let builder = Client::builder();
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "wasi"))]
|
|
||||||
let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? {
|
|
||||||
builder.proxy(proxy)
|
|
||||||
} else {
|
|
||||||
builder
|
|
||||||
};
|
|
||||||
builder.build()?
|
|
||||||
};
|
|
||||||
|
|
||||||
let vars = serde_json::to_string(&query.variables).unwrap();
|
|
||||||
|
|
||||||
let form = Form::new()
|
|
||||||
.text("query", query.query.to_string())
|
|
||||||
.text("operationName", query.operation_name.to_string())
|
|
||||||
.text("variables", vars);
|
|
||||||
|
|
||||||
let form = form_modifier(form);
|
|
||||||
|
|
||||||
let user_agent = format!(
|
|
||||||
"wapm/{} {} {}",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
whoami::platform(),
|
|
||||||
whoami_distro(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut res = client
|
|
||||||
.post(registry_url)
|
|
||||||
.multipart(form)
|
|
||||||
.bearer_auth(
|
|
||||||
env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()),
|
|
||||||
)
|
|
||||||
.header(USER_AGENT, user_agent);
|
|
||||||
|
|
||||||
if let Some(t) = timeout {
|
|
||||||
res = res.timeout(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = res.send()?;
|
|
||||||
let response_body: Response<R> = res.json()?;
|
|
||||||
if let Some(errors) = response_body.errors {
|
|
||||||
let error_messages: Vec<String> = errors.into_iter().map(|err| err.message).collect();
|
|
||||||
return Err(anyhow::anyhow!("{}", error_messages.join(", ")));
|
|
||||||
}
|
|
||||||
Ok(response_body.data.expect("missing response data"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_query<R, V>(
|
|
||||||
registry_url: &str,
|
|
||||||
login_token: &str,
|
|
||||||
query: &QueryBody<V>,
|
|
||||||
) -> anyhow::Result<R>
|
|
||||||
where
|
|
||||||
for<'de> R: serde::Deserialize<'de>,
|
|
||||||
V: serde::Serialize,
|
|
||||||
{
|
|
||||||
execute_query_modifier_inner(registry_url, login_token, query, None, |f| f)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_query_with_timeout<R, V>(
|
|
||||||
registry_url: &str,
|
|
||||||
login_token: &str,
|
|
||||||
timeout: Duration,
|
|
||||||
query: &QueryBody<V>,
|
|
||||||
) -> anyhow::Result<R>
|
|
||||||
where
|
|
||||||
for<'de> R: serde::Deserialize<'de>,
|
|
||||||
V: serde::Serialize,
|
|
||||||
{
|
|
||||||
execute_query_modifier_inner(registry_url, login_token, query, Some(timeout), |f| f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") {
|
pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") {
|
||||||
"/.private/wapm.toml"
|
"/.private/wapm.toml"
|
||||||
@@ -274,145 +17,6 @@ pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") {
|
|||||||
"wapm.toml"
|
"wapm.toml"
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Serialize, Debug, PartialEq)]
|
|
||||||
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,
|
|
||||||
|
|
||||||
/// Whether or not telemetry is enabled.
|
|
||||||
#[cfg(feature = "telemetry")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub telemetry: Telemetry,
|
|
||||||
|
|
||||||
/// Whether or not updated notifications are enabled.
|
|
||||||
#[cfg(feature = "update-notifications")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub update_notifications: UpdateNotifications,
|
|
||||||
|
|
||||||
/// The proxy to use when connecting to the Internet.
|
|
||||||
#[serde(default)]
|
|
||||||
pub proxy: Proxy,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn wax_default_cooldown() -> i32 {
|
|
||||||
5 * 60
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Default)]
|
|
||||||
pub struct Proxy {
|
|
||||||
pub url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Default)]
|
|
||||||
pub struct UpdateNotifications {
|
|
||||||
pub enabled: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "telemetry")]
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
|
||||||
pub struct Telemetry {
|
|
||||||
pub enabled: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum Registries {
|
|
||||||
Single(Registry),
|
|
||||||
Multi(MultiRegistry),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
|
||||||
pub struct MultiRegistry {
|
|
||||||
/// Currently active registry
|
|
||||||
pub current: String,
|
|
||||||
/// Map from "RegistryUrl" to "LoginToken", in order to
|
|
||||||
/// be able to be able to easily switch between registries
|
|
||||||
pub tokens: BTreeMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Registries {
|
|
||||||
fn default() -> Self {
|
|
||||||
Registries::Single(Registry {
|
|
||||||
url: format_graphql("https://registry.wapm.io"),
|
|
||||||
token: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
|
||||||
pub struct Registry {
|
|
||||||
pub url: String,
|
|
||||||
pub token: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_graphql(registry: &str) -> String {
|
|
||||||
if registry.ends_with("/graphql") {
|
|
||||||
registry.to_string()
|
|
||||||
} else if registry.ends_with('/') {
|
|
||||||
format!("{}graphql", registry)
|
|
||||||
} else {
|
|
||||||
format!("{}/graphql", registry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialWapmConfig {
|
|
||||||
pub fn from_file() -> Result<Self, String> {
|
|
||||||
let path = Self::get_file_location()?;
|
|
||||||
|
|
||||||
match std::fs::read_to_string(&path) {
|
|
||||||
Ok(config_toml) => {
|
|
||||||
toml::from_str(&config_toml).map_err(|e| format!("could not parse {path:?}: {e}"))
|
|
||||||
}
|
|
||||||
Err(_e) => Ok(Self::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_current_dir() -> std::io::Result<PathBuf> {
|
|
||||||
#[cfg(target_os = "wasi")]
|
|
||||||
if let Some(pwd) = std::env::var("PWD").ok() {
|
|
||||||
return Ok(PathBuf::from(pwd));
|
|
||||||
}
|
|
||||||
std::env::current_dir()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_folder() -> Result<PathBuf, String> {
|
|
||||||
Ok(
|
|
||||||
if let Some(folder_str) = env::var("WASMER_DIR").ok().filter(|s| !s.is_empty()) {
|
|
||||||
let folder = PathBuf::from(folder_str);
|
|
||||||
std::fs::create_dir_all(folder.clone())
|
|
||||||
.map_err(|e| format!("cannot create config directory: {e}"))?;
|
|
||||||
folder
|
|
||||||
} else {
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
let default_dir = Self::get_current_dir()
|
|
||||||
.ok()
|
|
||||||
.unwrap_or_else(|| PathBuf::from("/".to_string()));
|
|
||||||
#[cfg(feature = "dirs")]
|
|
||||||
let home_dir =
|
|
||||||
dirs::home_dir().ok_or(GlobalConfigError::CannotFindHomeDirectory)?;
|
|
||||||
#[cfg(not(feature = "dirs"))]
|
|
||||||
let home_dir = std::env::var("HOME")
|
|
||||||
.ok()
|
|
||||||
.unwrap_or_else(|| default_dir.to_string_lossy().to_string());
|
|
||||||
let mut folder = PathBuf::from(home_dir);
|
|
||||||
folder.push(".wasmer");
|
|
||||||
std::fs::create_dir_all(folder.clone())
|
|
||||||
.map_err(|e| format!("cannot create config directory: {e}"))?;
|
|
||||||
folder
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file_location() -> Result<PathBuf, String> {
|
|
||||||
Ok(Self::get_folder()?.join(GLOBAL_CONFIG_FILE_NAME))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub struct PackageDownloadInfo {
|
pub struct PackageDownloadInfo {
|
||||||
pub registry: String,
|
pub registry: String,
|
||||||
|
|||||||
24
lib/registry/src/login.rs
Normal file
24
lib/registry/src/login.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use crate::config::{format_graphql, Registries, UpdateRegistry};
|
||||||
|
use crate::PartialWapmConfig;
|
||||||
|
|
||||||
|
/// Login to a registry and save the token associated with it.
|
||||||
|
///
|
||||||
|
/// Also sets the registry as the currently active registry to provide a better UX.
|
||||||
|
pub fn login_and_save_token(registry: &str, token: &str) -> Result<(), anyhow::Error> {
|
||||||
|
let registry = format_graphql(registry);
|
||||||
|
let mut config = PartialWapmConfig::from_file().map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||||
|
config.registry.set_current_registry(®istry);
|
||||||
|
config.registry.set_login_token_for_registry(
|
||||||
|
&config.registry.get_current_registry(),
|
||||||
|
&token,
|
||||||
|
UpdateRegistry::Update,
|
||||||
|
);
|
||||||
|
config.save()?;
|
||||||
|
let username = crate::utils::get_username_registry_token(®istry, token);
|
||||||
|
if let Some(s) = username.ok().and_then(|o| o) {
|
||||||
|
println!("Login for WAPM user {:?} saved", s);
|
||||||
|
} else {
|
||||||
|
println!("Login for WAPM user saved");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
24
lib/registry/src/utils.rs
Normal file
24
lib/registry/src/utils.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use crate::{graphql::execute_query, PartialWapmConfig};
|
||||||
|
use graphql_client::GraphQLQuery;
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/whoami.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
struct WhoAmIQuery;
|
||||||
|
|
||||||
|
pub fn get_username() -> anyhow::Result<Option<String>> {
|
||||||
|
let config = PartialWapmConfig::from_file().map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||||
|
let registry = &config.registry.get_current_registry();
|
||||||
|
let q = WhoAmIQuery::build_query(who_am_i_query::Variables {});
|
||||||
|
let response: who_am_i_query::ResponseData = execute_query(registry, "", &q)?;
|
||||||
|
Ok(response.viewer.map(|viewer| viewer.username))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_username_registry_token(registry: &str, token: &str) -> anyhow::Result<Option<String>> {
|
||||||
|
let q = WhoAmIQuery::build_query(who_am_i_query::Variables {});
|
||||||
|
let response: who_am_i_query::ResponseData = execute_query(registry, token, &q)?;
|
||||||
|
Ok(response.viewer.map(|viewer| viewer.username))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user