Class: Terradoc

Inherits:
Object
  • Object
show all
Defined in:
lib/terradoc.rb,
lib/terradoc/version.rb

Overview

——————————————————————————– # ——————————————————————————– # ——————————————————————————– #

Constant Summary collapse

VERSION =
'0.1.0'.freeze

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Terradoc

——————————————————————————– # ——————————————————————————– #

Raises:

  • (StandardError)


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
# File 'lib/terradoc.rb', line 11

def initialize(options = {})
    @base_path = '.'
    @base_path = options[:path] unless options[:path].nil?
    @base_path.chomp!('/')
    raise StandardError.new("Path #{@base_path} does not exist - Aborting") unless Dir.exist? @base_path

    @output_file = 'CHANGELOG.md'
    @output_file = options[:output] unless options[:output].nil?

    @files   = []
    @pattern = "#{@base_path}/*.tf"

    @config = {
                'data-sources' => {
                                    'include'        => 'data',
                                    'match'          => /^(?!data).*/,
                                    'regex'          => /^data\s*"(?<name>.*)"\s*".*/,
                                    'split'          => /(?=data\s*")/,
                                    'start'          => '<!--Terradoc-data-sources-start-->',
                                    'end'            => '<!--Terradoc-data-sources-end-->',
                                    'raw_results'    => [],
                                    'sorted_results' => [],
                                    'data_table'     => []
                                  },
                'modules'      => {
                                    'include'        => 'module',
                                    'match'          => /^(?!module).*/,
                                    'regex'          => /^module\s*".*"\s*{\s*source\s*=\s*"(?<name>.*?)"/,
                                    'split'          => /(?=module\s*")/,
                                    'start'          => '<!--Terradoc-modules-start-->',
                                    'end'            => '<!--Terradoc-modules-end-->',
                                    'raw_results'    => [],
                                    'sorted_results' => [],
                                    'data_table'     => []
                                  },
                'outputs'      => {
                                    'include'        => 'output',
                                    'match'          => /^(?!output).*/,
                                    'regex'          => /^output\s*"(?<name>.*)"\s*{\s*(?<details>.*)\s*}/,
                                    'regex_stage_2'  => /description\s*=\s*"(?<description>.*?)".*/,
                                    'split'          => /(?=output\s*")/,
                                    'start'          => '<!--Terradoc-outputs-start-->',
                                    'end'            => '<!--Terradoc-outputs-end-->',
                                    'raw_results'    => [],
                                    'sorted_results' => [],
                                    'data_table'     => []
                                  },
                'resources'    => {
                                    'include'        => 'resource',
                                    'match'          =>  /^(?!resource).*/,
                                    'regex'          => /^resource\s*"(?<name>.*)"\s*".*/,
                                    'split'          => /(?=resource\s*")/,
                                    'start'          => '<!--Terradoc-resources-start-->',
                                    'end'            => '<!--Terradoc-resources-end-->',
                                    'raw_results'    => [],
                                    'sorted_results' => [],
                                    'data_table'     => []
                                  },
                'variables'    => {
                                    'include'        => 'variable',
                                    'match'          => /^(?!variable).*/,
                                    'regex'          => /^variable\s*"(?<name>.*)"\s*{\s*(?<details>.*)\s*}/,
                                    'regex_stage_2'  => /(description|type|default)\s*=\s*(.*?(?=description|type|default\s*=\s*|$))/,
                                    'split'          => /(?=variable\s*")/,
                                    'start'          => '<!--Terradoc-variables-start-->',
                                    'end'            => '<!--Terradoc-variables-end-->',
                                    'raw_results'    => [],
                                    'sorted_results' => [],
                                    'data_table'     => []
                                  }
              }

    terradoc_main
end

Instance Method Details

#clean_string(str) ⇒ Object

——————————————————————————– # ——————————————————————————– #



214
215
216
217
218
# File 'lib/terradoc.rb', line 214

def clean_string(str)
    return str if str.empty?

    return str.strip.gsub(/\"/, '').chomp('.').strip
end

#cleanup_array(array) ⇒ Object

——————————————————————————– # ——————————————————————————– #



141
142
143
144
145
146
# File 'lib/terradoc.rb', line 141

def cleanup_array(array)
    array = array.uniq { |k| k[:name] } if array.count.positive?
    array = array.sort_by { |k| k[:name] } if array.count.positive?

    return array
end

#generate_data_tablesObject

——————————————————————————– # ——————————————————————————– #



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/terradoc.rb', line 160

def generate_data_tables
    if @config['data-sources']['sorted_results'].size.positive?
        @config['data-sources']['data_table'] << '| Name |'
        @config['data-sources']['data_table'] << '| ---- |'
        @config['data-sources']['sorted_results'].each do |item|
            @config['data-sources']['data_table'] << "| #{item[:name]} |"
        end
    else
        @config['data-sources']['data_table'] << '> No data found'
    end

    if @config['modules']['sorted_results'].size.positive?
        @config['modules']['data_table'] << '| Name |'
        @config['modules']['data_table'] << '| ---- |'
        @config['modules']['sorted_results'] .each do |item|
            @config['modules']['data_table']  << "| #{item[:name]} |"
        end
    else
        @config['modules']['data_table'] << '> No data found'
    end

    if @config['outputs']['sorted_results'].size.positive?
        @config['outputs']['data_table'] << '| Name | Description |'
        @config['outputs']['data_table'] << '| ---- | ----------- |'
        @config['outputs']['sorted_results'].each do |item|
            @config['outputs']['data_table'] << "| #{item[:name]} | #{item[:description]} |"
        end
    else
        @config['outputs']['data_table'] << '> No data found'
    end

    if @config['resources']['sorted_results'].size.positive?
        @config['resources']['data_table'] << '| Name |'
        @config['resources']['data_table'] << '| ---- |'
        @config['resources']['sorted_results'].each do |item|
            @config['resources']['data_table'] << "| #{item[:name]} |"
        end
    else
        @config['resources']['data_table'] << '> No data found'
    end

    if @config['variables']['sorted_results'].size.positive?
        @config['variables']['data_table'] << '| Name | Description | Type | Default | Required? |'
        @config['variables']['data_table'] << '| ---- | ----------- | ---- |:-------:|:---------:|'
        @config['variables']['sorted_results'].each do |item|
            @config['variables']['data_table'] << "| #{item[:name]} | #{item[:description]} | #{item[:type]} | #{item[:default]} | #{item[:required]} |"
        end
    else
        @config['variables']['data_table'] << '> No data found'
    end
end

#generate_file_listObject

——————————————————————————– # ——————————————————————————– #



121
122
123
124
125
# File 'lib/terradoc.rb', line 121

def generate_file_list
    Dir.glob(@pattern).each do |file|
        @files << file
    end
end

#generate_output(lines, start_tag, end_tag, data_table) ⇒ Object

——————————————————————————– # ——————————————————————————– #



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/terradoc.rb', line 283

def generate_output(lines, start_tag, end_tag, data_table)
    processed = []

    skip_lines = false
    lines.each do |line|
        if line.casecmp?(end_tag) && skip_lines
            processed << line
            skip_lines = false
            next
        end

        next if skip_lines

        processed << line

        next unless line.casecmp?(start_tag)

        skip_lines = true
        data_table.each do |row|
            processed << row
        end
    end

    return processed
end

#load_file(filename) ⇒ Object

——————————————————————————– # ——————————————————————————– #



270
271
272
273
274
275
276
277
278
279
# File 'lib/terradoc.rb', line 270

def load_file(filename)
    lines = []

    begin
        lines = File.readlines(filename).each(&:chomp!)
    rescue SystemCallError
        raise StandardError.new("Failed to open file #{filename} for reading")
    end
    return lines
end

#process_file(data) ⇒ Object

——————————————————————————– # ——————————————————————————– #



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/terradoc.rb', line 222

def process_file(data)
    @config.each do |key, value|
        next unless data.include?(value['include'])

        items = data.split(value['split'])
        items.each do |item|
            result = {}

            item.strip!
            next if item.match(value['match'])

            matches = item.match(value['regex'])
            next unless matches.size.positive?
            next unless matches.names.include?('name')

            result[:name] = matches[:name].gsub('_', '\_')

            if matches.names.include?('details')
                if key == 'outputs'
                    description = '-'

                    parts = item.match(value['regex_stage_2'])
                    description = clean_string(parts[:description]) if parts.size.positive?

                    result[:description] = description
                else
                    matches[:details].scan(value['regex_stage_2']).each do |key2, value2|
                        result[key2.to_sym] = if key2 == 'default'
                                                  value2
                                              else
                                                  clean_string(value2)
                                              end
                    end

                    result[:required] = if result.key?(:default)
                                            'No'
                                        else
                                            'Yes'
                                        end
                end
            end
            value['raw_results'] << result
        end
    end
end

#process_filesObject

——————————————————————————– # ——————————————————————————– #



129
130
131
132
133
134
135
136
137
# File 'lib/terradoc.rb', line 129

def process_files
    @files.each do |file|
        lines = File.readlines(file)
        lines = lines.reject { |x| x.start_with? '#' }
        data = lines.join(' ').gsub("\r\n", ' ').gsub("\n", ' ').gsub("\r", ' ').squeeze(' ').strip

        process_file(data)
    end
end

#sort_detailsObject

——————————————————————————– # ——————————————————————————– #



150
151
152
153
154
155
156
# File 'lib/terradoc.rb', line 150

def sort_details
    @config['data-sources']['sorted_results'] = cleanup_array(@config['data-sources']['raw_results'])
    @config['modules']['sorted_results'] = cleanup_array(@config['modules']['raw_results'])
    @config['outputs']['sorted_results'] = cleanup_array(@config['outputs']['raw_results'])
    @config['resources']['sorted_results'] = cleanup_array(@config['resources']['raw_results'])
    @config['variables']['sorted_results'] = cleanup_array(@config['variables']['raw_results'])
end

#terradoc_mainObject

——————————————————————————– # ——————————————————————————– #



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
# File 'lib/terradoc.rb', line 88

def terradoc_main
    spinners = TTY::Spinner::Multi.new("[:spinner] Terradoc is processing your files (Path: #{@base_path}, Output File: #{@output_file})")

    sp1 = spinners.register '[:spinner] Locating files'
    sp2 = spinners.register '[:spinner] Processing files'
    sp3 = spinners.register '[:spinner] Sorting the results'
    sp4 = spinners.register '[:spinner] Generating data tables'
    sp5 = spinners.register '[:spinner] Writing output'

    sp1.auto_spin
    sp2.auto_spin
    sp3.auto_spin
    sp4.auto_spin
    sp5.auto_spin

    generate_file_list
    sp1.success

    process_files
    sp2.success

    sort_details
    sp3.success

    generate_data_tables
    sp4.success

    write_output
    sp5.success
end

#write_outputObject

——————————————————————————– # ——————————————————————————– #



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/terradoc.rb', line 311

def write_output
    permissions = 0o0644

    lines = load_file(@output_file)

    lines = generate_output(lines, @config['data-sources']['start'], @config['data-sources']['end'], @config['data-sources']['data_table'])
    lines = generate_output(lines, @config['modules']['start'], @config['modules']['end'], @config['modules']['data_table'])
    lines = generate_output(lines, @config['outputs']['start'], @config['outputs']['end'], @config['outputs']['data_table'])
    lines = generate_output(lines, @config['resources']['start'], @config['resources']['end'], @config['resources']['data_table'])
    lines = generate_output(lines, @config['variables']['start'], @config['variables']['end'], @config['variables']['data_table'])

    begin
        File.open(@output_file, 'w') do |f|
            lines.each do |line|
                f.puts line
            end
            f.chmod(permissions)
        end
    rescue SystemCallError
        raise StandardError.new("Failed to open file #{filename} for writing")
    end
end