@@ -26,6 +26,8 @@ pub struct CliOption {
2626 pub possible_values : Vec < String > ,
2727 /// Whether the option is required
2828 pub required : bool ,
29+ /// Whether this is a boolean flag
30+ pub is_boolean : bool ,
2931}
3032
3133/// Represents a CLI command from the JSON dump
@@ -132,16 +134,17 @@ fn format_options_as_markdown(options: &[CliOption], positionals: &[CliPositiona
132134 // Add long flag
133135 flag_line. push_str ( & format ! ( "**--{}**" , opt. long) ) ;
134136
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)
136139 if let Some ( value_name) = & opt. value_name {
137140 flag_line. push_str ( & format ! ( "=*{}*" , value_name) ) ;
138141 }
139142
140143 result. push_str ( & format ! ( "{}\n \n " , flag_line) ) ;
141144 result. push_str ( & format ! ( " {}\n \n " , opt. help) ) ;
142145
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 {
145148 result. push_str ( " Possible values:\n " ) ;
146149 for value in & opt. possible_values {
147150 result. push_str ( & format ! ( " - {}\n " , value) ) ;
@@ -191,10 +194,12 @@ pub fn update_markdown_with_subcommands(
191194 before, begin_marker, generated_subcommands, end_marker, after
192195 ) ;
193196
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+ }
198203 Ok ( ( ) )
199204}
200205
@@ -238,10 +243,12 @@ pub fn update_markdown_with_options(
238243 format ! ( "{}\n \n {}\n {}{}" , before, begin_marker, end_marker, after)
239244 } ;
240245
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+ }
245252 Ok ( ( ) )
246253}
247254
@@ -374,21 +381,6 @@ pub fn sync_all_man_pages(sh: &Shell) -> Result<()> {
374381 Ok ( ( ) )
375382}
376383
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-
392384/// Generate man pages from hand-written markdown sources
393385pub fn generate_man_pages ( sh : & Shell ) -> Result < ( ) > {
394386 let man_src_dir = Utf8Path :: new ( "docs/src/man" ) ;
@@ -432,18 +424,31 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
432424 let content = fs:: read_to_string ( & path) ?;
433425 let content_with_version = content. replace ( "<!-- VERSION PLACEHOLDER -->" , & version) ;
434426
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) ?;
438442
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( ) ) ) ?;
442446
443- // Clean up temporary file
444- fs:: remove_file ( & temp_path) ?;
447+ // Clean up temporary file
448+ fs:: remove_file ( & temp_path) ?;
445449
446- println ! ( "Generated {}" , output_file) ;
450+ println ! ( "Generated {}" , output_file) ;
451+ }
447452 }
448453
449454 // Apply post-processing fixes for apostrophe handling
@@ -495,9 +500,9 @@ pub fn update_manpages(sh: &Shell) -> Result<()> {
495500 // Check each command and create man page if missing
496501 for command_parts in commands_to_check {
497502 let filename = if command_parts. len ( ) == 1 {
498- format ! ( "bootc-{}.md" , command_parts[ 0 ] )
503+ format ! ( "bootc-{}.8. md" , command_parts[ 0 ] )
499504 } else {
500- format ! ( "bootc-{}.md" , command_parts. join( "-" ) )
505+ format ! ( "bootc-{}.8. md" , command_parts. join( "-" ) )
501506 } ;
502507
503508 let filepath = format ! ( "docs/src/man/{}" , filename) ;
@@ -511,14 +516,31 @@ pub fn update_manpages(sh: &Shell) -> Result<()> {
511516 let command_name_full = format ! ( "bootc {}" , command_parts. join( " " ) ) ;
512517 let command_description = cmd. about . as_deref ( ) . unwrap_or ( "TODO: Add description" ) ;
513518
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+
514536 let template = format ! (
515537 r#"# NAME
516538
517539{} - {}
518540
519541# SYNOPSIS
520542
521- **{}** [*OPTIONS*]
543+ {}
522544
523545# DESCRIPTION
524546
@@ -549,19 +571,19 @@ TODO: Add practical examples showing how to use this command.
549571 std:: fs:: write ( & filepath, template)
550572 . with_context ( || format ! ( "Writing template to {}" , filepath) ) ?;
551573
552- println ! ( "✓ Created man page template: {}" , filepath) ;
574+ println ! ( "Created man page template: {}" , filepath) ;
553575 created_count += 1 ;
554576 }
555577 }
556578 }
557579
558580 if created_count > 0 {
559- println ! ( "✓ Created {} new man page templates" , created_count) ;
581+ println ! ( "Created {} new man page templates" , created_count) ;
560582 } else {
561- println ! ( "✓ All commands already have man pages" ) ;
583+ println ! ( "All commands already have man pages" ) ;
562584 }
563585
564- println ! ( "🔄 Syncing OPTIONS sections..." ) ;
586+ println ! ( "Syncing OPTIONS sections..." ) ;
565587 sync_all_man_pages ( sh) ?;
566588
567589 println ! ( "Man pages updated." ) ;
@@ -585,6 +607,13 @@ fn apply_man_page_fixes(sh: &Shell, dir: &Utf8Path) -> Result<()> {
585607 . and_then ( |s| s. to_str ( ) )
586608 . map_or ( false , |e| e. chars ( ) . all ( |c| c. is_numeric ( ) ) )
587609 {
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+
588617 // Apply the same sed fixes as before
589618 let groffsub = r"1i .ds Aq \\(aq" ;
590619 let dropif = r"/\.g \.ds Aq/d" ;
0 commit comments