squared 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1795e8352e8278c8f4e26ced26ac936ce906ee8e626a9cf2ff2bf6a86412f09c
4
- data.tar.gz: '08b8e2538b6316ffb141c15e560187222cbb18e0a37f5c74af6ed5fda87e6dc3'
3
+ metadata.gz: fad15a8296b3ab7fec1ff1ac0bbe1f92b65b3bd263e8f9ea38cd62e1a8e3460e
4
+ data.tar.gz: 1ab92094b8d239572ac66a42014aafcca6585f86af1159060715e322d8138503
5
5
  SHA512:
6
- metadata.gz: a8d6bd537e66b744edd760ead7441aad45961bdc746e9ca81f577e8183893cd6209203f2ba507b5ab608bbf3ebe7a15aeb23aa84005a76bf10d0f3b3704c403d
7
- data.tar.gz: 9a29c720ce14050bc604c9b561c376f55437865610827075dd6a2a961ce82b22c1f4b3fd303884186f9c59b1a330ed73ce14ad16169724c3578d8d4e01388bfe
6
+ metadata.gz: f4b7ecd7671596c8da581f567e43a4968ec7ea2f060a75d92b4369d19d3e1638e2f2c91b70e3c0205bbc77ccd309341d5dbb47c1c774eead07f5c71a0bf13684
7
+ data.tar.gz: 1c88e0dabe3ef47f17527c604440775fdf5984a5389ad6e6e8ec4a9c8ade34a4c59423bdf0f1277e6ffb88a8484d774da3029f814cc37526b92f1d737257e18b
data/README.ruby.md CHANGED
@@ -28,9 +28,15 @@ gem install squared
28
28
 
29
29
  ## Example - Rakefile
30
30
 
31
+ Projects from any accessible folder can be added either relative to `REPO_ROOT` or absolutely. The same Rakefile can also manage other similarly cloned repositories remotely by setting the `REPO_ROOT` environment variable to the location. Missing projects will simply be excluded from the task runner.
32
+
31
33
  ```ruby
32
34
  require "squared"
33
35
 
36
+ # REPO_ROOT = /workspaces
37
+ # REPO_HOME = /workspaces/squared
38
+ # rake = /workspaces/squared/Rakefile
39
+
34
40
  Repo::Workspace
35
41
  .new("squared") # REPO_HOME
36
42
  .repo("https://github.com/anpham6/squared-repo", "nightly", run: 'build') # Optional
@@ -40,7 +46,7 @@ Repo::Workspace
40
46
  .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", ref: :ruby) # Ruby (with C extensions)
41
47
  .add("optparse", doc: "rake rdoc", group: "default") # Uses bundler/gem_tasks (without C extensions)
42
48
  .add("logger", copy: { from: "lib", glob: "**/*.rb", gemdir: "~/.rvm/gems/ruby-3.3.5/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true
43
- .add("android", "android-docs", run: false, doc: "make html", ref: :python) # task namespace "android" | directory "android-docs"
49
+ .add("android", "android-docs", run: false, doc: "make html", ref: :python) # Python
44
50
  .add("emc", "e-mc", copy: { from: "publish", into: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
45
51
  .add("pir", "pi-r", copy: { from: "publish", into: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
46
52
  .add("squared", log: { file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app") # Copy target (main)
@@ -51,6 +57,22 @@ Repo::Workspace
51
57
  .style(:banner, "bright_cyan", "bold", "bright_black!")
52
58
  .finalize! # Optional
53
59
  end
60
+ # pathname = /workspaces/pathname
61
+ # optparse = /workspaces/optparse
62
+ # log = /workspaces/logger
63
+ # android = /workspaces/android-docs
64
+ # emc = /workspaces/e-mc
65
+ # pir = /workspaces/pi-r
66
+ # squared = /workspaces/squared
67
+
68
+ Repo::Workspace
69
+ .new("squared")
70
+ .group("default", "ruby", run: "rake build", copy: "rake install", clean: "rake clean", pathname: { run: "rake compile" })
71
+ .build
72
+ # default = /workspaces/ruby/*
73
+ # pathname = /workspaces/ruby/pathname
74
+ # optparse = /workspaces/ruby/optparse
75
+ # logger = /workspaces/ruby/logger
54
76
  ```
55
77
 
56
78
  **NOTE**: The use of "**ref**" (class name) is only necessary when running `repo:init` for the first time into an empty directory.
@@ -6,7 +6,7 @@ module Squared
6
6
  module Common
7
7
  class JoinSet < ::Set
8
8
  def self.to_s
9
- /[^:]+$/.match(super.to_s)[0]
9
+ super.to_s.match(/[^:]+$/)[0]
10
10
  end
11
11
 
12
12
  def initialize(data = [], delim: ' ')
@@ -66,14 +66,12 @@ module Squared
66
66
  yield out
67
67
  elsif pipe.respond_to?(:puts)
68
68
  pipe.puts out
69
- elsif err
70
- defined?(__warn__) == 'method' ? __warn__(out) : warn(out)
71
69
  else
72
- puts out
70
+ err ? warn(out) : puts(out)
73
71
  end
74
72
  end
75
73
 
76
- def sub_style(val, *args, pat: nil, styles: nil, index: 1)
74
+ def sub_style(val, *args, styles: nil, pat: nil, index: 1)
77
75
  return val unless ENV['NO_COLOR'].to_s.empty?
78
76
 
79
77
  if pat && index != 0
@@ -3,8 +3,8 @@
3
3
  module Squared
4
4
  module Common
5
5
  module Shell
6
- def shell_escape(val)
7
- return val if ::Rake::Win32.windows?
6
+ def shell_escape(val, quote: false)
7
+ return (quote ? shell_quote(val, force: false) : val) if ::Rake::Win32.windows?
8
8
 
