Skip to content

Commit 17b908d

Browse files
committed
feat(release): confirm custom prerelease channels
1 parent 0502af3 commit 17b908d

3 files changed

Lines changed: 110 additions & 11 deletions

File tree

crates/vite_global_cli/src/commands/release/reporting.rs

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ pub(super) fn confirm_release(
533533
}
534534

535535
info_section!("Release summary:");
536+
let prerelease_channels = release_prerelease_channels(release_plans, options);
536537
raw_item_kv!("packages", release_plans.len());
537538
raw_item_kv!("publish", if options.skip_publish { "no" } else { "yes" });
538539
raw_item_kv!("changelog", if options.changelog { "yes" } else { "no" });
@@ -543,7 +544,7 @@ pub(super) fn confirm_release(
543544
"trusted publishing",
544545
readiness_report.trusted_publish.context.environment_summary(),
545546
);
546-
raw_item_kv!("prerelease tag", options.preid.as_deref().unwrap_or("stable"));
547+
raw_item_kv!("prerelease tag", format_release_prerelease_summary(&prerelease_channels),);
547548
if let Some(version) = options.version.as_deref() {
548549
raw_item_kv!("version override", version);
549550
}
@@ -554,19 +555,88 @@ pub(super) fn confirm_release(
554555
output::warn(&warning);
555556
}
556557

557-
output::raw_inline("Continue with this release? [Y/n] ");
558+
let custom_prerelease_channels = collect_nonstandard_prerelease_channels(&prerelease_channels);
559+
if !custom_prerelease_channels.is_empty() {
560+
let mut warning = String::from(
561+
"Non-standard prerelease channels can create a new release line if mistyped.",
562+
);
563+
warning.push_str(" Standard channels are `alpha`, `beta`, and `rc`.");
564+
output::warn(&warning);
565+
566+
let channel_count = custom_prerelease_channels.len();
567+
let suffix = if channel_count == 1 { "" } else { "s" };
568+
let joined_channels = custom_prerelease_channels.join(", ");
569+
let mut prompt = String::from("Continue with non-standard prerelease channel");
570+
prompt.push_str(suffix);
571+
prompt.push_str(" ");
572+
prompt.push_str(&joined_channels);
573+
prompt.push_str("? [y/N] ");
574+
if !prompt_for_confirmation(&prompt, false)? {
575+
output::info("Aborted.");
576+
return Ok(false);
577+
}
578+
}
579+
580+
if prompt_for_confirmation("Continue with this release? [Y/n] ", true)? {
581+
Ok(true)
582+
} else {
583+
output::info("Aborted.");
584+
Ok(false)
585+
}
586+
}
587+
588+
fn release_prerelease_channels(
589+
release_plans: &[PackageReleasePlan],
590+
options: &ReleaseOptions,
591+
) -> Vec<String> {
592+
let mut channels =
593+
Vec::with_capacity(release_plans.len() + usize::from(options.preid.is_some()));
594+
if let Some(preid) = options.preid.as_deref() {
595+
channels.push(preid.to_owned());
596+
}
597+
channels.extend(
598+
release_plans
599+
.iter()
600+
.filter_map(|plan| prerelease_channel(&plan.next_version).map(str::to_owned)),
601+
);
602+
unique_strings(channels)
603+
}
604+
605+
fn format_release_prerelease_summary(prerelease_channels: &[String]) -> String {
606+
match prerelease_channels {
607+
[] => String::from("stable"),
608+
[channel] => channel.clone(),
609+
_ => {
610+
let mut summary = String::from("mixed (");
611+
summary.push_str(&prerelease_channels.join(", "));
612+
summary.push(')');
613+
summary
614+
}
615+
}
616+
}
617+
618+
fn collect_nonstandard_prerelease_channels(prerelease_channels: &[String]) -> Vec<String> {
619+
prerelease_channels
620+
.iter()
621+
.filter(|channel| matches!(PrereleaseTag::parse(channel), PrereleaseTag::Custom(_)))
622+
.cloned()
623+
.collect()
624+
}
625+
626+
fn prompt_for_confirmation(prompt: &str, default_yes: bool) -> Result<bool, Error> {
627+
output::raw_inline(prompt);
558628
#[expect(clippy::disallowed_types)]
559629
let mut input = String::new();
560630
std::io::stdout().flush()?;
561631
std::io::stdin().read_line(&mut input)?;
562632

563-
match input.trim().to_ascii_lowercase().as_str() {
564-
"" | "y" | "yes" => Ok(true),
565-
_ => {
566-
output::info("Aborted.");
567-
Ok(false)
568-
}
569-
}
633+
let normalized = input.trim().to_ascii_lowercase();
634+
Ok(match normalized.as_str() {
635+
"" => default_yes,
636+
"y" | "yes" => true,
637+
"n" | "no" => false,
638+
_ => false,
639+
})
570640
}
571641

572642
/// Collects scripts worth surfacing in the readiness summary for one scope.

crates/vite_global_cli/src/commands/release/tests.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,34 @@ fn resolved_publish_tag_supports_custom_prerelease_channels() {
411411
assert_eq!(resolved_publish_tag(&plan, &make_release_options()), Some("canary"));
412412
}
413413

414+
#[test]
415+
fn release_prerelease_channels_use_planned_custom_version_when_preid_is_absent() {
416+
let mut plan = make_release_plan("pkg-a", &[], &[]);
417+
plan.next_version = Version::parse("1.0.1-canary.0").unwrap();
418+
419+
assert_eq!(release_prerelease_channels(&[plan], &make_release_options()), vec!["canary"]);
420+
}
421+
422+
#[test]
423+
fn collect_nonstandard_prerelease_channels_flags_custom_channels_only() {
424+
assert_eq!(
425+
collect_nonstandard_prerelease_channels(&[
426+
String::from("alpha"),
427+
String::from("canary"),
428+
String::from("beta"),
429+
]),
430+
vec![String::from("canary")]
431+
);
432+
}
433+
434+
#[test]
435+
fn format_release_prerelease_summary_marks_mixed_channels() {
436+
assert_eq!(
437+
format_release_prerelease_summary(&[String::from("alpha"), String::from("canary"),]),
438+
"mixed (alpha, canary)"
439+
);
440+
}
441+
414442
#[test]
415443
fn build_manifest_edits_updates_simple_internal_dependency_ranges() {
416444
let mut pkg_a = make_release_plan("pkg-a", &[], &[]);

docs/guide/release.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ vp release --preid beta --yes
6666
vp release --preid rc --yes
6767
```
6868

69-
Custom prerelease channels are also supported:
69+
Custom prerelease channels are also supported, but interactive runs ask for one extra `y/N`
70+
confirmation so a typo does not silently create a new channel:
7071

7172
```bash
72-
vp release --preid canary --yes
73+
vp release --preid canary
7374
```
7475

7576
### Retry a partial publish with an exact version

0 commit comments

Comments
 (0)