squared 0.4.10 → 0.4.12

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: ba613d7ba02a664fb4581573f6ee04d705f6bb2d1cb165c30c49d97eb259cbec
4
- data.tar.gz: 764888100c6a6560478e99ed1451f1593a86e120799e25400708d81be934102c
3
+ metadata.gz: 29f44c24914fda199da7719eed8e90da0b508d0c93cf234ff16581e7bf34d112
4
+ data.tar.gz: aec5400d8d7abfc44c4abd31bf091de65873f177c57b0782118c081e5875009b
5
5
  SHA512:
6
- metadata.gz: 1cf6f1f10babf7bfbbe665fe5fc97527cf1df4093a3e6b8b906a4bf8cf87f538c601faee86f40decb79455de340e1d8f510fb45d0acc17694cd97a4051010539
7
- data.tar.gz: 2b537c2c6a30a17191f32eb1012b225bc29fff9e9102b663a6dcf3ffd05465f4e8a0790f4cbdd6c63b21bd64c762e85dff1d22b4eb35053702a6692e92c994cc
6
+ metadata.gz: 48e5e2e436689d44a146562e4d791cebf5e19da9ca012c7cdd1860b2fd370c163933fdf58638bf48d4a1b1111170f5f8ed890269388a965dcf2e9e47619a4ea6
7
+ data.tar.gz: 816de955802b80b8687c6f8903187dfda9f60720696f4ea80ce1295df7e07a3c3bd38bf4b1da5f6ceb6f7ab0031953ccbab54a13adddc7a41c195adcb9f83912
data/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.12] - 2025-05-18
4
+
5
+ ### Added
6
+
7
+ - Git command stash with actions using index are interactive.
8
+ - Project tasks can be pipelined in separate thread groups.
9
+ - Global tasks can be pipelined with project tasks.
10
+ - Python venv module supports initialization options.
11
+
12
+ ### Changed
13
+
14
+ - Node command run parses recognized options per package manager.
15
+ - Project unpack uses merge with multiple headers.
16
+ - Invalid log directory does not abort program.
17
+ - Project accessor line_width was replaced with Rake equivalent.
18
+
19
+ ### Fixed
20
+
21
+ - Git pull option multiple was not detected.
22
+ - Git output used undefined method when displaying results.
23
+ - Workspace did not add prefix to duplicate project names.
24
+
25
+ ## [0.3.11] - 2025-05-15
26
+
27
+ - See `0.2.11`.
28
+
29
+ ## [0.2.11] - 2025-05-15
30
+
31
+ ### Fixed
32
+
33
+ - Disabled batch and alias tasks were not hidden.
34
+ - Workspace git did not parse multiple download URIs.
35
+
36
+ ## [0.1.8] - 2025-05-15
37
+
38
+ ### Fixed
39
+
40
+ - Disabled batch and alias tasks were not hidden.
41
+ - Log messages were written to terminal twice when emphasized.
42
+ - Node outdated interactive for major would sometimes deactivate.
43
+ - Node outdated interactive for major was mislabeled as minor.
44
+
45
+ ## [0.4.11] - 2025-05-09
46
+
47
+ ### Added
48
+
49
+ - Docker command build action bake is interactive.
50
+
51
+ ### Fixed
52
+
53
+ - Docker build command was not functional.
54
+
3
55
  ## [0.4.10] - 2025-05-08
4
56
 
5
57
  ### Added
@@ -655,6 +707,8 @@
655
707
 
656
708
  - Changelog was created.
657
709
 
710
+ [0.4.12]: https://github.com/anpham6/squared/releases/tag/v0.4.12-ruby
711
+ [0.4.11]: https://github.com/anpham6/squared/releases/tag/v0.4.11-ruby
658
712
  [0.4.10]: https://github.com/anpham6/squared/releases/tag/v0.4.10-ruby
659
713
  [0.4.9]: https://github.com/anpham6/squared/releases/tag/v0.4.9-ruby
660
714
  [0.4.8]: https://github.com/anpham6/squared/releases/tag/v0.4.8-ruby
