Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/piquelctl/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ pub(crate) struct CreateArgs {
#[command(flatten)]
pub(crate) deployment: DeploymentArgs,
/// Skip the confirmation prompt.
#[arg(long)]
#[arg(long, short)]
pub(crate) yes: bool,
}

Expand Down Expand Up @@ -604,7 +604,7 @@ pub(crate) struct ApplyArgs {
pub(crate) expected_generation: Option<u64>,

/// Skip the confirmation prompt.
#[arg(long)]
#[arg(long, short)]
pub(crate) yes: bool,

/// Replace whichever application has the manifest's name now, or create it.
Expand Down Expand Up @@ -650,7 +650,7 @@ pub(crate) struct DeletionFlags {
pub(crate) expected_generation: Option<u64>,

/// Skip the confirmation prompt.
#[arg(long)]
#[arg(long, short)]
pub(crate) yes: bool,

/// Skip the generation check.
Expand Down Expand Up @@ -787,7 +787,7 @@ pub(crate) struct OperationFlags {
#[arg(long, value_name = "GENERATION")]
pub(crate) expected_generation: Option<u64>,
/// Skip the confirmation prompt.
#[arg(long)]
#[arg(long, short)]
pub(crate) yes: bool,
/// Return once the daemon accepts the operation, without waiting for it to end.
#[arg(long)]
Expand Down
2 changes: 1 addition & 1 deletion apps/piquelctl/src/deployments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ pub(crate) struct RestoreArgs<T: Args> {
/// The deployment to restore, as `history` lists it; it must have succeeded.
deployment: String,
/// Skip the interactive confirmation prompt.
#[arg(long)]
#[arg(long, short)]
yes: bool,
/// Return after the daemon accepts the restore.
#[arg(long)]
Expand Down
2 changes: 1 addition & 1 deletion apps/piquelctl/src/editing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub(crate) struct EditFlags {
#[arg(long, conflicts_with = "expected_generation")]
force: bool,
/// Skip the confirmation prompt. You need it when not on a terminal.
#[arg(long)]
#[arg(long, short)]
yes: bool,
}
// Declares edit argument structs: a flattened target followed by one `value`,
Expand Down
2 changes: 1 addition & 1 deletion apps/piquelctl/src/editing/repositories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub(crate) enum WebhookCommand {
/// Application name or stable ID.
app: String,
/// Skip the confirmation prompt. You need it when not on a terminal.
#[arg(long)]
#[arg(long, short)]
yes: bool,
},
}
Expand Down
37 changes: 14 additions & 23 deletions apps/piquelctl/src/environments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,6 @@ pub(crate) enum EnvCommand {
},
/// List, regenerate, or delete an environment's generated secrets.
Secret {
/// Application name or stable ID.
application: String,
/// Environment name or stable ID. You can omit it when the
/// application has exactly one environment.
#[arg(long = "env", value_name = "ENV")]
environment: Option<String>,
#[command(subcommand)]
action: crate::secrets::GeneratedSecretAction,
},
Expand Down Expand Up @@ -228,7 +222,7 @@ pub(crate) struct ChangeFlags {
#[arg(long, value_name = "GENERATION")]
expected_generation: Option<u64>,
/// Skip the confirmation prompt. You need it when not on a terminal.
#[arg(long)]
#[arg(long, short)]
yes: bool,
/// Change the environment even if the application changed since the
/// command read it. It still asks for confirmation.
Expand Down Expand Up @@ -262,15 +256,7 @@ impl EnvCommand {
Self::Show(target) => show(client, console, target).await,
Self::Visibility(args) => args.run(client, console).await,
Self::Rollback(args) => args.run(client, console).await,
Self::Secret {
application,
environment,
action,
} => {
action
.run(client, console, application, environment.as_deref())
.await
}
Self::Secret { action } => action.run(client, console).await,
Self::Rename(args) => args.run(client, console).await,
Self::Delete(args) => args.run(client, console).await,
Self::Deploy {
Expand Down Expand Up @@ -634,15 +620,19 @@ pub(crate) struct PromoteArgs {
/// or not.
#[arg(long)]
plan: bool,
/// Promote even if the application changed since the command read it.
/// It still asks for confirmation.
#[arg(long, conflicts_with = "expected_generation")]
force: bool,
#[command(flatten)]
flags: OperationFlags,
}

impl PromoteArgs {
/// Plans, or confirms and promotes, the selected release, guarded by the
/// expected generation, defaulting to the current one. Waits unless
/// `--no-wait`. A plan listing unusable secrets fails as a conflict,
/// since promoting would.
/// expected generation, defaulting to the current one, unless `--force`.
/// Waits unless `--no-wait`. A plan listing unusable secrets fails as a
/// conflict, since promoting would.
async fn run(&self, client: &Client, console: &mut Console) -> Result<()> {
let (application, environment) =
select(client, &self.application, Some(&self.environment)).await?;
Expand Down Expand Up @@ -686,12 +676,13 @@ impl PromoteArgs {
),
)
.await?;
request.expected_generation = Some(
request.expected_generation = (!self.force).then(|| {
self.flags
.expected_generation
.unwrap_or(application.generation),
);
let accepted = retry_transport(|| client.promote_environment(id, &request, false)).await?;
.unwrap_or(application.generation)
});
let accepted =
retry_transport(|| client.promote_environment(id, &request, self.force)).await?;
let report = PromotionReport::new(&application, &accepted);
if self.flags.no_wait {
return console.emit(&report);
Expand Down
56 changes: 56 additions & 0 deletions apps/piquelctl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,62 @@ mod tests {
}
}

#[test]
fn parser_takes_env_secret_targets_positionally_and_short_yes() {
for arguments in [
&["piquelctl", "env", "secret", "list", "notes"][..],
&["piquelctl", "env", "secret", "list", "notes", "staging"],
&[
"piquelctl",
"env",
"secret",
"regenerate",
"notes",
"staging",
"db",
"-y",
],
&[
"piquelctl",
"env",
"secret",
"delete",
"notes",
"staging",
"db",
"-y",
],
&[
"piquelctl",
"env",
"promote",
"notes",
"production",
"--force",
"-y",
],
&["piquelctl", "app", "delete", "notes", "-y"],
] {
Cli::try_parse_from(arguments).expect("command parses");
}
for arguments in [
// A generated secret is always named in one environment.
&["piquelctl", "env", "secret", "regenerate", "notes", "db"][..],
&[
"piquelctl",
"env",
"promote",
"notes",
"production",
"--force",
"--expected-generation",
"3",
],
] {
assert!(Cli::try_parse_from(arguments).is_err(), "{arguments:?}");
}
}

#[test]
fn parser_rejects_conflicting_transports_and_invalid_timeout() {
assert!(
Expand Down
41 changes: 17 additions & 24 deletions apps/piquelctl/src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
commands::{fold_applications, resolve_application},
error::{CliError, ErrorKind, Result},
output::{Console, reports::report},
support::{PageArgs, read_input},
support::{PageArgs, read_hidden},
};
use clap::{
Args, Subcommand,
Expand All @@ -20,11 +20,7 @@ use piqueld_client::{
NotificationDestination, NotificationMessage,
},
};
use std::{
collections::BTreeMap,
io::{self, Read},
path::{Path, PathBuf},
};
use std::{collections::BTreeMap, path::PathBuf};

// `notification` subcommands; `///` on variants and fields is user-facing help.
#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -233,28 +229,25 @@ impl SetArgs {
Ok(settings)
}

/// Reads the URL from `--url-file` or stdin, if either was given.
async fn url(&self) -> Result<Option<String>> {
/// Reads the URL from `--url-file` or stdin, if either was given. A
/// terminal is asked for it without echo, since it holds a credential.
/// Refuses more than 8 KiB, more than any webhook URL needs.
async fn url(&self, console: &mut Console) -> Result<Option<String>> {
if self.url_file.is_none() && !self.url_stdin {
return Ok(None);
}
let file = self.url_file.clone();
// Stdin can block indefinitely; read off the runtime so Ctrl-C stays responsive.
let url = read_input("destination URL", move || Self::read_url(file.as_deref())).await?;
let url = read_hidden(console, "destination URL", self.url_file.clone(), 8192).await?;
if url.len() > 8192 {
return Err(CliError::new(
ErrorKind::Input,
"the destination URL must be at most 8192 bytes",
));
}
let url = String::from_utf8(url).map_err(|_| {
CliError::new(ErrorKind::Input, "the destination URL must be UTF-8 text")
})?;
Ok(Some(url.trim().to_owned()))
}

/// Reads at most 8 KiB, more than any webhook URL needs.
fn read_url(file: Option<&Path>) -> io::Result<String> {
let input: Box<dyn Read> = if let Some(path) = file {
Box::new(std::fs::File::open(path)?)
} else {
Box::new(io::stdin())
};
let mut url = String::new();
input.take(8192).read_to_string(&mut url)?;
Ok(url)
}
}

impl NotificationCommand {
Expand All @@ -276,7 +269,7 @@ impl NotificationCommand {
.iter()
.find(|destination| destination.name.as_str() == args.name);
let settings = args.apply(client, current.map(|d| &d.settings)).await?;
let url = args.url().await?;
let url = args.url(console).await?;
if current.is_none() && url.is_none() {
return Err(CliError::new(
ErrorKind::Input,
Expand Down
2 changes: 1 addition & 1 deletion apps/piquelctl/src/previews.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ pub(crate) struct PreviewArgs {
#[derive(Debug, Args)]
pub(crate) struct PreviewDeletionFlags {
/// Skip the confirmation prompt. You need it when not on a terminal.
#[arg(long)]
#[arg(long, short)]
yes: bool,
/// Return once the daemon accepts the deletion, without waiting for it
/// to finish.
Expand Down
Loading
Loading