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