squared 0.0.10 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.ruby.md +98 -26
- data/lib/squared/app.rb +10 -0
- data/lib/squared/common/base.rb +12 -16
- data/lib/squared/common/class.rb +26 -1
- data/lib/squared/common/format.rb +79 -24
- data/lib/squared/common/prompt.rb +36 -0
- data/lib/squared/common/shell.rb +4 -3
- data/lib/squared/common/system.rb +15 -47
- data/lib/squared/common/utils.rb +51 -16
- data/lib/squared/common.rb +1 -4
- data/lib/squared/config.rb +48 -43
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +245 -125
- data/lib/squared/workspace/project/base.rb +448 -188
- data/lib/squared/workspace/project/git.rb +97 -113
- data/lib/squared/workspace/project/node.rb +217 -109
- data/lib/squared/workspace/project/python.rb +37 -34
- data/lib/squared/workspace/project/ruby.rb +137 -100
- data/lib/squared/workspace/project.rb +0 -3
- data/lib/squared/workspace/repo.rb +32 -18
- data/lib/squared/workspace/series.rb +82 -53
- data/lib/squared/workspace.rb +6 -5
- data/lib/squared.rb +1 -11
- metadata +4 -3
- data/lib/squared/common/task.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d9e8c772c9f4d0ce5c54dbeb1b9583d2e477f155036faf34cfc547659c34ec4
|
4
|
+
data.tar.gz: 87de412a78c0071eabd4b26713cbf84978a92fca6ea1a6dfe18653ddf22c90ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08475f426b15c41dab20fbead5d7c1ead70f5d80384e0a0772cbe7b101512a4402a1bb1231b2550f7da16aac2796f8411f6a6e3e570ae728d59b03f88fea5960'
|
7
|
+
data.tar.gz: 5c0334640b20dda82641925cd94b41d76be4cd4e603355fef593a5cd69731ac926febab67f3ffa494267291ec9d89efba2f8a9ac4dde978ab2814a0f8a481a67
|
data/README.ruby.md
CHANGED
@@ -34,7 +34,14 @@ Projects from any accessible folder can be added either relative to `REPO_ROOT`
|
|
34
34
|
|
35
35
|
```ruby
|
36
36
|
require "squared"
|
37
|
-
|
37
|
+
|
38
|
+
require "squared/workspace"
|
39
|
+
require "squared/workspace/repo" # Optional
|
40
|
+
require "squared/workspace/project/node" #
|
41
|
+
require "squared/workspace/project/python" #
|
42
|
+
require "squared/workspace/project/ruby" #
|
43
|
+
# OR
|
44
|
+
require "squared/app" # All workspace related modules
|
38
45
|
|
39
46
|
# NODE_ENV = production
|
40
47
|
# REPO_ROOT = /workspaces
|
@@ -52,7 +59,7 @@ require "squared/workspace/repo" # Repo (optional)
|
|
52
59
|
|
53
60
|
Workspace::Application
|
54
61
|
.new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
|
55
|
-
.banner("group", "project", styles:
|
62
|
+
.banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
|
56
63
|
.repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
|
57
64
|
.run("rake install", ref: :ruby)
|
58
65
|
.depend(false, group: "default")
|
@@ -63,13 +70,17 @@ Workspace::Application
|
|
63
70
|
"CFLAGS" => "-fPIC -O1"
|
64
71
|
})
|
65
72
|
.add("optparse", doc: "rake rdoc", group: "default") # Uses bundler/gem_tasks (without C extensions)
|
66
|
-
.add("logger", copy: { from: "lib", glob: "**/*.rb",
|
67
|
-
.add("e-mc", "emc", copy: { from: "publish",
|
68
|
-
.add("pi-r", "pir", copy: { from: "publish",
|
73
|
+
.add("logger", copy: { from: "lib", glob: "**/*.rb", into: "~/.rvm/gems/ruby-3.3.5/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true
|
74
|
+
.add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
|
75
|
+
.add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
|
69
76
|
.add("squared", script: ["build:stage1", "build:stage2"], group: "app") do # Copy target (main)
|
70
|
-
add("publish/sqd-cli", "cli", exclude: [:git])
|
71
|
-
add("publish/sqd-serve")
|
77
|
+
add("publish/sqd-cli", "cli", exclude: [:git]) # rake cli:build
|
78
|
+
add("publish/sqd-serve") # rake sqd-serve:build
|
72
79
|
add("publish/sqd-admin", group: "sqd", exclude: [:base])
|
80
|
+
# OR
|
81
|
+
with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
|
82
|
+
# OR
|
83
|
+
add(["publish/sqd-cli", "publish/sqd-serve", "publish/sqd-admin"], true, exclude: [:base]) # rake squared:sqd-serve:build
|
73
84
|
end
|
74
85
|
.add("squared/sqd", exclude: [:git]) do
|
75
86
|
variable_set :script, "build:sqd" # Override detection
|
@@ -77,7 +88,7 @@ Workspace::Application
|
|
77
88
|
variable_set :clean, ["build/sqd/"]
|
78
89
|
end
|
79
90
|
.style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
|
80
|
-
.build(default: "status", parallel: ["pull", "fetch", "rebase", "copy", "clean"]) do |workspace|
|
91
|
+
.build(default: "status", parallel: ["pull", "fetch", "rebase", "copy", "clean", "outdated:ruby"]) do |workspace|
|
81
92
|
workspace
|
82
93
|
.enable_aixterm
|
83
94
|
.style({
|
@@ -97,28 +108,80 @@ Workspace::Application
|
|
97
108
|
.new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
|
98
109
|
.group("ruby", "default", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
|
99
110
|
pathname: {
|
100
|
-
run: "rake compile"
|
111
|
+
run: "rake compile" # rake rb:pathname:build
|
101
112
|
}
|
102
113
|
})
|
103
|
-
.with(:python) do
|
104
|
-
banner(
|
105
|
-
doc("make html")
|
106
|
-
run(false)
|
107
|
-
exclude(%i[base git])
|
108
|
-
add("android-docs", "android")
|
109
|
-
add("chrome-docs", "chrome")
|
110
|
-
end
|
114
|
+
.with(:python) do # ref=Symbol | group=String
|
115
|
+
banner([:name, ": ", :version], "path") # chrome-docs: 0.1.0 | /workspaces/chrome-docs
|
116
|
+
doc("make html") # rake rb:doc:python
|
117
|
+
run(false) # rake rb:build:python (disabled)
|
118
|
+
exclude(%i[base git]) # Project::Git.ref (superclass)
|
119
|
+
add("android-docs", "android") # rake rb:android:doc
|
120
|
+
add("chrome-docs", "chrome") # rake rb:chrome:doc
|
121
|
+
end #
|
111
122
|
.style("inline", "bold")
|
112
123
|
.build
|
113
124
|
```
|
114
125
|
|
115
|
-
**NOTE**: The use of "**ref**" (class name) is only necessary when
|
126
|
+
**NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
|
127
|
+
|
128
|
+
### Graph
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
Workspace::Application
|
132
|
+
.new(main: "squared")
|
133
|
+
.with(:python) do
|
134
|
+
add("android-docs", "android")
|
135
|
+
add("chrome-docs", "chrome", graph: "android")
|
136
|
+
end
|
137
|
+
.with(:node) do
|
138
|
+
graph(["build", "copy"]) # Optional
|
139
|
+
add("e-mc", "emc")
|
140
|
+
add("pi-r", "pir", graph: "emc")
|
141
|
+
add("squared-express", "express", graph: "pir")
|
142
|
+
add("squared", graph: ["chrome", "express"])
|
143
|
+
end
|
144
|
+
.with(:ruby) do
|
145
|
+
add("pathname")
|
146
|
+
add("fileutils", graph: "pathname")
|
147
|
+
add("optparse")
|
148
|
+
add("rake", graph: ["fileutils", "optparse"])
|
149
|
+
end
|
150
|
+
.build
|
151
|
+
```
|
152
|
+
|
153
|
+
```sh
|
154
|
+
rake pir:graph # emc + pir
|
155
|
+
rake express:graph # emc + pir + express
|
156
|
+
rake chrome:graph # android + chrome
|
157
|
+
rake graph:python # same
|
158
|
+
rake squared:graph # android + chrome + emc + pir + express + squared
|
159
|
+
rake graph:node # same
|
160
|
+
rake rake:graph # pathname + fileutils + optparse + rake
|
161
|
+
rake graph:ruby # same
|
162
|
+
rake graph # graph:node + graph:ruby
|
163
|
+
```
|
164
|
+
|
165
|
+
### Batch
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
Workspace::Series.batch(:ruby, :node, {
|
169
|
+
stage: %i[graph test],
|
170
|
+
reset: %i[stash pull]
|
171
|
+
})
|
172
|
+
```
|
173
|
+
|
174
|
+
### Rename
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
Workspace::Series.rename("depend", "install")
|
178
|
+
```
|
116
179
|
|
117
180
|
## Usage
|
118
181
|
|
119
182
|
```sh
|
120
|
-
rake -T
|
121
|
-
rake
|
183
|
+
rake -T # List tasks
|
184
|
+
rake # rake status (usually "build")
|
122
185
|
|
123
186
|
# GIT_OPTIONS=rebase
|
124
187
|
rake pull # All except "default" + "app"
|
@@ -145,6 +208,7 @@ rake build:app # squared + cli + sqd-serve
|
|
145
208
|
rake squared:build:workspace # cli + sqd-serve
|
146
209
|
rake pull:sqd # sqd-admin
|
147
210
|
rake squared:pull:workspace # sqd-serve + sqd-admin
|
211
|
+
rake squared:outdated:workspace # cli + sqd-serve + sqd-admin
|
148
212
|
```
|
149
213
|
|
150
214
|
## Methods
|
@@ -153,6 +217,7 @@ Task:
|
|
153
217
|
|
154
218
|
* run
|
155
219
|
* depend
|
220
|
+
* graph
|
156
221
|
* test
|
157
222
|
* doc
|
158
223
|
* clean
|
@@ -179,14 +244,21 @@ Non-task:
|
|
179
244
|
### Build
|
180
245
|
|
181
246
|
```ruby
|
247
|
+
# :env :run :opts
|
182
248
|
# LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -c
|
183
249
|
BUILD_${NAME} # gcc a.c -o a.o
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
#
|
188
|
-
|
189
|
-
BUILD_${NAME}
|
250
|
+
BUILD_OPTS_${NAME} # -c
|
251
|
+
BUILD_ENV_${NAME} # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
|
252
|
+
|
253
|
+
# :env :opts :script
|
254
|
+
# NODE_ENV="production" NO_COLOR="1" npm run --loglevel=error --workspaces=false build:dev
|
255
|
+
BUILD_${NAME} # build:dev
|
256
|
+
BUILD_OPTS_${NAME} # --loglevel=error --workspaces=false
|
257
|
+
BUILD_ENV_${NAME} # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
|
258
|
+
BUILD_DEV_${NAME} # pattern,0,1 (:dev)
|
259
|
+
BUILD_PROD_${NAME} # pattern,0,1 (:prod)
|
260
|
+
|
261
|
+
BUILD_${NAME}=0 # skip project
|
190
262
|
```
|
191
263
|
|
192
264
|
These options also support the project specific suffix `${NAME}`. (e.g. LOG_FILE_SQUARED)
|
data/lib/squared/app.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'workspace'
|
4
|
+
require_relative 'workspace/repo'
|
5
|
+
require_relative 'workspace/project/node'
|
6
|
+
require_relative 'workspace/project/python'
|
7
|
+
require_relative 'workspace/project/ruby'
|
8
|
+
require_relative 'config'
|
9
|
+
|
10
|
+
Common = Squared::Common
|
data/lib/squared/common/base.rb
CHANGED
@@ -2,10 +2,16 @@
|
|
2
2
|
|
3
3
|
module Squared
|
4
4
|
module Common
|
5
|
-
|
5
|
+
ARG = {
|
6
6
|
PIPE: 1,
|
7
|
+
OUT: nil,
|
8
|
+
VERBOSE: nil,
|
7
9
|
FAIL: false,
|
8
|
-
COMMON: true
|
10
|
+
COMMON: true,
|
11
|
+
BANNER: true,
|
12
|
+
SPACE: ' => ',
|
13
|
+
GRAPH: ['│', '─', '├', '└', '┬'],
|
14
|
+
COLOR: ENV.fetch('NO_COLOR', '').empty?
|
9
15
|
}
|
10
16
|
VAR = {
|
11
17
|
project: {},
|
@@ -70,6 +76,7 @@ module Squared
|
|
70
76
|
end
|
71
77
|
|
72
78
|
def __freeze__
|
79
|
+
ARG.freeze
|
73
80
|
VAR.each_value(&:freeze)
|
74
81
|
VAR[:theme].each_value(&:freeze)
|
75
82
|
VAR.freeze
|
@@ -77,30 +84,19 @@ module Squared
|
|
77
84
|
|
78
85
|
module_function
|
79
86
|
|
80
|
-
def env(key, default = nil, equals: nil, ignore: nil, **)
|
81
|
-
ret = ENV.fetch(key, '')
|
82
|
-
return ret == equals.to_s unless equals.nil?
|
83
|
-
|
84
|
-
ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
|
85
|
-
end
|
86
|
-
|
87
|
-
def message(*args, hint: nil)
|
88
|
-
args.reject(&:empty?).join(' => ') + (hint ? " (#{hint})" : '')
|
89
|
-
end
|
90
|
-
|
91
87
|
def as_a(obj, meth = nil, flat: nil, compact: false)
|
92
88
|
return [] if obj.nil?
|
93
89
|
|
94
90
|
unless obj.is_a?(::Array)
|
95
|
-
obj = if
|
91
|
+
obj = if obj.respond_to?(:to_a) && !obj.is_a?(::Hash) && (val = obj.to_a).is_a?(::Array)
|
96
92
|
val
|
97
93
|
else
|
98
94
|
[obj]
|
99
95
|
end
|
100
96
|
end
|
101
97
|
obj = obj.flatten(flat.is_a?(::Numeric) ? flat : nil) if flat
|
102
|
-
obj = obj.
|
103
|
-
|
98
|
+
obj = obj.compact if compact
|
99
|
+
meth ? obj.map(&meth) : obj
|
104
100
|
end
|
105
101
|
end
|
106
102
|
end
|
data/lib/squared/common/class.rb
CHANGED
@@ -1,12 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
6
|
module Squared
|
6
7
|
module Common
|
8
|
+
class SymSet
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def self.to_s
|
12
|
+
super.match(/[^:]+$/)[0]
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@data, :+, :each, :each_with_index, :entries, :to_a, :include?
|
16
|
+
|
17
|
+
def initialize(data = [])
|
18
|
+
@data = ::Set.new(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(val)
|
22
|
+
@data.add(val.to_sym)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
@data.to_s.sub('Set', SymSet.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
alias inspect to_s
|
30
|
+
end
|
31
|
+
|
7
32
|
class JoinSet < ::Set
|
8
33
|
def self.to_s
|
9
|
-
super.
|
34
|
+
super.match(/[^:]+$/)[0]
|
10
35
|
end
|
11
36
|
|
12
37
|
def initialize(data = [], delim: ' ')
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pathname'
|
3
4
|
require 'logger'
|
4
5
|
|
5
6
|
require_relative 'base'
|
@@ -40,7 +41,7 @@ module Squared
|
|
40
41
|
private
|
41
42
|
|
42
43
|
def sub_style(val, *args, styles: nil, pat: nil, index: 1)
|
43
|
-
return val unless
|
44
|
+
return val unless ARG[:COLOR]
|
44
45
|
|
45
46
|
if pat && index != 0
|
46
47
|
return val unless (data = pat.match(val))
|
@@ -106,7 +107,7 @@ module Squared
|
|
106
107
|
out
|
107
108
|
end
|
108
109
|
|
109
|
-
def check_style(
|
110
|
+
def check_style(args, empty: true)
|
110
111
|
ret = []
|
111
112
|
colors = __get__(:colors)
|
112
113
|
as_a(args, flat: true, compact: true).each do |val|
|
@@ -123,7 +124,7 @@ module Squared
|
|
123
124
|
ret if empty || !ret.empty?
|
124
125
|
end
|
125
126
|
|
126
|
-
def apply_style(data, key,
|
127
|
+
def apply_style(data, key, args, empty: true)
|
127
128
|
return if data.is_a?(::Symbol) && (data = __get__(:theme)[data]).nil?
|
128
129
|
|
129
130
|
set = ->(k, v) { data[k] = check_style(v, empty: empty) }
|
@@ -137,15 +138,15 @@ module Squared
|
|
137
138
|
def log_sym(level)
|
138
139
|
if level.is_a?(::Numeric)
|
139
140
|
case level
|
140
|
-
when Logger::DEBUG
|
141
|
+
when ::Logger::DEBUG
|
141
142
|
:debug
|
142
|
-
when Logger::INFO
|
143
|
+
when ::Logger::INFO
|
143
144
|
:info
|
144
|
-
when Logger::WARN
|
145
|
+
when ::Logger::WARN
|
145
146
|
:warn
|
146
|
-
when Logger::ERROR
|
147
|
+
when ::Logger::ERROR
|
147
148
|
:error
|
148
|
-
when Logger::FATAL
|
149
|
+
when ::Logger::FATAL
|
149
150
|
:fatal
|
150
151
|
else
|
151
152
|
:unknown
|
@@ -155,7 +156,7 @@ module Squared
|
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
158
|
-
def log_title(level, color:
|
159
|
+
def log_title(level, color: ARG[:COLOR])
|
159
160
|
theme = __get__(:theme)[:logger]
|
160
161
|
styles = theme[level = log_sym(level)] || theme[level = :unknown]
|
161
162
|
case (ret = +level.to_s.upcase)
|
@@ -165,7 +166,7 @@ module Squared
|
|
165
166
|
color ? sub_style(ret, *styles) : ret
|
166
167
|
end
|
167
168
|
|
168
|
-
def log_message(level, *args, subject: nil, hint: nil, color:
|
169
|
+
def log_message(level, *args, subject: nil, hint: nil, color: ARG[:COLOR])
|
169
170
|
args = args.map(&:to_s)
|
170
171
|
if args.size > 1
|
171
172
|
title = log_title(level, color: false)
|
@@ -183,49 +184,103 @@ module Squared
|
|
183
184
|
end
|
184
185
|
end
|
185
186
|
|
186
|
-
def
|
187
|
-
|
187
|
+
def puts_oe(*args, pipe: 1)
|
188
|
+
if pipe.is_a?(::Pathname)
|
189
|
+
begin
|
190
|
+
::File.open(pipe, 'a') do |f|
|
191
|
+
br = ::File::SEPARATOR == '\\' ? "\r\n" : "\n"
|
192
|
+
args.flatten.each { |val| f.write("#{strip_style(val.chomp)}#{br}") }
|
193
|
+
end
|
194
|
+
return
|
195
|
+
rescue StandardError
|
196
|
+
pipe = 2
|
197
|
+
end
|
198
|
+
end
|
199
|
+
(pipe == 2 ? $stderr : $stdout).puts(*args)
|
188
200
|
end
|
189
201
|
|
190
202
|
module_function
|
191
203
|
|
192
|
-
def
|
204
|
+
def message(*args, hint: nil, empty: false)
|
205
|
+
(empty ? args.reject { |val| val.nil? || val.empty? } : args).join(ARG[:SPACE]) + (hint ? " (#{hint})" : '')
|
206
|
+
end
|
207
|
+
|
208
|
+
def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, border: nil, pipe: nil)
|
193
209
|
n = 0
|
194
|
-
|
195
|
-
|
196
|
-
|
210
|
+
max = ->(v) { n = [n, v.max_by(&:size).size].max }
|
211
|
+
set = lambda do |v|
|
212
|
+
ret = as_a(v, :to_s)
|
213
|
+
max.(ret)
|
214
|
+
ret
|
197
215
|
end
|
216
|
+
title &&= set.(title)
|
217
|
+
footer &&= set.(footer)
|
198
218
|
if val.is_a?(::Array)
|
199
|
-
lines = val
|
219
|
+
lines = val.map(&:to_s)
|
200
220
|
else
|
201
221
|
lines = val.to_s.lines.map(&:chomp)
|
202
|
-
lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(
|
222
|
+
lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(StandardError))
|
203
223
|
end
|
204
|
-
n = cols ||
|
224
|
+
n = cols || max.(lines)
|
205
225
|
if $stdout.tty?
|
206
226
|
require 'io/console'
|
207
227
|
(n = [n, $stdout.winsize[1] - 4].min) rescue nil
|
208
228
|
end
|
209
229
|
out = []
|
210
230
|
bord = '-' * (n + 4)
|
211
|
-
|
231
|
+
bord = sub_style(bord, styles: border) if border
|
232
|
+
sub = as_a(sub)
|
212
233
|
pr = lambda do |line|
|
213
234
|
s = line.ljust(n)
|
214
|
-
sub
|
215
|
-
"| #{s} |"
|
235
|
+
sub.each { |h| s = sub_style(s, **h) }
|
236
|
+
s = "| #{s} |"
|
237
|
+
if border
|
238
|
+
s = sub_style(s, pat: /^(\|)(.+)$/m, styles: border)
|
239
|
+
s = sub_style(s, pat: /^(.+)(\|)$/m, styles: border, index: 2)
|
240
|
+
end
|
241
|
+
s
|
216
242
|
end
|
217
243
|
out << bord
|
218
|
-
|
244
|
+
if title
|
245
|
+
out += title.map { |t| pr.(t) }
|
246
|
+
out << bord
|
247
|
+
end
|
219
248
|
lines.each { |line| out << pr.(line) }
|
220
249
|
out << bord
|
250
|
+
if footer
|
251
|
+
unless sub.empty? && !right
|
252
|
+
footer.map! do |s|
|
253
|
+
s = s.rjust(n + 4) if right
|
254
|
+
sub.each { |h| s = sub_style(s, **h) }
|
255
|
+
s
|
256
|
+
end
|
257
|
+
end
|
258
|
+
out += footer
|
259
|
+
end
|
221
260
|
if block_given?
|
222
261
|
yield out
|
223
|
-
elsif pipe
|
262
|
+
elsif pipe
|
263
|
+
case pipe
|
264
|
+
when 0
|
265
|
+
pipe = $stdin
|
266
|
+
when 2
|
267
|
+
pipe = $stderr
|
268
|
+
else
|
269
|
+
pipe = $stdout unless pipe.respond_to?(:puts)
|
270
|
+
end
|
224
271
|
pipe.puts out
|
225
272
|
else
|
226
273
|
err ? warn(out) : puts(out)
|
227
274
|
end
|
228
275
|
end
|
276
|
+
|
277
|
+
def strip_style(val)
|
278
|
+
val.gsub(/\x1B\[(\d+;?)+m/, '')
|
279
|
+
end
|
280
|
+
|
281
|
+
def raise_error(*args, hint: nil, kind: ArgumentError)
|
282
|
+
raise kind, message(*args, hint: hint, empty: true)
|
283
|
+
end
|
229
284
|
end
|
230
285
|
end
|
231
286
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Squared
|
4
|
+
module Common
|
5
|
+
module Prompt
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 5, timeout: 15)
|
9
|
+
require 'readline'
|
10
|
+
require 'timeout'
|
11
|
+
agree = /^#{agree}$/i if agree.is_a?(::String)
|
12
|
+
cancel = /^#{cancel}$/i if cancel.is_a?(::String)
|
13
|
+
Timeout.timeout(timeout) do
|
14
|
+
begin
|
15
|
+
while (ch = Readline.readline(msg, true))
|
16
|
+
ch = ch.chomp
|
17
|
+
case (ch.empty? ? default : ch)
|
18
|
+
when agree
|
19
|
+
return true
|
20
|
+
when cancel
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
attempts -= 1
|
24
|
+
exit 1 unless attempts >= 0
|
25
|
+
end
|
26
|
+
rescue Interrupt
|
27
|
+
puts
|
28
|
+
exit 0
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/squared/common/shell.rb
CHANGED
@@ -9,9 +9,10 @@ module Squared
|
|
9
9
|
module_function
|
10
10
|
|
11
11
|
def shell_escape(val, quote: false)
|
12
|
-
|
12
|
+
val = val.to_s
|
13
|
+
return ::Shellwords.escape(val) unless ::Rake::Win32.windows?
|
13
14
|
|
14
|
-
quote ? shell_quote(
|
15
|
+
quote ? shell_quote(val, force: false) : val
|
15
16
|
end
|
16
17
|
|
17
18
|
def shell_quote(val, force: true)
|
@@ -22,7 +23,7 @@ module Squared
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def shell_split(val, quote: false, join: false)
|
25
|
-
ret = Shellwords.split(val).map do |opt|
|
26
|
+
ret = ::Shellwords.split(val).map do |opt|
|
26
27
|
if (data = /^(--?[^= ]+)(=|\s+)?(["'])?(.+?)\3?$/m.match(opt))
|
27
28
|
next opt unless data[2] && !data[3]
|
28
29
|
|
@@ -8,67 +8,37 @@ module Squared
|
|
8
8
|
module System
|
9
9
|
module_function
|
10
10
|
|
11
|
-
def shell(*
|
11
|
+
def shell(*args, **kwargs)
|
12
12
|
if RUBY_VERSION =~ /^2\.[0-5]\./
|
13
13
|
exception = kwargs.delete(:exception)
|
14
|
-
ret = system(*
|
15
|
-
|
14
|
+
ret = system(*args, **kwargs)
|
15
|
+
return ret if ret || !exception
|
16
16
|
|
17
|
-
|
17
|
+
raise $?.to_s
|
18
18
|
else
|
19
|
-
system(*
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 5, timeout: 15)
|
24
|
-
require 'readline'
|
25
|
-
require 'timeout'
|
26
|
-
agree = /^#{agree}$/i if agree.is_a?(::String)
|
27
|
-
cancel = /^#{cancel}$/i if cancel.is_a?(::String)
|
28
|
-
Timeout.timeout(timeout) do
|
29
|
-
begin
|
30
|
-
while (ch = Readline.readline(msg, true))
|
31
|
-
ch = ch.chomp
|
32
|
-
ch = default if ch.empty?
|
33
|
-
case ch
|
34
|
-
when agree
|
35
|
-
return true
|
36
|
-
when cancel
|
37
|
-
return false
|
38
|
-
end
|
39
|
-
attempts -= 1
|
40
|
-
exit 1 unless attempts >= 0
|
41
|
-
end
|
42
|
-
rescue Interrupt
|
43
|
-
puts
|
44
|
-
exit 0
|
45
|
-
else
|
46
|
-
false
|
47
|
-
end
|
19
|
+
system(*args, **kwargs)
|
48
20
|
end
|
49
21
|
end
|
50
22
|
|
51
23
|
def copy_d(src, dest, glob: ['**/*'], create: false, verbose: true)
|
52
|
-
src = Pathname.new(src)
|
53
|
-
dest = Pathname.new(dest)
|
24
|
+
src = ::Pathname.new(src)
|
25
|
+
dest = ::Pathname.new(dest)
|
54
26
|
raise "#{dest} (not found)" if !create && !dest.exist?
|
55
27
|
|
56
28
|
subdir = []
|
57
29
|
files = 0
|
58
30
|
dest.mkpath if create
|
59
|
-
glob
|
60
|
-
|
61
|
-
|
62
|
-
ent = Pathname.new(path)
|
63
|
-
next if ent.directory?
|
31
|
+
(glob.is_a?(::Array) ? glob : [glob]).each do |val|
|
32
|
+
::Dir.glob(src.join(val)) do |path|
|
33
|
+
next if (path = ::Pathname.new(path)).directory?
|
64
34
|
|
65
|
-
target = dest.join(
|
35
|
+
target = dest.join(path.relative_path_from(src))
|
66
36
|
dir = target.dirname
|
67
37
|
unless subdir.include?(dir.to_s)
|
68
38
|
dir.mkpath
|
69
39
|
subdir << dir.to_s
|
70
40
|
end
|
71
|
-
copy_f
|
41
|
+
copy_f path, target
|
72
42
|
files += 1
|
73
43
|
end
|
74
44
|
end
|
@@ -77,15 +47,13 @@ module Squared
|
|
77
47
|
|
78
48
|
def copy_f(src, dest, overwrite: true, verbose: false)
|
79
49
|
unless overwrite
|
80
|
-
path = Pathname.new(dest)
|
81
|
-
|
82
|
-
src = [src] unless src.is_a?(::Array)
|
83
|
-
src = src.reject { |val| path.join(File.basename(val)).exist? }
|
50
|
+
if (path = ::Pathname.new(dest)).directory?
|
51
|
+
src = (src.is_a?(::Array) ? src : [src]).reject { |val| path.join(::File.basename(val)).exist? }
|
84
52
|
elsif path.exist?
|
85
53
|
return
|
86
54
|
end
|
87
55
|
end
|
88
|
-
FileUtils.cp(src, dest, verbose: verbose)
|
56
|
+
::FileUtils.cp(src, dest, verbose: verbose)
|
89
57
|
end
|
90
58
|
end
|
91
59
|
end
|