@@ -666,6 +720,7 @@
666
720
  [0.4.2]: https://github.com/anpham6/squared/releases/tag/v0.4.2-ruby
667
721
  [0.4.1]: https://github.com/anpham6/squared/releases/tag/v0.4.1-ruby
668
722
  [0.4.0]: https://github.com/anpham6/squared/releases/tag/v0.4.0-ruby
723
+ [0.3.11]: https://github.com/anpham6/squared/releases/tag/v0.3.11-ruby
669
724
  [0.3.10]: https://github.com/anpham6/squared/releases/tag/v0.3.10-ruby
670
725
  [0.3.9]: https://github.com/anpham6/squared/releases/tag/v0.3.9-ruby
671
726
  [0.3.8]: https://github.com/anpham6/squared/releases/tag/v0.3.8-ruby
@@ -677,6 +732,7 @@
677
732
  [0.3.2]: https://github.com/anpham6/squared/releases/tag/v0.3.2-ruby
678
733
  [0.3.1]: https://github.com/anpham6/squared/releases/tag/v0.3.1-ruby
679
734
  [0.3.0]: https://github.com/anpham6/squared/releases/tag/v0.3.0-ruby
735
+ [0.2.11]: https://github.com/anpham6/squared/releases/tag/v0.2.11-ruby
680
736
  [0.2.10]: https://github.com/anpham6/squared/releases/tag/v0.2.10-ruby
681
737
  [0.2.9]: https://github.com/anpham6/squared/releases/tag/v0.2.9-ruby
682
738
  [0.2.8]: https://github.com/anpham6/squared/releases/tag/v0.2.8-ruby
@@ -688,6 +744,7 @@
688
744
  [0.2.2]: https://github.com/anpham6/squared/releases/tag/v0.2.2-ruby
689
745
  [0.2.1]: https://github.com/anpham6/squared/releases/tag/v0.2.1-ruby
690
746
  [0.2.0]: https://github.com/anpham6/squared/releases/tag/v0.2.0-ruby
747
+ [0.1.8]: https://github.com/anpham6/squared/releases/tag/v0.1.8-ruby
691
748
  [0.1.7]: https://github.com/anpham6/squared/releases/tag/v0.1.7-ruby
692
749
  [0.1.6]: https://github.com/anpham6/squared/releases/tag/v0.1.6-ruby
693
750
  [0.1.5]: https://github.com/anpham6/squared/releases/tag/v0.1.5-ruby
data/README.md CHANGED
@@ -140,13 +140,13 @@ rake clone # node + docs
140
140
  # PIPE_FAIL={0,1}
141
141
  # PORT=3000
142
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 .
143
+ docker build -t node --build-arg NODE_TAG=22 --build-arg NODE_INSTALL=pnpm -f slim.Dockerfile .
144
144
  NODE=22 docker buildx bake node
145
145
  # OR
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 .
146
+ docker build -t ruby --build-arg RUBY_TAG=3.4.0 --build-arg NODE_VERSION=22 --build-arg PIPE_FAIL=0 -f ruby.Dockerfile .
147
147
  RUBY=3.4.0 docker buildx bake ruby
148
148
  # OR
149
- docker build -t nginx --build-arg NGINX_VERSION=1.27 --build-arg PORT=3000 --build-arg NODE_VERSION=20 -f Dockerfile.nginx .
149
+ docker build -t nginx --build-arg NGINX_VERSION=1.27 --build-arg PORT=3000 --build-arg NODE_VERSION=20 -f nginx.Dockerfile .
150
150
  NGINX=1.27 docker buildx bake nginx
151
151
 
152
152
  # Express
data/README.ruby.md CHANGED
@@ -234,17 +234,74 @@ Workspace::Application
234
234
  .build(parallel: ["clone"]) # rake clone + rake clone:sync
