squared 0.4.6 → 0.4.7

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.
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'json'
4
4
  require 'date'
5
+ require 'time'
5
6
  require 'logger'
7
+ require 'digest'
6
8
 
7
9
  module Squared
8
10
  module Workspace
@@ -13,6 +15,7 @@ module Squared
13
15
  include Shell
14
16
  include Prompt
15
17
  include Utils
18
+ include Support
16
19
  include Rake::DSL
17
20
 
18
21
  VAR_SET = %i[parent global envname dependfile dependindex theme run script env pass].freeze
@@ -192,19 +195,24 @@ module Squared
192
195
  return if @log
193
196
 
194
197
  log = log.is_a?(Hash) ? log.dup : { file: log }
195
- unless (file = env('LOG_FILE'))
196
- file = case env('LOG_AUTO')
197
- when 'y', 'year'
198
- "#{@name}-#{Date.today.year}.log"
199
- when 'm', 'month'
200
- "#{@name}-#{Date.today.strftime('%Y-%m')}.log"
201
- when 'd', 'day', '1'
202
- "#{@name}-#{Date.today}.log"
203
- end
204
- end
205
- if file ||= log[:file]
206
- file = Date.today.strftime(file)
207
- file = (dir = env('LOG_DIR')) ? @workspace.home.join(dir, file) : @workspace.home.join(file)
198
+ if (file = env('LOG_FILE'))
199
+ file = Time.now.strftime(file)
200
+ elsif (val = env('LOG_AUTO'))
201
+ file = "#{@name}-%s.log" % [case val
202
+ when 'y', 'year'
203
+ Date.today.year
204
+ when 'm', 'month'
205
+ Date.today.strftime('%Y-%m')
206
+ when 'd', 'day', '1'
207
+ Date.today
208
+ else
209
+ val.include?('%') ? Time.now.strftime(val) : Time.now.strftime('%FT%T%:z')
210
+ end]
211
+ elsif (val = log[:file])
212
+ file = val.is_a?(String) ? Time.now.strftime(val) : "#{@name}-#{Date.today}.log"
213
+ end
214
+ if file
215
+ file = (val = env('LOG_DIR')) ? @workspace.home.join(val, file) : @workspace.home.join(file)
208
216
  begin
209
217
  file = file.realdirpath
210
218
  rescue StandardError => e
@@ -395,7 +403,7 @@ module Squared
395
403
 
396
404
  run_b(@run, sync: sync, from: from) if series?(@run)
397
405
  args = @output
398
- banner = verbosity > 0 if task_invoked?('build', 'build:sync', 'default')
406
+ banner = verbosetype > 1 if task_invoked?('build', 'build:sync', 'default')
399
407
  end
400
408
  if args.all? { |val| val.is_a?(Array) }
401
409
  cmd = []
@@ -450,7 +458,7 @@ module Squared
450
458
  end
451
459
 
452
460
  def archive(*, sync: invoked_sync?('archive'), **)
453
- return unless @archive.is_a?(Array)
461
+ return unless @archive.is_a?(Hash)
454
462
 
455
463
  unpack(path, **@archive, sync: sync, from: :archive)
456
464
  end
@@ -488,12 +496,11 @@ module Squared
488
496
  end
489
497
  else
490
498
  if @clean.is_a?(Enumerable) && !series?(@clean)
491
- as_a(@clean).each do |val|
492
- val = val.to_s
493
- path = basepath(val)
499
+ @clean.each do |val|
500
+ path = basepath(val = val.to_s)
494
501
  if path.directory? && val.match?(%r{[\\/]$})
495
502
  log&.warn "rm -rf #{path}"
496
- FileUtils.rm_rf(path, verbose: verbose)
503
+ rm_rf(path, verbose: verbose)
497
504
  else
498
505
  log&.warn "rm #{path}"
499
506
  (val.include?('*') ? Dir[path] : [path]).each do |file|
@@ -519,13 +526,13 @@ module Squared
519
526
  tasks ||= []
520
527
  split_escape(val).each do |task|
521
528
  if ref?(task.to_sym) && (script = workspace.script_get(:graph, ref: task.to_sym))
522
- tasks += script[:graph]
529
+ tasks.concat(script[:graph])
523
530
  else
524
531
  tasks << task
525
532
  end
526
533
  end
527
534
  end