9
9
  require 'shellwords'
10
10
  Shellwords.escape(val)
@@ -16,7 +16,7 @@ module Squared
16
16
  rescue StandardError => e
17
17
  raise if exception
18
18
 
19
- defined?(__warn__) == 'method' ? __warn__(e) : warn(e)
19
+ warn e
20
20
  end
21
21
 
22
22
  def invoked?(name)
@@ -12,7 +12,7 @@ module Squared
12
12
 
13
13
  class << self
14
14
  def to_s
15
- /[^:]+$/.match(super.to_s)[0]
15
+ super.to_s.match(/[^:]+$/)[0]
16
16
  end
17
17
  end
18
18
 
@@ -176,7 +176,7 @@ module Squared
176
176
  raise ArgumentError, message(reader.name, "#{File.basename(alt, '.*')}.#{ext.first}", hint: 'not found')
177
177
  end
178
178
  end
179
- project&.info "#{Viewer}(#{type}) => #{file} {#{keys.join(', ')}}"
179
+ project&.log&.info "#{Viewer}(#{type}) => #{file} {#{keys.join(', ')}}"
180
180
  doc = if reader.respond_to?(:load_file)
181
181
  reader.load_file(file, **@mime[type])
182
182
  else
@@ -225,7 +225,7 @@ module Squared
225
225
  end
226
226
  end
227
227
  rescue StandardError
228
- project&.warn "#{Viewer}(#{type}) => #{file ? "#{file} " : ''}{#{key}: undefined}"
228
+ project&.log&.warn "#{Viewer}(#{type}) => #{file ? "#{file} " : ''}{#{key}: undefined}"
229
229
  val = Regexp.escape($!.message)
