11require 'tempfile'
22
33OUTPUT_DIR = 'references'
4+ MANDIR = File . join ( OUTPUT_DIR , 'man' )
5+ TYPES_DIR = File . join ( OUTPUT_DIR , 'types' )
6+
47CONFIGURATION_ERB = File . join ( __dir__ , 'references/configuration.erb' )
58CONFIGURATION_MD = File . join ( OUTPUT_DIR , 'configuration.md' )
69METAPARAMETER_ERB = File . join ( __dir__ , 'references/metaparameter.erb' )
@@ -10,6 +13,14 @@ REPORT_MD = File.join(OUTPUT_DIR, 'report.md')
1013FUNCTIONS_TEMPLATE_ERB = File . join ( __dir__ , 'references/functions_template.erb' )
1114FUNCTION_ERB = File . join ( __dir__ , 'references/function.erb' )
1215FUNCTION_MD = File . join ( OUTPUT_DIR , 'function.md' )
16+ MAN_OVERVIEW_ERB = File . join ( __dir__ , 'references/man/overview.erb' )
17+ MAN_OVERVIEW_MD = File . join ( MANDIR , "overview.md" )
18+ MAN_ERB = File . join ( __dir__ , 'references/man.erb' )
19+ TYPES_OVERVIEW_ERB = File . join ( __dir__ , 'references/types/overview.erb' )
20+ TYPES_OVERVIEW_MD = File . join ( TYPES_DIR , 'overview.md' )
21+ UNIFIED_TYPE_ERB = File . join ( __dir__ , 'references/unified_type.erb' )
22+ UNIFIED_TYPE_MD = File . join ( OUTPUT_DIR , 'type.md' )
23+ SINGLE_TYPE_ERB = File . join ( __dir__ , 'references/types/single_type.erb' )
1324
1425def render_erb ( erb_file , variables )
1526 # Create a binding so only the variables we specify will be visible
@@ -38,6 +49,134 @@ def generate_reference(reference, erb, body, output)
3849 puts "Generated #{ output } "
3950end
4051
52+ # Render type information for the specified resource type
53+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L87-L112
54+ def render_resource_type ( name , this_type )
55+ sorted_attribute_list = this_type [ 'attributes' ] . keys . sort { |a , b |
56+ # Float namevar(s) to the top and ensure after
57+ # followed by the others in sort order
58+ if this_type [ 'attributes' ] [ a ] [ 'namevar' ]
59+ -1
60+ elsif this_type [ 'attributes' ] [ b ] [ 'namevar' ]
61+ 1
62+ elsif a == 'ensure'
63+ -1
64+ elsif b == 'ensure'
65+ 1
66+ else
67+ a <=> b
68+ end
69+ }
70+
71+ variables = {
72+ name : name ,
73+ this_type : this_type ,
74+ sorted_attribute_list : sorted_attribute_list ,
75+ sorted_feature_list : this_type [ 'features' ] . keys . sort ,
76+ longest_attribute_name : sorted_attribute_list . collect { |attr | attr . length } . max
77+ }
78+ erb = File . join ( __dir__ , 'references/types/type.erb' )
79+ render_erb ( erb , variables )
80+ end
81+
82+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type_strings.rb#L19-L99
83+ def extract_resource_types ( strings_data )
84+ strings_data [ 'resource_types' ] . reduce ( Hash . new ) do |memo , type |
85+ memo [ type [ 'name' ] ] = {
86+ 'description' => type [ 'docstring' ] [ 'text' ] ,
87+ 'features' => ( type [ 'features' ] || [ ] ) . reduce ( Hash . new ) { |memo , feature |
88+ memo [ feature [ 'name' ] ] = feature [ 'description' ]
89+ memo
90+ } ,
91+ 'providers' => strings_data [ 'providers' ] . select { |provider |
92+ provider [ 'type_name' ] == type [ 'name' ]
93+ } . reduce ( Hash . new ) { |memo , provider |
94+ description = provider [ 'docstring' ] [ 'text' ]
95+ if provider [ 'commands' ] || provider [ 'confines' ] || provider [ 'defaults' ]
96+ description = description + "\n "
97+ end
98+ if provider [ 'commands' ]
99+ description = description + "\n * Required binaries: `#{ provider [ 'commands' ] . values . sort . join ( '`, `' ) } `"
100+ end
101+ if provider [ 'confines' ]
102+ description = description + "\n * Confined to: `#{ provider [ 'confines' ] . map { |fact , val | "#{ fact } == #{ val } " } . join ( '`, `' ) } `"
103+ end
104+ if provider [ 'defaults' ]
105+ description = description + "\n * Default for: `#{ provider [ 'defaults' ] . map { |fact , val | "#{ fact } == #{ val } " } . join ( '`, `' ) } `"
106+ end
107+ if provider [ 'features' ]
108+ description = description + "\n * Supported features: `#{ provider [ 'features' ] . sort . join ( '`, `' ) } `"
109+ end
110+ memo [ provider [ 'name' ] ] = {
111+ 'features' => ( provider [ 'features' ] || [ ] ) ,
112+ 'description' => description
113+ }
114+ memo
115+ } ,
116+ 'attributes' => ( type [ 'parameters' ] || [ ] ) . reduce ( Hash . new ) { |memo , attribute |
117+ description = attribute [ 'description' ] || ''
118+ if attribute [ 'default' ]
119+ description = description + "\n \n Default: `#{ attribute [ 'default' ] } `"
120+ end
121+ if attribute [ 'values' ]
122+ description = description + "\n \n Allowed values:\n \n " + attribute [ 'values' ] . map { |val | "* `#{ val } `" } . join ( "\n " )
123+ end
124+ memo [ attribute [ 'name' ] ] = {
125+ 'description' => description ,
126+ 'kind' => 'parameter' ,
127+ 'namevar' => attribute [ 'isnamevar' ] ? true : false ,
128+ 'required_features' => attribute [ 'required_features' ] ,
129+ }
130+ memo
131+ } . merge ( ( type [ 'properties' ] || [ ] ) . reduce ( Hash . new ) { |memo , attribute |
132+ description = attribute [ 'description' ] || ''
133+ if attribute [ 'default' ]
134+ description = description + "\n \n Default: `#{ attribute [ 'default' ] } `"
135+ end
136+ if attribute [ 'values' ]
137+ description = description + "\n \n Allowed values:\n \n " + attribute [ 'values' ] . map { |val | "* `#{ val } `" } . join ( "\n " )
138+ end
139+ memo [ attribute [ 'name' ] ] = {
140+ 'description' => description ,
141+ 'kind' => 'property' ,
142+ 'namevar' => false ,
143+ 'required_features' => attribute [ 'required_features' ] ,
144+ }
145+ memo
146+ } ) . merge ( ( type [ 'checks' ] || [ ] ) . reduce ( Hash . new ) { |memo , attribute |
147+ description = attribute [ 'description' ] || ''
148+ if attribute [ 'default' ]
149+ description = description + "\n \n Default: `#{ attribute [ 'default' ] } `"
150+ end
151+ if attribute [ 'values' ]
152+ description = description + "\n \n Allowed values:\n \n " + attribute [ 'values' ] . map { |val | "* `#{ val } `" } . join ( "\n " )
153+ end
154+ memo [ attribute [ 'name' ] ] = {
155+ 'description' => description ,
156+ 'kind' => 'check' ,
157+ 'namevar' => false ,
158+ 'required_features' => attribute [ 'required_features' ] ,
159+ }
160+ memo
161+ } )
162+ }
163+ memo
164+ end
165+ end
166+
167+ # Extract type documentation from the current version of puppet. Based on
168+ # https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L52
169+ #
170+ # REMIND This is kind of convoluted and means we're using two completely different
171+ # code paths to generate the overview and unified page of types.
172+ def unified_page_resource_types
173+ type_json = %x{ruby #{ File . join ( __dir__ , 'references/get_typedocs.rb' ) } }
174+ type_data = JSON . load ( type_json )
175+ type_data . keys . sort . map do |name |
176+ render_resource_type ( name , type_data [ name ] )
177+ end
178+ end
179+
41180namespace :references do
42181 desc "Generate configuration reference"
43182 task :configuration do
@@ -100,4 +239,179 @@ namespace :references do
100239 # renders the preamble and list of functions
101240 generate_reference ( 'function' , FUNCTION_ERB , body , FUNCTION_MD )
102241 end
242+
243+ desc "Generate man as markdown references"
244+ task :man do
245+ FileUtils . mkdir_p ( MANDIR )
246+
247+ begin
248+ require 'pandoc-ruby'
249+ rescue LoadError
250+ abort ( "Run `bundle config set with documentation` and `bundle update` to install the `pandoc-ruby` gem." )
251+ end
252+
253+ pandoc = %x{which pandoc} . chomp
254+ unless File . executable? ( pandoc )
255+ abort ( "Please install the `pandoc` package." )
256+ end
257+
258+ sha = %x{git rev-parse HEAD} . chomp
259+ now = Time . now
260+
261+ # This is based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/man.rb#L24-L108
262+ core_apps = %w(
263+ agent
264+ apply
265+ lookup
266+ module
267+ resource
268+ )
269+ occasional_apps = %w(
270+ config
271+ describe
272+ device
273+ doc
274+ epp
275+ generate
276+ help
277+ node
278+ parser
279+ plugin
280+ script
281+ ssl
282+ )
283+ weird_apps = %w(
284+ catalog
285+ facts
286+ filebucket
287+ report
288+ )
289+
290+ variables = {
291+ sha : sha ,
292+ now : now ,
293+ title : 'Puppet Man Pages' ,
294+ core_apps : core_apps ,
295+ occasional_apps : occasional_apps ,
296+ weird_apps : weird_apps
297+ }
298+
299+ content = render_erb ( MAN_OVERVIEW_ERB , variables )
300+ File . write ( MAN_OVERVIEW_MD , content )
301+ puts "Generated #{ MAN_OVERVIEW_MD } "
302+
303+ # Generate manpages in roff
304+ Rake ::Task [ :gen_manpages ] . invoke
305+
306+ # Convert the roff formatted man pages to markdown, based on
307+ # https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/man.rb#L119-L128
308+ files = Pathname . glob ( File . join ( __dir__ , '../man/man8/*.8' ) )
309+ files . each do |f |
310+ next if File . basename ( f ) == "puppet.8"
311+
312+ app = File . basename ( f ) . delete_prefix ( 'puppet-' ) . delete_suffix ( ".8" )
313+
314+ body =
315+ PandocRuby . convert ( [ f ] , from : :man , to : :markdown )
316+ . gsub ( /#(.*?)\n / , '##\1' )
317+ . gsub ( /:\s \s \s \n \n ```\{ =html\} \n <!--\s -->\n ```/ , '' )
318+ . gsub ( /\n :\s \s \s \s / , '' )
319+ . chomp
320+
321+ variables = {
322+ sha : sha ,
323+ now : now ,
324+ title : "Man Page: puppet #{ app } " ,
325+ canonical : "/puppet/latest/man/#{ app } .html" ,
326+ body : body
327+ }
328+
329+ content = render_erb ( MAN_ERB , variables )
330+ output = File . join ( MANDIR , "#{ app } .md" )
331+ File . write ( output , content )
332+ puts "Generated #{ output } "
333+ end
334+ end
335+
336+ desc "Generate resource type references"
337+ task :type do
338+ FileUtils . mkdir_p ( TYPES_DIR )
339+
340+ # Locate puppet-strings
341+ begin
342+ require 'puppet-strings'
343+ require 'puppet-strings/version'
344+ rescue LoadError
345+ abort ( "Run `bundle config set with documentation` and `bundle update` to install the `puppet-strings` gem." )
346+ end
347+
348+ sha = %x{git rev-parse HEAD} . chomp
349+ now = Time . now
350+
351+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/strings.rb#L25-L26
352+ Tempfile . create do |tmpfile |
353+ puts "Running puppet strings #{ PuppetStrings ::VERSION } "
354+ PuppetStrings . generate ( [ 'lib/puppet/type/*.rb' ] , json : true , path : tmpfile . path )
355+ strings_data = JSON . load_file ( tmpfile . path )
356+
357+ # convert strings output to data the overview ERB template expects
358+ type_data = extract_resource_types ( strings_data )
359+
360+ # Generate overview.md
361+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L40-L47
362+ types = type_data . keys . reject do |type |
363+ type == 'component' || type == 'whit'
364+ end
365+
366+ variables = {
367+ title : 'Resource types overview' ,
368+ sha : sha ,
369+ now : now ,
370+ types : types
371+ }
372+
373+ # Render overview page
374+ content = render_erb ( TYPES_OVERVIEW_ERB , variables )
375+ File . write ( TYPES_OVERVIEW_MD , content )
376+ puts "Generated #{ TYPES_OVERVIEW_MD } "
377+
378+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L55-L70
379+ # unified page of types
380+ variables = {
381+ title : 'Resource Type Reference (Single-Page)' ,
382+ sha : sha ,
383+ now : now ,
384+ types : unified_page_resource_types
385+ }
386+
387+ content = render_erb ( UNIFIED_TYPE_ERB , variables )
388+ File . write ( UNIFIED_TYPE_MD , content )
389+ puts "Generated #{ UNIFIED_TYPE_MD } "
390+
391+ # Based on https://github.com/puppetlabs/puppet-docs/blob/1a13be3fc6981baa8a96ff832ab090abc986830e/lib/puppet_references/puppet/type.rb#L78-L85
392+ # one type per page
393+ types . each do |type |
394+ variables = {
395+ title : "Resource Type: #{ type } " ,
396+ type : type ,
397+ sha : sha ,
398+ now : now ,
399+ canonical : "/puppet/latest/types/#{ type } .html" ,
400+ body : render_resource_type ( type , type_data [ type ] )
401+ }
402+
403+ content = render_erb ( SINGLE_TYPE_ERB , variables )
404+ output = File . join ( TYPES_DIR , "#{ type } .md" )
405+ File . write ( output , content )
406+ puts "Generated #{ output } "
407+ end
408+ end
409+ end
410+
411+ desc "Generate all reference documentation"
412+ task :all do
413+ %w[ configuration function report metaparameter man type ] . each do |ref |
414+ Rake ::Task [ "references:#{ ref } " ] . invoke
415+ end
416+ end
103417end
0 commit comments