squared 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- module Repo
4
+ module Workspace
5
5
  module Project
6
6
  class Git < Base
7
7
  include Format
8
8
 
9
9
  REF = :git
10
- private_constant :REF
10
+ OPT_PULL = %w[all tags prune ff-only autostash dry-run].freeze
11
+ OPT_FETCH = %w[tags prune prune-tags depth=n dry-run].freeze
12
+ private_constant :REF, :OPT_PULL, :OPT_FETCH
11
13
 
12
14
  class << self
13
15
  def populate(workspace, parallel: [], **)
14
- return if workspace.series[:pull].empty?
16
+ return if workspace.series.pull.empty?
15
17
 
16
18
  desc 'all[git?=rebase|stash]'
17
- task :all, [:git] do |_, args|
19
+ task 'all', [:git] do |_, args|
18
20
  sync = ->(key) { parallel.include?(key) ? :"#{key}:sync" : key }
19
21
  pull = case args.git
20
22
  when 'rebase'
@@ -46,10 +48,10 @@ module Squared
46
48
  @@tasks[REF] = {
47
49
  checkout: %i[branch detach force merge],
48
50
  commit: %i[add amend amend-orig all no-all],
49
- diff: %i[head cached branch],
51
+ diff: %i[head cached branch files],
50
52
  fetch: %i[all submodules unshallow],
51
53
  files: %i[cached modified deleted others],
52
- pull: %i[rebase no-rebase commit no-commit submodules],
54
+ pull: %i[head rebase no-rebase commit no-commit submodules],
53
55
  stash: %i[push pop apply list clear],
54
56
  refs: %i[heads tags],
55
57
  reset: %i[head soft mixed hard merge keep submodules],
@@ -67,13 +69,13 @@ module Squared
67
69
  flags.each do |flag|
68
70
  case action
69
71
  when :pull
70
- desc format_desc(action, flag, %w[all tags prune ff-only autostash dry-run])
72
+ desc format_desc(action, flag, OPT_PULL)
71
73
  task flag, [:opts] do |_, args|
72
74
  opts = collect_args(args, :opts)
73
75
  pull(flag, opts: opts)
74
76
  end
75
77
  when :fetch
76
- desc format_desc(action, flag, %w[tags prune prune-tags depth=n dry-run])
78
+ desc format_desc(action, flag, OPT_FETCH)
77
79
  task flag, [:opts] do |_, args|
78
80
  opts = collect_args(args, :opts)
79
81
  fetch(flag, opts: opts)
@@ -82,7 +84,7 @@ module Squared
82
84
  if flag == :all
83
85
  desc format_desc(action, flag, 'message?')
84
86
  task flag, [:message] do |_, args|
85
- commit(:all, message: args.fetch(:message, nil))
87
+ commit(flag, message: args.fetch(:message, nil))
86
88
  end
87
89
  else
88
90
  desc format_desc(action, flag, 'pathspec+')
@@ -94,8 +96,8 @@ module Squared
94
96
  when :stash
95
97
  if flag == :push
96
98
  desc format_desc(action, flag, 'pathspec*')
97
- task :push, [:pathspec] do |_, args|
98
- stash(:push, collect_args(args, :pathspec))
99
+ task flag, [:pathspec] do |_, args|
100
+ stash(flag, collect_args(args, :pathspec))
99
101
  end
100
102
  else
101
103
  desc format_desc(action, flag, 'commit?')
@@ -120,18 +122,25 @@ module Squared
120
122
  task flag, [:pathspec] do |_, args|
121
123
  files = collect_args(args, :pathspec)
122
124
  index = /^\d+$/.match?(files.first) && !option('index') ? files.shift.to_i : 0
123
- diff(:head, files, index: index)
125
+ diff(flag, files, index: index)
124
126
  end
125
127
  when :cached
126
128
  desc format_desc(action, flag, 'pathspec*')
127
129
  task flag, [:pathspec] do |_, args|
128
- diff(:cached, collect_args(args, :pathspec))
130
+ diff(flag, collect_args(args, :pathspec))
129
131
  end
130
132
  when :branch
131
133
  desc format_desc(action, flag, 'name,pathspec*')
132
134
  task flag, [:name, :pathspec] do |_, args|
133
135
  guard_params(action, flag, args: args, key: :name)
134
- diff(:branch, collect_args(args, :pathspec), branch: args.name)
136
+ diff(flag, collect_args(args, :pathspec), branch: args.name)
137
+ end
138
+ when :files
139
+ desc format_desc(action, flag, 'path1,path2')
140
+ task flag, [:path1, :path2] do |_, args|
141
+ guard_params(action, flag, args: args, key: :path1)
142
+ guard_params(action, flag, args: args, key: :path2)
143
+ diff(flag, [args.path1, args.path2])
135
144
  end
136
145
  end
137
146
  when :checkout
@@ -157,12 +166,12 @@ module Squared
157
166
  commit = args.commit
158
167
  end
159
168
  guard_params('checkout', :branch, args: args, key: :create, pat: /^[Bb]$/) if create
160
- checkout(:branch, branch: args.name, create: create, commit: commit, detach: detach)
169
+ checkout(flag, branch: args.name, create: create, commit: commit, detach: detach)
161
170
  end
162
171
  when :detach
163
172
  desc format_desc(action, flag, 'branch/commit?')
164
173
  task flag, [:commit] do |_, args|
165
- checkout(:detach, commit: args.commit)
174
+ checkout(flag, commit: args.commit)
166
175
  end
167
176
  else
168
177
  desc format_desc(action, flag, 'pathspec*')
@@ -175,7 +184,7 @@ module Squared
175
184
  desc format_desc(action, flag, 'ref?=HEAD,pathspec+')
176
185
  task flag, [:ref, :pathspec] do |_, args|
177
186
  guard_params(action, flag, args: args, key: :pathspec)
178
- reset(:head, collect_args(args, :pathspec), ref: args.ref)
187
+ reset(flag, collect_args(args, :pathspec), ref: args.ref)
179
188
  end
180
189
  else
181
190
  desc format_desc(action, flag, 'ref?=HEAD')
@@ -202,14 +211,8 @@ module Squared
202
211
  elsif flag == :commit || option('commit')
203
212
  cmd << '--commit'
204
213
  end
205
- append_submodules flag
206
- opts.each do |val|
207
- case val
208
- when 'all', 'tags', 'prune', 'ff-only', 'autostash', 'dry-run'
209
- cmd << "--#{val}"
210
- end
211
- end
212
- source(sync: sync, stderr: true, exception: !workspace.multitask?)
214
+ append_pull opts, OPT_PULL, flag
215
+ source(sync: sync, stderr: true, exception: !workspace.series.multiple?)
213
216
  end
214
217
 
215
218
  def rebase
@@ -219,16 +222,8 @@ module Squared
219
222
  def fetch(flag = nil, opts: [])
220
223
  cmd = git_session 'fetch'
221
224
  cmd << '--all' if flag == :all || option('all')
222
- append_submodules flag
223
- opts.each do |val|
224
- case val
225
- when 'tags', 'prune', 'prune-tags', 'dry-run'
226
- cmd << "--#{val}"
227
- else
228
- cmd << "--depth=#{$1}" if /^depth=(\d+)$/.match(val)
229
- end
230
- end
231
- source(sync: invoked_sync?('fetch', flag), stderr: true, exception: !workspace.multitask?)
225
+ append_pull opts, OPT_FETCH, flag
226
+ source(sync: invoked_sync?('fetch', flag), stderr: true, exception: !workspace.series.multiple?)
232
227
  end
233
228
 
234
229
  def stash(flag = :push, files = [], commit: nil)
@@ -236,10 +231,10 @@ module Squared
236
231
  case flag
237
232
  when :apply, :pop
238
233
  cmd << '--index' if option('index')
239
- cmd << commit if commit
234
+ cmd << commit
240
235
  else
241
- %w[all staged include-untracked].each { |val| cmd << "--#{val}" if option(val) }
242
- append_message option('message')
236
+ append_option %w[all staged include-untracked]
237
+ append_message option('message', 'm', zero: false)
243
238
  append_pathspec files
244
239
  end
245
240
  source(sync: invoked_sync?('stash', flag), exception: workspace.exception)
@@ -259,9 +254,7 @@ module Squared
259
254
  'all'
260
255
  end}"
