squared 0.7.4 → 0.7.6
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 +124 -59
- data/README.md +11 -4
- data/lib/squared/common/base.rb +1 -0
- data/lib/squared/common/prompt.rb +1 -1
- data/lib/squared/common/shell.rb +10 -10
- data/lib/squared/common/system.rb +2 -1
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +9 -6
- data/lib/squared/workspace/project/base.rb +28 -21
- data/lib/squared/workspace/project/docker.rb +21 -21
- data/lib/squared/workspace/project/git.rb +158 -125
- data/lib/squared/workspace/project/node.rb +154 -64
- data/lib/squared/workspace/project/python.rb +52 -24
- data/lib/squared/workspace/project/ruby.rb +69 -40
- data/lib/squared/workspace/project/support/optionpartition.rb +72 -34
- data/lib/squared/workspace/project/support/utils.rb +4 -6
- data/lib/squared/workspace/repo.rb +5 -4
- metadata +2 -2
data/lib/squared/common/shell.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Squared
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def shell_quote(val, option: true, force: true, double: false, preserve: true, override: false)
|
|
56
|
+
def shell_quote(val, option: true, force: true, double: false, preserve: true, pass: false, override: false)
|
|
57
57
|
val = val.to_s
|
|
58
58
|
return val if (!force && !val.include?(' ')) || val.empty?
|
|
59
59
|
|
|
@@ -61,16 +61,16 @@ module Squared
|
|
|
61
61
|
pat = /\A(?:-[^=\s-](?:=|\s+)?|(--)?[^=\s-][^=\s]*(?(1)(?:=|\s+)|=))(["']).+\2\z/m
|
|
62
62
|
return val if val.match?(pat)
|
|
63
63
|
end
|
|
64
|
-
q = ->(s) { s.gsub("'\\\\''", "'") }
|
|
65
64
|
if val =~ QUOTE_VALUE
|
|
66
|
-
return val if $1 == '"' && Rake::Win32.windows? && val.match?(/(?:[#{File::SEPARATOR} ]|\\")/o)
|
|
65
|
+
return val if pass || ($1 == '"' && Rake::Win32.windows? && val.match?(/(?:[#{File::SEPARATOR} ]|\\")/o))
|
|
67
66
|
|
|
68
67
|
base = $2 unless preserve
|
|
69
68
|
end
|
|
69
|
+
q = -> { (base || val).gsub("'\\\\''", "'") }
|
|
70
70
|
if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
|
|
71
|
-
"\"#{q.call
|
|
71
|
+
"\"#{q.call.gsub(/(?<!\\)"/, '\\"')}\""
|
|
72
72
|
else
|
|
73
|
-
|
|
73
|
+
"'#{q.call.gsub("'", "'\\\\''")}'"
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
|
|
@@ -124,18 +124,18 @@ module Squared
|
|
|
124
124
|
b = []
|
|
125
125
|
c = []
|
|
126
126
|
d = []
|
|
127
|
-
|
|
127
|
+
target = [a, b]
|
|
128
128
|
j = -1
|
|
129
129
|
val.shellsplit.each_with_index do |opt, i|
|
|
130
130
|
if opt == '--'
|
|
131
|
-
|
|
131
|
+
target = [c, d]
|
|
132
132
|
elsif opt =~ /\A--?[^=]+(=|\z)/
|
|
133
133
|
j = $1 == '=' ? -1 : i
|
|
134
|
-
|
|
134
|
+
target[0] << [opt]
|
|
135
135
|
elsif j >= 0
|
|
136
|
-
|
|
136
|
+
target[0][j] << opt
|
|
137
137
|
else
|
|
138
|
-
|
|
138
|
+
target[1] << shell_quote(opt, option: false, force: force)
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
ret = [[a, b], [], [c, d]].flat_map do |e, f|
|
|
@@ -29,6 +29,7 @@ module Squared
|
|
|
29
29
|
module_function
|
|
30
30
|
|
|
31
31
|
def shell(*args, name: :system, **kwargs)
|
|
32
|
+
kwargs.delete(:exception) unless name == :system
|
|
32
33
|
if RUBY_ENGINE == 'jruby' && Rake::Win32.windows?
|
|
33
34
|
ex = kwargs[:exception]
|
|
34
35
|
if (dir = kwargs[:chdir]) && Dir.pwd != dir
|
|
@@ -43,7 +44,7 @@ module Squared
|
|
|
43
44
|
else
|
|
44
45
|
return Kernel.send(name, *args, **kwargs)
|
|
45
46
|
end
|
|
46
|
-
raise $?.to_s if !ret && ex
|
|
47
|
+
raise $?.to_s if !ret && ex
|
|
47
48
|
|
|
48
49
|
ret
|
|
49
50
|
end
|
data/lib/squared/version.rb
CHANGED
|
@@ -206,7 +206,7 @@ module Squared
|
|
|
206
206
|
|
|
207
207
|
return self
|
|
208
208
|
end
|
|
209
|
-
|
|
209
|
+
raise 'method not found'
|
|
210
210
|
rescue => e
|
|
211
211
|
warn log_message(Logger::WARN, e, subject: main, hint: hint || args)
|
|
212
212
|
end
|
|
@@ -226,7 +226,7 @@ module Squared
|
|
|
226
226
|
when Symbol
|
|
227
227
|
@ref = val
|
|
228
228
|
else
|
|
229
|
-
|
|
229
|
+
raise TypeError, "not a group/ref: #{kind || 'nil'}" if block_given?
|
|
230
230
|
end
|
|
231
231
|
if block_given?
|
|
232
232
|
instance_eval(&blk)
|
|
@@ -934,8 +934,8 @@ module Squared
|
|
|
934
934
|
group.each { |val| failed += val.action }
|
|
935
935
|
puts log_message(Logger::ERROR, *failed, subject: 'failed placement', hint: false), pipe: 2
|
|
936
936
|
end
|
|
937
|
-
level.each_with_index do |grp,
|
|
938
|
-
title = "Step #{
|
|
937
|
+
level.each_with_index do |grp, j|
|
|
938
|
+
title = "Step #{j.succ}#{if sync.include?(grp) && !(grp.size == 1 && series.parallel.include?(grp.first))
|
|
939
939
|
' (sync)'
|
|
940
940
|
end}"
|
|
941
941
|
emphasize(grp, title: title, cols: level.flatten(1).push(title), border: theme[:border],
|
|
@@ -950,7 +950,7 @@ module Squared
|
|
|
950
950
|
log_console(*args, pipe: kwargs[:pipe] || pipe)
|
|
951
951
|
end
|
|
952
952
|
|
|
953
|
-
def script_command(task, val, group, ref, on, &blk)
|
|
953
|
+
def script_command(task, val, group, ref, on = nil, &blk)
|
|
954
954
|
if block_given?
|
|
955
955
|
val = Struct::RunData.new(val, blk)
|
|
956
956
|
elsif !val
|
|
@@ -961,7 +961,7 @@ module Squared
|
|
|
961
961
|
as_a group, :to_sym
|
|
962
962
|
else
|
|
963
963
|
label = :ref
|
|
964
|
-
as_a
|
|
964
|
+
as_a(ref || :_, :to_sym)
|
|
965
965
|
end.each do |name|
|
|
966
966
|
@script[label][name][task] = val
|
|
967
967
|
@events[label][name][task] = on if on.is_a?(Hash)
|
|
@@ -1014,6 +1014,8 @@ module Squared
|
|
|
1014
1014
|
nil
|
|
1015
1015
|
elsif ref && target[:ref].key?(ref)
|
|
1016
1016
|
target[:ref][ref]
|
|
1017
|
+
else
|
|
1018
|
+
target[:ref][:_]
|
|
1017
1019
|
end
|
|
1018
1020
|
end
|
|
1019
1021
|
|
|
@@ -1027,6 +1029,7 @@ module Squared
|
|
|
1027
1029
|
require_relative rb
|
|
1028
1030
|
break
|
|
1029
1031
|
end
|
|
1032
|
+
nil
|
|
1030
1033
|
end
|
|
1031
1034
|
|
|
1032
1035
|
def root?(path, pass: [])
|
|
@@ -399,7 +399,7 @@ module Squared
|
|
|
399
399
|
end
|
|
400
400
|
|
|
401
401
|
def with(**kwargs, &blk)
|
|
402
|
-
@withargs = kwargs.empty?
|
|
402
|
+
@withargs = (kwargs unless kwargs.empty?)
|
|
403
403
|
if block_given?
|
|
404
404
|
instance_eval(&blk)
|
|
405
405
|
@withargs = nil
|
|
@@ -862,13 +862,14 @@ module Squared
|
|
|
862
862
|
end
|
|
863
863
|
|
|
864
864
|
def run(cmd = @session, var = nil, exception: exception?, sync: true, banner: true, from: nil, chdir: path,
|
|
865
|
-
interactive: nil, hint: nil, series: false, timeout: nil, **)
|
|
865
|
+
interactive: nil, hint: nil, series: false, timeout: nil, send: :system, **)
|
|
866
866
|
unless cmd
|
|
867
867
|
print_error('no command session started', subject: project, hint: from, pass: true)
|
|
868
868
|
return
|
|
869
869
|
end
|
|
870
870
|
cmd = cmd.target if cmd.is_a?(OptionPartition)
|
|
871
871
|
if interactive && sync && (!@session || !option('y'))
|
|
872
|
+
print_item(series: true)
|
|
872
873
|
msg, y, h = case interactive
|
|
873
874
|
when Array
|
|
874
875
|
interactive
|
|
@@ -901,7 +902,7 @@ module Squared
|
|
|
901
902
|
end
|
|
902
903
|
end
|
|
903
904
|
args = var.is_a?(Hash) ? [var, cmd] : [cmd]
|
|
904
|
-
ret = shell_t(*args, chdir: chdir, exception: exception, timeout: timeout || 0)
|
|
905
|
+
ret = shell_t(*args, name: send, chdir: chdir, exception: exception, timeout: timeout || 0)
|
|
905
906
|
end
|
|
906
907
|
rescue Timeout::Error => e
|
|
907
908
|
print_error(Logger::ERROR, cmd, subject: name, hint: e)
|
|
@@ -1350,7 +1351,7 @@ module Squared
|
|
|
1350
1351
|
else
|
|
1351
1352
|
if series?(obj)
|
|
1352
1353
|
obj.each(&:call)
|
|
1353
|
-
elsif obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
|
|
1354
|
+
elsif obj.is_a?(Array) && obj.any? { |val| val.nil? || !val.is_a?(String) }
|
|
1354
1355
|
build(*obj, **kwargs)
|
|
1355
1356
|
elsif obj
|
|
1356
1357
|
run_s(*Array(obj), **kwargs)
|
|
@@ -1556,8 +1557,8 @@ module Squared
|
|
|
1556
1557
|
|
|
1557
1558
|
def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
|
|
1558
1559
|
prefix = prefix.to_s.stripext
|
|
1559
|
-
if path && (
|
|
1560
|
-
cmd[0] = shell_quote(
|
|
1560
|
+
if path && (bin = shell_bin(prefix))
|
|
1561
|
+
cmd[0] = shell_quote(bin, force: false)
|
|
1561
1562
|
end
|
|
1562
1563
|
ret = JoinSet.new(cmd)
|
|
1563
1564
|
if options
|
|
@@ -1621,7 +1622,7 @@ module Squared
|
|
|
1621
1622
|
args.unshift(*a)
|
|
1622
1623
|
OptionPartition.uniq!(args, pass) if pass
|
|
1623
1624
|
end
|
|
1624
|
-
kwargs&.update(b) { |_,
|
|
1625
|
+
kwargs&.update(b) { |_, obj| obj }
|
|
1625
1626
|
nil
|
|
1626
1627
|
end
|
|
1627
1628
|
|
|
@@ -1667,8 +1668,8 @@ module Squared
|
|
|
1667
1668
|
end
|
|
1668
1669
|
|
|
1669
1670
|
def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
|
|
1670
|
-
grep =
|
|
1671
|
-
sub =
|
|
1671
|
+
grep = (matchmap(grep, prefix) unless grep.empty?)
|
|
1672
|
+
sub = (as_a(sub) unless stdin?)
|
|
1672
1673
|
ret = 0
|
|
1673
1674
|
lines = data.each_with_object([]) do |line, out|
|
|
1674
1675
|
next if grep&.none? { |pat| pat.match?(line) }
|
|
@@ -1789,7 +1790,7 @@ module Squared
|
|
|
1789
1790
|
workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
|
|
1790
1791
|
end
|
|
1791
1792
|
|
|
1792
|
-
def format_banner(cmd, banner: true, hint: nil, strip: nil, quote: false)
|
|
1793
|
+
def format_banner(cmd, banner: true, hint: nil, strip: nil, quote: false, command: true)
|
|
1793
1794
|
return unless banner && banner?
|
|
1794
1795
|
|
|
1795
1796
|
if (data = workspace.banner_get(*@ref, group: group))
|
|
@@ -1802,15 +1803,17 @@ module Squared
|
|
|
1802
1803
|
if verbose
|
|
1803
1804
|
out = []
|
|
1804
1805
|
if data.command
|
|
1805
|
-
if
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1806
|
+
if command
|
|
1807
|
+
if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
|
|
1808
|
+
arg = $3 || $2 || $1
|
|
1809
|
+
cmd = cmd.sub(arg, data.command == 0 ? arg.stripext : arg.stripext.upcase)
|
|
1810
|
+
end
|
|
1811
|
+
if strip || (strip.nil? && data.order.include?(:path))
|
|
1812
|
+
cmd = cmd.gsub(/(?:#{s = Regexp.escape(File.join(path, ''))}?(?=["'])|#{s})/, '')
|
|
1813
|
+
.gsub(/(?: -[^ ])? (?:""|'')/, '')
|
|
1814
|
+
end
|
|
1815
|
+
cmd.gsub!(/(?<= )(["'])([\w.-]+)\1(?= |\z)/, '\2') unless quote
|
|
1812
1816
|
end
|
|
1813
|
-
cmd.gsub!(/(?<= )(["'])([\w.-]+)\1(?= |\z)/, '\2') unless quote
|
|
1814
1817
|
out << cmd.subhint(hint)
|
|
1815
1818
|
end
|
|
1816
1819
|
data.order.each do |val|
|
|
@@ -2129,7 +2132,7 @@ module Squared
|
|
|
2129
2132
|
force = false
|
|
2130
2133
|
end
|
|
2131
2134
|
val = readline(val, force: force)
|
|
2132
|
-
ret << (val.empty?
|
|
2135
|
+
ret << (val unless val.empty?)
|
|
2133
2136
|
end
|
|
2134
2137
|
end
|
|
2135
2138
|
printsucc unless series
|
|
@@ -2188,7 +2191,7 @@ module Squared
|
|
|
2188
2191
|
files = files.select { |val| projectpath?(val) }.tap do |list|
|
|
2189
2192
|
next if pass || files.size == list.size
|
|
2190
2193
|
|
|
2191
|
-
|
|
2194
|
+
raise 'pathspec not within worktree'
|
|
2192
2195
|
end
|
|
2193
2196
|
end
|
|
2194
2197
|
files.map do |val|
|
|
@@ -2392,7 +2395,7 @@ module Squared
|
|
|
2392
2395
|
return if from == false
|
|
2393
2396
|
|
|
2394
2397
|
require 'timeout'
|
|
2395
|
-
unless (path.to_s == Dir.pwd || pass
|
|
2398
|
+
unless (path.to_s == Dir.pwd || pass) && (workspace.mri? || !workspace.windows?)
|
|
2396
2399
|
pwd = Dir.pwd
|
|
2397
2400
|
Dir.chdir(path)
|
|
2398
2401
|
end
|
|
@@ -2715,6 +2718,10 @@ module Squared
|
|
|
2715
2718
|
level.empty? ? ex != false && ex != Logger::INFO : ex.is_a?(Numeric) && level.include?(ex)
|
|
2716
2719
|
end
|
|
2717
2720
|
|
|
2721
|
+
def strict?
|
|
2722
|
+
(ARG[:STRICT] || exception?(Logger::FATAL)) && env('STRICT', notequals: '0')
|
|
2723
|
+
end
|
|
2724
|
+
|
|
2718
2725
|
def serve?
|
|
2719
2726
|
false
|
|
2720
2727
|
end
|
|
@@ -20,7 +20,7 @@ module Squared
|
|
|
20
20
|
sbom=q].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[all-resources ansi
|
|
23
|
+
common: %w[all-resources ansi=b compatibility dry-run env-file=p f|file=p parallel=n profile=b progress=b
|
|
24
24
|
project-directory=p p|project-name=e].freeze,
|
|
25
25
|
build: %w[check no-cache print pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
|
|
26
26
|
provenance=q sbom=q ssh=qq].freeze,
|
|
@@ -399,28 +399,28 @@ module Squared
|
|
|
399
399
|
end
|
|
400
400
|
|
|
401
401
|
def buildx(flag, opts = [], tag: nil, context: nil, from: nil)
|
|
402
|
+
if flag == :bake && context&.match?(%r{^["']?(https?|git)://}i)
|
|
403
|
+
shared = data[:shared].dup.push('f|file=q')
|
|
404
|
+
shared.delete('f|file=p')
|
|
405
|
+
end
|
|
402
406
|
cmd, opts = docker_session('buildx', opts: opts)
|
|
403
407
|
data = OPT_DOCKER[:buildx]
|
|
404
|
-
op = OptionPartition.new(opts, data[:common], cmd, project: self)
|
|
408
|
+
op = OptionPartition.new(opts, data[:common], cmd, project: self, strict: strict?)
|
|
405
409
|
op.append(flag, quote: false)
|
|
406
|
-
.parse(data[flag == :bake ? :bake : :build] + data[:shared])
|
|
410
|
+
.parse(data[flag == :bake ? :bake : :build] + (shared || data[:shared]))
|
|
407
411
|
case flag
|
|
408
412
|
when :build, :context
|
|
409
413
|
append_tag(tag || option('tag', ignore: false) || self.tag)
|
|
410
|
-
|
|
414
|
+
if shared
|
|
415
|
+
op.add_quote(context, preserve: false)
|
|
416
|
+
else
|
|
417
|
+
append_context context
|
|
418
|
+
end
|
|
411
419
|
when :bake
|
|
412
420
|
append_file(0, index: 3) unless from || op.arg?('f', 'file') || !anypath?(*COMPOSEFILE)
|
|
413
421
|
unless op.empty?
|
|
414
|
-
|
|
415
|
-
op.
|
|
416
|
-
if Dir.exist?(args.last)
|
|
417
|
-
if projectpath?(val = args.pop)
|
|
418
|
-
context = val
|
|
419
|
-
else
|
|
420
|
-
op.push(val)
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
op.append(args, escape: true, strip: /^:/)
|
|
422
|
+
context = op.pop if Dir.exist?(op.last) && projectpath?(op.last)
|
|
423
|
+
op.append(escape: true, strip: /^:/, clear: true)
|
|
424
424
|
contextdir context if context
|
|
425
425
|
end
|
|
426
426
|
end
|
|
@@ -442,7 +442,7 @@ module Squared
|
|
|
442
442
|
docker_session('compose', command, '--', *service)
|
|
443
443
|
else
|
|
444
444
|
cmd, opts = docker_session('compose', opts: opts)
|
|
445
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
|
|
445
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self, strict: strict?)
|
|
446
446
|
append_file(filetype, force: flag == :publish) unless op.arg?('f', 'file')
|
|
447
447
|
op << flag
|
|
448
448
|
op.parse(OPT_DOCKER[:compose].fetch(flag, []))
|
|
@@ -479,7 +479,7 @@ module Squared
|
|
|
479
479
|
list = data.fetch(flag, [])
|
|
480
480
|
list += data[:create] if (rc = flag == :run)
|
|
481
481
|
list += data[:update] if rc ||= flag == :create
|
|
482
|
-
op = OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
|
|
482
|
+
op = OptionPartition.new(opts, list, cmd, project: self, strict: strict?, args: rc || flag == :exec)
|
|
483
483
|
from = symjoin 'container', flag
|
|
484
484
|
case flag
|
|
485
485
|
when :run, :create, :exec
|
|
@@ -562,7 +562,7 @@ module Squared
|
|
|
562
562
|
|
|
563
563
|
def image(flag, opts = [], sync: true, id: nil, registry: nil, filter: nil)
|
|
564
564
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
565
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
|
|
565
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self, strict: strict?)
|
|
566
566
|
exception = exception?
|
|
567
567
|
banner = true
|
|
568
568
|
from = symjoin 'image', flag
|
|
@@ -645,7 +645,7 @@ module Squared
|
|
|
645
645
|
|
|
646
646
|
def network(flag, opts = [], target: nil)
|
|
647
647
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
648
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
|
|
648
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self, strict: strict?)
|
|
649
649
|
.clear
|
|
650
650
|
from = symjoin 'network', flag
|
|
651
651
|
if flag == :create
|
|
@@ -712,7 +712,7 @@ module Squared
|
|
|
712
712
|
def docker_session(*cmd, opts: nil)
|
|
713
713
|
return session('docker', *cmd) unless opts
|
|
714
714
|
|
|
715
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:common], project: self)
|
|
715
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:common], project: self, strict: strict?)
|
|
716
716
|
[session('docker', *op.to_a, *cmd), op.extras]
|
|
717
717
|
end
|
|
718
718
|
|
|
@@ -898,11 +898,11 @@ module Squared
|
|
|
898
898
|
when :service
|
|
899
899
|
['Choose a service',
|
|
900
900
|
'compose ps -a ' \
|
|
901
|
-
|
|
901
|
+
'--format="table {{.Service}}\t{{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}"']
|
|
902
902
|
else
|
|
903
903
|
['Choose an image',
|
|
904
904
|
'images -a ' \
|
|
905
|
-
|
|
905
|
+
'--format="table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}\t{{.Size}}"']
|
|
906
906
|
end
|
|
907
907
|
lines = `#{docker_output(cmd)}`.lines
|
|
908
908
|
if lines.size <= 1
|