squared 0.4.9 → 0.4.10

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: 81e1fa9c773a59c8cb351dec47eb27b4c858c6266c83bd14be02c621cf68ad57
4
- data.tar.gz: 23ccad44a3f5fbc1ade8d5950692fd401566f862b0131debebdd9d5d1a4d6dab
3
+ metadata.gz: ba613d7ba02a664fb4581573f6ee04d705f6bb2d1cb165c30c49d97eb259cbec
4
+ data.tar.gz: 764888100c6a6560478e99ed1451f1593a86e120799e25400708d81be934102c
5
5
  SHA512:
6
- metadata.gz: '00945ddcf48ae0482bdb40f143117ae464a03491fadf6c33b2e34c9f50b4d628c4b72b43c1a388e2c8c175067f62970c7ac60721330c1340a48103c4b972b560'
7
- data.tar.gz: d3846c85547ee3acb6d040703d21b7a61630f502c17da71f8d0ddf9032c67930819b933b3c3896a0e2b23c2664bfc2ed5454d95313849a9885c429d0cfacb5f1
6
+ metadata.gz: 1cf6f1f10babf7bfbbe665fe5fc97527cf1df4093a3e6b8b906a4bf8cf87f538c601faee86f40decb79455de340e1d8f510fb45d0acc17694cd97a4051010539
7
+ data.tar.gz: 2b537c2c6a30a17191f32eb1012b225bc29fff9e9102b663a6dcf3ffd05465f4e8a0790f4cbdd6c63b21bd64c762e85dff1d22b4eb35053702a6692e92c994cc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.10] - 2025-05-08
4
+
5
+ ### Added
6
+
7
+ - Git command pull and fetch action remote is interactive.
8
+ - Ruby command script uses multiline input when available.
9
+ - Ruby command file is interactive.
10
+ - Node command publish can accept access option levels.
11
+ - Docker command image action rm when called empty is interactive.
12
+
13
+ ### Fixed
14
+
15
+ - Disabled batch and alias tasks were not hidden.
16
+ - Docker tags from ENV were appended twice.
17
+ - Git events were not always registered.
18
+ - Git revbuild did not write lock data file with parallel tasks.
19
+
20
+ ### Changed
21
+
22
+ - Choice selection default was set to no timeout.
23
+ - Git command add can accept a file search pattern.
24
+
3
25
  ## [0.4.9] - 2025-04-27
4
26
 
5
27
  ### Added
@@ -633,6 +655,7 @@
633
655
 
634
656
  - Changelog was created.
635
657
 
658
+ [0.4.10]: https://github.com/anpham6/squared/releases/tag/v0.4.10-ruby
636
659
  [0.4.9]: https://github.com/anpham6/squared/releases/tag/v0.4.9-ruby
637
660
  [0.4.8]: https://github.com/anpham6/squared/releases/tag/v0.4.8-ruby
638
661
  [0.4.7]: https://github.com/anpham6/squared/releases/tag/v0.4.7-ruby
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # squared 5.4
1
+ # squared 5.5
2
2
 
3
3
  ## Documentation
4
4
 
