squared 0.4.2 → 0.4.4

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: 41bec81d0daef581416b2c3c7a6cefc66da439794df530c5b2e9c65f7feba6ae
4
- data.tar.gz: 8b74836f62055cc6d173148753420ac2bb621e951f1d339cae60ed35357c6f5f
3
+ metadata.gz: 76e13effa08fdb2534843e9fddf53139dbed8462375670966f6cc56ad89338ed
4
+ data.tar.gz: 0b012aabda216eeb70a76cca881512d34dd9be166ef39f702b0f4b71ee31d7a0
5
5
  SHA512:
6
- metadata.gz: f042541b80121e9ef547066105a6148a21c790cf50ef0931097ee1506f75fd0a154323671585ec4c86fce33ac2c4c3d8d52d380550091d88932992fc79491466
7
- data.tar.gz: 3fc67865ab3499416cac65f06652206af9dd762f898c561924b19f7c50c5430f640da0e03249ac1cdc39c0a2de1b0ba4125b8e2813805fa65259e5d285d44f79
6
+ metadata.gz: 043bc6751cbff73bc30408eecbd089691d712c47db5ebe2cca03bea20c6f500a22cc24f8b9286445bf0336efa9003c9367687c489d7bddefa808f9592e9b9e20
7
+ data.tar.gz: 927ce35b03ff2ad729ea4b900400657816c4221e7d0372c2f0e7fec69524e2c4d9059fc1d267498ba153e321017876aebe858d966f9028669010d3e924fd2adb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.4] - 2025-04-08
4
+
5
+ ### Added
6
+
7
+ - Git command restore was implemented.
8
+ - Python command venv was implemented.
9
+ - Python command run can use separate VIRTUAL_ENV per project.
10
+ - Python command depend can manage virtual environment.
11
+ - Python command pip action freeze was implemented.
12
+ - Ruby bundle command cache was implemented.
13
+ - Python command pip action uninstall was implemented.
14
+ - Docker command network was implemented.
15
+ - Ruby program irb command was implemented.
16
+ - Docker containers can have multiple tags.
17
+
18
+ ### Changed
19
+
20
+ - Base command unpack action ext accepts generic extensions.
21
+ - Docker images not matching project aliases are not listed.
22
+ - Python dependtype lists poetry.lock (2) before pyproject.toml (3).
23
+ - Rake command can be run with program options.
24
+
25
+ ### Fixed
26
+
27
+ - Unsafe git commands are prompted before execution.
28
+ - Unrecognized options are appended selectively on context.
29
+ - Hatch root options were not applied correctly.
30
+
31
+ ## [0.3.7] - 2025-04-08
32
+
33
+ - See `0.2.7`.
34
+
35
+ ## [0.2.7] - 2025-04-08
36
+
37
+ ### Fixed
38
+
39
+ - Paths that conflict with git options can be quoted.
40
+ - Bundle command update did not append package names.
41
+ - Git result status type was truncated.
42
+ - Log messages were concatenated without separator.
43
+ - Python pip environment options used undefined session.
44
+ - Regexp "o" modifier was not used properly.
45
+ - Rake did not run individual project Rakefile.
46
+ - Ruby commands did not delimit exec arguments.
47
+
48
+ ## [0.4.3] - 2025-04-01
49
+
50
+ ### Added
51
+
52
+ - Git global command branch shows ahead and behind values.
53
+ - Workspace theme style label warn was created.
54
+
55
+ ### Fixed
56
+
57
+ - Git and Docker did not order common options first.
58
+
59
+ ### Changed
60
+
61
+ - Git commit amend uses flag --force-with-lease.
62
+
3
63
  ## [0.4.2] - 2025-03-28
4
64
 
5
65
  ### Added
@@ -87,6 +147,7 @@
87
147
  - Git base class did not check for null Logger instance.
88
148
  - Banner border width extended past terminal edge.
89
149
  - Task program command options used unrecognized symbol.
150
+ - Python pip environment options used undefined session.
90
151
 
