cli-kit 1.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 +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +23 -0
- data/.travis.yml +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +21 -0
- data/README.md +2 -0
- data/bin/console +14 -0
- data/bin/testunit +23 -0
- data/cli-kit.gemspec +29 -0
- data/dev.yml +7 -0
- data/lib/cli/kit.rb +7 -0
- data/lib/cli/kit/system.rb +204 -0
- data/lib/cli/kit/version.rb +5 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 194c6fb318a717adb92458539db84f4e82c0c7d8
|
4
|
+
data.tar.gz: ed39204760603166cb42990fef86db6f69f1e44a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 325c4f7a072a9004275079d04b9925ef791a4d450ac63d757f9bf294234551a4b8f946e817d4287b146849d6823d46ae7a22a732b616bb1a2ceb1cbcd24f3fe2
|
7
|
+
data.tar.gz: 0507ec4b45eddf96c736c4b552a45bea2528cdff30666006c86d5796d71503b1e25ec9dea415d1b1220b86a055a29a12535e94ed671dec5719301a2e55ee7670
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
inherit_from:
|
2
|
+
- http://shopify.github.io/ruby-style-guide/rubocop.yml
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
Exclude:
|
6
|
+
- 'vendor/**/*'
|
7
|
+
TargetRubyVersion: 2.0
|
8
|
+
|
9
|
+
# This doesn't understand that <<~ doesn't exist in 2.0
|
10
|
+
Style/IndentHeredoc:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
# This doesn't take into account retrying from an exception
|
14
|
+
Lint/HandleExceptions:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
# allow String.new to create mutable strings
|
18
|
+
Style/EmptyLiteral:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
# allow the use of globals which makes sense in a CLI app like this
|
22
|
+
Style/GlobalVars:
|
23
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# NOTE: These are development-only dependencies
|
2
|
+
source "https://rubygems.org"
|
3
|
+
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'rubocop'
|
8
|
+
gem 'byebug'
|
9
|
+
gem 'method_source'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem 'mocha', require: false
|
14
|
+
gem 'minitest', '>= 5.0.0', require: false
|
15
|
+
gem 'minitest-reporters', require: false
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dev-kit (0.1.0)
|
5
|
+
dev-ui (>= 0.1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ansi (1.5.0)
|
11
|
+
ast (2.3.0)
|
12
|
+
builder (3.2.3)
|
13
|
+
byebug (9.0.6)
|
14
|
+
dev-ui (0.1.0)
|
15
|
+
metaclass (0.0.4)
|
16
|
+
method_source (0.8.2)
|
17
|
+
minitest (5.10.2)
|
18
|
+
minitest-reporters (1.1.14)
|
19
|
+
ansi
|
20
|
+
builder
|
21
|
+
minitest (>= 5.0)
|
22
|
+
ruby-progressbar
|
23
|
+
mocha (1.2.1)
|
24
|
+
metaclass (~> 0.0.1)
|
25
|
+
parallel (1.11.2)
|
26
|
+
parser (2.4.0.0)
|
27
|
+
ast (~> 2.2)
|
28
|
+
powerpack (0.1.1)
|
29
|
+
rainbow (2.2.2)
|
30
|
+
rake
|
31
|
+
rake (10.5.0)
|
32
|
+
rubocop (0.49.1)
|
33
|
+
parallel (~> 1.10)
|
34
|
+
parser (>= 2.3.3.1, < 3.0)
|
35
|
+
powerpack (~> 0.1)
|
36
|
+
rainbow (>= 1.99.1, < 3.0)
|
37
|
+
ruby-progressbar (~> 1.7)
|
38
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
39
|
+
ruby-progressbar (1.8.1)
|
40
|
+
unicode-display_width (1.3.0)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
bundler (~> 1.15)
|
47
|
+
byebug
|
48
|
+
dev-kit!
|
49
|
+
method_source
|
50
|
+
minitest (>= 5.0.0)
|
51
|
+
minitest-reporters
|
52
|
+
mocha
|
53
|
+
rake (~> 10.0)
|
54
|
+
rubocop
|
55
|
+
|
56
|
+
BUNDLED WITH
|
57
|
+
1.16.0
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Burke Libbey
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cli/kit"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/testunit
ADDED
@@ -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
|
+
CLI_TEST_ROOT = root + '/test'
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift(CLI_TEST_ROOT)
|
10
|
+
|
11
|
+
def test_files
|
12
|
+
Dir.glob(CLI_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
|
data/cli-kit.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cli/kit/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cli-kit"
|
8
|
+
spec.version = CLI::Kit::VERSION
|
9
|
+
spec.authors = ["Burke Libbey", "Julian Nadeau"]
|
10
|
+
spec.email = ["burke.libbey@shopify.com", "julian.nadeau@shopify.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Terminal UI framework extensions}
|
13
|
+
spec.description = %q{Terminal UI framework extensions}
|
14
|
+
spec.homepage = "https://github.com/shopify/cli-kit"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "cli-ui", ">= 1.0.0"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
29
|
+
end
|
data/dev.yml
ADDED
data/lib/cli/kit.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'cli/kit'
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'English'
|
5
|
+
|
6
|
+
module CLI
|
7
|
+
module Kit
|
8
|
+
module System
|
9
|
+
SUDO_PROMPT = CLI::UI.fmt("{{info:(sudo)}} Password: ")
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Ask for sudo access with a message explaning the need for it
|
13
|
+
# Will make subsequent commands capable of running with sudo for a period of time
|
14
|
+
#
|
15
|
+
# #### Parameters
|
16
|
+
# - `msg`: A message telling the user why sudo is needed
|
17
|
+
#
|
18
|
+
# #### Usage
|
19
|
+
# `ctx.sudo_reason("We need to do a thing")`
|
20
|
+
#
|
21
|
+
def sudo_reason(msg)
|
22
|
+
# See if sudo has a cached password
|
23
|
+
`env SUDO_ASKPASS=/usr/bin/false sudo -A true`
|
24
|
+
return if $CHILD_STATUS.success?
|
25
|
+
CLI::UI.with_frame_color(:blue) do
|
26
|
+
puts(CLI::UI.fmt("{{i}} #{msg}"))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Execute a command in the user's environment
|
31
|
+
# This is meant to be largely equivalent to backticks, only with the env passed in.
|
32
|
+
# Captures the results of the command without output to the console
|
33
|
+
#
|
34
|
+
# #### Parameters
|
35
|
+
# - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
|
36
|
+
# - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
|
37
|
+
# - `env`: process environment with which to execute this command
|
38
|
+
# - `**kwargs`: additional arguments to pass to Open3.capture2
|
39
|
+
#
|
40
|
+
# #### Returns
|
41
|
+
# - `output`: output (STDOUT) of the command execution
|
42
|
+
# - `status`: boolean success status of the command execution
|
43
|
+
#
|
44
|
+
# #### Usage
|
45
|
+
# `out, stat = CLI::Kit::System.capture2('ls', 'a_folder')`
|
46
|
+
#
|
47
|
+
def capture2(*a, sudo: false, env: ENV, **kwargs)
|
48
|
+
delegate_open3(*a, sudo: sudo, env: env, method: :capture2, **kwargs)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Execute a command in the user's environment
|
52
|
+
# This is meant to be largely equivalent to backticks, only with the env passed in.
|
53
|
+
# Captures the results of the command without output to the console
|
54
|
+
#
|
55
|
+
# #### Parameters
|
56
|
+
# - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
|
57
|
+
# - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
|
58
|
+
# - `env`: process environment with which to execute this command
|
59
|
+
# - `**kwargs`: additional arguments to pass to Open3.capture2e
|
60
|
+
#
|
61
|
+
# #### Returns
|
62
|
+
# - `output`: output (STDOUT merged with STDERR) of the command execution
|
63
|
+
# - `status`: boolean success status of the command execution
|
64
|
+
#
|
65
|
+
# #### Usage
|
66
|
+
# `out_and_err, stat = CLI::Kit::System.capture2e('ls', 'a_folder')`
|
67
|
+
#
|
68
|
+
def capture2e(*a, sudo: false, env: ENV, **kwargs)
|
69
|
+
delegate_open3(*a, sudo: sudo, env: env, method: :capture2e, **kwargs)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Execute a command in the user's environment
|
73
|
+
# This is meant to be largely equivalent to backticks, only with the env passed in.
|
74
|
+
# Captures the results of the command without output to the console
|
75
|
+
#
|
76
|
+
# #### Parameters
|
77
|
+
# - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
|
78
|
+
# - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
|
79
|
+
# - `env`: process environment with which to execute this command
|
80
|
+
# - `**kwargs`: additional arguments to pass to Open3.capture3
|
81
|
+
#
|
82
|
+
# #### Returns
|
83
|
+
# - `output`: STDOUT of the command execution
|
84
|
+
# - `error`: STDERR of the command execution
|
85
|
+
# - `status`: boolean success status of the command execution
|
86
|
+
#
|
87
|
+
# #### Usage
|
88
|
+
# `out, err, stat = CLI::Kit::System.capture3('ls', 'a_folder')`
|
89
|
+
#
|
90
|
+
def capture3(*a, sudo: false, env: ENV, **kwargs)
|
91
|
+
delegate_open3(*a, sudo: sudo, env: env, method: :capture3, **kwargs)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Execute a command in the user's environment
|
95
|
+
# Outputs result of the command without capturing it
|
96
|
+
#
|
97
|
+
# #### Parameters
|
98
|
+
# - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
|
99
|
+
# - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
|
100
|
+
# - `env`: process environment with which to execute this command
|
101
|
+
# - `**kwargs`: additional keyword arguments to pass to Process.spawn
|
102
|
+
#
|
103
|
+
# #### Returns
|
104
|
+
# - `status`: boolean success status of the command execution
|
105
|
+
#
|
106
|
+
# #### Usage
|
107
|
+
# `stat = CLI::Kit::System.system('ls', 'a_folder')`
|
108
|
+
#
|
109
|
+
def system(*a, sudo: false, env: ENV, **kwargs)
|
110
|
+
a = apply_sudo(*a, sudo)
|
111
|
+
|
112
|
+
out_r, out_w = IO.pipe
|
113
|
+
err_r, err_w = IO.pipe
|
114
|
+
in_stream = STDIN.closed? ? :close : STDIN
|
115
|
+
pid = Process.spawn(env, *resolve_path(a, env), 0 => in_stream, :out => out_w, :err => err_w, **kwargs)
|
116
|
+
out_w.close
|
117
|
+
err_w.close
|
118
|
+
|
119
|
+
handlers = if block_given?
|
120
|
+
{ out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
|
121
|
+
err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) }, }
|
122
|
+
else
|
123
|
+
{ out_r => ->(data) { STDOUT.write(data) },
|
124
|
+
err_r => ->(data) { STDOUT.write(data) }, }
|
125
|
+
end
|
126
|
+
|
127
|
+
previous_trailing = Hash.new('')
|
128
|
+
loop do
|
129
|
+
ios = [err_r, out_r].reject(&:closed?)
|
130
|
+
break if ios.empty?
|
131
|
+
|
132
|
+
readers, = IO.select(ios)
|
133
|
+
readers.each do |io|
|
134
|
+
begin
|
135
|
+
data, trailing = split_partial_characters(io.readpartial(4096))
|
136
|
+
handlers[io].call(previous_trailing[io] + data)
|
137
|
+
previous_trailing[io] = trailing
|
138
|
+
rescue IOError
|
139
|
+
io.close
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
Process.wait(pid)
|
145
|
+
$CHILD_STATUS
|
146
|
+
end
|
147
|
+
|
148
|
+
# Split off trailing partial UTF-8 Characters. UTF-8 Multibyte characters start with a 11xxxxxx byte that tells
|
149
|
+
# how many following bytes are part of this character, followed by some number of 10xxxxxx bytes. This simple
|
150
|
+
# algorithm will split off a whole trailing multi-byte character.
|
151
|
+
def split_partial_characters(data)
|
152
|
+
last_byte = data.getbyte(-1)
|
153
|
+
return [data, ''] if (last_byte & 0b1000_0000).zero?
|
154
|
+
|
155
|
+
# UTF-8 is up to 6 characters per rune, so we could never want to trim more than that, and we want to avoid
|
156
|
+
# allocating an array for the whole of data with bytes
|
157
|
+
min_bound = -[6, data.bytesize].min
|
158
|
+
final_bytes = data.byteslice(min_bound..-1).bytes
|
159
|
+
partial_character_sub_index = final_bytes.rindex { |byte| byte & 0b1100_0000 == 0b1100_0000 }
|
160
|
+
# Bail out for non UTF-8
|
161
|
+
return [data, ''] unless partial_character_sub_index
|
162
|
+
partial_character_index = min_bound + partial_character_sub_index
|
163
|
+
|
164
|
+
[data.byteslice(0...partial_character_index), data.byteslice(partial_character_index..-1)]
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def apply_sudo(*a, sudo)
|
170
|
+
a.unshift('sudo', '-S', '-p', SUDO_PROMPT, '--') if sudo
|
171
|
+
sudo_reason(sudo) if sudo.is_a?(String)
|
172
|
+
a
|
173
|
+
end
|
174
|
+
|
175
|
+
def delegate_open3(*a, sudo: raise, env: raise, method: raise, **kwargs)
|
176
|
+
a = apply_sudo(*a, sudo)
|
177
|
+
Open3.send(method, env, *resolve_path(a, env), **kwargs)
|
178
|
+
rescue Errno::EINTR
|
179
|
+
raise(Errno::EINTR, "command interrupted: #{a.join(' ')}")
|
180
|
+
end
|
181
|
+
|
182
|
+
# Ruby resolves the program to execute using its own PATH, but we want it to
|
183
|
+
# use the provided one, so we ensure ruby chooses to spawn a shell, which will
|
184
|
+
# parse our command and properly spawn our target using the provided environment.
|
185
|
+
#
|
186
|
+
# This is important because dev clobbers its own environment such that ruby
|
187
|
+
# means /usr/bin/ruby, but we want it to select the ruby targeted by the active
|
188
|
+
# project.
|
189
|
+
#
|
190
|
+
# See https://github.com/Shopify/dev/pull/625 for more details.
|
191
|
+
def resolve_path(a, env)
|
192
|
+
# If only one argument was provided, make sure it's interpreted by a shell.
|
193
|
+
return ["true ; " + a[0]] if a.size == 1
|
194
|
+
return a if a.first.include?('/')
|
195
|
+
item = env.fetch('PATH', '').split(':').detect do |f|
|
196
|
+
File.exist?("#{f}/#{a.first}")
|
197
|
+
end
|
198
|
+
a[0] = "#{item}/#{a.first}" if item
|
199
|
+
a
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cli-kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Burke Libbey
|
8
|
+
- Julian Nadeau
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-12-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: cli-ui
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.0.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.0.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.15'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.15'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: minitest
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '5.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '5.0'
|
70
|
+
description: Terminal UI framework extensions
|
71
|
+
email:
|
72
|
+
- burke.libbey@shopify.com
|
73
|
+
- julian.nadeau@shopify.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rubocop.yml"
|
80
|
+
- ".travis.yml"
|
81
|
+
- Gemfile
|
82
|
+
- Gemfile.lock
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- bin/console
|
86
|
+
- bin/testunit
|
87
|
+
- cli-kit.gemspec
|
88
|
+
- dev.yml
|
89
|
+
- lib/cli/kit.rb
|
90
|
+
- lib/cli/kit/system.rb
|
91
|
+
- lib/cli/kit/version.rb
|
92
|
+
homepage: https://github.com/shopify/cli-kit
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.6.14
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: Terminal UI framework extensions
|
116
|
+
test_files: []
|