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.
- checksums.yaml +4 -4
- data/lib/fsorg.rb +109 -113
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c401683bfc45360ee97ed03f82c6b4175991662e03b553189a7a1766838b578
|
|
4
|
+
data.tar.gz: f95ab0033bb60452eda403e6cd39ed8d7c84e3f66ed27693f98d5eab5663c7a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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 = { :
|
|
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 /\}
|
|
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
|
|
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
|
|
74
|
+
front_matter_raw += "#{line}\n"
|
|
72
75
|
else
|
|
73
|
-
rest += line
|
|
76
|
+
rest += "#{line}\n"
|
|
74
77
|
end
|
|
75
78
|
end
|
|
76
79
|
|
|
77
|
-
front_matter = YAML.
|
|
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
|
|
105
|
-
output << "WRITE #{$~[:filename]}
|
|
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,
|
|
117
|
-
if /^(?<content>.*)#\s.*$/ =~ line
|
|
118
|
-
|
|
119
|
+
@document.lines(chomp: true).each_with_index do |line, _index|
|
|
120
|
+
output << if /^(?<content>.*)#\s.*$/ =~ line
|
|
121
|
+
content
|
|
119
122
|
else
|
|
120
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
203
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
|
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
|
-
|
|
419
|
-
stdout_w.close
|
|
420
|
-
stderr_w.close
|
|
398
|
+
return if dry_run
|
|
421
399
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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.
|
|
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
|