@@ -15,10 +15,11 @@
15
15
  * [squared-express](https://github.com/anpham6/squared-express#readme)
16
16
  * [E-mc](https://github.com/anpham6/e-mc#readme)
17
17
  * [Pi-r](https://github.com/anpham6/pi-r#readme)
18
+ * [Pi-r2](https://github.com/anpham6/pi-r2#readme)
18
19
 
19
20
  ## Installation
20
21
 
21
- * NodeJS 16 LTS
22
+ * NodeJS 18
22
23
 
23
24
  ### NPX
24
25
 
@@ -131,26 +132,22 @@ rake clone # node + docs
131
132
 
132
133
  ```sh
133
134
  # NODE_TAG=latest
134
- # RUBY_VERSION=2.4.0-3.3.0
135
- # MANIFEST=nightly,staging,prod,android
135
+ # RUBY_VERSION=2.4.0-3.4.0
136
+ # MANIFEST=nightly,prod,latest,android
136
137
  # BUILD={dev,prod}
137
138
  # DEV={0,1,local}
138
139
  # DOCS=any
139
140
  # PIPE_FAIL={0,1}
140
141
  # PORT=3000
141
- docker build -t squared --build-arg MANIFEST=prod --build-arg SQUARED=prod .
142
- docker build -t node --build-arg NODE_TAG=20 --build-arg NODE_INSTALL=pnpm -f Dockerfile.slim . # no docs
143
- docker buildx bake node
142
+ docker build -t squared --build-arg MANIFEST=prod --build-arg NODE_ENV=production .
143
+ docker build -t node --build-arg NODE_TAG=22 --build-arg NODE_INSTALL=pnpm -f Dockerfile.slim .
144
+ NODE=22 docker buildx bake node
144
145
  # OR
145
- # RUBY_TAG=latest
146
- # NODE_VERSION=22.x
147
- docker build -t ruby --build-arg RUBY_TAG=3.0.0 --build-arg NODE_VERSION=20.x --build-arg PIPE_FAIL=0 -f Dockerfile.ruby .
148
- docker buildx bake ruby
146
+ docker build -t ruby --build-arg RUBY_TAG=3.4.0 --build-arg NODE_VERSION=22 --build-arg PIPE_FAIL=0 -f Dockerfile.ruby .
147
+ RUBY=3.4.0 docker buildx bake ruby
149
148
  # OR
150
- # NGINX_VERSION=1.27
151
- # NGINX_VARIANT=bookworm
152
- docker build -t nginx --build-arg NGINX_VERSION=1.27 --build-arg PORT=3000 --build-arg NODE_VERSION=18.x -f Dockerfile.nginx .
153
- docker buildx bake nginx
149
+ docker build -t nginx --build-arg NGINX_VERSION=1.27 --build-arg PORT=3000 --build-arg NODE_VERSION=20 -f Dockerfile.nginx .
150
+ NGINX=1.27 docker buildx bake nginx
154
151
 
155
152
  # Express
156
153
  docker run -it --name express --rm -p 3000:3000 \
@@ -521,6 +518,7 @@ squared.settings = {
521
518
  ],
522
519
  compressImages: false, // TinyPNG API Key <https://tinypng.com/developers>
523
520
  compressImages: "****************", // API key
521
+ compressImages: [{ plugin: "imagemin-pngquant", format: "png", options: { quality: [0.6, 0.8] } }], // v5.5
524
522
  convertImages: "", // png | jpeg | webp | gif | bmp
525
523
  showAttributes: true,
526
524
  showAttributes: {
@@ -541,7 +539,7 @@ squared.settings = {
541
539
  showErrorMessages: false,
542
540
  convertPixels: "dp", // "sp" | "pt" | "in" | "mm"
543
541
  convertLineHeight: "sp", // "dp" | "pt" | "in" | "mm"
544
- convertEntities: ["numeric"], // v5.4
542
+ convertEntities: ["numeric"],
545
543
  convertEntities: ["codepoints", {/* JSON (last) */}], // https://html.spec.whatwg.org/entities.json
546
544
  insertSpaces: 4,
547
545
  outputDocumentHandler: "android",
@@ -1361,4 +1359,4 @@ NOTE: Defining an element "**id**" will prevent it from being removed during the
1361
1359
 
1362
1360
  ## LICENSE
1363
1361
 
1364
- BSD 3-Clause
1362
+ BSD 3-Clause
data/README.ruby.md CHANGED
@@ -98,6 +98,9 @@ Workspace::Application
98
98
  with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
99
99
  # OR
100
100
  add(["publish/sqd-cli", "publish/sqd-serve"], true, exclude: [:base]) # rake squared:sqd-serve:build
101
+
102
+ # Git
103
+ revbuild(include: %w[src/ framework/ types/]) # Synchronous is recommended
101
104
  end
102
105
  .add("squared/sqd", exclude: :git, pass: [:node, "checkout", "bump"]) do # Skip initialize(:node) + squared:checkout:* + squared:bump:*
103
106
  variable_set :script, "build:sqd" # Override detection
@@ -116,9 +119,9 @@ Workspace::Application
116
119
  end
117
120
  end
118
121
  end
119
- .pass("pull", group: "default") { test? || doc? } # pathname:pull | optparse:pull
122
+ .pass("pull", group: "default") { test? || doc? } # rake pathname:pull | rake optparse:pull
120
123
  .style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
121
- .build(default: "build", parallel: ["pull", "fetch", "rebase", "archive", "copy", "clean", /^outdated:/], pass: ["publish"]) do |workspace|
124
+ .build(default: "build", parallel: ["pull", "fetch", "rebase", "archive", "clean", /^outdated:/], pass: ["publish"]) do |workspace|
122
125
  workspace
123
126
  .enable_aixterm
124
127
  .style({
@@ -194,9 +197,9 @@ Workspace::Application
194
197
  "pir": { # rake pir:clone
195
198
  uri: "https://github.com/anpham6/pi-r", #
196
199
  options: { #
197
- "origin": "github", # --origin='github'
200
+ "origin": "github", # --origin=github
198
201
  "recurse-submodules": false, # --no-recurse-submodules
199
- "shallow-exclude": ["v0.0.1", "v0.0.2"] # --shallow-exclude='v0.0.1' --shallow-exclude='v0.0.2'
202
+ "shallow-exclude": ["v0.0.1", "v0.0.2"] # --shallow-exclude=v0.0.1 --shallow-exclude=v0.0.2
200
203
  }
201
204
  }
202
205
  )
@@ -238,7 +241,7 @@ Workspace::Application
238
241
  .new(main: "squared")
239
242
  .graph(["depend"], ref: :git) # Optional
240
243
  .with(:python) do
241
- doc(windows? ? '.\make.bat html' : 'make html')
244
+ doc(windows? ? ".\make.bat html" : "make html") # rake android-docs:doc | rake doc:python
242
245
  add("android-docs", "android", venv: "/home/user/.venv") # rake android-docs:depend
243
246
  add("chrome-docs", "chrome", graph: "android", venv: ".venv") do # /workspaces/chrome-docs/.venv
244
247
  variable_set :dependindex, 2 # Use Poetry for dependencies (optional)
@@ -490,7 +493,7 @@ rake squared:log:view[H1,HEAD^5,all,lib,./H12345] # git log --all @~1 @^5 -- 'l
490
493
  All project binary programs can have their executable path set to a non-global alias.
491
494
 
492
495
  ```ruby
493
- Common::PATH.merge!({
496
+ Common::PATH.update({
494
497
  GIT: "/usr/bin/git", # PATH_GIT=/usr/bin/git
495
498
  TAR: "/opt/archivers/tar", # PATH_TAR=/opt/archivers/tar
496
499
  UNZIP: "/opt/archivers/unzip",
@@ -593,6 +596,7 @@ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
593
596
  | log | * | PATHSPEC=s |
594
597
  | pull | rebase | AUTOSTASH |
595
598
  | pull | remote | REFSPEC=s |
599
+ | pull | -remote | ALL |
596
600
  | pull | * | REBASE=0,1 FORCE RECURSE_SUBMODULES=0,1,s |
597
601
  | rebase | branch | HEAD=s |
598
602
  | rebase | onto | INTERACTIVE I HEAD=s |
@@ -621,6 +625,7 @@ DOCKER_OPTIONS_${NAME}=v,no-cache=false # project only (override)
621
625
  DOCKER_TAG=latest # all
622
626
  DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
623
627
  DOCKER_ALL=1 # list every image/container
628
+ DOCKER_Y=1 # confirm all
624
629
  ```
625
630
 
626
631
  | Command | Flag | ENV |
@@ -629,6 +634,7 @@ DOCKER_ALL=1 # list every image/container
629
634
  | buildx | bake | SERVICE=s |
630
635
  | compose | build | TARGET=s |
631
636
  | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
637
+ | image | rm | Y=0,1 |
632
638
  | image | push | TAG=s REGISTRY=s |
633
639
 
634
640
  ### Repo
@@ -8,8 +8,8 @@ module Squared
8
8
  def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 5, timeout: 30)
9
9
  require 'readline'
10
10
  require 'timeout'
11
- agree = /^#{agree}$/i if agree.is_a?(::String)
12
- cancel = /^#{cancel}$/i if cancel.is_a?(::String)
11
+ agree = /^#{Regexp.escape(agree)}$/i if agree.is_a?(::String)
12
+ cancel = /^#{Regexp.escape(cancel)}$/i if cancel.is_a?(::String)
13
13
  Timeout.timeout(timeout) do
14
14
  begin
15
15
  while (ch = Readline.readline(msg))
@@ -32,24 +32,28 @@ module Squared
32
32
  end
33
33
  end
34
34
 
35
- def choice(msg, list = nil, min: 1, max: 1, multiple: false, auto: true, force: true, attempts: 5, timeout: 60)
35
+ def choice(msg, list = nil, min: 1, max: 1, multiple: false, force: true, grep: nil, auto: true,
36
+ attempts: 5, timeout: 0)
36
37
  require 'readline'
37
38
  require 'timeout'
38
39
  if list
40
+ grep &&= (grep.is_a?(::Enumerable) ? grep : [grep]).map { |val| Regexp.new(val) }
39
41
  items = []
40
- list.each_with_index do |val, index|
41
- puts "#{index.succ.to_s.rjust(2)}. #{val}"
42
+ list.each do |val|
43
+ next if grep&.none? { |pat| pat.match?(line) }
44
+
42
45
  items << val.chomp
46
+ puts "#{items.size.to_s.rjust(2)}. #{val}"
43
47
  end
44
48
  max = items.size
49
+ raise_error 'empty selection list' if max == 0
50
+ min = [min, max].min
45
51
  if auto
46
52
  msg = "#{msg}: [1-#{max}#{if multiple
47
53
  "|,#{multiple.is_a?(Numeric) ? "{#{multiple}}" : ''}"
48
54
  end}] "
49
55
  end
50
56
  end
51
- return unless max >= min
52
-
53
57
  valid = ->(s) { s.match?(/^-?\d+$/) && s.to_i.between?(min, max) }
54
58
  Timeout.timeout(timeout) do
55
59
  begin
@@ -82,16 +86,33 @@ module Squared
82
86
  end
83
87
  end
84
88
 
85
- def readline(msg, history = false, force: nil)
89
+ def readline(msg, history = false, force: nil, multiline: nil, &blk)
86
90
  require 'readline'
91
+ multiline = if multiline && Readline.respond_to?(:readmultiline)
92
+ multiline.is_a?(::Enumerable) || block_given? ? multiline : [multiline.to_s]
93
+ end
94
+ prompt = lambda do
95
+ if !multiline
96
+ Readline.readline(msg, history)
97
+ elsif block_given?
98
+ Readline.readmultiline(msg, history, &blk)
99
+ else
100
+ Readline.readmultiline(msg, history) { |line| multiline.any? { |val| line.split.last.end_with?(val) } }
101
+ end
102
+ end
87
103
  case force
88
104
  when ::TrueClass, ::FalseClass
89
- msg = "#{msg} (#{force ? 'required' : 'optional'}): "
90
- ret = Readline.readline(msg, history)&.strip || ''
105
+ msg = "#{msg} %s " % if multiline
106
+ multiline.is_a?(::Enumerable) ? "{#{multiline.join('|')}}" : multiline
107
+ else
108
+ "(#{force ? 'required' : 'optional'}):"
109
+ end
110
+ ret = (prompt.call || '').strip
111
+ multiline.each { |val| break if ret.delete_suffix!(val) } if multiline.is_a?(::Enumerable)
91
112
  raise_error 'user cancelled' if force && ret.empty?
92
113
  ret
93
114
  else
94
- Readline.readline(msg, history)
115
+ prompt.call
95
116
  end
96
117
  end
97
118
  end
@@ -29,10 +29,10 @@ module Squared
29
29
  dest.mkpath if create
30
30
  if pass
31
31
  exclude = []
32
- pass = [pass] unless pass.is_a?(::Array)
32
+ pass = [pass] unless pass.is_a?(::Enumerable)
33
33
  pass.each { |val| exclude.concat(Dir.glob(src + val)) }
34
34
  end
35
- (glob.is_a?(::Array) ? glob : [glob]).each do |val|
35
+ (glob.is_a?(::Enumerable) ? glob : [glob]).each do |val|
36
36
  Dir.glob(src + val) do |path|
37
37
  next if exclude&.include?(path) || (path = Pathname.new(path)).directory?
38
38
 
@@ -79,7 +79,7 @@ module Squared
79
79
  def copy_guard(src, dest, link: nil, force: false, verbose: true)
80
80
  unless force
81
81
  if (path = Pathname.new(dest)).directory?
82
- src = [src] unless src.is_a?(::Array)
82
+ src = [src] unless src.is_a?(::Enumerable)
83
83
  src = src.reject { |val| path.join(File.basename(val)).exist? }
84
84
  return if src.empty?
85
85
  elsif path.exist?
@@ -89,7 +89,7 @@ module Squared
89
89
  case link
90
90
  when 'hard', 1
91
91
  FileUtils.ln(src, dest, force: force, verbose: verbose)
92
- when TrueClass, 'soft', 0
92
+ when ::TrueClass, 'soft', 0
93
93
  FileUtils.ln_s(src, dest, force: force, verbose: verbose)
94
94
  else
95
95
  FileUtils.cp(src, dest, verbose: verbose)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.4.9'
4
+ VERSION = '0.4.10'
5
5
  end
@@ -463,7 +463,7 @@ module Squared
463
463
  tasks << key if obj.has?(key, baseref)
464
464
  elsif (batch = series.batch_get(key))
465
465
  obj.allref.each do |ref|
466
- next unless (data = batch[ref])
466
+ next unless obj.has?(key, ref) && (data = batch[ref])
467
467
 
468
468
  data.each do |val|
469
469
  if (items = task_resolve(obj, val)).empty?
@@ -482,7 +482,7 @@ module Squared
482
482
  return [] if (base && !obj.ref?(baseref)) || !(data = series.alias_get(key))
483
483
 
484
484
  obj.allref.each do |ref|
485
- next unless (alt = data[ref])
485
+ next unless obj.has?(key, ref) && (alt = data[ref])
486
486
 
487
487
  ret = task_resolve(obj, alt)
488
488
  break unless ret.empty?
@@ -1505,11 +1505,11 @@ module Squared
1505
1505
  confirm("Upgrade to #{a}? #{b + e} [#{c}] ", d)
1506
1506
  end
1507
1507
 
1508
- def choice_index(msg, list, values: nil, multiple: false, accept: nil, series: false, trim: nil, column: nil,
1509
- attempts: 5, force: true)
1508
+ def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil,
1509
+ multiple: false, force: true, **kwargs)
1510
1510
  puts if !series && @@print_order > 0
1511
1511
  msg = "#{msg} (optional)" unless force
1512
- unless (ret = choice(msg, list, multiple: multiple, force: force, attempts: attempts)) || !force
1512
+ unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) || !force
1513
1513
  raise_error 'user cancelled'
1514
1514
  end
1515
1515
  if ret.nil? || ret.empty?
@@ -1855,14 +1855,14 @@ module Squared
1855
1855
  end
1856
1856
 
1857
1857
  def has_value?(data, val)
1858
- return data.value?(val) if data.is_a?(::Hash)
1859
- return data.is_a?(::Enumerable) && data.to_a.include?(val) if !val.is_a?(::Enumerable) || val.is_a?(::Hash)
1858
+ return data.value?(val) if data.is_a?(Hash)
1859
+ return data.is_a?(Enumerable) && data.to_a.include?(val) if !val.is_a?(Enumerable) || val.is_a?(Hash)
1860
1860
 
1861
1861
  val.to_a.any? do |obj|
1862
1862
  case data
1863
- when ::Hash
1863
+ when Hash
1864
1864
  data.value?(obj)
1865
- when ::Enumerable
1865
+ when Enumerable
1866
1866
  data.to_a.include?(obj)
1867
1867
  end
1868
1868
  end
@@ -196,7 +196,12 @@ module Squared
196
196
  when :list, :rm
197
197
  format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
198
198
  task flag do |_, args|
199
- image flag, args.to_a
199
+ args = args.to_a
200
+ if flag == :rm && args.empty?
201
+ choice_command :rm
202
+ else
203
+ image flag, args
204
+ end
200
205
  end
201
206
  end
202
207
  when 'network'
@@ -271,7 +276,6 @@ module Squared
271
276
  end
272
277
  if (val = option('tag', ignore: false))
273
278
  append_tag val
274
- ret << basic_option('tag', tagname(val))
275
279
  elsif !session_arg?('t', 'tag')
276
280
  append_tag tag
277
281
  end
@@ -331,7 +335,7 @@ module Squared
331
335
  def container(flag, opts = [], id: nil)
332
336
  cmd, opts = docker_session('container', flag, opts: opts)
333
337
  list = OPT_DOCKER[:container].fetch(flag, [])
334
- list.concat(OPT_DOCKER[:container][:update]) if flag == :run
338
+ list += OPT_DOCKER[:container][:update] if flag == :run
335
339
  op = OptionPartition.new(opts, list, cmd, project: self, args: flag == :run || flag == :exec)
336
340
  from = :"container:#{flag}"
337
341
  case flag
@@ -433,6 +437,8 @@ module Squared
433
437
  def image(flag, opts = [], sync: true, id: nil, registry: nil)
434
438
  cmd, opts = docker_session('image', flag, opts: opts)
435
439
  op = OptionPartition.new(opts, OPT_DOCKER[:image][flag], cmd, project: self)
440
+ exception = @exception
441
+ banner = true
436
442
  from = :"image:#{flag}"
437
443
  case flag
438
444
  when :list
@@ -455,6 +461,10 @@ module Squared
455
461
  when :rm
456
462
  if id
457
463
  op << id
464
+ if option('y')
465
+ exception = false
466
+ banner = false
467
+ end
458
468
  else
459
469
  if op.empty?
460
470
  list_image(flag, docker_output('image ls -a'), from: from) do |val|
@@ -476,9 +486,12 @@ module Squared
476
486
  img = docker_output 'image', 'tag', id, uri
477
487
  return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
478
488
 
479
- run(img, exception: true, sync: false, banner: false)
489
+ cmd = img
490
+ sync = false
491
+ exception = true
492
+ banner = false
480
493
  end
481
- run(sync: sync, from: from)
494
+ run(cmd, sync: sync, exception: exception, banner: banner, from: from)
482
495
  end
483
496
 
484
497
  def network(flag, opts = [], target: nil)
@@ -535,8 +548,12 @@ module Squared
535
548
  case flag
536
549
  when :run
537
550
  unless session_arg?('name', target: target)
538
- require 'random/formatter'
539
- target << basic_option('name', dnsname("#{name}_#{Random.new.alphanumeric(6)}"))
551
+ target << basic_option('name', dnsname("#{name}_%s" % if RUBY_VERSION >= '3.1'
552
+ require 'random/formatter'
553
+ Random.new.alphanumeric(6)
554
+ else
555
+ (0...6).map { rand(97..122).chr }.join
556
+ end))
540
557
  end
541
558
  when :exec
542
559
  raise_error('no command args', hint: from) if list.empty?
@@ -583,6 +600,8 @@ module Squared
583
600
  pwd_set do
584
601
  found = false
585
602
  index = 0
603
+ all = option('all', prefix: 'docker')
604
+ y = from == :'image:rm' && option('y', prefix: 'docker')
586
605
  pat = /^(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})(?:[_.,:-]|$)/
587
606
  IO.popen(session_done(cmd << '--format=json')).each do |line|
588
607
  data = JSON.parse(line)
@@ -596,41 +615,43 @@ module Squared
596
615
  id
597
616
  end)
598
617
  ee = data['Image'] || rt || aa
599
- next unless option('all') || ee.match?(pat) || aa.match?(pat)
600
-
601
- bb = index.succ.to_s
602
- cc = bb.size + 1
603
- a = sub_style(ee, styles: theme[:inline])
604
- b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a}#{ee == id ? '' : " (#{id})"}"
605
- c, d = no ? ['y/N', 'N'] : ['Y/n', 'Y']
606
- e = time_format(time_since(data['CreatedAt']), pass: ['ms'])
607
- f = sub_style(ARG[:BORDER][0], styles: theme[:inline])
608
- g = ' ' * (cc + 1)
609
- h = "#{sub_style(bb.rjust(cc), styles: theme[:current])} #{f} "
610
- puts unless index == 0
611
- puts "#{h + sub_style(aa, styles: theme[:subject])} (created #{e} ago)"
612
- cols = %w[Tag Status Ports]
613
- cols << case flag
614
- when :connect, :disconnect
615
- 'Networks'
616
- else
617
- 'Size'
618
- end
619
- cols.each do |key|
620
- next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
618
+ next unless all || ee.match?(pat) || aa.match?(pat)
619
+
620
+ unless y
621
+ bb = index.succ.to_s
622
+ cc = bb.size + 1
623
+ a = sub_style(ee, styles: theme[:inline])
624
+ b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a}#{ee == id ? '' : " (#{id})"}"
625
+ c, d = no ? ['y/N', 'N'] : ['Y/n', 'Y']
626
+ e = time_format(time_since(data['CreatedAt']), pass: ['ms'])
627
+ f = sub_style(ARG[:BORDER][0], styles: theme[:inline])
628
+ g = ' ' * (cc + 1)
629
+ h = "#{sub_style(bb.rjust(cc), styles: theme[:current])} #{f} "
630
+ puts unless index == 0
631
+ puts "#{h + sub_style(aa, styles: theme[:subject])} (created #{e} ago)"
632
+ cols = %w[Tag Status Ports]
633
+ cols << case flag
634
+ when :connect, :disconnect
635
+ 'Networks'
636
+ else
637
+ 'Size'
638
+ end
639
+ cols.each do |key|
640
+ next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
621
641
 
622
- puts "#{g + f} #{key}: #{as_a(data[key]).join(', ')}" unless data[key].to_s.empty?
623
- end
624
- w = 9 + flag.to_s.size + 4 + ee.size
625
- puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
626
- found = true
627
- index += 1
628
- next unless confirm("#{h + b}? [#{c}] ", d, timeout: 60)
642
+ puts "#{g + f} #{key}: #{as_a(data[key]).join(', ')}" unless data[key].to_s.empty?
643
+ end
644
+ w = 9 + flag.to_s.size + 4 + ee.size
645
+ puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
646
+ found = true
647
+ index += 1
648
+ next unless confirm("#{h + b}? [#{c}] ", d, timeout: 60)
629
649
 
630
- puts if @@print_order == 0
650
+ puts if @@print_order == 0
651
+ end
631
652
  yield id
632
653
  end
633
- puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) unless found
654
+ puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) unless found || y
634
655
  end
635
656
  rescue StandardError => e
636
657
  log.error e
@@ -658,7 +679,7 @@ module Squared
658
679
 
659
680
  def choice_command(flag)
660
681
  msg, cmd, index = case flag
661
- when :run
682
+ when :run, :rm
662
683
  ['Choose an image', 'images -a', 2]
663
684
  when :exec
664
685
  ['Choose a container', 'ps -a', 0]
@@ -671,16 +692,26 @@ module Squared
671
692
  puts log_message(Logger::INFO, 'none found', subject: name, hint: "docker #{cmd}")
672
693
  else
673
694
  puts " # #{header}"
695
+ multiple = false
696
+ parse = ->(val) { val.split(/\s+/)[index] }
674
697
  case flag
675
698
  when :run, :exec
676
699
  values = [['Options', flag == :run], ['Arguments', flag == :exec]]
677
700
  cmd = flag.to_s
701
+ when :rm
702
+ values = ['Options']
703
+ multiple = true
704
+ cmd = "image #{flag}"
678
705
  else
679
- values = [['Options', false], ['Container', true]]
706
+ values = ['Options', ['Container', true]]
680
707
  cmd = "network #{flag}"
681
708
  end
682
- out, opts, args = choice_index(msg, lines, values: values)
683
- ret = run docker_output(cmd, opts, '--', out.split(/\s+/)[index], args)
709
+ out, opts, args = choice_index(msg, lines, multiple: multiple, values: values)
710
+ ret = run docker_output(cmd, opts, '--', *(if out.is_a?(Array)
711
+ out.map! { |val| parse.call(val) }
712
+ else
713
+ [parse.call(out)]
714
+ end), args)
684
715
  print_success if success?(ret && cmd.start_with?('network'))
685
716
  end
686
717
  end
@@ -111,21 +111,30 @@ module Squared
111
111
  time_format(epoch, clock: clock)
112
112
  end
113
113
 
114
- def rev_clear(name)
114
+ def rev_clear(name, sync: true)
115
115
  if Dir.exist?(name) && (proj = find(name))
116
116
  name = proj.name
117
117
  end
118
- rev_write if rev_entry(name, 'revision', val: '', create: false)
118
+ rev_write(sync: sync) if rev_entry(name, 'revision', val: '', create: false)
119
119
  end
120
120
 
121
- def rev_write(name = nil, data = nil, utc: nil)
121
+ def rev_write(name = nil, data = nil, sync: true, utc: nil)
122
122
  return unless @revfile
123
123
 
124
124
  if name
125
125
  data&.each { |key, val| rev_entry(name, key, val: val) }
126
126
  rev_timeutc(name, utc) if utc
127
127
  end
128
- File.write(@revfile, JSON.pretty_generate(@revdoc))
128
+ sleep 0 while !sync && @revlock
129
+ begin
130
+ @revlock = true
131
+ File.write(@revfile, JSON.pretty_generate(@revdoc))
132
+ rescue StandardError => e
133
+ log&.debug e
134
+ warn log_message(Logger::WARN, e, pass: true) if warning?
135
+ ensure
136
+ @revlock = false
137
+ end
129
138
  end
130
139
 
131
140
  def git_clone?(path, name = nil)
@@ -355,8 +364,13 @@ module Squared
355
364
  if flag == :remote
356
365
  format_desc action, flag, 'remote,opts*'
357
366
  task flag, [:remote] do |_, args|
358
- remote = param_guard(action, flag, args: args, key: :remote)
359
- __send__(action, flag, args.extras, remote: remote)
367
+ if (remote = args.remote)
368
+ args = args.extras
369
+ else
370
+ remote = choice_remote
371
+ args = args.to_a.drop(1)
372
+ end
373
+ __send__(action, flag, args, remote: remote)
360
374
  end
361
375
  else
362
376
  format_desc action, flag, 'opts*'
@@ -845,12 +859,10 @@ module Squared
845
859
  args = args.to_a
846
860
  if args.empty? || args.last == ':'
847
861
  files = []
848
- git_spawn('status -s --porcelain', stdout: false).each do |line|
849
- next unless line =~ /^(.)(.)\s+(.+)$/
850
-
851
- case (flag == :staged ? $1 : $2)
852
- when 'A', 'M'
853
- files << $3
862
+ status_data.each do |line|
863
+ case (flag == :staged ? line[2] : line[1])
864
+ when /[AMDRTC]/
865
+ files << line[0]
854
866
  end
855
867
  end
856
868
  unless files.empty?
@@ -867,12 +879,11 @@ module Squared
867
879
  end
868
880
  when 'git'
869
881
  before = case flag
870
- when :rm then 'source+,destination'
882
+ when :mv then 'source+,destination'
871
883
  when :revert then 'commit+' end
872
884
  format_desc(action, flag, 'opts*', before: before, after: case flag
873
- when :add, :clean, :mv
874
- 'pathspec*'
875
- end)
885
+ when :add then 'pathspec*|:pattern:*'
886
+ when :clean, :rm then 'pathspec*' end)
876
887
  task flag do |_, args|
877
888
  git flag, args.to_a
878
889
  end
@@ -888,13 +899,13 @@ module Squared
888
899
  super
889
900
  end
890
901
 
891
- def depend(*, **)
892
- workspace.rev_clear(name)
902
+ def depend(*, sync: invoked_sync?('depend'), **)
903
+ workspace.rev_clear(name, sync: sync)
893
904
  super
894
905
  end
895
906
 
896
- def clean(*, **)
897
- workspace.rev_clear(name)
907
+ def clean(*, sync: invoked_sync?('clean'), **)
908
+ workspace.rev_clear(name, sync: sync)
898
909
  super
899
910
  end
900
911
 
@@ -915,7 +926,7 @@ module Squared
915
926
  end
916
927
  end
917
928
  append_pull(opts, OPT_GIT[:pull] + OPT_GIT[:fetch][:pull],
918
- no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag)
929
+ no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
919
930
  source(sync: sync, sub: if verbose
920
931
  [
921
932
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
@@ -957,11 +968,9 @@ module Squared
957
968
  end
958
969
 
959
970
  def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
960
- cmd, opts = git_session('fetch', opts: opts)
961
- append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]), remote: remote,
962
- flag: flag)
963
- cmd << '--all' if !remote && !session_arg?('multiple') && option('all')
964
- cmd << '--verbose' if verbose && !session_arg?('quiet')
971
+ opts = git_session('fetch', opts: opts).last
972
+ append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]),
973
+ remote: remote, flag: flag, from: :fetch)
965
974
  source(sync: sync, **threadargs)
966
975
  end
967
976
 
@@ -980,7 +989,7 @@ module Squared
980
989
  opts[:origin] = val if (val = option('origin', ignore: false))
981
990
  opts[:branch] = val if (val = option('branch', strict: true))
982
991
  opts[:local] = val != '0' if (val = option('local', strict: true))
983
- opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(:clone)
992
+ opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
984
993
  append_hash opts
985
994
  cmd << '--quiet' unless verbose
986
995
  append_value(data[0], path, delim: true)
@@ -1112,7 +1121,8 @@ module Squared
1112
1121
  msg = sub_style('completed', styles: theme[:active])
1113
1122
  puts log_message(Logger::INFO, name, msg, subject: 'revbuild', hint: time_format(epochtime - start))
1114
1123
  end
1115
- workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) }, utc: 'build')
1124
+ workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1125
+ sync: sync, utc: 'build')
1116
1126
  end
1117
1127
 
1118
1128
  def reset(flag, opts = [], refs: nil, ref: nil, mode: nil, commit: nil)
@@ -1600,17 +1610,25 @@ module Squared
1600
1610
  append_commit(*op.extras)
1601
1611
  end
1602
1612
  when :add, :clean
1603
- if flag == :add && op.empty? && !op.arg?('pathspec-from-file')
1604
- files = []
1605
- red = color(:red)
1606
- git_spawn('status -s --porcelain -uall', stdout: false).each do |line|
1607
- next unless line =~ /^.(.) (.+)$/
1608
-
1609
- files << "#{sub_style($1, styles: red)} #{$2}"
1613
+ if flag == :add && !op.arg?('pathspec-from-file')
1614
+ grep, list = op.extras.partition { |val| val.start_with?(':') && val.end_with?(':') }
1615
+ if list.empty? || !grep.empty?
1616
+ red = color(:red)
1617
+ grep.map! { |val| Regexp.new(val[1..-2]) }
1618
+ files = status_data.map! do |a, b|
1619
+ next unless grep.empty? || grep.any? { |pat| pat.match?(a) }
1620
+
1621
+ "#{sub_style(b, styles: red)} #{a}"
1622
+ end
1623
+ .compact
1624
+ unless files.empty?
1625
+ files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1626
+ accept: 'Add?')
1627
+ end
1628
+ op.swap(list + files)
1610
1629
  end
1611
- files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/, accept: 'Add?')
1612
1630
  end
1613
- append_pathspec(files || op.extras)
1631
+ append_pathspec(op.extras)
1614
1632
  verbose = flag == :add && !op.arg?('verbose')
1615
1633
  print_success if success?(source) && verbose
1616
1634
  return
@@ -1619,7 +1637,7 @@ module Squared
1619
1637
  raise_error 'no source/destination' unless refs.size > 1
1620
1638
  op.merge(refs)
1621
1639
  when :rm
1622
- append_pathspec(op.extras, expect: !op.arg?('pathspec-from-file'))
1640
+ append_pathspec(op.extras, expect: true)
1623
1641
  end
1624
1642
  source(sync: false, stderr: true)
1625
1643
  end
@@ -1639,13 +1657,13 @@ module Squared
1639
1657
  private
1640
1658
 
1641
1659
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1642
- multiple: false, sub: nil)
1660
+ multiple: false, **kwargs)
1643
1661
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1644
1662
  banner = nil if multiple && banner
1645
1663
  if cmd.respond_to?(:done)
1646
1664
  if io && banner == false
1647
1665
  from = nil
1648
- elsif !from && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z][a-z\-]{2,}\z/) })
1666
+ elsif !from && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z\-]*\z/) })
1649
1667
  from = :"git:#{from}"
1650
1668
  end
1651
1669
  banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
@@ -1677,7 +1695,7 @@ module Squared
1677
1695
  require 'open3'
1678
1696
  if stderr
1679
1697
  Open3.popen3(cmd) do |_, out, err|
1680
- n = write_lines(out, banner: banner, sub: sub, pass: true)
1698
+ n = write_lines(out, banner: banner, pass: true, **kwargs)
1681
1699
  if n == 0
1682
1700
  n = write_lines(err, banner: banner)
1683
1701
  print_success if success?(n == 0 && !banner.nil?)
@@ -1686,7 +1704,7 @@ module Squared
1686
1704
  end
1687
1705
  end
1688
1706
  else
1689
- Open3.popen2e(cmd) { |_, out| write_lines(out, banner: banner) }
1707
+ Open3.popen2e(cmd) { |_, out| write_lines(out, banner: banner, **kwargs) }
1690
1708
  end
1691
1709
  end
1692
1710
  rescue StandardError => e
@@ -1703,13 +1721,8 @@ module Squared
1703
1721
  end
1704
1722
 
1705
1723
  def write_lines(data, banner: nil, loglevel: nil, grep: nil, sub: nil, pass: false, first: false)
1706
- grep = as_a(grep).map do |val|
1707
- next val if val.is_a?(Regexp)
1708
-
1709
- Regexp.new(val == '*' ? '.+' : val.to_s)
1710
- end
1711
- grep = nil if grep.empty?
1712
- sub = nil if stdin?
1724
+ grep &&= as_a(grep).yield_self { |a| a.empty? || a.include?('*') ? nil : a.map { |val| Regexp.new(val) } }
1725
+ sub &&= stdin? ? nil : as_a(sub)
1713
1726
  ret = 0
1714
1727
  out = []
1715
1728
  data.each do |line|
@@ -1739,7 +1752,7 @@ module Squared
1739
1752
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1740
1753
  styles << :bold if styles.size <= 1
1741
1754
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1742
- sub: { pat: /\A(\d+)(.+)\z/m, styles: styles })
1755
+ sub: { pat: /^(\d+)(.+)$/, styles: styles })
1743
1756
  else
1744
1757
  puts empty_status("No #{type} were #{action}", 'grep', grep.is_a?(Array) ? case grep.size
1745
1758
  when 0
@@ -1786,8 +1799,8 @@ module Squared
1786
1799
  glob = kwargs.fetch(:include, [])
1787
1800
  pass = kwargs.fetch(:exclude, [])
1788
1801
  ret = {}
1789
- git_spawn('status -s --porcelain', *args, stdout: false).each do |line|
1790
- next unless (file = line[/^[A-Z ?!]{3}"?(.+?)"?$/, 1])
1802
+ status_data(*args).each do |line|
1803
+ file = line.first
1791
1804
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1792
1805
  next if !pass.empty? && pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1793
1806
 
@@ -1796,9 +1809,19 @@ module Squared
1796
1809
  ret
1797
1810
  end
1798
1811
 
1799
- def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil)
1812
+ def status_data(*args)
1813
+ ret = []
1814
+ git_spawn('status -z -uall', *args).split("\x0").each do |line|
1815
+ next unless line =~ /^(.)(.) (.+)$/
1816
+
1817
+ ret << [$3, $2, $1]
1818
+ end
1819
+ ret
1820
+ end
1821
+
1822
+ def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1800
1823
  target << '--force' if option('force', target: target)
1801
- append_submodules(target: target)
1824
+ append_submodules(target: target, from: from)
1802
1825
  return if !remote && opts.empty?
1803
1826
 
1804
1827
  refspec = []
@@ -1823,6 +1846,7 @@ module Squared
1823
1846
  op.errors << opt
1824
1847
  end
1825
1848
  end
1849
+ op << '--verbose' if (flag || from == :fetch) && verbose && !op.arg?('quiet')
1826
1850
  if remote
1827
1851
  op.append(remote, delim: true)
1828
1852
  if (val = option('refspec', target: target, strict: true))
@@ -1834,6 +1858,8 @@ module Squared
1834
1858
  elsif op.arg?('--multiple')
1835
1859
  op.swap.merge(op.map! { |opt| shell_escape(opt, quote: true) })
1836
1860
  return
1861
+ elsif option('all')
1862
+ cmd << '--all'
1837
1863
  end
1838
1864
  op.clear(errors: true, subject: flag.to_s) if flag
1839
1865
  end
@@ -1874,7 +1900,7 @@ module Squared
1874
1900
  append_first('head', 'tree-ish', 'object', target: target, flag: false, ignore: false)
1875
1901
  end
1876
1902
 
1877
- def append_submodules(from = nil, target: @session)
1903
+ def append_submodules(target: @session, from: nil)
1878
1904
  return unless (val = option('recurse-submodules', target: target, ignore: false))
1879
1905
 
1880
1906
  if from == :clone
@@ -1928,7 +1954,7 @@ module Squared
1928
1954
  target.include?('--dry-run') || !option('dry-run', target: target).nil?
1929
1955
  end
1930
1956
 
1931
- def quiet?(target: @session)
1957
+ def quiet?(*, target: @session, **)
1932
1958
  return false unless target
1933
1959
 
1934
1960
  target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
@@ -191,22 +191,21 @@ module Squared
191
191
  end
192
192
  end
193
193
  when 'publish'
194
- format_desc(action, flag, 'otp?,dry-run?=true', before: flag == :tag ? 'tag' : nil)
194
+ format_desc(action, flag, 'otp?,dry-run?,public|restricted?', before: flag == :tag ? 'tag' : nil)
195
195
  task flag do |_, args|
196
- if flag == :latest
197
- otp, dryrun = args.to_a
198
- else
199
- args = param_guard(action, flag, args: args.to_a)
200
- tag, otp, dryrun = args
196
+ args = args.to_a
197
+ dryrun = true if args.delete('dry-run') || args.delete('true') || args.delete('d')
198
+ if args.delete('public') || args.delete('p')
199
+ access = 'public'
200
+ elsif args.delete('restricted') || args.delete('r')
201
+ access = 'restricted'
201
202
  end
202
- check = ->(val) { val == 'dry-run' || val == 'true' }
203
- if check.call(otp)
204
- dryrun = true
205
- otp = nil
203
+ if flag == :latest
204
+ otp = args.first
206
205
  else
207
- dryrun &&= check.call(dryrun)
206
+ tag, otp = param_guard(action, flag, args: args)
208
207
  end
209
- publish(flag, otp: otp, tag: tag, dryrun: dryrun)
208
+ publish(flag, otp: otp, tag: tag, dryrun: dryrun, access: access)
210
209
  end
211
210
  end
212
211
  end
@@ -217,7 +216,7 @@ module Squared
217
216
  end
218
217
 
219
218
  def copy(from: 'build', into: 'node_modules', scope: nil, also: nil, create: nil, workspace: false,
220
- link: false, force: false, override: false, **kwargs)
219
+ link: false, force: false, override: false, sync: invoked_sync?('copy'), **kwargs)
221
220
  glob = kwargs[:include]
222
221
  pass = kwargs[:exclude]
223
222
  if @copy && !override
@@ -239,7 +238,7 @@ module Squared
239
238
  items = []
240
239
  if build? && path != @workspace.home && @workspace.home?
241
240
  items << @workspace.home
242
- @workspace.rev_clear(@workspace.find(@workspace.home).name)
241
+ @workspace.rev_clear(@workspace.find(@workspace.home).name, sync: sync)
243
242
  end
244
243
  items.concat(as_a(also)) if also
245
244
  return if items.empty?
@@ -250,13 +249,13 @@ module Squared
250
249
  case dir
251
250
  when Pathname
252
251
  dest = dir
253
- @workspace.rev_clear(dest)
252
+ @workspace.rev_clear(dest, sync: sync)
254
253
  when String
255
254
  dest = @workspace.root + dir
256
- @workspace.rev_clear(dest)
255
+ @workspace.rev_clear(dest, sync: sync)
257
256
  when Symbol
258
257
  if (proj = @workspace.find(name: dir))
259
- @workspace.rev_clear(proj.name)
258
+ @workspace.rev_clear(proj.name, sync: sync)
260
259
  dest = proj.path
261
260
  else
262
261
  log.warn message("copy project :#{dir}", hint: 'not found')
@@ -274,10 +273,10 @@ module Squared
274
273
  glob = dir[:include]
275
274
  pass = dir[:exclude]
276
275
  dest = items.first unless dest && dest != true
277
- @workspace.rev_clear(dest) unless dest == true
276
+ @workspace.rev_clear(dest, sync: sync) unless dest == true
278
277
  when Project::Base
279
278
  dest = dir.path
280
- @workspace.rev_clear(dir.name)
279
+ @workspace.rev_clear(dir.name, sync: sync)
281
280
  else
282
281
  raise_error "copy: given #{dir}"
283
282
  end
@@ -329,7 +328,7 @@ module Squared
329
328
  if @depend && !flag
330
329
  super
331
330
  elsif outdated?
332
- workspace.rev_clear(name)
331
+ workspace.rev_clear(name, sync: sync)
333
332
  return update if !flag && env('NODE_UPDATE')
334
333
 
335
334
  if (yarn = dependtype(:yarn)) > 0
@@ -548,7 +547,7 @@ module Squared
548
547
  package 'update'
549
548
  end
550
549
 
551
- def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, dryrun: nil, **)
550
+ def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, dryrun: nil, access: nil)
552
551
  if read_packagemanager(:private)
553
552
  if warning?
554
553
  warn log_message(Logger::WARN, 'invalid task "publish"', subject: name, hint: 'private', pass: true)
@@ -558,11 +557,10 @@ module Squared
558
557
  return unless version
559
558
 
560
559
  cmd = session 'npm', 'publish'
561
- otp = option('otp') if otp.nil?
562
- tag = option('tag') if tag.nil?
563
560
  dryrun = dryrun?('npm') if dryrun.nil?
564
- cmd << basic_option('otp', otp) if otp
565
- cmd << shell_option('tag', tag) if tag
561
+ cmd << basic_option('otp', otp) if otp ||= option('otp')
562
+ cmd << basic_option('tag', tag) if tag ||= option('tag')
563
+ cmd << basic_option('access', access) if access ||= option('access')
566
564
  if verbose
567
565
  if dryrun
568
566
  cmd << '--dry-run'
@@ -760,6 +758,10 @@ module Squared
760
758
  outdated?
761
759
  end
762
760
 
761
+ def refresh?
762
+ !Node.prod?
763
+ end
764
+
763
765
  def yarn?
764
766
  (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependext).exist?
765
767
  if (rc = rootpath('.yarnrc.yml', ascend: dependext)).exist?
@@ -229,7 +229,7 @@ module Squared
229
229
  super
230
230
  elsif outdated?
231
231
  venv_init
232
- workspace.rev_clear(name)
232
+ workspace.rev_clear(name, sync: sync)
233
233
  if !flag && dependtype == 1
234
234
  cmd = poetry_session 'install', '-n'
235
235
  cmd << '--no-root' if option('no-root')
@@ -219,13 +219,20 @@ module Squared
219
219
  when :file
220
220
  format_desc action, flag, 'path,opts*,args*'
221
221
  task flag, [:rb] do |_, args|
222
- file = param_guard(action, flag, args: args, key: :rb)
223
- ruby(flag, args.to_a.drop(1), file: file)
222
+ if (file = args.rb)
223
+ args = args.to_a.drop(1)
224
+ else
225
+ file, opts, extra = choice_index('Select a file', Dir.glob('*.rb', base: path),
226
+ values: %w[Options Arguments], force: true, series: true)
227
+ args = OptionPartition.strip(opts)
228
+ ENV['RUBY_ARGS'] = extra if extra
229
+ end
230
+ ruby(flag, args, file: file)
224
231
  end
225
232
  when :script
226
- format_desc action, flag, 'RUBY_E?,opts*,args*'
233
+ format_desc action, flag, 'opts*,args*'
227
234
  task flag do |_, args|
228
- command = ENV['RUBY_E'] || readline('Enter script', force: true)
235
+ command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: ['##', ';'])
229
236
  ruby(flag, args.to_a, command: command)
230
237
  end
231
238
  when :version
@@ -246,7 +253,7 @@ module Squared
246
253
  if @depend
247
254
  super
248
255
  elsif outdated?
249
- workspace.rev_clear(name)
256
+ workspace.rev_clear(name, sync: sync)
250
257
  cmd = bundle_session 'install'
251
258
  cmd << '--without=development' if prod?
252
259
  if (n = option('jobs')).to_i > 0
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.4.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubygems_version: 3.6.2
126
+ rubygems_version: 3.6.7
127
127
  specification_version: 4
128
128
  summary: Rake task generator for managing multi-language workspaces.
129
129
  test_files: []