diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index 2f7f98e5..02a58e15 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -107,7 +107,7 @@ pub struct Config { #[serde(default, skip_serializing_if = "Option::is_none")] pub namespace_map_path: Option, - /// List of creds availabe in keyring + /// List of creds available in keyring #[serde(default, skip_serializing_if = "IndexSet::is_empty")] pub keys: IndexSet, diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 337aa4df..5ea97b48 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -6,7 +6,7 @@ use crate::storage::PackageInfo; use anyhow::{anyhow, Context, Result}; use bytes::Bytes; use futures_util::{Stream, StreamExt, TryStreamExt}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use reqwest::{Body, IntoUrl}; use secrecy::Secret; use semver::{Version, VersionReq}; @@ -71,6 +71,8 @@ where ignore_federation_hints: bool, auto_accept_federation_hints: bool, disable_interactive: bool, + keyring_backend: Option, + keys: IndexSet, } impl Client { @@ -86,6 +88,8 @@ impl Client, + keys: IndexSet, ) -> ClientResult { let api = api::Client::new(url, auth_token)?; Ok(Self { @@ -96,6 +100,8 @@ impl Client Client, + ) -> ClientResult { + let publish_info = if let Some(publish_info) = publish_info { + publish_info + } else { + self.registry + .load_publish() + .await? + .ok_or(ClientError::NotPublishing)? + }; + + let registry_domain = self + .get_warg_registry(publish_info.name.namespace()) + .await?; + let signing_key = keyring::Keyring::new( + self.keyring_backend + .as_deref() + .unwrap_or(keyring::Keyring::DEFAULT_BACKEND), + )? + .get_signing_key( + registry_domain.map(|domain| domain.to_string()).as_deref(), + &self.keys, + Some(&self.url().to_string()), + )?; + + let res = self.publish_with_info(&signing_key, publish_info).await; + self.registry.store_publish(None).await?; + res + } + /// Submits the provided publish information. /// /// Any publish information in client storage is ignored. @@ -1344,6 +1391,12 @@ impl FileSystemClient { let disable_interactive = cfg!(not(feature = "cli-interactive")) || config.disable_interactive; + let (keyring_backend, keys) = if cfg!(feature = "keyring") { + (config.keyring_backend.clone(), config.keys.clone()) + } else { + (None, IndexSet::new()) + }; + #[cfg(feature = "keyring")] if auth_token.is_none() && config.keyring_auth { auth_token = crate::keyring::Keyring::from_config(config)?.get_auth_token(&url)? @@ -1358,6 +1411,8 @@ impl FileSystemClient { config.ignore_federation_hints, config.auto_accept_federation_hints, disable_interactive, + keyring_backend, + keys, )?)) } @@ -1399,6 +1454,12 @@ impl FileSystemClient { let disable_interactive = cfg!(not(feature = "cli-interactive")) || config.disable_interactive; + let (keyring_backend, keys) = if cfg!(feature = "keyring") { + (config.keyring_backend.clone(), config.keys.clone()) + } else { + (None, IndexSet::new()) + }; + #[cfg(feature = "keyring")] if auth_token.is_none() && config.keyring_auth { auth_token = @@ -1414,6 +1475,8 @@ impl FileSystemClient { config.ignore_federation_hints, config.auto_accept_federation_hints, disable_interactive, + keyring_backend, + keys, ) } diff --git a/src/commands/publish.rs b/src/commands/publish.rs index 3dec35bb..05af2ff0 100644 --- a/src/commands/publish.rs +++ b/src/commands/publish.rs @@ -1,6 +1,7 @@ use super::CommonOptions; use anyhow::{anyhow, bail, Context, Result}; use clap::{Args, Subcommand}; +use dialoguer::{theme::ColorfulTheme, Confirm}; use futures::TryStreamExt; use itertools::Itertools; use std::{future::Future, path::PathBuf, time::Duration}; @@ -275,6 +276,20 @@ pub struct PublishYankCommand { impl PublishYankCommand { /// Executes the command. pub async fn exec(self) -> Result<()> { + if !Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(format!( + "`Yank` revokes a version, making it unavailable. It is permanent and cannot be reversed. +Yank `{version}` of `{package}`?", + version = &self.version, + package = &self.name, + )) + .default(false) + .interact()? + { + println!("Aborted and did not yank."); + return Ok(()); + } + let config = self.common.read_config()?; let client = self.common.create_client(&config)?; let registry_domain = client.get_warg_registry(self.name.namespace()).await?;