kuby-core 0.11.2 → 0.11.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/lib/kuby.rb +4 -2
- data/lib/kuby/commands.rb +10 -1
- data/lib/kuby/definition.rb +15 -2
- data/lib/kuby/dev_setup.rb +113 -23
- data/lib/kuby/docker/dev_spec.rb +7 -0
- data/lib/kuby/plugin_registry.rb +1 -1
- data/lib/kuby/plugins/rails_app/generators/kuby.rb +19 -1
- data/lib/kuby/plugins/rails_app/mysql.rb +5 -0
- data/lib/kuby/plugins/rails_app/plugin.rb +2 -0
- data/lib/kuby/plugins/rails_app/postgres.rb +19 -0
- data/lib/kuby/plugins/rails_app/sqlite.rb +13 -0
- data/lib/kuby/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e4b89049516b86853e5b3b4d41b4484cafae1244468d6721d5410af0ca071d1
|
4
|
+
data.tar.gz: 2b1d09c7e479cefd8b085dd4313d4f1ae4294b1b9fc4c6c22d713036b07178bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ae4889774de571b2198fde29edf5b9f58386c3f97f75d44d9e17a31c88b954f83dca6046ac41b2d5b18615d99b6a00b87e0d33136bc3515c46be6e46316eeb5
|
7
|
+
data.tar.gz: c43d154adad210913a7c181c41ff8cdabde9f60e6dff6719666c7b1ca6c4077686e8cadf8ac6cbf534c74e3de9f627f8ef6079d92958969b07d48b58acd585d9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## 0.11.8
|
2
|
+
* Alias Rails `config_map` to `env`.
|
3
|
+
|
4
|
+
## 0.11.7
|
5
|
+
* Properly namespace constant lookup for `Kubernetes::MissingPluginError`.
|
6
|
+
* Add missing `#storage` method for Postgres plugin.
|
7
|
+
|
8
|
+
## 0.11.6
|
9
|
+
* Fix Rails generator.
|
10
|
+
- Causing `undefined method 'module_parent_name'`. Apparently `module_parent_name` wasn't introduced until Rails 6.
|
11
|
+
|
12
|
+
## 0.11.5
|
13
|
+
* Raise friendlier error when attempting to add Docker credentials in the development environment.
|
14
|
+
* Raise friendlier error when attempting to set a username and password for SQLite databases.
|
15
|
+
|
16
|
+
## 0.11.4
|
17
|
+
* Fix bug causing crash when running CLI commands.
|
18
|
+
- Turns out was caused by adding a Sorbet type annotation inside an anonymous singleton class and forgetting to extend `T::Sig`. Thanks @lazyatom!
|
19
|
+
|
20
|
+
## 0.11.3
|
21
|
+
* I've gone back and forth a few times on this, but I decided to put the initializer code back into the Rails generator.
|
22
|
+
|
1
23
|
## 0.11.2
|
2
24
|
* Attempt to auto-require the requested provider if it isn't registered.
|
3
25
|
* Adjust error message when provider can't be found to include reminder to add a require statement.
|
data/lib/kuby.rb
CHANGED
@@ -59,8 +59,10 @@ module Kuby
|
|
59
59
|
tls_enabled false
|
60
60
|
|
61
61
|
database do
|
62
|
-
|
63
|
-
|
62
|
+
if requires_credentials?
|
63
|
+
user(DEFAULT_DB_USER)
|
64
|
+
password(DEFAULT_DB_PASSWORD)
|
65
|
+
end
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
data/lib/kuby/commands.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
|
+
|
2
3
|
require 'kuby/version'
|
3
4
|
require 'gli'
|
4
5
|
|
5
6
|
module Kuby
|
6
7
|
class Commands
|
8
|
+
extend T::Sig
|
7
9
|
extend GLI::App
|
8
10
|
|
9
11
|
# GLI doesn't have a wildcard option, so it's impossible to tell it to
|
@@ -17,8 +19,12 @@ module Kuby
|
|
17
19
|
# avoid the usual series of cryptic alias_method calls (note that there
|
18
20
|
# is no singleton class version of #prepend in the Ruby language).
|
19
21
|
singleton_class.send(:prepend, Module.new do
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
sig { params(args: T::Array[String]).void }
|
20
25
|
def run(args)
|
21
26
|
if idx = args.index('rails') || idx = args.index('rake')
|
27
|
+
@rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
|
22
28
|
@rails_options = args[idx..-1]
|
23
29
|
super(args[0..idx])
|
24
30
|
else
|
@@ -27,10 +33,12 @@ module Kuby
|
|
27
33
|
end
|
28
34
|
end)
|
29
35
|
|
36
|
+
sig { returns(Kuby::Tasks) }
|
30
37
|
def self.tasks
|
31
38
|
Kuby::Tasks.new(Kuby.environment)
|
32
39
|
end
|
33
40
|
|
41
|
+
sig { void }
|
34
42
|
def self.must_be_dev_env!
|
35
43
|
unless Kuby.environment.development?
|
36
44
|
fail "Command not supported in the '#{Kuby.environment.name}' environment"
|
@@ -67,6 +75,7 @@ module Kuby
|
|
67
75
|
rc.action do |global_options, options, args|
|
68
76
|
must_be_dev_env!
|
69
77
|
exit 1 unless tasks.dev_deployment_ok
|
78
|
+
@rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
|
70
79
|
Kuby::RailsCommands.run(@rails_options)
|
71
80
|
end
|
72
81
|
|
data/lib/kuby/definition.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
|
+
|
2
3
|
module Kuby
|
3
4
|
class Definition
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { returns(String) }
|
4
8
|
attr_reader :app_name
|
5
9
|
|
10
|
+
sig { params(app_name: String, block: T.nilable(T.proc.void)).void }
|
6
11
|
def initialize(app_name, &block)
|
7
12
|
@app_name = app_name
|
13
|
+
@environments = T.let(@environments, T.nilable(T::Hash[Symbol, Environment]))
|
8
14
|
end
|
9
15
|
|
16
|
+
sig {
|
17
|
+
params(
|
18
|
+
name: Symbol,
|
19
|
+
block: T.nilable(T.proc.void)
|
20
|
+
).returns(Environment)
|
21
|
+
}
|
10
22
|
def environment(name = Kuby.env, &block)
|
11
23
|
name = name.to_s
|
12
24
|
|
@@ -16,9 +28,10 @@ module Kuby
|
|
16
28
|
environments[name].instance_eval(&block)
|
17
29
|
end
|
18
30
|
|
19
|
-
environments[name]
|
31
|
+
T.must(environments[name])
|
20
32
|
end
|
21
33
|
|
34
|
+
sig { returns(T::Hash[Symbol, Environment]) }
|
22
35
|
def environments
|
23
36
|
@environments ||= {}
|
24
37
|
end
|
data/lib/kuby/dev_setup.rb
CHANGED
@@ -1,19 +1,34 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
|
+
|
2
3
|
module Kuby
|
3
4
|
class Spinner
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
PIECES = T.let(%w(- \\ | /).freeze, T::Array[String])
|
8
|
+
INTERVAL = T.let(0.2, Float) # seconds
|
9
|
+
|
10
|
+
sig {
|
11
|
+
params(
|
12
|
+
message: String,
|
13
|
+
block: T.proc.params(spinner: Spinner).void
|
14
|
+
)
|
15
|
+
.void
|
16
|
+
}
|
17
|
+
def self.spin(message, &block)
|
8
18
|
yield new(message)
|
9
19
|
end
|
10
20
|
|
11
|
-
|
21
|
+
sig { returns(String) }
|
22
|
+
attr_reader :message
|
23
|
+
|
24
|
+
sig { returns(Symbol) }
|
25
|
+
attr_reader :status
|
12
26
|
|
27
|
+
sig { params(message: String).void }
|
13
28
|
def initialize(message)
|
14
|
-
@message = message
|
15
|
-
@status = :running
|
16
|
-
@thread = Thread.new do
|
29
|
+
@message = T.let(message, String)
|
30
|
+
@status = T.let(:running, Symbol)
|
31
|
+
@thread = T.let(Thread.new do
|
17
32
|
counter = 0
|
18
33
|
|
19
34
|
while true
|
@@ -33,14 +48,16 @@ module Kuby
|
|
33
48
|
end
|
34
49
|
|
35
50
|
puts
|
36
|
-
end
|
51
|
+
end, Thread)
|
37
52
|
end
|
38
53
|
|
54
|
+
sig { void }
|
39
55
|
def success
|
40
56
|
@status = :success
|
41
57
|
@thread.join
|
42
58
|
end
|
43
59
|
|
60
|
+
sig { void }
|
44
61
|
def failure
|
45
62
|
@status = :failure
|
46
63
|
@thread.join
|
@@ -48,66 +65,116 @@ module Kuby
|
|
48
65
|
end
|
49
66
|
|
50
67
|
class SetupTask
|
51
|
-
|
68
|
+
extend T::Sig
|
52
69
|
|
70
|
+
sig { returns(String) }
|
71
|
+
attr_reader :message
|
72
|
+
|
73
|
+
sig { returns(T.proc.void) }
|
74
|
+
attr_reader :callable
|
75
|
+
|
76
|
+
sig { params(message: String, callable: T.proc.void).void }
|
53
77
|
def initialize(message, callable)
|
54
78
|
@message = message
|
55
79
|
@callable = callable
|
56
80
|
end
|
57
81
|
|
82
|
+
sig { void }
|
58
83
|
def run
|
59
84
|
callable.call
|
60
85
|
end
|
61
86
|
end
|
62
87
|
|
63
88
|
class Pipe
|
64
|
-
|
89
|
+
extend T::Sig
|
90
|
+
|
91
|
+
sig { returns(Symbol) }
|
92
|
+
attr_reader :name
|
93
|
+
|
94
|
+
sig { returns(T.untyped) }
|
95
|
+
attr_reader :cli
|
65
96
|
|
97
|
+
sig { returns(StringIO) }
|
98
|
+
attr_reader :out
|
99
|
+
|
100
|
+
sig { returns(StringIO) }
|
101
|
+
attr_reader :err
|
102
|
+
|
103
|
+
sig { params(name: Symbol, cli: T.untyped).void }
|
66
104
|
def initialize(name, cli)
|
67
|
-
@name = name
|
68
|
-
@cli = cli
|
69
|
-
@out = StringIO.new
|
70
|
-
@err = StringIO.new
|
105
|
+
@name = T.let(name, Symbol)
|
106
|
+
@cli = T.let(cli, T.untyped)
|
107
|
+
@out = T.let(StringIO.new, StringIO)
|
108
|
+
@err = T.let(StringIO.new, StringIO)
|
71
109
|
end
|
72
110
|
|
111
|
+
sig { params(block: T.proc.void).void }
|
73
112
|
def wrap(&block)
|
74
113
|
cli.with_pipes(out, err) do
|
75
114
|
block.call
|
76
115
|
end
|
77
116
|
end
|
78
117
|
|
118
|
+
sig { returns(T::Boolean) }
|
79
119
|
def success?
|
80
120
|
cli.last_status.nil? || cli.last_status.success?
|
81
121
|
end
|
82
122
|
end
|
83
123
|
|
84
124
|
class Pipes
|
125
|
+
extend T::Sig
|
126
|
+
extend T::Generic
|
127
|
+
|
85
128
|
include Enumerable
|
86
129
|
|
87
|
-
|
130
|
+
Elem = type_member(fixed: Pipe)
|
131
|
+
|
132
|
+
sig { returns(T::Array[Pipe]) }
|
133
|
+
attr_reader :pipes
|
88
134
|
|
135
|
+
sig { returns(T.nilable(StandardError)) }
|
136
|
+
attr_reader :ex
|
137
|
+
|
138
|
+
sig { params(clis: T::Hash[Symbol, T.untyped]).returns(Pipes) }
|
89
139
|
def self.build(clis)
|
90
140
|
new(clis.map { |name, cli| Pipe.new(name, cli) })
|
91
141
|
end
|
92
142
|
|
143
|
+
sig { params(pipes: T::Array[Pipe]).void }
|
93
144
|
def initialize(pipes)
|
94
|
-
@
|
145
|
+
@ex = T.let(@ex, T.nilable(StandardError))
|
146
|
+
@pipes = T.let(pipes, T::Array[Pipe])
|
95
147
|
end
|
96
148
|
|
149
|
+
sig {
|
150
|
+
override.params(
|
151
|
+
block: T.proc.params(package: Pipe).void
|
152
|
+
)
|
153
|
+
.void
|
154
|
+
}
|
97
155
|
def each(&block)
|
98
156
|
pipes.each(&block)
|
99
157
|
end
|
100
158
|
|
159
|
+
sig { params(block: T.proc.void).void }
|
101
160
|
def wrap(&block)
|
102
161
|
do_wrap(pipes, &block)
|
103
162
|
end
|
104
163
|
|
164
|
+
sig { returns(T::Boolean) }
|
105
165
|
def success?
|
106
166
|
pipes.all?(&:success?) && !ex
|
107
167
|
end
|
108
168
|
|
109
169
|
private
|
110
170
|
|
171
|
+
sig {
|
172
|
+
params(
|
173
|
+
remaining_pipes: T::Array[Pipe],
|
174
|
+
block: T.proc.void
|
175
|
+
)
|
176
|
+
.void
|
177
|
+
}
|
111
178
|
def do_wrap(remaining_pipes, &block)
|
112
179
|
if remaining_pipes.empty?
|
113
180
|
begin
|
@@ -119,20 +186,28 @@ module Kuby
|
|
119
186
|
return
|
120
187
|
end
|
121
188
|
|
122
|
-
remaining_pipes[0].wrap do
|
123
|
-
do_wrap(remaining_pipes[1..-1], &block)
|
189
|
+
T.must(remaining_pipes[0]).wrap do
|
190
|
+
do_wrap(T.must(remaining_pipes[1..-1]), &block)
|
124
191
|
end
|
125
192
|
end
|
126
193
|
end
|
127
194
|
|
128
195
|
class SetupTaskList
|
129
|
-
|
196
|
+
extend T::Sig
|
197
|
+
|
198
|
+
sig { returns(T::Array[SetupTask]) }
|
199
|
+
attr_reader :tasks
|
130
200
|
|
201
|
+
sig { returns T::Hash[Symbol, T.untyped] }
|
202
|
+
attr_reader :clis
|
203
|
+
|
204
|
+
sig { params(tasks: T::Array[SetupTask], clis: T::Hash[Symbol, T.untyped]).void }
|
131
205
|
def initialize(tasks, clis)
|
132
206
|
@tasks = tasks
|
133
207
|
@clis = clis
|
134
208
|
end
|
135
209
|
|
210
|
+
sig { void }
|
136
211
|
def run
|
137
212
|
tasks.each do |task|
|
138
213
|
pipes = Pipes.build(clis)
|
@@ -160,6 +235,7 @@ module Kuby
|
|
160
235
|
|
161
236
|
private
|
162
237
|
|
238
|
+
sig { params(pipe: Pipe).void }
|
163
239
|
def print_streams(pipe)
|
164
240
|
unless pipe.out.string.strip.empty?
|
165
241
|
puts("========= #{pipe.name.upcase} STDOUT ========")
|
@@ -172,27 +248,37 @@ module Kuby
|
|
172
248
|
end
|
173
249
|
end
|
174
250
|
|
251
|
+
sig { params(ex: T.nilable(StandardError)).void }
|
175
252
|
def print_error(ex)
|
176
253
|
return unless ex
|
177
254
|
puts("========= RUBY ERROR ========")
|
178
255
|
puts(ex.message)
|
179
|
-
puts(ex.backtrace.join("\n"))
|
256
|
+
puts(T.must(ex.backtrace).join("\n"))
|
180
257
|
end
|
181
258
|
end
|
182
259
|
|
183
260
|
class DevSetup
|
261
|
+
extend T::Sig
|
262
|
+
|
263
|
+
sig { returns(Environment) }
|
184
264
|
attr_reader :environment
|
185
265
|
|
266
|
+
sig { params(environment: Environment).void }
|
186
267
|
def initialize(environment)
|
187
|
-
@environment = environment
|
268
|
+
@environment = T.let(environment, Environment)
|
269
|
+
@setup_tasks = T.let(@setup_tasks, T.nilable(T::Array[SetupTask]))
|
270
|
+
@clis = T.let(@clis, T.nilable(T::Hash[Symbol, T.untyped]))
|
271
|
+
@tasks = T.let(@tasks, T.nilable(Tasks))
|
188
272
|
end
|
189
273
|
|
274
|
+
sig { void }
|
190
275
|
def run
|
191
276
|
SetupTaskList.new(setup_tasks, clis).run
|
192
277
|
end
|
193
278
|
|
194
279
|
private
|
195
280
|
|
281
|
+
sig { returns(T::Array[SetupTask]) }
|
196
282
|
def setup_tasks
|
197
283
|
@setup_tasks ||= [
|
198
284
|
SetupTask.new(
|
@@ -231,14 +317,17 @@ module Kuby
|
|
231
317
|
]
|
232
318
|
end
|
233
319
|
|
320
|
+
sig { returns(Kubernetes::Spec) }
|
234
321
|
def kubernetes
|
235
322
|
environment.kubernetes
|
236
323
|
end
|
237
324
|
|
325
|
+
sig { returns(Docker::Spec) }
|
238
326
|
def docker
|
239
327
|
environment.docker
|
240
328
|
end
|
241
329
|
|
330
|
+
sig { returns T::Hash[Symbol, T.untyped] }
|
242
331
|
def clis
|
243
332
|
@clis ||= {
|
244
333
|
kubectl: kubernetes.provider.kubernetes_cli,
|
@@ -249,6 +338,7 @@ module Kuby
|
|
249
338
|
}
|
250
339
|
end
|
251
340
|
|
341
|
+
sig { returns(Tasks) }
|
252
342
|
def tasks
|
253
343
|
@tasks ||= Kuby::Tasks.new(environment)
|
254
344
|
end
|
data/lib/kuby/docker/dev_spec.rb
CHANGED
@@ -115,6 +115,13 @@ module Kuby
|
|
115
115
|
layer_stack.includes?(name)
|
116
116
|
end
|
117
117
|
|
118
|
+
sig {
|
119
|
+
params(block: T.nilable(T.proc.void)).returns(Credentials)
|
120
|
+
}
|
121
|
+
def credentials(&block)
|
122
|
+
raise 'Docker credentials are not supported in the development environment'
|
123
|
+
end
|
124
|
+
|
118
125
|
sig { returns(Dockerfile) }
|
119
126
|
def to_dockerfile
|
120
127
|
Dockerfile.new.tap do |df|
|
data/lib/kuby/plugin_registry.rb
CHANGED
@@ -12,7 +12,7 @@ module Kuby
|
|
12
12
|
plugins_by_env = plugins[plugin_name]
|
13
13
|
|
14
14
|
unless plugins_by_env
|
15
|
-
raise MissingPluginError, "no plugin registered with name #{plugin_name}, "\
|
15
|
+
raise Kubernetes::MissingPluginError, "no plugin registered with name #{plugin_name}, "\
|
16
16
|
'do you need to add a gem to your Gemfile?'
|
17
17
|
end
|
18
18
|
|
@@ -3,7 +3,25 @@ require 'rails/generators'
|
|
3
3
|
require 'rails/generators/base'
|
4
4
|
|
5
5
|
class KubyGenerator < Rails::Generators::Base
|
6
|
+
def create_initializer_file
|
7
|
+
create_file(
|
8
|
+
File.join(*%w(config initializers kuby.rb)),
|
9
|
+
<<~END
|
10
|
+
require 'kuby'
|
11
|
+
Kuby.load!
|
12
|
+
END
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
6
16
|
def create_config_file
|
17
|
+
app_class = Rails.application.class
|
18
|
+
|
19
|
+
app_name = if app_class.respond_to?(:module_parent_name)
|
20
|
+
app_class.module_parent_name
|
21
|
+
else
|
22
|
+
app_class.parent_name
|
23
|
+
end
|
24
|
+
|
7
25
|
create_file(
|
8
26
|
'kuby.rb',
|
9
27
|
<<~END
|
@@ -11,7 +29,7 @@ class KubyGenerator < Rails::Generators::Base
|
|
11
29
|
require 'active_support/encrypted_configuration'
|
12
30
|
|
13
31
|
# Define a production Kuby deploy environment
|
14
|
-
Kuby.define('#{
|
32
|
+
Kuby.define('#{app_name}') do
|
15
33
|
environment(:production) do
|
16
34
|
# Because the Rails environment isn't always loaded when
|
17
35
|
# your Kuby config is loaded, provide access to Rails
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: false
|
2
|
+
|
2
3
|
require 'kube-dsl'
|
3
4
|
require 'kuby/kube-db'
|
4
5
|
|
@@ -18,6 +19,10 @@ module Kuby
|
|
18
19
|
password(config['password'])
|
19
20
|
end
|
20
21
|
|
22
|
+
def requires_credentials?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
21
26
|
def name
|
22
27
|
:postgres
|
23
28
|
end
|
@@ -58,6 +63,20 @@ module Kuby
|
|
58
63
|
end
|
59
64
|
end
|
60
65
|
end
|
66
|
+
|
67
|
+
def storage(amount)
|
68
|
+
database do
|
69
|
+
spec do
|
70
|
+
storage do
|
71
|
+
resources do
|
72
|
+
requests do
|
73
|
+
set :storage, amount
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
61
80
|
|
62
81
|
def secret(&block)
|
63
82
|
context = self
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
|
2
3
|
module Kuby
|
3
4
|
module Plugins
|
4
5
|
module RailsApp
|
@@ -14,6 +15,18 @@ module Kuby
|
|
14
15
|
environment.docker.package_phase.add(:sqlite_client)
|
15
16
|
end
|
16
17
|
|
18
|
+
def requires_credentials?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def user(_user)
|
23
|
+
raise 'SQLite databases do not require a username or password'
|
24
|
+
end
|
25
|
+
|
26
|
+
def password(_password)
|
27
|
+
raise 'SQLite databases do not require a username or password'
|
28
|
+
end
|
29
|
+
|
17
30
|
def name
|
18
31
|
:sqlite
|
19
32
|
end
|
data/lib/kuby/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Dutro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|