230
230
  key = key.sub(/(#{val})\.|\.(#{val})|(#{val})/) do
231
231
  s = "<#{$3 || $2 || $1}>"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
3
  require 'date'
5
4
 
6
5
  module Squared
@@ -12,7 +11,6 @@ module Squared
12
11
  include Shell
13
12
  include Task
14
13
  include ::Rake::DSL
15
- extend Forwardable
16
14
 
17
15
  class << self
18
16
  include Common::Task
@@ -31,7 +29,7 @@ module Squared
31
29
  end
32
30
 
33
31
  def to_s
34
- /[^:]+$/.match(super.to_s)[0]
32
+ super.to_s.match(/[^:]+$/)[0]
35
33
  end
36
34
 
37
35
  def to_sym
@@ -42,15 +40,9 @@ module Squared
42
40
  @@print_order = 0
43
41
  @@tasks = {}
44
42
 
45
- alias __warn__ warn
46
-
47
- attr_reader :name, :project, :workspace, :group, :path, :logger, :theme
43
+ attr_reader :name, :project, :workspace, :group, :path, :log, :theme
48
44
  attr_accessor :warning
49
45
 
50
- protected :logger
51
-
52
- def_delegators :logger, :log, :<<, :debug, :info, :warn, :error, :fatal, :unknown
53
-
54
46
  def initialize(name, path, workspace, *, group: nil, log: nil, common: true, **kwargs)
55
47
  @name = name.to_s
56
48
  @path = workspace.root_path(path.to_s)
@@ -63,12 +55,14 @@ module Squared
63
55
  @output = [kwargs[:run], nil]
64
56
  @copy = kwargs[:copy]
65
57
  @clean = kwargs[:clean]
66
- @theme = if common
58
+ @warning = workspace.warning
59
+ @theme = if !workspace.verbose
60
+ {}
61
+ elsif common
67
62
  workspace.theme
68
63
  else
69
64
  __get__(:theme)[:project][to_sym] ||= {}
70
65
  end
71
- @warning = workspace.warning
72
66
  log = { file: log } unless log.is_a?(::Hash)
73
67
  if (logfile = env('LOG_FILE')).nil? && (auto = env('LOG_AUTO'))
74
68
  logfile = case auto
@@ -87,10 +81,10 @@ module Squared
87
81
  logfile.realdirpath
88
82
  rescue StandardError => e
89
83
  logfile = nil
90
- __warn__ e if @warning
84
+ warn e if @warning
91
85
  end
92
86
  end
93
- @logger = Logger.new(logfile, progname: @name, level: env('LOG_LEVEL') || log[:level] || Logger::INFO)
87
+ @log = Logger.new(logfile, progname: @name, level: env('LOG_LEVEL') || log[:level] || Logger::INFO)
94
88
  end
95
89
 
96
90
  def initialize_build(ref, **kwargs)
@@ -100,15 +94,13 @@ module Squared
100
94
  elsif @script && @output[0] != false
101
95
  @output[0] ||= @script[:run]
102
96
  end
97
+ @dev = kwargs.delete(:dev)
103
98
  if env('BUILD', suffix: 'DEV', equals: '0')
104
99
  @dev = false
105
- else
106
- @dev = kwargs.delete(:dev)
107
- if env('BUILD', suffix: 'DEV')
108
- @dev = true
109
- elsif @dev.nil?
110
- @dev = workspace.dev?
111
- end
100
+ elsif env('BUILD', suffix: 'DEV')
101
+ @dev = true
102
+ elsif @dev.nil?
103
+ @dev = workspace.dev?
112
104
  end
113
105
  end
114
106
 
@@ -125,12 +117,8 @@ module Squared
125
117
  def populate(*)
126
118
  namespace name do
127
119
  workspace.series.each_key do |key|
128
- case key
129
- when :build, :refresh, :depend, :outdated, :doc, :test, :copy, :clean
130
- next unless has?(key)
131
- else
132
- next unless workspace.task_defined?(self, key)
133
- end
120
+ next unless Workspace::TASK_KEYS.include?(key) ? has?(key) : workspace.task_defined?(self, key)
121
+
134
122
  desc message(name, key)
135
123
  task key do
136
124
  __send__(key)
@@ -201,7 +189,7 @@ module Squared
201
189
  dir = base_path(dir) unless dir.absolute?
202
190
  next unless dir.directory?
203
191
 
204
- warn "rm -rf #{dir}"
192
+ log.warn "rm -rf #{dir}"
205
193
  dir.rmtree(verbose: true)
206
194
  else
207
195
  files = val.include?('*') ? Dir[base_path(val)] : [base_path(val)]
@@ -209,7 +197,7 @@ module Squared
209
197
  begin
210
198
  File.delete(file) if File.file?(file)
211
199
  rescue StandardError => e
212
- error e
200
+ log.error e
213
201
  end
214
202
  end
215
203
  end
@@ -275,12 +263,12 @@ module Squared
275
263
  return if req && !base_path(req).exist?
276
264
 
277
265
  cmd = close_session(cmd)
278
- info cmd
266
+ log.info cmd
279
267
  print_item format_banner(cmd, banner: banner)
280
268
  begin
281
269
  shell(cmd, chdir: path, exception: exception)
282
270
  rescue StandardError => e
283
- error e
271
+ log.error e
284
272
  raise
285
273
  end
286
274
  end
@@ -324,26 +312,40 @@ module Squared
324
312
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
325
313
  end
326
314
 
327
- def print_banner(*lines, styles: nil, pad: 0, first: false)
315
+ def print_banner(*lines, styles: theme[:banner], border: theme[:border])
316
+ pad = 0
317
+ if styles
318
+ if styles.any? { |s| s.to_s.end_with?('!') }
319
+ pad = 1
320
+ elsif styles.size <= 1
321
+ styles = [:bold] + styles
322
+ end
323
+ end
328
324
  n = max_width(lines)
329
325
  ch = ' ' * pad
330
326
  index = -1
331
327
  out = lines.map do |val|
332
328
  index += 1
333
329
  val = ch + val.ljust(n - (pad * 2)) + ch
334
- if styles && (!first || index == 0)
330
+ if styles && (pad == 1 || index == 0)
335
331
  sub_style(val, *styles)
336
332
  else
337
333
  val
338
334
  end
339
335
  end
340
- out << ('-' * n)
336
+ out << sub_style('-' * n, styles: border)
341
337
  out.join("\n")
342
338
  end
343
339
 
344
- def print_footer(*lines)
340
+ def print_footer(*lines, sub: nil, border: theme[:border])
345
341
  n = max_width(lines)
346
- ['-' * n, *lines.map { |val| val.ljust(n) }].join("\n")
342
+ sub = as_a(sub)
343
+ lines.map! do |val|
344
+ s = val.ljust(n)
345
+ sub.each { |h| s = sub_style(s, **h) }
346
+ s
347
+ end
348
+ [sub_style('-' * n, styles: border), *lines].join("\n")
347
349
  end
348
350
 
349
351
  def format_desc(action, flag, opts = nil, req: 'opts*')
@@ -355,20 +357,12 @@ module Squared
355
357
  message(name, action, opts ? "#{flag}[#{opts}]" : flag)
356
358
  end
357
359
 
358
- def format_banner(cmd, banner: true, multitask: false)
360
+ def format_banner(cmd, banner: true, multiple: false)
359
361
  return unless banner
360
362
 
361
363
  if verbose?
362
- pad = 0
363
- if (args = theme[:banner])
364
- if args.any? { |s| s.to_s.end_with?('!') }
365
- pad = 1
366
- elsif args.size <= 1
367
- args = [:bold] + args
368
- end
369
- end
370
- print_banner(cmd.sub(/^\S+/, &:upcase), path.to_s, styles: args, pad: pad, first: pad == 0)
371
- elsif multitask && workspace.multitask?
364
+ print_banner(cmd.sub(/^\S+/, &:upcase), path.to_s)
365
+ elsif multiple && workspace.multiple?
372
366
  "## #{path} ##"
373
367
  end
374
368
  end
@@ -408,13 +402,13 @@ module Squared
408
402
  end
409
403
 
410
404
  def invoked_sync?(action)
411
- return true if invoked?(sync = "#{action}:sync")
405
+ return true if workspace.sync?("#{action}:sync")
412
406
 
413
407
  missing = ->(val) { ::Rake::Task.tasks.none? { |item| item.name == val } }
414
408
  check = lambda do |val|
415
409
  if invoked?(val)
416
410
  missing.("#{val}:sync")
417
- elsif invoked?("#{val}:sync")
411
+ elsif workspace.sync?("#{val}:sync")
418
412
  true
419
413
  end
420
414
  end
@@ -426,7 +420,7 @@ module Squared
426
420
  ret = check.("#{action}:#{base.to_sym}")
427
421
  return ret unless ret.nil?
428
422
  end
429
- invoked?("#{name}:#{action}") && (!invoked?(action) || missing.(sync))
423
+ invoked?("#{name}:#{action}") && (!invoked?(action) || missing.("#{action}:sync"))
430
424
  end
431
425
 
432
426
  private
@@ -7,7 +7,9 @@ module Squared
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: [], **)
@@ -49,7 +51,7 @@ module Squared
49
51
  diff: %i[head cached branch],
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)
@@ -202,14 +204,8 @@ module Squared
202
204
  elsif flag == :commit || option('commit')
203
205
  cmd << '--commit'
204
206
  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?)
207
+ append_pull opts, OPT_PULL, flag
208
+ source(sync: sync, stderr: true, exception: !workspace.multiple?)
213
209
  end
214
210
 
