cli-kit 3.3.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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