cli-kit 3.1.0 → 3.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 283b38ecb21fd3caac8399447f06f5bccff261af
4
- data.tar.gz: fe47b61139d7df53eca531693ba385c44a0c6455
2
+ SHA256:
3
+ metadata.gz: a1c78454dde57819601c7ef40040c02943b72ea2f3831718a2a4362731c2cf9a
4
+ data.tar.gz: 6e4e1adcdd54eb38d419b2d2d936ec03a05190cf72baaabd1002e1407dfdcf49
5
5
  SHA512:
6
- metadata.gz: 6f8723304b346c20418debde33e203bc3794ae0defc4e34308a7c2fbf5364fb22c41f7b7e800bbe5f2fb76a0ac90ca37e980c5ec641c71b3deb19d277d029b19
7
- data.tar.gz: 6f08ef957f6f63b6fe108b6b6e118b5f887a9050ae8aed548445d8b77ff8ec47ff5a488f881b3e95e3682c30ba7098196876096a5cd70fd7fe312dd74292928c
6
+ metadata.gz: f57fe58683a342a39957fd19cedc79641583456100e7e63b71e4e395b0fc705938ccaa1574bc36d975dda6f6b5c7fcbf28406972432abcf3ac890bb1334e2715
7
+ data.tar.gz: 5f1f0bf80809f7ff4ae97ab4cd0871ceaef6b4c4967b06fdcae5ccb1c869524c351c1a2acf7cab83cd2416ce37766033b5c63d6ff2480fd660067d74dcc51f8f
@@ -0,0 +1 @@
1
+ @Shopify/dev-infra
@@ -0,0 +1,2 @@
1
+ enabled:
2
+ - cla
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cli-kit (3.1.0)
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.1.4)
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.16.1
56
+ 1.17.3
data/TODO.md ADDED
@@ -0,0 +1,5 @@
1
+ * Build a help system
2
+ * Build a tabular data formatter
3
+ * Suppress colour by default when printing to non-TTY
4
+ * Do frames in a way that works on BK when running on BK
5
+
@@ -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', 'Julian Nadeau', 'Lisa Ugray']
10
- spec.email = ['burke.libbey@shopify.com', 'julian.nadeau@shopify.com', 'lisa.ugray@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'
@@ -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
@@ -1,5 +1,4 @@
1
1
  require '__app__'
2
- require 'json'
3
2
 
4
3
  module __App__
5
4
  module Commands
@@ -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
@@ -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) || false
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
@@ -16,8 +16,14 @@ module CLI
16
16
  begin
17
17
  command.call(args, command_name)
18
18
  rescue => e
19
- $stderr.puts "This command ran with ID: #{id}"
20
- $stderr.puts "Please include this information in any issues/report along with relevant logs"
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
@@ -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 = File.readlines(path) if path && File.exist?(path)
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
@@ -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
- item = env.fetch('PATH', '').split(':').detect do |f|
195
- File.exist?("#{f}/#{a.first}")
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
@@ -1,5 +1,5 @@
1
1
  module CLI
2
2
  module Kit
3
- VERSION = "3.1.0"
3
+ VERSION = "3.3.0"
4
4
  end
5
5
  end
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.1.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
- - Julian Nadeau
8
+ - Aaron Olson
9
9
  - Lisa Ugray
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-07-27 00:00:00.000000000 Z
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
- - julian.nadeau@shopify.com
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
- rubyforge_project:
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