215
211
  def rebase
@@ -219,16 +215,8 @@ module Squared
219
215
  def fetch(flag = nil, opts: [])
220
216
  cmd = git_session 'fetch'
221
217
  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?)
218
+ append_pull opts, OPT_FETCH, flag
219
+ source(sync: invoked_sync?('fetch', flag), stderr: true, exception: !workspace.multiple?)
232
220
  end
233
221
 
234
222
  def stash(flag = :push, files = [], commit: nil)
@@ -448,9 +436,9 @@ module Squared
448
436
 
449
437
  def source(cmd = @session, exception: true, banner: true, io: false, sync: true, stdout: false, stderr: false)
450
438
  cmd = close_session(cmd)
451
- info cmd
439
+ log.info cmd
452
440
  begin
453
- banner = format_banner(cmd.gsub(trailing_slash(path), ''), banner: banner, multitask: true)
441
+ banner = format_banner(cmd.gsub(trailing_slash(path), ''), banner: banner, multiple: true)
454
442
  cmd = cmd.sub(/^git\b/, "git --work-tree #{shell_quote(path)} --git-dir #{shell_quote(gitdir)}")
455
443
  if io
456
444
  [IO.popen(cmd), banner]
@@ -481,7 +469,7 @@ module Squared
481
469
  end
482
470
  end
483
471
  rescue StandardError => e
484
- error e
472
+ log.error e
485
473
  raise if exception
486
474
 
487
475
  emphasize e, message('git', cmd)
@@ -497,7 +485,7 @@ module Squared
497
485
  next if grep && !line.match?(grep)
498
486
 
499
487
  if loglevel
500
- log loglevel, line
488
+ log.add loglevel, line
501
489
  else
502
490
  sub&.each { |h| line = sub_style(line, **h) }
503
491
  if banner
@@ -516,9 +504,10 @@ module Squared
516
504
  return unless verbose?
517
505
 
518
506
  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}"
507
+ styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
508
+ styles << :bold if styles.size <= 1
509
+ puts print_footer("#{size} #{size == 1 ? type.sub(/s$/, '') : type}",
510
+ sub: { styles: styles, pat: /^(\d+)(.+)$/ })
522
511
  else
523
512
  puts empty_status("No #{type} were #{action}", 'grep', grep)
524
513
  end
@@ -529,13 +518,24 @@ module Squared
529
518
  end
530
519
 
531
520
  def source_path?(val)
532
- return val.to_s.start_with?("#{path}#{::File::SEPARATOR}") if Pathname.new(val).absolute?
521
+ return val.to_s.start_with?(File.join(path, '').to_s) if Pathname.new(val).absolute?
533
522
 
534
523
  !val.match?(%r{^\.\.[/\\]})
535
524
  end
536
525
 
537
526
  private
538
527
 
528
+ def append_pull(opts, list, flag = nil)
529
+ append_submodules flag
530
+ opts.each do |opt|
531
+ if list.include?(opt)
532
+ @session << "--#{opt}"
533
+ elsif opt.match(/^depth=(\d+)$/)
534
+ @session << "--depth=#{$1}"
535
+ end
536
+ end
537
+ end
538
+
539
539
  def append_commit(val)
540
540
  val = val.to_s.strip
541
541
  val.empty? ? 'HEAD' : val
@@ -33,6 +33,8 @@ module Squared
33
33
  run: nil
34
34
  }.freeze
35
35
 
36
+ attr_reader :package
37
+
36
38
  def initialize(name, path, workspace, *, **kwargs)
37
39
  super
38
40
  initialize_script(REF)
@@ -43,6 +45,7 @@ module Squared
43
45
  else
44
46
  @output[1] = (@script && @script[:run]) || workspace.script
45
47
  end
48
+ @package = base_path('package.json')
46
49
  @dev = workspace.bool_match(env('BUILD', suffix: 'DEV'), kwargs.delete(:dev))
47
50
  @prod = workspace.bool_match(env('BUILD', suffix: 'PROD'), kwargs.delete(:prod))
48
51
  @pm = {}
@@ -129,7 +132,7 @@ module Squared
129
132
 
130
133
  from = base_path(from)
131
134
  dest = dest.join(subdir, into || project)
132
- warn "cp #{from.join(glob)} #{dest}"
135
+ log.warn "cp #{from.join(glob)} #{dest}"
133
136
  copy_d(from, dest, glob: glob, verbose: verbose?)
134
137
  end
135
138
  end
@@ -200,10 +203,10 @@ module Squared
200
203
  require 'json'
201
204
  rev ||= prod? ? :patch : :minor
202
205
  cmd = pnpm? ? 'pnpm outdated' : 'npm outdated'
203
- print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multitask: true)
206
+ print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multiple: true)
204
207
  pwd = Dir.pwd
205
208
  Dir.chdir(path)
206
- info cmd
209
+ log.info cmd
207
210
  data = `#{cmd} --json --loglevel=error`
208
211
  Dir.chdir(pwd)
209
212
  json = JSON.parse(doc = package.read)
@@ -366,7 +369,7 @@ module Squared
366
369
  end
367
370
 
368
371
  def depend?
369
- !!@depend || outdated?
372
+ outdated? || !!@depend
370
373
  end
371
374
 
372
375
  def copy?
@@ -465,12 +468,6 @@ module Squared
465
468
  end
466
469
  end
467
470
  end
468
-
469
- private
470
-
471
- def package
472
- @package ||= base_path('package.json')
473
- end
474
471
  end
475
472
  end
476
473
  end
@@ -6,7 +6,11 @@ module Squared
6
6
  class Python < Git
7
7
  REF = :python
8
8
  REQUIREMENTS = %w[requirements.txt pyproject.toml setup.py].freeze
