-
Notifications
You must be signed in to change notification settings - Fork 0
/
docs_builder.rb
executable file
·160 lines (124 loc) · 5.51 KB
/
docs_builder.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env ruby
require 'erb'
require 'fileutils'
require 'logger'
require 'optparse'
require 'English'
require_relative 'lib/chef_doc_builder'
VERSION = '0.1.0'.freeze
def nil_or_empty_any?(*values)
values.any? { |v| v.nil? || (v.respond_to?(:empty?) && v.empty?) }
end
# Note - These are escaped Symbol, not String as optparse returns key names with dashes
options = {
'doc-directory': nil,
'log-level': Logger::Severity::INFO,
'template-file': "#{__dir__}/templates/doc_template.erb",
'template-index-file': "#{__dir__}/templates/doc_index.erb",
'cookbook-prefix': Dir.pwd.split('/').last,
'resource-directory': "#{Dir.pwd}/resources",
overwrite: false,
}
optparse = OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
opts.on_head('-h', '--help', 'Prints this help') do
puts opts
exit
end
opts.on_head('-l Level', '--log-level=Level', String, 'Set script log level') { |v| Object.const_get("Logger::Severity::#{v.upcase}") }
opts.on_head('-v', '--version', 'Show version and exit') do
puts VERSION
exit
end
opts.separator('')
opts.separator 'Docs builder options:'
opts.on('-d DIR', '--doc-directory=DIR', String, :REQUIRED, 'Documentation directory (Required)') do |d|
if nil_or_empty_any?(File.expand_path(d))
logger.fatal("Unable to expand the supplied documentation directory #{d}!")
exit 2
end
File.expand_path(d)
end
opts.on('-f File', '--resource-file=File', 'Resource file for single documentation generation') do |r|
raise IOError, "File #{File.expand_path(r)} does not exist" unless File.exist?(File.expand_path(r))
File.expand_path(r)
end
opts.on('-i File', '--index-template=File', String, 'Index template file (Defaults to templates/doc_index.erb)') do |i|
raise IOError, "Template file #{File.expand_path(i)} does not exist" unless File.exist?(File.expand_path(i))
File.expand_path(i)
end
opts.on('-o', '--overwrite', 'Overwrite existing markdown files')
opts.on('-p Prefix', '--cookbook-prefix=Prefix', 'Cookbook prefix override (Defaults to current directory name)')
opts.on('-r Dir', '--resource-directory=Dir', String, 'Resource files directory (Defaults to ./resources)') do |r|
raise IOError, "Directory #{File.expand_path(r)} does not exist" unless Dir.exist?(File.expand_path(r))
File.expand_path(r)
end
opts.on('-t File', '--template=File', String, 'Template file (Defaults to templates/doc_template.erb)') do |t|
raise IOError, "Template file #{File.expand_path(t)} does not exist" unless File.exist?(File.expand_path(t))
File.expand_path(t)
end
end
optparse.parse!(into: options)
logger = Logger.new($stdout, level: options[:'log-level'], progname: File.basename(__FILE__))
# Check for required options
begin
logger.debug("Parsed options: \n#{options}")
missing_opts = [:'doc-directory'].filter { |o| nil_or_empty_any?(options.fetch(o, nil)) }
logger.debug("Found missing options: #{missing_opts.join(', ')}")
raise OptionParser::MissingArgument, missing_opts.join(', ') unless missing_opts.empty?
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
logger.fatal($ERROR_INFO.to_s)
puts("\n#{optparse}\n")
exit 1
end
# Get resource files sans extention
begin
files = if options[:'resource-file']
Array(File.basename(options[:'resource-file'], '.*'))
else
Dir.children(options[:'resource-directory']).filter { |f| File.extname(f).eql?('.rb') }.map { |f| File.basename(f, '.*') }.sort
end
rescue Errno::ENOENT
logger.fatal("Unable to open resource directory #{options[:'resource-directory']}")
exit 3
end
# Build dummy resources
logger.info("Building dummy resources from #{files.count} custom resource definitions")
resources = files.map { |rf| ChefDocBuilder::DummyResource.new("#{options[:'cookbook-prefix']}_#{rf}").load_from_file("#{options[:'resource-directory']}/#{rf}.rb") }
# Render Templates
FileUtils.mkdir_p(options[:'doc-directory'])
doc_count = 0
resources.each do |resource|
filename = "#{resource.name}.md"
if File.exist?(File.join(options[:'doc-directory'], filename)) && !options[:overwrite]
logger.info("Skip creating doc file #{filename} as it already exists and overwrite is not set")
next
end
logger.info("Write file #{filename}")
variables = {
resource_name: filename.delete_suffix('.md'),
actions: resource.actions,
libraries: resource.libraries,
properties: resource.properties,
uses: resource.uses,
provides: resource.provide,
requires: resource.requires,
}
logger.debug("Write file vars #{variables}")
file_content = ERB.new(File.read(options[:'template-file']), trim_mode: '<>').result_with_hash(variables)
File.write(File.join(options[:'doc-directory'], filename), file_content)
doc_count += 1
end
logger.info("Wrote #{doc_count} doc files")
# Index
if (File.exist?(File.join(options[:'doc-directory'], 'README.md')) && !options[:overwrite]) || options[:'resource-file']
logger.info('Skip creating README index file as it already exists and overwrite is not set')
else
logger.info("Updating index file for #{doc_count} doc files")
variables = {}
variables['resources'] = files.map do |file|
{ 'name' => "#{options[:'cookbook-prefix']}_#{file}", 'path' => File.join(options[:'doc-directory'], "#{options[:'cookbook-prefix']}_#{file}.md") }
end
file_content = ERB.new(File.read(options[:'template-index-file']), trim_mode: '<>').result_with_hash(variables)
File.write(File.join(options[:'doc-directory'], 'README.md'), file_content)
end