235
235
  ```
236
236
 
237
- ### Graph
237
+ ### Build
238
+
239
+ #### Chain
240
+
241
+ There has to be at least one project which uses the ``step`` attribute. Other placement attributes are ignored.
242
+
243
+ **NOTE**: Projects can only reference non-global namespaced tasks. (e.g. with ":")
244
+
245
+ ```ruby
246
+ Workspace::Application
247
+ .new
248
+ .with(:node) do
249
+ add("e-mc", "emc") do
250
+ chain "all", "clean", step: 1 # Required
251
+ chain "all", "build", step: 2
252
+ end
253
+ add("pi-r", "pir") do
254
+ chain "all", "build", after: "emc:build" # step: 3
255
+ end
256
+ add("pi-r2", "pir2") do
257
+ chain "all", "build", before: "squared" # step: 3
258
+ end
259
+ add("squared-express", "express") do
260
+ chain "all", "clean", with: "emc" # step: 1
261
+ chain "all", "build", with: "pir" # step: 3
262
+ end
263
+ add("squared") do
264
+ revbuild(include: %w[src/ framework/ types/]) # Git revision build command (optional)
265
+ chain "all", "revbuild", after: "express:build" # step: 4
266
+ chain "publish", "bump:patch", "publish:latest", step: 0, sync: true # rake publish -> squared:bump:patch -> squared:publish:latest
267
+ end
268
+ end
269
+ .with(:python) do
270
+ doc("make html")
271
+ add("android-docs") do
272
+ chain "all", "doc", with: "squared", after: "squared" # step: 4
273
+ end
274
+ add("chrome-docs") do
275
+ chain "all", "doc", with: "squared", before: "squared:revbuild" # Same
276
+ end
277
+ end
278
+ .chain "all", "status", with: "squared", after: "android-docs" # Global tasks (e.g. without ":")
279
+ .build
280
+ ```
281
+
282
+ ```sh
283
+ rake all # all[1-3-4]
284
+ rake all:print
285
+ ```
286
+
287
+ Threaded is the default when there are two or more tasks. Using ``with`` and either **before** or **after** will create a synchronous group.
288
+
289
+ * Step 1: emc:clean + express:clean (thread)
290
+ * Step 2: emc:build (sync)
291
+ * Step 3: pir:build + express:build + pir2:build (thread)
292
+ * Step 4: chrome-docs:doc + squared:revbuild + android-docs:doc + status (sync)
293
+
294
+ #### Graph
238
295
 
239
296
  ```ruby
240
297
  Workspace::Application
241
298
  .new(main: "squared")
242
- .graph(["depend"], ref: :git) # Optional
299
+ .graph(["depend"], ref: :git) # Optional
243
300
  .with(:python) do
244
301
  doc(windows? ? ".\make.bat html" : "make html") # rake android-docs:doc | rake doc:python
245
302
  add("android-docs", "android", venv: "/home/user/.venv") # rake android-docs:depend
246
- add("chrome-docs", "chrome", graph: "android", venv: ".venv") do # /workspaces/chrome-docs/.venv
247
- variable_set :dependindex, 2 # Use Poetry for dependencies (optional)
303
+ add("chrome-docs", "chrome", graph: "android", venv: %w[.venv --clear]) do # /workspaces/chrome-docs/.venv
304
+ variable_set :dependindex, 1 # Use Poetry for dependencies (optional)
248
305
  end
249
306
  end
250
307
  .with(:node) do
@@ -344,18 +401,14 @@ rake squared:graph:run[express,pir] # emc + pir + express + squared
344
401
  rake squared:graph:run[node,-emc] # pir + express + squared
345
402
  ```
346
403
 
347
- ### Batch
404
+ ### Tasks
348
405
 
349
406
  ```ruby
350
407
  Workspace::Series.batch(:ruby, :node, {
351
408
  stage: %i[graph test],
352
409
  reset: %i[stash pull]
353
410
  })
354
- ```
355
-
356
- ### Rename
357
411
 
358
- ```ruby
359
412
  Workspace::Series.rename("depend", "install")