261
256
  end
262
- if (val = option('pathspec'))
263
- append_pathspec split_escape(val)
264
- end
257
+ append_pathspec
265
258
  out, banner = source(io: true)
266
259
  if banner && invoked_sync?('status')
267
260
  print_item banner
@@ -302,17 +295,16 @@ module Squared
302
295
  when :branch
303
296
  cmd << '--detach' if detach == 'd' || option('detach')
304
297
  if create
305
- cmd << "-#{create} #{branch}"
298
+ cmd << "-#{create}" << branch
306
299
  if (val = option('start-point'))
307
300
  cmd << val
308
301
  end
309
302
  else
310
303
  cmd << branch
311
304
  end
312
- cmd << commit if commit
305
+ cmd << commit
313
306
  when :detach
314
- cmd << "--#{flag}"
315
- cmd << commit if commit
307
+ cmd << "--#{flag}" << commit
316
308
  else
317
309
  cmd << "--#{flag}"
318
310
  append_ours
@@ -351,10 +343,13 @@ module Squared
351
343
 
352
344
  def diff(flag, files = [], branch: nil, index: 0)
353
345
  cmd = git_session 'diff'
354
- if /^#[0-9a-f]{5,40}\#$/.match?(sha = files.first)
355
- files.shift
356
- else
357
- sha = nil
346
+ unless flag == :files
347
+ if /^#[0-9a-f]{5,40}\#$/.match?(sha = files.first)
348
+ sha = sha[1..-2]
349
+ files.shift
350
+ else
351
+ sha = nil
352
+ end
358
353
  end