91
152
  ## [0.3.5] - 2025-03-06
92
153
 
@@ -390,9 +451,12 @@
390
451
 
391
452
  - Changelog was created.
392
453
 
454
+ [0.4.4]: https://github.com/anpham6/squared/releases/tag/v0.4.4-ruby
455
+ [0.4.3]: https://github.com/anpham6/squared/releases/tag/v0.4.3-ruby
393
456
  [0.4.2]: https://github.com/anpham6/squared/releases/tag/v0.4.2-ruby
394
457
  [0.4.1]: https://github.com/anpham6/squared/releases/tag/v0.4.1-ruby
395
458
  [0.4.0]: https://github.com/anpham6/squared/releases/tag/v0.4.0-ruby
459
+ [0.3.7]: https://github.com/anpham6/squared/releases/tag/v0.3.7-ruby
396
460
  [0.3.6]: https://github.com/anpham6/squared/releases/tag/v0.3.6-ruby
397
461
  [0.3.5]: https://github.com/anpham6/squared/releases/tag/v0.3.5-ruby
398
462
  [0.3.4]: https://github.com/anpham6/squared/releases/tag/v0.3.4-ruby
@@ -400,6 +464,7 @@
400
464
  [0.3.2]: https://github.com/anpham6/squared/releases/tag/v0.3.2-ruby
401
465
  [0.3.1]: https://github.com/anpham6/squared/releases/tag/v0.3.1-ruby
402
466
  [0.3.0]: https://github.com/anpham6/squared/releases/tag/v0.3.0-ruby
467
+ [0.2.7]: https://github.com/anpham6/squared/releases/tag/v0.2.7-ruby
403
468
  [0.2.6]: https://github.com/anpham6/squared/releases/tag/v0.2.6-ruby
404
469
  [0.2.5]: https://github.com/anpham6/squared/releases/tag/v0.2.5-ruby
405
470
  [0.2.4]: https://github.com/anpham6/squared/releases/tag/v0.2.4-ruby
data/README.ruby.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # squared 0.4
2
2
 