9
- private_constant :REF, :REQUIREMENTS
9
+ OPT_USER = %w[pre dry-run].freeze
10
+ OPT_FORCE = [*OPT_USER, 'user'].freeze
11
+ OPT_UPGRADE = [*OPT_FORCE, 'eager'].freeze
12
+ OPT_GENERAL = %w{venv isolated no-cache [v]erbose}.freeze
13
+ private_constant :REF, :REQUIREMENTS, :OPT_USER, :OPT_FORCE, :OPT_UPGRADE, :OPT_GENERAL
10
14
 
11
15
  class << self
12
16
  def populate(*); end
@@ -29,13 +33,17 @@ module Squared
29
33
  end
30
34
  end
31
35
 
36
+ attr_reader :requirements
37
+
32
38
  def initialize(name, path, workspace, *, **kwargs)
33
39
  super
40
+ @reqindex = REQUIREMENTS.index { |file| base_path(file).exist? } || 0
41
+ @requirements = base_path(REQUIREMENTS[@reqindex])
34
42
  initialize_build(REF, **kwargs)
35
43
  end
36
44
 
37
45
  @@tasks[REF] = {
38
- install: %i[upgrade force]
46
+ install: %i[user upgrade force]
39
47
  }.freeze
40
48
 
41
49
  def populate(*)
@@ -48,16 +56,15 @@ module Squared
48
56
  flags.each do |flag|
49
57
  case action
50
58
  when :install
51
- options = lambda do |*opts|
52
- opts += %w[pre venv no-cache dry-run]
53
- format_desc(action, flag, opts)
54
- end
55
- case flag
56
- when :upgrade
57
- desc options.('eager')
58
- when :force
59
- desc options.()
60
- end
59
+ list = case flag
60
+ when :upgrade
61
+ OPT_UPGRADE
62
+ when :force
63
+ OPT_FORCE
64
+ else
65
+ OPT_USER
66
+ end
67
+ desc format_desc(action, flag, list + OPT_GENERAL)
61
68
  task flag do |_, args|
62
69
  depend(flag, opts: collect_args(args, :opts), override: true)
63
70
  end
@@ -72,22 +79,21 @@ module Squared
72
79
  if @depend && !override
73
80
  super
74
81
  elsif outdated?
75
- case install_type
82
+ case (type = install_type)
76
83
  when 1, 2
77
84
  cmd = pip_session 'install'
78
85
  case flag
86
+ when :user
87
+ cmd << '--user'
88
+ append_general opts, OPT_USER
79
89
  when :upgrade
80
90
  cmd << '--upgrade'
81
- cmd << '--upgrade-strategy=eager' if opts.include?('eager')
91
+ append_general opts, OPT_UPGRADE
82
92
  when :force
83
93
  cmd << '--force-reinstall'
94
+ append_general opts, OPT_FORCE
84
95
  end
85
- cmd << '--pre' if opts.include?('pre')
86
- cmd << '--require-virtualenv' if opts.include?('venv')
87
- cmd << '--no-cache-dir' if opts.include?('no-cache')
88
- cmd << '--dry-run' if opts.include?('dry-run')
89
- append_nocolor
90
- cmd << (install_type == 1 ? '-r requirements.txt' : '.')
96
+ cmd << (type == 1 ? '-r requirements.txt' : '.')
91
97
  run(exception: workspace.exception)
92
98
  when 3
93
99
  run_s "#{@bin} setup.py install"
@@ -98,14 +104,11 @@ module Squared
98
104
  def outdated(*); end
99
105
 
100
106
  def install_type(*)
101
- return @requirements if @requirements
102
-
103
- ret = REQUIREMENTS.index { |file| base_path(file).exist? }
104
- @requirements = ret ? ret + 1 : 0
107
+ requirements.exist? ? @reqindex + 1 : 0
105
108
  end
106
109
 
107
110
  def depend?
108
- !!@depend || outdated?
111
+ outdated? || !!@depend
109
112
  end
110
113
 
111
114
  def outdated?
@@ -117,6 +120,30 @@ module Squared
117
120
  def pip_session(*cmd)
118
121
  session('pip', *cmd)
119
122
  end
123
+
124
+ def append_general(opts, list = [])
125
+ list += OPT_GENERAL
126
+ opts.each do |opt|
127
+ next unless (v = opt.match(/^v+$/)) || list.include?(opt)
128
+
129
+ @session << case opt
130
+ when 'eager'
131
+ '--upgrade-strategy=eager'
132
+ when 'venv'
133
+ '--require-virtualenv'
134
+ when 'no-cache'
135
+ '--no-cache-dir'
136
+ else
137
+ (v ? "-#{v[0]}" : "--#{opt}")
138
+ end
139
+ end
140
+ @session << '--user' if env('PIP_USER')
141
+ @session << '--no-input' if env('PIP_NO_INPUT')
142
+ if (val = env('PIP_PROXY'))
143
+ @session << "--proxy=#{shell_escape(val, quote: true)}"
144
+ end
145
+ append_nocolor
146
+ end
120
147
  end
121
148
  end
122
149
  end
@@ -33,11 +33,15 @@ module Squared
33
33
  rake: nil
34
34
  }.freeze
35
35
 
36
+ attr_reader :gemfile
37
+
36
38
  def initialize(name, path, workspace, *, **kwargs)
37
39
  super
38
40
  initialize_build(REF, **kwargs)
39
41
  @version = env('BUILD', suffix: 'VERSION', strict: true) || kwargs.delete(:version)
40
42
  @autodetect = kwargs.key?(:autodetect) ? kwargs.delete(:autodetect) : false