359
354
  if (val = option('unified')).to_i > 0
360
355
  cmd << "--unified=#{val}"
@@ -366,6 +361,8 @@ module Squared
366
361
  cmd << '--merge-base' if option('merge-base')
367
362
  when :branch
368
363
  cmd << branch
364
+ when :files
365
+ cmd << '--no-index'
369
366
  else
370
367
  if (val = option('index')) || index > 0
371
368
  cmd << "HEAD~#{val || index}"
@@ -373,25 +370,24 @@ module Squared
373
370
  cmd << '--merge-base'
374
371
  end
375
372
  end
376
- cmd << sha if sha
377
- append_pathspec files
378
- source
373
+ cmd << sha
374
+ append_pathspec(files, pass: flag == :files)
375
+ source(exception: cmd.include?('--exit-code'))
379
376
  end
380
377
 
381
378
  def commit(flag, files = [], message: nil, pass: false)
382
- message = option('message') if message.nil?
379
+ message ||= option('message', 'm', zero: false)
383
380
  amend = flag.to_s.start_with?('amend')
384
381
  if !message && !amend
385
382
  return if pass
386
383
 
387
- raise ArgumentError, message('commit', 'GIT_MESSAGE="description"', hint: 'missing')
384
+ raise_error('commit', 'GIT_MESSAGE="description"', hint: 'missing')
388
385
  end
389
386
  pathspec = if flag == :all || (amend && files.size == 1 && files.first == '*')
390
387
  '--all'
391
388
  else
392
389
  files = source_path(as_a(files))
393
- raise ArgumentError, message('commit', 'pathspec', hint: 'missing') if files.empty?
394
-
390
+ raise_error('commit', 'pathspec', hint: 'missing') if files.empty?
395
391
  "-- #{files.join(' ')}"
396
392
  end
397
393
  if !push?
@@ -404,7 +400,7 @@ module Squared
404
400
  break
405
401
  end
406
402
  end
407
- raise ArgumentError, message('commit', 'work tree is not usable') unless push?
403
+ raise_error('commit', 'work tree is not usable') unless push?
408
404
 
