Skip to content

Commit 775f89b

Browse files
authored
Merge pull request #9465 from joshcooper/references_types_12057
(PUP-12057) Generate resource type references
2 parents c0efabb + 93fc700 commit 775f89b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+14320
-0
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ group(:documentation, optional: true) do
7676
gem 'gettext-setup', '~> 1.0', require: false, platforms: [:ruby]
7777
gem 'ronn', '~> 0.7.3', require: false, platforms: [:ruby]
7878
gem 'puppet-strings', require: false, platforms: [:ruby]
79+
gem 'pandoc-ruby', require: false, platforms: [:ruby]
7980
end
8081

8182
if File.exist? "#{__FILE__}.local"

rakelib/generate_references.rake

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
require 'tempfile'
22

33
OUTPUT_DIR = 'references'
4+
MANDIR = File.join(OUTPUT_DIR, 'man')
5+
TYPES_DIR = File.join(OUTPUT_DIR, 'types')
6+
47
CONFIGURATION_ERB = File.join(__dir__, 'references/configuration.erb')
58
CONFIGURATION_MD = File.join(OUTPUT_DIR, 'configuration.md')
69
METAPARAMETER_ERB = File.join(__dir__, 'references/metaparameter.erb')
@@ -10,6 +13,14 @@ REPORT_MD = File.join(OUTPUT_DIR, 'report.md')
1013
FUNCTIONS_TEMPLATE_ERB = File.join(__dir__, 'references/functions_template.erb')
1114
FUNCTION_ERB = File.join(__dir__, 'references/function.erb')
1215
FUNCTION_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

1425
def 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}"
3950
end
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\nDefault: `#{attribute['default']}`"
120+
end
121+
if attribute['values']
122+
description = description + "\n\nAllowed 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\nDefault: `#{attribute['default']}`"
135+
end
136+
if attribute['values']
137+
description = description + "\n\nAllowed 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\nDefault: `#{attribute['default']}`"
150+
end
151+
if attribute['values']
152+
description = description + "\n\nAllowed 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+
41180
namespace :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
103417
end

0 commit comments

Comments
 (0)