@@ -26,6 +26,8 @@ pub struct CliOption {
26
26
pub possible_values : Vec < String > ,
27
27
/// Whether the option is required
28
28
pub required : bool ,
29
+ /// Whether this is a boolean flag
30
+ pub is_boolean : bool ,
29
31
}
30
32
31
33
/// Represents a CLI command from the JSON dump
@@ -132,16 +134,17 @@ fn format_options_as_markdown(options: &[CliOption], positionals: &[CliPositiona
132
134
// Add long flag
133
135
flag_line. push_str ( & format ! ( "**--{}**" , opt. long) ) ;
134
136
135
- // Add value name if option takes argument
137
+ // Add value name if option takes argument (but not for boolean flags)
138
+ // Boolean flags are detected by having no value_name (set to None in cli_json.rs)
136
139
if let Some ( value_name) = & opt. value_name {
137
140
flag_line. push_str ( & format ! ( "=*{}*" , value_name) ) ;
138
141
}
139
142
140
143
result. push_str ( & format ! ( "{}\n \n " , flag_line) ) ;
141
144
result. push_str ( & format ! ( " {}\n \n " , opt. help) ) ;
142
145
143
- // Add possible values for enums
144
- if !opt. possible_values . is_empty ( ) {
146
+ // Add possible values for enums (but not for boolean flags)
147
+ if !opt. possible_values . is_empty ( ) && !opt . is_boolean {
145
148
result. push_str ( " Possible values:\n " ) ;
146
149
for value in & opt. possible_values {
147
150
result. push_str ( & format ! ( " - {}\n " , value) ) ;
@@ -191,10 +194,12 @@ pub fn update_markdown_with_subcommands(
191
194
before, begin_marker, generated_subcommands, end_marker, after
192
195
) ;
193
196
194
- fs:: write ( markdown_path, new_content)
195
- . with_context ( || format ! ( "Writing to {}" , markdown_path) ) ?;
196
-
197
- println ! ( "Updated subcommands in {}" , markdown_path) ;
197
+ // Only write if content has changed to avoid updating mtime unnecessarily
198
+ if new_content != content {
199
+ fs:: write ( markdown_path, new_content)
200
+ . with_context ( || format ! ( "Writing to {}" , markdown_path) ) ?;
201
+ println ! ( "Updated subcommands in {}" , markdown_path) ;
202
+ }
198
203
Ok ( ( ) )
199
204
}
200
205
@@ -238,10 +243,12 @@ pub fn update_markdown_with_options(
238
243
format ! ( "{}\n \n {}\n {}{}" , before, begin_marker, end_marker, after)
239
244
} ;
240
245
241
- fs:: write ( markdown_path, new_content)
242
- . with_context ( || format ! ( "Writing to {}" , markdown_path) ) ?;
243
-
244
- println ! ( "Updated {}" , markdown_path) ;
246
+ // Only write if content has changed to avoid updating mtime unnecessarily
247
+ if new_content != content {
248
+ fs:: write ( markdown_path, new_content)
249
+ . with_context ( || format ! ( "Writing to {}" , markdown_path) ) ?;
250
+ println ! ( "Updated {}" , markdown_path) ;
251
+ }
245
252
Ok ( ( ) )
246
253
}
247
254
@@ -374,21 +381,6 @@ pub fn sync_all_man_pages(sh: &Shell) -> Result<()> {
374
381
Ok ( ( ) )
375
382
}
376
383
377
- /// Test the sync workflow
378
- pub fn test_sync_workflow ( sh : & Shell ) -> Result < ( ) > {
379
- println ! ( "🧪 Testing man page sync workflow..." ) ;
380
-
381
- // Create a backup of current files
382
- let test_dir = "target/test-sync" ;
383
- sh. create_dir ( test_dir) ?;
384
-
385
- // Run sync
386
- sync_all_man_pages ( sh) ?;
387
-
388
- println ! ( "✅ Sync workflow test completed successfully" ) ;
389
- Ok ( ( ) )
390
- }
391
-
392
384
/// Generate man pages from hand-written markdown sources
393
385
pub fn generate_man_pages ( sh : & Shell ) -> Result < ( ) > {
394
386
let man_src_dir = Utf8Path :: new ( "docs/src/man" ) ;
@@ -432,18 +424,31 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
432
424
let content = fs:: read_to_string ( & path) ?;
433
425
let content_with_version = content. replace ( "<!-- VERSION PLACEHOLDER -->" , & version) ;
434
426
435
- // Create temporary file with version-replaced content
436
- let temp_path = format ! ( "{}.tmp" , path. display( ) ) ;
437
- fs:: write ( & temp_path, content_with_version) ?;
427
+ // Check if we need to regenerate by comparing input and output modification times
428
+ let should_regenerate = if let ( Ok ( input_meta) , Ok ( output_meta) ) =
429
+ ( fs:: metadata ( & path) , fs:: metadata ( & output_file) )
430
+ {
431
+ input_meta. modified ( ) . unwrap_or ( std:: time:: UNIX_EPOCH )
432
+ > output_meta. modified ( ) . unwrap_or ( std:: time:: UNIX_EPOCH )
433
+ } else {
434
+ // If output doesn't exist or we can't get metadata, regenerate
435
+ true
436
+ } ;
437
+
438
+ if should_regenerate {
439
+ // Create temporary file with version-replaced content
440
+ let temp_path = format ! ( "{}.tmp" , path. display( ) ) ;
441
+ fs:: write ( & temp_path, content_with_version) ?;
438
442
439
- cmd ! ( sh, "go-md2man -in {temp_path} -out {output_file}" )
440
- . run ( )
441
- . with_context ( || format ! ( "Converting {} to man page" , path. display( ) ) ) ?;
443
+ cmd ! ( sh, "go-md2man -in {temp_path} -out {output_file}" )
444
+ . run ( )
445
+ . with_context ( || format ! ( "Converting {} to man page" , path. display( ) ) ) ?;
442
446
443
- // Clean up temporary file
444
- fs:: remove_file ( & temp_path) ?;
447
+ // Clean up temporary file
448
+ fs:: remove_file ( & temp_path) ?;
445
449
446
- println ! ( "Generated {}" , output_file) ;
450
+ println ! ( "Generated {}" , output_file) ;
451
+ }
447
452
}
448
453
449
454
// Apply post-processing fixes for apostrophe handling
@@ -495,9 +500,9 @@ pub fn update_manpages(sh: &Shell) -> Result<()> {
495
500
// Check each command and create man page if missing
496
501
for command_parts in commands_to_check {
497
502
let filename = if command_parts. len ( ) == 1 {
498
- format ! ( "bootc-{}.md" , command_parts[ 0 ] )
503
+ format ! ( "bootc-{}.8. md" , command_parts[ 0 ] )
499
504
} else {
500
- format ! ( "bootc-{}.md" , command_parts. join( "-" ) )
505
+ format ! ( "bootc-{}.8. md" , command_parts. join( "-" ) )
501
506
} ;
502
507
503
508
let filepath = format ! ( "docs/src/man/{}" , filename) ;
@@ -511,14 +516,31 @@ pub fn update_manpages(sh: &Shell) -> Result<()> {
511
516
let command_name_full = format ! ( "bootc {}" , command_parts. join( " " ) ) ;
512
517
let command_description = cmd. about . as_deref ( ) . unwrap_or ( "TODO: Add description" ) ;
513
518
519
+ // Generate SYNOPSIS line with proper arguments
520
+ let mut synopsis = format ! ( "**{}** \\ [*OPTIONS...*\\ ]" , command_name_full) ;
521
+
522
+ // Add positional arguments
523
+ for positional in & cmd. positionals {
524
+ if positional. required {
525
+ synopsis. push_str ( & format ! ( " <*{}*>" , positional. name. to_uppercase( ) ) ) ;
526
+ } else {
527
+ synopsis. push_str ( & format ! ( " \\ [*{}*\\ ]" , positional. name. to_uppercase( ) ) ) ;
528
+ }
529
+ }
530
+
531
+ // Add subcommand if this command has subcommands
532
+ if !cmd. subcommands . is_empty ( ) {
533
+ synopsis. push_str ( " <*SUBCOMMAND*>" ) ;
534
+ }
535
+
514
536
let template = format ! (
515
537
r#"# NAME
516
538
517
539
{} - {}
518
540
519
541
# SYNOPSIS
520
542
521
- **{}** [*OPTIONS*]
543
+ {}
522
544
523
545
# DESCRIPTION
524
546
@@ -549,19 +571,19 @@ TODO: Add practical examples showing how to use this command.
549
571
std:: fs:: write ( & filepath, template)
550
572
. with_context ( || format ! ( "Writing template to {}" , filepath) ) ?;
551
573
552
- println ! ( "✓ Created man page template: {}" , filepath) ;
574
+ println ! ( "Created man page template: {}" , filepath) ;
553
575
created_count += 1 ;
554
576
}
555
577
}
556
578
}
557
579
558
580
if created_count > 0 {
559
- println ! ( "✓ Created {} new man page templates" , created_count) ;
581
+ println ! ( "Created {} new man page templates" , created_count) ;
560
582
} else {
561
- println ! ( "✓ All commands already have man pages" ) ;
583
+ println ! ( "All commands already have man pages" ) ;
562
584
}
563
585
564
- println ! ( "🔄 Syncing OPTIONS sections..." ) ;
586
+ println ! ( "Syncing OPTIONS sections..." ) ;
565
587
sync_all_man_pages ( sh) ?;
566
588
567
589
println ! ( "Man pages updated." ) ;
@@ -585,6 +607,13 @@ fn apply_man_page_fixes(sh: &Shell, dir: &Utf8Path) -> Result<()> {
585
607
. and_then ( |s| s. to_str ( ) )
586
608
. map_or ( false , |e| e. chars ( ) . all ( |c| c. is_numeric ( ) ) )
587
609
{
610
+ // Check if the file already has the fix applied
611
+ let content = fs:: read_to_string ( & path) ?;
612
+ if content. starts_with ( ".ds Aq \\ (aq\n " ) {
613
+ // Already fixed, skip
614
+ continue ;
615
+ }
616
+
588
617
// Apply the same sed fixes as before
589
618
let groffsub = r"1i .ds Aq \\(aq" ;
590
619
let dropif = r"/\.g \.ds Aq/d" ;
0 commit comments