409
405
  cmd = git_session 'commit'
410
406
  cmd << '--dry-run' if option('dry-run')
@@ -448,9 +444,9 @@ module Squared
448
444
 
449
445
  def source(cmd = @session, exception: true, banner: true, io: false, sync: true, stdout: false, stderr: false)
450
446
  cmd = close_session(cmd)
451
- info cmd
447
+ log.info cmd
452
448
  begin
453
- banner = format_banner(cmd.gsub(trailing_slash(path), ''), banner: banner, multitask: true)
449
+ banner = format_banner(cmd.gsub(File.join(path, ''), ''), banner: banner, multiple: true)
454
450
  cmd = cmd.sub(/^git\b/, "git --work-tree #{shell_quote(path)} --git-dir #{shell_quote(gitdir)}")
455
451
  if io
456
452
  [IO.popen(cmd), banner]
@@ -481,10 +477,10 @@ module Squared
481
477
  end
482
478
  end
483
479
  rescue StandardError => e
484
- error e
480
+ log.error e
485
481
  raise if exception
486
482
 
487
- emphasize e, message('git', cmd)
483
+ warn e
488
484
  end
489
485
  end
490
486
 
@@ -497,7 +493,7 @@ module Squared
497
493
  next if grep && !line.match?(grep)
498
494
 
499
495
  if loglevel
500
- log loglevel, line
496
+ log.add loglevel, line
501
497
  else
502
498
  sub&.each { |h| line = sub_style(line, **h) }
503
499
  if banner
@@ -516,37 +512,53 @@ module Squared
516
512
  return unless verbose?
517
513
 
518
514
  if size > 0
519
- args = theme[:banner]&.reject { |s| s.to_s.end_with?('!') } || []
520
- args << :bold if args.size <= 1
521
- puts print_footer "#{sub_style(size, *args)} #{size == 1 ? type.sub(/s$/, '') : type}"
515
+ styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
516
+ styles << :bold if styles.size <= 1
517
+ puts print_footer("#{size} #{size == 1 ? type.sub(/s$/, '') : type}",
518
+ sub: { styles: styles, pat: /^(\d+)(.+)$/ })
522
519
  else
523
520
  puts empty_status("No #{type} were #{action}", 'grep', grep)
524
521
  end
525
522
  end
526
523
 
527
- def source_path(files)
528
- files.select { |val| source_path?(val) }.map { |val| val == '.' ? '.' : shell_quote(base_path(val.strip)) }
524
+ def source_path(files, pass: false)
525
+ files = files.select { |val| source_path?(val) } unless pass
526
+ files.map { |val| val == '.' ? '.' : shell_quote(base_path(val.strip)) }
529
527
  end
530
528
 
531
529
  def source_path?(val)
532
- return val.to_s.start_with?("#{path}#{::File::SEPARATOR}") if Pathname.new(val).absolute?
530
+ return val.to_s.start_with?(File.join(path, '').to_s) if Pathname.new(val).absolute?
533
531
 
534
532
  !val.match?(%r{^\.\.[/\\]})
535
533
  end
536
534
 
537
535
  private
538
536
 
537
+ def append_pull(opts, list, flag = nil)
538
+ append_submodules flag
539
+ opts.each do |opt|
540
+ if list.include?(opt)
541
+ @session << "--#{opt}"
542
+ elsif opt.match(/^depth=(\d+)$/)
543
+ @session << "--depth=#{$1}"
544
+ end
545
+ end
546
+ end
547
+
539
548
  def append_commit(val)
540
549
  val = val.to_s.strip
541
550
  val.empty? ? 'HEAD' : val
542
551
  end
543
552
 
544
- def append_pathspec(files, expect: false)
545
- files = source_path(files)
546
- if files.empty?
547
- raise ArgumentError, message('pathspec not within worktree', hint: 'invalid') if expect
548
- else
553
+ def append_pathspec(files = [], expect: false, pass: false)
554
+ if files.empty? && (val = option('pathspec'))
555
+ files = split_escape(val)
556
+ end
557
+ files = source_path(files, pass: pass)
558
+ if !files.empty?
549
559
  @session << "-- #{files.join(' ')}"