360
413
  ```
361
414
 
@@ -423,6 +476,7 @@ Non-task:
423
476
  * active
424
477
  * inline
425
478
  * subject
479
+ * border
426
480
  * warn
427
481
  * caution
428
482
  * current
@@ -561,7 +615,6 @@ LOG_AUTO # year,y,month,m,day,d,1
561
615
  # Optional
562
616
  LOG_DIR # exist?
563
617
  LOG_LEVEL # See gem "logger"
564
- LOG_COLUMNS # terminal width (default: 80)
565
618
  ```
566
619
 
567
620
  ### Git
@@ -46,6 +46,7 @@ module Squared
46
46
  active: [:bold],
47
47
  inline: [:bold],
48
48
  subject: [:bold],
49
+ border: nil,
49
50
  warn: %i[white red!],
50
51
  caution: %i[black yellow!],
51
52
  current: nil,
@@ -167,7 +167,7 @@ module Squared
167
167
  color ? sub_style(ret, *styles) : ret
168
168
  end
169
169
 
170
- def log_message(level, *args, subject: nil, hint: nil, color: ARG[:COLOR], append: true, pass: false)
170
+ def log_message(level, *args, subject: nil, hint: nil, append: true, pass: false, color: ARG[:COLOR])
171
171
  args = args.map(&:to_s)
172
172
  if level.is_a?(::Numeric)
173
173
  if append && respond_to?(:log)
@@ -176,10 +176,10 @@ module Squared
176
176
  end
177
177
  return false if !pass && level < ARG[:LEVEL]
178
178
  end
179
- if args.size > 1 && !hint
179
+ if (args.size > 1 && !hint) || hint == false
180
180
  title = log_title(level, color: false)
