cli-kit 3.3.0 → 4.0.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
2
  SHA256:
3
- metadata.gz: a1c78454dde57819601c7ef40040c02943b72ea2f3831718a2a4362731c2cf9a
4
- data.tar.gz: 6e4e1adcdd54eb38d419b2d2d936ec03a05190cf72baaabd1002e1407dfdcf49
3
+ metadata.gz: 523252459712e507c4579e244e6330eb3e8a5d8c71334d4e41553903ab651cd6
4
+ data.tar.gz: 459cb0a684e2890718d0cffdd74cd8540bd91a1995dfe1db593df9a189da20d7
5
5
  SHA512:
6
- metadata.gz: f57fe58683a342a39957fd19cedc79641583456100e7e63b71e4e395b0fc705938ccaa1574bc36d975dda6f6b5c7fcbf28406972432abcf3ac890bb1334e2715
7
- data.tar.gz: 5f1f0bf80809f7ff4ae97ab4cd0871ceaef6b4c4967b06fdcae5ccb1c869524c351c1a2acf7cab83cd2416ce37766033b5c63d6ff2480fd660067d74dcc51f8f
6
+ metadata.gz: 8579924711573ce6338b52dc44c47ad40c12f925bbd6f3ad6cee5be9f44b2b4f315e6d272adc309788b0418ee6806567660f0386d7e91d9d09a524beeb0a4a1f
7
+ data.tar.gz: b5507292ba97fd6f13b83b6426e81e4500e0ef1480387164c936164b7593bf7daac98e39cbb835362f198c39e562f2d12adf92384365a9c99a7f0ed42c158a45
@@ -0,0 +1,10 @@
1
+ version: 2
2
+
3
+ updates:
4
+ - package-ecosystem: bundler
5
+ directory: "/"
6
+ schedule:
7
+ interval: weekly
8
+ time: "07:00"
9
+ timezone: America/Toronto
10
+ open-pull-requests-limit: 99
@@ -0,0 +1,32 @@
1
+ name: Ruby
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ style:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: '2.7'
14
+ bundler-cache: true
15
+ - name: Check style
16
+ run: bundle exec rake style
17
+ test:
18
+ strategy:
19
+ matrix:
20
+ os: [macos-latest, ubuntu-latest, windows-latest]
21
+ ruby-version: ['2.6', '2.7']
22
+
23
+ runs-on: ${{ matrix.os }}
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby-version }}
30
+ bundler-cache: true
31
+ - name: Run tests
32
+ run: bundle exec rake test
data/.rubocop.yml CHANGED
@@ -1,9 +1,11 @@
1
- inherit_from:
2
- - http://shopify.github.io/ruby-style-guide/rubocop.yml
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop-cli.yml
3
3
 
4
4
  AllCops:
5
- Exclude: [ 'gen/template/**/*' ]
6
- TargetRubyVersion: 2.3
5
+ Exclude:
6
+ - gen/template/**/*
7
+ - vendor/**/*
8
+ TargetRubyVersion: 2.5
7
9
 
8
10
  Style/ClassAndModuleChildren:
9
11
  Exclude:
@@ -12,15 +14,8 @@ Style/ClassAndModuleChildren:
12
14
  Style/FrozenStringLiteralComment:
13
15
  Enabled: false
14
16
 
15
- Shopify/RubocopComments:
16
- Enabled: false
17
-
18
- # This doesn't understand that <<~ doesn't exist in 2.0
19
- Layout/IndentHeredoc:
20
- Enabled: false
21
-
22
17
  # This doesn't take into account retrying from an exception
23
- Lint/HandleExceptions:
18
+ Lint/SuppressedException:
24
19
  Enabled: false
25
20
 
26
21
  # allow String.new to create mutable strings
@@ -39,6 +34,5 @@ Style/RegexpLiteral:
39
34
  Style/MultilineBlockChain:
40
35
  Enabled: false
41
36
 
42
- # allow using names to be more expressive
43
- Performance/RedundantBlockCall:
44
- Enabled: false
37
+ Style/StringLiterals:
38
+ EnforcedStyle: single_quotes
data/Gemfile CHANGED
@@ -1,16 +1,17 @@
1
1
  # NOTE: These are development-only dependencies
2
- source "https://rubygems.org"
2
+ source 'https://rubygems.org'
3
3
 
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
- gem 'rubocop', '~> 0.56.0'
7
+ gem 'rubocop'
8
+ gem 'rubocop-shopify'
8
9
  gem 'byebug'
9
10
  gem 'method_source'
10
11
  end
11
12
 
12
13
  group :test do
13
- gem 'mocha', '~> 1.5.0', require: false
14
+ gem 'mocha', '~> 1.13.0', require: false
14
15
  gem 'minitest', '>= 5.0.0', require: false
15
16
  gem 'minitest-reporters', require: false
16
17
  end