560
+ elsif expect
561
+ raise_error(pass ? 'pathspec not present' : 'pathspec not within worktree', hint: 'invalid')
550
562
  end
551
563
  end
552
564
 
@@ -555,9 +567,7 @@ module Squared
555
567
  end
556
568
 
557
569
  def append_head
558
- return unless (val = option('head')) || (val = option('tree-ish'))
559
-
560
- @session << val
570
+ @session << (option('head') || option('tree-ish'))
561
571
  end
562
572
 
563
573
  def append_ours
@@ -576,11 +586,19 @@ module Squared
576
586
  end
577
587
  end
578
588
 
579
- def option(val, equals: nil)
580
- ret = ENV.fetch("GIT_#{val.gsub(/\W/, '_').upcase}", '')
581
- return ret == equals.to_s unless equals.nil?
589
+ def append_option(list)
590
+ list.each { |val| @session << "--#{val}" if option(val) }
591
+ end
582
592
 
583
- ret unless ret.empty? || ret == '0'
593
+ def option(*args, equals: nil, zero: true)
594
+ for val in args
595
+ break if (ret = ENV["GIT_#{val.gsub(/\W/, '_').upcase}"])
596
+ end
597
+ if !equals.nil?
598
+ ret == equals.to_s
599
+ elsif !ret.nil? && !ret.empty? && !(ret == '0' && zero)
600
+ ret
601
+ end
584
602
  end
585
603
 
586
604
  def git_session(*cmd)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- module Repo
4
+ module Workspace
5
5
  module Project
6
6
  class Node < Git
7
7
  REF = :node
@@ -33,19 +33,21 @@ module Squared
33
33
  run: nil
34
34
  }.freeze
35
35
 
36
- def initialize(name, path, workspace, *, **kwargs)
36
+ attr_reader :package
37
+
38
+ def initialize(name, path, workspace, *, dev: nil, prod: nil, **kwargs)
37
39
  super
38
40
  initialize_script(REF)
39
41
  if (opts = env('BUILD', strict: true))
40
- raise ArgumentError, message("BUILD_#{@name.upcase}", opts) if @output[0].is_a?(::Array)
41
-
42
+ raise_error("BUILD_#{@name.upcase}", opts) if @output[0].is_a?(::Array)
42
43
  @output[1] = opts
43
44
  else
44
45
  @output[1] = (@script && @script[:run]) || workspace.script
45
46
  end
46
- @dev = workspace.bool_match(env('BUILD', suffix: 'DEV'), kwargs.delete(:dev))
47
- @prod = workspace.bool_match(env('BUILD', suffix: 'PROD'), kwargs.delete(:prod))
47
+ @dev = dev
48
+ @prod = prod
48
49
  @pm = {}
50
+ @package = base_path('package.json')
49
51
  end
50
52
 
51
53
  def populate(*)
@@ -105,7 +107,7 @@ module Squared
105
107
  elsif dir.is_a?(::String)
106
108
  dest = workspace.root_path(dir)
107
109
  elsif dir.is_a?(::Symbol)
108
- dest = Repo.resolve(dir)&.path
110
+ dest = Workspace.resolve(dir)&.path
109
111
  elsif dir.is_a?(Project)
110
112
  dest = dir.path
111
113
  elsif dir.is_a?(::Hash)
@@ -129,7 +131,7 @@ module Squared
129
131
 
130
132
  from = base_path(from)
131
133
  dest = dest.join(subdir, into || project)
132
- warn "cp #{from.join(glob)} #{dest}"
134
+ log.warn "cp #{from.join(glob)} #{dest}"
133
135
  copy_d(from, dest, glob: glob, verbose: verbose?)
134
136
  end
135
137
  end
@@ -192,7 +194,7 @@ module Squared
192
194
  append_nocolor
193
195
  end