528
- pass += split_escape(val) if (val = env('GRAPH', suffix: 'PASS'))
535
+ pass.concat(split_escape(val)) if (val = env('GRAPH', suffix: 'PASS'))
529
536
  start, neg = start.partition { |name| !name.start_with?('-') }
530
537
  data = graph_collect(self, start, pass: neg.map! { |name| name[1..-1] })
531
538
  unless out
@@ -582,11 +589,10 @@ module Squared
582
589
  if (val = env('HEADERS')) && (val = parse_json(val, hint: "HEADERS_#{@envname}"))
583
590
  headers = val
584
591
  end
585
- require 'open-uri'
586
592
  data = nil
587
593
  (uri = as_a(uri)).each_with_index do |url, index|
588
594
  last = index == uri.size - 1
589
- URI.open(url, headers) do |f|
595
+ fetch_uri(url, headers) do |f|
590
596
  data = f.read
591
597
  if algo && algo.hexdigest(data) != digest
592
598
  data = nil
@@ -610,7 +616,7 @@ module Squared
610
616
  raise_error('no content', hint: url)
611
617
  end
612
618
  end
613
- ext ||= URI.parse(uri).path[/^.+?\.((?:tar\.)?\w+)$/i, 1]
619
+ ext ||= URI.parse(uri).path[/\.(\w+)(\?|$)/i, 1]
614
620
  if (n = env("#{ext == 'zip' || ext == '7z' ? ext.upcase : 'TAR'}_DEPTH", ignore: false))
615
621
  depth = n.to_i
616
622
  end
@@ -624,7 +630,7 @@ module Squared
624
630
  target.rmtree
625
631
  target.mkpath
626
632
  end
627
- case ext
633
+ case ext.downcase
628
634
  when 'zip', 'aar'
629
635
  session 'unzip', shell_quote(file.path), quote_option('d', target)
630
636
  when 'tar', 'tgz', 'tar.gz', 'tar.xz', 'gz', 'xz'
@@ -648,8 +654,8 @@ module Squared
648
654
  break unless entry.directory?
649
655
 
650
656
  dest = target.join(File.basename(file.path))
651
- FileUtils.mv(entry, dest)
652
- dest.children.each { |child| FileUtils.mv(child, target) }
657
+ mv(entry, dest)
658
+ dest.children.each { |child| mv(child, target) }
653
659
  dest.rmdir
654
660
  target = entry
655
661
  depth -= 1
@@ -1043,7 +1049,7 @@ module Squared
1043
1049
  next if (objs = data.fetch(proj.name, [])).include?(target)
1044
1050
 
1045
1051
  deps << proj
1046
- deps += objs
1052
+ deps.concat(objs)
1047
1053
  end
1048
1054
  end
1049
1055
  data[target.name] = deps.uniq.reject { |proj| proj == target }
@@ -1066,7 +1072,7 @@ module Squared
1066
1072
  end
1067
1073
 
1068
1074
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
1069
- prefix = prefix.to_s.upcase
1075
+ prefix = stripext(prefix.to_s).upcase
1070
1076
  if path && (val = ENV["PATH_#{prefix}"] || PATH[prefix] || PATH[prefix.to_sym])
1071
1077
  cmd[0] = shell_quote(val, force: false)
1072
1078
  end
@@ -1077,9 +1083,9 @@ module Squared
1077
1083
  main ? @session = ret : ret
1078
1084
  end
1079
1085
 
1080
- def session_delete(*list, target: @session)
1086
+ def session_delete(*args, target: @session)
1081
1087
  ret = []
1082
- list.each do |val|
1088
+ args.each do |val|
1083
1089
  pat = /^#{Regexp.escape(shell_option(val))}(?: |=|$)/
1084
1090
  if (key = target.find { |opt| opt.match?(pat) })
1085
1091
  target.delete(key)
@@ -1101,122 +1107,25 @@ module Squared
1101
1107
  cmd.done
1102
1108
  end
1103
1109
 
1104
- def option(*args, prefix: @session&.first, **kwargs)
1110
+ def option(*args, target: @session, prefix: target&.first, **kwargs)
1105
1111
  if prefix
1106
- prefix = File.basename(prefix, File.extname(prefix))
1107
1112
  args.each do |val|