181
- sub = { pat: /\A(#{Regexp.escape(title)})(.*)\z/m, styles: __get__(:theme)[:logger][log_sym(level)] } if color
182
- emphasize(args, title: title + (subject ? " #{subject}" : ''), sub: sub)
181
+ sub = [pat: /\A(#{Regexp.escape(title)})(.*)\z/m, styles: __get__(:theme)[:logger][log_sym(level)]] if color
182
+ emphasize(args, title: title + (subject ? " #{subject}" : ''), sub: sub, pipe: -1)
183
183
  else
184
184
  msg = [log_title(level, color: color)]
185
185
  msg << (color ? sub_style(subject, styles: (@theme && @theme[:subject]) || :bold) : subject) if subject
@@ -211,7 +211,8 @@ module Squared
211
211
  (empty ? args.reject { |val| val.nil? || val.empty? } : args).join(space) + (hint ? " (#{hint})" : '')
212
212
  end
213
213
 
214
- def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, border: nil, pipe: nil)
214
+ def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, pipe: nil,
215
+ border: @theme && @theme[:border])
215
216
  n = 0
216
217
  max = ->(v) { n = [n, v.max_by(&:size).size].max }
217
218
  set = lambda do |v|
@@ -271,6 +272,8 @@ module Squared
271
272
  yield out
272
273
  elsif pipe
273
274
  case pipe
275
+ when -1
276
+ return out
274
277
  when 0
275
278
  pipe = $stdin
276
279
  when 2
@@ -47,9 +47,7 @@ module Squared
47
47
 
48
48
  def task_invoked?(*args)
49
49
  Rake::Task.tasks.any? do |obj|
50
- next unless obj.already_invoked
51
-
52
- args.any? { |val| val.is_a?(::Regexp) ? obj.name.match?(val) : val == obj.name }
50
+ obj.already_invoked && args.any? { |val| val.is_a?(::Regexp) ? obj.name.match?(val) : val == obj.name }
53
51
  end
54
52
  end
55
53
 
@@ -157,15 +155,13 @@ module Squared
157
155
  when ::Numeric
158
156
  return key if key.between?(0, 2)
159
157
  end
158
+ return default unless default.is_a?(::String)
159
+
160
160
  begin
161
- if default.is_a?(::String)
162
- default = (root ? Pathname.new(root) + default : Pathname.new(default)).realdirpath
163
- end
161
+ (root ? Pathname.new(root) + default : Pathname.new(default)).realdirpath
164
162
  rescue StandardError => e
165
163
  warn e
166
164
  1
167
- else
168
- default
169
165
  end
170
166
  end
171
167
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.4.10'
4
+ VERSION = '0.4.12'
5
5
  end
@@ -106,6 +106,7 @@ module Squared
106
106
  else
107
107
  @theme = {}
108
108
  end
109
+ @chain = {}
109
110
  @script = {
110
111
  group: {},
111
112
  ref: {},
@@ -132,8 +133,9 @@ module Squared
132
133
  def initialize_session
133
134
  return unless @pipe.is_a?(Pathname)
134
135
 
135
- bord = '#' * Project.line_width
136
- puts bord, format('Session started on %s by %s', Time.now.to_s, @main), bord
136
+ msg = "Session started on #{Time.now} by #{@main}"
137
+ bord = '#' * s.size
138
+ puts bord, msg, bord
137
139
  end
138
140
 
139
141
  def build(parallel: [], pass: nil, **kwargs)
@@ -165,6 +167,8 @@ module Squared
165
167
  __build__(**kwargs)
166
168
 
167
169
  yield self if block_given?
170
+
171
+ __chain__(**kwargs)
168
172
  @closed = true
169
173
  self
170
174
  end
@@ -197,6 +201,34 @@ module Squared
197
201
  script_command :run, script, group, ref, on, &blk
198
202
  end
199
203
 
204
+ def chain(task, *action, project: nil, step: 0, with: nil, before: nil, after: nil, sync: false,
205
+ group: @group, ref: @ref)
206
+ keys = if project
207
+ action.map! { |val| task_join(project.name, val) }
208
+ nil
209
+ elsif (target = group || ref)
210
+ action.map! { |val| task_name(task_join(val, target)) }
211
+ nil
212
+ else
213
+ action.map! { |val| task_name(val) }
214
+ prefix ? nil : @project.keys
215
+ end
216
+ ns = lambda do |val|
217
+ next if (ret = as_a(val, :to_s, flat: true)).empty?
218
+
219
+ ret.map! do |arg|
220
+ if arg.include?(':') || (keys && !keys.include?(arg))
221
+ task_name(arg)
222
+ else
223
+ /\A#{Regexp.escape(task_name(arg))}:/
224
+ end
225
+ end
226
+ end
227
+ data = Support::ChainData.new(action, step, ns.call(with), ns.call(before), ns.call(after), sync)
228
+ (@chain[task_name(task.to_s)] ||= []) << data
229
+ self
230
+ end
231
+
200
232
  def script(script, group: @group, ref: @ref, on: nil)
201
233
  script_command :script, script, group, ref, on
202
234
  end
@@ -305,7 +337,7 @@ module Squared
305
337
  index = 0
306
338
  while @project[name]
307
339
  index += 1
308
- name = "#{project}-#{index}"
340
+ name = task_name "#{project}-#{index}"
309
341
  end
310
342
  proj = ((if !ref.is_a?(Class)
311
343
  Application.find(ref, path: path)
@@ -506,7 +538,7 @@ module Squared
506
538
  def format_desc(val, opts = nil, arg: 'opts*', before: nil, after: nil, out: false)
507
539
  return unless TASK_METADATA || out
508
540
 
509
- val = val.to_s.split(':') if val.is_a?(String)
541
+ val = val.split(':') if val.is_a?(String)
510
542
  if before || after || opts
511
543
  pos = []
512
544
  pos << (before.is_a?(Array) ? before.join(',') : before) if before
@@ -602,6 +634,7 @@ module Squared
602
634
  end
603
635
 
604
636
  def powershell?
637
+ return true if ENV['SHELL']&.end_with?(File.join('', 'pwsh'))
605
638
  return false unless windows?
606
639
 
607
640
  case ENV['TERM_PROGRAM']
@@ -650,13 +683,129 @@ module Squared
650
683
  task Rake.application.default_task_name => out
651
684
  end
652
685
 
686
+ def __chain__(*)
687
+ @chain.each do |key, group|
688
+ level = []
689
+ sync = []
690
+ failed = []
691
+ i = 0
692
+ pass = nil
693
+ until (i > 0 && !group.compact! && !pass) || group.empty?
694
+ group.each_with_index do |data, j|
695
+ if i == 0
696
+ action, reject = data.action.partition { |val| task_defined?(val) }
697
+ failed.concat(reject)
698
+ next group[j] = nil if action.empty?
699
+
700
+ step = data.step
701
+ data.action = action
702
+ else
703
+ step = 0
704
+ catch :found do
705
+ has = ->(c, d) { c.any? { |e| e.is_a?(Regexp) ? d.match?(e) : d == e } }
706
+ w = data.with
707
+ a = data.after
708
+ b = data.before
709
+ level.each_with_index do |tasks, k|
710
+ with = lambda do |n|
711
+ tasks.insert(n, *data.action)
712
+ sync << tasks
713
+ data.action.clear
714
+ end
715
+ tasks&.each_with_index do |v1, l|
716
+ index = k if w && has.call(w, v1)
717
+ if a && has.call(a, v1)
718
+ if index
719
+ with.call(l + 1)
720
+ throw :found
721
+ else
722
+ index = k + 1
723
+ end
724
+ elsif b && has.call(b, v1)
725
+ if index
726
+ with.call(l)
727
+ throw :found
728
+ else
729
+ index = k - 1
730
+ end
731
+ elsif index
732
+ if a || b
733
+ tasks.each_with_index do |v2, m|
734
+ if a && has.call(a, v2)
735
+ with.call(m + 1)
736
+ throw :found
737
+ elsif b && has.call(b, v2)
738
+ with.call(m)
739
+ throw :found
740
+ end
741
+ end
742
+ if !pass
743
+ pass = [i, data]
744
+ elsif pass.include?(data)
745
+ if i == pass.first + 1
746
+ pass.delete(data)
747
+ pass = nil if pass.size == 1
748
+ end
749
+ else
750
+ pass << data
751
+ end
752
+ next
753
+ end
754
+ else
755
+ next
756
+ end
757
+ step = index == -1 ? -1 : index + 1
758
+ throw :found
759
+ end
760
+ end
761
+ end
762
+ end
763
+ if step == -1
764
+ level.unshift(data.action)
765
+ step = 0
766
+ elsif step > 0
767
+ (level[step -= 1] ||= []).concat(data.action)
768
+ elsif !data.action.empty?
769
+ next
770
+ end
771
+ sync << level[step] if data.sync
772
+ group[j] = nil
773
+ pass = nil
774
+ end
775
+ i += 1
776
+ end
777
+ level.compact!
778
+ sync.uniq!
779
+ series.chain(key, level, sync: sync)
780
+ next if task_defined?(key = task_join(key, 'print'))
781
+
782
+ format_desc key
783
+ task key do
784
+ unless failed.empty? && group.empty?
785
+ puts log_message(Logger::ERROR, *(failed + group.map { |val| val.action }.flatten),
786
+ subject: 'failed placement', hint: false, pass: true)
787
+ end
788
+ cols = level.flatten(1).map(&:size).max
789
+ level.each_with_index do |grp, n|
790
+ title = "Step #{n.succ}#{if !sync.include?(grp) || (grp.size == 1 && series.parallel.include?(grp.first))
791
+ ''
792
+ else
793
+ ' (sync)'
794
+ end}"
795
+ emphasize(grp, title: title, cols: [cols, title.size].max, border: theme[:border],
796
+ sub: [pat: /\A(Step \d+)(.*)\z/, styles: theme[:header]])
797
+ end
798
+ end
799
+ end
800
+ end
801
+
653
802
  def puts(*args)
654
803
  puts_oe(*args, pipe: pipe)
655
804
  end
656
805
 
657
806
  def script_command(task, val, group, ref, on, &blk)
658
807
  if block_given?
659
- val = Struct.new(:run, :block).new(val, blk)
808
+ val = Support::RunData.new(val, blk)
660
809
  elsif !val
661
810
  return self
662
811
  end