194
196
  append_loglevel
195
- run(exception: workspace.exception)
197
+ run(exception: workspace.exception, sync: invoked_sync?('depend'))
196
198
  end
197
199
  end
198
200
 
@@ -200,37 +202,37 @@ module Squared
200
202
  require 'json'
201
203
  rev ||= prod? ? :patch : :minor
202
204
  cmd = pnpm? ? 'pnpm outdated' : 'npm outdated'
203
- print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multitask: true)
205
+ if invoked_sync?('outdated')
206
+ print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multiple: true)
207
+ end
204
208
  pwd = Dir.pwd
205
209
  Dir.chdir(path)
206
- info cmd
210
+ log.info cmd
207
211
  data = `#{cmd} --json --loglevel=error`
208
212
  Dir.chdir(pwd)
209
213
  json = JSON.parse(doc = package.read)
210
- pat = /^(\d+)(\.)(\d+)(\.)(\d+)$/
211
214
  dep1 = json['dependencies'] || {}
212
215
  dep2 = json['devDependencies'] || {}
213
216
  found = []
214
217
  avail = []
215
218
  if !data.empty?
216
- validate = ->(ver) { ver.match?(pat) }
217
219
  JSON.parse(data).each_pair do |key, val|
218
220
  val = val.find { |item| item['dependent'] == json['name'] } if val.is_a?(Array)
219
221
  next unless val && (file = dep1[key] || dep2[key])
220
222
 
221
223
  ch = file[0]
222
- unless ch.match?(/[~^]/)
224
+ unless ch =~ /[~^]/
223
225
  avail << [key, file, val['latest'], true]
224
226
  next
225
227
  end
226
228
  file = file[1..-1]
227
229
  cur = val['current']
228
- want = if rev == :major && validate.(val['latest'])
230
+ want = if rev == :major && val['latest'] =~ SEM_VER
229
231
  [val['latest'], val['wanted']].max { |a, b| a <=> b }
230
232
  else
231
233
  val['wanted']
232
234
  end
233
- next unless (cur != want || file != want) && (validate.(want) || !validate.(file))
235
+ next unless (cur != want || file != want) && (want.match?(SEM_VER) || !file.match?(SEM_VER))
234
236
 
235
237
  a, b = file.split('.')
236
238
  c, d = want.split('.')
@@ -241,7 +243,7 @@ module Squared
241
243
  when :minor
242
244
  upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
243
245
  when :patch
244
- upgrade = a == c && b == d
246
+ upgrade = a == c && b == d && Regexp.last_match(5)
245
247
  end
246
248
  if upgrade
247
249
  next if file == want
@@ -301,7 +303,7 @@ module Squared
301
303
  a = sub_style(a, :bold)
302
304
  sub_style(c, :green, :bold)
303
305
  else
304
- sub_style(c, :green, :bold, pat: pat, index: d)
306
+ sub_style(c, :green, :bold, pat: SEM_VER, index: d)
305
307
  end
306
308
  puts "#{pad_ord.(i, found)}. #{a}#{b.ljust(col2)}#{c}"
307
309
  end
@@ -349,7 +351,7 @@ module Squared
349
351
  elsif args.is_a?(::String)
350
352
  cmd << args
351
353
  else
352
- raise ArgumentError, message("#{cmd.first} script name", hint: args.nil? ? 'missing' : 'invalid')
354
+ raise_error("#{cmd.first} script name", hint: args.nil? ? 'missing' : 'invalid')
353
355
  end
354
356
  cmd.join(' ')
355
357
  end
@@ -366,7 +368,7 @@ module Squared
366
368
  end
367
369
 
368
370
  def depend?
369
- !!@depend || outdated?
371
+ outdated? || !!@depend
370
372
  end
371
373
 
372
374
  def copy?
@@ -465,12 +467,6 @@ module Squared
465
467
  end
466
468
  end
467
469
  end
468
-
469
- private
470
-
471
- def package
472
- @package ||= base_path('package.json')
473
- end
474
470
  end
475
471
  end
476
472
  end