43
+ index = GEMFILE.index { |file| base_path(file).exist? } || 0
44
+ @gemfile = base_path(GEMFILE[index])
41
45
  return if !@output[0].nil? || !@copy.nil? || @version || @autodetect || (file = rakefile).nil?
42
46
 
43
47
  begin
@@ -51,7 +55,7 @@ module Squared
51
55
  break
52
56
  end
53
57
  rescue StandardError => e
54
- error e
58
+ log.error e
55
59
  end
56
60
  end
57
61
 
@@ -130,14 +134,15 @@ module Squared
130
134
  val
131
135
  end}"
132
136
  end
133
- append_opts opts, OPT_INSTALL
137
+ append_bundle opts, OPT_INSTALL
134
138
  when :'with-g', :'without-g'
135
139
  guard_params('install', flag, args: group)
136
140
  cmd = gem_session 'install', '-g'
137
141
  opt = flag.to_s.sub('-g', '')
138
- group.each { |s| cmd << "--#{opt}=#{shell_escape(s)}" }
142
+ group.each { |s| cmd << "--#{opt}=#{shell_escape(s, quote: true)}" }
139
143
  else
140
144
  cmd = bundle_session 'install'
145
+ append_bundle
141
146
  end
142
147
  run(banner: banner?, exception: workspace.exception)
143
148
  end
@@ -160,7 +165,7 @@ module Squared
160
165
  a = base_path(val)
161
166
  b = dest.join(val)
162
167
  c = glob[i] || '**/*'
163
- warn "cp #{a.join(c)} #{b}"
168
+ log.warn "cp #{a.join(c)} #{b}"
164
169
  copy_d(a, b, glob: c, verbose: verbose?)
165
170
  end
166
171
  end
@@ -173,7 +178,7 @@ module Squared
173
178
  when :all, :major, :patch, :minor
174
179
  cmd << "--#{flag}"
175
180
  end
176
- append_opts opts, OPT_UPDATE
181
+ append_bundle opts, OPT_UPDATE
177
182
  run(banner: banner?, exception: workspace.exception)
178
183
  end
179
184
 
@@ -203,7 +208,7 @@ module Squared
203
208
  end
204
209
 
205
210
  def copy?
206
- return true if @copy && (@copy.is_a?(::String) || @copy.fetch(:gemdir, nil))
211
+ return true if @copy.is_a?(::String) || @copy&.fetch(:gemdir, nil)
207
212
  return gemdir? if @gemdir
208
213
 
209
214
  gempath = -> { "gems#{::File::SEPARATOR}#{project}-#{@version}" }
@@ -213,11 +218,11 @@ module Squared
213
218
  end
214
219
  return false unless @autodetect
215
220
 
216
- quit = ->(hint) { raise ArgumentError, message('failed to parse', hint: hint) }
221
+ unsafe = ->(hint) { raise ArgumentError, message('failed to parse', hint: hint) }
217
222
  begin
218
223
  out = `gem -C #{shell_quote(path)} list --local -d #{project}`
219
224
  data = /#{Regexp.escape(project)} \(([^)]+)\)/.match(out)
220
- quit.('version') unless data
225
+ unsafe.('version') unless data
221
226
  ver = data[1].split(/\s*,\s*/)
222
227
  if @version
223
228
  ver.unshift(@version)
