Skip to content

Commit e80ea88

Browse files
committed
publish: address --yes review feedback
- Rename YesFlags fields for clarity: remote -> publish_to_remote, migrate -> migrate_major_version. - Replace the catch-all `all: bool` with an explicit `delete_data: bool` field plus a `delete-data` value, since the field is only used to gate the destructive `confirm_and_clear` prompt. - Switch the arg to ArgAction::Append + num_args(0..=1) so users can accumulate values across repeated invocations (--yes=migrate --yes=skip-login). - Use ',' as the in-string separator (more familiar than '|').
1 parent b2d5a14 commit e80ea88

2 files changed

Lines changed: 70 additions & 33 deletions

File tree

crates/cli/src/subcommands/publish.rs

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::{ensure, Context};
22
use clap::Arg;
3-
use clap::ArgAction::{Set, SetTrue};
3+
use clap::ArgAction::{self, Set, SetTrue};
44
use clap::{value_parser, ArgMatches, ValueEnum};
55
use reqwest::{StatusCode, Url};
66
use spacetimedb_client_api_messages::name::{is_identity, parse_database_name, PublishResult};
@@ -33,38 +33,39 @@ pub enum YesValue {
3333
BreakClients,
3434
/// Don't prompt the user to log in; act non-interactively for auth.
3535
SkipLogin,
36+
/// Skip the destructive "this will DESTROY ... ALL corresponding data" confirmation.
37+
DeleteData,
3638
}
3739

3840
/// Decoded `--yes` selections, used at every prompt site.
3941
#[derive(Clone, Copy, Debug, Default)]
4042
pub struct YesFlags {
41-
pub remote: bool,
42-
pub migrate: bool,
43+
pub publish_to_remote: bool,
44+
pub migrate_major_version: bool,
4345
pub break_clients: bool,
4446
pub skip_login: bool,
45-
/// True iff `--yes` was passed with no value or `--yes=all`. Used for destructive prompts
46-
/// not represented by an explicit option (e.g. `confirm_and_clear`) to preserve back-compat.
47-
pub all: bool,
47+
pub delete_data: bool,
4848
}
4949

