fsorg 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fsorg.rb +109 -113
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68068de4061b604e9b71e8134e9f17dd1d902c0a6e7c269cbfbb0c13d6dd5d24
4
- data.tar.gz: bd84015820aedcd7a7510bdc75d17655552159ff22713a0d63f396950e36e6aa
3
+ metadata.gz: 7c401683bfc45360ee97ed03f82c6b4175991662e03b553189a7a1766838b578
4
+ data.tar.gz: f95ab0033bb60452eda403e6cd39ed8d7c84e3f66ed27693f98d5eab5663c7a3
5
5
  SHA512:
6
- metadata.gz: 61a442489d374f4274df638384f9955323398930f6b002a550e4f288ddfffb68c1d4e596d4eb9f60c5730f57994bb64269325906ae52dec3d93153a92bb9b9d3
7
- data.tar.gz: 293cbf041418dcd7b05fc63234ec5de5505c2e38c3efff48322d15525cc6240fb761deb380dcdec1bf51b39e92844852bcf0d656d5b2bdf118dbb1b98f80e156
6
+ metadata.gz: 83d7e4eae3fd10ab1acca9d12b35880075a33dc4d19cc3e5ecc7c792860dfbd16fcebe401ffd93fc395199b4f6d7a5116bc8061e59ff9f61eb9f25202599c5b4
7
+ data.tar.gz: a8795af243ce4767ebeb885b3cc2dc9765a809da18ce2edb62293a58cd1eee5559b472cc3963fedb04179b01b08200caed39dc74a6b6fdb6c40fd541dea5597c
data/lib/fsorg.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "colorize"
2
4
  require "set"
3
5
  require "shellwords"
@@ -7,17 +9,17 @@ PERMISSIONS_PATTERN = /[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+/
7
9
 
8
10
  def d(thing)
9
11
  p thing
10
- return thing
12
+ thing
11
13
  end
12
14
 
13
- class Fsorg
15
+ class Fsorg # rubocop:disable Metrics/ClassLength,Style/Documentation
14
16
  attr_accessor :data, :document, :document_path, :root_directory
15
17
 
16
18
  def initialize(root_directory, data, document, absolute_path)
17
19
  @root_directory = root_directory
18
20
  @shell = "/usr/bin/env bash"
19
21
  @document_path = absolute_path
20
- @data = { :HERE => @root_directory.to_s }.merge data
22
+ @data = { HERE: @root_directory.to_s }.merge data
21
23
  @document = document
22
24
  @current_line = 0
23
25
  @current_depth = -1
@@ -29,10 +31,10 @@ class Fsorg
29
31
 
30
32
  def depth_change(line)
31
33
  change = 0
32
- if /\}$/ =~ line
34
+ if /\}\s*$/ =~ line
33
35
  change -= 1
34
36
  end
