@@ -39,6 +39,7 @@ struct ProductConfig {
39
39
///
40
40
/// This value is _not_ used by `checkout`, that uses [`ProductVersionConfig::mirror`] instead.
41
41
/// `init --mirror` copies this value into [`ProductVersionConfig::mirror`].
42
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
42
43
default_mirror : Option < String > ,
43
44
}
44
45
@@ -180,24 +181,10 @@ enum Cmd {
180
181
pv : ProductVersion ,
181
182
} ,
182
183
183
- /// Creates a patchable.toml for a given product version
184
+ /// Creates patchable.toml configuration files
184
185
Init {
185
- #[ clap( flatten) ]
186
- pv : ProductVersion ,
187
-
188
- /// The upstream commit-ish (such as druid-28.0.0) that the patch series applies to
189
- ///
190
- /// Refs (such as tags and branches) will be resolved to commit IDs.
191
- #[ clap( long) ]
192
- base : String ,
193
-
194
- /// Mirror the product version to the default mirror repository
195
- #[ clap( long) ]
196
- mirror : bool ,
197
-
198
- /// Use SSH for git operations
199
- #[ clap( long) ]
200
- ssh : bool ,
186
+ #[ clap( subcommand) ]
187
+ init_type : InitType ,
201
188
} ,
202
189
203
190
/// Shows the patch directory for a given product version
@@ -218,6 +205,41 @@ enum Cmd {
218
205
ImagesDir ,
219
206
}
220
207
208
+ #[ derive( clap:: Parser ) ]
209
+ enum InitType {
210
+ /// Creates a patchable.toml for a given product
211
+ Product {
212
+ /// The product name slug (such as druid)
213
+ product : String ,
214
+ /// The upstream repository URL (e.g. https://github.com/apache/druid.git)
215
+ #[ clap( long) ]
216
+ upstream : String ,
217
+ /// The default mirror repository URL (e.g. https://github.com/stackabletech/druid.git)
218
+ #[ clap( long) ]
219
+ default_mirror : Option < String > ,
220
+ } ,
221
+
222
+ /// Creates a patchable.toml for a given product version
223
+ Version {
224
+ #[ clap( flatten) ]
225
+ pv : ProductVersion ,
226
+
227
+ /// The upstream commit-ish (such as druid-28.0.0) that the patch series applies to
228
+ ///
229
+ /// Refs (such as tags and branches) will be resolved to commit IDs.
230
+ #[ clap( long) ]
231
+ base : String ,
232
+
233
+ /// Mirror the product version to the default mirror repository
234
+ #[ clap( long) ]
235
+ mirror : bool ,
236
+
237
+ /// Use SSH for git operations
238
+ #[ clap( long) ]
239
+ ssh : bool ,
240
+ } ,
241
+ }
242
+
221
243
#[ derive( Debug , Snafu ) ]
222
244
pub enum Error {
223
245
#[ snafu( display( "failed to configure git logging" ) ) ]
@@ -474,116 +496,167 @@ fn main() -> Result<()> {
474
496
) ;
475
497
}
476
498
477
- Cmd :: Init {
478
- pv,
479
- base,
480
- mirror,
481
- ssh,
482
- } => {
483
- let ctx = ProductVersionContext {
484
- pv,
485
- images_repo_root,
486
- } ;
499
+ Cmd :: Init { init_type } => match init_type {
500
+ InitType :: Product {
501
+ product,
502
+ upstream,
503
+ default_mirror,
504
+ } => {
505
+ let product_config_path = ProductVersionContext {
506
+ pv : ProductVersion {
507
+ product : product. clone ( ) ,
508
+ version : "" . to_string ( ) ,
509
+ } ,
510
+ images_repo_root,
511
+ }
512
+ . product_config_path ( ) ;
487
513
488
- let product_repo_root = ctx. product_repo ( ) ;
489
- let product_repo = tracing:: info_span!(
490
- "finding product repository" ,
491
- product. repository = ?product_repo_root,
492
- )
493
- . in_scope ( || repo:: ensure_bare_repo ( & product_repo_root) )
494
- . context ( OpenProductRepoForCheckoutSnafu ) ?;
514
+ tracing:: info!(
515
+ path = ?product_config_path,
516
+ "creating product configuration directory and file"
517
+ ) ;
495
518
496
- let config = ctx. load_product_config ( ) ?;
497
- let upstream = if ssh {
498
- utils:: rewrite_git_https_url_to_ssh ( & config. upstream ) . context ( UrlRewriteSnafu ) ?
499
- } else {
500
- config. upstream
501
- } ;
519
+ let product_config_dir = product_config_path
520
+ . parent ( )
521
+ . expect ( "product config should have a hard-coded parent" ) ;
522
+
523
+ std:: fs:: create_dir_all ( product_config_dir) . context ( CreatePatchDirSnafu {
524
+ path : product_config_dir,
525
+ } ) ?;
502
526
503
- // --base can be a reference, but patchable.toml should always have a resolved commit id,
504
- // so that it cannot be changed under our feet (without us knowing so, anyway...).
505
- tracing:: info!( ?base, "resolving base commit-ish" ) ;
506
- let base_commit = repo:: resolve_and_fetch_commitish ( & product_repo, & base, & upstream)
507
- . context ( FetchBaseCommitSnafu ) ?;
508
- tracing:: info!( ?base, base. commit = ?base_commit, "resolved base commit" ) ;
509
-
510
- let mirror_url = if mirror {
511
- let mut mirror_url = config
512
- . default_mirror
513
- . context ( InitMirrorNotConfiguredSnafu ) ?;
514
- if ssh {
515
- mirror_url =
516
- utils:: rewrite_git_https_url_to_ssh ( & mirror_url) . context ( UrlRewriteSnafu ) ?
527
+ let product_config = ProductConfig {
528
+ upstream,
529
+ default_mirror,
517
530
} ;
518
- // Add mirror remote
519
- let mut mirror_remote =
520
- product_repo
521
- . remote_anonymous ( & mirror_url)
522
- . context ( AddMirrorRemoteSnafu {
523
- url : mirror_url. clone ( ) ,
524
- } ) ?;
525
531
526
- // Push the base commit to the mirror
527
- tracing:: info!( commit = %base_commit, base = base, url = mirror_url, "pushing commit to mirror" ) ;
528
- let mut callbacks = setup_git_credentials ( ) ;
532
+ let config_toml =
533
+ toml:: to_string_pretty ( & product_config) . context ( SerializeConfigSnafu ) ?;
534
+ File :: create_new ( & product_config_path)
535
+ . and_then ( |mut f| f. write_all ( config_toml. as_bytes ( ) ) )
536
+ . context ( SaveConfigSnafu {
537
+ path : & product_config_path,
538
+ } ) ?;
539
+
540
+ tracing:: info!(
541
+ config. path = ?product_config_path,
542
+ product = product,
543
+ "created configuration for product"
544
+ ) ;
545
+ }
529
546
530
- // Add progress tracking for push operation
531
- let ( span_push, mut quant_push) =
532
- utils:: setup_progress_tracking ( tracing:: info_span!( "pushing" ) ) ;
533
- let _ = span_push. enter ( ) ;
547
+ InitType :: Version {
548
+ pv,
549
+ base,
550
+ mirror,
551
+ ssh,
552
+ } => {
553
+ let ctx = ProductVersionContext {
554
+ pv,
555
+ images_repo_root,
556
+ } ;
534
557
535
- callbacks. push_transfer_progress ( move |current, total, _| {
536
- if total > 0 {
537
- quant_push. update_span_progress ( current, total, & span_push) ;
538
- }
539
- } ) ;
558
+ let product_repo_root = ctx. product_repo ( ) ;
559
+ let product_repo = tracing:: info_span!(
560
+ "finding product repository" ,
561
+ product. repository = ?product_repo_root,
562
+ )
563
+ . in_scope ( || repo:: ensure_bare_repo ( & product_repo_root) )
564
+ . context ( OpenProductRepoForCheckoutSnafu ) ?;
540
565
541
- let mut push_options = git2:: PushOptions :: new ( ) ;
542
- push_options. remote_callbacks ( callbacks) ;
566
+ let config = ctx. load_product_config ( ) ?;
567
+ let upstream = if ssh {
568
+ utils:: rewrite_git_https_url_to_ssh ( & config. upstream )
569
+ . context ( UrlRewriteSnafu ) ?
570
+ } else {
571
+ config. upstream
572
+ } ;
543
573
544
- // Always push the commit as a Git tag named like the value of `base`
545
- let refspec = format ! ( "{base_commit}:refs/tags/{base}" ) ;
546
- tracing:: info!( refspec, "constructed push refspec" ) ;
574
+ // --base can be a reference, but patchable.toml should always have a resolved commit id,
575
+ // so that it cannot be changed under our feet (without us knowing so, anyway...).
576
+ tracing:: info!( ?base, "resolving base commit-ish" ) ;
577
+ let base_commit =
578
+ repo:: resolve_and_fetch_commitish ( & product_repo, & base, & upstream)
579
+ . context ( FetchBaseCommitSnafu ) ?;
580
+ tracing:: info!( ?base, base. commit = ?base_commit, "resolved base commit" ) ;
581
+
582
+ let mirror_url = if mirror {
583
+ let mut mirror_url = config
584
+ . default_mirror
585
+ . context ( InitMirrorNotConfiguredSnafu ) ?;
586
+ if ssh {
587
+ mirror_url = utils:: rewrite_git_https_url_to_ssh ( & mirror_url)
588
+ . context ( UrlRewriteSnafu ) ?
589
+ } ;
590
+ // Add mirror remote
591
+ let mut mirror_remote = product_repo. remote_anonymous ( & mirror_url) . context (
592
+ AddMirrorRemoteSnafu {
593
+ url : mirror_url. clone ( ) ,
594
+ } ,
595
+ ) ?;
596
+
597
+ // Push the base commit to the mirror
598
+ tracing:: info!( commit = %base_commit, base = base, url = mirror_url, "pushing commit to mirror" ) ;
599
+ let mut callbacks = setup_git_credentials ( ) ;
600
+
601
+ // Add progress tracking for push operation
602
+ let ( span_push, mut quant_push) =
603
+ utils:: setup_progress_tracking ( tracing:: info_span!( "pushing" ) ) ;
604
+ let _ = span_push. enter ( ) ;
605
+
606
+ callbacks. push_transfer_progress ( move |current, total, _| {
607
+ if total > 0 {
608
+ quant_push. update_span_progress ( current, total, & span_push) ;
609
+ }
610
+ } ) ;
611
+
612
+ let mut push_options = git2:: PushOptions :: new ( ) ;
613
+ push_options. remote_callbacks ( callbacks) ;
614
+
615
+ // Always push the commit as a Git tag named like the value of `base`
616
+ let refspec = format ! ( "{base_commit}:refs/tags/{base}" ) ;
617
+ tracing:: info!( refspec, "constructed push refspec" ) ;
618
+
619
+ mirror_remote
620
+ . push ( & [ & refspec] , Some ( & mut push_options) )
621
+ . context ( PushToMirrorSnafu {
622
+ url : & mirror_url,
623
+ refspec : & refspec,
624
+ commit : base_commit,
625
+ } ) ?;
547
626
548
- mirror_remote
549
- . push ( & [ & refspec] , Some ( & mut push_options) )
550
- . context ( PushToMirrorSnafu {
551
- url : & mirror_url,
552
- refspec : & refspec,
553
- commit : base_commit,
554
- } ) ?;
627
+ tracing:: info!( "successfully pushed base ref to mirror" ) ;
628
+ Some ( mirror_url)
629
+ } else {
630
+ tracing:: warn!(
631
+ "this version is not mirrored, re-run with --mirror before merging into main"
632
+ ) ;
633
+ None
634
+ } ;
555
635
556
- tracing:: info!( "successfully pushed base ref to mirror" ) ;
557
- Some ( mirror_url)
558
- } else {
559
- tracing:: warn!(
560
- "this version is not mirrored, re-run with --mirror before merging into main"
561
- ) ;
562
- None
563
- } ;
636
+ tracing:: info!( "saving version-level configuration" ) ;
637
+ let config = ProductVersionConfig {
638
+ base : base_commit,
639
+ mirror : mirror_url,
640
+ } ;
641
+ let config_path = ctx. version_config_path ( ) ;
642
+ if let Some ( config_dir) = config_path. parent ( ) {
643
+ std:: fs:: create_dir_all ( config_dir)
644
+ . context ( CreatePatchDirSnafu { path : config_dir } ) ?;
645
+ }
564
646
565
- tracing:: info!( "saving version-level configuration" ) ;
566
- let config = ProductVersionConfig {
567
- base : base_commit,
568
- mirror : mirror_url,
569
- } ;
570
- let config_path = ctx. version_config_path ( ) ;
571
- if let Some ( config_dir) = config_path. parent ( ) {
572
- std:: fs:: create_dir_all ( config_dir)
573
- . context ( CreatePatchDirSnafu { path : config_dir } ) ?;
574
- }
575
- let config_toml = toml:: to_string_pretty ( & config) . context ( SerializeConfigSnafu ) ?;
576
- File :: create_new ( & config_path)
577
- . and_then ( |mut f| f. write_all ( config_toml. as_bytes ( ) ) )
578
- . context ( SaveConfigSnafu { path : & config_path } ) ?;
647
+ let config_toml = toml:: to_string_pretty ( & config) . context ( SerializeConfigSnafu ) ?;
648
+ File :: create_new ( & config_path)
649
+ . and_then ( |mut f| f. write_all ( config_toml. as_bytes ( ) ) )
650
+ . context ( SaveConfigSnafu { path : & config_path } ) ?;
579
651
580
- tracing:: info!(
581
- config. path = ?config_path,
582
- product = ctx. pv. product,
583
- version = ctx. pv. version,
584
- "created configuration for product version"
585
- ) ;
586
- }
652
+ tracing:: info!(
653
+ config. path = ?config_path,
654
+ product = ctx. pv. product,
655
+ version = ctx. pv. version,
656
+ "created configuration for product version"
657
+ ) ;
658
+ }
659
+ } ,
587
660
588
661
Cmd :: PatchDir { pv } => {
589
662
let ctx = ProductVersionContext {
0 commit comments