From 16c4cb3ca6227574a6f1c2f60b86188a7eb46c24 Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Thu, 2 May 2024 15:56:03 -0500 Subject: [PATCH 1/6] refactoring clientside federation --- Cargo.lock | 3 +- Cargo.toml | 3 +- crates/client/Cargo.toml | 1 + crates/client/src/api.rs | 148 +++++---- crates/client/src/config.rs | 10 + crates/client/src/depsolve.rs | 16 +- crates/client/src/lib.rs | 530 ++++++++++++++++++------------ crates/client/src/registry_url.rs | 6 + crates/client/src/storage.rs | 72 ++-- crates/client/src/storage/fs.rs | 73 +--- src/bin/warg.rs | 114 +------ src/commands.rs | 55 +--- src/commands/bundle.rs | 35 +- src/commands/clear.rs | 2 +- src/commands/config.rs | 15 + src/commands/dependencies.rs | 135 +++----- src/commands/download.rs | 21 +- src/commands/info.rs | 20 +- src/commands/lock.rs | 36 +- src/commands/login.rs | 21 +- src/commands/publish.rs | 69 ++-- src/commands/reset.rs | 22 +- src/commands/update.rs | 20 +- tests/client.rs | 32 +- tests/depsolve.rs | 10 +- tests/memory/mod.rs | 6 +- tests/postgres/mod.rs | 6 +- tests/server.rs | 21 +- tests/support/mod.rs | 2 + 29 files changed, 718 insertions(+), 786 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c076954..c3b7e232 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1977,7 +1977,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -4688,6 +4688,7 @@ dependencies = [ "async-trait", "bytes", "clap", + "dialoguer", "dirs 5.0.1", "futures-util", "indexmap 2.2.4", diff --git a/Cargo.toml b/Cargo.toml index 50588fa0..b3e0bc31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ indexmap.workspace = true semver.workspace = true wat = "1.0.85" wasmprinter = "0.2.78" -dialoguer = "0.11.0" +dialoguer = { workspace = true } itertools = "0.12.1" secrecy= { workspace = true } @@ -72,6 +72,7 @@ homepage = "https://warg.io/" repository = "https://github.com/bytecodealliance/registry" [workspace.dependencies] +dialoguer = "0.11.0" ptree = "0.4.0" warg-api = { path = "crates/api", version = "0.5.0-dev" } warg-credentials = { path = "crates/credentials", version = "0.5.0-dev" } diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 1f2ba7a8..86d7e10d 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -24,6 +24,7 @@ clap = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } +dialoguer = { workspace = true } tokio-util = { workspace = true } tempfile = { workspace = true } reqwest = { workspace = true } diff --git a/crates/client/src/api.rs b/crates/client/src/api.rs index eea7edd1..264e292f 100644 --- a/crates/client/src/api.rs +++ b/crates/client/src/api.rs @@ -5,7 +5,7 @@ use bytes::Bytes; use futures_util::{Stream, TryStreamExt}; use indexmap::IndexMap; use reqwest::{ - header::{HeaderMap, HeaderName, HeaderValue}, + header::{HeaderMap, HeaderValue}, Body, IntoUrl, Method, RequestBuilder, Response, StatusCode, }; use secrecy::{ExposeSecret, Secret}; @@ -159,11 +159,11 @@ async fn into_result) -> Result; + fn warg_header(self, registry_header: Option<&RegistryDomain>) -> Result; } impl WithWargHeader for RequestBuilder { - fn warg_header(self, registry_header: &Option) -> Result { + fn warg_header(self, registry_header: Option<&RegistryDomain>) -> Result { if let Some(reg) = registry_header { Ok(self.header(REGISTRY_HEADER_NAME, HeaderValue::try_from(reg.clone())?)) } else { @@ -219,13 +219,20 @@ impl Client { /// Gets the latest checkpoint from the registry. pub async fn latest_checkpoint( &self, + registry_domain: Option<&RegistryDomain>, ) -> Result, ClientError> { let url = self.url.join(paths::fetch_checkpoint()); - tracing::debug!("getting latest checkpoint at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "getting latest checkpoint at `{url}` with registry header: {registry_header}" + ); + } else { + tracing::debug!("getting latest checkpoint at `{url}`"); + } into_result::<_, FetchError>( self.client .get(url) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, @@ -233,43 +240,26 @@ impl Client { .await } - /// Gets the latest checkpoints from registries. - pub async fn latest_checkpoints( - &self, - registries: impl Iterator, - ) -> Result>> { - let mut timestamps = IndexMap::new(); - for reg in registries.into_iter() { - let url = self.url.join(paths::fetch_checkpoint()); - let registry_header = HeaderName::try_from(REGISTRY_HEADER_NAME).unwrap(); - let header_val = HeaderValue::try_from(reg).unwrap(); - let res: SerdeEnvelope = into_result::<_, FetchError>( - self.client - .get(url) - .header(registry_header, header_val) - .auth(self.auth_token()) - .send() - .await?, - ) - .await?; - timestamps.insert(reg.clone(), res); - } - Ok(timestamps) - } - /// Verify checkpoint of the registry. pub async fn verify_checkpoint( &self, + registry_domain: Option<&RegistryDomain>, request: SerdeEnvelope, ) -> Result { let url = self.url.join(paths::verify_checkpoint()); - tracing::debug!("verifying checkpoint at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "verifying checkpoint at `{url}` with registry header: {registry_header}" + ); + } else { + tracing::debug!("verifying checkpoint at `{url}`"); + } let response = self .client .post(url) .json(&request) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?; @@ -279,15 +269,20 @@ impl Client { /// Fetches package log entries from the registry. pub async fn fetch_logs( &self, + registry_domain: Option<&RegistryDomain>, request: FetchLogsRequest<'_>, ) -> Result { let url = self.url.join(paths::fetch_logs()); - tracing::debug!("fetching logs at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!("fetching logs at `{url}` with registry header: {registry_header}"); + } else { + tracing::debug!("fetching logs at `{url}`"); + } let response = self .client .post(&url) .json(&request) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?; @@ -306,15 +301,22 @@ impl Client { /// Fetches package names from the registry. pub async fn fetch_package_names( &self, + registry_domain: Option<&RegistryDomain>, request: FetchPackageNamesRequest<'_>, ) -> Result { let url = self.url.join(paths::fetch_package_names()); - tracing::debug!("fetching package names at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "fetching package names at `{url}` with registry header: {registry_header}" + ); + } else { + tracing::debug!("fetching package names at `{url}`"); + } let response = self .client .post(url) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .json(&request) .send() @@ -323,14 +325,23 @@ impl Client { } /// Gets ledger sources from the registry. - pub async fn ledger_sources(&self) -> Result { + pub async fn ledger_sources( + &self, + registry_domain: Option<&RegistryDomain>, + ) -> Result { let url = self.url.join(paths::ledger_sources()); - tracing::debug!("getting ledger sources at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "getting ledger sources at `{url}` with registry header: {registry_header}" + ); + } else { + tracing::debug!("getting ledger sources at `{url}`"); + } into_result::<_, LedgerError>( self.client .get(url) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, @@ -341,20 +352,28 @@ impl Client { /// Publish a new record to a package log. pub async fn publish_package_record( &self, + registry_domain: Option<&RegistryDomain>, log_id: &LogId, request: PublishRecordRequest<'_>, ) -> Result { let url = self.url.join(&paths::publish_package_record(log_id)); - tracing::debug!( - "appending record to package `{name}` at `{url}`", - name = request.package_name - ); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "appending record to package `{name}` at `{url}` with registry header: {registry_header}", + name = request.package_name + ); + } else { + tracing::debug!( + "appending record to package `{name}` at `{url}`", + name = request.package_name + ); + } let response = self .client .post(url) .json(&request) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?; @@ -364,16 +383,21 @@ impl Client { /// Gets a package record from the registry. pub async fn get_package_record( &self, + registry_domain: Option<&RegistryDomain>, log_id: &LogId, record_id: &RecordId, ) -> Result { let url = self.url.join(&paths::package_record(log_id, record_id)); - tracing::debug!("getting record `{record_id}` for package `{log_id}` at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!("getting record `{record_id}` for package `{log_id}` at `{url}` with registry header: {registry_header}"); + } else { + tracing::debug!("getting record `{record_id}` for package `{log_id}` at `{url}`"); + } into_result::<_, PackageError>( self.client .get(url) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, @@ -384,15 +408,20 @@ impl Client { /// Gets a content sources from the registry. pub async fn content_sources( &self, + registry_domain: Option<&RegistryDomain>, digest: &AnyHash, ) -> Result { let url = self.url.join(&paths::content_sources(digest)); - tracing::debug!("getting content sources for digest `{digest}` at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!("getting content sources for digest `{digest}` at `{url}` with registry header: {registry_header}"); + } else { + tracing::debug!("getting content sources for digest `{digest}` at `{url}`"); + } into_result::<_, ContentError>( self.client .get(url) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, @@ -403,11 +432,11 @@ impl Client { /// Downloads the content associated with a given record. pub async fn download_content( &self, + registry_domain: Option<&RegistryDomain>, digest: &AnyHash, ) -> Result>, ClientError> { - tracing::debug!("requesting content download for digest `{digest}`"); - - let ContentSourcesResponse { content_sources } = self.content_sources(digest).await?; + let ContentSourcesResponse { content_sources } = + self.content_sources(registry_domain, digest).await?; let sources = content_sources .get(digest) @@ -438,26 +467,28 @@ impl Client { self.warg_registry_header = registry; } - /// Get warg-registry header value - pub fn get_warg_registry(&self) -> &Option { - &self.warg_registry_header - } - /// Proves the inclusion of the given package log heads in the registry. pub async fn prove_inclusion( &self, + registry_domain: Option<&RegistryDomain>, request: InclusionRequest, checkpoint: &Checkpoint, leafs: &[LogLeaf], ) -> Result<(), ClientError> { let url = self.url.join(paths::prove_inclusion()); - tracing::debug!("proving checkpoint inclusion at `{url}`"); + if let Some(registry_header) = registry_domain { + tracing::debug!( + "proving checkpoint inclusion at `{url}` with registry header: {registry_header}" + ); + } else { + tracing::debug!("proving checkpoint inclusion at `{url}`"); + } let response = into_result::( self.client .post(url) .json(&request) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, @@ -470,6 +501,7 @@ impl Client { /// Proves consistency between two log roots. pub async fn prove_log_consistency( &self, + registry_domain: Option<&RegistryDomain>, request: ConsistencyRequest, from_log_root: Cow<'_, AnyHash>, to_log_root: Cow<'_, AnyHash>, @@ -479,7 +511,7 @@ impl Client { self.client .post(url) .json(&request) - .warg_header(self.get_warg_registry())? + .warg_header(registry_domain)? .auth(self.auth_token()) .send() .await?, diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index f103a0d5..0b356be7 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -114,6 +114,14 @@ pub struct Config { /// Whether or not an auth key should be retreived from keyring #[serde(default)] pub keyring_auth: bool, + + /// Whether or ignore registry hints provided by a warg server + #[serde(default)] + pub ignore_federation_hints: bool, + + /// Whether or not to auto accept registry hint or ask the user to confirm + #[serde(default)] + pub auto_accept_federation_hints: bool, } impl Config { @@ -192,6 +200,8 @@ impl Config { }), keys: self.keys.clone(), keyring_auth: self.keyring_auth, + ignore_federation_hints: self.ignore_federation_hints, + auto_accept_federation_hints: self.auto_accept_federation_hints, }; serde_json::to_writer_pretty( diff --git a/crates/client/src/depsolve.rs b/crates/client/src/depsolve.rs index 5a9c96f5..1d14f39b 100644 --- a/crates/client/src/depsolve.rs +++ b/crates/client/src/depsolve.rs @@ -101,9 +101,10 @@ impl LockListBuilder { match import.kind { ImportKind::Locked(_) | ImportKind::Unlocked => { let id = PackageName::new(import.name.clone())?; + let registry_domain = client.get_warg_registry(id.namespace()).await?; if let Some(info) = client .registry() - .load_package(client.api.get_warg_registry(), &id) + .load_package(registry_domain.as_ref(), &id) .await? { let release = info.state.releases().last(); @@ -117,7 +118,10 @@ impl LockListBuilder { client.download(&id, &VersionReq::STAR).await?; if let Some(info) = client .registry() - .load_package(client.api.get_warg_registry(), &id) + .load_package( + client.get_warg_registry(id.namespace()).await?.as_ref(), + &id, + ) .await? { let release = info.state.releases().last(); @@ -213,7 +217,13 @@ where if let Some(info) = self .client .registry() - .load_package(self.client.api.get_warg_registry(), &pkg_id) + .load_package( + self.client + .get_warg_registry(pkg_id.namespace()) + .await? + .as_ref(), + &pkg_id, + ) .await? { let release = if parsed_imp.req != VersionReq::STAR { diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index f2d3fc23..50768373 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -3,8 +3,9 @@ #![deny(missing_docs)] use crate::storage::PackageInfo; use anyhow::{anyhow, Context, Result}; +use dialoguer::theme::ColorfulTheme; +use dialoguer::Confirm; use indexmap::IndexMap; -use reqwest::header::HeaderValue; use reqwest::{Body, IntoUrl}; use secrecy::Secret; use semver::{Version, VersionReq}; @@ -18,7 +19,7 @@ use storage::{ }; use thiserror::Error; use warg_api::v1::{ - fetch::{FetchError, FetchLogsRequest, FetchLogsResponse}, + fetch::{FetchError, FetchLogsRequest}, package::{ MissingContent, PackageError, PackageRecord, PackageRecordState, PublishRecordRequest, UploadEndpoint, @@ -31,7 +32,7 @@ use warg_protocol::package::ReleaseState; use warg_protocol::{ operator, package, registry::{LogId, LogLeaf, PackageName, RecordId, RegistryLen, TimestampedCheckpoint}, - PublishedProtoEnvelope, SerdeEnvelope, + PublishedProtoEnvelope, }; use wasm_compose::graph::{CompositionGraph, EncodeOptions, ExportIndex, InstanceId}; @@ -60,6 +61,8 @@ where content: C, namespace_map: N, api: api::Client, + ignore_federation_hints: bool, + auto_accept_federation_hints: bool, } impl Client { @@ -71,6 +74,8 @@ impl Client>, + ignore_federation_hints: bool, + auto_accept_federation_hints: bool, ) -> ClientResult { let api = api::Client::new(url, auth_token)?; Ok(Self { @@ -78,6 +83,8 @@ impl Client Client Result, ClientError> { + let operator = self + .registry() + .load_operator(Some(&RegistryDomain::from_str(namespace)?)) + .await?; + if let Some(op) = operator { + match op.state.namespace_state(namespace) { + Some(warg_protocol::operator::NamespaceState::Imported { registry }) => { + return Ok(Some(RegistryDomain::from_str(registry)?)); + } + Some(warg_protocol::operator::NamespaceState::Defined) => { + return Ok(None); + } + _ => (), + } + }; + let nm_map = self.namespace_map.load_namespace_map().await?; + Ok(if let Some(nm_map) = &nm_map { + nm_map + .get(namespace) + .map(|domain| RegistryDomain::from_str(domain).unwrap()) + } else { + None + }) + } + /// Stores namespace mapping in local storage pub async fn store_namespace( &self, @@ -120,10 +158,10 @@ impl Client ClientResult<()> { + pub async fn reset_registry(&self) -> ClientResult<()> { tracing::info!("resetting registry local state"); self.registry - .reset(all_registries) + .reset(true) .await .or(Err(ClientError::ResettingRegistryLocalStateFailed)) } @@ -137,45 +175,6 @@ impl Client ClientResult<()> { - self.update_checkpoint(&self.api.latest_checkpoint().await?, vec![]) - .await?; - let operator = self.registry().load_operator(&None).await?; - let operator_log_maps_namespace = if let Some(op) = operator { - let namespace_state = op.state.namespace_state(namespace); - if let Some(nm) = namespace_state { - if let warg_protocol::operator::NamespaceState::Imported { registry } = nm { - self.api - .set_warg_registry(Some(RegistryDomain::from_str(registry)?)); - } - true - } else { - false - } - } else { - false - }; - if !operator_log_maps_namespace { - let map = self.namespace_map().load_namespace_map().await?; - if let Some(map) = map { - let namespace = map.get(namespace); - if let Some(nm) = namespace { - self.api - .set_warg_registry(Some(RegistryDomain::from_str(nm)?)); - } else { - self.api.set_warg_registry(None); - } - } - } - Ok(()) - } - - /// Get warg-registry header value - pub fn get_warg_registry(&self) -> &Option { - self.api.get_warg_registry() - } - /// Locks component pub async fn lock_component(&self, info: &PackageInfo) -> ClientResult> { let mut builder = LockListBuilder::default(); @@ -194,7 +193,7 @@ impl Client Client { - return Err(ClientError::CannotInitializePackage { name: package.name }) + let package = match self.package(&info.name).await { + Ok(package) => { + if initializing { + return Err(ClientError::CannotInitializePackage { name: package.name }); + } else if info.head.is_none() { + // If we're not initializing the package and a head was not explicitly specified, + // updated to the latest checkpoint to get the latest known head. + info.head = package.state.head().as_ref().map(|h| h.digest.clone()); + } + package } - (false, false) => { - return Err(ClientError::MustInitializePackage { name: package.name }) + Err(ClientError::PackageDoesNotExist { .. }) => { + if !initializing { + return Err(ClientError::MustInitializePackage { name: info.name }); + } + PackageInfo::new(info.name.clone()) } - _ => (), - } + err => err?, + }; + let registry_domain = self.get_warg_registry(package.name.namespace()).await?; let record = info.finalize(signing_key)?; let log_id = LogId::package_log::(&package.name); let record = self .api .publish_package_record( + registry_domain.as_ref(), &log_id, PublishRecordRequest { package_name: Cow::Borrowed(&package.name), @@ -429,8 +425,11 @@ impl Client ClientResult<()> { + let registry_domain = self.get_warg_registry(package.namespace()).await?; let log_id = LogId::package_log::(package); - let mut current = self.get_package_record(package, &log_id, record_id).await?; + let mut current = self + .get_package_record(registry_domain.as_ref(), package, &log_id, record_id) + .await?; loop { match current.state { @@ -438,6 +437,7 @@ impl Client { + self.update().await?; return Ok(()); } PackageRecordState::Rejected { reason } => { @@ -449,55 +449,22 @@ impl Client { tokio::time::sleep(interval).await; - current = self.get_package_record(package, &log_id, record_id).await?; + current = self + .get_package_record(registry_domain.as_ref(), package, &log_id, record_id) + .await?; } } } } - /// Updates every package log in every client registry storage to the latest registry checkpoint. - pub async fn update_all(&mut self) -> ClientResult<()> { - let packages = self.registry.load_all_packages().await?; - let checkpoints = self.api.latest_checkpoints(packages.keys()).await?; - self.update_checkpoints(checkpoints, packages).await?; - Ok(()) - } - - /// Updates every package log in client storage to the latest registry checkpoint. + /// Updates all package logs in client registry storage to the latest registry checkpoint. pub async fn update(&self) -> ClientResult<()> { - tracing::info!("updating all packages to latest checkpoint"); - - let mut updating = self.registry.load_packages().await?; + tracing::info!("updating downloaded package logs"); - self.update_checkpoint(&self.api.latest_checkpoint().await?, &mut updating) - .await?; - - Ok(()) - } - - /// Inserts or updates the logs of the specified packages in client storage to - /// the latest registry checkpoint. - pub async fn upsert<'a, I>(&self, packages: I) -> Result<(), ClientError> - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, - { - tracing::info!("updating specific packages to latest checkpoint"); - - let packages = packages.into_iter(); - let mut updating = Vec::with_capacity(packages.len()); - for package in packages { - updating.push( - self.registry - .load_package(self.api.get_warg_registry(), package) - .await? - .unwrap_or_else(|| PackageInfo::new(package.clone())), - ); + for mut packages in self.registry.load_all_packages().await?.into_values() { + self.update_checkpoints(&mut packages).await?; } - self.update_checkpoint(&self.api.latest_checkpoint().await?, &mut updating) - .await?; - Ok(()) } @@ -516,11 +483,18 @@ impl Client Result, ClientError> { - tracing::info!("downloading package `{name}` with requirement `{requirement}`"); - let info = self.fetch_package(name).await?; + let info = self.package(package).await?; + + let registry_domain = self.get_warg_registry(package.namespace()).await?; + + if let Some(ref registry_header) = registry_domain { + tracing::info!("downloading package `{package}` with requirement `{requirement}` with registry header: {registry_header}"); + } else { + tracing::info!("downloading package `{package}` with requirement `{requirement}`"); + } match info.state.find_latest_release(requirement) { Some(release) => { @@ -528,7 +502,9 @@ impl Client Client Result { - tracing::info!("downloading version {version} of package `{package}`"); - let info = self.fetch_package(package).await?; + let info = self.package(package).await?; + + let registry_domain = self.get_warg_registry(package.namespace()).await?; + + if let Some(ref registry_header) = registry_domain { + tracing::info!("downloading version {version} of package `{package}` with registry header: {registry_header}"); + } else { + tracing::info!("downloading version {version} of package `{package}`"); + } let release = info.state @@ -574,71 +557,169 @@ impl Client( + async fn update_packages_and_return_federated_packages<'a>( &self, - ts_checkpoint: &SerdeEnvelope, - packages: impl IntoIterator, - ) -> Result<(), ClientError> { + registry_domain: Option<&RegistryDomain>, + packages: impl IntoIterator, + ) -> Result, Vec<&'a mut PackageInfo>>, ClientError> { + let ts_checkpoint = self.api.latest_checkpoint(registry_domain).await?; let checkpoint = &ts_checkpoint.as_ref().checkpoint; - tracing::info!( - "updating to checkpoint log length `{}`", - checkpoint.log_length - ); + if let Some(registry_header) = registry_domain { + tracing::info!( + "updating to checkpoint log length `{}` with registry header: {registry_header}", + checkpoint.log_length + ); + } else { + tracing::info!( + "updating to checkpoint log length `{}`", + checkpoint.log_length + ); + } + + // operator log info let mut operator = self .registry - .load_operator(self.api.get_warg_registry()) + .load_operator(registry_domain) .await? .unwrap_or_default(); - // Map package names to package logs that need to be updated + // map package names to package logs that need to be updated let mut packages = packages .into_iter() .filter_map(|p| match &p.checkpoint { // Don't bother updating if the package is already at the specified checkpoint - Some(c) if c == checkpoint => None, + // If `registry` field is not set, then update. + Some(c) if p.registry.is_some() && c == checkpoint => None, _ => Some((LogId::package_log::(&p.name), p)), }) .inspect(|(_, p)| tracing::info!("package `{name}` will be updated", name = p.name)) .collect::>(); - if packages.is_empty() { - return Ok(()); + + // if operator log and all packages are up to date at the latest checkpoint, then return + if operator.checkpoint.is_some_and(|c| &c == checkpoint) && packages.is_empty() { + return Ok(IndexMap::default()); } - let mut last_known = packages - .iter() - .map(|(id, p)| (id.clone(), p.head_fetch_token.clone())) - .collect::>(); + // federated packages in other registries + let mut federated_packages: IndexMap, Vec<&mut PackageInfo>> = + IndexMap::with_capacity(packages.len()); + // loop and fetch logs loop { - // let response: FetchLogsResponse = match self - let response: FetchLogsResponse = self + let response = match self .api - .fetch_logs(FetchLogsRequest { - log_length: checkpoint.log_length, - operator: operator - .head_fetch_token - .as_ref() - .map(|t| Cow::Borrowed(t.as_str())), - limit: None, - packages: Cow::Borrowed(&last_known), - }) + .fetch_logs( + registry_domain, + FetchLogsRequest { + log_length: checkpoint.log_length, + operator: operator + .head_fetch_token + .as_ref() + .map(|t| Cow::Borrowed(t.as_str())), + limit: None, + // last known fetch token for each package log ID + packages: Cow::Owned( + packages + .iter() + .map(|(id, p)| (id.clone(), p.head_fetch_token.clone())) + .collect::>(), + ), + }, + ) .await .inspect(|res| { for warning in res.warnings.iter() { tracing::warn!("Fetch warning from registry: {}", warning.message); } - }) - .map_err(|e| { - ClientError::translate_log_not_found(e, |id| { - packages.get(id).map(|p| p.name.clone()) - }) - })?; + }) { + Ok(res) => Ok(res), + Err(err) => match &err { + api::ClientError::Fetch(FetchError::LogNotFound(log_id)) + | api::ClientError::Package(PackageError::LogNotFound(log_id)) => { + if let Some(name) = packages.get(log_id).map(|p| p.name.clone()) { + Err(ClientError::PackageDoesNotExist { name }) + } else { + Err(ClientError::Api(err)) + } + } + api::ClientError::LogNotFoundWithHint(log_id, hint) => { + match hint.to_str().ok().map(|s| s.split_once('=')) { + Some(Some((namespace, registry))) + if !self.ignore_federation_hints + && packages.contains_key(log_id) => + { + let package_name = &packages.get(log_id).unwrap().name; + + if self.auto_accept_federation_hints + || Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(format!( +"Package `{package_name}` is not in `{current_registry}` registry. +Registry recommends using `{registry}` registry for packages in `{namespace}` namespace. +Accept recommendation y/N\n", +current_registry = registry_domain.map(|d| d.as_str()).unwrap_or(&self.url().safe_label()), +)) + .default(true) + .interact() + .unwrap() + { + let federated_registry_domain = + Some(RegistryDomain::from_str(registry)?); + self.store_namespace( + namespace.to_string(), + federated_registry_domain.clone().unwrap(), + ) + .await?; + + // filter packages with namespace in other registry + packages = packages + .into_iter() + .filter_map(|(log_id, package_info)| { + if package_info.name.namespace() == namespace { + if let Some(package_set) = federated_packages + .get_mut(&federated_registry_domain) + { + package_set.push(package_info); + } else { + federated_packages.insert( + federated_registry_domain.clone(), + vec![package_info], + ); + } + + None + } else { + Some((log_id, package_info)) + } + }) + .collect(); + + // continue fetching logs from this registry + continue; + } else { + Err(ClientError::PackageDoesNotExist { + name: package_name.clone(), + }) + } + } + _ => { + if let Some(name) = packages.get(log_id).map(|p| p.name.clone()) { + Err(ClientError::PackageDoesNotExist { name }) + } else { + Err(ClientError::Api(err)) + } + } + } + } + _ => Err(ClientError::Api(err)), + }, + }?; for record in response.operator { let proto_envelope: PublishedProtoEnvelope = @@ -694,20 +775,16 @@ impl Client Client Client Client { self.api .prove_log_consistency( + registry_domain, ConsistencyRequest { from: from_log_length, to: to_log_length, @@ -797,61 +872,101 @@ impl Client( - &mut self, - ts_checkpoints: IndexMap>, - mut packages: IndexMap>, + &self, + packages: impl IntoIterator, ) -> Result<(), ClientError> { - for (name, ts_checkpoint) in ts_checkpoints { - if self.url().safe_label() != name { - self.api - .set_warg_registry(Some(RegistryDomain::from_str(&name)?)); + // first collect the packages that we already have namespace mappings for + let mut federated_packages: IndexMap, Vec<&mut PackageInfo>> = + IndexMap::new(); + for package in packages.into_iter() { + let registry_domain = self.get_warg_registry(package.name.namespace()).await?; + if let Some(package_set) = federated_packages.get_mut(®istry_domain) { + package_set.push(package); } else { - self.api.set_warg_registry(None) + federated_packages.insert(registry_domain, vec![package]); } - let mut packages = packages.get_mut(&name.clone()); - if let Some(pkgs) = &mut packages { - self.update_checkpoint(&ts_checkpoint, pkgs.as_mut_slice()) - .await?; + } + + while let Some((registry_domain, packages)) = federated_packages.pop() { + for (registry_domain, mut packages) in self + .update_packages_and_return_federated_packages(registry_domain.as_ref(), packages) + .await? + .into_iter() + { + if let Some(package_set) = federated_packages.get_mut(®istry_domain) { + package_set.reserve(packages.len()); + package_set.append(&mut packages); + } else { + federated_packages.insert(registry_domain, packages); + } } } Ok(()) } - async fn fetch_package(&self, name: &PackageName) -> Result { + /// Fetches package logs. + pub async fn fetch_packages( + &self, + names: impl IntoIterator, + ) -> Result, ClientError> { + let mut packages: Vec = names + .into_iter() + .map(|name| PackageInfo::new(name.clone())) + .collect(); + self.update_checkpoints(packages.iter_mut()).await?; + Ok(packages) + } + + /// Retrieves the `PackageInfo` from local storage, if present, otherwise fetches from the + /// registry. + pub async fn package(&self, name: &PackageName) -> Result { + let registry_domain = self.get_warg_registry(name.namespace()).await?; match self .registry - .load_package(self.api.get_warg_registry(), name) + .load_package(registry_domain.as_ref(), name) .await? { - Some(info) => { + Some(mut info) => { tracing::info!("log for package `{name}` already exists in storage"); + if info.registry.is_none() { + info.registry = registry_domain + .clone() + .or_else(|| Some(self.url().registry_domain())); + } Ok(info) } None => { let mut info = PackageInfo::new(name.clone()); - self.update_checkpoint(&self.api.latest_checkpoint().await?, [&mut info]) - .await?; - + self.update_checkpoints([&mut info]).await?; Ok(info) } } @@ -859,13 +974,14 @@ impl Client, package: &PackageName, log_id: &LogId, record_id: &RecordId, ) -> ClientResult { let record = self .api - .get_package_record(log_id, record_id) + .get_package_record(registry_domain, log_id, record_id) .await .map_err(|e| { ClientError::translate_log_not_found(e, |id| { @@ -883,7 +999,11 @@ impl Client Result { + async fn download_content( + &self, + registry_domain: Option<&RegistryDomain>, + digest: &AnyHash, + ) -> Result { match self.content.content_location(digest) { Some(path) => { tracing::info!("content for digest `{digest}` already exists in storage"); @@ -892,7 +1012,7 @@ impl Client { self.content .store_content( - Box::pin(self.api.download_content(digest).await?), + Box::pin(self.api.download_content(registry_domain, digest).await?), Some(digest), ) .await?; @@ -956,6 +1076,8 @@ impl FileSystemClient { content, namespace_map, auth_token, + config.ignore_federation_hints, + config.auto_accept_federation_hints, )?)) } @@ -982,6 +1104,8 @@ impl FileSystemClient { FileSystemContentStorage::lock(content_dir)?, FileSystemNamespaceMapStorage::new(namespace_map_path), auth_token, + config.ignore_federation_hints, + config.auto_accept_federation_hints, ) } } @@ -1000,6 +1124,15 @@ pub struct PackageDownload { /// Represents an error returned by Warg registry clients. #[derive(Debug, Error)] pub enum ClientError { + /// Similar Namespace + #[error("Namespace `{namespace}` not found in operator log but found namespace `{e}`, which has alternative casing.")] + SimilarNamespace { + /// Namespace + namespace: String, + /// Provided Error + e: String, + }, + /// No home registry registry server URL is configured. #[error("no home registry registry server URL is configured")] NoHomeRegistryUrl, @@ -1066,15 +1199,6 @@ pub enum ClientError { name: PackageName, }, - /// The package does not exist with hint. - #[error("package `{name}` does not exist")] - PackageDoesNotExistWithHint { - /// The missing package. - name: PackageName, - /// The registry hint - hint: HeaderValue, - }, - /// The package version does not exist. #[error("version `{version}` of package `{name}` does not exist")] PackageVersionDoesNotExist { @@ -1170,14 +1294,6 @@ impl ClientError { return Self::PackageDoesNotExist { name }; } } - api::ClientError::LogNotFoundWithHint(log_id, hint) => { - if let Some(name) = lookup(log_id) { - return Self::PackageDoesNotExistWithHint { - name, - hint: hint.clone(), - }; - } - } _ => {} } diff --git a/crates/client/src/registry_url.rs b/crates/client/src/registry_url.rs index e45988c4..44845b20 100644 --- a/crates/client/src/registry_url.rs +++ b/crates/client/src/registry_url.rs @@ -1,3 +1,4 @@ +use crate::storage::RegistryDomain; use anyhow::{anyhow, bail, Context, Result}; use reqwest::IntoUrl; use url::{Host, Url}; @@ -89,6 +90,11 @@ impl RegistryUrl { label } + /// Returns `RegistryDomain` from the `RegistryUrl`. + pub fn registry_domain(&self) -> RegistryDomain { + RegistryDomain::new(self.safe_label()) + } + pub(crate) fn into_url(self) -> Url { self.0 } diff --git a/crates/client/src/storage.rs b/crates/client/src/storage.rs index 0dc09f51..bdbd131d 100644 --- a/crates/client/src/storage.rs +++ b/crates/client/src/storage.rs @@ -7,7 +7,7 @@ use futures_util::Stream; use indexmap::IndexMap; use reqwest::header::HeaderValue; use serde::{Deserialize, Serialize}; -use std::{path::PathBuf, pin::Pin, str::FromStr, time::SystemTime}; +use std::{fmt, path::PathBuf, pin::Pin, str::FromStr, time::SystemTime}; use warg_crypto::{ hash::{AnyHash, HashAlgorithm}, signing::{self, KeyID, PublicKey}, @@ -23,14 +23,20 @@ mod fs; pub use fs::*; /// Registry domain used for warg header values -#[derive(Clone)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct RegistryDomain(String); -// impl From for RegistryDomain { -// fn from(value: String) -> Self { -// RegistryDomain(value) -// } -// } +impl RegistryDomain { + /// Creates new `RegistryDomain` from string. + pub fn new(registry: String) -> Self { + Self(registry) + } + + /// Extracts a string slice for the registry domain. + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} impl FromStr for RegistryDomain { type Err = Error; @@ -40,19 +46,12 @@ impl FromStr for RegistryDomain { } } -impl ToString for RegistryDomain { - fn to_string(&self) -> String { - self.0.clone() +impl fmt::Display for RegistryDomain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{registry_domain}", registry_domain = &self.0) } } -// impl TryFrom for RegistryName ... - -// impl From for HeaderValue { -// fn from(value: RegistryDomain) -> Self { -// HeaderValue::to_str(&value.to_string()) -// } -// } impl TryFrom for HeaderValue { type Error = Error; @@ -61,14 +60,6 @@ impl TryFrom for HeaderValue { } } -// impl TryInto for HeaderValue { -// type Error = Error; - -// fn try_into(self) -> std::result::Result { -// // Ok(HeaderValue::from_str(&value.to_string())?) - -// } -// } /// Trait for registry storage implementations. /// /// Stores information such as package/operator logs and checkpoints @@ -86,13 +77,13 @@ pub trait RegistryStorage: Send + Sync { /// Loads most recent checkpoint async fn load_checkpoint( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ) -> Result>>; /// Stores most recent checkpoint async fn store_checkpoint( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ts_checkpoint: &SerdeEnvelope, ) -> Result<()>; @@ -101,35 +92,32 @@ pub trait RegistryStorage: Send + Sync { /// Returns `Ok(None)` if the information is not present. async fn load_operator( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ) -> Result>; /// Stores the operator information in the storage. async fn store_operator( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, operator: OperatorInfo, ) -> Result<()>; - /// Loads the package information for all packages in the home registry storage . - async fn load_packages(&self) -> Result>; - - /// Loads the package information for all packages in all registry storages. - async fn load_all_packages(&self) -> Result>>; + /// Loads the package information for all packages. + async fn load_all_packages(&self) -> Result>>; /// Loads the package information from the storage. /// /// Returns `Ok(None)` if the information is not present. async fn load_package( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, package: &PackageName, ) -> Result>; /// Stores the package information in the storage. async fn store_package( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, info: &PackageInfo, ) -> Result<()>; @@ -203,6 +191,12 @@ pub trait NamespaceMapStorage: Send + Sync { #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct OperatorInfo { + /// The registry domain where the package is published. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub registry: Option, + /// The last known checkpoint since checking the registry. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub checkpoint: Option, /// The current operator log state #[serde(default)] pub state: operator::LogState, @@ -220,7 +214,10 @@ pub struct OperatorInfo { pub struct PackageInfo { /// The package name to publish. pub name: PackageName, - /// The last known checkpoint of the package. + /// The registry domain where the package is published. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub registry: Option, + /// The last known checkpoint since checking the registry. #[serde(default, skip_serializing_if = "Option::is_none")] pub checkpoint: Option, /// The current package log state @@ -239,6 +236,7 @@ impl PackageInfo { pub fn new(name: impl Into) -> Self { Self { name: name.into(), + registry: None, checkpoint: None, state: package::LogState::default(), head_registry_index: None, diff --git a/crates/client/src/storage/fs.rs b/crates/client/src/storage/fs.rs index 79568319..3b806ec4 100644 --- a/crates/client/src/storage/fs.rs +++ b/crates/client/src/storage/fs.rs @@ -16,6 +16,7 @@ use std::{ fs, path::{Path, PathBuf}, pin::Pin, + str::FromStr, }; use tempfile::NamedTempFile; use tokio::io::{AsyncWriteExt, BufReader, BufWriter}; @@ -81,7 +82,7 @@ impl FileSystemRegistryStorage { }) } - fn operator_path(&self, namespace_registry: &Option) -> PathBuf { + fn operator_path(&self, namespace_registry: Option<&RegistryDomain>) -> PathBuf { if let Some(nm) = namespace_registry { return self .registries_dir @@ -93,7 +94,7 @@ impl FileSystemRegistryStorage { fn package_path( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, name: &PackageName, ) -> PathBuf { if let Some(nm) = namespace_registry { @@ -131,7 +132,7 @@ impl RegistryStorage for FileSystemRegistryStorage { async fn load_checkpoint( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ) -> Result>> { if let Some(nm) = namespace_registry { return load(&self.registries_dir.join(nm.to_string()).join("checkpoint")).await; @@ -141,7 +142,7 @@ impl RegistryStorage for FileSystemRegistryStorage { async fn store_checkpoint( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ts_checkpoint: &SerdeEnvelope, ) -> Result<()> { if let Some(nm) = namespace_registry { @@ -154,45 +155,7 @@ impl RegistryStorage for FileSystemRegistryStorage { store(&self.base_dir.join("checkpoint"), ts_checkpoint).await } - async fn load_packages(&self) -> Result> { - let mut packages = Vec::new(); - - let packages_dir = self.base_dir.join(PACKAGE_LOGS_DIR); - if !packages_dir.exists() { - return Ok(vec![]); - } - - for entry in WalkDir::new(&packages_dir) { - let entry = entry.with_context(|| { - anyhow!( - "failed to walk directory `{path}`", - path = packages_dir.display() - ) - })?; - - let path = entry.path(); - if !path.is_file() { - continue; - } - - if let Some(name) = path.file_name().and_then(OsStr::to_str) { - if name.starts_with('.') { - continue; - } - } - - packages.push(load(path).await?.ok_or_else(|| { - anyhow!( - "failed to load package state from `{path}`", - path = path.display() - ) - })?); - } - - Ok(packages) - } - - async fn load_all_packages(&self) -> Result>> { + async fn load_all_packages(&self) -> Result>> { let mut all_packages = IndexMap::new(); let regs = fs::read_dir(self.registries_dir.clone())?; for reg in regs { @@ -223,7 +186,7 @@ impl RegistryStorage for FileSystemRegistryStorage { })?; packages.push(info); } - all_packages.insert(name.to_string(), packages); + all_packages.insert(RegistryDomain::from_str(name)?, packages); }; } Ok(all_packages) @@ -231,14 +194,14 @@ impl RegistryStorage for FileSystemRegistryStorage { async fn load_operator( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, ) -> Result> { Ok(load(&self.operator_path(namespace_registry)).await?) } async fn store_operator( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, info: OperatorInfo, ) -> Result<()> { store(&self.operator_path(namespace_registry), info).await @@ -246,7 +209,7 @@ impl RegistryStorage for FileSystemRegistryStorage { async fn load_package( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, package: &PackageName, ) -> Result> { Ok(load(&self.package_path(namespace_registry, package)).await?) @@ -254,7 +217,7 @@ impl RegistryStorage for FileSystemRegistryStorage { async fn store_package( &self, - namespace_registry: &Option, + namespace_registry: Option<&RegistryDomain>, info: &PackageInfo, ) -> Result<()> { store(&self.package_path(namespace_registry, &info.name), info).await @@ -432,28 +395,26 @@ impl ContentStorage for FileSystemContentStorage { /// Represents a namespace_domain map storage using the local file system. pub struct FileSystemNamespaceMapStorage { - base_dir: PathBuf, + path: PathBuf, } impl FileSystemNamespaceMapStorage { /// Creates new namespace_domain mapping config - pub fn new(base_dir: impl Into) -> Self { - Self { - base_dir: base_dir.into(), - } + pub fn new(path: impl Into) -> Self { + Self { path: path.into() } } } #[async_trait] impl NamespaceMapStorage for FileSystemNamespaceMapStorage { async fn load_namespace_map(&self) -> Result>> { - let namespace_path = &self.base_dir; + let namespace_path = &self.path; let namespace_map = load(namespace_path).await?; Ok(namespace_map) } async fn reset_namespaces(&self) -> Result<()> { - remove(&self.base_dir).await?; + delete(&self.path).await?; Ok(()) } @@ -465,7 +426,7 @@ impl NamespaceMapStorage for FileSystemNamespaceMapStorage { let mut mapping = self.load_namespace_map().await?.unwrap_or_default(); mapping.insert(namespace, registry_domain.to_string()); let json = serde_json::to_string(&mapping)?; - fs::write(&self.base_dir, json)?; + fs::write(&self.path, json)?; Ok(()) } } diff --git a/src/bin/warg.rs b/src/bin/warg.rs index e088e25a..02d3b67e 100644 --- a/src/bin/warg.rs +++ b/src/bin/warg.rs @@ -1,11 +1,10 @@ use anyhow::Result; use clap::Parser; -use dialoguer::{theme::ColorfulTheme, Confirm}; use std::process::exit; use tracing_subscriber::EnvFilter; use warg_cli::commands::{ BundleCommand, ClearCommand, ConfigCommand, DependenciesCommand, DownloadCommand, InfoCommand, - KeyCommand, LockCommand, LoginCommand, LogoutCommand, PublishCommand, ResetCommand, Retry, + KeyCommand, LockCommand, LoginCommand, LogoutCommand, PublishCommand, ResetCommand, UpdateCommand, }; use warg_client::ClientError; @@ -50,19 +49,19 @@ async fn main() -> Result<()> { WargCli::Config(cmd) => cmd.exec().await, WargCli::Info(cmd) => cmd.exec().await, WargCli::Key(cmd) => cmd.exec().await, - WargCli::Lock(cmd) => cmd.exec(None).await, - WargCli::Bundle(cmd) => cmd.exec(None).await, - WargCli::Dependencies(cmd) => cmd.exec(None).await, - WargCli::Download(cmd) => cmd.exec(None).await, - WargCli::Update(cmd) => cmd.exec(None).await, - WargCli::Publish(cmd) => cmd.exec(None).await, + WargCli::Lock(cmd) => cmd.exec().await, + WargCli::Bundle(cmd) => cmd.exec().await, + WargCli::Dependencies(cmd) => cmd.exec().await, + WargCli::Download(cmd) => cmd.exec().await, + WargCli::Update(cmd) => cmd.exec().await, + WargCli::Publish(cmd) => cmd.exec().await, WargCli::Reset(cmd) => cmd.exec().await, WargCli::Clear(cmd) => cmd.exec().await, WargCli::Login(cmd) => cmd.exec().await, WargCli::Logout(cmd) => cmd.exec().await, } { if let Some(e) = e.downcast_ref::() { - describe_client_error_or_retry(e).await?; + describe_client_error(e).await?; } else { eprintln!("error: {e:?}"); } @@ -72,102 +71,7 @@ async fn main() -> Result<()> { Ok(()) } -async fn describe_client_error_or_retry(e: &ClientError) -> Result<()> { - match e { - ClientError::NoHomeRegistryUrl => { - eprintln!("error: {e}; use the `config` subcommand to set a home registry URL"); - } - ClientError::PackageValidationFailed { name, inner } => { - eprintln!("error: the log for package `{name}` is invalid: {inner}") - } - ClientError::PackageLogEmpty { name } => { - eprintln!("error: the log for package `{name}` is empty (the registry could be lying)"); - eprintln!("see issue https://github.com/bytecodealliance/registry/issues/66"); - } - ClientError::PackageDoesNotExistWithHint { name, hint } => { - let hint_reg = hint.to_str().unwrap(); - let mut terms = hint_reg.split('='); - let namespace = terms.next(); - let registry = terms.next(); - if let (Some(namespace), Some(registry)) = (namespace, registry) { - let prompt = format!( - "The package `{}`, does not exist in the registry you're using.\nHowever, the package namespace `{namespace}` does exist in the registry at {registry}.\nWould you like to configure your warg cli to use this registry for packages with this namespace in the future? y/N\n", - name.name() - ); - if Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt(prompt) - .interact() - .unwrap() - { - if let Err(e) = match WargCli::parse() { - WargCli::Config(cmd) => cmd.exec().await, - WargCli::Info(cmd) => cmd.exec().await, - WargCli::Key(cmd) => cmd.exec().await, - WargCli::Lock(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Bundle(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Dependencies(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Download(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Update(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Publish(cmd) => { - cmd.exec(Some(Retry::new( - namespace.to_string(), - registry.to_string(), - ))) - .await - } - WargCli::Reset(cmd) => cmd.exec().await, - WargCli::Clear(cmd) => cmd.exec().await, - WargCli::Login(cmd) => cmd.exec().await, - WargCli::Logout(cmd) => cmd.exec().await, - } { - if let Some(e) = e.downcast_ref::() { - describe_client_error(e).await?; - } else { - eprintln!("error: {e:?}"); - } - exit(1); - } - } - } - } - _ => { - eprintln!("error: {e}") - } - } - Ok(()) -} - -async fn describe_client_error(e: &ClientError) -> Result<()> { +pub async fn describe_client_error(e: &ClientError) -> Result<()> { match e { ClientError::NoHomeRegistryUrl => { eprintln!("error: {e}; use the `config` subcommand to set a default URL"); diff --git a/src/commands.rs b/src/commands.rs index 201e47ba..a5fe0369 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,12 +4,7 @@ use anyhow::Result; use clap::Args; use secrecy::Secret; use std::path::PathBuf; -use std::str::FromStr; -use warg_client::storage::ContentStorage; -use warg_client::storage::NamespaceMapStorage; use warg_client::storage::RegistryDomain; -use warg_client::storage::RegistryStorage; -use warg_client::Client; use warg_client::RegistryUrl; use warg_client::{ClientError, Config, FileSystemClient, StorageLockResult}; use warg_credentials::keyring::{get_auth_token, get_signing_key}; @@ -73,11 +68,7 @@ impl CommonOptions { } /// Creates the warg client to use. - pub async fn create_client( - &self, - config: &Config, - retry: Option, - ) -> Result { + pub fn create_client(&self, config: &Config) -> Result { let client = match FileSystemClient::try_new_with_config( self.registry.as_deref(), config, @@ -97,25 +88,17 @@ impl CommonOptions { ) } }?; - if let Some(retry) = retry { - retry.store_namespace(&client).await?; - } Ok(client) } /// Gets the signing key for the given registry URL. - pub fn signing_key( + pub async fn signing_key( &self, - client: &Client, + registry_domain: Option<&RegistryDomain>, ) -> Result { - let registry_url = if let Some(nm) = &client.get_warg_registry() { - Some(RegistryUrl::new(nm.to_string())?) - } else { - None - }; let config = self.read_config()?; get_signing_key( - registry_url.map(|reg| reg.safe_label()).as_deref(), + registry_domain.map(|domain| domain.to_string()).as_deref(), &config.keys, config.home_url.as_deref(), ) @@ -134,33 +117,3 @@ impl CommonOptions { Ok(None) } } - -/// Namespace mapping to store when retrying a command after receiving a hint header -pub struct Retry { - namespace: String, - registry: String, -} - -impl Retry { - /// New Retry - pub fn new(namespace: String, registry: String) -> Self { - Self { - namespace, - registry, - } - } - - /// Map namespace using Retry information - pub async fn store_namespace( - &self, - client: &Client, - ) -> Result<()> { - client - .store_namespace( - self.namespace.clone(), - RegistryDomain::from_str(&self.registry)?, - ) - .await?; - Ok(()) - } -} diff --git a/src/commands/bundle.rs b/src/commands/bundle.rs index 8256fea8..9e144771 100644 --- a/src/commands/bundle.rs +++ b/src/commands/bundle.rs @@ -1,9 +1,8 @@ -use super::{CommonOptions, Retry}; -use anyhow::{bail, Result}; +use super::CommonOptions; +use anyhow::Result; use clap::Args; -use semver::VersionReq; -use warg_client::storage::RegistryStorage; use warg_protocol::registry::PackageName; + /// Bundle With Registry Dependencies #[derive(Args)] pub struct BundleCommand { @@ -18,29 +17,13 @@ pub struct BundleCommand { impl BundleCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.package.namespace()).await?; - println!("registry: {url}", url = client.url()); - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &self.package) - .await? - { - client.bundle_component(&info).await?; - } else { - client.download(&self.package, &VersionReq::STAR).await?; - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &self.package) - .await? - { - client.bundle_component(&info).await?; - } else { - bail!("Unable to find package {}", self.package.name()) - } - } + let client = self.common.create_client(&config)?; + + let info = client.package(&self.package).await?; + client.bundle_component(&info).await?; + Ok(()) } } diff --git a/src/commands/clear.rs b/src/commands/clear.rs index 0f9ccc6d..b9ce20a7 100644 --- a/src/commands/clear.rs +++ b/src/commands/clear.rs @@ -14,7 +14,7 @@ impl ClearCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; println!("clearing local content cache..."); client.clear_content_cache().await?; diff --git a/src/commands/config.rs b/src/commands/config.rs index a151df19..3ed8a26c 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -19,6 +19,14 @@ pub struct ConfigCommand { #[clap(long, value_name = "CONTENT")] pub content_dir: Option, + /// Ignore federation hints. + #[clap(long)] + pub ignore_federation_hints: bool, + + /// Auto accept federation hints. + #[clap(long)] + pub auto_accept_federation_hints: bool, + /// Overwrite the existing configuration file. #[clap(long)] pub overwrite: bool, @@ -70,10 +78,17 @@ impl ConfigCommand { namespace_map_path: self.namespace_path.map(|p| cwd.join(p)), keys: self.common.read_config()?.keys, keyring_auth: false, + ignore_federation_hints: self.ignore_federation_hints, + auto_accept_federation_hints: self.auto_accept_federation_hints, }; config.write_to_file(&path)?; + // reset when changing home registry + let client = self.common.create_client(&config)?; + client.reset_namespaces().await?; + client.reset_registry().await?; + println!( "created warg configuration file `{path}`", path = path.display(), diff --git a/src/commands/dependencies.rs b/src/commands/dependencies.rs index e51781af..e8174a15 100644 --- a/src/commands/dependencies.rs +++ b/src/commands/dependencies.rs @@ -1,17 +1,18 @@ -use super::{CommonOptions, Retry}; +use super::CommonOptions; use anyhow::{bail, Result}; use async_recursion::async_recursion; use clap::Args; use ptree::{output::print_tree, TreeBuilder}; +use std::default::Default; use std::fs; use warg_client::{ - storage::{ContentStorage, PackageInfo, RegistryStorage}, + storage::PackageInfo, version_util::{ create_child_node, new_tree, version_string, DependencyImportParser, ImportKind, }, FileSystemClient, }; -use warg_protocol::{package::ReleaseState, registry::PackageName, VersionReq}; +use warg_protocol::{registry::PackageName, VersionReq}; use wasmparser::{Chunk, ComponentImport, ComponentImportSectionReader, Parser, Payload}; /// Print Dependency Tree @@ -28,18 +29,12 @@ pub struct DependenciesCommand { impl DependenciesCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.package.namespace()).await?; + let client = self.common.create_client(&config)?; - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &self.package) - .await? - { - Self::print_package_info(&client, &info).await?; - } + let info = client.package(&self.package).await?; + Self::print_package_info(&client, &info).await?; Ok(()) } @@ -54,87 +49,63 @@ impl DependenciesCommand { ) -> Result<()> { client.download(id, &version).await?; - let package = client - .registry() - .load_package(client.get_warg_registry(), id) - .await?; - if let Some(pkg) = package { - let latest = pkg.state.releases().last(); - if let Some(l) = latest { - if let ReleaseState::Released { content } = &l.state { - let path = client.content().content_location(content); - if let Some(p) = path { - let bytes = fs::read(p)?; - let deps = parser.parse(&bytes)?; - for dep in deps { - let mut dep_parser = DependencyImportParser { - next: dep.name.0, - offset: 0, - }; - let dep = dep_parser.parse()?; - let v = version_string(&dep.req); - let grand_child = create_child_node(node, &dep.name, &v); - match dep.kind { - ImportKind::Locked(_) | ImportKind::Unlocked => { - let id = PackageName::new(dep.name)?; - Self::parse_deps(&id, dep.req, client, grand_child, parser) - .await?; - } - ImportKind::Interface(_) => {} - } - grand_child.end_child(); - } + if let Some(download) = client.download(id, &version).await? { + let bytes = fs::read(download.path)?; + let deps = parser.parse(&bytes)?; + for dep in deps { + let mut dep_parser = DependencyImportParser { + next: dep.name.0, + offset: 0, + }; + let dep = dep_parser.parse()?; + let v = version_string(&dep.req); + let grand_child = create_child_node(node, &dep.name, &v); + match dep.kind { + ImportKind::Locked(_) | ImportKind::Unlocked => { + let id = PackageName::new(dep.name)?; + Self::parse_deps(&id, dep.req, client, grand_child, parser).await?; } + ImportKind::Interface(_) => {} } + grand_child.end_child(); } } + Ok(()) } async fn print_package_info(client: &FileSystemClient, info: &PackageInfo) -> Result<()> { let mut parser = DepsParser::new(); - let root_package = client - .registry() - .load_package(client.get_warg_registry(), &info.name) - .await?; - if let Some(rp) = root_package { - let latest = rp.state.releases().last(); - if let Some(l) = latest { - client.download(&info.name, &VersionReq::STAR).await?; - let mut tree = new_tree(info.name.namespace(), info.name.name(), &l.version); - if let ReleaseState::Released { content } = &l.state { - let path = client.content().content_location(content); - if let Some(p) = path { - let bytes = fs::read(&p)?; - let deps = parser.parse(&bytes)?; - for dep in deps { - let mut dep_parser = DependencyImportParser { - next: dep.name.0, - offset: 0, - }; - let dep = dep_parser.parse()?; - let v = version_string(&dep.req); - let child = create_child_node(&mut tree, &dep.name, &v); - match dep.kind { - ImportKind::Locked(_) | ImportKind::Unlocked => { - Self::parse_deps( - &PackageName::new(dep.name)?, - dep.req, - client, - child, - &mut parser, - ) - .await?; - } - ImportKind::Interface(_) => {} - } - child.end_child(); - } + + if let Some(download) = client.download(&info.name, &Default::default()).await? { + let mut tree = new_tree(info.name.namespace(), info.name.name(), &download.version); + let bytes = fs::read(&download.path)?; + let deps = parser.parse(&bytes)?; + for dep in deps { + let mut dep_parser = DependencyImportParser { + next: dep.name.0, + offset: 0, + }; + let dep = dep_parser.parse()?; + let v = version_string(&dep.req); + let child = create_child_node(&mut tree, &dep.name, &v); + match dep.kind { + ImportKind::Locked(_) | ImportKind::Unlocked => { + Self::parse_deps( + &PackageName::new(dep.name)?, + dep.req, + client, + child, + &mut parser, + ) + .await?; } - let built = tree.build(); - print_tree(&built)? + ImportKind::Interface(_) => {} } + child.end_child(); } + let built = tree.build(); + print_tree(&built)? } Ok(()) } diff --git a/src/commands/download.rs b/src/commands/download.rs index 703874f6..7e08b8ba 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,4 +1,4 @@ -use super::{CommonOptions, Retry}; +use super::CommonOptions; use anyhow::{anyhow, Result}; use clap::Args; use warg_protocol::{registry::PackageName, VersionReq}; @@ -15,29 +15,30 @@ pub struct DownloadCommand { pub name: PackageName, #[clap(long, short, value_name = "VERSION")] /// The version requirement of the package to download; defaults to `*`. - pub version: Option, + pub version: Option, } impl DownloadCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.name.namespace()).await?; + let client = self.common.create_client(&config)?; println!("downloading package `{name}`...", name = self.name); + // if user specifies exact verion, then set the `VersionReq` to exact match + let version = match &self.version { + Some(version) => VersionReq::parse(&format!("={}", version))?, + None => VersionReq::STAR, + }; + let res = client - .download( - &self.name, - self.version.as_ref().unwrap_or(&VersionReq::STAR), - ) + .download(&self.name, &version) .await? .ok_or_else(|| { anyhow!( "a version of package `{name}` that satisfies `{version}` was not found", name = self.name, - version = self.version.as_ref().unwrap_or(&VersionReq::STAR) ) })?; diff --git a/src/commands/info.rs b/src/commands/info.rs index 2a4921a9..7481d90d 100644 --- a/src/commands/info.rs +++ b/src/commands/info.rs @@ -28,28 +28,28 @@ impl InfoCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; println!("registry: {url}", url = client.url()); println!("\npackages in client storage:"); match self.package { Some(package) => { - client.refresh_namespace(package.namespace()).await?; - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &package) - .await? - { - Self::print_package_info(&info); + let info = client.package(&package).await?; + if let Some(registry) = client.get_warg_registry(package.namespace()).await? { + println!("registry: {registry}"); } + Self::print_package_info(&info); } None => { client .registry() - .load_packages() + .load_all_packages() .await? .iter() - .for_each(Self::print_package_info); + .for_each(|(registry, packages)| { + println!("registry: {registry}"); + packages.iter().for_each(Self::print_package_info); + }); } } diff --git a/src/commands/lock.rs b/src/commands/lock.rs index 0fa6b7e8..73f9e532 100644 --- a/src/commands/lock.rs +++ b/src/commands/lock.rs @@ -1,11 +1,6 @@ -use super::{CommonOptions, Retry}; +use super::CommonOptions; use anyhow::Result; use clap::Args; -use semver::VersionReq; -use warg_client::{ - storage::{PackageInfo, RegistryStorage}, - FileSystemClient, -}; use warg_protocol::registry::PackageName; /// Print Dependency Tree @@ -22,32 +17,13 @@ pub struct LockCommand { impl LockCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.package.namespace()).await?; - println!("registry: {url}", url = client.url()); - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &self.package) - .await? - { - Self::lock(client, &info).await?; - } else { - client.download(&self.package, &VersionReq::STAR).await?; - if let Some(info) = client - .registry() - .load_package(client.get_warg_registry(), &self.package) - .await? - { - Self::lock(client, &info).await?; - } - } - Ok(()) - } + let client = self.common.create_client(&config)?; + + let info = client.package(&self.package).await?; + client.lock_component(&info).await?; - async fn lock(client: FileSystemClient, info: &PackageInfo) -> Result<()> { - client.lock_component(info).await?; Ok(()) } } diff --git a/src/commands/login.rs b/src/commands/login.rs index 6a3ecfe1..f0b87590 100644 --- a/src/commands/login.rs +++ b/src/commands/login.rs @@ -19,6 +19,14 @@ pub struct LoginCommand { /// The subcommand to execute. #[clap(flatten)] keyring_entry: KeyringEntryArgs, + + /// Ignore federation hints. + #[clap(long)] + pub ignore_federation_hints: bool, + + /// Auto accept federation hints. + #[clap(long)] + pub auto_accept_federation_hints: bool, } #[derive(Args)] @@ -51,10 +59,21 @@ impl LoginCommand { .transpose()? .map(|u| u.to_string()); let mut config = self.common.read_config()?; + config.ignore_federation_hints = self.ignore_federation_hints; + config.auto_accept_federation_hints = self.auto_accept_federation_hints; + if home_url.is_some() { config.home_url = home_url.clone(); config.write_to_file(&Config::default_config_path()?)?; + + // reset if changing home registry + let client = self.common.create_client(&config)?; + client.reset_namespaces().await?; + client.reset_registry().await?; } + + config.keyring_auth = true; + if config.keys.is_empty() { config.keys.insert("default".to_string()); let key = SigningKey::random(&mut OsRng).into(); @@ -66,7 +85,6 @@ impl LoginCommand { .context("failed to read token")?; self.keyring_entry .set_entry(self.common.read_config()?.home_url, &token)?; - config.keyring_auth = true; config.write_to_file(&Config::default_config_path()?)?; println!("auth token was set successfully, and generated default key",); println!("Public Key: {public_key}"); @@ -79,7 +97,6 @@ impl LoginCommand { .context("failed to read token")?; self.keyring_entry .set_entry(self.common.read_config()?.home_url, &token)?; - config.keyring_auth = true; config.write_to_file(&Config::default_config_path()?)?; println!("auth token was set successfully",); Ok(()) diff --git a/src/commands/publish.rs b/src/commands/publish.rs index 553adf15..3dec35bb 100644 --- a/src/commands/publish.rs +++ b/src/commands/publish.rs @@ -1,4 +1,4 @@ -use super::{CommonOptions, Retry}; +use super::CommonOptions; use anyhow::{anyhow, bail, Context, Result}; use clap::{Args, Subcommand}; use futures::TryStreamExt; @@ -83,13 +83,13 @@ pub enum PublishCommand { impl PublishCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { match self { - Self::Init(cmd) => cmd.exec(retry).await, - Self::Release(cmd) => cmd.exec(retry).await, - Self::Yank(cmd) => cmd.exec(retry).await, - Self::Grant(cmd) => cmd.exec(retry).await, - Self::Revoke(cmd) => cmd.exec(retry).await, + Self::Init(cmd) => cmd.exec().await, + Self::Release(cmd) => cmd.exec().await, + Self::Yank(cmd) => cmd.exec().await, + Self::Grant(cmd) => cmd.exec().await, + Self::Revoke(cmd) => cmd.exec().await, Self::Start(cmd) => cmd.exec().await, Self::List(cmd) => cmd.exec().await, Self::Abort(cmd) => cmd.exec().await, @@ -116,13 +116,12 @@ pub struct PublishInitCommand { impl PublishInitCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; + let client = self.common.create_client(&config)?; + let registry_domain = client.get_warg_registry(self.name.namespace()).await?; - client.refresh_namespace(self.name.namespace()).await?; - - let signing_key = self.common.signing_key(&client)?; + let signing_key = self.common.signing_key(registry_domain.as_ref()).await?; match enqueue(&client, &self.name, |_| { std::future::ready(Ok(PublishEntry::Init)) }) @@ -188,11 +187,11 @@ pub struct PublishReleaseCommand { impl PublishReleaseCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.name.namespace()).await?; - let signing_key = self.common.signing_key(&client)?; + let client = self.common.create_client(&config)?; + let registry_domain = client.get_warg_registry(self.name.namespace()).await?; + let signing_key = self.common.signing_key(registry_domain.as_ref()).await?; let path = self.path.clone(); let version = self.version.clone(); @@ -275,11 +274,11 @@ pub struct PublishYankCommand { impl PublishYankCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.name.namespace()).await?; - let signing_key = self.common.signing_key(&client)?; + let client = self.common.create_client(&config)?; + let registry_domain = client.get_warg_registry(self.name.namespace()).await?; + let signing_key = self.common.signing_key(registry_domain.as_ref()).await?; let version = self.version.clone(); match enqueue(&client, &self.name, move |_| async move { @@ -353,11 +352,11 @@ pub struct PublishGrantCommand { impl PublishGrantCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.name.namespace()).await?; - let signing_key = self.common.signing_key(&client)?; + let client = self.common.create_client(&config)?; + let registry_domain = client.get_warg_registry(self.name.namespace()).await?; + let signing_key = self.common.signing_key(registry_domain.as_ref()).await?; match enqueue(&client, &self.name, |_| async { Ok(PublishEntry::Grant { @@ -435,11 +434,11 @@ pub struct PublishRevokeCommand { impl PublishRevokeCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; - client.refresh_namespace(self.name.namespace()).await?; - let signing_key = self.common.signing_key(&client)?; + let client = self.common.create_client(&config)?; + let registry_domain = client.get_warg_registry(self.name.namespace()).await?; + let signing_key = self.common.signing_key(registry_domain.as_ref()).await?; match enqueue(&client, &self.name, |_| async { Ok(PublishEntry::Revoke { @@ -506,8 +505,7 @@ impl PublishStartCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, None).await?; - client.refresh_namespace(self.name.namespace()).await?; + let client = self.common.create_client(&config)?; match client.registry().load_publish().await? { Some(info) => bail!("a publish is already in progress for package `{name}`; use `publish abort` to abort the current publish", name = info.name), @@ -541,7 +539,7 @@ impl PublishListCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; match client.registry().load_publish().await? { Some(info) => { @@ -597,7 +595,7 @@ impl PublishAbortCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; match client.registry().load_publish().await? { Some(info) => { @@ -629,7 +627,7 @@ impl PublishSubmitCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; match client.registry().load_publish().await? { Some(info) => { @@ -638,7 +636,7 @@ impl PublishSubmitCommand { name = info.name ); - let signing_key = self.common.signing_key(&client)?; + let signing_key = self.common.signing_key(None).await?; let record_id = client.publish_with_info(&signing_key, info.clone()).await?; client.registry().store_publish(None).await?; @@ -707,8 +705,7 @@ impl PublishWaitCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, None).await?; - client.refresh_namespace(self.name.namespace()).await?; + let client = self.common.create_client(&config)?; let record_id = RecordId::from(self.record_id); println!( diff --git a/src/commands/reset.rs b/src/commands/reset.rs index c16c1ed9..b6e6bffb 100644 --- a/src/commands/reset.rs +++ b/src/commands/reset.rs @@ -8,31 +8,17 @@ pub struct ResetCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// Whether to reset all registries. - #[clap(long)] - pub all: bool, - /// Whether to reset namespace mappings - #[clap(long)] - pub namespaces: bool, } impl ResetCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let client = self.common.create_client(&config, None).await?; + let client = self.common.create_client(&config)?; - if self.all { - println!("resetting local data for all registries..."); - client.reset_registry(true).await?; - } else { - println!("resetting local data for registry `{}`...", client.url()); - client.reset_registry(false).await?; - } - - if self.namespaces { - client.reset_namespaces().await?; - } + println!("resetting local registry data..."); + client.reset_registry().await?; + client.reset_namespaces().await?; Ok(()) } diff --git a/src/commands/update.rs b/src/commands/update.rs index 4c9b8376..9bc8c29e 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,31 +1,23 @@ -use super::{CommonOptions, Retry}; +use super::CommonOptions; use anyhow::Result; -use clap::{ArgAction, Args}; +use clap::Args; -/// Update all local package logs for a registry. +/// Update all local package logs. #[derive(Args)] pub struct UpdateCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - - /// The common command options. - #[clap(short, long, value_name = "ALL", action = ArgAction::SetTrue)] - pub all: bool, } impl UpdateCommand { /// Executes the command. - pub async fn exec(self, retry: Option) -> Result<()> { + pub async fn exec(self) -> Result<()> { let config = self.common.read_config()?; - let mut client = self.common.create_client(&config, retry).await?; + let client = self.common.create_client(&config)?; println!("updating package logs to the latest available versions..."); - if self.all { - client.update_all().await?; - } else { - client.update().await?; - } + client.update().await?; Ok(()) } diff --git a/tests/client.rs b/tests/client.rs index 20a26db9..e61a4ec1 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -37,9 +37,8 @@ async fn client_incrementally_fetches() -> Result<()> { ) .await?; - // Here we don't wait for a single publish operation to complete, except for the last one - // If the last one is accepted, it implies that all the previous ones were accepted as well let name = PackageName::new(PACKAGE_NAME)?; + let mut head = client .publish_with_info( &signing_key, @@ -51,6 +50,12 @@ async fn client_incrementally_fetches() -> Result<()> { ) .await?; + client + .wait_for_publish(&name, &head, Duration::from_millis(100)) + .await?; + + // Here we don't wait for a single publish operation to complete, except for the last one + // If the last one is accepted, it implies that all the previous ones were accepted as well for i in 1..=RELEASE_COUNT { head = client .publish_with_info( @@ -84,18 +89,21 @@ async fn client_incrementally_fetches() -> Result<()> { client.update().await?; // Fetch the package log - client.upsert([&name]).await?; - - // Ensure that the package is in the packages list - let packages = client.registry().load_packages().await?; - assert_eq!(packages[0].name.as_ref(), PACKAGE_NAME); + let package = client.package(&name).await?; // Ensure the package log exists and has releases with all with the same digest - let package = client - .registry() - .load_package(client.get_warg_registry(), &name) - .await? - .context("package does not exist in client storage")?; + assert_eq!( + package.name, + client + .registry() + .load_package( + client.get_warg_registry(name.namespace()).await?.as_ref(), + &name, + ) + .await? + .context("package does not exist in client storage")? + .name + ); let mut count = 0; for release in package.state.releases() { diff --git a/tests/depsolve.rs b/tests/depsolve.rs index 25ec0102..b3e33f07 100644 --- a/tests/depsolve.rs +++ b/tests/depsolve.rs @@ -77,9 +77,8 @@ async fn depsolve() -> Result<()> { ) .await?; - client.update().await?; client - .upsert([ + .fetch_packages([ &PackageName::new("test:add")?, &PackageName::new("test:inc")?, &PackageName::new("test:five")?, @@ -89,7 +88,10 @@ async fn depsolve() -> Result<()> { let info = client .registry() - .load_package(client.get_warg_registry(), &PackageName::new("test:meet")?) + .load_package( + client.get_warg_registry("test").await?.as_ref(), + &PackageName::new("test:meet")?, + ) .await? .context("package does not exist in client storage")?; @@ -147,7 +149,7 @@ async fn publish_package( name: name.clone(), head: Some(head), entries: vec![PublishEntry::Release { - version: format!("1.0.0").parse().unwrap(), + version: "1.0.0".to_string().parse().unwrap(), content: add_digest.clone(), }], }, diff --git a/tests/memory/mod.rs b/tests/memory/mod.rs index 9747d796..09d85433 100644 --- a/tests/memory/mod.rs +++ b/tests/memory/mod.rs @@ -17,7 +17,7 @@ async fn it_publishes_a_component() -> Result<()> { // There should be two log entries in the registry let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; assert_eq!( ts_checkpoint.as_ref().checkpoint.log_length, 2, @@ -36,7 +36,7 @@ async fn it_yanks_a_package() -> Result<()> { // There should be three entries in the registry let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; assert_eq!( ts_checkpoint.as_ref().checkpoint.log_length, 3, @@ -53,7 +53,7 @@ async fn it_publishes_a_wit_package() -> Result<()> { // There should be two log entries in the registry let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; assert_eq!( ts_checkpoint.as_ref().checkpoint.log_length, 2, diff --git a/tests/postgres/mod.rs b/tests/postgres/mod.rs index 8ca146fc..645ddce4 100644 --- a/tests/postgres/mod.rs +++ b/tests/postgres/mod.rs @@ -65,7 +65,7 @@ async fn it_works_with_postgres() -> TestResult { // There should be two log entries in the registry let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; assert_eq!( ts_checkpoint.as_ref().checkpoint.log_length, packages.len() as RegistryLen + 2, /* publishes + initial checkpoint + yank */ @@ -83,7 +83,7 @@ async fn it_works_with_postgres() -> TestResult { packages.push(PackageName::new("test:unknown-key")?); let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; assert_eq!( ts_checkpoint.as_ref().checkpoint.log_length, packages.len() as RegistryLen + 2, /* publishes + initial checkpoint + yank*/ @@ -96,7 +96,7 @@ async fn it_works_with_postgres() -> TestResult { fs::remove_dir_all(root.join("registries"))?; let client = create_client(&config)?; - client.upsert(packages.iter()).await?; + client.fetch_packages(packages.iter()).await?; // Finally, after a restart, ensure the packages can be downloaded for package in packages { diff --git a/tests/server.rs b/tests/server.rs index 755ec986..9d0abe29 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -17,7 +17,7 @@ use warg_api::v1::{ }; use warg_client::{ api, - storage::{PublishEntry, PublishInfo, RegistryStorage}, + storage::{PublishEntry, PublishInfo}, ClientError, Config, }; use warg_crypto::{ @@ -41,7 +41,7 @@ mod postgres; async fn test_initial_checkpoint(config: &Config) -> Result<()> { let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; let checkpoint = &ts_checkpoint.as_ref().checkpoint; // There should be only a single log entry (the initial operator log entry) @@ -83,8 +83,6 @@ async fn test_component_publishing(config: &Config) -> Result<()> { ) .await?; - // Assert that the package can be downloaded - client.upsert([&name]).await?; let download = client .download(&name, &PACKAGE_VERSION.parse()?) .await? @@ -149,8 +147,6 @@ async fn test_package_yanking(config: &Config) -> Result<()> { .wait_for_publish(&name, &record_id, Duration::from_millis(100)) .await?; - // Assert that the package is yanked - client.upsert([&name]).await?; let opt = client.download(&name, &PACKAGE_VERSION.parse()?).await?; assert!(opt.is_none(), "expected no download, got {opt:?}"); Ok(()) @@ -173,8 +169,6 @@ async fn test_wit_publishing(config: &Config) -> Result<()> { ) .await?; - // Assert that the package can be downloaded - client.upsert([&name]).await?; let download = client .download(&name, &PACKAGE_VERSION.parse()?) .await? @@ -409,12 +403,7 @@ async fn test_custom_content_url(config: &Config) -> Result<()> { ) .await?; - client.upsert([&name]).await?; - let package = client - .registry() - .load_package(client.get_warg_registry(), &name) - .await? - .expect("expected the package to exist"); + let package = client.package(&name).await?; package .state .release(&Version::parse(PACKAGE_VERSION)?) @@ -422,7 +411,7 @@ async fn test_custom_content_url(config: &Config) -> Result<()> { // Look up the content URL for the record let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ContentSourcesResponse { content_sources } = client.content_sources(&digest).await?; + let ContentSourcesResponse { content_sources } = client.content_sources(None, &digest).await?; assert_eq!(content_sources.len(), 1); let sources = content_sources .get(&digest) @@ -484,7 +473,7 @@ async fn test_fetch_package_names(config: &Config) -> Result<()> { async fn test_get_ledger(config: &Config) -> Result<()> { let client = api::Client::new(config.home_url.as_ref().unwrap(), None)?; - let ts_checkpoint = client.latest_checkpoint().await?; + let ts_checkpoint = client.latest_checkpoint(None).await?; let checkpoint = &ts_checkpoint.as_ref().checkpoint; let url = Url::parse(config.home_url.as_ref().unwrap())? diff --git a/tests/support/mod.rs b/tests/support/mod.rs index b69b9fb2..97d476a2 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -167,6 +167,8 @@ pub async fn spawn_server( namespace_map_path: Some(root.join("namespaces")), keys: IndexSet::new(), keyring_auth: false, + ignore_federation_hints: false, + auto_accept_federation_hints: false, }; Ok((instance, config)) From ac901b93ea5ae3aec9b23a50136670bc0e07c1b8 Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Thu, 2 May 2024 16:30:30 -0500 Subject: [PATCH 2/6] upgraded protox version; dropped protoc compilation from github action --- .github/workflows/main.yml | 12 -------- Cargo.lock | 63 +++++++++++++++++++++++++++++--------- Cargo.toml | 2 +- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd512156..846b7e5b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,10 +21,6 @@ jobs: - name: Install Rust run: rustup update stable --no-self-update && rustup default stable && rustup target add wasm32-wasi && rustup target add wasm32-unknown-unknown shell: bash - - name: Install Protobuf Compiler - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Build all crates run: cargo build --all --features warg-server/debug - name: Run all tests @@ -41,10 +37,6 @@ jobs: - name: Install Rust run: rustup update stable --no-self-update && rustup default stable && rustup target add wasm32-wasi && rustup target add wasm32-unknown-unknown shell: bash - - name: Install Protobuf Compiler - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install diesel-cli run: cargo install diesel_cli - name: Build all crates @@ -59,10 +51,6 @@ jobs: - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable --no-self-update && rustup default stable - - name: Install Protobuf Compiler - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install warg CLI run: cargo install --locked --path . diff --git a/Cargo.lock b/Cargo.lock index c3b7e232..87a5df91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2349,7 +2349,16 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" dependencies = [ - "logos-derive", + "logos-derive 0.13.0", +] + +[[package]] +name = "logos" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" +dependencies = [ + "logos-derive 0.14.0", ] [[package]] @@ -2366,13 +2375,37 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "logos-codegen" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" +dependencies = [ + "beef", + "fnv", + "lazy_static 1.4.0", + "proc-macro2", + "quote", + "regex-syntax 0.8.2", + "syn 2.0.48", +] + [[package]] name = "logos-derive" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" dependencies = [ - "logos-codegen", + "logos-codegen 0.13.0", +] + +[[package]] +name = "logos-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" +dependencies = [ + "logos-codegen 0.14.0", ] [[package]] @@ -2459,21 +2492,21 @@ dependencies = [ [[package]] name = "miette" -version = "5.10.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ + "cfg-if", "miette-derive", - "once_cell", "thiserror", "unicode-width", ] [[package]] name = "miette-derive" -version = "5.10.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", @@ -3222,11 +3255,11 @@ dependencies = [ [[package]] name = "prost-reflect" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" +checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f" dependencies = [ - "logos", + "logos 0.14.0", "miette", "once_cell", "prost", @@ -3244,9 +3277,9 @@ dependencies = [ [[package]] name = "protox" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bb76c5f6221de491fe2c8f39b106330bbd9762c6511119c07940e10eb9ff11" +checksum = "a29b3c5596eb23a849deba860b53ffd468199d9ad5fe4402a7d55379e16aa2d2" dependencies = [ "bytes", "miette", @@ -3259,11 +3292,11 @@ dependencies = [ [[package]] name = "protox-parse" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4581f441c58863525a3e6bec7b8de98188cf75239a56c725a3e7288450a33f" +checksum = "033b939d76d358f7c32120c86c71f515bae45e64f2bde455200356557276276c" dependencies = [ - "logos", + "logos 0.13.0", "miette", "prost-types", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index b3e0bc31..3f558701 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,5 +142,5 @@ regex = "1" wasm-encoder = "0.41.0" wasm-compose = "0.5.2" wasmparser = "0.121.0" -protox = "0.5.1" +protox = "0.6.0" toml = "0.8.2" From 82c43cdc1e48b633abb45aaacef25ab8ee69465d Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 3 May 2024 10:34:31 -0500 Subject: [PATCH 3/6] fixed doc comment wording --- crates/client/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index 0b356be7..890df10d 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -115,11 +115,11 @@ pub struct Config { #[serde(default)] pub keyring_auth: bool, - /// Whether or ignore registry hints provided by a warg server + /// Ignore registry hints provided by a warg server #[serde(default)] pub ignore_federation_hints: bool, - /// Whether or not to auto accept registry hint or ask the user to confirm + /// Auto accept registry hint or ask the user to confirm #[serde(default)] pub auto_accept_federation_hints: bool, } From fe522f59f0c68d492ca924b59eb852ecde92daf1 Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 3 May 2024 11:20:42 -0500 Subject: [PATCH 4/6] improved printed error messages; ask user if wants to initialize the package if not initialized and doing a package publish --- Cargo.toml | 2 +- crates/api/src/v1/package.rs | 34 +---- crates/client/src/lib.rs | 146 +++++++++++++++----- crates/server/openapi.yaml | 4 +- crates/server/src/api/v1/package.rs | 6 - crates/server/src/datastore/memory.rs | 19 +-- crates/server/src/datastore/mod.rs | 9 -- crates/server/src/datastore/postgres/mod.rs | 19 +-- src/bin/warg.rs | 42 +++++- src/commands/download.rs | 11 +- tests/server.rs | 2 +- 11 files changed, 162 insertions(+), 132 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f558701..08e71555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ wat = "1.0.85" wasmprinter = "0.2.78" dialoguer = { workspace = true } itertools = "0.12.1" -secrecy= { workspace = true } +secrecy = { workspace = true } [dev-dependencies] reqwest = { workspace = true } diff --git a/crates/api/src/v1/package.rs b/crates/api/src/v1/package.rs index 15097a7d..74b8b307 100644 --- a/crates/api/src/v1/package.rs +++ b/crates/api/src/v1/package.rs @@ -127,12 +127,6 @@ pub enum PackageError { /// The provided package's namespace is imported from another registry. #[error("namespace `{0}` is an imported namespace from another registry")] NamespaceImported(String), - /// The provided package's namespace conflicts with an existing namespace where the name only differs by case. - #[error("namespace conflicts with existing namespace `{0}`; package namespaces must be unique in a case insensitive way")] - NamespaceConflict(String), - /// The provided package name conflicts with an existing package where the name only differs by case. - #[error("the package conflicts with existing package name `{0}`; package names must be unique in a case insensitive way")] - PackageNameConflict(PackageName), /// The operation was not authorized by the registry. #[error("unauthorized operation: {0}")] Unauthorized(String), @@ -156,13 +150,9 @@ impl PackageError { /// Returns the HTTP status code of the error. pub fn status(&self) -> u16 { match self { - // Note: this is 403 and not a 401 as the registry does not use - // HTTP authentication. - Self::Unauthorized { .. } => 403, + Self::Unauthorized { .. } => 401, Self::LogNotFound(_) | Self::RecordNotFound(_) | Self::NamespaceNotDefined(_) => 404, - Self::NamespaceImported(_) - | Self::NamespaceConflict(_) - | Self::PackageNameConflict(_) => 409, + Self::NamespaceImported(_) => 409, Self::RecordNotSourcing => 405, Self::Rejection(_) => 422, Self::NotSupported(_) => 501, @@ -189,7 +179,7 @@ where ::Owned: Serialize + for<'b> Deserialize<'b>, { Unauthorized { - status: Status<403>, + status: Status<401>, message: Cow<'a, str>, }, NotFound { @@ -225,7 +215,7 @@ impl Serialize for PackageError { fn serialize(&self, serializer: S) -> Result { match self { Self::Unauthorized(message) => RawError::Unauthorized::<()> { - status: Status::<403>, + status: Status::<401>, message: Cow::Borrowed(message), } .serialize(serializer), @@ -253,18 +243,6 @@ impl Serialize for PackageError { id: Cow::Borrowed(namespace), } .serialize(serializer), - Self::NamespaceConflict(existing) => RawError::Conflict { - status: Status::<409>, - ty: EntityType::Namespace, - id: Cow::Borrowed(existing), - } - .serialize(serializer), - Self::PackageNameConflict(existing) => RawError::Conflict { - status: Status::<409>, - ty: EntityType::Name, - id: Cow::Borrowed(existing), - } - .serialize(serializer), Self::RecordNotSourcing => RawError::RecordNotSourcing::<()> { status: Status::<405>, } @@ -322,11 +300,7 @@ impl<'de> Deserialize<'de> for PackageError { )), }, RawError::Conflict { status: _, ty, id } => match ty { - EntityType::Namespace => Ok(Self::NamespaceConflict(id.into_owned())), EntityType::NamespaceImport => Ok(Self::NamespaceImported(id.into_owned())), - EntityType::Name => Ok(Self::PackageNameConflict( - PackageName::new(id.into_owned()).unwrap(), - )), _ => Err(serde::de::Error::invalid_value( Unexpected::Enum, &"a valid entity type", diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 50768373..7374c705 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -1,7 +1,7 @@ //! A client library for Warg component registries. #![deny(missing_docs)] -use crate::storage::PackageInfo; +use crate::storage::{PackageInfo, PublishEntry}; use anyhow::{anyhow, Context, Result}; use dialoguer::theme::ColorfulTheme; use dialoguer::Confirm; @@ -108,8 +108,7 @@ impl Client Client Client { + Err(ClientError::PackageDoesNotExist { + name, + has_auth_token, + }) => { if !initializing { - return Err(ClientError::MustInitializePackage { name: info.name }); + let prompt = if has_auth_token { + format!( +"Package `{package_name}` does not already exist or you do not have access. +Do you wish to initialize `{package_name}` and publish release y/N\n", +package_name = &info.name, +) + } else { + format!( +"Package `{package_name}` does not already exist or you do not have access. +You may be required to login. Try: `warg login` +Do you wish to initialize `{package_name}` and publish release y/N\n", +package_name = &info.name, +) + }; + if Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(prompt) + .default(false) + .interact() + .unwrap() + { + info.entries.insert(0, PublishEntry::Init); + } else { + return Err(ClientError::MustInitializePackage { + name, + has_auth_token, + }); + } } PackageInfo::new(info.name.clone()) } @@ -353,6 +379,7 @@ impl Client(&package.name); + let record_id = RecordId::package_record::(&record); let record = self .api .publish_package_record( @@ -365,14 +392,26 @@ impl Client { + ClientError::PublishRejected { + name: package.name.clone(), + reason, + record_id, } - }) + } + api::ClientError::Package(PackageError::Unauthorized(reason)) => { + ClientError::Unauthorized(reason) + } + e => { + ClientError::translate_log_not_found(e, self.api.auth_token().is_some(), |id| { + if id == &log_id { + Some(package.name.clone()) + } else { + None + } + }) + } })?; // TODO: parallelize this @@ -407,6 +446,9 @@ impl Client { + ClientError::Unauthorized(reason) + } _ => e.into(), })?; } @@ -612,6 +654,7 @@ impl Client Client { if let Some(name) = packages.get(log_id).map(|p| p.name.clone()) { - Err(ClientError::PackageDoesNotExist { name }) + Err(ClientError::PackageDoesNotExist { + name, + has_auth_token, + }) } else { Err(ClientError::Api(err)) } @@ -705,12 +751,16 @@ current_registry = registry_domain.map(|d| d.as_str()).unwrap_or(&self.url().saf } else { Err(ClientError::PackageDoesNotExist { name: package_name.clone(), + has_auth_token, }) } } _ => { if let Some(name) = packages.get(log_id).map(|p| p.name.clone()) { - Err(ClientError::PackageDoesNotExist { name }) + Err(ClientError::PackageDoesNotExist { + name, + has_auth_token, + }) } else { Err(ClientError::Api(err)) } @@ -916,14 +966,13 @@ current_registry = registry_domain.map(|d| d.as_str()).unwrap_or(&self.url().saf } while let Some((registry_domain, packages)) = federated_packages.pop() { - for (registry_domain, mut packages) in self + for (registry_domain, packages) in self .update_packages_and_return_federated_packages(registry_domain.as_ref(), packages) .await? .into_iter() { if let Some(package_set) = federated_packages.get_mut(®istry_domain) { - package_set.reserve(packages.len()); - package_set.append(&mut packages); + package_set.extend(packages); } else { federated_packages.insert(registry_domain, packages); } @@ -983,14 +1032,23 @@ current_registry = registry_domain.map(|d| d.as_str()).unwrap_or(&self.url().saf .api .get_package_record(registry_domain, log_id, record_id) .await - .map_err(|e| { - ClientError::translate_log_not_found(e, |id| { - if id == log_id { - Some(package.clone()) - } else { - None + .map_err(|e| match e { + api::ClientError::Package(PackageError::Rejection(reason)) => { + ClientError::PublishRejected { + name: package.clone(), + reason, + record_id: record_id.clone(), } - }) + } + e => { + ClientError::translate_log_not_found(e, self.api.auth_token().is_some(), |id| { + if id == log_id { + Some(package.clone()) + } else { + None + } + }) + } })?; Ok(record) } @@ -1124,15 +1182,6 @@ pub struct PackageDownload { /// Represents an error returned by Warg registry clients. #[derive(Debug, Error)] pub enum ClientError { - /// Similar Namespace - #[error("Namespace `{namespace}` not found in operator log but found namespace `{e}`, which has alternative casing.")] - SimilarNamespace { - /// Namespace - namespace: String, - /// Provided Error - e: String, - }, - /// No home registry registry server URL is configured. #[error("no home registry registry server URL is configured")] NoHomeRegistryUrl, @@ -1145,6 +1194,10 @@ pub enum ClientError { #[error("clear content cache failed")] ClearContentCacheFailed, + /// Unauthorized rejection + #[error("unauthorized: {0}")] + Unauthorized(String), + /// Checkpoint signature failed verification #[error("invalid checkpoint signature")] InvalidCheckpointSignature, @@ -1179,6 +1232,8 @@ pub enum ClientError { MustInitializePackage { /// The name of the package that must be initialized. name: PackageName, + /// Client has authentication credentials. + has_auth_token: bool, }, /// There is no publish operation in progress. @@ -1197,6 +1252,8 @@ pub enum ClientError { PackageDoesNotExist { /// The missing package. name: PackageName, + /// Client has authentication credentials. + has_auth_token: bool, }, /// The package version does not exist. @@ -1208,6 +1265,15 @@ pub enum ClientError { name: PackageName, }, + /// The package version requirement does not exist. + #[error("version that satisfies requirement `{version}` was not found for package `{name}`")] + PackageVersionRequirementDoesNotExist { + /// The missing version requirement of the package. + version: VersionReq, + /// The package with the missing version. + name: PackageName, + }, + /// The package failed validation. #[error("package `{name}` failed validation: {inner}")] PackageValidationFailed { @@ -1285,13 +1351,17 @@ pub enum ClientError { impl ClientError { fn translate_log_not_found( e: api::ClientError, + has_auth_token: bool, lookup: impl Fn(&LogId) -> Option, ) -> Self { match &e { api::ClientError::Fetch(FetchError::LogNotFound(id)) | api::ClientError::Package(PackageError::LogNotFound(id)) => { if let Some(name) = lookup(id) { - return Self::PackageDoesNotExist { name }; + return Self::PackageDoesNotExist { + name, + has_auth_token, + }; } } _ => {} diff --git a/crates/server/openapi.yaml b/crates/server/openapi.yaml index e79845db..5f0c67a3 100644 --- a/crates/server/openapi.yaml +++ b/crates/server/openapi.yaml @@ -222,9 +222,9 @@ paths: application/json: schema: "$ref": "#/components/schemas/PackageRecord" - "403": + "401": description: | - The key used to sign the record was not authorized to publish a record to the log. + Unauthorized rejection from the registry. headers: Warg-Registry: $ref: "#/components/headers/WargRegistryHeader" diff --git a/crates/server/src/api/v1/package.rs b/crates/server/src/api/v1/package.rs index 72abec50..25881701 100644 --- a/crates/server/src/api/v1/package.rs +++ b/crates/server/src/api/v1/package.rs @@ -145,13 +145,7 @@ impl From for PackageApiError { PackageError::Unauthorized(e.to_string()) } DataStoreError::PackageNamespaceNotDefined(id) => PackageError::NamespaceNotDefined(id), - DataStoreError::PackageNamespaceConflict { existing, .. } => { - PackageError::NamespaceConflict(existing) - } DataStoreError::PackageNamespaceImported(id) => PackageError::NamespaceImported(id), - DataStoreError::PackageNameConflict { existing, .. } => { - PackageError::PackageNameConflict(existing) - } // Other errors are internal server errors e => { tracing::error!("unexpected data store error: {e}"); diff --git a/crates/server/src/datastore/memory.rs b/crates/server/src/datastore/memory.rs index d8553ea3..6bc21276 100644 --- a/crates/server/src/datastore/memory.rs +++ b/crates/server/src/datastore/memory.rs @@ -74,7 +74,6 @@ struct State { operators: IndexMap>, packages: IndexMap>, package_names: IndexMap>, - package_names_lowercase: IndexMap, checkpoints: IndexMap>, records: IndexMap>, log_leafs: IndexMap, @@ -313,10 +312,6 @@ impl DataStore for MemoryDataStore { state .package_names .insert(log_id.clone(), Some(package_name.clone())); - state.package_names_lowercase.insert( - package_name.as_ref().to_ascii_lowercase(), - package_name.clone(), - ); assert!(prev.is_none()); Ok(()) @@ -749,19 +744,7 @@ impl DataStore for MemoryDataStore { } } - // verify package name is unique in a case insensitive way - match state - .package_names_lowercase - .get(&package_name.as_ref().to_ascii_lowercase()) - { - Some(existing) if existing.as_ref() != package_name.as_ref() => { - Err(DataStoreError::PackageNameConflict { - name: package_name.clone(), - existing: existing.clone(), - }) - } - _ => Ok(()), - } + Ok(()) } async fn verify_timestamped_checkpoint_signature( diff --git a/crates/server/src/datastore/mod.rs b/crates/server/src/datastore/mod.rs index 5b5297e0..19c14eac 100644 --- a/crates/server/src/datastore/mod.rs +++ b/crates/server/src/datastore/mod.rs @@ -54,15 +54,6 @@ pub enum DataStoreError { #[error("the package record was invalid: {0}")] PackageValidationFailed(#[from] package::ValidationError), - #[error("the package `{name}` conflicts with package `{existing}`; package names must be unique in a case insensitive way")] - PackageNameConflict { - name: PackageName, - existing: PackageName, - }, - - #[error("the package namespace `{namespace}` conflicts with existing namespace `{existing}`; package namespaces must be unique in a case insensitive way")] - PackageNamespaceConflict { namespace: String, existing: String }, - #[error("the package namespace `{0}` is not defined")] PackageNamespaceNotDefined(String), diff --git a/crates/server/src/datastore/postgres/mod.rs b/crates/server/src/datastore/postgres/mod.rs index af647cde..cbb06deb 100644 --- a/crates/server/src/datastore/postgres/mod.rs +++ b/crates/server/src/datastore/postgres/mod.rs @@ -991,24 +991,7 @@ impl DataStore for PostgresDataStore { } } - // verify package name is unique in a case insensitive way - match schema::logs::table - .select(schema::logs::name) - .filter( - lower(schema::logs::name).eq(TextRef(&package_name.as_ref().to_ascii_lowercase())), - ) - .first::>(&mut conn) - .await - .optional()? - { - Some(Some(name)) if name != package_name.as_ref() => { - Err(DataStoreError::PackageNameConflict { - name: package_name.clone(), - existing: PackageName::new(name).unwrap(), - }) - } - _ => Ok(()), - } + Ok(()) } async fn verify_timestamped_checkpoint_signature( diff --git a/src/bin/warg.rs b/src/bin/warg.rs index 02d3b67e..8d66d08a 100644 --- a/src/bin/warg.rs +++ b/src/bin/warg.rs @@ -74,15 +74,51 @@ async fn main() -> Result<()> { pub async fn describe_client_error(e: &ClientError) -> Result<()> { match e { ClientError::NoHomeRegistryUrl => { - eprintln!("error: {e}; use the `config` subcommand to set a default URL"); + eprintln!("Registry not set. Use `config` or `login` subcommand to set registry."); } ClientError::PackageValidationFailed { name, inner } => { - eprintln!("error: the log for package `{name}` is invalid: {inner}") + eprintln!("The log for package `{name}` validation failed: {inner}") } ClientError::PackageLogEmpty { name } => { - eprintln!("error: the log for package `{name}` is empty (the registry could be lying)"); + eprintln!("The log for package `{name}` is empty (the registry could be lying)"); eprintln!("see issue https://github.com/bytecodealliance/registry/issues/66"); } + ClientError::PackageDoesNotExist { + name, + has_auth_token, + } => { + eprintln!("Package `{name}` was not found or you do not have access."); + if !has_auth_token { + eprintln!("You may be required to login. Try: `warg login`"); + } + } + ClientError::PackageVersionDoesNotExist { name, version } => { + eprintln!("Package `{name}` version `{version}` was not found.") + } + ClientError::PackageVersionRequirementDoesNotExist { name, version } => { + eprintln!( + "Package `{name}` version that satisfies requirement `{version}` was not found." + ) + } + ClientError::MustInitializePackage { + name, + has_auth_token, + } => { + eprintln!("Package `{name}` is not initialized or you do not have access."); + if !has_auth_token { + eprintln!("You may be required to login. Try: `warg login`"); + } + eprintln!("To initialize package: `warg publish init {name}`"); + } + ClientError::CannotInitializePackage { name } => { + eprintln!("Package `{name}` is already initialized.") + } + ClientError::PublishRejected { name, reason, .. } => { + eprintln!("Package `{name}` publish rejected: {reason}") + } + ClientError::Unauthorized(reason) => { + eprintln!("Unauthorized: {reason}") + } _ => { eprintln!("error: {e}") } diff --git a/src/commands/download.rs b/src/commands/download.rs index 7e08b8ba..b6301286 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,6 +1,7 @@ use super::CommonOptions; -use anyhow::{anyhow, Result}; +use anyhow::Result; use clap::Args; +use warg_client::ClientError; use warg_protocol::{registry::PackageName, VersionReq}; /// Download a warg registry package. @@ -35,11 +36,9 @@ impl DownloadCommand { let res = client .download(&self.name, &version) .await? - .ok_or_else(|| { - anyhow!( - "a version of package `{name}` that satisfies `{version}` was not found", - name = self.name, - ) + .ok_or_else(|| ClientError::PackageVersionRequirementDoesNotExist { + name: self.name.clone(), + version, })?; println!( diff --git a/tests/server.rs b/tests/server.rs index 9d0abe29..e2b7a6ff 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -375,7 +375,7 @@ async fn test_invalid_signature(config: &Config) -> Result<()> { let body = response.text().await?; assert_eq!( status, - StatusCode::FORBIDDEN, + StatusCode::UNAUTHORIZED, "unexpected response from server: {status}\n{body}", ); assert!( From df2efa7548445afced267999ab85b74fff332920 Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 3 May 2024 11:55:04 -0500 Subject: [PATCH 5/6] revised tracing::debug!() --- crates/client/src/api.rs | 116 +++++++++++++++++---------------------- crates/client/src/lib.rs | 38 ++++++------- 2 files changed, 66 insertions(+), 88 deletions(-) diff --git a/crates/client/src/api.rs b/crates/client/src/api.rs index 264e292f..5f1441c0 100644 --- a/crates/client/src/api.rs +++ b/crates/client/src/api.rs @@ -222,13 +222,11 @@ impl Client { registry_domain: Option<&RegistryDomain>, ) -> Result, ClientError> { let url = self.url.join(paths::fetch_checkpoint()); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "getting latest checkpoint at `{url}` with registry header: {registry_header}" - ); - } else { - tracing::debug!("getting latest checkpoint at `{url}`"); - } + tracing::debug!( + desc = "getting latest checkpoint", + url = url, + registry_header = ?registry_domain + ); into_result::<_, FetchError>( self.client .get(url) @@ -247,13 +245,11 @@ impl Client { request: SerdeEnvelope, ) -> Result { let url = self.url.join(paths::verify_checkpoint()); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "verifying checkpoint at `{url}` with registry header: {registry_header}" - ); - } else { - tracing::debug!("verifying checkpoint at `{url}`"); - } + tracing::debug!( + desc = "verifying checkpoint", + url = url, + registry_header = ?registry_domain + ); let response = self .client @@ -273,11 +269,11 @@ impl Client { request: FetchLogsRequest<'_>, ) -> Result { let url = self.url.join(paths::fetch_logs()); - if let Some(registry_header) = registry_domain { - tracing::debug!("fetching logs at `{url}` with registry header: {registry_header}"); - } else { - tracing::debug!("fetching logs at `{url}`"); - } + tracing::debug!( + desc = "fetching logs", + url = url, + registry_header = ?registry_domain + ); let response = self .client .post(&url) @@ -305,14 +301,11 @@ impl Client { request: FetchPackageNamesRequest<'_>, ) -> Result { let url = self.url.join(paths::fetch_package_names()); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "fetching package names at `{url}` with registry header: {registry_header}" - ); - } else { - tracing::debug!("fetching package names at `{url}`"); - } - + tracing::debug!( + desc = "fetching package names", + url = url, + registry_header = ?registry_domain + ); let response = self .client .post(url) @@ -330,14 +323,11 @@ impl Client { registry_domain: Option<&RegistryDomain>, ) -> Result { let url = self.url.join(paths::ledger_sources()); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "getting ledger sources at `{url}` with registry header: {registry_header}" - ); - } else { - tracing::debug!("getting ledger sources at `{url}`"); - } - + tracing::debug!( + desc = "getting ledger sources", + url = url, + registry_header = ?registry_domain + ); into_result::<_, LedgerError>( self.client .get(url) @@ -357,18 +347,12 @@ impl Client { request: PublishRecordRequest<'_>, ) -> Result { let url = self.url.join(&paths::publish_package_record(log_id)); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "appending record to package `{name}` at `{url}` with registry header: {registry_header}", - name = request.package_name - ); - } else { - tracing::debug!( - "appending record to package `{name}` at `{url}`", - name = request.package_name - ); - } - + tracing::debug!( + desc = "publishing to package", + log_id = log_id.to_string(), + url = url, + registry_header = ?registry_domain + ); let response = self .client .post(url) @@ -388,12 +372,13 @@ impl Client { record_id: &RecordId, ) -> Result { let url = self.url.join(&paths::package_record(log_id, record_id)); - if let Some(registry_header) = registry_domain { - tracing::debug!("getting record `{record_id}` for package `{log_id}` at `{url}` with registry header: {registry_header}"); - } else { - tracing::debug!("getting record `{record_id}` for package `{log_id}` at `{url}`"); - } - + tracing::debug!( + desc = "getting package record", + log_id = log_id.to_string(), + record_id = record_id.to_string(), + url = url, + registry_header = ?registry_domain + ); into_result::<_, PackageError>( self.client .get(url) @@ -412,12 +397,12 @@ impl Client { digest: &AnyHash, ) -> Result { let url = self.url.join(&paths::content_sources(digest)); - if let Some(registry_header) = registry_domain { - tracing::debug!("getting content sources for digest `{digest}` at `{url}` with registry header: {registry_header}"); - } else { - tracing::debug!("getting content sources for digest `{digest}` at `{url}`"); - } - + tracing::debug!( + desc = "getting content sources for digest", + digest = digest.to_string(), + url = url, + registry_header = ?registry_domain + ); into_result::<_, ContentError>( self.client .get(url) @@ -476,14 +461,11 @@ impl Client { leafs: &[LogLeaf], ) -> Result<(), ClientError> { let url = self.url.join(paths::prove_inclusion()); - if let Some(registry_header) = registry_domain { - tracing::debug!( - "proving checkpoint inclusion at `{url}` with registry header: {registry_header}" - ); - } else { - tracing::debug!("proving checkpoint inclusion at `{url}`"); - } - + tracing::debug!( + desc = "proving checkpoint inclusion", + url = url, + registry_header = ?registry_domain + ); let response = into_result::( self.client .post(url) diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 7374c705..2494814e 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -532,11 +532,12 @@ package_name = &info.name, let registry_domain = self.get_warg_registry(package.namespace()).await?; - if let Some(ref registry_header) = registry_domain { - tracing::info!("downloading package `{package}` with requirement `{requirement}` with registry header: {registry_header}"); - } else { - tracing::info!("downloading package `{package}` with requirement `{requirement}`"); - } + tracing::debug!( + desc = "downloading package", + package = package.as_ref(), + version_requirement = requirement.to_string(), + registry_header = ?registry_domain + ); match info.state.find_latest_release(requirement) { Some(release) => { @@ -575,11 +576,12 @@ package_name = &info.name, let registry_domain = self.get_warg_registry(package.namespace()).await?; - if let Some(ref registry_header) = registry_domain { - tracing::info!("downloading version {version} of package `{package}` with registry header: {registry_header}"); - } else { - tracing::info!("downloading version {version} of package `{package}`"); - } + tracing::debug!( + desc = "downloading package version", + package = package.as_ref(), + version = version.to_string(), + registry_header = ?registry_domain + ); let release = info.state @@ -613,17 +615,11 @@ package_name = &info.name, let ts_checkpoint = self.api.latest_checkpoint(registry_domain).await?; let checkpoint = &ts_checkpoint.as_ref().checkpoint; - if let Some(registry_header) = registry_domain { - tracing::info!( - "updating to checkpoint log length `{}` with registry header: {registry_header}", - checkpoint.log_length - ); - } else { - tracing::info!( - "updating to checkpoint log length `{}`", - checkpoint.log_length - ); - } + tracing::debug!( + desc = "updating to checkpoint", + log_length = checkpoint.log_length, + registry_header = ?registry_domain + ); // operator log info let mut operator = self From c8a4abddd407262245ffe9f9fe4daae1d6de7ecb Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 3 May 2024 15:32:54 -0500 Subject: [PATCH 6/6] simplified the tracing::debug! --- crates/client/src/api.rs | 54 ++++++++++++++++++++-------------------- crates/client/src/lib.rs | 12 ++++----- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/crates/client/src/api.rs b/crates/client/src/api.rs index 5f1441c0..91576345 100644 --- a/crates/client/src/api.rs +++ b/crates/client/src/api.rs @@ -223,9 +223,9 @@ impl Client { ) -> Result, ClientError> { let url = self.url.join(paths::fetch_checkpoint()); tracing::debug!( - desc = "getting latest checkpoint", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "getting latest checkpoint", ); into_result::<_, FetchError>( self.client @@ -246,9 +246,9 @@ impl Client { ) -> Result { let url = self.url.join(paths::verify_checkpoint()); tracing::debug!( - desc = "verifying checkpoint", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "verifying checkpoint", ); let response = self @@ -270,9 +270,9 @@ impl Client { ) -> Result { let url = self.url.join(paths::fetch_logs()); tracing::debug!( - desc = "fetching logs", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "fetching logs", ); let response = self .client @@ -302,9 +302,9 @@ impl Client { ) -> Result { let url = self.url.join(paths::fetch_package_names()); tracing::debug!( - desc = "fetching package names", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "fetching package names", ); let response = self .client @@ -324,9 +324,9 @@ impl Client { ) -> Result { let url = self.url.join(paths::ledger_sources()); tracing::debug!( - desc = "getting ledger sources", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "getting ledger sources", ); into_result::<_, LedgerError>( self.client @@ -348,10 +348,10 @@ impl Client { ) -> Result { let url = self.url.join(&paths::publish_package_record(log_id)); tracing::debug!( - desc = "publishing to package", log_id = log_id.to_string(), - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "publishing to package", ); let response = self .client @@ -373,11 +373,11 @@ impl Client { ) -> Result { let url = self.url.join(&paths::package_record(log_id, record_id)); tracing::debug!( - desc = "getting package record", log_id = log_id.to_string(), record_id = record_id.to_string(), - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "getting package record", ); into_result::<_, PackageError>( self.client @@ -398,10 +398,10 @@ impl Client { ) -> Result { let url = self.url.join(&paths::content_sources(digest)); tracing::debug!( - desc = "getting content sources for digest", digest = digest.to_string(), - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "getting content sources for digest", ); into_result::<_, ContentError>( self.client @@ -462,9 +462,9 @@ impl Client { ) -> Result<(), ClientError> { let url = self.url.join(paths::prove_inclusion()); tracing::debug!( - desc = "proving checkpoint inclusion", - url = url, - registry_header = ?registry_domain + url, + registry_header = ?registry_domain, + "proving checkpoint inclusion", ); let response = into_result::( self.client diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 2494814e..c02ea7ee 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -533,10 +533,10 @@ package_name = &info.name, let registry_domain = self.get_warg_registry(package.namespace()).await?; tracing::debug!( - desc = "downloading package", package = package.as_ref(), version_requirement = requirement.to_string(), - registry_header = ?registry_domain + registry_header = ?registry_domain, + "downloading", ); match info.state.find_latest_release(requirement) { @@ -577,10 +577,10 @@ package_name = &info.name, let registry_domain = self.get_warg_registry(package.namespace()).await?; tracing::debug!( - desc = "downloading package version", package = package.as_ref(), version = version.to_string(), - registry_header = ?registry_domain + registry_header = ?registry_domain, + "downloading exact version", ); let release = @@ -616,9 +616,9 @@ package_name = &info.name, let checkpoint = &ts_checkpoint.as_ref().checkpoint; tracing::debug!( - desc = "updating to checkpoint", log_length = checkpoint.log_length, - registry_header = ?registry_domain + registry_header = ?registry_domain, + "updating to checkpoint", ); // operator log info