squared 0.5.18 → 0.6.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/CHANGELOG.md +87 -105
- data/README.md +101 -69
- data/lib/squared/common/base.rb +1 -22
- data/lib/squared/common/format.rb +40 -34
- data/lib/squared/common/prompt.rb +57 -34
- data/lib/squared/common/shell.rb +71 -55
- data/lib/squared/common/system.rb +69 -36
- data/lib/squared/common/utils.rb +29 -6
- data/lib/squared/config.rb +27 -34
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +80 -81
- data/lib/squared/workspace/project/base.rb +534 -369
- data/lib/squared/workspace/project/docker.rb +376 -273
- data/lib/squared/workspace/project/git.rb +348 -335
- data/lib/squared/workspace/project/node.rb +499 -272
- data/lib/squared/workspace/project/python.rb +332 -203
- data/lib/squared/workspace/project/ruby.rb +664 -354
- data/lib/squared/workspace/project/support/class.rb +192 -188
- data/lib/squared/workspace/repo.rb +43 -41
- data/lib/squared/workspace/series.rb +6 -6
- data/lib/squared/workspace/support/base.rb +3 -24
- data/lib/squared/workspace/support/variables.rb +48 -0
- data/lib/squared/workspace/support.rb +1 -1
- data/lib/squared/workspace.rb +4 -6
- metadata +3 -3
- data/lib/squared/workspace/support/data.rb +0 -11
|
@@ -39,6 +39,10 @@ module Squared
|
|
|
39
39
|
TEXT_STYLE = [:bold, :dim, :italic, :underline, :blinking, nil, :inverse, :hidden, :strikethrough].freeze
|
|
40
40
|
private_constant :AIX_TERM, :BOX_GRAPH, :BOX_BORDER, :TEXT_STYLE
|
|
41
41
|
|
|
42
|
+
String.define_method(:stripstyle) { gsub(/\x1B\[(?:\d+;?)+m/, '') }
|
|
43
|
+
String.define_method(:stripext) { File.basename(self, '.*') }
|
|
44
|
+
String.define_method(:subhint) { |s| s.nil? || (s.is_a?(::String) && s.empty?) ? self : "#{self} (#{s})" }
|
|
45
|
+
|
|
42
46
|
def enable_aixterm
|
|
43
47
|
unless (colors = __get__(:colors)).frozen?
|
|
44
48
|
colors.update(AIX_TERM)
|
|
@@ -67,7 +71,6 @@ module Squared
|
|
|
67
71
|
end
|
|
68
72
|
wrap = ->(s, n) { "\x1B[#{n.join(';')}m#{s}\x1B[0m" }
|
|
69
73
|
code = []
|
|
70
|
-
args.clear if args.size == 1 && args.first.nil?
|
|
71
74
|
args.concat(Array(styles)).flatten.each_with_index do |type, i|
|
|
72
75
|
next unless type
|
|
73
76
|
|
|
@@ -86,7 +89,7 @@ module Squared
|
|
|
86
89
|
end
|
|
87
90
|
else
|
|
88
91
|
t = type.to_sym
|
|
89
|
-
if (c = __get__(:colors)[t])
|
|
92
|
+
if (c = __get__(:colors)[t] || __get__(:colors)[t.to_s.sub('bright_', '').to_sym])
|
|
90
93
|
if index == -1
|
|
91
94
|
s = wrap.call(s, [c])
|
|
92
95
|
else
|
|
@@ -95,7 +98,7 @@ module Squared
|
|
|
95
98
|
else
|
|
96
99
|
next unless (n = TEXT_STYLE.index(t))
|
|
97
100
|
|
|
98
|
-
s = "\x1B[#{n
|
|
101
|
+
s = "\x1B[#{n.succ}m#{s}\x1B[#{n == 0 ? 22 : n + 21}m"
|
|
99
102
|
end
|
|
100
103
|
end
|
|
101
104
|
if index == -1
|
|
@@ -127,8 +130,8 @@ module Squared
|
|
|
127
130
|
colors = __get__(:colors)
|
|
128
131
|
Array(args).flatten.compact.each do |val|
|
|
129
132
|
if !val.is_a?(::Numeric)
|
|
130
|
-
|
|
131
|
-
ret <<
|
|
133
|
+
k = val.to_sym
|
|
134
|
+
ret << k if colors.key?(k) || colors.key?(k.to_s.sub('bright_', '').to_sym) || TEXT_STYLE.include?(k)
|
|
132
135
|
elsif val.between?(0, 256)
|
|
133
136
|
ret << val
|
|
134
137
|
elsif val < 0 && (b = val.to_s.split('.')[1])
|
|
@@ -152,6 +155,10 @@ module Squared
|
|
|
152
155
|
end
|
|
153
156
|
end
|
|
154
157
|
|
|
158
|
+
def opt_style(styles, pat = nil, index = 1)
|
|
159
|
+
{ styles: styles, pat: pat, index: index }
|
|
160
|
+
end
|
|
161
|
+
|
|
155
162
|
def log_sym(level)
|
|
156
163
|
if level.is_a?(::Numeric)
|
|
157
164
|
case level
|
|
@@ -181,20 +188,23 @@ module Squared
|
|
|
181
188
|
args = args.map(&:to_s)
|
|
182
189
|
if level.is_a?(::Numeric)
|
|
183
190
|
if append && respond_to?(:log)
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
(log rescue nil).tap do |ref|
|
|
192
|
+
ref.add(level, message(subject, *args, hint: hint, space: ', ')) if ref.is_a?(::Logger)
|
|
193
|
+
end
|
|
186
194
|
end
|
|
187
|
-
return false
|
|
195
|
+
return false if !pass && level < ARG[:LEVEL]
|
|
188
196
|
end
|
|
189
|
-
if
|
|
197
|
+
if (args.size > 1 && !hint) || hint == false
|
|
190
198
|
title = log_title(level, color: false)
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
emphasize(args,
|
|
200
|
+
title: title + (subject ? " #{subject}" : ''),
|
|
201
|
+
pipe: -1,
|
|
202
|
+
sub: if color
|
|
203
|
+
opt_style(__get__(:theme)[:logger][log_sym(level)], /\A(#{Regexp.escape(title)})(.*)\z/m)
|
|
204
|
+
end)
|
|
193
205
|
else
|
|
194
206
|
msg = [log_title(level, color: color)]
|
|
195
|
-
if subject
|
|
196
|
-
msg << (color ? sub_style(subject.to_s, (@theme.is_a?(::Hash) && @theme[:subject]) || :bold) : subject)
|
|
197
|
-
end
|
|
207
|
+
msg << (color ? sub_style(subject.to_s, styles: (@theme && @theme[:subject]) || :bold) : subject) if subject
|
|
198
208
|
msg << args.shift if msg.size == 1
|
|
199
209
|
message(msg.join(' '), *args, hint: hint)
|
|
200
210
|
end
|
|
@@ -207,7 +217,7 @@ module Squared
|
|
|
207
217
|
begin
|
|
208
218
|
File.open(pipe, 'a') do |f|
|
|
209
219
|
br = File::SEPARATOR == '\\' ? "\r\n" : "\n"
|
|
210
|
-
args.flatten.each { |val| f.write(
|
|
220
|
+
args.flatten.each { |val| f.write(val.chomp.stripstyle + br) }
|
|
211
221
|
end
|
|
212
222
|
return
|
|
213
223
|
rescue StandardError
|
|
@@ -217,17 +227,16 @@ module Squared
|
|
|
217
227
|
(pipe == 2 ? $stderr : $stdout).puts(*args)
|
|
218
228
|
end
|
|
219
229
|
|
|
220
|
-
alias puts_oe log_console
|
|
221
|
-
|
|
222
230
|
module_function
|
|
223
231
|
|
|
224
232
|
def message(*args, hint: nil, empty: false, space: ARG[:SPACE])
|
|
225
233
|
(empty ? args.reject { |val| val.nil? || (val.respond_to?(:empty?) && val.empty?) } : args)
|
|
226
|
-
.join(space)
|
|
234
|
+
.join(space)
|
|
235
|
+
.subhint(hint)
|
|
227
236
|
end
|
|
228
237
|
|
|
229
238
|
def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, pipe: nil,
|
|
230
|
-
border: @theme
|
|
239
|
+
border: @theme && @theme[:border])
|
|
231
240
|
n = 0
|
|
232
241
|
max = ->(a) { n = [n, a.max_by(&:size).size].max }
|
|
233
242
|
set = ->(s) { Array(s).map(&:to_s).tap { |a| max.call(a) } }
|
|
@@ -239,7 +248,7 @@ module Squared
|
|
|
239
248
|
lines = val.to_s.lines(chomp: true)
|
|
240
249
|
lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(::StandardError))
|
|
241
250
|
end
|
|
242
|
-
n = cols || max.call(lines)
|
|
251
|
+
n = (cols.is_a?(::Array) ? cols.map(&:size).max : cols) || max.call(lines)
|
|
243
252
|
if $stdout.tty?
|
|
244
253
|
require 'io/console'
|
|
245
254
|
(n = [n, $stdout.winsize[1] - 4].min) rescue nil
|
|
@@ -257,14 +266,14 @@ module Squared
|
|
|
257
266
|
sub.each { |h| s = sub_style(s, **h) }
|
|
258
267
|
s = "#{b0} #{s} #{b0}"
|
|
259
268
|
if border
|
|
260
|
-
s = sub_style(s,
|
|
261
|
-
s = sub_style(s,
|
|
269
|
+
s = sub_style(s, **opt_style(border, /\A(#{Regexp.escape(b0)})(.+)\z/m))
|
|
270
|
+
s = sub_style(s, **opt_style(border, /\A(.+)(#{Regexp.escape(b0)})\z/m, 2))
|
|
262
271
|
end
|
|
263
272
|
s
|
|
264
273
|
end
|
|
265
274
|
out << draw.call(b2, b3)
|
|
266
275
|
if title
|
|
267
|
-
out.concat(title.map { |t| pr.call(t) })
|
|
276
|
+
out.concat(title.map! { |t| pr.call(t) })
|
|
268
277
|
out << draw.call(b6, b7)
|
|
269
278
|
end
|
|
270
279
|
lines.each { |line| out << pr.call(line) }
|
|
@@ -292,22 +301,19 @@ module Squared
|
|
|
292
301
|
else
|
|
293
302
|
pipe = $stdout unless pipe.respond_to?(:puts)
|
|
294
303
|
end
|
|
295
|
-
pipe.puts
|
|
304
|
+
pipe.puts out
|
|
296
305
|
else
|
|
297
306
|
err ? warn(out) : puts(out)
|
|
298
307
|
end
|
|
299
308
|
end
|
|
300
309
|
|
|
301
|
-
def
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
def raise_error(*args, hint: nil, kind: ArgumentError)
|
|
310
|
-
raise kind, message(*args, hint: hint, empty: true), caller_locations(1).map(&:to_s)
|
|
310
|
+
def raise_error(*args, hint: nil, kind: RuntimeError, start: 0)
|
|
311
|
+
kind = args.shift if args.first.is_a?(::Class) && args.first < ::Exception
|
|
312
|
+
raise kind, message(*args, hint: hint, empty: true), if ARG[:BACKTRACE]
|
|
313
|
+
caller(start.succ)
|
|
314
|
+
else
|
|
315
|
+
caller_locations(start.succ, 1).first&.base_label
|
|
316
|
+
end
|
|
311
317
|
end
|
|
312
318
|
end
|
|
313
319
|
end
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
unless defined?(Readline)
|
|
4
|
+
if RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.7'
|
|
5
|
+
require 'readline'
|
|
6
|
+
else
|
|
7
|
+
begin
|
|
8
|
+
require 'reline'
|
|
9
|
+
Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline)
|
|
10
|
+
Readline = Reline
|
|
11
|
+
rescue LoadError
|
|
12
|
+
require 'readline'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
3
17
|
module Squared
|
|
4
18
|
module Common
|
|
5
19
|
module Prompt
|
|
6
20
|
module_function
|
|
7
21
|
|
|
8
|
-
def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts:
|
|
9
|
-
require 'readline'
|
|
22
|
+
def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 3, timeout: 60)
|
|
10
23
|
require 'timeout'
|
|
11
24
|
if agree == 'Y' && cancel == 'N' && !msg.match?(%r{\[(?:Yn|nY|Y/n|y/N)\]})
|
|
12
25
|
case default
|
|
@@ -38,9 +51,8 @@ module Squared
|
|
|
38
51
|
end
|
|
39
52
|
end
|
|
40
53
|
|
|
41
|
-
def choice(msg, list = nil, min: 1, max: 1, multiple: false,
|
|
42
|
-
attempts:
|
|
43
|
-
require 'readline'
|
|
54
|
+
def choice(msg, list = nil, min: 1, max: 1, multiple: false, index: false, grep: nil, border: nil, auto: true,
|
|
55
|
+
force: true, attempts: 3, timeout: 0)
|
|
44
56
|
require 'timeout'
|
|
45
57
|
if list
|
|
46
58
|
grep &&= Array(grep).map { |val| Regexp.new(val) }
|
|
@@ -52,39 +64,51 @@ module Squared
|
|
|
52
64
|
puts '%2d. %s' % [items.size, val]
|
|
53
65
|
end
|
|
54
66
|
max = items.size
|
|
55
|
-
|
|
67
|
+
raise ArgumentError, 'empty selection list' if max == 0
|
|
68
|
+
|
|
56
69
|
min = grep ? 1 : [min, max].min
|
|
57
70
|
if auto
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
auto.times { puts } if auto.is_a?(::Numeric)
|
|
72
|
+
if border == true
|
|
73
|
+
puts print_footer
|
|
74
|
+
elsif border
|
|
75
|
+
puts print_footer(border: border)
|
|
76
|
+
end
|
|
77
|
+
msg = "#{msg + (force ? ':' : '?')} [#{min}-#{max}#{if (n = multiple)
|
|
78
|
+
"|,#{n.is_a?(::Numeric) ? "{#{n}}" : '*'}"
|
|
79
|
+
end}] "
|
|
61
80
|
end
|
|
62
81
|
end
|
|
63
|
-
|
|
82
|
+
between = ->(s) { s.match?(/^\d+$/) && s.to_i.between?(min, max) }
|
|
64
83
|
Timeout.timeout(timeout) do
|
|
65
84
|
while (ch = Readline.readline(msg))
|
|
66
85
|
unless (ch = ch.strip).empty?
|
|
67
86
|
if multiple
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
k = if ch == '*'
|
|
88
|
+
(min..max).to_a
|
|
89
|
+
else
|
|
90
|
+
ch.split(/\s*,\s*/).map! do |s|
|
|
91
|
+
if s =~ /^(\d+)-(\d+)$/
|
|
92
|
+
next unless between.call($1) && between.call($2)
|
|
72
93
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
i = $1.to_i
|
|
95
|
+
j = $2.to_i
|
|
96
|
+
next (i..j).to_a if i < j
|
|
97
|
+
elsif between.call(s)
|
|
98
|
+
s.to_i
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
unless k.include?(nil)
|
|
103
|
+
k.flatten!
|
|
104
|
+
k.uniq!
|
|
105
|
+
k.sort!
|
|
106
|
+
unless multiple.is_a?(::Numeric) && multiple != k.size
|
|
107
|
+
return index || !items ? k : k.map! { |i| items[i.pred] }
|
|
78
108
|
end
|
|
79
109
|
end
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
b.uniq!
|
|
83
|
-
b.sort!
|
|
84
|
-
return items ? b.map! { |i| items[i - 1] } : b unless multiple.is_a?(::Numeric) && multiple != b.size
|
|
85
|
-
end
|
|
86
|
-
elsif valid.call(ch)
|
|
87
|
-
return items ? items[ch.to_i - 1] : ch.to_i
|
|
110
|
+
elsif between.call(ch)
|
|
111
|
+
return index || !items ? ch.to_i : items[ch.to_i.pred]
|
|
88
112
|
end
|
|
89
113
|
end
|
|
90
114
|
attempts -= 1
|
|
@@ -97,12 +121,11 @@ module Squared
|
|
|
97
121
|
puts
|
|
98
122
|
exit 0
|
|
99
123
|
else
|
|
100
|
-
|
|
124
|
+
[] if multiple
|
|
101
125
|
end
|
|
102
126
|
end
|
|
103
127
|
|
|
104
128
|
def readline(msg, history = false, force: nil, multiline: nil, &blk)
|
|
105
|
-
require 'readline'
|
|
106
129
|
multiline = if multiline && Readline.respond_to?(:readmultiline)
|
|
107
130
|
multiline.is_a?(::Enumerable) || block_given? ? multiline : [multiline.to_s]
|
|
108
131
|
end
|
|
@@ -117,11 +140,11 @@ module Squared
|
|
|
117
140
|
end
|
|
118
141
|
case force
|
|
119
142
|
when ::TrueClass, ::FalseClass
|
|
120
|
-
msg = "#{msg}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
143
|
+
msg = "#{msg}%s%s " % if multiline
|
|
144
|
+
[' ', multiline.is_a?(::Enumerable) ? "{#{multiline.to_a.join('|')}}" : multiline]
|
|
145
|
+
else
|
|
146
|
+
[force ? ':' : '?', '']
|
|
147
|
+
end
|
|
125
148
|
ret = (prompt.call || '').strip
|
|
126
149
|
multiline.each { |val| break if ret.delete_suffix!(val.to_s) } if multiline.is_a?(::Enumerable)
|
|
127
150
|
exit 1 if force && ret.empty?
|
data/lib/squared/common/shell.rb
CHANGED
|
@@ -6,14 +6,11 @@ require 'shellwords'
|
|
|
6
6
|
module Squared
|
|
7
7
|
module Common
|
|
8
8
|
module Shell
|
|
9
|
-
QUOTE_VALUE = /\A(["'])(.*)\1\z/m.freeze
|
|
10
|
-
private_constant :QUOTE_VALUE
|
|
11
|
-
|
|
12
9
|
module_function
|
|
13
10
|
|
|
14
11
|
def shell_escape(val, quote: false, force: false, double: false, option: false, override: false)
|
|
15
|
-
if (r = /\A(--?)([
|
|
16
|
-
if (data = r[2].match(
|
|
12
|
+
if (r = /\A(--?)([^= ]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
|
|
13
|
+
if (data = r[2].match(/\A(["'])(.+)\1\z/))
|
|
17
14
|
double = data[1] == '"'
|
|
18
15
|
override = true
|
|
19
16
|
elsif !r[3] || r[6]
|
|
@@ -28,9 +25,9 @@ module Squared
|
|
|
28
25
|
|
|
29
26
|
r[7]
|
|
30
27
|
end
|
|
31
|
-
r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt,
|
|
32
|
-
elsif option && val =~ /\A(
|
|
33
|
-
return val if $2.match?(
|
|
28
|
+
r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, double: double, force: force, override: override)
|
|
29
|
+
elsif option && val =~ /\A([^=]+)=(.+)\z/m
|
|
30
|
+
return val if $2.match?(/\A(["']).+\1\z/m)
|
|
34
31
|
|
|
35
32
|
"#{$1}=%s" % if $2.include?(' ')
|
|
36
33
|
shell_quote($2, option: false)
|
|
@@ -40,7 +37,7 @@ module Squared
|
|
|
40
37
|
Shellwords.escape($2)
|
|
41
38
|
end
|
|
42
39
|
elsif Rake::Win32.windows?
|
|
43
|
-
quote ? shell_quote(val,
|
|
40
|
+
quote ? shell_quote(val, double: double, force: force) : val
|
|
44
41
|
elsif val.empty?
|
|
45
42
|
''
|
|
46
43
|
else
|
|
@@ -48,60 +45,46 @@ module Squared
|
|
|
48
45
|
end
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
def shell_quote(val, option: true, force: true, double: false,
|
|
48
|
+
def shell_quote(val, option: true, force: true, double: false, override: false)
|
|
52
49
|
val = val.to_s
|
|
53
50
|
return val if (!force && !val.include?(' ')) || val.empty?
|
|
54
51
|
|
|
55
|
-
if option
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
q = ->(s) { s.gsub("'\\\\''", "'") }
|
|
60
|
-
if val =~ QUOTE_VALUE
|
|
61
|
-
return val if $1 == '"' && Rake::Win32.windows? && val.match?(/(?:[#{File::SEPARATOR} ]|\\")/o)
|
|
62
|
-
|
|
63
|
-
base = $2 unless preserve
|
|
64
|
-
end
|
|
65
|
-
if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
|
|
66
|
-
"\"#{q.call(base || val).gsub(/(?<!\\)"/, '\\"')}\""
|
|
52
|
+
if option && val.match?(/(?:\A|\A[^=\s]+(?:=|\s+)|#{Rake::Win32.windows? ? '[\\\/]' : '\/'})(["']).+\1\z/mo)
|
|
53
|
+
val
|
|
54
|
+
elsif double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
|
|
55
|
+
"\"#{val.gsub(/(?<!\\)"/, '\\"')}\""
|
|
67
56
|
else
|
|
68
|
-
|
|
57
|
+
"'#{val.gsub("'", "'\\\\''")}'"
|
|
69
58
|
end
|
|
70
59
|
end
|
|
71
60
|
|
|
72
|
-
def shell_option(flag, val = nil,
|
|
73
|
-
|
|
61
|
+
def shell_option(flag, val = nil, sep: '=', escape: true, quote: true, force: true, double: false, merge: false,
|
|
62
|
+
override: false)
|
|
74
63
|
flag = flag.to_s
|
|
75
|
-
if flag =~
|
|
64
|
+
if flag =~ /\A(["'])(.+)\1\z/
|
|
76
65
|
double = $1 == '"'
|
|
77
66
|
flag = $2
|
|
78
67
|
escape = false
|
|
79
68
|
override = true
|
|
80
69
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
elsif quote
|
|
100
|
-
shell_quote(val, option: option, force: force, double: double, override: override)
|
|
101
|
-
else
|
|
102
|
-
val
|
|
103
|
-
end}"
|
|
104
|
-
end}"
|
|
70
|
+
b = if flag[0] == '-'
|
|
71
|
+
flag[1] == '-' ? sep : ' '
|
|
72
|
+
elsif flag.size == 1
|
|
73
|
+
a = '-'
|
|
74
|
+
merge ? '' : ' '
|
|
75
|
+
else
|
|
76
|
+
a = '--'
|
|
77
|
+
sep
|
|
78
|
+
end
|
|
79
|
+
"#{a}#{flag}#{unless val.nil?
|
|
80
|
+
"#{b}#{if escape
|
|
81
|
+
shell_escape(val, quote: quote, double: double, override: override)
|
|
82
|
+
elsif quote
|
|
83
|
+
shell_quote(val, option: false, force: force, double: double, override: override)
|
|
84
|
+
else
|
|
85
|
+
val
|
|
86
|
+
end}"
|
|
87
|
+
end}"
|
|
105
88
|
end
|
|
106
89
|
|
|
107
90
|
def shell_split(val, join: nil, **kwargs)
|
|
@@ -111,11 +94,45 @@ module Squared
|
|
|
111
94
|
ret.join(join.is_a?(::String) ? join : ' ')
|
|
112
95
|
end
|
|
113
96
|
|
|
97
|
+
def shell_parse(val, escape: false, force: true, **kwargs)
|
|
98
|
+
a = []
|
|
99
|
+
b = []
|
|
100
|
+
c = []
|
|
101
|
+
d = []
|
|
102
|
+
e = [a, b]
|
|
103
|
+
j = -1
|
|
104
|
+
val.shellsplit.each_with_index do |opt, i|
|
|
105
|
+
if opt == '--'
|
|
106
|
+
e = [c, d]
|
|
107
|
+
elsif opt =~ /\A--?[^=]+(=|\z)/
|
|
108
|
+
j = $1 == '=' ? -1 : i
|
|
109
|
+
e[0] << [opt]
|
|
110
|
+
elsif j >= 0
|
|
111
|
+
e[0][j] << opt
|
|
112
|
+
else
|
|
113
|
+
e[1] << shell_quote(opt, option: false, force: force)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
ret = [[a, b], [], [c, d]].flat_map do |e, f|
|
|
117
|
+
next '--' unless e
|
|
118
|
+
|
|
119
|
+
e.flat_map do |item|
|
|
120
|
+
if item.size == 1
|
|
121
|
+
fill_option(item.first)
|
|
122
|
+
else
|
|
123
|
+
flag = item.shift
|
|
124
|
+
item.map! { |s| shell_option(flag, s, escape: escape, force: force, **kwargs) }
|
|
125
|
+
end
|
|
126
|
+
end.concat(f)
|
|
127
|
+
end
|
|
128
|
+
ret.pop if ret.last == '--'
|
|
129
|
+
ret
|
|
130
|
+
end
|
|
131
|
+
|
|
114
132
|
def shell_bin(name, env: true)
|
|
115
|
-
key = name.
|
|
116
|
-
key
|
|
117
|
-
|
|
118
|
-
option: false, force: false, double: true)
|
|
133
|
+
key = name.upcase
|
|
134
|
+
shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name, option: false, force: false,
|
|
135
|
+
double: true)
|
|
119
136
|
end
|
|
120
137
|
|
|
121
138
|
def line_width(lines)
|
|
@@ -124,7 +141,6 @@ module Squared
|
|
|
124
141
|
end
|
|
125
142
|
|
|
126
143
|
def fill_option(val, **kwargs)
|
|
127
|
-
return val unless val.is_a?(::String)
|
|
128
144
|
return "-#{val}" if val.match?(/\A(?:[a-z]\d*|\d)\z/i)
|
|
129
145
|
|
|
130
146
|
shell_escape(val.start_with?('-') ? val : "--#{val}", **kwargs)
|
|
@@ -6,6 +6,25 @@ require 'rake'
|
|
|
6
6
|
module Squared
|
|
7
7
|
module Common
|
|
8
8
|
module System
|
|
9
|
+
class << self
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def parse_link(val)
|
|
13
|
+
case val
|
|
14
|
+
when ::TrueClass, 's'
|
|
15
|
+
1
|
|
16
|
+
when 'r'
|
|
17
|
+
2
|
|
18
|
+
when 'h'
|
|
19
|
+
3
|
|
20
|
+
else
|
|
21
|
+
raise ArgumentError, "unrecognized 'link' flag: #{val}" if val
|
|
22
|
+
|
|
23
|
+
0
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
9
28
|
module_function
|
|
10
29
|
|
|
11
30
|
def shell(*args, name: :system, **kwargs)
|
|
@@ -29,11 +48,11 @@ module Squared
|
|
|
29
48
|
raise $?.to_s
|
|
30
49
|
end
|
|
31
50
|
|
|
32
|
-
def copy_dir(src, dest, glob = ['**/*'], create: false, link: nil,
|
|
33
|
-
|
|
51
|
+
def copy_dir(src, dest, glob = ['**/*'], create: false, link: nil, preserve: nil, force: false, verbose: true,
|
|
52
|
+
pass: nil, hidden: false)
|
|
34
53
|
base = Pathname.new(src)
|
|
35
54
|
target = Pathname.new(dest)
|
|
36
|
-
raise
|
|
55
|
+
raise Errno::ENOENT, dest.cleanpath.to_s unless create || target.parent.exist?
|
|
37
56
|
|
|
38
57
|
subdir = {}
|
|
39
58
|
target.mkpath if create
|
|
@@ -57,53 +76,67 @@ module Squared
|
|
|
57
76
|
end
|
|
58
77
|
count = 0
|
|
59
78
|
soft = 0
|
|
79
|
+
type = System.send :parse_link, link
|
|
60
80
|
subdir.each do |dir, files|
|
|
61
|
-
|
|
62
|
-
files.dup
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
if link == 'hard'
|
|
73
|
-
FileUtils.ln(file, dir, force: force, verbose: false)
|
|
74
|
-
else
|
|
75
|
-
FileUtils.ln_s(file, dir, force: force, verbose: false)
|
|
81
|
+
unless type == 0
|
|
82
|
+
items = files.dup
|
|
83
|
+
files.clear
|
|
84
|
+
items.each do |file|
|
|
85
|
+
if file.exist?
|
|
86
|
+
if !file.symlink?
|
|
87
|
+
files << file
|
|
88
|
+
elsif !force
|
|
89
|
+
next
|
|
76
90
|
end
|
|
77
|
-
soft += 1
|
|
78
91
|
end
|
|
92
|
+
case type
|
|
93
|
+
when 1
|
|
94
|
+
FileUtils.ln_s(file, dir, force: force, verbose: false)
|
|
95
|
+
when 2
|
|
96
|
+
FileUtils.ln_s(file.relative_path_from(dir), dir, force: force, verbose: false)
|
|
97
|
+
else
|
|
98
|
+
FileUtils.ln(file, dir, force: force, verbose: false)
|
|
99
|
+
end
|
|
100
|
+
soft += 1
|
|
79
101
|
end
|
|
80
102
|
end
|
|
81
103
|
next if files.empty?
|
|
82
104
|
|
|
83
|
-
out = FileUtils.cp(files, dir, verbose: false)
|
|
105
|
+
out = FileUtils.cp(files, dir, preserve: preserve, verbose: false)
|
|
84
106
|
count += out.size
|
|
85
107
|
end
|
|
86
108
|
puts [target.realpath, subdir.size, soft > 0 ? "#{count}+#{soft}" : count].join(' => ') if verbose
|
|
87
109
|
end
|
|
88
110
|
|
|
89
|
-
def copy_guard(src, dest, link: nil, force: false, verbose: true)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
def copy_guard(*src, dest, base: '.', create: false, link: nil, preserve: nil, force: false, verbose: true)
|
|
112
|
+
src = src.compact.flatten
|
|
113
|
+
dest = Pathname.new(dest).realdirpath
|
|
114
|
+
base = Pathname.new(base).realpath
|
|
115
|
+
dir = if dest.directory?
|
|
116
|
+
true
|
|
117
|
+
elsif src.size > 1
|
|
118
|
+
raise Errno::ENOENT, dest.cleanpath.to_s unless create && !dest.exist?
|
|
119
|
+
|
|
120
|
+
dest.mkpath
|
|
121
|
+
true
|
|
122
|
+
end
|
|
123
|
+
targets = src.map! { |file| [base + file, dir ? dest + File.basename(file) : dest] }
|
|
124
|
+
return if !force && (targets = targets.reject { |to| to[1].exist? }).empty?
|
|
125
|
+
|
|
126
|
+
type = System.send :parse_link, link
|
|
127
|
+
targets.each do |file, to|
|
|
128
|
+
case type
|
|
129
|
+
when 0
|
|
130
|
+
FileUtils.cp(file, to, preserve: preserve, verbose: verbose)
|
|
131
|
+
when 1
|
|
132
|
+
FileUtils.ln_s(file, to, force: force, verbose: verbose)
|
|
133
|
+
when 2
|
|
134
|
+
FileUtils.ln_s(file.relative_path_from(dir ? to.dirname : to), to, force: force, verbose: verbose)
|
|
135
|
+
else
|
|
136
|
+
FileUtils.ln(file, to, force: force, verbose: verbose)
|
|
97
137
|
end
|
|
98
138
|
end
|
|
99
|
-
|
|
100
|
-
when 'hard', 1
|
|
101
|
-
FileUtils.ln(src, dest, force: force, verbose: verbose)
|
|
102
|
-
when ::TrueClass, 'soft', 0
|
|
103
|
-
FileUtils.ln_s(src, dest, force: force, verbose: verbose)
|
|
104
|
-
else
|
|
105
|
-
FileUtils.cp(src, dest, verbose: verbose)
|
|
106
|
-
end
|
|
139
|
+
nil
|
|
107
140
|
end
|
|
108
141
|
end
|
|
109
142
|
end
|