squared 0.0.4 → 0.0.5

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 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