5050
impl YesFlags {
5151
pub fn all() -> Self {
5252
Self {
53-
remote: true,
54-
migrate: true,
53+
publish_to_remote: true,
54+
migrate_major_version: true,
5555
break_clients: true,
5656
skip_login: true,
57-
all: true,
57+
delete_data: true,
5858
}
5959
}
6060

6161
fn merge(&mut self, value: YesValue) {
6262
match value {
6363
YesValue::All => *self = YesFlags::all(),
64-
YesValue::Remote => self.remote = true,
65-
YesValue::Migrate => self.migrate = true,
64+
YesValue::Remote => self.publish_to_remote = true,
65+
YesValue::Migrate => self.migrate_major_version = true,
6666
YesValue::BreakClients => self.break_clients = true,
6767
YesValue::SkipLogin => self.skip_login = true,
68+
YesValue::DeleteData => self.delete_data = true,
6869
}
6970
}
7071
}
@@ -272,17 +273,19 @@ i.e. only lowercase ASCII letters and numbers, separated by dashes."),
272273
Arg::new("yes")
273274
.long("yes")
274275
.short('y')
275-
.num_args(0..=4)
276+
.action(ArgAction::Append)
277+
.num_args(0..=1)
276278
.require_equals(true)
277-
.value_delimiter('|')
279+
.value_delimiter(',')
278280
.default_missing_value("all")
279281
.value_parser(value_parser!(YesValue))
280282
.help(
281283
"Skip confirmation prompts. With no value, defaults to 'all' \
282284
(equivalent to --yes=all). To skip specific prompts, pass one or \
283-
more of: all, remote, migrate, break-clients, skip-login -- joined \
284-
by '|', e.g. --yes='migrate|break-clients'. The value must be \
285-
attached with '=' (so `--yes my-db` treats `my-db` as the database name)."
285+
more of: all, remote, migrate, break-clients, skip-login, delete-data. \
286+
Multiple values can be comma-separated (--yes=migrate,break-clients) or \
287+
given via repeated flags (--yes=migrate --yes=break-clients). The value \
288+
must be attached with '=' (so `--yes my-db` treats `my-db` as the database name)."
286289
)
287290
)
288291
.arg(
@@ -532,7 +535,7 @@ async fn execute_publish_configs<'a>(
532535
};
533536
if server_address != "localhost" && server_address != "127.0.0.1" {
534537
println!("You are about to publish to a non-local server: {server_address}");
535-
if !y_or_n(yes.remote, "Are you sure you want to proceed?")? {
538+
if !y_or_n(yes.publish_to_remote, "Are you sure you want to proceed?")? {
536539
anyhow::bail!("Publish aborted by user.");
537540
}
538541
}
@@ -553,7 +556,7 @@ async fn execute_publish_configs<'a>(
553556

554557
// note that this only happens in the case where we've passed a `name_or_identity`, but that's required if we pass `--clear-database`.
555558
if clear_database == ClearMode::Always {
556-
builder = confirm_and_clear(name_or_identity, yes.all, builder)?;
559+
builder = confirm_and_clear(name_or_identity, yes.delete_data, builder)?;
557560
} else {
558561
builder = apply_pre_publish_if_needed(
559562
builder,
@@ -744,7 +747,7 @@ async fn apply_pre_publish_if_needed(
744747
PrePublishResult::ManualMigrate(manual) => manual.major_version_upgrade,
745748
};
746749
if major_version_upgrade {
747-
confirm_major_version_upgrade(yes.migrate)?;
750+
confirm_major_version_upgrade(yes.migrate_major_version)?;
748751
}
749752

750753
match pre {
@@ -757,7 +760,7 @@ async fn apply_pre_publish_if_needed(
757760
println!("{}", manual.reason);
758761
println!("Proceeding with database clear due to --delete-data=on-conflict.");
759762

760-
builder = confirm_and_clear(name_or_identity, yes.all, builder)?;
763+
builder = confirm_and_clear(name_or_identity, yes.delete_data, builder)?;
761764
}
762765
PrePublishResult::AutoMigrate(auto) => {
763766
println!("{}", auto.migrate_plan);
@@ -1219,32 +1222,58 @@ mod tests {
12191222
fn test_yes_flag_no_value_is_all() {
12201223
let matches = cli().get_matches_from(vec!["publish", "--yes"]);
12211224
let yes = yes_flags_from_args(&matches);
1222-
assert!(yes.all);
1223-
assert!(yes.remote && yes.migrate && yes.break_clients && yes.skip_login);
1225+
assert!(
1226+
yes.publish_to_remote
1227+
&& yes.migrate_major_version
1228+
&& yes.break_clients
1229+
&& yes.skip_login
1230+
&& yes.delete_data
1231+
);
12241232
}
12251233

12261234
#[test]
12271235
fn test_yes_flag_explicit_all() {
12281236
let matches = cli().get_matches_from(vec!["publish", "--yes=all"]);
12291237
let yes = yes_flags_from_args(&matches);
1230-
assert!(yes.all);
1231-
assert!(yes.remote && yes.migrate && yes.break_clients && yes.skip_login);
1238+
assert!(
1239+
yes.publish_to_remote
1240+
&& yes.migrate_major_version
1241+
&& yes.break_clients
1242+
&& yes.skip_login
1243+
&& yes.delete_data
1244+
);
12321245
}
12331246

12341247
#[test]
12351248
fn test_yes_flag_single_value() {
12361249
let matches = cli().get_matches_from(vec!["publish", "--yes=remote"]);
12371250
let yes = yes_flags_from_args(&matches);
1238-
assert!(yes.remote);
1239-
assert!(!yes.migrate && !yes.break_clients && !yes.skip_login && !yes.all);
1251+
assert!(yes.publish_to_remote);
1252+
assert!(!yes.migrate_major_version && !yes.break_clients && !yes.skip_login && !yes.delete_data);
1253+
}
1254+
1255+
#[test]
1256+
fn test_yes_flag_comma_separated_multiple_values() {
1257+
let matches = cli().get_matches_from(vec!["publish", "--yes=migrate,skip-login"]);
1258+
let yes = yes_flags_from_args(&matches);
1259+
assert!(yes.migrate_major_version && yes.skip_login);
1260+
assert!(!yes.publish_to_remote && !yes.break_clients && !yes.delete_data);
12401261
}
12411262

12421263
#[test]
1243-
fn test_yes_flag_pipe_separated_multiple_values() {
1244-
let matches = cli().get_matches_from(vec!["publish", "--yes=migrate|skip-login"]);
1264+
fn test_yes_flag_repeated_invocations_accumulate() {
1265+
let matches = cli().get_matches_from(vec!["publish", "--yes=migrate", "--yes=skip-login"]);
12451266
let yes = yes_flags_from_args(&matches);
1246-
assert!(yes.migrate && yes.skip_login);
1247-
assert!(!yes.remote && !yes.break_clients && !yes.all);
1267+
assert!(yes.migrate_major_version && yes.skip_login);
1268+
assert!(!yes.publish_to_remote && !yes.break_clients && !yes.delete_data);
1269+
}
1270+
1271+
#[test]
1272+
fn test_yes_flag_delete_data_only() {
1273+
let matches = cli().get_matches_from(vec!["publish", "--yes=delete-data"]);
1274+
let yes = yes_flags_from_args(&matches);
1275+
assert!(yes.delete_data);
1276+
assert!(!yes.publish_to_remote && !yes.migrate_major_version && !yes.break_clients && !yes.skip_login);
12481277
}
12491278

12501279
#[test]
@@ -1253,7 +1282,7 @@ mod tests {
12531282
// plus the token treated as the positional database name. require_equals enforces this.
12541283
let matches = cli().get_matches_from(vec!["publish", "--yes", "my-db"]);
12551284
let yes = yes_flags_from_args(&matches);
1256-
assert!(yes.all);
1285+
assert!(yes.delete_data && yes.publish_to_remote);
12571286
assert_eq!(
12581287
matches.get_one::<String>("name|identity").map(String::as_str),
12591288
Some("my-db"),
@@ -1270,6 +1299,12 @@ mod tests {
12701299
fn test_yes_flag_omitted_means_no_skips() {
12711300
let matches = cli().get_matches_from(vec!["publish", "my-db"]);
12721301
let yes = yes_flags_from_args(&matches);
1273-
assert!(!yes.all && !yes.remote && !yes.migrate && !yes.break_clients && !yes.skip_login);
1302+
assert!(
1303+
!yes.publish_to_remote
1304+
&& !yes.migrate_major_version
1305+
&& !yes.break_clients
1306+
&& !yes.skip_login
1307+
&& !yes.delete_data
1308+
);
12741309
}
12751310
}

docs/docs/00300-resources/00200-reference/00100-cli-reference/00100-cli-reference.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Run `spacetime help publish` for more detailed information.
108108
If an organization is given, the organization member's permissions apply to the new database.
109109
An organization can only be set when a database is created, not when it is updated.
110110
* `-s`, `--server <SERVER>` — The nickname, domain name or URL of the server to host the database.
111-
* `-y`, `--yes <YES>` — Skip confirmation prompts. With no value, defaults to 'all' (equivalent to --yes=all). To skip specific prompts, pass one or more of: all, remote, migrate, break-clients, skip-login -- joined by '|', e.g. --yes='migrate|break-clients'. The value must be attached with '=' (so `--yes my-db` treats `my-db` as the database name).
111+
* `-y`, `--yes <YES>` — Skip confirmation prompts. With no value, defaults to 'all' (equivalent to --yes=all). To skip specific prompts, pass one or more of: all, remote, migrate, break-clients, skip-login, delete-data. Multiple values can be comma-separated (--yes=migrate,break-clients) or given via repeated flags (--yes=migrate --yes=break-clients). The value must be attached with '=' (so `--yes my-db` treats `my-db` as the database name).
112112

113113
Possible values:
114114
- `all`:
@@ -121,6 +121,8 @@ Run `spacetime help publish` for more detailed information.
121121
Skip the "this will BREAK existing clients" confirmation
122122
- `skip-login`:
123123
Don't prompt the user to log in; act non-interactively for auth
124+
- `delete-data`:
125+
Skip the destructive "this will DESTROY ... ALL corresponding data" confirmation
124126

125127
* `--no-config` — Ignore spacetime.json configuration
126128
* `--env <ENV>` — Environment name for config file layering (e.g., dev, staging)

0 commit comments

Comments
 (0)