3
3
  * [source](https://github.com/anpham6/squared)
4
- * [manifest](https://github.com/anpham6/squared-repo)
5
4
  * [docs](https://squared.readthedocs.io)
6
5
 
7
6
  ## Version Compatibility
@@ -24,6 +23,7 @@ gem install squared
24
23
  ### Optional
25
24
 
26
25
  * [Repo](https://source.android.com/docs/setup/reference/repo)
26
+ * https://github.com/anpham6/squared-repo
27
27
  * Python 3.6
28
28
  * Not compatible with Windows
29
29
 
@@ -155,13 +155,13 @@ Workspace::Application
155
155
 
156
156
  **NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
157
157
 
158
- ## Archive
158
+ ### Archive
159
159
 
160
160
  ```ruby
161
- # HEADERS={"Authorization":"Bearer RANDOM-TOKEN"} (hash/json)
162
- # ZIP_DEPTH=0 | default=1
163
- # TAR_DEPTH=0 | TAR_DEPTH_SQUARED
164
- # UNPACK_FORCE=1 | Remove target directory
161
+ # HEADERS={"Authorization":"Bearer RANDOM-TOKEN"} | hash/json
162
+ # ZIP_DEPTH=0 | default=1
163
+ # TAR_DEPTH=0 | TAR_DEPTH_SQUARED
164
+ # UNPACK_FORCE=1 | Remove target directory
165
165
 
166
166
  Workspace::Application
167
167
  .new(main: "squared")
@@ -171,7 +171,7 @@ Workspace::Application
171
171
  uri: "https://github.com/anpham6/chrome-docs/archive/refs/tags/v0.5.0.tar.gz", # URI.open (required)
172
172
  digest: "e3d55d2004d4770dd663254c9272dc3baad0d57a5bd14ca767de6546cdf14680", # SHA1 | SHA256 | SHA384 | SHA512 | MD5
173
173
  digest: "rmd160:47b7790a511eed675fec1a3e742845fef058799b", # RMD160
174
- ext: "tar.gz", # zip | tar | tar.gz | tgz | tar.xz | txz
174
+ ext: "tar.gz", # zip | tar | tar.gz | tgz | tar.xz | txz | 7z
175
175
  depth: 1, # nested directories (e.g. --strip-components)
176
176
  headers: { # URI.open
177
177
  "Authorization" => "Bearer RANDOM-TOKEN"
@@ -238,8 +238,10 @@ Workspace::Application
238
238
  .new(main: "squared")
239
239
  .graph(["depend"], ref: :git) # Optional
240
240
  .with(:python) do
241
- add("android-docs", "android")
242
- add("chrome-docs", "chrome", graph: "android")
241
+ add("android-docs", "android", venv: "/home/user/.venv") # rake android-docs:depend
242
+ add("chrome-docs", "chrome", graph: "android", venv: ".venv") do # /workspaces/chrome-docs/.venv
243
+ variable_set :dependindex, 2 # Use Poetry for dependencies (optional)
244
+ end
243
245
  end
244
246
  .with(:node) do
245
247
  graph(["build", "copy"], on: { # Overrides "git"
@@ -409,6 +411,7 @@ Non-task:
409
411
  * active
410
412
  * inline
411
413
  * subject
414
+ * warn
412
415
  * caution
413
416
  * current
414
417
  * extra
@@ -417,6 +420,47 @@ Non-task:
417
420
  * yellow
418
421
  * green
419
422
 
423
+ ## Git
424
+
425
+ Most project classes will inherit from `Git` which enables these tasks:
426
+
427
+ | Task | Git | Command |
428
+ | :--------- | :--------------- | :-------------------------------------------- |
429
+ | branch | branch | create set delete move copy list edit current |
430
+ | checkout | checkout | commit branch track detach path |
431
+ | commit | commit | add all amend amend-orig |
432
+ | diff | diff | head cached branch files between contain |
433
+ | fetch | fetch | origin remote |
434
+ | files | ls-files | cached modified deleted others ignored |
435
+ | git | | clean mv rm |
436
+ | merge | merge | commit no-commit send |
437
+ | pull | pull | origin remote |
438
+ | rebase | rebase | branch onto send |
439
+ | refs | ls-remote --refs | heads tags remote |
440
+ | reset | reset | commit index patch mode |
441
+ | restore | restore | staged worktree |
442
+ | rev | rev | commit branch output parseopt |
443
+ | show | show | format oneline |
444
+ | stash | stash | push pop apply drop list |
445
+ | tag | tag | add delete list |
446
+
447
+ You can disable all of them at once using the `exclude` property.
448
+
449
+ ```ruby
450
+ Workspace::Application
451
+ .new
452
+ .add("squared", exclude: :git)
453
+ ```
454
+
455
+ You can disable one or more of them using the `pass` property as a *string*.
456
+
457
+ ```ruby
458
+ Workspace::Application
459
+ .new
460
+ .add("squared", pass: ["pull"], ref: :node)
461
+ .pass("pull", ref: :node) { read_packagemanager(:private) }
462
+ ```
463
+
420
464
  ## Environment
421
465
 
422
466
  ### Path
@@ -438,7 +482,7 @@ Common::PATH.merge!({
438
482
 
439
483
  ### Build
440
484
 
441
- ```ruby
485
+ ```sh
442
486
  Workspace::Application
443
487
  .new
444
488
  .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
@@ -454,19 +498,23 @@ BUILD_${NAME}_TYPE # debug
454
498
 
455
499
  # :env :script :opts :args
456
500
  # NODE_ENV="production" NO_COLOR="1" npm run build:dev --loglevel=error --workspaces=false -- --quiet
457
- BUILD_${NAME} # build:dev
458
- BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
459
- BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
460
- BUILD_${NAME}_DEV # pattern,0,1 (:dev)
461
- BUILD_${NAME}_PROD # pattern,0,1 (:prod)
462
- ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
463
-
464
- BUILD_${NAME}=0 # skip project
501
+ BUILD_${NAME} # build:dev
502
+ BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
503
+ BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
504
+ BUILD_${NAME}_DEV # pattern,0,1 (:dev)
505
+ BUILD_${NAME}_PROD # pattern,0,1 (:prod)
506
+ ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
507
+
508
+ BUILD_${NAME}=0 # skip project
509
+ BUILD_${NAME}_VERSION=0.1.0 # publish + detection
510
+
511
+ BANNER=0 # hide banner
512
+ BANNER_${NAME}=0 #
465
513
  ```
466
514
 
467
515
  ### Graph
468
516
 
469
- ```ruby
517
+ ```sh
470
518
  GRAPH_${NAME} # depend,build => squared:depend + squared:build
471
519
  GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
472
520
  ```
@@ -475,7 +523,7 @@ GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
475
523
 
476
524
  These global options also can target the project suffix `${NAME}`. (e.g. LOG_FILE_EMC)
477
525
 
478
- ```ruby
526
+ ```sh
479
527
  LOG_FILE # %Y-%m-%d.log
480
528
  # OR
481
529
  LOG_AUTO # year,y,month,m,day,d,1
@@ -485,11 +533,72 @@ LOG_LEVEL # See gem "logger"
485
533
  LOG_COLUMNS # terminal width (default: 80)
486
534
  ```
487
535
 
536
+ ### Git
537
+
538
+ ```sh
539
+ GIT_OPTIONS=q,strategy=ort # all
540
+ GIT_OPTIONS_${NAME}=v,ff-only # project only
541
+ GIT_AUTOSTASH=1 # rebase (all)
542
+ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
543
+ ```
544
+
545
+ | Command | Flag | ENV |
546
+ | :--------- | :---------------- | :-------------------------------------------------------------------- |
547
+ | pull | rebase | AUTOSTASH |
548
+ | pull | remote | REFSPEC=s |
549
+ | pull | * | REBASE=0,1 FORCE RECURSE_SUBMODULES=0,1,s |
550
+ | rebase | branch | HEAD=s |
551
+ | rebase | onto | INTERACTIVE I HEAD=s |
552
+ | fetch | -remote | ALL |
553
+ | fetch | remote | REFSPEC=s |
554
+ | fetch | * | FORCE RECURSE_SUBMODULES=0,1,s |
555
+ | clone | * | DEPTH=n ORIGIN=s BRANCH=s LOCAL=0,1 |
556
+ | stash | push | PATHSPEC=s |
557
+ | stash | global | ALL=0,1 KEEP_INDEX=0,1 INCLUDE_UNTRACKED=0,1 STAGED=0,1 MESSAGE=s M=s |
558
+ | status | global | LONG IGNORE_SUBMODULES=s,0-3 PATHSPEC=s |
559
+ | revbuild | global | UNTRACKED_FILES=s IGNORE_SUBMODULES=s IGNORED=s (status) |
560
+ | reset | mode (mixed) | N REFRESH=0 |
561
+ | reset | index | PATHSPEC=s |
562
+ | reset | -commit | HEAD=s |
563
+ | checkout | branch | DETACH TRACK=s |
564
+ | checkout | global path | HEAD=s PATHSPEC=s |
565
+ | checkout | * | FORCE MERGE |
566
+ | tag | add | SIGN HEAD=s |
567
+ | log | * | PATHSPEC=s |
568
+ | diff | -between -contain | MERGE_BASE |
569
+ | diff | head branch | INDEX=n |
570
+ | diff | * | PATHSPEC=s |
571
+ | commit | * | MESSAGE=s M=s REPOSITORY=s DRY_RUN EDIT=0 |
572
+ | branch | create | TRACK=0,1,s FORCE |
573
+ | branch | move copy | FORCE |
574
+ | branch | global | SYNC |
575
+ | restore | * | PATHSPEC=s |
576
+ | show | -online | ABBREV=n |
577
+ | rev | commit branch | HEAD=s |
578
+ | git | rm | PATHSPEC=s |
579
+
580
+ ### Docker
581
+
582
+ ```sh
583
+ DOCKER_OPTIONS=q,no-cache # all
584
+ DOCKER_OPTIONS_${NAME}=v,no-cache=false # project only (override)
585
+ DOCKER_TAG=latest # all
586
+ DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
587
+ ```
588
+
589
+ | Command | Flag | ENV |
590
+ | :--------- | :---------------- | :---------------------------------------------- |
591
+ | buildx | build | TAG=s |
592
+ | buildx | bake | SERVICE=s |
593
+ | compose | build | TARGET=s |
594
+ | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
595
+ | image | push | TAG=s REGISTRY=s |
596
+
488
597
  ### Repo
489
598
 
490
599
  These global options also can target the application main suffix `${NAME}`. (e.g. REPO_ROOT_SQUARED)
491
600
 
492
- ```ruby
601
+ ```sh
493
602
  REPO_ROOT # parent dir
494
603
  REPO_HOME # project dir (main)
495
604
  REPO_BUILD # run,script
@@ -499,51 +608,12 @@ REPO_DEV # pattern,0,1
499
608
  REPO_PROD # pattern,0,1
500
609
  REPO_WARN # 0,1
501
610
  REPO_SYNC # 0,1
611
+ REPO_URL # manifest repository
502
612
  REPO_MANIFEST # e.g. latest,nightly,prod
503
613
  REPO_DRYRUN # 0,1,2
504
614
  REPO_TIMEOUT # confirm dialog (seconds)
505
615
  ```
506
616
 
507
- ## Git
508
-
509
- Most project classes will inherit from `Git` which enables these tasks:
510
-
511
- | Task | Git | Command |
512
- | :--------- | :--------------- | :-------------------------------------------- |
513
- | branch | branch | create set delete move copy list edit current |
514
- | checkout | checkout | commit branch track detach path |
515
- | commit | commit | add all amend amend-orig |
516
- | diff | diff | head cached branch files between contain |
517
- | fetch | fetch | origin remote |
518
- | files | ls-files | cached modified deleted others ignored |
519
- | git | | clean mv restore rm |
520
- | merge | merge | commit no-commit send |
521
- | pull | pull | origin remote |
522
- | rebase | rebase | branch onto send |
523
- | refs | ls-remote --refs | heads tags remote |
524
- | reset | reset | commit index patch mode |
525
- | rev | rev | commit branch output parseopt |
526
- | show | show | format oneline |
527
- | stash | stash | push pop apply drop list |
528
- | tag | tag | add delete list |
529
-
530
- You can disable all of them at once using the `exclude` property.
531
-
532
- ```ruby
533
- Workspace::Application
534
- .new
535
- .add("squared", exclude: :git)
536
- ```
537
-
538
- You can disable one or more of them using the `pass` property as a *string*.
539
-
540
- ```ruby
541
- Workspace::Application
542
- .new
543
- .add("squared", pass: ["pull"], ref: :node)
544
- .pass("pull", ref: :node) { read_packagemanager(:private) }
545
- ```
546
-
547
617
  ## LICENSE
548
618
 
549
619
  BSD 3-Clause
@@ -45,7 +45,8 @@ module Squared
45
45
  active: [:bold],
46
46
  inline: [:bold],
47
47
  subject: [:bold],
48
- caution: [:red],
48
+ warn: %i[white red!],
49
+ caution: %i[black yellow!],
49
50
  current: nil,
50
51
  extra: nil,
51
52
  major: [:bold]
@@ -185,12 +185,8 @@ module Squared
185
185
  emphasize(args, title: title + (subject ? " #{subject}" : ''), sub: sub)
186
186
  else
187
187
  msg = [log_title(level, color: color)]
188
- if subject
189
- msg << (color ? sub_style(subject, styles: (@theme && @theme[:subject]) || :bold) : subject)
190
- else
191
- msg += args
192
- args.clear
193
- end
188
+ msg << (color ? sub_style(subject, styles: (@theme && @theme[:subject]) || :bold) : subject) if subject
189
+ msg << args.shift if msg.size == 1
194
190
  message(msg.join(' '), *args, hint: hint)
195
191
  end
196
192
  end
@@ -35,7 +35,8 @@ module Squared
35
35
 
36
36
  def shell_quote(val, option: true, force: true, double: false, override: false)
37
37
  val = val.to_s
38
- return val if (!force && !val.include?(' ')) || (option && val.match?(/(?:^|\S=|[^=]\s+)(["']).+\1\z/m))
38
+ return val if !force && !val.include?(' ')
39
+ return val if option && val.match?(/(?:^|\S=|[^=]\s+|#{Rake::Win32.windows? ? '[\\\/]' : '\/'})(["']).+\1\z/m)
39
40
 
40
41
  if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
41
42
  "\"#{val.gsub(/(?<!\\)"/, '\\"')}\""
@@ -9,7 +9,7 @@ module Squared
9
9
  module_function
10
10
 
11
11
  def split_escape(val, char: ',')
12
- val.split(/\s*(?<!\\)#{char}\s*/o)
12
+ val.split(/\s*(?<!\\)#{char}\s*/)
13
13
  end
14
14
 
15
15
  def split_option(val)
@@ -105,12 +105,12 @@ module Squared
105
105
  ret = env_value(key, suffix: suffix, strict: strict)
106
106
  return ret == equals.to_s unless equals.nil?
107
107
 
108
- ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
108
+ ret.empty? || (ignore && as_a(ignore).any? { |val| val.to_s == ret }) ? default : ret
109
109
  end
110
110
 
111
111
  def env_value(key, default = '', suffix: nil, strict: false)
112
112
  if suffix
113
- if (ret = ENV["#{key}#{@envname ? "_#{@envname}" : ''}_#{suffix}"])
113
+ if (ret = ENV["#{key + (@envname ? "_#{@envname}" : '')}_#{suffix}"])
114
114
  return ret
115
115
  elsif strict
116
116
  return default
@@ -124,7 +124,10 @@ module Squared
124
124
  end
125
125
 
126
126
  def env_bool(key, default = false, suffix: nil, strict: false, index: false)
127
- if key.is_a?(::String)
127
+ case key
128
+ when nil
129
+ default
130
+ when ::String
128
131
  case (val = env_value(key, suffix: suffix, strict: strict))
129
132
  when ''
130
133
  default
@@ -134,7 +137,7 @@ module Squared
134
137
  index && val.to_i > 0 ? val.to_i : true
135
138
  end
136
139
  else
137
- key.nil? ? default : key
140
+ key
138
141
  end
139
142
  end
140
143
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.4.2'
4
+ VERSION = '0.4.4'
5
5
  end
@@ -404,7 +404,7 @@ module Squared
404
404
  end
405
405
 
406
406
  def task_localname(val)
407
- prefix && val.is_a?(String) ? val.sub(/\A#{Regexp.escape(prefix)}:/o, '') : val.to_s
407
+ prefix && val.is_a?(String) ? val.sub(/\A#{Regexp.escape(prefix)}:/, '') : val.to_s
408
408
  end
409
409
 
410
410
  def task_desc(*args, **kwargs)
@@ -590,6 +590,15 @@ module Squared
590
590
  File.exist?('/.dockerenv')
591
591
  end
592
592
 
593
+ def powershell?
594
+ case ENV['TERM_PROGRAM']
595
+ when 'powershell.exe', 'vscode'
596
+ true
597
+ else
598
+ !ENV['PSModulePath'].nil? || !ENV['POWERSHELL_DISTRIBUTION_CHANNEL'].nil?
599
+ end
600
+ end
601
+
593
602
  def rootpath(*args)
594
603
  root.join(*args)
595
604
  end