data/Gemfile.lock CHANGED
@@ -1,56 +1,62 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cli-kit (3.3.0)
4
+ cli-kit (4.0.0)
5
5
  cli-ui (>= 1.1.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ansi (1.5.0)
11
- ast (2.4.0)
12
- builder (3.2.3)
13
- byebug (9.0.6)
14
- cli-ui (1.2.3)
15
- metaclass (0.0.4)
16
- method_source (0.8.2)
17
- minitest (5.10.2)
18
- minitest-reporters (1.1.14)
11
+ ast (2.4.2)
12
+ builder (3.2.4)
13
+ byebug (11.1.3)
14
+ cli-ui (1.5.1)
15
+ method_source (1.0.0)
16
+ minitest (5.14.4)
17
+ minitest-reporters (1.4.3)
19
18
  ansi
20
19
  builder
21
20
  minitest (>= 5.0)
22
21
  ruby-progressbar
23
- mocha (1.5.0)
24
- metaclass (~> 0.0.1)
25
- parallel (1.12.1)
26
- parser (2.5.1.0)
27
- ast (~> 2.4.0)
28
- powerpack (0.1.1)
22
+ mocha (1.13.0)
23
+ parallel (1.21.0)
24
+ parser (3.0.2.0)
25
+ ast (~> 2.4.1)
29
26
  rainbow (3.0.0)
30
- rake (10.5.0)
31
- rubocop (0.56.0)
27
+ rake (13.0.6)
28
+ regexp_parser (2.1.1)
29
+ rexml (3.2.5)
30
+ rubocop (1.22.3)
32
31
  parallel (~> 1.10)
33
- parser (>= 2.5)
34
- powerpack (~> 0.1)
32
+ parser (>= 3.0.0.0)
35
33
  rainbow (>= 2.2.2, < 4.0)
34
+ regexp_parser (>= 1.8, < 3.0)
35
+ rexml
36
+ rubocop-ast (>= 1.12.0, < 2.0)
36
37
  ruby-progressbar (~> 1.7)
37
- unicode-display_width (~> 1.0, >= 1.0.1)
38
- ruby-progressbar (1.8.1)
39
- unicode-display_width (1.3.2)
38
+ unicode-display_width (>= 1.4.0, < 3.0)
39
+ rubocop-ast (1.12.0)
40
+ parser (>= 3.0.1.1)
41
+ rubocop-shopify (2.3.0)
42
+ rubocop (~> 1.22)
43
+ ruby-progressbar (1.11.0)
44
+ unicode-display_width (2.1.0)
40
45
 
41
46
  PLATFORMS
42
47
  ruby
43
48
 
44
49
  DEPENDENCIES
45
- bundler (~> 1.15)
50
+ bundler (~> 2.1)
46
51
  byebug
47
52
  cli-kit!
48
53
  method_source
49
54
  minitest (>= 5.0.0)
50
55
  minitest-reporters
51
- mocha (~> 1.5.0)
52
- rake (~> 10.0)
53
- rubocop (~> 0.56.0)
56
+ mocha (~> 1.13.0)
57
+ rake (~> 13.0)
58
+ rubocop
59
+ rubocop-shopify
54
60
 
55
61
  BUNDLED WITH
56
- 1.17.3
62
+ 2.2.24
data/Rakefile CHANGED
@@ -1 +1,27 @@
1
- require "bundler/gem_tasks"
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
+ require 'rake/testtask'
3
+ require 'rubocop/rake_task'
4
+ require 'bundler/gem_tasks'
5
+
6
+ TEST_ROOT = File.expand_path('../test', __FILE__)
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs += ['test']
10
+ t.test_files = FileList[File.join(TEST_ROOT, '**', '*_test.rb')]
11
+ t.verbose = false
12
+ t.warning = false
13
+ end
14
+
15
+ RuboCop::RakeTask.new(:style) do |t|
16
+ t.options = ['--display-cop-names']
17
+ end
18
+
19
+ task :test_gen_bundler do
20
+ sh 'DEPS=bundler bin/test_gen'
21
+ end
22
+
23
+ task :test_gen_vendor do
24
+ sh 'DEPS=vendor bin/test_gen'
25
+ end
26
+
27
+ task(default: [:test, :style, :test_gen_bundler, :test_gen_vendor])
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "cli/kit"
3
+ require 'bundler/setup'
4
+ require 'cli/kit'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "cli/kit"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
data/bin/test_gen CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ set -euo pipefail
2
3
 
3
4
  # Make sure we're in the cli kit directory
4
5
  CURR_DIR=$(dirname "$0")
@@ -19,7 +20,9 @@ mv myapp ../
19
20
  cd ../myapp
20
21
 
21
22
  # Test
22
- bundle install
23
+ if [[ $DEPS == 'bundler' ]]; then
24
+ bundle install
25
+ fi
23
26
  bin/testunit
24
27
 
25
28
  if [[ $DEPS == 'vendor' ]]; then
data/bin/testunit CHANGED
@@ -9,12 +9,12 @@ CLI_TEST_ROOT = root + '/test'
9
9
  $LOAD_PATH.unshift(CLI_TEST_ROOT)
10
10
 
11
11
  def test_files
12
- Dir.glob(CLI_TEST_ROOT + "/**/*_test.rb")
12
+ Dir.glob(CLI_TEST_ROOT + '/**/*_test.rb')
13
13
  end
14
14
 
15
15
  if ARGV.empty?
16
16
  test_files.each { |f| require(f) }
17
- exit 0
17
+ exit(0)
18
18
  end
19
19
 
20
20
  # A list of files is presumed to be specified
data/cli-kit.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'cli/kit/version'
@@ -14,16 +15,16 @@ Gem::Specification.new do |spec|
14
15
  spec.homepage = 'https://github.com/shopify/cli-kit'
15
16
  spec.license = 'MIT'
16
17
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ spec.files = %x(git ls-files -z).split("\x0").reject do |f|
18
19
  f.match(%r{^(test|spec|features)/})
19
20
  end
20
21
  spec.bindir = 'exe'
21
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ['lib']
23
24
 
24
- spec.add_runtime_dependency 'cli-ui', '>= 1.1.4'
25
+ spec.add_runtime_dependency('cli-ui', '>= 1.1.4')
25
26
 
26
- spec.add_development_dependency 'bundler', '~> 1.15'
27
- spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'minitest', '~> 5.0'
27
+ spec.add_development_dependency('bundler', '~> 2.1')
28
+ spec.add_development_dependency('minitest', '~> 5.0')
29
+ spec.add_development_dependency('rake', '~> 13.0')
29
30
  end
data/dev.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  up:
2
- - ruby: 2.3.3
2
+ - ruby: 2.7.0
3
3
  - bundler
4
4
 
5
5
  commands:
@@ -5,12 +5,12 @@ require 'cli/kit'
5
5
 
6
6
  CLI::UI::StdoutRouter.enable
7
7
 
8
- include CLI::Kit
8
+ include(CLI::Kit)
9
9
 
10
10
  registry = CommandRegistry.new(default: 'hello', contextual_resolver: nil)
11
11
  registry.add(Class.new(BaseCommand) do
12
12
  def call(_args, _name)
13
- puts "hello, world!"
13
+ puts 'hello, world!'
14
14
  end
15
15
  end, 'hello')
16
16
 
@@ -30,7 +30,7 @@ module Example
30
30
  register(:Hello, 'hello') do
31
31
  Class.new(Example::Command) do
32
32
  def call(_args, _name)
33
- puts "hello, world!"
33
+ puts 'hello, world!'
34
34
  end
35
35
  end
36
36
  end
@@ -4,15 +4,15 @@ module Gen
4
4
  module Commands
5
5
  class Help < Gen::Command
6
6
  def call(_args, _name)
7
- puts CLI::UI.fmt("{{bold:Available commands}}")
8
- puts ""
7
+ puts CLI::UI.fmt('{{bold:Available commands}}')
8
+ puts ''
9
9
 
10
10
  Gen::Commands::Registry.resolved_commands.each do |name, klass|
11
11
  puts CLI::UI.fmt("{{command:#{Gen::TOOL_NAME} #{name}}}")
12
- if klass.respond_to?(:help) && help = klass.help
12
+ if klass.respond_to?(:help) && (help = klass.help)
13
13
  puts CLI::UI.fmt(help)
14
14
  end
15
- puts ""
15
+ puts ''
16
16
  end
17
17
  end
18
18
  end
@@ -18,20 +18,20 @@ module Gen
18
18
  # false -> delete file
19
19
  # string -> rename file before applying template substitutions
20
20
  VENDOR_TRANSLATIONS = {
21
- 'Gemfile' => false,
22
- 'exe/__app__-gems' => false,
21
+ 'Gemfile' => false,
22
+ 'exe/__app__-gems' => false,
23
23
  'exe/__app__-vendor' => 'exe/__app__',
24
- 'dev-gems.yml' => false,
25
- 'dev-vendor.yml' => 'dev.yml',
24
+ 'dev-gems.yml' => false,
25
+ 'dev-vendor.yml' => 'dev.yml',
26
26
  }.freeze
27
27
  private_constant :VENDOR_TRANSLATIONS
28
28
 
29
29
  BUNDLER_TRANSLATIONS = {
30
- 'bin/update-deps' => false,
31
- 'exe/__app__-gems' => 'exe/__app__',
30
+ 'bin/update-deps' => false,
31
+ 'exe/__app__-gems' => 'exe/__app__',
32
32
  'exe/__app__-vendor' => false,
33
- 'dev-gems.yml' => 'dev.yml',
34
- 'dev-vendor.yml' => false,
33
+ 'dev-gems.yml' => 'dev.yml',
34
+ 'dev-vendor.yml' => false,
35
35
  }.freeze
36
36
  private_constant :BUNDLER_TRANSLATIONS
37
37
 
@@ -58,8 +58,8 @@ module Gen
58
58
  private
59
59
 
60
60
  def ask_vendor?
61
- return 'vendor' if ENV['DEPS'] == 'vendor'
62
- return 'bundler' if ENV['DEPS'] == 'bundler'
61
+ return true if ENV['DEPS'] == 'vendor'
62
+ return false if ENV['DEPS'] == 'bundler'
63
63
 
64
64
  vendor = nil
65
65
  CLI::UI::Frame.open('Configuration') do
@@ -116,7 +116,7 @@ module Gen
116
116
  out, stat = Open3.capture2e('git', '-C', dir, 'clone', "https://github.com/shopify/#{repo}")
117
117
  unless stat.success?
118
118
  STDERR.puts(out)
119
- error("git clone failed")
119
+ error('git clone failed')
120
120
  end
121
121
  end
122
122
 
@@ -19,13 +19,13 @@ module CLI
19
19
  cmd = new
20
20
  stats_tags = cmd.stats_tags(args, command_name)
21
21
  begin
22
- statsd_increment("cli.command.invoked", tags: stats_tags)
23
- statsd_time("cli.command.time", tags: stats_tags) do
22
+ statsd_increment('cli.command.invoked', tags: stats_tags)
23
+ statsd_time('cli.command.time', tags: stats_tags) do
24
24
  cmd.call(args, command_name)
25
25
  end
26
- statsd_increment("cli.command.success", tags: stats_tags)
26
+ statsd_increment('cli.command.success', tags: stats_tags)
27
27
  rescue Exception => e # rubocop:disable Lint/RescueException
28
- statsd_increment("cli.command.exception", tags: stats_tags + ["exception:#{e.class}"])
28
+ statsd_increment('cli.command.exception', tags: stats_tags + ["exception:#{e.class}"])
29
29
  raise e
30
30
  end
31
31
  end
@@ -38,7 +38,7 @@ module CLI
38
38
  end
39
39
 
40
40
  def call(_args, _command_name)
41
- raise NotImplementedError
41
+ raise NotImplementedError, "#{self.class.name} must implement #{__method__}"
42
42
  end
43
43
 
44
44
  def has_subcommands?
@@ -18,22 +18,24 @@ module CLI
18
18
  # `name` : the name of the config value you are looking for
19
19
  #
20
20
  # #### Returns
21
- # `value` : the value of the config variable (false if none)
21
+ # `value` : the value of the config variable (nil if none)
22
22
  #
23
23
  # #### Example Usage
24
24
  # `config.get('name.of.config')`
25
25
  #
26
- def get(section, name, default: false)
26
+ def get(section, name, default: nil)
27
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
31
  def get_bool(section, name, default: false)
32
- case get(section, name, default: default).to_s
33
- when "true"
32
+ case get(section, name, default: default)
33
+ when 'true'
34
34
  true
35
- when "false"
35
+ when 'false'
36
36
  false
37
+ when default
38
+ default
37
39
  else
38
40
  raise CLI::Kit::Abort, "Invalid config: #{section}.#{name} is expected to be true or false"
39
41
  end
@@ -116,7 +118,7 @@ module CLI
116
118
 
117
119
  def ini
118
120
  @ini ||= CLI::Kit::Ini
119
- .new(file, default_section: "[global]", convert_types: false)
121
+ .new(file, default_section: '[global]', convert_types: false)
120
122
  .tap(&:parse)
121
123
  end
122
124
 
@@ -22,7 +22,7 @@ module CLI
22
22
  end
23
23
 
24
24
  def handle_exception(error)
25
- if notify_with = exception_for_submission(error)
25
+ if (notify_with = exception_for_submission(error))
26
26
  logs = begin
27
27
  File.read(@log_file)
28
28
  rescue => e
@@ -46,7 +46,7 @@ module CLI
46
46
  when CLI::Kit::Abort, CLI::Kit::AbortSilent # Not a bug
47
47
  nil
48
48
  when SignalException
49
- skip = %w(SIGTERM SIGHUP SIGINT)
49
+ skip = ['SIGTERM', 'SIGHUP', 'SIGINT']
50
50
  skip.include?(error.message) ? nil : error
51
51
  when SystemExit # "exit N" called
52
52
  case error.status
@@ -56,7 +56,7 @@ module CLI
56
56
  # if it was `exit 30`, translate the exit code to 1, and submit nothing.
57
57
  # 30 is used to signal normal failures that are not indicative of bugs.
58
58
  # However, users should see it presented as 1.
59
- exit 1
59
+ exit(1)
60
60
  else
61
61
  # A weird termination status happened. `error.exception "message"` will maintain backtrace
62
62
  # but allow us to set a message
@@ -83,18 +83,24 @@ module CLI
83
83
 
84
84
  CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
85
85
  rescue Interrupt
86
- $stderr.puts(format_error_message("Interrupt"))
86
+ stderr_puts_message('Interrupt')
87
87
  CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
88
88
  rescue Errno::ENOSPC
89
89
  message = if @tool_name
90
90
  "Your disk is full - {{command:#{@tool_name}}} requires free space to operate"
91
91
  else
92
- "Your disk is full - free space is required to operate"
92
+ 'Your disk is full - free space is required to operate'
93
93
  end
94
- $stderr.puts(format_error_message(message))
94
+ stderr_puts_message(message)
95
95
  CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
96
96
  end
97
97
 
98
+ def stderr_puts_message(message)
99
+ $stderr.puts(format_error_message(message))
100
+ rescue Errno::EPIPE
101
+ nil
102
+ end
103
+
98
104
  def exception_reporter
99
105
  if @exception_reporter_or_proc.respond_to?(:report)
100
106
  @exception_reporter_or_proc
@@ -13,19 +13,17 @@ module CLI
13
13
  def call(command, command_name, args)
14
14
  with_traps do
15
15
  with_logging do |id|
16
+ command.call(args, command_name)
17
+ rescue => e
16
18
  begin
17
- command.call(args, command_name)
18
- rescue => e
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
27
- raise 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'
21
+ rescue SystemCallError
22
+ # Outputting to stderr is best-effort. Avoid raising another error when outputting debug info so that
23
+ # we can detect and log the original error, which may even be the source of this error.
24
+ nil
28
25
  end
26
+ raise e
29
27
  end
30
28
  end
31
29
  end
@@ -41,11 +39,9 @@ module CLI
41
39
  end
42
40
  end
43
41
 
44
- def with_traps
42
+ def with_traps(&block)
45
43
  twrap('QUIT', method(:quit_handler)) do
46
- twrap('INFO', method(:info_handler)) do
47
- yield
48
- end
44
+ twrap('INFO', method(:info_handler), &block)
49
45
  end
50
46
  end
51
47
 
@@ -53,10 +49,17 @@ module CLI
53
49
  return yield unless Signal.list.key?(signal)
54
50
 
55
51
  begin
56
- prev_handler = trap(signal, handler)
52
+ begin
53
+ prev_handler = trap(signal, handler)
54
+ installed = true
55
+ rescue ArgumentError
56
+ # If we couldn't install a signal handler because the signal is
57
+ # reserved, remember not to uninstall it later.
58
+ installed = false
59
+ end
57
60
  yield
58
61
  ensure
59
- trap(signal, prev_handler)
62
+ trap(signal, prev_handler) if installed
60
63
  end
61
64
  end
62
65
 
data/lib/cli/kit/ini.rb CHANGED
@@ -61,11 +61,11 @@ module CLI
61
61
  private
62
62
 
63
63
  def to_ini(h, git_format: false)
64
- optional_tab = git_format ? "\t" : ""
64
+ optional_tab = git_format ? "\t" : ''
65
65
  str = []
66
66
  h.each do |k, v|
67
67
  if section_designator?(k)
68
- str << "" unless str.empty? || git_format
68
+ str << '' unless str.empty? || git_format
69
69
  str << k
70
70
  str << to_ini(v, git_format: git_format)
71
71
  else
@@ -10,9 +10,10 @@ module CLI
10
10
  # Constructor for CLI::Kit::Logger
11
11
  #
12
12
  # @param debug_log_file [String] path to the file where debug logs should be stored
13
- def initialize(debug_log_file:)
13
+ def initialize(debug_log_file:, env_debug_name: 'DEBUG')
14
14
  FileUtils.mkpath(File.dirname(debug_log_file))
15
15
  @debug_logger = ::Logger.new(debug_log_file, MAX_NUM_LOGS, MAX_LOG_SIZE)
16
+ @env_debug_name = env_debug_name
16
17
  end
17
18
 
18
19
  # Functionally equivalent to Logger#info
@@ -60,7 +61,7 @@ module CLI
60
61
  #
61
62
  # @param msg [String] the message to log
62
63
  def debug(msg)
63
- $stdout.puts CLI::UI.fmt(msg) if ENV['DEBUG']
64
+ $stdout.puts CLI::UI.fmt(msg) if debug?
64
65
  @debug_logger.debug(format_debug(msg))
65
66
  end
66
67
 
@@ -71,6 +72,11 @@ module CLI
71
72
  return msg unless CLI::UI::StdoutRouter.current_id
72
73
  "[#{CLI::UI::StdoutRouter.current_id[:id]}] #{msg}"
73
74
  end
75
+
76
+ def debug?
77
+ val = ENV[@env_debug_name]
78
+ val && val != '0' && val != ''
79
+ end
74
80
  end
75
81
  end
76
82
  end
@@ -25,7 +25,7 @@ module CLI
25
25
  private
26
26
 
27
27
  def command_not_found(name)
28
- CLI::UI::Frame.open("Command not found", color: :red, timing: false) do
28
+ CLI::UI::Frame.open('Command not found', color: :red, timing: false) do
29
29
  $stderr.puts(CLI::UI.fmt("{{command:#{@tool_name} #{name}}} was not found"))
30
30
  end
31
31
 
@@ -43,7 +43,7 @@ module CLI
43
43
 
44
44
  # If we have any matches left, tell the user
45
45
  if possible_matches.any?
46
- CLI::UI::Frame.open("{{bold:Did you mean?}}", timing: false, color: :blue) do
46
+ CLI::UI::Frame.open('{{bold:Did you mean?}}', timing: false, color: :blue) do
47
47
  possible_matches.each do |possible_match|
48
48
  $stderr.puts CLI::UI.fmt("{{command:#{@tool_name} #{possible_match}}}")
49
49
  end
@@ -10,7 +10,7 @@ module CLI
10
10
  def assert_all_commands_run(should_raise: true)
11
11
  errors = CLI::Kit::System.error_message
12
12
  CLI::Kit::System.reset!
13
- assert false, errors if should_raise && !errors.nil?
13
+ assert(false, errors) if should_raise && !errors.nil?
14
14
  errors
15
15
  end
16
16
 
@@ -134,8 +134,8 @@ module CLI
134
134
  #
135
135
  # Note: Must set allow or success
136
136
  #
137
- def fake(*a, stdout: "", stderr: "", allow: nil, success: nil, sudo: false, env: {})
138
- raise ArgumentError, "success or allow must be set" if success.nil? && allow.nil?
137
+ def fake(*a, stdout: '', stderr: '', allow: nil, success: nil, sudo: false, env: {})
138
+ raise ArgumentError, 'success or allow must be set' if success.nil? && allow.nil?
139
139
 
140
140
  @delegate_open3 ||= {}
141
141
  @delegate_open3[a.join(' ')] = {
@@ -196,22 +196,22 @@ module CLI
196
196
 
197
197
  unless errors[:unexpected].empty?
198
198
  final_error << CLI::UI.fmt(<<~EOF)
199
- {{bold:Unexpected command invocations:}}
200
- {{command:#{errors[:unexpected].join("\n")}}}
199
+ {{bold:Unexpected command invocations:}}
200
+ {{command:#{errors[:unexpected].join("\n")}}}
201
201
  EOF
202
202
  end
203
203
 
204
204
  unless errors[:not_run].empty?
205
205
  final_error << CLI::UI.fmt(<<~EOF)
206
- {{bold:Expected commands were not run:}}
207
- {{command:#{errors[:not_run].join("\n")}}}
206
+ {{bold:Expected commands were not run:}}
207
+ {{command:#{errors[:not_run].join("\n")}}}
208
208
  EOF
209
209
  end
210
210
 
211
211
  unless errors[:other].empty?
212
212
  final_error << CLI::UI.fmt(<<~EOF)
213
- {{bold:Commands were not run as expected:}}
214
- #{errors[:other].map { |cmd, msg| "{{command:#{cmd}}}\n#{msg}" }.join("\n\n")}
213
+ {{bold:Commands were not run as expected:}}
214
+ #{errors[:other].map { |cmd, msg| "{{command:#{cmd}}}\n#{msg}" }.join("\n\n")}
215
215
  EOF
216
216
  end
217
217
 
@@ -6,7 +6,7 @@ require 'English'
6
6
  module CLI
7
7
  module Kit
8
8
  module System
9
- SUDO_PROMPT = CLI::UI.fmt("{{info:(sudo)}} Password: ")
9
+ SUDO_PROMPT = CLI::UI.fmt('{{info:(sudo)}} Password: ')
10
10
  class << self
11
11
  # Ask for sudo access with a message explaning the need for it
12
12
  # Will make subsequent commands capable of running with sudo for a period of time
@@ -19,7 +19,7 @@ module CLI
19
19
  #
20
20
  def sudo_reason(msg)
21
21
  # See if sudo has a cached password
22
- `env SUDO_ASKPASS=/usr/bin/false sudo -A true`
22
+ %x(env SUDO_ASKPASS=/usr/bin/false sudo -A true)
23
23
  return if $CHILD_STATUS.success?
24
24
  CLI::UI.with_frame_color(:blue) do
25
25
  puts(CLI::UI.fmt("{{i}} #{msg}"))
@@ -90,6 +90,18 @@ module CLI
90
90
  delegate_open3(*a, sudo: sudo, env: env, method: :capture3, **kwargs)
91
91
  end
92
92
 
93
+ def popen2(*a, sudo: false, env: ENV, **kwargs, &block)
94
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen2, **kwargs, &block)
95
+ end
96
+
97
+ def popen2e(*a, sudo: false, env: ENV, **kwargs, &block)
98
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen2e, **kwargs, &block)
99
+ end
100
+
101
+ def popen3(*a, sudo: false, env: ENV, **kwargs, &block)
102
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen3, **kwargs, &block)
103
+ end
104
+
93
105
  # Execute a command in the user's environment
94
106
  # Outputs result of the command without capturing it
95
107
  #
@@ -100,7 +112,7 @@ module CLI
100
112
  # - `**kwargs`: additional keyword arguments to pass to Process.spawn
101
113
  #
102
114
  # #### Returns
103
- # - `status`: boolean success status of the command execution
115
+ # - `status`: The `Process:Status` result for the command execution
104
116
  #
105
117
  # #### Usage
106
118
  # `stat = CLI::Kit::System.system('ls', 'a_folder')`
@@ -116,11 +128,15 @@ module CLI
116
128
  err_w.close
117
129
 
118
130
  handlers = if block_given?
119
- { out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
120
- err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) } }
131
+ {
132
+ out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
133
+ err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) },
134
+ }
121
135
  else
122
- { out_r => ->(data) { STDOUT.write(data) },
123
- err_r => ->(data) { STDOUT.write(data) } }
136
+ {
137
+ out_r => ->(data) { STDOUT.write(data) },
138
+ err_r => ->(data) { STDOUT.write(data) },
139
+ }
124
140
  end
125
141
 
126
142
  previous_trailing = Hash.new('')
@@ -130,13 +146,11 @@ module CLI
130
146
 
131
147
  readers, = IO.select(ios)
132
148
  readers.each do |io|
133
- begin
134
- data, trailing = split_partial_characters(io.readpartial(4096))
135
- handlers[io].call(previous_trailing[io] + data)
136
- previous_trailing[io] = trailing
137
- rescue IOError
138
- io.close
139
- end
149
+ data, trailing = split_partial_characters(io.readpartial(4096))
150
+ handlers[io].call(previous_trailing[io] + data)
151
+ previous_trailing[io] = trailing
152
+ rescue IOError
153
+ io.close
140
154
  end
141
155
  end
142
156
 
@@ -163,6 +177,14 @@ module CLI
163
177
  [data.byteslice(0...partial_character_index), data.byteslice(partial_character_index..-1)]
164
178
  end
165
179
 
180
+ def os
181
+ return :mac if /darwin/.match(RUBY_PLATFORM)
182
+ return :linux if /linux/.match(RUBY_PLATFORM)
183
+ return :windows if /mingw32/.match(RUBY_PLATFORM)
184
+
185
+ raise "Could not determine OS from platform #{RUBY_PLATFORM}"
186
+ end
187
+
166
188
  private
167
189
 
168
190
  def apply_sudo(*a, sudo)
@@ -171,11 +193,11 @@ module CLI
171
193
  a
172
194
  end
173
195
 
174
- def delegate_open3(*a, sudo: raise, env: raise, method: raise, **kwargs)
196
+ def delegate_open3(*a, sudo: raise, env: raise, method: raise, **kwargs, &block)
175
197
  a = apply_sudo(*a, sudo)
176
- Open3.send(method, env, *resolve_path(a, env), **kwargs)
198
+ Open3.send(method, env, *resolve_path(a, env), **kwargs, &block)
177
199
  rescue Errno::EINTR
178
- raise(Errno::EINTR, "command interrupted: #{a.join(' ')}")
200
+ raise(Errno::EINTR, "command interrupted: #{a.join(" ")}")
179
201
  end
180
202
 
181
203
  # Ruby resolves the program to execute using its own PATH, but we want it to
@@ -189,17 +211,30 @@ module CLI
189
211
  # See https://github.com/Shopify/dev/pull/625 for more details.
190
212
  def resolve_path(a, env)
191
213
  # If only one argument was provided, make sure it's interpreted by a shell.
192
- return ["true ; " + a[0]] if a.size == 1
214
+ if a.size == 1
215
+ if os == :windows
216
+ return ['break && ' + a[0]]
217
+ else
218
+ return ['true ; ' + a[0]]
219
+ end
220
+ end
193
221
  return a if a.first.include?('/')
194
222
 
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)
223
+ item = which(a.first, env)
224
+ a[0] = item if item
225
+ a
226
+ end
227
+
228
+ def which(cmd, env)
229
+ exts = os == :windows ? env.fetch('PATHEXT').split(';') : ['']
230
+ env.fetch('PATH', '').split(File::PATH_SEPARATOR).each do |path|
231
+ exts.each do |ext|
232
+ exe = File.join(path, "#{cmd}#{ext}")
233
+ return exe if File.executable?(exe) && !File.directory?(exe)
234
+ end
199
235
  end
200
236
 
201
- a[0] = "#{item}/#{a.first}" if item
202
- a
237
+ nil
203
238
  end
204
239
  end
205
240
  end
data/lib/cli/kit/util.rb CHANGED
@@ -2,7 +2,7 @@ module CLI
2
2
  module Kit
3
3
  module Util
4
4
  class << self
5
- def snake_case(camel_case, seperator = "_")
5
+ def snake_case(camel_case, seperator = '_')
6
6
  camel_case.to_s # MyCoolThing::MyAPIModule
7
7
  .gsub(/::/, '/') # MyCoolThing/MyAPIModule
8
8
  .gsub(/([A-Z]+)([A-Z][a-z])/, "\\1#{seperator}\\2") # MyCoolThing::MyAPI_Module
@@ -43,15 +43,15 @@ module CLI
43
43
  # non-empty line in the whole string
44
44
  #
45
45
  def strip_heredoc(str)
46
- str.gsub(/^#{str.scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
46
+ str.gsub(/^#{str.scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
47
47
  end
48
48
 
49
49
  # Joins an array with commas and "and", using the Oxford comma.
50
50
  def english_join(array)
51
- return "" if array.nil?
52
- return array.join(" and ") if array.length < 3
51
+ return '' if array.nil?
52
+ return array.join(' and ') if array.length < 3
53
53
 
54
- "#{array[0..-2].join(', ')}, and #{array[-1]}"
54
+ "#{array[0..-2].join(", ")}, and #{array[-1]}"
55
55
  end
56
56
 
57
57
  # Execute a block within the context of a variable enviroment
@@ -77,15 +77,15 @@ module CLI
77
77
  # Converts a number to a human readable format on the SI scale
78
78
  #
79
79
  def to_si_scale(number, unit = '', factor: 1000, precision: 2, space: false)
80
- raise ArgumentError, "factor should only be 1000 or 1024" unless [1000, 1024].include?(factor)
80
+ raise ArgumentError, 'factor should only be 1000 or 1024' unless [1000, 1024].include?(factor)
81
81
 
82
- small_scale = %w(m µ n p f a z y)
83
- big_scale = %w(k M G T P E Z Y)
82
+ small_scale = ['m', 'µ', 'n', 'p', 'f', 'a', 'z', 'y']
83
+ big_scale = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
84
84
  negative = number < 0
85
85
  number = number.abs.to_f
86
86
 
87
87
  if number == 0 || number.between?(1, factor)
88
- prefix = ""
88
+ prefix = ''
89
89
  scale = 0
90
90
  else
91
91
  scale = Math.log(number, factor).floor
@@ -107,7 +107,7 @@ module CLI
107
107
  fnum = fnum.to_i if (fnum.to_i.to_f * divider) == number
108
108
 
109
109
  fnum = -fnum if negative
110
- prefix = " " + prefix if space
110
+ prefix = ' ' + prefix if space
111
111
 
112
112
  "#{fnum}#{prefix}#{unit}"
113
113
  end
@@ -1,5 +1,5 @@
1
1
  module CLI
2
2
  module Kit
3
- VERSION = "3.3.0"
3
+ VERSION = '4.0.0'
4
4
  end
5
5
  end
data/lib/cli/kit.rb CHANGED
@@ -51,7 +51,7 @@ module CLI
51
51
  # 1. rescue Abort or Bug
52
52
  # 2. Print a contextualized error message
53
53
  # 3. Re-raise AbortSilent or BugSilent respectively.
54
- GenericAbort = Class.new(Exception)
54
+ GenericAbort = Class.new(Exception) # rubocop:disable Lint/InheritException
55
55
  Abort = Class.new(GenericAbort)
56
56
  Bug = Class.new(GenericAbort)
57
57
  BugSilent = Class.new(GenericAbort)
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.3.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  - Aaron Olson
9
9
  - Lisa Ugray
10
- autorequire:
10
+ autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2019-06-13 00:00:00.000000000 Z
13
+ date: 2021-11-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: cli-ui
@@ -32,42 +32,42 @@ dependencies:
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '1.15'
35
+ version: '2.1'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '1.15'
42
+ version: '2.1'
43
43
  - !ruby/object:Gem::Dependency
44
- name: rake
44
+ name: minitest
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '10.0'
49
+ version: '5.0'
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: '10.0'
56
+ version: '5.0'
57
57
  - !ruby/object:Gem::Dependency
58
- name: minitest
58
+ name: rake
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '5.0'
63
+ version: '13.0'
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '5.0'
70
+ version: '13.0'
71
71
  description: Terminal UI framework extensions
72
72
  email:
73
73
  - burke.libbey@shopify.com
@@ -79,10 +79,11 @@ extensions: []
79
79
  extra_rdoc_files: []
80
80
  files:
81
81
  - ".github/CODEOWNERS"
82
+ - ".github/dependabot.yml"
82
83
  - ".github/probots.yml"
84
+ - ".github/workflows/ruby.yml"
83
85
  - ".gitignore"
84
86
  - ".rubocop.yml"
85
- - ".travis.yml"
86
87
  - Gemfile
87
88
  - Gemfile.lock
88
89
  - LICENSE.txt
@@ -142,7 +143,7 @@ homepage: https://github.com/shopify/cli-kit
142
143
  licenses:
143
144
  - MIT
144
145
  metadata: {}
145
- post_install_message:
146
+ post_install_message:
146
147
  rdoc_options: []
147
148
  require_paths:
148
149
  - lib
@@ -157,8 +158,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
158
  - !ruby/object:Gem::Version
158
159
  version: '0'
159
160
  requirements: []
160
- rubygems_version: 3.0.2
161
- signing_key:
161
+ rubygems_version: 3.2.20
162
+ signing_key:
162
163
  specification_version: 4
163
164
  summary: Terminal UI framework extensions
164
165
  test_files: []
data/.travis.yml DELETED
@@ -1,14 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.3.3
6
- - 2.5.0
7
- os:
8
- - linux
9
- - osx
10
- script:
11
- - bin/testunit
12
- - git clone https://github.com/Shopify/cli-ui.git
13
- - DEPS=vendor bin/test_gen
14
- - DEPS=bundler bin/test_gen