cli-kit 3.0.0 → 3.0.1
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 +4 -4
- data/.rubocop.yml +4 -0
- data/.travis.yml +10 -1
- data/Gemfile.lock +3 -3
- data/README.md +102 -4
- data/Rakefile +1 -0
- data/bin/test_gen +28 -0
- data/gen/lib/gen/commands/help.rb +1 -1
- data/gen/lib/gen/generator.rb +3 -1
- data/gen/template/Gemfile +6 -0
- data/gen/template/bin/testunit +23 -0
- data/gen/template/bin/update-deps +2 -1
- data/gen/template/test/example_test.rb +15 -0
- data/gen/template/test/test_helper.rb +21 -0
- data/lib/cli/kit.rb +1 -0
- data/lib/cli/kit/command_registry.rb +1 -1
- data/lib/cli/kit/config.rb +21 -0
- data/lib/cli/kit/error_handler.rb +39 -34
- data/lib/cli/kit/executor.rb +8 -4
- data/lib/cli/kit/ini.rb +9 -4
- data/lib/cli/kit/support.rb +9 -0
- data/lib/cli/kit/support/test_helper.rb +226 -0
- data/lib/cli/kit/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6c63b562d9e105af0a13a110ad973bbb56d40a4
|
4
|
+
data.tar.gz: f89728ea2fd0f05ed1c9b6f16dbad900c4bc5595
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7088f3a7a238311cb8aaad7e83f74611ef30f2cbe0db5bbdc393d6c7da7ca51e68d54884c73707ea9751644c9ebdec6156829f939f4cf65044b5a690b053450a
|
7
|
+
data.tar.gz: 19fbec7a9eaee07f188c69f1d776404227f554dc38e44ff08bd2160026a45aac0e843526703ba0a1312bbcb853d2496bd5b5bf2b41d232f1ac033353b05b83c3
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
|
+
cache: bundler
|
3
4
|
rvm:
|
4
5
|
- 2.3.3
|
5
|
-
|
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
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cli-kit (3.0.
|
4
|
+
cli-kit (3.0.1)
|
5
5
|
cli-ui (>= 1.1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -11,7 +11,7 @@ GEM
|
|
11
11
|
ast (2.3.0)
|
12
12
|
builder (3.2.3)
|
13
13
|
byebug (9.0.6)
|
14
|
-
cli-ui (1.1.
|
14
|
+
cli-ui (1.1.1)
|
15
15
|
metaclass (0.0.4)
|
16
16
|
method_source (0.8.2)
|
17
17
|
minitest (5.10.2)
|
@@ -54,4 +54,4 @@ DEPENDENCIES
|
|
54
54
|
rubocop
|
55
55
|
|
56
56
|
BUNDLED WITH
|
57
|
-
1.16.
|
57
|
+
1.16.1
|
data/README.md
CHANGED
@@ -11,8 +11,106 @@
|
|
11
11
|
to build a number of internal developer tools, along with
|
12
12
|
[cli-ui](https://github.com/shopify/cli-ui).
|
13
13
|
|
14
|
-
##
|
14
|
+
## Getting Started
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
To begin creating your first `cli-kit` application, run:
|
17
|
+
```bash
|
18
|
+
gem install cli-kit
|
19
|
+
cli-kit new myproject
|
20
|
+
```
|
21
|
+
|
22
|
+
Where `myproject` is the name of the application you wish to create. Then, you will be prompted to
|
23
|
+
select how the project consumes `cli-kit` and `cli-ui`. The available options are:
|
24
|
+
- Vendor (faster execution, more difficult to update dependencies)
|
25
|
+
- Bundler (slower execution, easier dependency management)
|
26
|
+
|
27
|
+
You're now ready to write your very first `cli-kit` application!
|
28
|
+
|
29
|
+
## How do `cli-kit` Applications Work?
|
30
|
+
|
31
|
+
The executable for your `cli-kit` app is stored in the "exe" directory. To execute the app, simply
|
32
|
+
run:
|
33
|
+
```bash
|
34
|
+
./exe/myproject
|
35
|
+
```
|
36
|
+
|
37
|
+
### Folder Structure
|
38
|
+
* `/exe/` - Location of the executables for your application.
|
39
|
+
* `/lib/` - Location of the resources for your app (modules, classes, helpers, etc).
|
40
|
+
* `myproject.rb` - This file is the starting point for where to look for all other files. It
|
41
|
+
configures autoload and autocall for the app.
|
42
|
+
* `myproject/` - Stores the various commands/entry points.
|
43
|
+
* `entry_point.rb` - This is the file that is first called from the executable. It handles
|
44
|
+
loading and commands.
|
45
|
+
* `commands.rb` - Registers the various commands that your application is able to handle.
|
46
|
+
* `commands/` - Stores Ruby files for each command (help, new, add, etc).
|
47
|
+
|
48
|
+
## Adding a New Command to your App
|
49
|
+
|
50
|
+
### Registering the Command
|
51
|
+
|
52
|
+
Let's say that you'd like your program to be able to handle a specific task, and you'd like to
|
53
|
+
_register_ a new handler for the command for that task, like `myproject add` to add 2 numbers, like
|
54
|
+
in a calculator app.
|
55
|
+
To do this, open `/lib/myproject/commands.rb`. Then, add a new line into the module, like this:
|
56
|
+
```ruby
|
57
|
+
register :Add, 'add', 'myproject/commands/add'
|
58
|
+
```
|
59
|
+
|
60
|
+
The format for this is `register :<CommandClass>, '<command-at-cli>', '<path/to/command.rb>'`
|
61
|
+
|
62
|
+
### Creating the Command Action
|
63
|
+
|
64
|
+
The action for a specific command is stored in its own Ruby file, in the `/lib/myproject/commands/`
|
65
|
+
directory. Here is an example of the `add` command in our previous to-do app example:
|
66
|
+
```ruby
|
67
|
+
require 'myproject'
|
68
|
+
|
69
|
+
module Myproject
|
70
|
+
module Commands
|
71
|
+
class Add < Myproject::Command
|
72
|
+
def call(args, _name)
|
73
|
+
# command action goes here
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.help
|
77
|
+
# help or instructions go here
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
```
|
84
|
+
|
85
|
+
The `call(args, _name)` method is what actually runs when the `myproject add` command is executed.
|
86
|
+
|
87
|
+
- **Note:** The `args` parameter represents all the arguments the user has specified.
|
88
|
+
|
89
|
+
Let's say that you are trying to compute the sum of 2 numbers that the user has specified as
|
90
|
+
arguments. For example:
|
91
|
+
```ruby
|
92
|
+
def call(args, _name)
|
93
|
+
sum = args.map(&:to_i).inject(&:+)
|
94
|
+
puts sum
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
### Getting Help
|
99
|
+
|
100
|
+
Above, you'll notice that we also have a `self.help` method. This method is what runs when the user
|
101
|
+
has incorrectly used the command, or has requested help. For example:
|
102
|
+
```ruby
|
103
|
+
def self.help
|
104
|
+
"Print the sum of 2 numbers.\nUsage: {{command:#{Myproject::TOOL_NAME} add}} 5 7"
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
## User Interfaces
|
109
|
+
|
110
|
+
`cli-kit` also features `cli-ui`, another gem from us here at Shopify, which allows for the use of
|
111
|
+
powerful command-line user interface elements. For more details on how to use `cli-ui`, visit the
|
112
|
+
[`cli-ui`](https://github.com/Shopify/cli-ui) repo.
|
113
|
+
|
114
|
+
## Examples
|
115
|
+
|
116
|
+
- [A Simple To-Do App](https://github.com/Shopify/cli-kit-example)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/test_gen
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Make sure we're in the cli kit directory
|
4
|
+
CURR_DIR=$(dirname "$0")
|
5
|
+
cd $CURR_DIR
|
6
|
+
cd ../
|
7
|
+
|
8
|
+
# Clean up
|
9
|
+
function finish {
|
10
|
+
cd $CURR_DIR
|
11
|
+
cd ../../
|
12
|
+
rm -rf myapp
|
13
|
+
}
|
14
|
+
trap finish EXIT
|
15
|
+
|
16
|
+
# Generate app and move up a level to be at the same level as cli-kit
|
17
|
+
bundle exec ruby exe/cli-kit new myapp
|
18
|
+
mv myapp ../
|
19
|
+
cd ../myapp
|
20
|
+
|
21
|
+
# Test
|
22
|
+
bundle install
|
23
|
+
bin/testunit
|
24
|
+
|
25
|
+
if [[ $DEPS == 'vendor' ]]; then
|
26
|
+
git clone https://github.com/Shopify/cli-ui.git ../cli-ui
|
27
|
+
bin/update-deps
|
28
|
+
fi
|
data/gen/lib/gen/generator.rb
CHANGED
@@ -27,7 +27,6 @@ module Gen
|
|
27
27
|
private_constant :VENDOR_TRANSLATIONS
|
28
28
|
|
29
29
|
BUNDLER_TRANSLATIONS = {
|
30
|
-
'bin' => false,
|
31
30
|
'bin/update-deps' => false,
|
32
31
|
'exe/__app__-gems' => 'exe/__app__',
|
33
32
|
'exe/__app__-vendor' => false,
|
@@ -59,6 +58,9 @@ module Gen
|
|
59
58
|
private
|
60
59
|
|
61
60
|
def ask_vendor?
|
61
|
+
return 'vendor' if ENV['DEPS'] == 'vendor'
|
62
|
+
return 'bundler' if ENV['DEPS'] == 'bundler'
|
63
|
+
|
62
64
|
vendor = nil
|
63
65
|
CLI::UI::Frame.open('Configuration') do
|
64
66
|
q = 'How would you like the application to consume {{command:cli-kit}} and {{command:cli-ui}}?'
|
data/gen/template/Gemfile
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
root = File.expand_path('../..', __FILE__)
|
7
|
+
TEST_ROOT = root + '/test'
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift(TEST_ROOT)
|
10
|
+
|
11
|
+
def test_files
|
12
|
+
Dir.glob(TEST_ROOT + "/**/*_test.rb")
|
13
|
+
end
|
14
|
+
|
15
|
+
if ARGV.empty?
|
16
|
+
test_files.each { |f| require(f) }
|
17
|
+
exit 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# A list of files is presumed to be specified
|
21
|
+
ARGV.each do |a|
|
22
|
+
require a.sub(%r{^test/}, '')
|
23
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift(File.expand_path("../../vendor/deps/cli-ui/lib", __FILE__))
|
4
4
|
require 'open3'
|
@@ -39,6 +39,7 @@ deps.each do |dep|
|
|
39
39
|
bail(
|
40
40
|
"dependency is not checked out: {{yellow:#{dep}}}.\n" \
|
41
41
|
" This repo {{bold_blue:(github.com/shopify/#{dep})}} must be cloned at {{bold_blue:#{path}}} for this script to succeed.\n" \
|
42
|
+
" Currently, SOURCE_ROOT is set to {{bold_blue:#{source_path}}}.\n" \
|
42
43
|
" Alternatively, you can set {{bold_blue:SOURCE_ROOT}} to a directory containing {{yellow:#{dep}}}.\n" \
|
43
44
|
" {{bold_blue:SOURCE_ROOT}} defaults to {{bold_blue:../}}."
|
44
45
|
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module __App__
|
4
|
+
class ExampleTest < MiniTest::Test
|
5
|
+
include CLI::Kit::Support::TestHelper
|
6
|
+
|
7
|
+
def test_example
|
8
|
+
CLI::Kit::System.fake("ls -al", stdout: "a\nb", success: true)
|
9
|
+
assert_all_commands_run do
|
10
|
+
out, = CLI::Kit::System.capture2('ls', '-al')
|
11
|
+
assert_equal ['a', 'b'], out.split("\n")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
addpath = lambda do |p|
|
3
|
+
path = File.expand_path("../../#{p}", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
|
5
|
+
end
|
6
|
+
addpath.call("lib")
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'cli/kit'
|
10
|
+
|
11
|
+
require 'fileutils'
|
12
|
+
require 'tmpdir'
|
13
|
+
require 'tempfile'
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'bundler/setup'
|
17
|
+
|
18
|
+
CLI::UI::StdoutRouter.enable
|
19
|
+
|
20
|
+
require 'minitest/autorun'
|
21
|
+
require 'mocha/mini_test'
|
data/lib/cli/kit.rb
CHANGED
data/lib/cli/kit/config.rb
CHANGED
@@ -43,6 +43,27 @@ module CLI
|
|
43
43
|
write_config
|
44
44
|
end
|
45
45
|
|
46
|
+
# Unsets a config value in the config file
|
47
|
+
#
|
48
|
+
# #### Parameters
|
49
|
+
# `section` : the section of the config you are deleting
|
50
|
+
# `name` : the name of the config you are deleting
|
51
|
+
#
|
52
|
+
# #### Example Usage
|
53
|
+
# `config.unset('section', 'name.of.config')`
|
54
|
+
#
|
55
|
+
def unset(section, name)
|
56
|
+
set(section, name, nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Gets the hash for the entire section
|
60
|
+
#
|
61
|
+
# #### Parameters
|
62
|
+
# `section` : the section of the config you are getting
|
63
|
+
#
|
64
|
+
# #### Example Usage
|
65
|
+
# `config.get_section('section')`
|
66
|
+
#
|
46
67
|
def get_section(section)
|
47
68
|
(all_configs["[#{section}]"] || {}).dup
|
48
69
|
end
|
@@ -20,43 +20,37 @@ module CLI
|
|
20
20
|
handle_abort(&block)
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
def handle_exception(error)
|
24
|
+
if notify_with = exception_for_submission(error)
|
25
|
+
logs = begin
|
26
|
+
File.read(@log_file)
|
27
|
+
rescue => e
|
28
|
+
"(#{e.class}: #{e.message})"
|
29
|
+
end
|
30
|
+
exception_reporter.report(notify_with, logs)
|
31
|
+
end
|
27
32
|
end
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
CLI::Kit::EXIT_SUCCESS
|
32
|
-
rescue CLI::Kit::GenericAbort => e
|
33
|
-
is_bug = e.is_a?(CLI::Kit::Bug) || e.is_a?(CLI::Kit::BugSilent)
|
34
|
-
is_silent = e.is_a?(CLI::Kit::AbortSilent) || e.is_a?(CLI::Kit::BugSilent)
|
35
|
-
|
36
|
-
print_error_message(e) unless is_silent
|
37
|
-
(@exception = e) if is_bug
|
38
|
-
|
39
|
-
CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
40
|
-
rescue Interrupt
|
41
|
-
$stderr.puts(format_error_message("Interrupt"))
|
42
|
-
return CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
43
|
-
end
|
34
|
+
# maybe we can get rid of this.
|
35
|
+
attr_writer :exception
|
44
36
|
|
45
|
-
|
46
|
-
notify_with = nil
|
37
|
+
private
|
47
38
|
|
39
|
+
def exception_for_submission(error)
|
48
40
|
case error
|
49
41
|
when nil # normal, non-error termination
|
42
|
+
nil
|
50
43
|
when Interrupt # ctrl-c
|
44
|
+
nil
|
51
45
|
when CLI::Kit::Abort, CLI::Kit::AbortSilent # Not a bug
|
46
|
+
nil
|
52
47
|
when SignalException
|
53
48
|
skip = %w(SIGTERM SIGHUP SIGINT)
|
54
|
-
|
55
|
-
notify_with = error
|
56
|
-
end
|
49
|
+
skip.include?(error.message) ? nil : error
|
57
50
|
when SystemExit # "exit N" called
|
58
51
|
case error.status
|
59
52
|
when CLI::Kit::EXIT_SUCCESS # submit nothing if it was `exit 0`
|
53
|
+
nil
|
60
54
|
when CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
61
55
|
# if it was `exit 30`, translate the exit code to 1, and submit nothing.
|
62
56
|
# 30 is used to signal normal failures that are not indicative of bugs.
|
@@ -65,20 +59,31 @@ module CLI
|
|
65
59
|
else
|
66
60
|
# A weird termination status happened. `error.exception "message"` will maintain backtrace
|
67
61
|
# but allow us to set a message
|
68
|
-
|
62
|
+
error.exception("abnormal termination status: #{error.status}")
|
69
63
|
end
|
70
64
|
else
|
71
|
-
|
65
|
+
error
|
72
66
|
end
|
67
|
+
end
|
73
68
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
69
|
+
def install!
|
70
|
+
at_exit { handle_exception(@exception || $ERROR_INFO) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def handle_abort
|
74
|
+
yield
|
75
|
+
CLI::Kit::EXIT_SUCCESS
|
76
|
+
rescue CLI::Kit::GenericAbort => e
|
77
|
+
is_bug = e.is_a?(CLI::Kit::Bug) || e.is_a?(CLI::Kit::BugSilent)
|
78
|
+
is_silent = e.is_a?(CLI::Kit::AbortSilent) || e.is_a?(CLI::Kit::BugSilent)
|
79
|
+
|
80
|
+
print_error_message(e) unless is_silent
|
81
|
+
(@exception = e) if is_bug
|
82
|
+
|
83
|
+
CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
84
|
+
rescue Interrupt
|
85
|
+
$stderr.puts(format_error_message("Interrupt"))
|
86
|
+
return CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
82
87
|
end
|
83
88
|
|
84
89
|
def exception_reporter
|
data/lib/cli/kit/executor.rb
CHANGED
@@ -28,10 +28,14 @@ module CLI
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def twrap(signal, handler)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
return yield unless Signal.list.key?(signal)
|
32
|
+
|
33
|
+
begin
|
34
|
+
prev_handler = trap(signal, handler)
|
35
|
+
yield
|
36
|
+
ensure
|
37
|
+
trap(signal, prev_handler)
|
38
|
+
end
|
35
39
|
end
|
36
40
|
|
37
41
|
def quit_handler(_sig)
|
data/lib/cli/kit/ini.rb
CHANGED
@@ -46,21 +46,26 @@ module CLI
|
|
46
46
|
@ini
|
47
47
|
end
|
48
48
|
|
49
|
+
def git_format
|
50
|
+
to_ini(@ini, git_format: true).flatten.join("\n")
|
51
|
+
end
|
52
|
+
|
49
53
|
def to_s
|
50
54
|
to_ini(@ini).flatten.join("\n")
|
51
55
|
end
|
52
56
|
|
53
57
|
private
|
54
58
|
|
55
|
-
def to_ini(h)
|
59
|
+
def to_ini(h, git_format: false)
|
60
|
+
optional_tab = git_format ? "\t" : ""
|
56
61
|
str = []
|
57
62
|
h.each do |k, v|
|
58
63
|
if section_designator?(k)
|
59
|
-
str << "" unless str.empty?
|
64
|
+
str << "" unless str.empty? || git_format
|
60
65
|
str << k
|
61
|
-
str << to_ini(v)
|
66
|
+
str << to_ini(v, git_format: git_format)
|
62
67
|
else
|
63
|
-
str << "#{k} = #{v}"
|
68
|
+
str << "#{optional_tab}#{k} = #{v}"
|
64
69
|
end
|
65
70
|
end
|
66
71
|
str
|
@@ -0,0 +1,226 @@
|
|
1
|
+
module CLI
|
2
|
+
module Kit
|
3
|
+
module Support
|
4
|
+
module TestHelper
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
CLI::Kit::System.reset!
|
8
|
+
end
|
9
|
+
|
10
|
+
def assert_all_commands_run(should_raise: true)
|
11
|
+
errors = CLI::Kit::System.error_message
|
12
|
+
CLI::Kit::System.reset!
|
13
|
+
assert false, errors if should_raise && !errors.nil?
|
14
|
+
errors
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
super
|
19
|
+
assert_all_commands_run
|
20
|
+
end
|
21
|
+
|
22
|
+
class FakeSuccess
|
23
|
+
def initialize(success)
|
24
|
+
@success = success
|
25
|
+
end
|
26
|
+
|
27
|
+
def success?
|
28
|
+
@success
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ::CLI
|
33
|
+
module Kit
|
34
|
+
module System
|
35
|
+
class << self
|
36
|
+
alias_method :original_system, :system
|
37
|
+
def system(*a, sudo: false, env: {}, **kwargs)
|
38
|
+
expected_command = expected_command(*a, sudo: sudo, env: env)
|
39
|
+
|
40
|
+
# In the case of an unexpected command, expected_command will be nil
|
41
|
+
return FakeSuccess.new(false) if expected_command.nil?
|
42
|
+
|
43
|
+
# Otherwise handle the command
|
44
|
+
if expected_command[:allow]
|
45
|
+
original_system(*a, sudo: sudo, env: env, **kwargs)
|
46
|
+
else
|
47
|
+
FakeSuccess.new(expected_command[:success])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :original_capture2, :capture2
|
52
|
+
def capture2(*a, sudo: false, env: {}, **kwargs)
|
53
|
+
expected_command = expected_command(*a, sudo: sudo, env: env)
|
54
|
+
|
55
|
+
# In the case of an unexpected command, expected_command will be nil
|
56
|
+
return [nil, FakeSuccess.new(false)] if expected_command.nil?
|
57
|
+
|
58
|
+
# Otherwise handle the command
|
59
|
+
if expected_command[:allow]
|
60
|
+
original_capture2(*a, sudo: sudo, env: env, **kwargs)
|
61
|
+
else
|
62
|
+
[
|
63
|
+
expected_command[:stdout],
|
64
|
+
FakeSuccess.new(expected_command[:success]),
|
65
|
+
]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :original_capture2e, :capture2e
|
70
|
+
def capture2e(*a, sudo: false, env: {}, **kwargs)
|
71
|
+
expected_command = expected_command(*a, sudo: sudo, env: env)
|
72
|
+
|
73
|
+
# In the case of an unexpected command, expected_command will be nil
|
74
|
+
return [nil, FakeSuccess.new(false)] if expected_command.nil?
|
75
|
+
|
76
|
+
# Otherwise handle the command
|
77
|
+
if expected_command[:allow]
|
78
|
+
original_capture2ecapture2e(*a, sudo: sudo, env: env, **kwargs)
|
79
|
+
else
|
80
|
+
[
|
81
|
+
expected_command[:stdout],
|
82
|
+
FakeSuccess.new(expected_command[:success]),
|
83
|
+
]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
alias_method :original_capture3, :capture3
|
88
|
+
def capture3(*a, sudo: false, env: {}, **kwargs)
|
89
|
+
expected_command = expected_command(*a, sudo: sudo, env: env)
|
90
|
+
|
91
|
+
# In the case of an unexpected command, expected_command will be nil
|
92
|
+
return [nil, nil, FakeSuccess.new(false)] if expected_command.nil?
|
93
|
+
|
94
|
+
# Otherwise handle the command
|
95
|
+
if expected_command[:allow]
|
96
|
+
original_capture3(*a, sudo: sudo, env: env, **kwargs)
|
97
|
+
else
|
98
|
+
[
|
99
|
+
expected_command[:stdout],
|
100
|
+
expected_command[:stderr],
|
101
|
+
FakeSuccess.new(expected_command[:success]),
|
102
|
+
]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Sets up an expectation for a command and stubs out the call (unless allow is true)
|
107
|
+
#
|
108
|
+
# #### Parameters
|
109
|
+
# `*a` : the command, represented as a splat
|
110
|
+
# `stdout` : stdout to stub the command with (defaults to empty string)
|
111
|
+
# `stderr` : stderr to stub the command with (defaults to empty string)
|
112
|
+
# `allow` : allow determines if the command will be actually run, or stubbed. Defaults to nil (stub)
|
113
|
+
# `success` : success status to stub the command with (Defaults to nil)
|
114
|
+
# `sudo` : expectation of sudo being set or not (defaults to false)
|
115
|
+
# `env` : expectation of env being set or not (defaults to {})
|
116
|
+
#
|
117
|
+
# Note: Must set allow or success
|
118
|
+
#
|
119
|
+
def fake(*a, stdout: "", stderr: "", allow: nil, success: nil, sudo: false, env: {})
|
120
|
+
raise ArgumentError, "success or allow must be set" if success.nil? && allow.nil?
|
121
|
+
|
122
|
+
@delegate_open3 ||= {}
|
123
|
+
@delegate_open3[a.join(' ')] = {
|
124
|
+
expected: {
|
125
|
+
sudo: sudo,
|
126
|
+
env: env,
|
127
|
+
},
|
128
|
+
actual: {
|
129
|
+
sudo: nil,
|
130
|
+
env: nil,
|
131
|
+
},
|
132
|
+
stdout: stdout,
|
133
|
+
stderr: stderr,
|
134
|
+
allow: allow,
|
135
|
+
success: success,
|
136
|
+
run: false,
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
# Resets the faked commands
|
141
|
+
#
|
142
|
+
def reset!
|
143
|
+
@delegate_open3 = {}
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the errors associated to a test run
|
147
|
+
#
|
148
|
+
# #### Returns
|
149
|
+
# `errors` (String) a string representing errors found on this run, nil if none
|
150
|
+
def error_message
|
151
|
+
errors = {
|
152
|
+
unexpected: [],
|
153
|
+
not_run: [],
|
154
|
+
other: {},
|
155
|
+
}
|
156
|
+
|
157
|
+
@delegate_open3.each do |cmd, opts|
|
158
|
+
if opts[:unexpected]
|
159
|
+
errors[:unexpected] << cmd
|
160
|
+
elsif opts[:run]
|
161
|
+
error = []
|
162
|
+
|
163
|
+
if opts[:expected][:sudo] != opts[:actual][:sudo]
|
164
|
+
error << "- sudo was supposed to be #{opts[:expected][:sudo]} but was #{opts[:actual][:sudo]}"
|
165
|
+
end
|
166
|
+
|
167
|
+
if opts[:expected][:env] != opts[:actual][:env]
|
168
|
+
error << "- env was supposed to be #{opts[:expected][:env]} but was #{opts[:actual][:env]}"
|
169
|
+
end
|
170
|
+
|
171
|
+
errors[:other][cmd] = error.join("\n") unless error.empty?
|
172
|
+
else
|
173
|
+
errors[:not_run] << cmd
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
final_error = []
|
178
|
+
|
179
|
+
unless errors[:unexpected].empty?
|
180
|
+
final_error << CLI::UI.fmt(<<~EOF)
|
181
|
+
{{bold:Unexpected command invocations:}}
|
182
|
+
{{command:#{errors[:unexpected].join("\n")}}}
|
183
|
+
EOF
|
184
|
+
end
|
185
|
+
|
186
|
+
unless errors[:not_run].empty?
|
187
|
+
final_error << CLI::UI.fmt(<<~EOF)
|
188
|
+
{{bold:Expected commands were not run:}}
|
189
|
+
{{command:#{errors[:not_run].join("\n")}}}
|
190
|
+
EOF
|
191
|
+
end
|
192
|
+
|
193
|
+
unless errors[:other].empty?
|
194
|
+
final_error << CLI::UI.fmt(<<~EOF)
|
195
|
+
{{bold:Commands were not run as expected:}}
|
196
|
+
#{errors[:other].map { |cmd, msg| "{{command:#{cmd}}}\n#{msg}" }.join("\n\n")}
|
197
|
+
EOF
|
198
|
+
end
|
199
|
+
|
200
|
+
return nil if final_error.empty?
|
201
|
+
"\n" + final_error.join("\n") # Initial new line for formatting reasons
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
def expected_command(*a, sudo: raise, env: raise)
|
207
|
+
expected_cmd = @delegate_open3[a.join(' ')]
|
208
|
+
|
209
|
+
if expected_cmd.nil?
|
210
|
+
@delegate_open3[a.join(' ')] = { unexpected: true }
|
211
|
+
return nil
|
212
|
+
end
|
213
|
+
|
214
|
+
expected_cmd[:run] = true
|
215
|
+
expected_cmd[:actual][:sudo] = sudo
|
216
|
+
expected_cmd[:actual][:env] = env
|
217
|
+
expected_cmd
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/cli/kit/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cli-kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-05-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: cli-ui
|
@@ -85,7 +85,9 @@ files:
|
|
85
85
|
- Gemfile.lock
|
86
86
|
- LICENSE.txt
|
87
87
|
- README.md
|
88
|
+
- Rakefile
|
88
89
|
- bin/console
|
90
|
+
- bin/test_gen
|
89
91
|
- bin/testunit
|
90
92
|
- cli-kit.gemspec
|
91
93
|
- dev.yml
|
@@ -99,6 +101,7 @@ files:
|
|
99
101
|
- gen/template/.gitignore
|
100
102
|
- gen/template/Gemfile
|
101
103
|
- gen/template/README.md
|
104
|
+
- gen/template/bin/testunit
|
102
105
|
- gen/template/bin/update-deps
|
103
106
|
- gen/template/dev-gems.yml
|
104
107
|
- gen/template/dev-vendor.yml
|
@@ -109,6 +112,8 @@ files:
|
|
109
112
|
- gen/template/lib/__app__/commands/example.rb
|
110
113
|
- gen/template/lib/__app__/commands/help.rb
|
111
114
|
- gen/template/lib/__app__/entry_point.rb
|
115
|
+
- gen/template/test/example_test.rb
|
116
|
+
- gen/template/test/test_helper.rb
|
112
117
|
- lib/cli/kit.rb
|
113
118
|
- lib/cli/kit/autocall.rb
|
114
119
|
- lib/cli/kit/base_command.rb
|
@@ -120,6 +125,8 @@ files:
|
|
120
125
|
- lib/cli/kit/levenshtein.rb
|
121
126
|
- lib/cli/kit/resolver.rb
|
122
127
|
- lib/cli/kit/ruby_backports/enumerable.rb
|
128
|
+
- lib/cli/kit/support.rb
|
129
|
+
- lib/cli/kit/support/test_helper.rb
|
123
130
|
- lib/cli/kit/system.rb
|
124
131
|
- lib/cli/kit/version.rb
|
125
132
|
homepage: https://github.com/shopify/cli-kit
|