1108
- ret = env("#{prefix}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
1113
+ ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
1109
1114
  return ret if ret
1110
1115
  end
1111
1116
  end
1112
1117
  nil
1113
1118
  end
1114
1119
 
1115
- def option_sanitize(opts, list, target: @session, no: nil, single: nil, args: false, first: false, pass: ['='])
1116
- ret = []
1117
- reg = []
1118
- bare = []
1119
- e = []
1120
- b = []
1121
- m = []
1122
- p = []
1123
- q = []
1124
- qq = []
1125
- i = []
1126
- f = []
1127
- si = []
1128
- list.map do |val|
1129
- x, y = val.split('|')
1130
- if y
1131
- if (n = val.index('='))
1132
- x += val[n..-1]
1133
- end
1134
- [x, y]
1135
- else
1136
- x
1137
- end
1138
- end
1139
- .flatten
1140
- .each do |val|
1141
- if (n = val.index('='))
1142
- flag = val[0, n]
1143
- case val[n + 1]
1144
- when 'e'
1145
- e << flag
1146
- when 'b'
1147
- b << flag
1148
- when 'm'
1149
- m << flag
1150
- when 'q'
1151
- qq << flag if val[n + 2] == 'q'
1152
- q << flag
1153
- when 'p'
1154
- p << flag
1155
- when 'i'
1156
- i << flag
1157
- when 'f'
1158
- f << flag
1159
- when 'n'
1160
- si << flag
1161
- else
1162
- reg << Regexp.escape(flag)
1163
- end
1164
- bare << flag if val.end_with?('?')
1165
- else
1166
- bare << val
1167
- end
1168
- end
1169
- no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
1170
- bare += no
1171
- found = false
1172
- opts.each do |opt|
1173
- next ret << opt if found
1174
-
1175
- if single&.match?(opt)
1176
- target << "-#{opt}"
1177
- elsif bare.include?(opt)
1178
- target << (opt.size == 1 ? "-#{opt}" : "--#{opt}")
1179
- elsif opt.start_with?('no-') && no.include?(name = opt[3..-1])
1180
- target << "--no-#{name}"
1181
- else
1182
- if opt =~ /\A([^=]+)=(.+)\z/
1183
- key = $1
1184
- val = $2
1185
- match = ->(flag, pat) { flag.include?(key) && pat.match?(val) }
1186
- if e.include?(key)
1187
- target << shell_option(key, val)
1188
- elsif q.include?(key)
1189
- target << quote_option(key, val, double: qq.include?(key))
1190
- elsif p.include?(key)
1191
- target << quote_option(key, basepath(val))
1192
- elsif m.include?(key)
1193
- target << basic_option(key, val, merge: true)
1194
- elsif b.include?(key) || match.(i, /^\d+$/) || match.(f, /^\d*(?:\.\d+)?$/) || match.(si, /^-?\d+$/)
1195
- target << basic_option(key, val)
1196
- else
1197
- ret << opt
1198
- end
1199
- opt = key
1200
- else
1201
- ret << opt
1202
- found = true if args
1203
- end
1204
- found = true if first && pass.none? { |s| opt.include?(s) }
1205
- end
1206
- end
1207
- [ret, reg.empty? ? /\A\s+\z/ : /^(#{reg.join('|')})=(.+)$/]
1120
+ def option_sanitize(opts, list, target: @session, **kwargs)
1121
+ op = OptionPartition.new(opts, list, target, project: self, **kwargs)
1122
+ [op.extras, op.values]
1208
1123
  end
1209
1124
 
1210
- def option_clear(opts, target: @session, pass: true, append: false, **kwargs)
1211
- return if opts.empty?
1212
-
1213
- kwargs[:subject] ||= target&.first
1214
- kwargs[:hint] ||= 'unrecognized'
1215
- warn log_message(Logger::WARN, opts.join(', '), pass: true, **kwargs)
1216
- append_value(opts, delim: true) if append
1217
- return if pass || confirm("Run? [#{sub_style(target, styles: theme[:inline])}] [y/N] ", 'N', timeout: 30)
1125
+ def option_clear(opts, target: @session, **kwargs)
1126
+ return unless target
1218
1127
 
1219
- raise_error 'user cancelled'
1128
+ OptionPartition.clear(target, opts, styles: theme[:inline], **kwargs)
1220
1129
  end
1221
1130
 
1222
1131
  def print_item(*val)
@@ -1331,7 +1240,7 @@ module Squared
1331
1240
  out = ["No #{type} were found:", '']
1332
1241
  unless grep.empty?
1333
1242
  i = 0
1334
- out += grep.map { |s| "#{i += 1}. #{s}" }
1243
+ out.concat(grep.map { |s| "#{i += 1}. #{s}" })
1335
1244
  out << ''
1336
1245
  end
1337
1246
  if from
@@ -1358,7 +1267,6 @@ module Squared
1358
1267
  end
1359
1268
 
1360
1269
  def append_hash(data, target: @session, build: false)
1361
- target ||= []
1362
1270
  if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1363
1271
  type = "__#{type}__"
1364
1272
  if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
@@ -1411,22 +1319,17 @@ module Squared
1411
1319
  end
1412
1320
  end
1413
1321
 
1414
- def append_value(*list, target: @session, delim: false, escape: false, quote: true)
1415
- return if (list = list.flatten).empty?
1322
+ def append_value(*list, target: @session, **kwargs)
1323
+ return unless target
1416
1324
 
1417
- target << '--' if delim && !target.include?('--')
1418
- list.map! do |val|
1419
- ret = escape ? shell_escape(val, quote: quote) : shell_quote(val)
1420
- target << ret
1421
- ret
1422
- end
1325
+ OptionPartition.append(target, *list, **kwargs)
1423
1326
  end
1424
1327
 
1425
1328
  def append_first(*list, target: @session, flag: true, equals: false, escape: true, quote: true, force: true,
1426
1329
  **kwargs)
1427
- return if (list = list.flatten).empty?
1330
+ return if list.empty?
1428
1331
 
1429
- list.each do |opt|
1332
+ list.flatten.each do |opt|
1430
1333
  next unless (val = option(opt, **kwargs))
1431
1334
 
1432
1335
  return target << (if flag
@@ -1440,11 +1343,11 @@ module Squared
1440
1343
 
1441
1344
  def append_option(*list, target: @session, no: false, equals: false, escape: true, quote: true, force: true,
1442
1345
  **kwargs)
1443
- return if (list = list.flatten).empty?
1346
+ return if list.empty?
1444
1347
 
1445
1348
  ret = []
1446
- list.each do |flag|
1447
- next unless (val = option(flag, **kwargs))
1349
+ list.flatten.each do |flag|
1350
+ next unless (val = option(flag, target: target, **kwargs))
1448
1351
 
1449
1352
  if val == '0' && no
1450
1353
  flag = "no-#{flag}"
@@ -1456,50 +1359,52 @@ module Squared
1456
1359
  end
1457
1360
 
1458
1361
  def append_nocolor(target: @session)
1459
- target << '--no-color' if !ARG[:COLOR] || stdin? || option('no-color', ignore: false)
1362
+ target << '--no-color' if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0')
1460
1363
  end
1461
1364
 
1462
1365
  def merge_opts(base, data)
1463
- return data unless base
1464
- return base unless data
1465
-
1466
- ret = case data
1467
- when String
1468
- case base
1469
- when String
1470
- "#{base} #{data}"
1471
- when Hash
1472
- "#{append_hash(base).join(' ')} #{data}"
1473
- when Enumerable
1474
- "#{base.to_a.join(' ')} #{data}"
1475
- end
1476
- when Hash
1477
- case base
1478
- when String
1479
- "#{base} #{append_hash(data).join(' ')}"
1480
- when Hash
1481
- base.merge(data)
1482
- when Enumerable
1483
- base.to_a + append_hash(data)
1484
- end
1485
- when Enumerable
1486
- case base
1487
- when String
1488
- "#{base} #{data.to_a.join(' ')}"
1489
- when Hash
1490
- "#{append_hash(base).join(' ')} #{data.to_a.join(' ')}"
1491
- when Enumerable
1492
- base.to_a + data.to_a
1493
- end
1494
- else
1495
- base
1496
- end
1497
- ret || data
1366
+ case data
1367
+ when String
1368
+ case base
1369
+ when String
1370
+ "#{base} #{data}"
1371
+ when Hash
1372
+ "#{append_hash(base).join(' ')} #{data}"
1373
+ when Enumerable
1374
+ "#{base.to_a.join(' ')} #{data}"
1375
+ else
1376
+ data
1377
+ end
1378
+ when Hash
1379
+ case base
1380
+ when String
1381
+ "#{base} #{append_hash(data).join(' ')}"
1382
+ when Hash
1383
+ base.merge(data)
1384
+ when Enumerable
1385
+ Set.new(base.to_a + append_hash(data)).to_a
1386
+ else
1387
+ data
1388
+ end
1389
+ when Enumerable
1390
+ case base
1391
+ when String
1392
+ "#{base} #{data.to_a.join(' ')}"
1393
+ when Hash
1394
+ "#{append_hash(base).join(' ')} #{data.to_a.join(' ')}"
1395
+ when Enumerable
1396
+ Set.new(base.to_a + data.to_a).to_a
1397
+ else
1398
+ data
1399
+ end
1400
+ else
1401
+ base
1402
+ end
1498
1403
  end
1499
1404
 
1500
1405
  def collect_hash(data, pass: [])
1501
1406
  ret = []
1502
- data.each { |key, val| ret += val unless pass.include?(key) }
1407
+ data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1503
1408
  ret
1504
1409
  end
1505
1410
 
@@ -1513,6 +1418,15 @@ module Squared
1513
1418
  ret
1514
1419
  end
1515
1420
 
1421
+ def fetch_uri(*args, **kwargs, &blk)
1422
+ require 'open-uri'
1423
+ if RUBY_VERSION < '2.5'
1424
+ open(*args, **kwargs, &blk)
1425
+ else
1426
+ URI.open(*args, **kwargs, &blk)
1427
+ end
1428
+ end
1429
+
1516
1430
  def param_guard(action, flag, args: nil, key: nil, pat: nil, values: nil)
1517
1431
  if args && key
1518
1432
  val = args.fetch(key, nil)
@@ -1539,12 +1453,11 @@ module Squared
1539
1453
  end
1540
1454
  end
1541
1455
 
1542
- def relativepath(*files, all: false)
1543
- return [] if files.empty?
1456
+ def relativepath(*list, all: false)
1457
+ return [] if list.empty?
1544
1458
 
1545
- pat = /^#{Regexp.escape(File.join(path, ''))}/
1546
- files.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1547
- val = val.absolute? ? val.to_s.sub(pat, '') : val.to_s
1459
+ list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1460
+ val = val.absolute? ? val.relative_path_from(path).to_s : val.to_s
1548
1461
  val = val[2..-1] if val.start_with?('./')
1549
1462
  val = "#{val}*" if all && val.end_with?('/')
1550
1463
  val
@@ -1553,11 +1466,11 @@ module Squared
1553
1466
 
1554
1467
  def projectmap(files, parent: false, pass: true)
1555
1468
  unless parent
1556
- project = files.select { |val| projectpath?(val) }
1557
- raise_error 'pathspec not within worktree' unless pass || files.size == project.size
1558
- files = project
1469
+ proj = files.select { |val| projectpath?(val) }
1470
+ raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
1471
+ files = proj
1559
1472
  end
1560
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val.strip)) }
1473
+ files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
1561
1474
  end
1562
1475
 
1563
1476
  def semver(val)
@@ -1597,7 +1510,18 @@ module Squared
1597
1510
  end
1598
1511
 
1599
1512
  def epochtime
1600
- DateTime.now.strftime('%Q').to_i
1513
+ Time.now.strftime('%s%L').to_i
1514
+ end
1515
+
1516
+ def verbosetype
1517
+ case verbose
1518
+ when true
1519
+ 1
1520
+ when Numeric
1521
+ verbose.succ
1522
+ else
1523
+ 0
1524
+ end
1601
1525
  end
1602
1526
 
1603
1527
  def on(event, from, *args, **kwargs)
@@ -1747,7 +1671,8 @@ module Squared
1747
1671
  end
1748
1672
 
1749
1673
  def projectpath?(val)
1750
- Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?('..')
1674
+ val = Pathname.new(val).cleanpath
1675
+ val.absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?(File.join('..', ''))
1751
1676
  end
1752
1677
 
1753
1678
  def semmajor?(cur, want)
@@ -1767,11 +1692,8 @@ module Squared
1767
1692
  val.is_a?(Array) && val.all? { |p| p.is_a?(Proc) || p.is_a?(Method) }
1768
1693
  end
1769
1694
 
1770
- def session_arg?(*list, target: @session, value: false)
1771
- list.any? do |val|
1772
- pat = /^#{Regexp.escape(shell_option(val))}#{value ? '[ =].' : '(?:[ =]|$)'}/
1773
- target.any? { |opt| opt.match?(pat) }
1774
- end
1695
+ def session_arg?(*args, target: @session, **kwargs)
1696
+ !!target && OptionPartition.arg?(target, *args, **kwargs)
1775
1697
  end
1776
1698
 
1777
1699
  def from_sync?(*val)