squared 0.4.19 → 0.5.0

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.
data/README.ruby.md ADDED
@@ -0,0 +1,722 @@
1
+ # squared 0.5
2
+
3
+ * [source](https://github.com/anpham6/squared)
4
+ * [docs](https://squared.readthedocs.io)
5
+
6
+ ## Version Compatibility
7
+
8
+ | Date | squared | Min | Max |
9
+ | :--------: | ------: | -----: | -----: |
10
+ | 2024-12-07 | 0.1.0 | 2.4.0 | 3.3.6 |
11
+ | 2025-01-07 | 0.2.0 | | 3.4.0 |
12
+ | 2025-02-07 | 0.3.0 | | 3.4.1 |
13
+ | 2025-03-06 | 0.4.0 | | 3.4.2 |
14
+ | 2025-06-16 | 0.5.0 | 2.5.0 | 3.4.3 |
15
+
16
+ The range chart indicates the latest Ruby tested against at the time of release.
17
+
18
+ ## Installation
19
+
20
+ ```sh
21
+ gem install squared
22
+ ```
23
+
24
+ ### Optional
25
+
26
+ * [Repo](https://source.android.com/docs/setup/reference/repo)
27
+ * https://github.com/anpham6/squared-repo
28
+ * Python 3.6
29
+ * Not compatible with Windows
30
+
31
+ ```sh
32
+ mkdir -p ~/.bin
33
+ PATH="${HOME}/.bin:${PATH}"
34
+ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
35
+ chmod a+rx ~/.bin/repo
36
+ ```
37
+
38
+ ## Example - Rakefile
39
+
40
+ Projects from any accessible folder can be added relative to the parent directory (e.g. *REPO_ROOT*) or absolutely. The same Rakefile can also manage other similarly cloned `Repo` repositories remotely by setting the `REPO_ROOT` environment variable to the location. Missing projects will simply be excluded from the task runner.
41
+
42
+ ```ruby
43
+ require "squared"
44
+
45
+ require "squared/workspace"
46
+ require "squared/workspace/repo" # Optional
47
+ require "squared/workspace/project/node" #
48
+ require "squared/workspace/project/python" #
49
+ require "squared/workspace/project/ruby" #
50
+ require "squared/workspace/project/docker" #
51
+ # OR
52
+ require "squared/app" # All workspace related modules
53
+
54
+ # NODE_ENV = production
55
+
56
+ # REPO_ROOT = /workspaces |
57
+ # REPO_HOME = /workspaces/squared | Dir.pwd
58
+ # rake = /workspaces/squared/Rakefile | main?
59
+ # OR
60
+ # REPO_ROOT = /workspaces | Dir.pwd
61
+ # rake = /workspaces/Rakefile |
62
+ # REPO_HOME = /workspaces/squared | main: "squared"
63
+
64
+ # pathname = /workspaces/pathname
65
+ # optparse = /workspaces/optparse
66
+ # log = /workspaces/logger
67
+ # emc = /workspaces/e-mc
68
+ # pir = /workspaces/pi-r
69
+ # squared = /workspaces/squared
70
+ # cli = /workspaces/squared/publish/sqd-cli
71
+ # sqd-serve = /workspaces/squared/publish/sqd-serve
72
+ # sqd = /workspaces/squared/sqd
73
+
74
+ Workspace::Application
75
+ .new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
76
+ .banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
77
+ .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
78
+ .run("rake install", ref: :ruby)
79
+ .depend(false, group: "default")
80
+ .clean("rake clean", group: "default")
81
+ .clean(["build/"], group: "app")
82
+ .log({ file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app")
83
+ .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", env: { # Ruby (with C extensions)
84
+ "CFLAGS" => "-fPIC -O1"
85
+ })
86
+ .add("optparse", doc: "rake rdoc", group: "default") # Uses bundler/gem_tasks (without C extensions)
87
+ .add("logger", copy: { from: "lib", glob: "**/*.rb", into: "~/.rvm/gems/ruby-3.4.0/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true | "rvm" | "rbenv" | "asdf" | "bundler"
88
+ .add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
89
+ .add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
90
+ .add("squared", script: ["build:stage1", "build:stage2"], group: "app") do # Copy target (main)
91
+ # Repo (global)
92
+ as(:run, "build:dev", "dev") # npm run build:dev -> npm run dev
93
+ as(:run, { "build:dev": "dev", "build:prod": "prod" })
94
+
95
+ add("publish/sqd-cli", "cli", exclude: [:git]) # rake cli:build
96
+ add("publish/sqd-serve") # rake sqd-serve:build
97
+ add("publish/sqd-admin", group: "sqd", exclude: [:base])
98
+ # OR
99
+ with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
100
+ # OR
101
+ add(["publish/sqd-cli", "publish/sqd-serve"], true, exclude: [:base]) # rake squared:sqd-serve:build
102
+
103
+ # Git
104
+ revbuild(include: %w[src/ framework/ types/]) # Synchronous is recommended
105
+ end
106
+ .add("squared/sqd", exclude: :git, pass: [:node, "checkout", "bump"]) do # Skip initialize(:node) + squared:checkout:* + squared:bump:*
107
+ variable_set :script, "build:sqd" # Override detection
108
+ variable_set :depend, false
109
+ variable_set :clean, ["build/sqd/"]
110
+ end
111
+ .with(:docker, pass: ["unpack"]) do
112
+ .add("squared", "docker", file: "Dockerfile", context: ".", tag: "latest", registry: "localhost:5000", username: "squared",
113
+ args: "--ssh=default",
114
+ secrets: ["id=github,env=GITHUB_TOKEN"],
115
+ mounts: ["src=.,dst=/project,ro,bind-propagation=rshared"]) do # Docker
116
+ series(:clean) do # run | depend | doc | lint | test | copy | clean
117
+ File.read(basepath("docker-bake.hcl"))
118
+ .scan(/\btags\s+=\s+\["([^"]+)"\]/)
119
+ .each { |val| image(:rm, tag: val.first) }
120
+ end
121
+ end
122
+ end
123
+ .pass("pull", group: "default") { test? || doc? } # rake pathname:pull | rake optparse:pull
124
+ .style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
125
+ .build(default: "build", parallel: ["pull", "fetch", "rebase", "archive", "clean", /^outdated:/], pass: ["publish"]) do |workspace|
126
+ workspace
127
+ .enable_aixterm
128
+ .style({
129
+ banner: ["bright_cyan", "bold", "bright_black!"],
130
+ border: "bright_white"
131
+ })
132
+ end
133
+
134
+ # default = /workspaces/ruby/*
135
+ # pathname = /workspaces/ruby/pathname
136
+ # optparse = /workspaces/ruby/optparse
137
+ # logger = /workspaces/ruby/logger
138
+ # android = /workspaces/android-docs
139
+ # chrome = /workspaces/chrome-docs
140
+
141
+ Workspace::Application
142
+ .new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
143
+ .group("ruby", "default", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
144
+ pathname: {
145
+ run: "rake compile" # rake rb:pathname:build
146
+ }
147
+ })
148
+ .with(:python, editable: false) do # ref=Symbol | group=String
149
+ banner([:name, ": ", :version], "path") # chrome-docs: 0.1.0 | /workspaces/chrome-docs
150
+ doc("make html") # rake rb:doc:python
151
+ run(false) # rake rb:build:python (disabled)
152
+ exclude(%i[base git]) # Project::Git.ref (superclass)
153
+ add("android-docs", "android") # rake rb:android:doc
154
+ add("chrome-docs", "chrome") # rake rb:chrome:doc
155
+ end
156
+ .style("inline", "bold")
157
+ .build
158
+ ```
159
+
160
+ **NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
161
+
162
+ ### Archive
163
+
164
+ ```ruby
165
+ # HEADERS={"Authorization":"Bearer RANDOM-TOKEN"} | hash/json
166
+ # ZIP_DEPTH=0 | default=1
167
+ # TAR_DEPTH=0 | TAR_DEPTH_SQUARED
168
+ # UNPACK_FORCE=1 | Remove target directory
169
+
170
+ Workspace::Application
171
+ .new(main: "squared")
172
+ .with(:python) do
173
+ add("android-docs", "android", archive: "https://github.com/anpham6/android-docs/archive/refs/tags/v0.3.0.zip")
174
+ add("chrome-docs", "chrome", archive: {
175
+ uri: "https://github.com/anpham6/chrome-docs/archive/refs/tags/v0.5.0.tar.gz", # URI.open (required)
176
+ digest: "e3d55d2004d4770dd663254c9272dc3baad0d57a5bd14ca767de6546cdf14680", # SHA1 | SHA256 | SHA384 | SHA512 | MD5
177
+ digest: "rmd160:47b7790a511eed675fec1a3e742845fef058799b", # RMD160
178
+ ext: "tar.gz", # zip | tar | tar.gz | tgz | tar.xz | txz | 7z
179
+ depth: 1, # nested directories (e.g. --strip-components)
180
+ headers: { # URI.open
181
+ "Authorization" => "Bearer RANDOM-TOKEN"
182
+ }
183
+ })
184
+ end
185
+ .add("squared", release: "https://github.com/anpham6/squared/archive/refs/tags/??") # squared:unpack:zip[v5.4.0,/tmp/squared]
186
+ end
187
+ ```
188
+
189
+ ### Clone
190
+
191
+ The task is only active when the project directory is empty or does not exist.
192
+
193
+ ```ruby
194
+ Workspace::Application
195
+ .new(main: "squared")
196
+ .git(
197
+ "emc": "https://github.com/anpham6/e-mc", # rake emc:clone
198
+ "pir": { # rake pir:clone
199
+ uri: "https://github.com/anpham6/pi-r", #
200
+ options: { #
201
+ "origin": "github", # --origin=github
202
+ "recurse-submodules": false, # --no-recurse-submodules
203
+ "shallow-exclude": ["v0.0.1", "v0.0.2"] # --shallow-exclude=v0.0.1 --shallow-exclude=v0.0.2
204
+ }
205
+ }
206
+ )
207
+ .git("squared", "/path/to/squared", options: { local: true }) # Relative paths resolve from workspace root
208
+ .git(
209
+ {
210
+ emc: { uri: "e-mc", options: { "depth": 2 } }, # https://github.com/anpham6/e-mc
211
+ pir: "pi-r" # Maps task alias to repository folder
212
+ },
213
+ base: "https://github.com/anpham6", # Required
214
+ repo: ["squared", "android-docs", "chrome-docs"], # https://github.com/anpham6/squared
215
+ options: { # Only "repo"
216
+ "depth": 1,
217
+ "quiet": true
218
+ }
219
+ )
220
+ .with(:node) do # rake clone:node
221
+ add("e-mc", "emc") # https://github.com/anpham6/e-mc
222
+ add("pi-r", "pir") # https://github.com/anpham6/pi-r
223
+ add("squared") # https://github.com/anpham6/squared
224
+ end
225
+ .with(:python) do # rake clone:python
226
+ add("android-docs")
227
+ add("chrome-docs") do
228
+ revbuild(include: "source/", exclude: ["source/conf.py"]) # Limit files being watched
229
+ end
230
+ end
231
+ .git("https://github.com/anpham6", ["emc", "pir"]) # Targets any defined project
232
+ .git("https://github.com/anpham6", cache: true) # Uses already defined root projects + revbuild
233
+ .revbuild # Enables task revbuild (squared.revb)
234
+ .revbuild(file: "../build.json") # $ROOT/build.json
235
+ .build(parallel: ["clone"]) # rake clone + rake clone:sync
236
+ ```
237
+
238
+ ### Build
239
+
240
+ #### Chain
241
+
242
+ There has to be at least one project which uses the ``step`` attribute. Other placement attributes are ignored.
243
+
244
+ **NOTE**: Projects can only reference non-global namespaced tasks. (e.g. with ":")
245
+
246
+ ```ruby
247
+ Workspace::Application
248
+ .new
249
+ .with(:node) do
250
+ add("e-mc", "emc") do
251
+ chain "all", "clean", step: 1 # Required
252
+ chain "all", "build", step: 2
253
+ end
254
+ add("pi-r", "pir") do
255
+ chain "all", "build", after: "emc:build" # step: 3
256
+ end
257
+ add("pi-r2", "pir2") do
258
+ chain "all", "build", before: "squared" # step: 3
259
+ end
260
+ add("squared-express", "express") do
261
+ chain "all", "clean", with: "emc" # step: 1
262
+ chain "all", "build", with: "pir" # step: 3
263
+ end
264
+ add("squared") do
265
+ revbuild(include: %w[src/ framework/ types/]) # Git revision build command (optional)
266
+ chain "all", "revbuild", after: "express:build" # step: 4
267
+ chain "publish", "bump:patch", "publish:latest", step: 0, sync: true # rake publish
268
+ end
269
+ end
270
+ .with(:python) do
271
+ doc("make html")
272
+ add("android-docs") do
273
+ chain "all", "doc", with: "squared", after: "squared" # step: 4
274
+ end
275
+ add("chrome-docs") do
276
+ chain "all", "doc", with: "squared", before: "squared:revbuild" # Same
277
+ end
278
+ end
279
+ .chain("all", "status", with: "squared", after: "android-docs") # Global tasks (e.g. without ":")
280
+ .build
281
+ ```
282
+
283
+ ```sh
284
+ rake all # all[1-3-4]
285
+ rake all:print
286
+ ```
287
+
288
+ Threaded is the default when there are two or more tasks. Using ``with`` and either **before** or **after** will create a synchronous group.
289
+
290
+ * Step 1: emc:clean + express:clean (thread)
291
+ * Step 2: emc:build (sync)
292
+ * Step 3: pir:build + express:build + pir2:build (thread)
293
+ * Step 4: chrome-docs:doc + squared:revbuild + android-docs:doc + status (sync)
294
+
295
+ #### Graph
296
+
297
+ ```ruby
298
+ Workspace::Application
299
+ .new(main: "squared")
300
+ .graph(["depend"], ref: :git) # Optional
301
+ .with(:python) do
302
+ doc(windows? ? ".\make.bat html" : "make html") # rake android-docs:doc | rake doc:python
303
+ add("android-docs", "android", venv: "/home/user/.venv") # rake android-docs:depend
304
+ add("chrome-docs", "chrome", graph: "android", venv: %w[.venv --clear]) do # /workspaces/chrome-docs/.venv
305
+ variable_set :dependindex, 1 # Use Poetry for dependencies (optional)
306
+ end
307
+ end
308
+ .with(:node) do
309
+ graph(["build", "copy"], on: { # Overrides "git"
310
+ first: proc { puts "1" },
311
+ last: proc { puts "2" }
312
+ })
313
+ script("build:dev") # npm run build:dev
314
+ # OR
315
+ run([nil, "build:dev", { "PATH" => "~/.bin" }, "--workspace", "--silent"]) # PATH="~/.bin" npm run build:dev --workspace -- --silent
316
+ # OR
317
+ run({ # Same
318
+ script: "build:dev", #
319
+ env: { "PATH" => "~/.bin" }, #
320
+ opts: "--workspace", #
321
+ args: "--silent" #
322
+ })
323
+
324
+ add("e-mc", "emc") do
325
+ first("build", "emc:clean", "emc:depend") # rake emc:clean && rake emc:depend && rake emc:build && echo "123"
326
+ last("build", out: "123") { |out: nil| puts out } #
327
+ error("build") { |err: nil| log.debug err } #
328
+ end
329
+ add("pi-r", "pir", graph: "emc", first: {
330
+ build: proc { puts self.name } # puts "pir"
331
+ })
332
+ add("squared-express", "express", graph: "pir")
333
+ add("squared", graph: ["chrome", "express"]) do
334
+ first("git:ls-files") { puts "1" } # skipped
335
+ first("git:ls-files", override: true) { puts "2" } # puts "2"
336
+ last("git:ls-files") { puts workspace.root } # puts "/workspaces"
337
+ end
338
+ end
339
+ .with(:ruby) do
340
+ run("gem build") # gem build
341
+ # OR
342
+ run("gem build", on: { first: -> { p "2" }, last: -> { p "4" } }) do # run | depend | graph | clean | doc | lint | test
343
+ p "1"
344
+ end
345
+ # OR
346
+ run(on: { first: -> { p "pass" }, last: -> { p "pass" } }) do
347
+ p "1"
348
+ end
349
+ # OR
350
+ run(["gem build", "--force", { "RUBY_VERSION" => "3.4.0" }]) # RUBY_VERSION="3.4.0" gem build --force
351
+ # OR
352
+ run({ #
353
+ command: "gem build", # RUBY_VERSION="3.4.0" gem build --silent --force
354
+ opts: "--force", # composable
355
+ env: { "PATH" => "~/.bin" }, #
356
+ args: { silent: true } #
357
+ })
358
+ # OR
359
+ run(["gem pristine", ["gem build", "gem cleanup"], nil, "--debug"]) # gem pristine --debug && gem build --debug && gem cleanup --debug
360
+ #
361
+ # All commands are either Array or Hash
362
+ #
363
+ run([ # PATH="~/.bin" GEM_HOME="~/.gems/ruby-3.4.0" (merged)
364
+ ["gem pristine", "--all", { "PATH" => "~/.bin" }, "--silent"], # gem pristine --silent --all
365
+ ["gem build", { strict: true }, { "GEM_HOME" => "~/.gems/ruby-3.4.0" }] # gem build --strict
366
+ ]) #
367
+ # OR
368
+ run([ # Same
369
+ { #
370
+ env: { "PATH" => "~/.bin" }, #
371
+ command: "gem pristine", #
372
+ opts: "--all", args: "--silent" #
373
+ }, #
374
+ { #
375
+ env: { "GEM_HOME" => "~/.gems/ruby-3.4.0" }, #
376
+ command: "gem build", #
377
+ opts: { strict: true } #
378
+ } #
379
+ ])
380
+
381
+ add("pathname", test: ["rake test", { jobs: ENV["RAKE_JOBS"] }]) # rake test --jobs 4
382
+ add("fileutils", graph: "pathname")
383
+ add("optparse", run: "gem build", env: { "PATH" => "~/.bin" }, opts: "-v") # PATH="~/.bin" gem build -v
384
+ add("rake", graph: ["fileutils", "optparse"])
385
+ banner(command: false) # Always hide banner
386
+ end
387
+ .build
388
+ ```
389
+
390
+ ```sh
391
+ rake pir:graph # emc + pir
392
+ rake express:graph # emc + pir + express
393
+ rake chrome:graph # android + chrome
394
+ rake graph:python # same
395
+ rake squared:graph # android + chrome + emc + pir + express + squared
396
+ rake graph:node # same
397
+ rake rake:graph # pathname + fileutils + optparse + rake
398
+ rake graph:ruby # same
399
+ rake graph # graph:node + graph:ruby
400
+
401
+ rake squared:graph:run[express,pir] # emc + pir + express + squared
402
+ rake squared:graph:run[node,-emc] # pir + express + squared
403
+ ```
404
+
405
+ ### Tasks
406
+
407
+ ```ruby
408
+ Workspace::Series.batch(:ruby, :node, {
409
+ stage: %i[graph test],
410
+ reset: %i[stash pull]
411
+ })
412
+
413
+ Workspace::Series.rename("depend", "install")
414
+ ```
415
+
416
+ ## Usage
417
+
418
+ ```sh
419
+ rake -T # List tasks
420
+ rake # rake status (usually "build")
421
+
422
+ # GIT_OPTIONS=rebase
423
+ rake pull # All except "default" + "app"
424
+ rake pull:ruby # pathname + optparse + logger
425
+ rake pull:default # pathname + optparse
426
+ rake pull:app # squared
427
+ rake pull:node # emc + pir + squared
428
+
429
+ rake build # All except "android"
430
+ rake doc # optparse + android
431
+ rake depend # All except "default"
432
+
433
+ rake build:ruby # rake compile + rake install + rake install
434
+
435
+ rake clean # All except "default" + "app"
436
+ rake clean:ruby # rake clean + rake clean + ["tmp/"]
437
+ rake clean:default # rake clean + rake clean + skip
438
+ rake clean:app # none + skip + ["build/"]
439
+ rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
440
+
441
+ rake squared:run[#] # List scripts (node)
442
+ rake squared:rake[#] # List tasks (ruby)
443
+ ```
444
+
445
+ ```sh
446
+ rake build:app # squared + cli + sqd-serve
447
+ rake squared:build:workspace # cli + sqd-serve
448
+ rake pull:sqd # sqd-admin
449
+ rake squared:pull:workspace # sqd-serve + sqd-admin
450
+ rake squared:outdated:workspace # cli + sqd-serve + sqd-admin
451
+ ```
452
+
453
+ ## Methods
454
+
455
+ Task:
456
+
457
+ * run
458
+ * script
459
+ * depend
460
+ * archive
461
+ * graph
462
+ * doc
463
+ * lint
464
+ * test
465
+ * clean
466
+
467
+ Non-task:
468
+
469
+ * log
470
+ * exclude
471
+
472
+ ## Styles
473
+
474
+ * banner
475
+ * border
476
+ * header
477
+ * active
478
+ * inline
479
+ * subject
480
+ * border
481
+ * warn
482
+ * caution
483
+ * current
484
+ * latest
485
+ * extra
486
+ * major
487
+ * red
488
+ * yellow
489
+ * green
490
+
491
+ ## Git
492
+
493
+ Most project classes will inherit from `Git` which enables these tasks:
494
+
495
+ | Task | Git | Command |
496
+ | :--------- | :--------------- | :-------------------------------------------- |
497
+ | branch | branch | create set delete move copy list edit current |
498
+ | checkout | checkout | commit branch track detach path |
499
+ | commit | commit | add all amend amend-orig fixup |
500
+ | diff | diff | head cached branch files between contain |
501
+ | fetch | fetch | origin remote |
502
+ | files | ls-files | cached modified deleted others |
503
+ | git | | add clean mv rm revert |
504
+ | merge | merge | commit no-commit send |
505
+ | pull | pull | origin remote |
506
+ | rebase | rebase | branch onto send |
507
+ | refs | ls-remote --refs | heads tags remote |
508
+ | reset | reset | commit index patch mode |
509
+ | restore | restore | staged worktree |
510
+ | rev | rev | commit output |
511
+ | show | show | format oneline |
512
+ | stash | stash | push pop apply drop list |
513
+ | switch | switch | create detach merge |
514
+ | tag | tag | add sign delete list |
515
+
516
+ You can disable all of them at once using the `exclude` property.
517
+
518
+ ```ruby
519
+ Workspace::Application.exclude('autostash', 'rebase')
520
+
521
+ Workspace::Application
522
+ .new
523
+ .add("squared", exclude: :git)
524
+ ```
525
+
526
+ You can disable one or more of them using the `pass` property as a *string*.
527
+
528
+ ```ruby
529
+ Workspace::Application
530
+ .new
531
+ .add("squared", pass: ["pull"], ref: :node)
532
+ .pass("pull", ref: :node) { read_packagemanager(:private) }
533
+ ```
534
+
535
+ ### Commit Hash
536
+
537
+ Commands which use commit hashes are parsed using a ":" prefix as to not be confused for an option.
538
+
539
+ ```sh
540
+ rake squared:log:view[:af012345] # git log af012345
541
+ rake squared:log:view[H1,HEAD^5,all,lib,./H12345] # git log --all @~1 @^5 -- 'lib' 'H12345'
542
+ ```
543
+
544
+ ## Environment
545
+
546
+ ### Path
547
+
548
+ All project binary programs can have their executable path set to a non-global alias.
549
+
550
+ ```ruby
551
+ Common::PATH.update({
552
+ GIT: "/usr/bin/git", # PATH_GIT=/usr/bin/git
553
+ TAR: "/opt/archivers/tar", # PATH_TAR=/opt/archivers/tar
554
+ UNZIP: "/opt/archivers/unzip",
555
+ GEM: "~/.rvm/gems/ruby-3.4.0/bin/gem",
556
+ BUNDLE: "~/.rvm/gems/ruby-3.4.0/bin/bundle",
557
+ RAKE: "~/.rvm/gems/ruby-3.4.0/bin/rake",
558
+ NPM: "/opt/node/v22.0.0/bin/npm",
559
+ PYTHON: "#{ENV["PYTHONPATH"]}/bin/python"
560
+ })
561
+ ```
562
+
563
+ ### Build
564
+
565
+ ```ruby
566
+ Workspace::Application
567
+ .new
568
+ .add("squared", run: "gcc a.c -o a.o", opts: { __debug__: { g: true, O2: true, c: nil }, c: true, j: 4 }) # gcc a.c -o a.o -c -j4
569
+ ```
570
+
571
+ ```sh
572
+ BUILD_TYPE # global
573
+ ${PROG}_COLOR=0 # --no-color (e.g. GIT_COLOR)
574
+
575
+ # :env :run :args :opts :type
576
+ # LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -g -O2
577
+ BUILD_${NAME} # gcc a.c -o a.o
578
+ BUILD_${NAME}_OPTS # -g
579
+ BUILD_${NAME}_ENV # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
580
+ BUILD_${NAME}_TYPE # debug
581
+
582
+ # :env :script :opts :args
583
+ # NODE_ENV="production" NO_COLOR="1" npm run build:dev --loglevel=error --workspaces=false -- --quiet
584
+ BUILD_${NAME} # build:dev
585
+ BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
586
+ BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
587
+ BUILD_${NAME}_DEV # pattern,0,1 (:dev)
588
+ BUILD_${NAME}_PROD # pattern,0,1 (:prod)
589
+ ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
590
+
591
+ BUILD_${NAME}=0 # skip project
592
+ BUILD_${NAME}_VERSION=0.1.0 # publish + detection
593
+
594
+ BANNER=0 # hide banner
595
+ BANNER_${NAME}=0 #
596
+
597
+ REVBUILD_FORCE=1 # Rebuild all targets
598
+ REVBUILD_FORCE_${NAME}=1 # Rebuild project
599
+
600
+ PREREQS_${NAME}=build,copy # Class method name to invoke
601
+ PREREQS_${REF}=depend # e.g. Node
602
+ ```
603
+
604
+ ### Graph
605
+
606
+ ```sh
607
+ GRAPH_${NAME} # depend,build => squared:depend + squared:build
608
+ GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
609
+ ```
610
+
611
+ ### Logger
612
+
613
+ These global options also can target the project suffix `${NAME}`. (e.g. LOG_FILE_EMC)
614
+
615
+ ```sh
616
+ LOG_FILE # %Y-%m-%d.log
617
+ # OR
618
+ LOG_AUTO # year,y,month,m,day,d,1
619
+ # Optional
620
+ LOG_DIR # exist?
621
+ LOG_LEVEL # See gem "logger"
622
+ ```
623
+
624
+ ### Git
625
+
626
+ ```sh
627
+ GIT_OPTIONS=q,strategy=ort # all
628
+ GIT_OPTIONS_${NAME}=v,ff # project only
629
+ GIT_AUTOSTASH=1 # rebase (all)
630
+ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
631
+ ```
632
+
633
+ | Command | Flag | ENV |
634
+ | :--------- | :---------------- | :-------------------------------------------------------------------- |
635
+ | branch | create | TRACK=0,1,s FORCE |
636
+ | branch | move copy | FORCE |
637
+ | branch | set delete | COUNT=n |
638
+ | branch | global | SYNC |
639
+ | checkout | branch | DETACH TRACK=s COUNT=n |
640
+ | checkout | detach | REFLOG=1 |
641
+ | checkout | track | COUNT=n |
642
+ | checkout | global path | HEAD=s PATHSPEC=s |
643
+ | checkout | * | FORCE MERGE |
644
+ | clone | * | DEPTH=n ORIGIN=s BRANCH=s REVISION=s LOCAL=0,1 |
645
+ | commit | * | UPSTREAM=s DRY_RUN EDIT=0 M|MESSAGE=s |
646
+ | diff | -between -contain | MERGE_BASE |
647
+ | diff | head branch | INDEX=n |
648
+ | diff | * | PATHSPEC=s |
649
+ | fetch | -remote | ALL |
650
+ | fetch | remote | REFSPEC=s |
651
+ | fetch | * | FORCE RECURSE_SUBMODULES=0,1,s |
652
+ | git | rm | PATHSPEC=s |
653
+ | log | * | PATHSPEC=s |
654
+ | pull | rebase | AUTOSTASH |
655
+ | pull | remote | REFSPEC=s |
656
+ | pull | -remote | ALL |
657
+ | pull | * | REBASE=0,1 FORCE RECURSE_SUBMODULES=0,1,s |
658
+ | rebase | branch | HEAD=s |
659
+ | rebase | onto | INTERACTIVE I HEAD=s |
660
+ | reset | mode (mixed) | N REFRESH=0 |
661
+ | reset | index | PATHSPEC=s |
662
+ | reset | commit | COUNT=n REFLOG=1 |
663
+ | reset | -commit | HEAD=s |
664
+ | restore | * | PATHSPEC=s |
665
+ | revbuild | global | UNTRACKED_FILES=s IGNORE_SUBMODULES=s IGNORED=s (status) |
666
+ | stash | push | PATHSPEC=s |
667
+ | stash | global | ALL=0,1 KEEP_INDEX=0,1 INCLUDE_UNTRACKED=0,1 STAGED=0,1 M|MESSAGE=s |
668
+ | status | global | BRANCH LONG IGNORE_SUBMODULES=s,0-3 PATHSPEC=s |
669
+ | switch | detach | REFLOG=1 |
670
+ | switch | -detach | HEAD=s |
671
+ | switch | * | FORCE |
672
+ | tag | add | SIGN FORCE HEAD=s M|MESSAGE=s |
673
+ | tag | sign | FORCE HEAD=s M|MESSAGE=s |
674
+ | tag | delete | COUNT=n |
675
+ | rev | commit branch | HEAD=s |
676
+
677
+ ### Docker
678
+
679
+ ```sh
680
+ DOCKER_OPTIONS=q,no-cache # all
681
+ DOCKER_OPTIONS_${NAME}=v,no-cache=false # project only (override)
682
+ DOCKER_TAG=latest # all
683
+ DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
684
+ DOCKER_ALL=1 # list every image/container
685
+ DOCKER_Y=1 # confirm all
686
+
687
+ BUILD_SQUARED_OPTS="NODE_TAG=24 RUBY_VERSION=3.4.0" DOCKER_SQUARED_OPTS="--no-cache --label=v1" rake squared:build
688
+ docker build --no-cache --label=v1 --build-arg='NODE_TAG=24' --build-arg='RUBY_VERSION=3.4.0' .
689
+ ```
690
+
691
+ | Command | Flag | ENV |
692
+ | :--------- | :---------------- | :---------------------------------------------- |
693
+ | buildx | build | TAG=s |
694
+ | buildx | bake | SERVICE=s |
695
+ | compose | build | TARGET=s |
696
+ | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
697
+ | image | rm | Y=0,1 |
698
+ | image | push | TAG=s REGISTRY=s |
699
+
700
+ ### Repo
701
+
702
+ These global options also can target the application main suffix `${NAME}`. (e.g. REPO_ROOT_SQUARED)
703
+
704
+ ```sh
705
+ REPO_ROOT # parent dir
706
+ REPO_HOME # project dir (main)
707
+ REPO_BUILD # run,script
708
+ REPO_GROUP # string
709
+ REPO_REF # e.g. ruby,node
710
+ REPO_DEV # pattern,0,1
711
+ REPO_PROD # pattern,0,1
712
+ REPO_WARN # 0,1
713
+ REPO_SYNC # 0,1
714
+ REPO_URL # manifest repository
715
+ REPO_MANIFEST # e.g. latest,nightly,prod
716
+ REPO_DRYRUN # 0,1,2
717
+ REPO_TIMEOUT # confirm dialog (seconds)
718
+ ```
719
+
720
+ ## LICENSE
721
+
722
+ BSD 3-Clause