squared 0.5.22 → 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 +89 -203
- data/README.md +104 -72
- data/lib/squared/common/base.rb +1 -22
- data/lib/squared/common/format.rb +40 -38
- data/lib/squared/common/prompt.rb +58 -40
- data/lib/squared/common/shell.rb +71 -56
- 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 +86 -104
- data/lib/squared/workspace/project/base.rb +544 -401
- data/lib/squared/workspace/project/docker.rb +396 -292
- data/lib/squared/workspace/project/git.rb +352 -341
- data/lib/squared/workspace/project/node.rb +500 -283
- data/lib/squared/workspace/project/python.rb +368 -247
- data/lib/squared/workspace/project/ruby.rb +695 -394
- data/lib/squared/workspace/project/support/class.rb +209 -212
- data/lib/squared/workspace/repo.rb +46 -43
- data/lib/squared/workspace/series.rb +13 -21
- 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,22 +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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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)
|
|
195
205
|
else
|
|
196
206
|
msg = [log_title(level, color: color)]
|
|
197
|
-
if subject
|
|
198
|
-
msg << (color ? sub_style(subject.to_s, (@theme.is_a?(::Hash) && @theme[:subject]) || :bold) : subject)
|
|
199
|
-
end
|
|
207
|
+
msg << (color ? sub_style(subject.to_s, styles: (@theme && @theme[:subject]) || :bold) : subject) if subject
|
|
200
208
|
msg << args.shift if msg.size == 1
|
|
201
209
|
message(msg.join(' '), *args, hint: hint)
|
|
202
210
|
end
|
|
@@ -209,7 +217,7 @@ module Squared
|
|
|
209
217
|
begin
|
|
210
218
|
File.open(pipe, 'a') do |f|
|
|
211
219
|
br = File::SEPARATOR == '\\' ? "\r\n" : "\n"
|
|
212
|
-
args.flatten.each { |val| f.write(
|
|
220
|
+
args.flatten.each { |val| f.write(val.chomp.stripstyle + br) }
|
|
213
221
|
end
|
|
214
222
|
return
|
|
215
223
|
rescue StandardError
|
|
@@ -219,17 +227,16 @@ module Squared
|
|
|
219
227
|
(pipe == 2 ? $stderr : $stdout).puts(*args)
|
|
220
228
|
end
|
|
221
229
|
|
|
222
|
-
alias puts_oe log_console
|
|
223
|
-
|
|
224
230
|
module_function
|
|
225
231
|
|
|
226
232
|
def message(*args, hint: nil, empty: false, space: ARG[:SPACE])
|
|
227
233
|
(empty ? args.reject { |val| val.nil? || (val.respond_to?(:empty?) && val.empty?) } : args)
|
|
228
|
-
.join(space)
|
|
234
|
+
.join(space)
|
|
235
|
+
.subhint(hint)
|
|
229
236
|
end
|
|
230
237
|
|
|
231
238
|
def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, pipe: nil,
|
|
232
|
-
border: @theme
|
|
239
|
+
border: @theme && @theme[:border])
|
|
233
240
|
n = 0
|
|
234
241
|
max = ->(a) { n = [n, a.max_by(&:size).size].max }
|
|
235
242
|
set = ->(s) { Array(s).map(&:to_s).tap { |a| max.call(a) } }
|
|
@@ -241,9 +248,7 @@ module Squared
|
|
|
241
248
|
lines = val.to_s.lines(chomp: true)
|
|
242
249
|
lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(::StandardError))
|
|
243
250
|
end
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
n = cols || max.call(lines)
|
|
251
|
+
n = (cols.is_a?(::Array) ? cols.map(&:size).max : cols) || max.call(lines)
|
|
247
252
|
if $stdout.tty?
|
|
248
253
|
require 'io/console'
|
|
249
254
|
(n = [n, $stdout.winsize[1] - 4].min) rescue nil
|
|
@@ -261,14 +266,14 @@ module Squared
|
|
|
261
266
|
sub.each { |h| s = sub_style(s, **h) }
|
|
262
267
|
s = "#{b0} #{s} #{b0}"
|
|
263
268
|
if border
|
|
264
|
-
s = sub_style(s,
|
|
265
|
-
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))
|
|
266
271
|
end
|
|
267
272
|
s
|
|
268
273
|
end
|
|
269
274
|
out << draw.call(b2, b3)
|
|
270
275
|
if title
|
|
271
|
-
out.concat(title.map { |t| pr.call(t) })
|
|
276
|
+
out.concat(title.map! { |t| pr.call(t) })
|
|
272
277
|
out << draw.call(b6, b7)
|
|
273
278
|
end
|
|
274
279
|
lines.each { |line| out << pr.call(line) }
|
|
@@ -296,22 +301,19 @@ module Squared
|
|
|
296
301
|
else
|
|
297
302
|
pipe = $stdout unless pipe.respond_to?(:puts)
|
|
298
303
|
end
|
|
299
|
-
pipe.puts
|
|
304
|
+
pipe.puts out
|
|
300
305
|
else
|
|
301
306
|
err ? warn(out) : puts(out)
|
|
302
307
|
end
|
|
303
308
|
end
|
|
304
309
|
|
|
305
|
-
def
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def raise_error(*args, hint: nil, kind: ArgumentError)
|
|
314
|
-
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
|
|
315
317
|
end
|
|
316
318
|
end
|
|
317
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,40 +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
|
-
|
|
72
|
-
|
|
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)
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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] }
|
|
79
108
|
end
|
|
80
109
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
b.uniq!
|
|
84
|
-
b.sort!
|
|
85
|
-
return items ? b.map! { |i| items[i - 1] } : b unless multiple.is_a?(::Numeric) && multiple != b.size
|
|
86
|
-
end
|
|
87
|
-
elsif valid.call(ch)
|
|
88
|
-
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]
|
|
89
112
|
end
|
|
90
113
|
end
|
|
91
114
|
attempts -= 1
|
|
@@ -98,12 +121,11 @@ module Squared
|
|
|
98
121
|
puts
|
|
99
122
|
exit 0
|
|
100
123
|
else
|
|
101
|
-
|
|
124
|
+
[] if multiple
|
|
102
125
|
end
|
|
103
126
|
end
|
|
104
127
|
|
|
105
128
|
def readline(msg, history = false, force: nil, multiline: nil, &blk)
|
|
106
|
-
require 'readline'
|
|
107
129
|
multiline = if multiline && Readline.respond_to?(:readmultiline)
|
|
108
130
|
multiline.is_a?(::Enumerable) || block_given? ? multiline : [multiline.to_s]
|
|
109
131
|
end
|
|
@@ -113,20 +135,16 @@ module Squared
|
|
|
113
135
|
elsif block_given?
|
|
114
136
|
Readline.readmultiline(msg, history, &blk)
|
|
115
137
|
else
|
|
116
|
-
Readline.readmultiline(msg, history)
|
|
117
|
-
next if line.strip.empty?
|
|
118
|
-
|
|
119
|
-
multiline.any? { |val| line.split.last.end_with?(val.to_s) }
|
|
120
|
-
end
|
|
138
|
+
Readline.readmultiline(msg, history) { |line| multiline.any? { |val| line.split.last.end_with?(val.to_s) } }
|
|
121
139
|
end
|
|
122
140
|
end
|
|
123
141
|
case force
|
|
124
142
|
when ::TrueClass, ::FalseClass
|
|
125
|
-
msg = "#{msg}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
msg = "#{msg}%s%s " % if multiline
|
|
144
|
+
[' ', multiline.is_a?(::Enumerable) ? "{#{multiline.to_a.join('|')}}" : multiline]
|
|
145
|
+
else
|
|
146
|
+
[force ? ':' : '?', '']
|
|
147
|
+
end
|
|
130
148
|
ret = (prompt.call || '').strip
|
|
131
149
|
multiline.each { |val| break if ret.delete_suffix!(val.to_s) } if multiline.is_a?(::Enumerable)
|
|
132
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,12 +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
|
-
|
|
116
|
-
key
|
|
117
|
-
|
|
118
|
-
shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name,
|
|
119
|
-
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)
|
|
120
136
|
end
|
|
121
137
|
|
|
122
138
|
def line_width(lines)
|
|
@@ -125,7 +141,6 @@ module Squared
|
|
|
125
141
|
end
|
|
126
142
|
|
|
127
143
|
def fill_option(val, **kwargs)
|
|
128
|
-
return val unless val.is_a?(::String)
|
|
129
144
|
return "-#{val}" if val.match?(/\A(?:[a-z]\d*|\d)\z/i)
|
|
130
145
|
|
|
131
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
|