35
- if /^\{/ =~ line
37
+ if /^\s*\{/ =~ line
36
38
  change += 1
37
39
  end
38
40
  change
@@ -43,7 +45,7 @@ class Fsorg
43
45
  strip_comments
44
46
  desugar
45
47
  process_includes
46
- # TODO process_see
48
+ # TODO: process_see
47
49
  process_for
48
50
  process_if
49
51
  turn_puts_into_runs
@@ -52,13 +54,14 @@ class Fsorg
52
54
  process_shell
53
55
  end
54
56
 
55
- def process_front_matter
57
+ def process_front_matter # rubocop:disable Metrics/MethodLength
56
58
  unless /^-+$/ =~ @document.lines(chomp: true).first
57
59
  return
58
60
  end
59
61
 
60
62
  # Remove front matter from document
61
- front_matter_raw, rest = "", ""
63
+ front_matter_raw = ""
64
+ rest = ""
62
65
  inside_front_matter = false
63
66
  @document.lines(chomp: true).each_with_index do |line, index|
64
67
  @current_linecur = index + 1
@@ -68,19 +71,19 @@ class Fsorg
68
71
  end
69
72
 
70
73
  if inside_front_matter
71
- front_matter_raw += line + "\n"
74
+ front_matter_raw += "#{line}\n"
72
75
  else
73
- rest += line + "\n"
76
+ rest += "#{line}\n"
74
77
  end
75
78
  end
76
79
 
77
- front_matter = YAML.load(front_matter_raw, symbolize_names: true) or {}
80
+ front_matter = YAML.safe_load(front_matter_raw, symbolize_names: true) or {}
78
81
 
79
82
  @data = front_matter.merge @data
80
83
  @document = rest
81
84
  end
82
85
 
83
- def desugar
86
+ def desugar # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
84
87
  output = []
85
88
  inside_write_directive_shorthand = false
86
89
  @document.lines(chomp: true).each_with_index do |line, index|
@@ -101,8 +104,8 @@ class Fsorg
101
104
  output << $~[1]
102
105
  output << $~[2]
103
106
  output << "}"
104
- elsif /^(?<filename>.+?)\s*(\s+\((?<permissions>#{PERMISSIONS_PATTERN.to_s})\))?\s*\[$/.match line.strip
105
- output << "WRITE #{$~[:filename]} " + ($~[:permissions] ? "MODE #{$~[:permissions]}" : "") + " {"
107
+ elsif /^(?<filename>.+?)\s*(\s+\((?<permissions>#{PERMISSIONS_PATTERN})\))?\s*\[$/.match line.strip
108
+ output << "WRITE #{$~[:filename]} #{$~[:permissions] ? "MODE #{$~[:permissions]}" : ""} {"
106
109
  inside_write_directive_shorthand = true
107
110
  else
108
111
  output << line
@@ -113,26 +116,26 @@ class Fsorg
113
116
 
114
117
  def strip_comments
115
118
  output = []
116
- @document.lines(chomp: true).each_with_index do |line, index|
117
- if /^(?<content>.*)#\s.*$/ =~ line
118
- output << content
119
+ @document.lines(chomp: true).each_with_index do |line, _index|
120
+ output << if /^(?<content>.*)#\s.*$/ =~ line
121
+ content
119
122
  else
120
- output << line
123
+ line
121
124
  end
122
125
  end
123
126
  @document = output.join "\n"
124
127
  end
125
128
 
126
- def process_includes
129
+ def process_includes # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
127
130
  output = []
128
131
 
129
132
  @document.lines(chomp: true).each_with_index do |line, index|
130
133
  @current_line = index + 1
131
134
  if line.start_with? "INCLUDE "
132
- filepath = @document_path.parent.join(line.sub /^INCLUDE /, "")
135
+ filepath = @document_path.parent.join(line.sub(/^INCLUDE /, ""))
133
136
  included_raw = File.new(filepath).read.strip
134
137
  included_fsorg = Fsorg.new(@root_directory, @data, included_raw, filepath)
135
- included_fsorg.preprocess
138
+ included_fsorg.preprocess
136
139
  @data = included_fsorg.data.merge @data
137
140
  output += included_fsorg.document.lines(chomp: true)
138
141
  else
@@ -145,30 +148,29 @@ class Fsorg
145
148
 
146
149
  def store_writes
147
150
  output = []
148
-
149
151
 
150
152
  @document = output.join "\n"
151
153
  end
152
154
 
153
- def turn_puts_into_runs
155
+ def turn_puts_into_runs # rubocop:disable Metrics/MethodLength
154
156
  output = []
155
157
  @document.lines(chomp: true).each_with_index do |line, index|
156
158
  @current_line = index + 1
157
- if /^PUT\s+(?<source>.+?)(\s+AS\s+(?<destination>.+?))?(\s+MODE\s+(?<permissions>.+?))?$/.match(line.strip)
158
- output << "RUN install -D \"$DOCUMENT_DIR/#{$~[:source]}\" #{($~[:destination] || $~[:source]).shellescape}" + (if $~[:permissions]
159
+ output << if /^PUT\s+(?<source>.+?)(\s+AS\s+(?<destination>.+?))?(\s+MODE\s+(?<permissions>.+?))?$/.match(line.strip) # rubocop:disable Lint/MixedRegexpCaptureTypes
160
+ "RUN install -D \"$FSORG_ROOT/#{$~[:source]}\" #{($~[:destination] || $~[:source]).shellescape}" + (if $~[:permissions]
159
161
  " -m #{$~[:permissions].shellescape}"
160
162
  else
161
163
  ""
162
164
  end)
163
165
  else
164
- output << line
166
+ line
165
167
  end
166
168
  end
167
169
 
168
170
  @document = output.join "\n"
169
171
  end
170
172
 
171
- def process_for
173
+ def process_for # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
172
174
  output = []
173
175
  inside_for_directive = false
174
176
  body = []
@@ -178,7 +180,7 @@ class Fsorg
178
180
  @current_line = index + 1
179
181
  if inside_for_directive
180
182
  current_depth += depth_change line.strip
181
- if current_depth == 0
183
+ if current_depth.zero?
182
184
  inside_for_directive = false
183
185
  output += repeat_for_each(args[:iteratee], args[:iterator], body)
184
186
  else
@@ -196,14 +198,11 @@ class Fsorg
196
198
  end
197
199
  end
198
200
 
199
- def repeat_for_each(iteratee, iterator, directives)
201
+ def repeat_for_each(iteratee, iterator, directives) # rubocop:disable Metrics/AbcSize
200
202
  output = []
201
- unless data[iterator.to_sym]
202
- raise "[#{@document_path}:#{@current_line}]".colorize :red + "Variable '#{iterator}' not found (iterators cannot be asked for interactively). Available variables at this point: #{data.keys.join(", ")}."
203
- end
204
- unless data[iterator.to_sym].is_a? Array
205
- raise "[#{@document_path}:#{@current_line}]".colorize :red + "Cannot iterate over '#{iterator}', which is of type #{data[iterator.to_sym].class}."
206
- end
203
+ raise "[#{@document_path}:#{@current_line}]".colorize :red + "Variable '#{iterator}' not found (iterators cannot be asked for interactively). Available variables at this point: #{data.keys.join(", ")}." unless data[iterator.to_sym]
204
+ raise "[#{@document_path}:#{@current_line}]".colorize :red + "Cannot iterate over '#{iterator}', which is of type #{data[iterator.to_sym].class}." unless data[iterator.to_sym].is_a? Array
205
+
207
206
  data[iterator.to_sym].each do |item|
208
207
  output += directives.map do |directive|
209
208
  directive.gsub "{{#{iteratee}}}", item.to_s
@@ -212,7 +211,7 @@ class Fsorg
212
211
  output
213
212
  end
214
213
 
215
- def process_if
214
+ def process_if # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
216
215
  output = []
217
216
  inside_if = false
218
217
  body_if = []
@@ -221,11 +220,11 @@ class Fsorg
221
220
  current_condition = nil
222
221
  current_depth = 0
223
222
 
224
- @document.lines(chomp: true).each_with_index do |line, index|
223
+ @document.lines(chomp: true).each_with_index do |line, index| # rubocop:disable Metrics/BlockLength
225
224
  @current_line = index + 1
226
225
  if inside_if
227
226
  current_depth += depth_change line.strip
228
- if current_depth == 0
227
+ if current_depth.zero?
229
228
  inside_if = false
230
229
  inside_else = false
231
230
  output += body_if if evaluates_to_true? current_condition
@@ -234,7 +233,7 @@ class Fsorg
234
233
  end
235
234
  elsif inside_else
236
235
  current_depth += depth_change line.strip
237
- if current_depth == 0
236
+ if current_depth.zero?
238
237
  inside_else = false
239
238
  inside_if = false
240
239
  output += body_else if evaluates_to_false? current_condition
@@ -247,9 +246,8 @@ class Fsorg
247
246
  current_depth = 1
248
247
  inside_if = true
249
248
  elsif /^(\}\s*)?ELSE\s*\{$/.match(line.strip)
250
- if current_condition.nil?
251
- raise "[#{@document_path}:#{@current_line}] Cannot use ELSE without IF."
252
- end
249
+ raise "[#{@document_path}:#{@current_line}] Cannot use ELSE without IF." if current_condition.nil?
250
+
253
251
  inside_else = true
254
252
  current_depth = 1
255
253
  else
@@ -264,29 +262,25 @@ class Fsorg
264
262
  end
265
263
 
266
264
  def evaluates_to_true?(condition)
267
- unless @data.include? condition.to_sym
268
- raise "[#{@document_path}:#{@current_line}] Variable '#{condition}' not found. Available variables at this point: #{data.keys.join(", ")}."
269
- end
265
+ raise "[#{@document_path}:#{@current_line}] Variable '#{condition}' not found. Available variables at this point: #{data.keys.join(", ")}." unless @data.include? condition.to_sym
270
266
 
271
267
  @data[condition.to_sym]
272
268
  end
273
269
 
274
270
  def ask_missing_variables
275
- @document.scan /\{\{(?<variable>[^}]+?)\}\}/ do |variable|
276
- unless @data.include? variable[0].to_sym
277
- @data[variable[0].to_sym] = :ask
278
- end
271
+ @document.scan(/\{\{(?<variable>[^}]+?)\}\}/) do |variable|
272
+ @data[variable[0].to_sym] = :ask unless @data.include? variable[0].to_sym
279
273
  end
280
274
 
281
275
  @data.each do |key, value|
282
276
  if value == :ask
283
277
  print "#{key}? "
284
- @data[key] = YAML.load STDIN.gets.chomp
278
+ @data[key] = YAML.safe_load $stdin.gets.chomp
285
279
  end
286
280
  end
287
281
  end
288
282
 
289
- def process_root
283
+ def process_root # rubocop:disable Metrics/MethodLength
290
284
  @document = @document.lines(chomp: true).map.with_index do |line, index|
291
285
  @current_line = index + 1
292
286
  if /^ROOT\s+(?<root>.+?)$/.match line.strip
@@ -318,18 +312,16 @@ class Fsorg
318
312
  @data[:HERE]
319
313
  end
320
314
 
321
- def walk(dry_run, quiet, verbose)
315
+ def walk(dry_run, quiet, verbose) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
322
316
  current_path = [@root_directory]
323
317
  @data[:HERE] = @root_directory
324
318
  file_to_write = {}
325
319
  inside_write_directive = -> { !file_to_write.keys.empty? }
326
- current_path_as_pathname = -> { current_path.reduce(Pathname.new "") { |path, fragment| path.join fragment } }
320
+ current_path_as_pathname = -> { current_path.reduce(Pathname.new("")) { |path, fragment| path.join fragment } }
327
321
 
328
- if verbose
329
- puts "Data is #{@data.except :HERE}".light_black
330
- end
322
+ puts "Data is #{@data.except :HERE}".light_black if verbose
331
323
 
332
- @document.lines(chomp: true).each_with_index do |line, index|
324
+ @document.lines(chomp: true).each_with_index do |line, index| # rubocop:disable Metrics/BlockLength
333
325
  @current_line = index + 1
334
326
  @current_depth = current_path.length - 1
335
327
 
@@ -337,29 +329,25 @@ class Fsorg
337
329
  line = line.gsub "{{#{key}}}", value.to_s
338
330
  end
339
331
 
340
- if inside_write_directive.()
332
+ if inside_write_directive.call
341
333
  if line.strip == "}"
342
334
  do_write file_to_write, dry_run, quiet
343
335
  file_to_write = {}
344
336
  else
345
- file_to_write[:content] += line + "\n"
337
+ file_to_write[:content] += "#{line}\n"
346
338
  end
347
- elsif /^WRITE(\s+INTO)?\s+(?<destination>.+?)(?:\s+MODE\s+(?<permissions>.+?))?\s*\{$/.match line.strip
339
+ elsif /^WRITE(\s+INTO)?\s+(?<destination>.+?)(?:\s+MODE\s+(?<permissions>.+?))?\s*\{$/.match line.strip # rubocop:disable Lint/MixedRegexpCaptureTypes
348
340
  file_to_write = $~.named_captures.transform_keys(&:to_sym)
349
341
  file_to_write[:content] = ""
350
342
  elsif /^(?<leaf>.+?)\s+\{/ =~ line.strip
351
343
  current_path << leaf
352
- @data[:HERE] = current_path_as_pathname.()
353
- if verbose
354
- puts "currenly #{current_path.map { |fragment| fragment.to_s }.join " -> "}".light_black
355
- end
344
+ @data[:HERE] = current_path_as_pathname.call
345
+ puts "currenly #{current_path.map(&:to_s).join " -> "}".light_black if verbose
356
346
  do_mkpath current_location, dry_run, quiet
357
347
  elsif line.strip == "}"
358
348
  current_path.pop
359
- @data[:HERE] = current_path_as_pathname.()
360
- if verbose
361
- puts "currenly #{current_path.map { |fragment| fragment.to_s }.join " -> "}".light_black
362
- end
349
+ @data[:HERE] = current_path_as_pathname.call
350
+ puts "currenly #{current_path.map(&:to_s).join " -> "}".light_black if verbose
363
351
  elsif /^RUN\s+(?<command>.+?)$/ =~ line.strip
364
352
  environment = {
365
353
  "FSORG_ROOT" => @root_directory.to_s,
@@ -373,58 +361,54 @@ class Fsorg
373
361
  end
374
362
 
375
363
  def do_mkpath(path, dry_run, quiet)
376
- unless quiet
377
- puts "#{" " * @current_depth}+ ".cyan.bold + path.relative_path_from(@root_directory).to_s
378
- end
379
- unless dry_run
380
- path.mkpath
381
- end
364
+ puts "#{" " * @current_depth}+ ".cyan.bold + path.relative_path_from(@root_directory).to_s unless quiet
365
+ path.mkpath unless dry_run
382
366
  end
383
367
 
384
- def do_write(future_file, dry_run, quiet)
368
+ def do_write(future_file, dry_run, quiet) # rubocop:disable Metrics/AbcSize
385
369
  indentation = " " * @current_depth
386
370
  dest = from_relative_to_root(current_location) / future_file[:destination]
387
- unless dest.parent.relative_path_from(@root_directory).to_s == "."
388
- do_mkpath dest.parent, dry_run, quiet
389
- end
371
+ do_mkpath dest.parent, dry_run, quiet unless dest.parent.relative_path_from(@root_directory).to_s == "."
390
372
 
391
373
  unless quiet
392
374
  puts "#{indentation}> ".cyan.bold + dest.relative_path_from(@root_directory).to_s + (future_file[:permissions] ? " mode #{future_file[:permissions]}".yellow : "")
393
375
  end
394
- unless dry_run
395
- dest.write deindent replace_data future_file[:content]
396
- # Not using dest.chmod as the syntax for permissions is more than just integers,
397
- # and matches in fact the exact syntax of chmod's argument, per the manpage, chmod(1) (line "Each MODE is of the form…")
398
- `chmod #{future_file[:permissions]} #{dest}` if future_file[:permissions]
399
- end
376
+
377
+ return if dry_run
378
+
379
+ dest.write ensure_final_newline(dedent(replace_data(future_file[:content])))
380
+ # Not using dest.chmod as the syntax for permissions is more than just integers,
381
+ # and matches in fact the exact syntax of chmod's argument, per the manpage, chmod(1) (line "Each MODE is of the form…")
382
+ `chmod #{future_file[:permissions]} #{dest}` if future_file[:permissions]
400
383
  end
401
384
 
402
385
  def replace_data(content)
403
- content.gsub /\{\{(?<variable>[^}]+?)\}\}/ do |interpolation|
386
+ content.gsub(/\{\{(?<variable>[^}]+?)\}\}/) do |interpolation|
404
387
  variable = interpolation[2..-3]
405
- @data[variable.to_sym]
388
+ @data[variable.to_sym]
406
389
  end
407
390
  end
408
391
 
409
- def do_run(command, inside, environment, dry_run, quiet, verbose)
392
+ def do_run(command, inside, environment, dry_run, quiet, verbose) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/ParameterLists
410
393
  indentation = " " * @current_depth
411
394
  unless quiet
412
395
  puts "#{indentation}$ ".cyan.bold + command + (verbose ? " at #{inside.relative_path_from(@root_directory)}".light_blue + " with ".light_black + (format_environment_hash environment) : "")
413
396
  end
414
- unless dry_run
415
- stdout, stdout_w = IO.pipe
416
- stderr, stderr_w = IO.pipe
417
397
 
418
- system environment, command, { :chdir => inside.to_s, :out => stdout_w, :err => stderr_w }
419
- stdout_w.close
420
- stderr_w.close
398
+ return if dry_run
421
399
 
422
- stdout.read.each_line(chomp: true) do |line|
423
- puts " " + indentation + line
424
- end
425
- stderr.read.each_line(chomp: true) do |line|
426
- puts " " + indentation + line.red
427
- end
400
+ stdout, stdout_w = IO.pipe
401
+ stderr, stderr_w = IO.pipe
402
+
403
+ system environment, command, { chdir: inside.to_s, out: stdout_w, err: stderr_w }
404
+ stdout_w.close
405
+ stderr_w.close
406
+
407
+ stdout.read.each_line(chomp: true) do |line|
408
+ puts " #{indentation}#{line}"
409
+ end
410
+ stderr.read.each_line(chomp: true) do |line|
411
+ puts " #{indentation}#{line.red}"
428
412
  end
429
413
  end
430
414
 
@@ -435,21 +419,33 @@ class Fsorg
435
419
  end
436
420
  end
437
421
 
438
- def deindent(text)
439
- using_tabs = text.lines(chomp: true).any? { |line| /^\t/ =~ line }
440
- indenting_with = using_tabs ? "\t" : " "
441
- depth = text.lines.map do |line|
442
- count = 0
443
- until line[count] != indenting_with
444
- count += 1
422
+ def dedent(text) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
423
+ lines = text.split "\n"
424
+ return text if lines.empty?
425
+
426
+ indents = lines.map do |line|
427
+ if line =~ /\S/
428
+ line.start_with?(" ") ? line.match(/^ +/).offset(0)[1] : 0
445
429
  end
446
- count
447
- end.min
448
- pattern = /^#{using_tabs ? '\t' : " "}{#{depth}}/
430
+ end
431
+ indents.compact!
432
+ if indents.empty?
433
+ # No lines had any non-whitespace characters.
434
+ return ([""] * lines.size).join "\n"
435
+ end
449
436
 
450
- text.lines(chomp: true).map do |line|
451
- line.sub pattern, ""
452
- end.join "\n"
437
+ min_indent = indents.min
438
+ return text if min_indent.zero?
439
+
440
+ lines.map { |line| line =~ /\S/ ? line.gsub(/^ {#{min_indent}}/, "") : line }.join "\n"
441
+ end
442
+
443
+ def ensure_final_newline(text)
444
+ if text.end_with? "\n"
445
+ text
446
+ else
447
+ "#{text}\n"
448
+ end
453
449
  end
454
450
 
455
451
  def capture_output
@@ -458,7 +454,7 @@ def capture_output
458
454
  $stdout = StringIO.new
459
455
  $stderr = StringIO.new
460
456
  yield
461
- return $stdout.string, $stderr.string
457
+ [$stdout.string, $stderr.string]
462
458
  ensure
463
459
  $stdout = old_stdout
464
460
  $stderr = old_stderr
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fsorg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ewen Le Bihan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-27 00:00:00.000000000 Z
11
+ date: 2024-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  requirements: []
97
- rubygems_version: 3.3.8
97
+ rubygems_version: 3.3.25
98
98
  signing_key:
99
99
  specification_version: 4
100
100
  summary: Create directories from a file that describes them