@@ -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.
0 commit comments