@@ -227,14 +232,14 @@ module Squared
227
232
  data = /\(#{Regexp.escape(v)}(?:,[^)]+|\b)\):([^\n]+)/.match(out)
228
233
  next unless data
229
234
 
230
- warn "using version #{v} (given #{@version})" if @version && @version != v
235
+ log.warn "using version #{v} (given #{@version})" if @version && @version != v
231
236
  @version = v
232
237
  break
233
238
  end
234
- quit.('path') unless data
239
+ unsafe.('path') unless data
235
240
  @gemdir = Pathname.new(data[1].strip).join(gempath.())
236
241
  rescue StandardError => e
237
- error e
242
+ log.error e
238
243
  @version = nil
239
244
  @gemdir = nil
240
245
  @autodetect = false
@@ -244,13 +249,11 @@ module Squared
244
249
  end
245
250
 
246
251
  def depend?
247
- !!@depend || outdated?
252
+ outdated? || !!@depend
248
253
  end
249
254
 
250
255
  def outdated?
251
- return @outdated unless @outdated.nil?
252
-
253
- @outdated = GEMFILE.any? { |file| base_path(file).exist? }
256
+ gemfile.exist?
254
257
  end
255
258
 
256
259
  def dev?
@@ -259,15 +262,18 @@ module Squared
259
262
 
260
263
  private
261
264
 
262
- def append_opts(opts, list = [])
265
+ def append_bundle(opts = nil, list = nil)
263
266
  if (val = env('BUNDLE_JOBS')).to_i > 0
264
267
  @session << "-j#{val}"
265
268
  end
266
- list.each do |flag|
267
- if opts.include?(flag)
268
- @session << "--#{flag}"
269
- elsif /^g(?:roup)?=(.+)$/.match(flag)
270
- @session << "--group=#{$1}"
269
+ @session << '--norc' if env('BUNDLE_NORC')
270
+ return unless opts && list
271
+
272
+ opts.each do |opt|
273
+ if list.include?(opt)
274
+ @session << "--#{opt}"
275
+ elsif opt.match(/^g(?:roup)?=(.+)$/)
276
+ @session << "--group=#{shell_escape($1, quote: true)}"
271
277
  end
272
278
  end
273
279
  end
@@ -11,15 +11,16 @@ module Squared
11
11
 
12
12
  TASK_NAME = {
13
13
  build: [],
14
- copy: [],
14
+ refresh: [],
15
15
  depend: [],
16
16
  outdated: [],
17
- refresh: [],
18
17
  doc: [],
19
18
  test: [],
19
+ copy: [],
20
20
  clean: []
21
21
  }
22
22
  TASK_BASE = {}
23
+ TASK_KEYS = TASK_NAME.keys.freeze
23
24
  private_constant :TASK_NAME, :TASK_BASE
24
25
 
25
26
  class << self
@@ -44,7 +45,7 @@ module Squared
44
45
  end
45
46
 
46
47
  def to_s
47
- /[^:]+$/.match(super.to_s)[0]
48
+ super.to_s.match(/[^:]+$/)[0]
48
49
  end
49
50
 
50
51
  attr_reader :project_kind
@@ -52,7 +53,7 @@ module Squared
52
53
 
53
54
  @project_kind = []
54
55
 
55
- attr_reader :main, :root, :home, :series, :theme
56
+ attr_reader :main, :root, :home, :series, :theme, :sync, :multiple, :parallel
56
57
  attr_accessor :manifest, :manifest_url, :exception, :pipe, :verbose, :warning
57
58
 
58
59
  def initialize(main, *, common: true, **)
@@ -91,7 +92,6 @@ module Squared
91
92
  end
92
93
  @root ||= @home.parent
93
94
  @series = TASK_NAME.dup
94
- @theme = common ? __get__(:theme)[:workspace] : {}
95
95
  @project = {}
96
96
  @script = {
97
97
  group: {},
@@ -103,14 +103,18 @@ module Squared
103
103
  @exception = !env('PIPE_FAIL', ignore: '0').nil?
104
104
  @pipe = !env('PIPE_OUT', ignore: '0').nil?
105
105
  @verbose = !@pipe
106
- @warning = !empty?(@root, init: false)
106
+ @warning = !empty?(@root, repo: false)
107
+ @sync = []
108
+ @multiple = []
109
+ @parallel = []
110
+ @theme = common && @verbose ? __get__(:theme)[:workspace] : {}
107
111
  end
108
112
 
109
113
  def build(**kwargs)
110
114
  return unless enabled?
111
115
 
112
116
  default = kwargs[:default]
113
- parallel = env('REPO_SYNC', ignore: '0') ? [] : (kwargs[:parallel] || []).map!(&:to_sym)
117
+ multi = env('REPO_SYNC', ignore: '0') ? [] : kwargs.fetch(:parallel, []).map!(&:to_sym)
114
118
 
115
119
  group = {}
116
120
  parent = {}
@@ -182,14 +186,13 @@ module Squared
182
186
  parse_opts.(args)
183
187
  stage ||= :all
184
188
  repo[:sync].invoke
185
- # rubocop:disable Style/CombinableLoops
186
- @project.each_value { |proj| proj.depend if proj.enabled? }
187
- @project.each_value do |proj|
188
- next unless proj.enabled? && proj.build?
189
-
190
- proj.has?(:dev) ? proj.refresh : proj.build
189
+ @project.select do |_, proj|
190
+ if proj.enabled?
191
+ proj.depend
192
+ proj.build?
193
+ end
191
194
  end
192
- # rubocop:enable Style/CombinableLoops
195
+ .each_value { |proj| proj.has?(:dev) ? proj.refresh : proj.build }
193
196
  end
194
197
 
195
198
  desc status.("init[manifest?=#{target},{0}]", target)
@@ -235,20 +238,27 @@ module Squared
235
238
  series.each do |key, items|
236
239
  next if items.empty?
237
240
 
238
- unless (valid = parallel.include?(key))
239
- group = key.to_s
240
- valid = parallel.include?(group.split(':').first.to_sym) if group.include?(':')
241
- end
242
- if valid && items.size > 1
243
- desc "#{key} (thread)"
244
- multitask key => items
245
-
246
- desc "#{key} (sync)"
247
- task "#{key}:sync": items
248
- else
249
- desc key.to_s
250
- task key => items
241
+ s = key.to_s
242
+ if items.size > 1
243
+ multiple << s
244
+ unless (valid = multi.include?(key))
245
+ group = key.to_s
246
+ valid = multi.include?(group.split(':').first.to_sym) if group.include?(':')
247
+ end
248
+ if valid
249
+ desc "#{key} (thread)"
250
+ multitask key => items
251
+ parallel << s
252
+
253
+ desc "#{key} (sync)"
254
+ task "#{key}:sync": items
255
+ sync << "#{key}:sync"
256
+ multiple << "#{key}:sync"
257
+ next
258
+ end
251
259
  end
260
+ desc s
261
+ task key => items
252
262
  end
253
263
 
254
264
  yield self if block_given?
@@ -259,6 +269,7 @@ module Squared
259
269
  @manifest = manifest
260
270
  script = case (val = env('REPO_BUILD'))
261
271
  when 'verbose'
272
+ @verbose = true
262
273
  run ? "#{run}:verbose" : nil
263
274
  when 'silent'
264
275
  @verbose = false
@@ -273,7 +284,9 @@ module Squared
273
284
  when '1'
274
285
  @warning = true
275
286
  end
276
- script ? run(script, dev: bool_match(env('REPO_DEV'), dev), prod: bool_match(env('REPO_DEV'), prod)) : self
287
+ return self unless script
288
+
289
+ run(script, dev: bool_match(env('REPO_DEV'), dev), prod: bool_match(env('REPO_DEV'), prod))
277
290
  end
278
291
 
279
292
  def run(script, group: nil, ref: nil, **kwargs)
@@ -303,9 +316,8 @@ module Squared
303
316
  script_command :test, script, group, ref
304
317
  end
305
318
 
306
- def add(name, path = nil, **kwargs)
319
+ def add(name, path = nil, ref: nil, **kwargs)
307
320
  path = root_path((path || name).to_s)
308
- ref = kwargs[:ref]
309
321
  project = if !ref.is_a?(::Class)
310
322
  Workspace.find(path: path, ref: ref)
311
323
  elsif ref < Project::Base
@@ -317,6 +329,27 @@ module Squared
317
329
  self
318
330
  end
319
331
 
332
+ def group(name, path, **kwargs)
333
+ root_path(path).children.map do |ent|
334
+ next unless (dir = Pathname.new(ent)).directory?
335
+
336
+ index = 0
337
+ basename = dir.basename.to_s.to_sym
338
+ override = kwargs.delete(basename)
339
+ while @project[basename]
340
+ index += 1
341
+ basename = :"#{basename}-#{index}"
342
+ end
343
+ [basename, dir, override]
344
+ end
345
+ .each do |basename, dir, override|
346
+ opts = kwargs.dup
347
+ opts.merge!(override) if override
348
+ add(basename, dir, group: name, **opts)
349
+ end
350
+ self
351
+ end
352
+
320
353
  def compose(name, &blk)
321
354
  namespace(name, &blk)
322
355
  self
@@ -360,8 +393,7 @@ module Squared
360
393
 
361
394
  def read_manifest(*)
362
395
  require 'rexml/document'
363
- file = root_path('.repo/manifest.xml')
364
- return unless file.exist?
396
+ return unless (file = root_path('.repo/manifest.xml')).exist?
365
397
 
366
398
  doc = REXML::Document.new(file.read)
367
399
  doc.elements['manifest/include'].attributes['name']&.sub('.xml', '')
@@ -408,19 +440,29 @@ module Squared
408
440
  !manifest_url.nil? && (empty?(root) || @override)
409
441
  end
410
442
 
411
- def multitask?
412
- ::Rake::Task.tasks.any? do |item|
413
- next unless item.already_invoked
443
+ def multiple?(val = nil)
444
+ return multiple.include?(val) && invoked?(val) if val
414
445
 
415
- item.name.end_with?(':sync') || (!item.name.include?(':') && !item.name.start_with?('all', 'init'))
416
- end
446
+ ::Rake::Task.tasks.any? { |item| item.already_invoked && multiple.include?(item.name) }
447
+ end
448
+
449
+ def sync?(val = nil)
450
+ return sync.include?(val) && invoked?(val) if val
451
+
452
+ ::Rake::Task.tasks.any? { |item| item.already_invoked && sync.include?(item.name) }
453
+ end
454
+
455
+ def parallel?(val = nil)
456
+ return parallel.include?(val) && invoked?(val) if val
457
+
458
+ ::Rake::Task.tasks.any? { |item| item.already_invoked && parallel.include?(item.name) }
417
459
  end
418
460
 
419
- def empty?(dir, init: true)
420
- return true if dir.empty? || (init && dir.join('.repo').directory?)
461
+ def empty?(dir, repo: true)
462
+ return true if dir.empty? || (repo && dir.join('.repo').directory?)
421
463
  return true if dir.children.size == 1 && dir.join(dir.children.first).to_s == __FILE__
422
464
 
423
- dir == root && !env('REPO_ROOT').nil? && root.children.none? do |path|
465
+ dir.to_s == root.to_s && !env('REPO_ROOT').nil? && root.children.none? do |path|
424
466
  path.directory? && !path.basename.to_s.start_with?('.') && path.to_s != home.to_s
425
467
  end
426
468
  end
@@ -430,7 +472,7 @@ module Squared
430
472
  end
431
473
 
432
474
  def dev?(script: nil, pat: nil, global: nil)
433
- with?(:dev, script: script, pat: pat, global: (pat.nil? && global.nil?) || global)
475
+ with?(:dev, script: script, pat: pat, global: global || (pat.nil? && global.nil?))
434
476
  end
435
477
 
436
478
  def prod?(script: nil, pat: nil, global: false)
@@ -441,7 +483,7 @@ module Squared
441
483
 
442
484
  protected
443
485
 
444
- def confirm(msg, agree: 'Y', cancel: 'N', attempts: 5, timeout: 15)
486
+ def confirm(msg, agree: 'Y', cancel: 'N', default: nil, attempts: 5, timeout: 15)
445
487
  require 'readline'
446
488
  require 'timeout'
447
489
  Timeout.timeout(ENV.fetch('REPO_TIMEOUT', timeout).to_i) do
@@ -454,6 +496,14 @@ module Squared
454
496
  break
455
497
  when cancel
456
498
  break
499
+ when ''
500
+ case default
501
+ when agree
502
+ ret = true
503
+ break
504
+ when cancel
505
+ break
506
+ end
457
507
  end
458
508
  attempts -= 1
459
509
  exit 1 unless attempts >= 0
@@ -484,15 +534,16 @@ module Squared
484
534
  def repo_prompt(path)
485
535
  return false unless path.directory?
486
536
 
487
- confirm "#{log_title(:warn)} \"#{sub_style(path, :bold)}\" is not empty. Continue with installation? [y/N] "
537
+ confirm("#{log_title(:warn)} \"#{sub_style(path, :bold)}\" is not empty. Continue with installation? [y/N] ",
538
+ default: 'N')
488
539
  end
489
540
 
490
541
  def with?(state, script: nil, pat: nil, global: false)
491
- pat = @script[state] if pat.nil? && global
492
- return pat == true unless pat.is_a?(::Regexp)
493
- return false if !script && !global
494
-
495
- pat.match?(script || @script[:build])
542
+ if global
543
+ pat = @script[state] if pat.nil?
544
+ script ||= @script[:build]
545
+ end
546
+ pat.is_a?(::Regexp) ? pat.match?(script) : pat == true
496
547
  end
497
548
  end
498
549
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-02 00:00:00.000000000 Z
11
+ date: 2024-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake