cli-kit 3.1.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +1 -0
- data/.github/probots.yml +2 -0
- data/Gemfile.lock +3 -3
- data/TODO.md +5 -0
- data/cli-kit.gemspec +2 -2
- data/examples/README.md +21 -0
- data/examples/minimal/example.rb +22 -0
- data/examples/single-file/example.rb +71 -0
- data/examples/todo-list/README.md +1 -0
- data/gen/template/lib/__app__/commands/example.rb +0 -1
- data/lib/cli/kit/base_command.rb +4 -3
- data/lib/cli/kit/config.rb +4 -4
- data/lib/cli/kit/error_handler.rb +10 -1
- data/lib/cli/kit/executor.rb +8 -2
- data/lib/cli/kit/ini.rb +7 -3
- data/lib/cli/kit/support/test_helper.rb +18 -0
- data/lib/cli/kit/system.rb +6 -2
- data/lib/cli/kit/version.rb +1 -1
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a1c78454dde57819601c7ef40040c02943b72ea2f3831718a2a4362731c2cf9a
|
4
|
+
data.tar.gz: 6e4e1adcdd54eb38d419b2d2d936ec03a05190cf72baaabd1002e1407dfdcf49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f57fe58683a342a39957fd19cedc79641583456100e7e63b71e4e395b0fc705938ccaa1574bc36d975dda6f6b5c7fcbf28406972432abcf3ac890bb1334e2715
|
7
|
+
data.tar.gz: 5f1f0bf80809f7ff4ae97ab4cd0871ceaef6b4c4967b06fdcae5ccb1c869524c351c1a2acf7cab83cd2416ce37766033b5c63d6ff2480fd660067d74dcc51f8f
|
data/.github/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@Shopify/dev-infra
|
data/.github/probots.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cli-kit (3.
|
4
|
+
cli-kit (3.3.0)
|
5
5
|
cli-ui (>= 1.1.4)
|
6
6
|
|
7
7
|
GEM
|
@@ -11,7 +11,7 @@ GEM
|
|
11
11
|
ast (2.4.0)
|
12
12
|
builder (3.2.3)
|
13
13
|
byebug (9.0.6)
|
14
|
-
cli-ui (1.
|
14
|
+
cli-ui (1.2.3)
|
15
15
|
metaclass (0.0.4)
|
16
16
|
method_source (0.8.2)
|
17
17
|
minitest (5.10.2)
|
@@ -53,4 +53,4 @@ DEPENDENCIES
|
|
53
53
|
rubocop (~> 0.56.0)
|
54
54
|
|
55
55
|
BUNDLED WITH
|
56
|
-
1.
|
56
|
+
1.17.3
|
data/TODO.md
ADDED
data/cli-kit.gemspec
CHANGED
@@ -6,8 +6,8 @@ require 'cli/kit/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'cli-kit'
|
8
8
|
spec.version = CLI::Kit::VERSION
|
9
|
-
spec.authors = ['Burke Libbey', '
|
10
|
-
spec.email = ['burke.libbey@shopify.com', '
|
9
|
+
spec.authors = ['Burke Libbey', 'Aaron Olson', 'Lisa Ugray']
|
10
|
+
spec.email = ['burke.libbey@shopify.com', 'aaron.olson@shopify.com', 'lisa.ugray@shopify.com']
|
11
11
|
|
12
12
|
spec.summary = 'Terminal UI framework extensions'
|
13
13
|
spec.description = 'Terminal UI framework extensions'
|
data/examples/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Examples
|
2
|
+
|
3
|
+
**Note: If you're looking to bootstrap a new app, consider running `cli-kit new` instead of
|
4
|
+
copy/pasting one of these.**
|
5
|
+
|
6
|
+
## Full Example
|
7
|
+
|
8
|
+
The full example lives in its own repository at https://github.com/Shopify/cli-kit-example.
|
9
|
+
This is quite similar to the output of `cli-kit new`, but fleshed out a little bit more with
|
10
|
+
a couple of commands.
|
11
|
+
|
12
|
+
## Single-File starting point
|
13
|
+
|
14
|
+
Maybe it appeals to you to start from a single file and grow, rather than generating framework.
|
15
|
+
Check out the [`examples/single-file`](single-file) example.
|
16
|
+
|
17
|
+
## Minimal example
|
18
|
+
|
19
|
+
This example demonstrates the core of what `cli-kit` does very simply, but in general, shouldn't be
|
20
|
+
used for anything other than quick hacks. It leaves out a few best practices that help CLI apps
|
21
|
+
scale gracefully. Find it at [`examples/minimal`](minimal).
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cli/ui'
|
4
|
+
require 'cli/kit'
|
5
|
+
|
6
|
+
CLI::UI::StdoutRouter.enable
|
7
|
+
|
8
|
+
include CLI::Kit
|
9
|
+
|
10
|
+
registry = CommandRegistry.new(default: 'hello', contextual_resolver: nil)
|
11
|
+
registry.add(Class.new(BaseCommand) do
|
12
|
+
def call(_args, _name)
|
13
|
+
puts "hello, world!"
|
14
|
+
end
|
15
|
+
end, 'hello')
|
16
|
+
|
17
|
+
executor = Executor.new(log_file: '/tmp/example.log')
|
18
|
+
error_handler = ErrorHandler.new(log_file: '/tmp/example.log', exception_reporter: nil)
|
19
|
+
resolver = Resolver.new(tool_name: 'example', command_registry: registry)
|
20
|
+
entry_point = ->(args) { executor.call(*resolver.call(args)) }
|
21
|
+
|
22
|
+
exit(error_handler.call { entry_point.call(ARGV.dup) }) if __FILE__ == $PROGRAM_NAME
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cli/ui'
|
4
|
+
require 'cli/kit'
|
5
|
+
|
6
|
+
CLI::UI::StdoutRouter.enable
|
7
|
+
|
8
|
+
module Example
|
9
|
+
extend CLI::Kit::Autocall
|
10
|
+
|
11
|
+
TOOL_NAME = 'example'
|
12
|
+
ROOT = File.expand_path('../..', __FILE__)
|
13
|
+
LOG_FILE = '/tmp/example.log'
|
14
|
+
|
15
|
+
module Commands
|
16
|
+
extend CLI::Kit::Autocall
|
17
|
+
|
18
|
+
Registry = CLI::Kit::CommandRegistry.new(
|
19
|
+
default: 'hello',
|
20
|
+
contextual_resolver: nil
|
21
|
+
)
|
22
|
+
|
23
|
+
def self.register(const, cmd, path = nil, &block)
|
24
|
+
path ? autoload(const, path) : autocall(const, &block)
|
25
|
+
Registry.add(->() { const_get(const) }, cmd)
|
26
|
+
end
|
27
|
+
|
28
|
+
# register(:Hello, 'hello', 'a/b/hello')
|
29
|
+
|
30
|
+
register(:Hello, 'hello') do
|
31
|
+
Class.new(Example::Command) do
|
32
|
+
def call(_args, _name)
|
33
|
+
puts "hello, world!"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
autocall(:EntryPoint) do
|
40
|
+
Module.new do
|
41
|
+
def self.call(args)
|
42
|
+
cmd, command_name, args = Example::Resolver.call(args)
|
43
|
+
Example::Executor.call(cmd, command_name, args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
autocall(:Config) { CLI::Kit::Config.new(tool_name: TOOL_NAME) }
|
49
|
+
autocall(:Command) { CLI::Kit::BaseCommand }
|
50
|
+
|
51
|
+
autocall(:Executor) { CLI::Kit::Executor.new(log_file: LOG_FILE) }
|
52
|
+
autocall(:Resolver) do
|
53
|
+
CLI::Kit::Resolver.new(
|
54
|
+
tool_name: TOOL_NAME,
|
55
|
+
command_registry: Example::Commands::Registry
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
autocall(:ErrorHandler) do
|
60
|
+
CLI::Kit::ErrorHandler.new(
|
61
|
+
log_file: LOG_FILE,
|
62
|
+
exception_reporter: nil
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if __FILE__ == $PROGRAM_NAME
|
68
|
+
exit(Example::ErrorHandler.call do
|
69
|
+
Example::EntryPoint.call(ARGV.dup)
|
70
|
+
end)
|
71
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Full example at https://github.com/Shopify/cli-kit-example
|
data/lib/cli/kit/base_command.rb
CHANGED
@@ -17,21 +17,22 @@ module CLI
|
|
17
17
|
|
18
18
|
def self.call(args, command_name)
|
19
19
|
cmd = new
|
20
|
-
stats_tags = cmd.stats_tags(args)
|
20
|
+
stats_tags = cmd.stats_tags(args, command_name)
|
21
21
|
begin
|
22
22
|
statsd_increment("cli.command.invoked", tags: stats_tags)
|
23
23
|
statsd_time("cli.command.time", tags: stats_tags) do
|
24
24
|
cmd.call(args, command_name)
|
25
25
|
end
|
26
26
|
statsd_increment("cli.command.success", tags: stats_tags)
|
27
|
-
rescue => e
|
27
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
28
28
|
statsd_increment("cli.command.exception", tags: stats_tags + ["exception:#{e.class}"])
|
29
29
|
raise e
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def stats_tags(args)
|
33
|
+
def stats_tags(args, command_name)
|
34
34
|
tags = ["task:#{self.class}"]
|
35
|
+
tags << "command:#{command_name}" if command_name
|
35
36
|
tags << "subcommand:#{args.first}" if args&.first && has_subcommands?
|
36
37
|
tags
|
37
38
|
end
|
data/lib/cli/kit/config.rb
CHANGED
@@ -23,13 +23,13 @@ module CLI
|
|
23
23
|
# #### Example Usage
|
24
24
|
# `config.get('name.of.config')`
|
25
25
|
#
|
26
|
-
def get(section, name)
|
27
|
-
all_configs.dig("[#{section}]", name) ||
|
26
|
+
def get(section, name, default: false)
|
27
|
+
all_configs.dig("[#{section}]", name) || default
|
28
28
|
end
|
29
29
|
|
30
30
|
# Coalesce and enforce the value of a config to a boolean
|
31
|
-
def get_bool(section, name)
|
32
|
-
case get(section, name).to_s
|
31
|
+
def get_bool(section, name, default: false)
|
32
|
+
case get(section, name, default: default).to_s
|
33
33
|
when "true"
|
34
34
|
true
|
35
35
|
when "false"
|
@@ -4,9 +4,10 @@ require 'English'
|
|
4
4
|
module CLI
|
5
5
|
module Kit
|
6
6
|
class ErrorHandler
|
7
|
-
def initialize(log_file:, exception_reporter:)
|
7
|
+
def initialize(log_file:, exception_reporter:, tool_name: nil)
|
8
8
|
@log_file = log_file
|
9
9
|
@exception_reporter_or_proc = exception_reporter || NullExceptionReporter
|
10
|
+
@tool_name = tool_name
|
10
11
|
end
|
11
12
|
|
12
13
|
module NullExceptionReporter
|
@@ -84,6 +85,14 @@ module CLI
|
|
84
85
|
rescue Interrupt
|
85
86
|
$stderr.puts(format_error_message("Interrupt"))
|
86
87
|
CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
88
|
+
rescue Errno::ENOSPC
|
89
|
+
message = if @tool_name
|
90
|
+
"Your disk is full - {{command:#{@tool_name}}} requires free space to operate"
|
91
|
+
else
|
92
|
+
"Your disk is full - free space is required to operate"
|
93
|
+
end
|
94
|
+
$stderr.puts(format_error_message(message))
|
95
|
+
CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
87
96
|
end
|
88
97
|
|
89
98
|
def exception_reporter
|
data/lib/cli/kit/executor.rb
CHANGED
@@ -16,8 +16,14 @@ module CLI
|
|
16
16
|
begin
|
17
17
|
command.call(args, command_name)
|
18
18
|
rescue => e
|
19
|
-
|
20
|
-
|
19
|
+
begin
|
20
|
+
$stderr.puts "This command ran with ID: #{id}"
|
21
|
+
$stderr.puts "Please include this information in any issues/report along with relevant logs"
|
22
|
+
rescue SystemCallError
|
23
|
+
# Outputting to stderr is best-effort. Avoid raising another error when outputting debug info so that
|
24
|
+
# we can detect and log the original error, which may even be the source of this error.
|
25
|
+
nil
|
26
|
+
end
|
21
27
|
raise e
|
22
28
|
end
|
23
29
|
end
|
data/lib/cli/kit/ini.rb
CHANGED
@@ -15,8 +15,12 @@ module CLI
|
|
15
15
|
class Ini
|
16
16
|
attr_accessor :ini
|
17
17
|
|
18
|
-
def initialize(path = nil, default_section: nil, convert_types: true)
|
19
|
-
@config =
|
18
|
+
def initialize(path = nil, config: nil, default_section: nil, convert_types: true)
|
19
|
+
@config = if path && File.exist?(path)
|
20
|
+
File.readlines(path)
|
21
|
+
elsif config
|
22
|
+
config.lines
|
23
|
+
end
|
20
24
|
@ini = {}
|
21
25
|
@current_key = nil
|
22
26
|
@default_section = default_section
|
@@ -39,7 +43,7 @@ module CLI
|
|
39
43
|
|
40
44
|
# Otherwise set the values
|
41
45
|
else
|
42
|
-
k, v = l.split('=').map(&:strip)
|
46
|
+
k, v = l.split('=', 2).map(&:strip)
|
43
47
|
set_val(k, v)
|
44
48
|
end
|
45
49
|
end
|
@@ -19,6 +19,24 @@ module CLI
|
|
19
19
|
assert_all_commands_run
|
20
20
|
end
|
21
21
|
|
22
|
+
module FakeConfig
|
23
|
+
require 'tmpdir'
|
24
|
+
require 'fileutils'
|
25
|
+
|
26
|
+
def setup
|
27
|
+
super
|
28
|
+
@tmpdir = Dir.mktmpdir
|
29
|
+
@prev_xdg = ENV['XDG_CONFIG_HOME']
|
30
|
+
ENV['XDG_CONFIG_HOME'] = @tmpdir
|
31
|
+
end
|
32
|
+
|
33
|
+
def teardown
|
34
|
+
FileUtils.rm_rf(@tmpdir)
|
35
|
+
ENV['XDG_CONFIG_HOME'] = @prev_xdg
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
22
40
|
class FakeSuccess
|
23
41
|
def initialize(success)
|
24
42
|
@success = success
|
data/lib/cli/kit/system.rb
CHANGED
@@ -191,9 +191,13 @@ module CLI
|
|
191
191
|
# If only one argument was provided, make sure it's interpreted by a shell.
|
192
192
|
return ["true ; " + a[0]] if a.size == 1
|
193
193
|
return a if a.first.include?('/')
|
194
|
-
|
195
|
-
|
194
|
+
|
195
|
+
paths = env.fetch('PATH', '').split(':')
|
196
|
+
item = paths.detect do |f|
|
197
|
+
command_path = "#{f}/#{a.first}"
|
198
|
+
File.executable?(command_path) && File.file?(command_path)
|
196
199
|
end
|
200
|
+
|
197
201
|
a[0] = "#{item}/#{a.first}" if item
|
198
202
|
a
|
199
203
|
end
|
data/lib/cli/kit/version.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cli-kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
|
-
-
|
8
|
+
- Aaron Olson
|
9
9
|
- Lisa Ugray
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2019-06-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: cli-ui
|
@@ -71,13 +71,15 @@ dependencies:
|
|
71
71
|
description: Terminal UI framework extensions
|
72
72
|
email:
|
73
73
|
- burke.libbey@shopify.com
|
74
|
-
-
|
74
|
+
- aaron.olson@shopify.com
|
75
75
|
- lisa.ugray@shopify.com
|
76
76
|
executables:
|
77
77
|
- cli-kit
|
78
78
|
extensions: []
|
79
79
|
extra_rdoc_files: []
|
80
80
|
files:
|
81
|
+
- ".github/CODEOWNERS"
|
82
|
+
- ".github/probots.yml"
|
81
83
|
- ".gitignore"
|
82
84
|
- ".rubocop.yml"
|
83
85
|
- ".travis.yml"
|
@@ -86,11 +88,16 @@ files:
|
|
86
88
|
- LICENSE.txt
|
87
89
|
- README.md
|
88
90
|
- Rakefile
|
91
|
+
- TODO.md
|
89
92
|
- bin/console
|
90
93
|
- bin/test_gen
|
91
94
|
- bin/testunit
|
92
95
|
- cli-kit.gemspec
|
93
96
|
- dev.yml
|
97
|
+
- examples/README.md
|
98
|
+
- examples/minimal/example.rb
|
99
|
+
- examples/single-file/example.rb
|
100
|
+
- examples/todo-list/README.md
|
94
101
|
- exe/cli-kit
|
95
102
|
- gen/lib/gen.rb
|
96
103
|
- gen/lib/gen/commands.rb
|
@@ -150,8 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
157
|
- !ruby/object:Gem::Version
|
151
158
|
version: '0'
|
152
159
|
requirements: []
|
153
|
-
|
154
|
-
rubygems_version: 2.6.14
|
160
|
+
rubygems_version: 3.0.2
|
155
161
|
signing_key:
|
156
162
|
specification_version: 4
|
157
163
|
summary: Terminal UI framework extensions
|