shopify-cli 0.9.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +3 -0
- data/CHANGELOG.md +24 -2
- data/RELEASING.md +4 -4
- data/docs/_config.yml +3 -0
- data/docs/_data/nav.yml +9 -0
- data/docs/getting-started/index.md +5 -40
- data/docs/getting-started/install/index.md +75 -0
- data/docs/getting-started/migrate/index.md +96 -0
- data/docs/getting-started/uninstall/index.md +37 -0
- data/docs/getting-started/upgrade/index.md +37 -0
- data/docs/index.md +5 -6
- data/lib/project_types/extension/cli.rb +2 -1
- data/lib/project_types/extension/commands/tunnel.rb +1 -1
- data/lib/project_types/extension/forms/register.rb +2 -3
- data/lib/project_types/extension/graphql/get_app_by_api_key.graphql +9 -0
- data/lib/project_types/extension/tasks/converters/app_converter.rb +27 -0
- data/lib/project_types/extension/tasks/get_app.rb +22 -0
- data/lib/project_types/extension/tasks/get_apps.rb +1 -6
- data/lib/project_types/node/forms/create.rb +3 -54
- data/lib/project_types/node/messages/messages.rb +3 -14
- data/lib/project_types/rails/forms/create.rb +3 -52
- data/lib/project_types/rails/messages/messages.rb +2 -13
- data/lib/project_types/script/cli.rb +5 -5
- data/lib/project_types/script/commands/create.rb +5 -4
- data/lib/project_types/script/commands/push.rb +1 -0
- data/lib/project_types/script/config/extension_points.yml +3 -3
- data/lib/project_types/script/errors.rb +1 -0
- data/lib/project_types/script/forms/create.rb +8 -4
- data/lib/project_types/script/layers/application/build_script.rb +7 -10
- data/lib/project_types/script/layers/application/create_script.rb +16 -14
- data/lib/project_types/script/layers/application/project_dependencies.rb +3 -9
- data/lib/project_types/script/layers/application/push_script.rb +13 -11
- data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +106 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +64 -0
- data/lib/project_types/script/layers/infrastructure/errors.rb +2 -2
- data/lib/project_types/script/layers/infrastructure/project_creator.rb +23 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +6 -3
- data/lib/project_types/script/layers/infrastructure/script_repository.rb +8 -38
- data/lib/project_types/script/layers/infrastructure/task_runner.rb +18 -0
- data/lib/project_types/script/messages/messages.rb +5 -6
- data/lib/project_types/script/script_project.rb +22 -9
- data/lib/project_types/script/templates/ts/as-pect.d.ts +1 -0
- data/lib/project_types/script/ui/error_handler.rb +5 -0
- data/lib/shopify-cli/admin_api.rb +1 -2
- data/lib/shopify-cli/admin_api/populate_resource_command.rb +10 -1
- data/lib/shopify-cli/admin_api/schema.rb +11 -1
- data/lib/shopify-cli/api.rb +2 -0
- data/lib/shopify-cli/context.rb +60 -0
- data/lib/shopify-cli/core/entry_point.rb +6 -0
- data/lib/shopify-cli/core/finalize.rb +13 -0
- data/lib/shopify-cli/git.rb +14 -10
- data/lib/shopify-cli/messages/messages.rb +22 -2
- data/lib/shopify-cli/tasks.rb +1 -0
- data/lib/shopify-cli/tasks/select_org_and_shop.rb +77 -0
- data/lib/shopify-cli/tunnel.rb +33 -1
- data/lib/shopify-cli/version.rb +1 -1
- data/vendor/deps/cli-ui/REVISION +1 -1
- data/vendor/deps/cli-ui/lib/cli/ui.rb +52 -11
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +11 -7
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +34 -21
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +107 -149
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +99 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +119 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +158 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +112 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +9 -15
- data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +47 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +9 -7
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +39 -14
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +62 -44
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +7 -2
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +23 -3
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +34 -10
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +12 -7
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +26 -16
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +3 -3
- data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +75 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +27 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +61 -0
- metadata +24 -9
- data/lib/project_types/extension/features/tunnel_url.rb +0 -20
- data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +0 -73
- data/lib/project_types/script/layers/infrastructure/assemblyscript_wasm_builder.rb +0 -39
- data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +0 -36
- data/lib/project_types/script/layers/infrastructure/script_builder.rb +0 -34
- data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +0 -59
- data/vendor/deps/cli-ui/lib/cli/ui/box.rb +0 -15
data/lib/shopify-cli/tunnel.rb
CHANGED
@@ -11,7 +11,7 @@ module ShopifyCli
|
|
11
11
|
class Tunnel
|
12
12
|
extend SingleForwardable
|
13
13
|
|
14
|
-
def_delegators :new, :start, :stop, :auth
|
14
|
+
def_delegators :new, :start, :stop, :auth, :stats, :urls
|
15
15
|
|
16
16
|
class FetchUrlError < RuntimeError; end
|
17
17
|
class NgrokError < RuntimeError; end
|
@@ -23,6 +23,10 @@ module ShopifyCli
|
|
23
23
|
linux: 'https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip',
|
24
24
|
}
|
25
25
|
|
26
|
+
NGROK_TUNNELS_URI = URI.parse('http://localhost:4040/api/tunnels')
|
27
|
+
TUNNELS_FIELD = 'tunnels'
|
28
|
+
PUBLIC_URL_FIELD = 'public_url'
|
29
|
+
|
26
30
|
##
|
27
31
|
# will find and stop a running tunnel process. It will also output if the
|
28
32
|
# operation was successful or not
|
@@ -82,6 +86,34 @@ module ShopifyCli
|
|
82
86
|
ctx.system(File.join(ShopifyCli::CACHE_DIR, 'ngrok'), 'authtoken', token)
|
83
87
|
end
|
84
88
|
|
89
|
+
##
|
90
|
+
# will return the statistics of the current running tunnels
|
91
|
+
#
|
92
|
+
# #### Returns
|
93
|
+
#
|
94
|
+
# * `stats` - the hash of running statistics returning from the ngrok api
|
95
|
+
#
|
96
|
+
def stats
|
97
|
+
response = Net::HTTP.get_response(NGROK_TUNNELS_URI)
|
98
|
+
JSON.parse(response.body)
|
99
|
+
rescue
|
100
|
+
{}
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# will return the urls of the current running tunnels
|
105
|
+
#
|
106
|
+
# #### Returns
|
107
|
+
#
|
108
|
+
# * `stats` - the array of urls
|
109
|
+
#
|
110
|
+
def urls
|
111
|
+
tunnels = stats.dig(TUNNELS_FIELD)
|
112
|
+
tunnels.map { |tunnel| tunnel.dig(PUBLIC_URL_FIELD) }
|
113
|
+
rescue
|
114
|
+
[]
|
115
|
+
end
|
116
|
+
|
85
117
|
private
|
86
118
|
|
87
119
|
def install(ctx)
|
data/lib/shopify-cli/version.rb
CHANGED
data/vendor/deps/cli-ui/REVISION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
c59f601fe271432dfe304f30f08a48b2a343606e
|
@@ -3,14 +3,15 @@ module CLI
|
|
3
3
|
autoload :ANSI, 'cli/ui/ansi'
|
4
4
|
autoload :Glyph, 'cli/ui/glyph'
|
5
5
|
autoload :Color, 'cli/ui/color'
|
6
|
-
autoload :Box, 'cli/ui/box'
|
7
6
|
autoload :Frame, 'cli/ui/frame'
|
7
|
+
autoload :Printer, 'cli/ui/printer'
|
8
8
|
autoload :Progress, 'cli/ui/progress'
|
9
9
|
autoload :Prompt, 'cli/ui/prompt'
|
10
10
|
autoload :Terminal, 'cli/ui/terminal'
|
11
11
|
autoload :Truncater, 'cli/ui/truncater'
|
12
12
|
autoload :Formatter, 'cli/ui/formatter'
|
13
13
|
autoload :Spinner, 'cli/ui/spinner'
|
14
|
+
autoload :Widgets, 'cli/ui/widgets'
|
14
15
|
|
15
16
|
# Convenience accessor to +CLI::UI::Spinner::SpinGroup+
|
16
17
|
SpinGroup = Spinner::SpinGroup
|
@@ -42,7 +43,23 @@ module CLI
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
#
|
46
|
+
# Frame style resolution using +CLI::UI::Frame::FrameStyle.lookup+.
|
47
|
+
# Will lookup using +FrameStyle.lookup+ if the input is a symbol. Otherwise,
|
48
|
+
# we assume it's a valid FrameStyle
|
49
|
+
#
|
50
|
+
# ==== Attributes
|
51
|
+
#
|
52
|
+
# * +input+ - frame style to resolve
|
53
|
+
def self.resolve_style(input)
|
54
|
+
case input
|
55
|
+
when Symbol
|
56
|
+
CLI::UI::Frame::FrameStyle.lookup(input)
|
57
|
+
else
|
58
|
+
input
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Convenience Method for +CLI::UI::Prompt.confirm+
|
46
63
|
#
|
47
64
|
# ==== Attributes
|
48
65
|
#
|
@@ -52,18 +69,18 @@ module CLI
|
|
52
69
|
CLI::UI::Prompt.confirm(question, **kwargs)
|
53
70
|
end
|
54
71
|
|
55
|
-
#
|
72
|
+
# Convenience Method for +CLI::UI::Prompt.ask+
|
56
73
|
#
|
57
74
|
# ==== Attributes
|
58
75
|
#
|
59
76
|
# * +question+ - question to ask
|
60
|
-
# * +kwargs+ -
|
77
|
+
# * +kwargs+ - arguments for +Prompt.ask+
|
61
78
|
#
|
62
79
|
def self.ask(question, **kwargs)
|
63
80
|
CLI::UI::Prompt.ask(question, **kwargs)
|
64
81
|
end
|
65
82
|
|
66
|
-
#
|
83
|
+
# Convenience Method to resolve text using +CLI::UI::Formatter.format+
|
67
84
|
# Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
|
68
85
|
#
|
69
86
|
# ==== Attributes
|
@@ -75,10 +92,10 @@ module CLI
|
|
75
92
|
return input if input.nil?
|
76
93
|
formatted = CLI::UI::Formatter.new(input).format
|
77
94
|
return formatted unless truncate_to
|
78
|
-
|
95
|
+
CLI::UI::Truncater.call(formatted, truncate_to)
|
79
96
|
end
|
80
97
|
|
81
|
-
#
|
98
|
+
# Convenience Method to format text using +CLI::UI::Formatter.format+
|
82
99
|
# Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
|
83
100
|
#
|
84
101
|
# https://user-images.githubusercontent.com/3074765/33799827-6d0721a2-dd01-11e7-9ab5-c3d455264afe.png
|
@@ -96,7 +113,18 @@ module CLI
|
|
96
113
|
CLI::UI::Formatter.new(input).format(enable_color: enable_color)
|
97
114
|
end
|
98
115
|
|
99
|
-
#
|
116
|
+
# Convenience Method for +CLI::UI::Printer.puts+
|
117
|
+
#
|
118
|
+
# ==== Attributes
|
119
|
+
#
|
120
|
+
# * +msg+ - Message to print
|
121
|
+
# * +kwargs+ - keyword arguments for +Printer.puts+
|
122
|
+
#
|
123
|
+
def self.puts(msg, **kwargs)
|
124
|
+
CLI::UI::Printer.puts(msg, **kwargs)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Convenience Method for +CLI::UI::Frame.open+
|
100
128
|
#
|
101
129
|
# ==== Attributes
|
102
130
|
#
|
@@ -107,7 +135,7 @@ module CLI
|
|
107
135
|
CLI::UI::Frame.open(*args, &block)
|
108
136
|
end
|
109
137
|
|
110
|
-
#
|
138
|
+
# Convenience Method for +CLI::UI::Spinner.spin+
|
111
139
|
#
|
112
140
|
# ==== Attributes
|
113
141
|
#
|
@@ -118,7 +146,7 @@ module CLI
|
|
118
146
|
CLI::UI::Spinner.spin(*args, &block)
|
119
147
|
end
|
120
148
|
|
121
|
-
#
|
149
|
+
# Convenience Method to override frame color using +CLI::UI::Frame.with_frame_color+
|
122
150
|
#
|
123
151
|
# ==== Attributes
|
124
152
|
#
|
@@ -142,7 +170,7 @@ module CLI
|
|
142
170
|
CLI::UI::StdoutRouter.duplicate_output_to = File.open(path, 'w')
|
143
171
|
yield
|
144
172
|
ensure
|
145
|
-
if file_descriptor = CLI::UI::StdoutRouter.duplicate_output_to
|
173
|
+
if (file_descriptor = CLI::UI::StdoutRouter.duplicate_output_to)
|
146
174
|
file_descriptor.close
|
147
175
|
CLI::UI::StdoutRouter.duplicate_output_to = nil
|
148
176
|
end
|
@@ -181,6 +209,19 @@ module CLI
|
|
181
209
|
end
|
182
210
|
|
183
211
|
self.enable_color = $stdout.tty?
|
212
|
+
|
213
|
+
# Set the default frame style.
|
214
|
+
# Convenience method for setting the default frame style with +CLI::UI::Frame.frame_style=+
|
215
|
+
#
|
216
|
+
# Raises ArgumentError if +frame_style+ is not valid
|
217
|
+
#
|
218
|
+
# ==== Attributes
|
219
|
+
#
|
220
|
+
# * +symbol+ - the default frame style to use for frames
|
221
|
+
#
|
222
|
+
def self.frame_style=(frame_style)
|
223
|
+
Frame.frame_style = frame_style.to_sym
|
224
|
+
end
|
184
225
|
end
|
185
226
|
end
|
186
227
|
|
@@ -31,15 +31,19 @@ module CLI
|
|
31
31
|
BOLD = new('1', :bold)
|
32
32
|
WHITE = new('97', :white)
|
33
33
|
|
34
|
+
# 240 is very dark gray; 255 is very light gray. 244 is somewhat dark.
|
35
|
+
GRAY = new('38;5;244', :grey)
|
36
|
+
|
34
37
|
MAP = {
|
35
|
-
red:
|
36
|
-
green:
|
37
|
-
yellow:
|
38
|
-
blue:
|
38
|
+
red: RED,
|
39
|
+
green: GREEN,
|
40
|
+
yellow: YELLOW,
|
41
|
+
blue: BLUE,
|
39
42
|
magenta: MAGENTA,
|
40
|
-
cyan:
|
41
|
-
reset:
|
42
|
-
bold:
|
43
|
+
cyan: CYAN,
|
44
|
+
reset: RESET,
|
45
|
+
bold: BOLD,
|
46
|
+
gray: GRAY,
|
43
47
|
}.freeze
|
44
48
|
|
45
49
|
class InvalidColorName < ArgumentError
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require 'strscan'
|
2
|
+
require('cli/ui')
|
3
|
+
require('strscan')
|
5
4
|
|
6
5
|
module CLI
|
7
6
|
module UI
|
@@ -13,39 +12,40 @@ module CLI
|
|
13
12
|
#
|
14
13
|
SGR_MAP = {
|
15
14
|
# presentational
|
16
|
-
'red'
|
17
|
-
'green'
|
18
|
-
'yellow'
|
19
|
-
|
20
|
-
'blue'
|
21
|
-
'magenta'
|
22
|
-
'cyan'
|
23
|
-
'bold'
|
24
|
-
'italic'
|
15
|
+
'red' => '31',
|
16
|
+
'green' => '32',
|
17
|
+
'yellow' => '33',
|
18
|
+
# default blue is low-contrast against black in some default terminal color scheme
|
19
|
+
'blue' => '94', # 9x = high-intensity fg color x
|
20
|
+
'magenta' => '35',
|
21
|
+
'cyan' => '36',
|
22
|
+
'bold' => '1',
|
23
|
+
'italic' => '3',
|
25
24
|
'underline' => '4',
|
26
|
-
'reset'
|
25
|
+
'reset' => '0',
|
27
26
|
|
28
27
|
# semantic
|
29
|
-
'error'
|
28
|
+
'error' => '31', # red
|
30
29
|
'success' => '32', # success
|
31
30
|
'warning' => '33', # yellow
|
32
|
-
'info'
|
31
|
+
'info' => '94', # bright blue
|
33
32
|
'command' => '36', # cyan
|
34
33
|
}.freeze
|
35
34
|
|
36
35
|
BEGIN_EXPR = '{{'
|
37
36
|
END_EXPR = '}}'
|
38
37
|
|
38
|
+
SCAN_WIDGET = %r[@widget/(?<handle>\w+):(?<args>.*?)}}]
|
39
39
|
SCAN_FUNCNAME = /\w+:/
|
40
40
|
SCAN_GLYPH = /.}}/
|
41
|
-
SCAN_BODY =
|
41
|
+
SCAN_BODY = %r{
|
42
42
|
.*?
|
43
43
|
(
|
44
44
|
#{BEGIN_EXPR} |
|
45
45
|
#{END_EXPR} |
|
46
46
|
\z
|
47
47
|
)
|
48
|
-
|
48
|
+
}mx
|
49
49
|
|
50
50
|
DISCARD_BRACES = 0..-3
|
51
51
|
|
@@ -123,7 +123,7 @@ module CLI
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def parse_expr(sc, stack)
|
126
|
-
if match = sc.scan(SCAN_GLYPH)
|
126
|
+
if (match = sc.scan(SCAN_GLYPH))
|
127
127
|
glyph_handle = match[0]
|
128
128
|
begin
|
129
129
|
glyph = Glyph.lookup(glyph_handle)
|
@@ -136,7 +136,20 @@ module CLI
|
|
136
136
|
index
|
137
137
|
)
|
138
138
|
end
|
139
|
-
elsif match = sc.scan(
|
139
|
+
elsif (match = sc.scan(SCAN_WIDGET))
|
140
|
+
match_data = SCAN_WIDGET.match(match) # Regexp.last_match doesn't work here
|
141
|
+
widget_handle = match_data['handle']
|
142
|
+
begin
|
143
|
+
widget = Widgets.lookup(widget_handle)
|
144
|
+
emit(widget.call(match_data['args']), stack)
|
145
|
+
rescue Widgets::InvalidWidgetHandle
|
146
|
+
index = sc.pos - 2 # rewind past '}}'
|
147
|
+
raise(FormatError.new(
|
148
|
+
"invalid widget handle at index #{index}: '#{widget_handle}'",
|
149
|
+
@text, index,
|
150
|
+
))
|
151
|
+
end
|
152
|
+
elsif (match = sc.scan(SCAN_FUNCNAME))
|
140
153
|
funcname = match.chop
|
141
154
|
stack.push(funcname)
|
142
155
|
else
|
@@ -153,10 +166,10 @@ module CLI
|
|
153
166
|
|
154
167
|
def parse_body(sc, stack = [])
|
155
168
|
match = sc.scan(SCAN_BODY)
|
156
|
-
if match
|
169
|
+
if match&.end_with?(BEGIN_EXPR)
|
157
170
|
emit(match[DISCARD_BRACES], stack)
|
158
171
|
parse_expr(sc, stack)
|
159
|
-
elsif match
|
172
|
+
elsif match&.end_with?(END_EXPR)
|
160
173
|
emit(match[DISCARD_BRACES], stack)
|
161
174
|
if stack.pop == LITERAL_BRACES
|
162
175
|
emit('}}', stack)
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'cli/ui'
|
3
|
+
require 'cli/ui/frame/frame_stack'
|
4
|
+
require 'cli/ui/frame/frame_style'
|
2
5
|
|
3
6
|
module CLI
|
4
7
|
module UI
|
@@ -7,6 +10,22 @@ module CLI
|
|
7
10
|
class << self
|
8
11
|
DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
|
9
12
|
|
13
|
+
def frame_style
|
14
|
+
@frame_style ||= FrameStyle::Box
|
15
|
+
end
|
16
|
+
|
17
|
+
# Set the default frame style.
|
18
|
+
#
|
19
|
+
# Raises ArgumentError if +frame_style+ is not valid
|
20
|
+
#
|
21
|
+
# ==== Attributes
|
22
|
+
#
|
23
|
+
# * +symbol+ or +FrameStyle+ - the default frame style to use for frames
|
24
|
+
#
|
25
|
+
def frame_style=(frame_style)
|
26
|
+
@frame_style = CLI::UI.resolve_style(frame_style)
|
27
|
+
end
|
28
|
+
|
10
29
|
# Opens a new frame. Can be nested
|
11
30
|
# Can be invoked in two ways: block and blockless
|
12
31
|
# * In block form, the frame is closed automatically when the block returns
|
@@ -27,6 +46,7 @@ module CLI
|
|
27
46
|
# * +:failure_text+ - If the block failed, what do we output? Defaults to nil
|
28
47
|
# * +:success_text+ - If the block succeeds, what do we output? Defaults to nil
|
29
48
|
# * +:timing+ - How long did the frame content take? Invalid for blockless. Defaults to true for the block form
|
49
|
+
# * +frame_style+ - The frame style to use for this frame
|
30
50
|
#
|
31
51
|
# ==== Example
|
32
52
|
#
|
@@ -34,7 +54,7 @@ module CLI
|
|
34
54
|
#
|
35
55
|
# CLI::UI::Frame.open('Open') { puts 'hi' }
|
36
56
|
#
|
37
|
-
# Output:
|
57
|
+
# Default Output:
|
38
58
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
39
59
|
# ┃ hi
|
40
60
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (0.0s) ━━
|
@@ -43,7 +63,7 @@ module CLI
|
|
43
63
|
#
|
44
64
|
# CLI::UI::Frame.open('Open')
|
45
65
|
#
|
46
|
-
# Output:
|
66
|
+
# Default Output:
|
47
67
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
48
68
|
#
|
49
69
|
#
|
@@ -52,8 +72,10 @@ module CLI
|
|
52
72
|
color: DEFAULT_FRAME_COLOR,
|
53
73
|
failure_text: nil,
|
54
74
|
success_text: nil,
|
55
|
-
timing: nil
|
75
|
+
timing: nil,
|
76
|
+
frame_style: self.frame_style
|
56
77
|
)
|
78
|
+
frame_style = CLI::UI.resolve_style(frame_style)
|
57
79
|
color = CLI::UI.resolve_color(color)
|
58
80
|
|
59
81
|
unless block_given?
|
@@ -61,18 +83,17 @@ module CLI
|
|
61
83
|
raise ArgumentError, "failure_text is not compatible with blockless invocation"
|
62
84
|
elsif success_text
|
63
85
|
raise ArgumentError, "success_text is not compatible with blockless invocation"
|
64
|
-
elsif
|
86
|
+
elsif timing
|
65
87
|
raise ArgumentError, "timing is not compatible with blockless invocation"
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
69
|
-
|
70
|
-
|
71
|
-
t_start = Time.now.to_f
|
91
|
+
t_start = Time.now
|
72
92
|
CLI::UI.raw do
|
73
|
-
|
93
|
+
print prefix.chop
|
94
|
+
puts frame_style.open(text, color: color)
|
74
95
|
end
|
75
|
-
FrameStack.push(color)
|
96
|
+
FrameStack.push(color: color, style: frame_style)
|
76
97
|
|
77
98
|
return unless block_given?
|
78
99
|
|
@@ -82,14 +103,14 @@ module CLI
|
|
82
103
|
success = yield
|
83
104
|
rescue
|
84
105
|
closed = true
|
85
|
-
t_diff =
|
106
|
+
t_diff = elasped(t_start, timing)
|
86
107
|
close(failure_text, color: :red, elapsed: t_diff)
|
87
108
|
raise
|
88
109
|
else
|
89
110
|
success
|
90
111
|
ensure
|
91
112
|
unless closed
|
92
|
-
t_diff =
|
113
|
+
t_diff = elasped(t_start, timing)
|
93
114
|
if success != false
|
94
115
|
close(success_text, color: color, elapsed: t_diff)
|
95
116
|
else
|
@@ -99,8 +120,8 @@ module CLI
|
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
102
|
-
#
|
103
|
-
#
|
123
|
+
# Adds a divider in a frame
|
124
|
+
# Used to separate information within a single frame
|
104
125
|
#
|
105
126
|
# ==== Attributes
|
106
127
|
#
|
@@ -109,31 +130,38 @@ module CLI
|
|
109
130
|
# ==== Options
|
110
131
|
#
|
111
132
|
# * +:color+ - The color of the frame. Defaults to +DEFAULT_FRAME_COLOR+
|
112
|
-
# *
|
133
|
+
# * +frame_style+ - The frame style to use for this frame
|
113
134
|
#
|
114
135
|
# ==== Example
|
115
136
|
#
|
116
|
-
# CLI::UI::Frame.
|
137
|
+
# CLI::UI::Frame.open('Open') { CLI::UI::Frame.divider('Divider') }
|
117
138
|
#
|
118
|
-
# Output:
|
119
|
-
#
|
139
|
+
# Default Output:
|
140
|
+
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
141
|
+
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
142
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
120
143
|
#
|
144
|
+
# ==== Raises
|
121
145
|
#
|
122
|
-
|
123
|
-
|
146
|
+
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
147
|
+
#
|
148
|
+
def divider(text, color: nil, frame_style: nil)
|
149
|
+
fs_item = FrameStack.pop
|
150
|
+
raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item
|
151
|
+
|
152
|
+
color = CLI::UI.resolve_color(color) || fs_item.color
|
153
|
+
frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
|
124
154
|
|
125
|
-
FrameStack.pop
|
126
|
-
kwargs = {}
|
127
|
-
if elapsed
|
128
|
-
kwargs[:right_text] = "(#{elapsed.round(2)}s)"
|
129
|
-
end
|
130
155
|
CLI::UI.raw do
|
131
|
-
|
156
|
+
print prefix.chop
|
157
|
+
puts frame_style.divider(text, color: color)
|
132
158
|
end
|
159
|
+
|
160
|
+
FrameStack.push(fs_item)
|
133
161
|
end
|
134
162
|
|
135
|
-
#
|
136
|
-
#
|
163
|
+
# Closes a frame
|
164
|
+
# Automatically called for a block-form +open+
|
137
165
|
#
|
138
166
|
# ==== Attributes
|
139
167
|
#
|
@@ -141,51 +169,69 @@ module CLI
|
|
141
169
|
#
|
142
170
|
# ==== Options
|
143
171
|
#
|
144
|
-
# * +:color+ - The color of the frame. Defaults to
|
172
|
+
# * +:color+ - The color of the frame. Defaults to nil
|
173
|
+
# * +:elapsed+ - How long did the frame take? Defaults to nil
|
174
|
+
# * +frame_style+ - The frame style to use for this frame. Defaults to nil
|
145
175
|
#
|
146
176
|
# ==== Example
|
147
177
|
#
|
148
|
-
# CLI::UI::Frame.
|
178
|
+
# CLI::UI::Frame.close('Close')
|
149
179
|
#
|
150
|
-
# Output:
|
151
|
-
#
|
152
|
-
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
153
|
-
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
180
|
+
# Default Output:
|
181
|
+
# ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
154
182
|
#
|
155
183
|
# ==== Raises
|
156
184
|
#
|
157
185
|
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
158
186
|
#
|
159
|
-
def
|
187
|
+
def close(text, color: nil, elapsed: nil, frame_style: nil)
|
160
188
|
fs_item = FrameStack.pop
|
161
|
-
raise UnnestedFrameException, "
|
162
|
-
|
163
|
-
|
189
|
+
raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item
|
190
|
+
|
191
|
+
color = CLI::UI.resolve_color(color) || fs_item.color
|
192
|
+
frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
|
193
|
+
|
194
|
+
kwargs = {}
|
195
|
+
if elapsed
|
196
|
+
kwargs[:right_text] = "(#{elapsed.round(2)}s)"
|
197
|
+
end
|
164
198
|
|
165
199
|
CLI::UI.raw do
|
166
|
-
|
200
|
+
print prefix.chop
|
201
|
+
puts frame_style.close(text, color: color, **kwargs)
|
167
202
|
end
|
168
|
-
FrameStack.push(item)
|
169
203
|
end
|
170
204
|
|
171
205
|
# Determines the prefix of a frame entry taking multi-nested frames into account
|
172
206
|
#
|
173
207
|
# ==== Options
|
174
208
|
#
|
175
|
-
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
209
|
+
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
176
210
|
#
|
177
|
-
def prefix(color:
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
211
|
+
def prefix(color: Thread.current[:cliui_frame_color_override])
|
212
|
+
+''.tap do |output|
|
213
|
+
items = FrameStack.items
|
214
|
+
|
215
|
+
items[0..-2].each do |item|
|
216
|
+
output << item.color.code << item.frame_style.prefix
|
217
|
+
end
|
218
|
+
|
219
|
+
if (item = items.last)
|
220
|
+
final_color = color || item.color
|
221
|
+
output << CLI::UI.resolve_color(final_color).code \
|
222
|
+
<< item.frame_style.prefix \
|
223
|
+
<< ' ' \
|
224
|
+
<< CLI::UI::Color::RESET.code
|
225
|
+
end
|
182
226
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
227
|
+
end
|
228
|
+
|
229
|
+
# The width of a prefix given the number of Frames in the stack
|
230
|
+
# Does _not_ account for the space at the end of the final prefix
|
231
|
+
def prefix_width
|
232
|
+
FrameStack.items.reduce(0) do |width, item|
|
233
|
+
width + item.frame_style.prefix_width
|
187
234
|
end
|
188
|
-
pfx
|
189
235
|
end
|
190
236
|
|
191
237
|
# Override a color for a given thread.
|
@@ -202,107 +248,19 @@ module CLI
|
|
202
248
|
Thread.current[:cliui_frame_color_override] = prev
|
203
249
|
end
|
204
250
|
|
205
|
-
# The width of a prefix given the number of Frames in the stack
|
206
|
-
#
|
207
|
-
def prefix_width
|
208
|
-
w = FrameStack.items.size
|
209
|
-
w.zero? ? 0 : w + 1
|
210
|
-
end
|
211
|
-
|
212
251
|
private
|
213
252
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
prefix << ' ' << text << ' '
|
226
|
-
end
|
227
|
-
|
228
|
-
termwidth = CLI::UI::Terminal.width
|
229
|
-
|
230
|
-
suffix = +''
|
231
|
-
if right_text
|
232
|
-
suffix << ' ' << right_text << ' '
|
233
|
-
end
|
234
|
-
|
235
|
-
suffix_width = CLI::UI::ANSI.printing_width(suffix)
|
236
|
-
suffix_end = termwidth - 2
|
237
|
-
suffix_start = suffix_end - suffix_width
|
238
|
-
|
239
|
-
prefix_width = CLI::UI::ANSI.printing_width(prefix)
|
240
|
-
prefix_start = 0
|
241
|
-
prefix_end = prefix_start + prefix_width
|
242
|
-
|
243
|
-
if prefix_end > suffix_start
|
244
|
-
suffix = ''
|
245
|
-
# if prefix_end > termwidth
|
246
|
-
# we *could* truncate it, but let's just let it overflow to the
|
247
|
-
# next line and call it poor usage of this API.
|
248
|
-
end
|
249
|
-
|
250
|
-
o = +''
|
251
|
-
|
252
|
-
is_ci = ![0, '', nil].include?(ENV['CI'])
|
253
|
-
|
254
|
-
# Jumping around the line can cause some unwanted flashes
|
255
|
-
o << CLI::UI::ANSI.hide_cursor
|
256
|
-
|
257
|
-
o << if is_ci
|
258
|
-
# In CI, we can't use absolute horizontal positions because of timestamps.
|
259
|
-
# So we move around the line by offset from this cursor position.
|
260
|
-
CLI::UI::ANSI.cursor_save
|
261
|
-
else
|
262
|
-
# Outside of CI, we reset to column 1 so that things like ^C don't
|
263
|
-
# cause output misformatting.
|
264
|
-
"\r"
|
265
|
-
end
|
266
|
-
|
267
|
-
o << color.code
|
268
|
-
o << CLI::UI::Box::Heavy::HORZ * termwidth # draw a full line
|
269
|
-
o << print_at_x(prefix_start, prefix, is_ci)
|
270
|
-
o << color.code
|
271
|
-
o << print_at_x(suffix_start, suffix, is_ci)
|
272
|
-
o << CLI::UI::Color::RESET.code
|
273
|
-
o << CLI::UI::ANSI.show_cursor
|
274
|
-
o << "\n"
|
275
|
-
|
276
|
-
o
|
277
|
-
end
|
278
|
-
|
279
|
-
def print_at_x(x, str, is_ci)
|
280
|
-
if is_ci
|
281
|
-
CLI::UI::ANSI.cursor_restore + CLI::UI::ANSI.cursor_forward(x) + str
|
282
|
-
else
|
283
|
-
CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
module FrameStack
|
288
|
-
ENVVAR = 'CLI_FRAME_STACK'
|
289
|
-
|
290
|
-
def self.items
|
291
|
-
ENV.fetch(ENVVAR, '').split(':').map(&:to_sym)
|
292
|
-
end
|
293
|
-
|
294
|
-
def self.push(item)
|
295
|
-
curr = items
|
296
|
-
curr << item.name
|
297
|
-
ENV[ENVVAR] = curr.join(':')
|
298
|
-
end
|
299
|
-
|
300
|
-
def self.pop
|
301
|
-
curr = items
|
302
|
-
ret = curr.pop
|
303
|
-
ENV[ENVVAR] = curr.join(':')
|
304
|
-
ret.nil? ? nil : ret.to_sym
|
305
|
-
end
|
253
|
+
# If timing is:
|
254
|
+
# Numeric: return it
|
255
|
+
# false: return nil
|
256
|
+
# true or nil: defaults to Time.new
|
257
|
+
# Time: return the difference with start
|
258
|
+
def elasped(start, timing)
|
259
|
+
return timing if timing.is_a?(Numeric)
|
260
|
+
return if timing.is_a?(FalseClass)
|
261
|
+
|
262
|
+
timing = Time.new if timing.is_a?(TrueClass) || timing.nil?
|
263
|
+
timing - start
|
306
264
|
end
|
307
265
|
end
|
308
266
|
end
|