poise-service 1.0.3 → 1.0.4
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/CHANGELOG.md +5 -0
- data/README.md +6 -4
- data/chef/templates/default/sysvinit.sh.erb +7 -1
- data/chef/templates/default/upstart.conf.erb +13 -1
- data/lib/poise_service/resources/poise_service.rb +1 -1
- data/lib/poise_service/resources/poise_service_test.rb +34 -7
- data/lib/poise_service/service_providers/dummy.rb +16 -3
- data/lib/poise_service/service_providers/sysvinit.rb +9 -4
- data/lib/poise_service/version.rb +1 -1
- data/test/spec/service_providers/sysvinit_spec.rb +20 -10
- data/test/spec/service_providers/upstart_spec.rb +29 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e4487545f34995b8df8d3ff8ec3972163020bf8
|
4
|
+
data.tar.gz: 829b479bd922af7b93f95346d95b96af74200129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd0ee4d31ce879997b6af819fae90cdfda026098b163da17b536b552a145858c78640595cbd5532b08b2b41e1098a4a13d1fffac85e6c521195e96ca7d11ca17
|
7
|
+
data.tar.gz: 85169c58d825b707b8f558b00d990b6e296dc406297306c1376eaac5beed1ca423628cd8edce1e8f4f50d25b069a04f8402d95c800855bcb8f14d644ac9ed1e3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Poise-Service Changelog
|
2
2
|
|
3
|
+
## v1.0.4
|
4
|
+
|
5
|
+
* Set GID correctly in all service providers.
|
6
|
+
* Allow overriding the path to the generated sysvinit script.
|
7
|
+
|
3
8
|
## v1.0.3
|
4
9
|
|
5
10
|
* [#10](https://github.com/poise/poise-service/pull/10) Fixes for ensuring services are restarted when their command or user changes.
|
data/README.md
CHANGED
@@ -47,6 +47,8 @@ framework is used.
|
|
47
47
|
* Upstart
|
48
48
|
* systemd
|
49
49
|
* [Runit](https://github.com/poise/poise-service-runit)
|
50
|
+
* [Solaris](https://github.com/sh9189/poise-service-solaris)
|
51
|
+
* [AIX](https://github.com/johnbellone/poise-service-aix)
|
50
52
|
* *Supervisor (coming soon!)*
|
51
53
|
|
52
54
|
|
@@ -168,8 +170,8 @@ end
|
|
168
170
|
```
|
169
171
|
|
170
172
|
Unlike resource attributes, service options can be different for each provider.
|
171
|
-
Not all providers support the same options so make sure to
|
172
|
-
documentation for each provider to see what options
|
173
|
+
Not all providers support the same options so make sure to check the
|
174
|
+
documentation for each provider to see what options are available.
|
173
175
|
|
174
176
|
### `poise_service_options`
|
175
177
|
|
@@ -236,8 +238,7 @@ end
|
|
236
238
|
|
237
239
|
By default a PID file will be created in `/var/run/service_name.pid`. You can
|
238
240
|
use the `pid_file` option detailed below to override this and rely on your
|
239
|
-
process creating a PID file in the given path.
|
240
|
-
RHEL-family platforms as automatic PID detection there is more finicky.
|
241
|
+
process creating a PID file in the given path.
|
241
242
|
|
242
243
|
#### Options
|
243
244
|
|
@@ -252,6 +253,7 @@ RHEL-family platforms as automatic PID detection there is more finicky.
|
|
252
253
|
* `user` – Override the service user.
|
253
254
|
* `never_restart` – Never try to restart the service.
|
254
255
|
* `never_reload` – Never try to reload the service.
|
256
|
+
* `script_path` – Override the path to the generated service script.
|
255
257
|
|
256
258
|
### `upstart`
|
257
259
|
|
@@ -38,6 +38,7 @@ _start() {
|
|
38
38
|
<%# Implementing this using RedHat's bash helpers is too painful. Sorry. %>
|
39
39
|
<%# See dummy.rb for a more commented version of this code. %>
|
40
40
|
/opt/chef/embedded/bin/ruby <<EOH
|
41
|
+
require 'etc'
|
41
42
|
pid_file = <%= @pid_file.inspect %>
|
42
43
|
File.unlink(pid_file) if File.exist?(pid_file)
|
43
44
|
if Process.fork
|
@@ -48,7 +49,12 @@ else
|
|
48
49
|
<%- unless @pid_file_external -%>
|
49
50
|
IO.write(pid_file, Process.pid)
|
50
51
|
<%- end -%>
|
51
|
-
|
52
|
+
ent = Etc.getpwnam(<%= @user.inspect %>)
|
53
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
54
|
+
Process.initgroups(ent.name, ent.gid)
|
55
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
56
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
57
|
+
end
|
52
58
|
Kernel.exec(*<%= Shellwords.split(@command).inspect %>)
|
53
59
|
exit!
|
54
60
|
end
|
@@ -25,7 +25,19 @@ reload signal <%= @reload_signal %>
|
|
25
25
|
<%- if @upstart_features[:setuid] -%>
|
26
26
|
exec <%= @command %>
|
27
27
|
<%- else -%>
|
28
|
-
|
28
|
+
script
|
29
|
+
exec /opt/chef/embedded/bin/ruby <<EOH
|
30
|
+
require 'etc'
|
31
|
+
ent = Etc.getpwnam(<%= @user.inspect %>)
|
32
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
33
|
+
Process.initgroups(ent.name, ent.gid)
|
34
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
35
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
36
|
+
end
|
37
|
+
ENV["HOME"] = Dir.home(<%= @user.inspect %>) rescue nil
|
38
|
+
exec(*<%= Shellwords.split(@command).inspect %>)
|
39
|
+
EOH
|
40
|
+
end script
|
29
41
|
<%- end -%>
|
30
42
|
<%- if !@upstart_features[:kill_signal] && @stop_signal != 'TERM' -%>
|
31
43
|
pre-stop script
|
@@ -44,6 +44,10 @@ module PoiseService
|
|
44
44
|
# Service provider to set for the test group.
|
45
45
|
# @return [Symbol]
|
46
46
|
attribute(:service_provider, kind_of: Symbol)
|
47
|
+
# @!attribute service_options
|
48
|
+
# Service options to set for the test group.
|
49
|
+
# @return [Hash, nil]
|
50
|
+
attribute(:service_options, kind_of: [Hash, nil])
|
47
51
|
# @!attribute base_port
|
48
52
|
# Port number to start from for the test group.
|
49
53
|
# @return [Integer]
|
@@ -68,7 +72,9 @@ server.mount_proc '/' do |req, res|
|
|
68
72
|
res.body = {
|
69
73
|
directory: Dir.getwd,
|
70
74
|
user: Etc.getpwuid(Process.uid).name,
|
75
|
+
euser: Etc.getpwuid(Process.euid).name,
|
71
76
|
group: Etc.getgrgid(Process.gid).name,
|
77
|
+
egroup: Etc.getgrgid(Process.egid).name,
|
72
78
|
environment: ENV.to_hash,
|
73
79
|
file_data: FILE_DATA,
|
74
80
|
pid: Process.pid,
|
@@ -142,19 +148,28 @@ EOH
|
|
142
148
|
|
143
149
|
def create_tests
|
144
150
|
poise_service "poise_test_#{new_resource.name}" do
|
145
|
-
|
151
|
+
if new_resource.service_provider
|
152
|
+
provider new_resource.service_provider
|
153
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
154
|
+
end
|
146
155
|
command "/usr/bin/poise_test #{new_resource.base_port}"
|
147
156
|
end
|
148
157
|
|
149
158
|
poise_service "poise_test_#{new_resource.name}_params" do
|
150
|
-
|
159
|
+
if new_resource.service_provider
|
160
|
+
provider new_resource.service_provider
|
161
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
162
|
+
end
|
151
163
|
command "/usr/bin/poise_test #{new_resource.base_port + 1}"
|
152
164
|
environment POISE_ENV: new_resource.name
|
153
165
|
user 'poise'
|
154
166
|
end
|
155
167
|
|
156
168
|
poise_service "poise_test_#{new_resource.name}_noterm" do
|
157
|
-
|
169
|
+
if new_resource.service_provider
|
170
|
+
provider new_resource.service_provider
|
171
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
172
|
+
end
|
158
173
|
action [:enable, :disable]
|
159
174
|
command "/usr/bin/poise_test_noterm #{new_resource.base_port + 2}"
|
160
175
|
stop_signal 'kill'
|
@@ -163,7 +178,10 @@ EOH
|
|
163
178
|
{'restart' => 3, 'reload' => 4}.each do |action, port|
|
164
179
|
# Stop it before writing the file so we always start with first.
|
165
180
|
poise_service "poise_test_#{new_resource.name}_#{action} stop" do
|
166
|
-
|
181
|
+
if new_resource.service_provider
|
182
|
+
provider new_resource.service_provider
|
183
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
184
|
+
end
|
167
185
|
action(:disable)
|
168
186
|
service_name "poise_test_#{new_resource.name}_#{action}"
|
169
187
|
end
|
@@ -175,7 +193,10 @@ EOH
|
|
175
193
|
|
176
194
|
# Launch the service, reading in first.
|
177
195
|
poise_service "poise_test_#{new_resource.name}_#{action}" do
|
178
|
-
|
196
|
+
if new_resource.service_provider
|
197
|
+
provider new_resource.service_provider
|
198
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
199
|
+
end
|
179
200
|
command "/usr/bin/poise_test #{new_resource.base_port + port} /etc/poise_test_#{new_resource.name}_#{action}"
|
180
201
|
end
|
181
202
|
|
@@ -197,13 +218,19 @@ EOH
|
|
197
218
|
|
198
219
|
# Test changing the service definition itself.
|
199
220
|
poise_service "poise_test_#{new_resource.name}_change" do
|
200
|
-
|
221
|
+
if new_resource.service_provider
|
222
|
+
provider new_resource.service_provider
|
223
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
224
|
+
end
|
201
225
|
command "/usr/bin/poise_test #{new_resource.base_port + 5}"
|
202
226
|
end
|
203
227
|
|
204
228
|
poise_service "poise_test_#{new_resource.name}_change_second" do
|
205
229
|
service_name "poise_test_#{new_resource.name}_change"
|
206
|
-
|
230
|
+
if new_resource.service_provider
|
231
|
+
provider new_resource.service_provider
|
232
|
+
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
233
|
+
end
|
207
234
|
command "/usr/bin/poise_test #{new_resource.base_port + 6}"
|
208
235
|
end
|
209
236
|
|
@@ -14,6 +14,7 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
+
require 'etc'
|
17
18
|
require 'shellwords'
|
18
19
|
|
19
20
|
require 'poise_service/service_providers/base'
|
@@ -45,7 +46,8 @@ module PoiseService
|
|
45
46
|
else
|
46
47
|
# :nocov:
|
47
48
|
Chef::Log.debug("[#{new_resource}] Forked")
|
48
|
-
# First child, daemonize and go to town.
|
49
|
+
# First child, daemonize and go to town. This handles multi-fork,
|
50
|
+
# setsid, and shutting down stdin/out/err.
|
49
51
|
Process.daemon(true)
|
50
52
|
Chef::Log.debug("[#{new_resource}] Daemonized")
|
51
53
|
# Daemonized, set up process environment.
|
@@ -57,8 +59,14 @@ module PoiseService
|
|
57
59
|
end
|
58
60
|
Chef::Log.debug("[#{new_resource}] Process environment configured")
|
59
61
|
IO.write(pid_file, Process.pid)
|
60
|
-
Chef::Log.debug("[#{new_resource}] PID written to #{pid_file}
|
61
|
-
|
62
|
+
Chef::Log.debug("[#{new_resource}] PID written to #{pid_file}")
|
63
|
+
ent = Etc.getpwnam(new_resource.user)
|
64
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
65
|
+
Process.initgroups(ent.name, ent.gid)
|
66
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
67
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
68
|
+
end
|
69
|
+
Chef::Log.debug("[#{new_resource}] Changed privs to #{new_resource.user} (#{ent.uid}:#{ent.gid})")
|
62
70
|
# Split the command so we don't get an extra sh -c.
|
63
71
|
Chef::Log.debug("[#{new_resource}] Execing #{new_resource.command}")
|
64
72
|
Kernel.exec(*Shellwords.split(new_resource.command))
|
@@ -71,16 +79,21 @@ module PoiseService
|
|
71
79
|
|
72
80
|
def action_stop
|
73
81
|
return unless pid
|
82
|
+
Chef::Log.debug("[#{new_resource}] Stopping with #{new_resource.stop_signal}. Current PID is #{pid.inspect}.")
|
74
83
|
Process.kill(new_resource.stop_signal, pid)
|
75
84
|
::File.unlink(pid_file)
|
76
85
|
end
|
77
86
|
|
78
87
|
def action_restart
|
88
|
+
return if options['never_restart']
|
79
89
|
action_stop
|
80
90
|
action_start
|
81
91
|
end
|
82
92
|
|
83
93
|
def action_reload
|
94
|
+
return if options['never_reload']
|
95
|
+
return unless pid
|
96
|
+
Chef::Log.debug("[#{new_resource}] Reloading with #{new_resource.reload_signal}. Current PID is #{pid.inspect}.")
|
84
97
|
Process.kill(new_resource.reload_signal, pid)
|
85
98
|
end
|
86
99
|
|
@@ -36,13 +36,14 @@ module PoiseService
|
|
36
36
|
super.tap do |r|
|
37
37
|
r.provider(case node['platform_family']
|
38
38
|
when 'debian'
|
39
|
-
Chef::Provider::Service::
|
39
|
+
Chef::Provider::Service::Debian
|
40
40
|
when 'rhel'
|
41
|
-
Chef::Provider::Service::
|
41
|
+
Chef::Provider::Service::Redhat
|
42
42
|
else
|
43
43
|
# This will explode later in the template, but better than nothing for later.
|
44
44
|
Chef::Provider::Service::Init
|
45
45
|
end)
|
46
|
+
r.init_command(script_path)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
@@ -56,7 +57,7 @@ module PoiseService
|
|
56
57
|
# Sigh scoping.
|
57
58
|
pid_file_ = pid_file
|
58
59
|
# Render the service template
|
59
|
-
service_template(
|
60
|
+
service_template(script_path, 'sysvinit.sh.erb') do
|
60
61
|
mode '755'
|
61
62
|
variables.update(
|
62
63
|
daemon: daemon,
|
@@ -69,7 +70,7 @@ module PoiseService
|
|
69
70
|
end
|
70
71
|
|
71
72
|
def destroy_service
|
72
|
-
file
|
73
|
+
file script_path do
|
73
74
|
action :delete
|
74
75
|
end
|
75
76
|
|
@@ -78,6 +79,10 @@ module PoiseService
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
82
|
+
def script_path
|
83
|
+
options['script_path'] || "/etc/init.d/#{new_resource.service_name}"
|
84
|
+
end
|
85
|
+
|
81
86
|
def pid_file
|
82
87
|
options['pid_file'] || "/var/run/#{new_resource.service_name}.pid"
|
83
88
|
end
|
@@ -28,10 +28,10 @@ describe PoiseService::ServiceProviders::Sysvinit do
|
|
28
28
|
context 'on Ubuntu' do
|
29
29
|
let(:chefspec_options) { { platform: 'ubuntu', version: '14.04'} }
|
30
30
|
|
31
|
-
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-
|
32
|
-
start-stop-daemon --start --quiet --background
|
33
|
-
--pidfile "/var/run/test.pid" --make-pidfile
|
34
|
-
--chuid "root" --chdir "/"
|
31
|
+
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-EOH) }
|
32
|
+
start-stop-daemon --start --quiet --background \\
|
33
|
+
--pidfile "/var/run/test.pid" --make-pidfile \\
|
34
|
+
--chuid "root" --chdir "/" \\
|
35
35
|
--exec "myapp" -- --serve
|
36
36
|
EOH
|
37
37
|
|
@@ -41,10 +41,10 @@ EOH
|
|
41
41
|
override_attributes['poise-service']['test']['pid_file'] = '/tmp/pid'
|
42
42
|
end
|
43
43
|
|
44
|
-
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-
|
45
|
-
start-stop-daemon --start --quiet --background
|
46
|
-
--pidfile "/tmp/pid"
|
47
|
-
--chuid "root" --chdir "/"
|
44
|
+
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-EOH) }
|
45
|
+
start-stop-daemon --start --quiet --background \\
|
46
|
+
--pidfile "/tmp/pid" \\
|
47
|
+
--chuid "root" --chdir "/" \\
|
48
48
|
--exec "myapp" -- --serve
|
49
49
|
EOH
|
50
50
|
end # /context with an external PID file
|
@@ -56,7 +56,12 @@ EOH
|
|
56
56
|
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-EOH) }
|
57
57
|
Dir.chdir("/")
|
58
58
|
IO.write(pid_file, Process.pid)
|
59
|
-
|
59
|
+
ent = Etc.getpwnam("root")
|
60
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
61
|
+
Process.initgroups(ent.name, ent.gid)
|
62
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
63
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
64
|
+
end
|
60
65
|
Kernel.exec(*["myapp", "--serve"])
|
61
66
|
EOH
|
62
67
|
|
@@ -68,7 +73,12 @@ EOH
|
|
68
73
|
|
69
74
|
it { is_expected.to render_file('/etc/init.d/test').with_content(<<-EOH) }
|
70
75
|
Dir.chdir("/")
|
71
|
-
|
76
|
+
ent = Etc.getpwnam("root")
|
77
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
78
|
+
Process.initgroups(ent.name, ent.gid)
|
79
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
80
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
81
|
+
end
|
72
82
|
Kernel.exec(*["myapp", "--serve"])
|
73
83
|
EOH
|
74
84
|
end # /context with an external PID file
|
@@ -32,7 +32,7 @@ describe PoiseService::ServiceProviders::Upstart do
|
|
32
32
|
|
33
33
|
context 'with upstart 0.6.5' do
|
34
34
|
let(:upstart_version) { '0.6.5' }
|
35
|
-
it { is_expected.to render_file('/etc/init/test.conf').with_content(<<-
|
35
|
+
it { is_expected.to render_file('/etc/init/test.conf').with_content(<<-EOS) }
|
36
36
|
# test generated by poise-service for poise_service[test]
|
37
37
|
|
38
38
|
description "test"
|
@@ -45,8 +45,20 @@ respawn limit 10 5
|
|
45
45
|
umask 022
|
46
46
|
chdir /
|
47
47
|
|
48
|
-
|
48
|
+
script
|
49
|
+
exec /opt/chef/embedded/bin/ruby <<EOH
|
50
|
+
require 'etc'
|
51
|
+
ent = Etc.getpwnam("root")
|
52
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
53
|
+
Process.initgroups(ent.name, ent.gid)
|
54
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
55
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
56
|
+
end
|
57
|
+
ENV["HOME"] = Dir.home("root") rescue nil
|
58
|
+
exec(*["myapp", "--serve"])
|
49
59
|
EOH
|
60
|
+
end script
|
61
|
+
EOS
|
50
62
|
|
51
63
|
context 'with a stop signal' do
|
52
64
|
recipe do
|
@@ -56,7 +68,7 @@ EOH
|
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
59
|
-
it { is_expected.to render_file('/etc/init/test.conf').with_content(<<-
|
71
|
+
it { is_expected.to render_file('/etc/init/test.conf').with_content(<<-EOS) }
|
60
72
|
# test generated by poise-service for poise_service[test]
|
61
73
|
|
62
74
|
description "test"
|
@@ -69,14 +81,26 @@ respawn limit 10 5
|
|
69
81
|
umask 022
|
70
82
|
chdir /
|
71
83
|
|
72
|
-
|
84
|
+
script
|
85
|
+
exec /opt/chef/embedded/bin/ruby <<EOH
|
86
|
+
require 'etc'
|
87
|
+
ent = Etc.getpwnam("root")
|
88
|
+
if Process.euid != ent.uid || Process.egid != ent.gid
|
89
|
+
Process.initgroups(ent.name, ent.gid)
|
90
|
+
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
91
|
+
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
92
|
+
end
|
93
|
+
ENV["HOME"] = Dir.home("root") rescue nil
|
94
|
+
exec(*["myapp", "--serve"])
|
95
|
+
EOH
|
96
|
+
end script
|
73
97
|
pre-stop script
|
74
98
|
PID=`initctl status test | sed 's/^.*process \\([0-9]*\\)$/\\1/'`
|
75
99
|
if [ -n "$PID" ]; then
|
76
100
|
kill -KILL "$PID"
|
77
101
|
fi
|
78
102
|
end script
|
79
|
-
|
103
|
+
EOS
|
80
104
|
end # /context with a stop signal
|
81
105
|
end # /context with upstart 0.6.5
|
82
106
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: poise-service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Kantrowitz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: halite
|