bankai-docker 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d314159811ac96d23c11e0d76fc8be0a25725d777842c2dc2712cde62d71e8c
4
- data.tar.gz: 36069b1bb2fd5776527009ed0bcd504a998ebdc70c81084607b735c82fddd716
3
+ metadata.gz: f68fe68004c5ab50af0785203a2ae80a7488ec2fea15e70c8ec1adbac9d174c5
4
+ data.tar.gz: e69ba33eb18f3695bff08aac0871d867eeca02f1e983ad840323800f4a0c5e02
5
5
  SHA512:
6
- metadata.gz: 32e12d710314698a8de918bc92b18a6f0c79bfc8e28551e9b0c3fa18c3e63fbe999765a7c08852f9f29020367fa805de9de4377b72da07c698f3ec4b24de23ef
7
- data.tar.gz: e2cd532fb98f4b6fad37a795a3ea438dfcb93fa3fab8dbe404205e107d5649743fbaf2143c6c9cdbe43f72e3fbc4c8c55cdf1f8e3d5c3f1677d261a2bf821caa
6
+ metadata.gz: 67d7845371f2afa3218c24e1923542aa6a1eff76999881e40317aa2503213866a00543adb1e0389f95386de53357f940a0ab42c03b90fcb45c60f2eabba87abd
7
+ data.tar.gz: 84d8ec996eba867ed4f2ceb3e8f4bedf7fac8b51667a95518ec75b86bbc10f7c821361d4e9b07da97f5abae640b38190514489beb6476db85cda78ee345fbda3
data/.gitignore CHANGED
@@ -11,3 +11,5 @@
11
11
  .rspec_status
12
12
 
13
13
  coverage/
14
+
15
+ Gemfile.lock
data/README.md CHANGED
@@ -43,12 +43,13 @@ vim config/docker.rb
43
43
 
44
44
  * [ ] Auto detect Dockerfile
45
45
  * [ ] Auto detect .dockerignore
46
- * [ ] Auto package install detect from Gemfile
46
+ * [x] Auto package install detect from Gemfile
47
47
  * [ ] DSL
48
48
  * [ ] Gem Install
49
49
  * [ ] Specify Ruby/Node Version
50
- * [ ] Package Install
51
- * [ ] Rails Commands
50
+ * [x] Package Install
51
+ * [ ] Bundle / Rake / Rails Commands
52
+ * [x] Auto `COPY --from` rules
52
53
 
53
54
  ## Development
54
55
 
@@ -2,9 +2,11 @@ require 'bankai/docker/version'
2
2
  require 'bankai/docker/generators/install'
3
3
  require 'bankai/docker/railtie'
4
4
  require 'bankai/docker/dsl'
5
+ require 'bankai/docker/package'
5
6
  require 'bankai/docker/file'
6
7
  require 'bankai/docker/stage'
7
8
  require 'bankai/docker/command'
9
+ require 'bankai/docker/copy'
8
10
 
9
11
  module Bankai
10
12
  # :nodoc:
@@ -77,14 +77,13 @@ module Bankai
77
77
  private
78
78
 
79
79
  def cache_options
80
- @cache_options = @layers.map do |target|
81
- "--cache-from #{@name}:#{target}"
80
+ @cache_options = File.stages.map do |_, stage|
81
+ "--cache-from #{stage.tag}"
82
82
  end.join(' ')
83
83
  end
84
84
 
85
85
  def install_build_tasks
86
- @layers.each do |target|
87
- stage = File.stages[target]
86
+ File.stages.each do |target, stage|
88
87
  task target, :name do |_, args|
89
88
  File.instance.update_name(args[:name])
90
89
  @name ||= File.name
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Bankai
6
+ module Docker
7
+ # The generated files copy manager
8
+ class Copy
9
+ include Singleton
10
+
11
+ class << self
12
+ def add(stage, source, destination)
13
+ instance.add(stage, source, destination)
14
+ end
15
+
16
+ def to_s
17
+ instance.to_s
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @items = {}
23
+ end
24
+
25
+ def add(stage, source, destination)
26
+ stage = stage.to_sym
27
+ @items[stage] ||= []
28
+ @items[stage] << [source, destination]
29
+ end
30
+
31
+ def to_s
32
+ @items.flat_map do |stage, items|
33
+ items.uniq.map do |source, destination|
34
+ "COPY --from=#{stage} #{source} #{destination}"
35
+ end
36
+ end.join("\n")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -3,3 +3,4 @@
3
3
  require_relative 'dsl/argument'
4
4
  require_relative 'dsl/name'
5
5
  require_relative 'dsl/commands'
6
+ require_relative 'dsl/gemfile'
@@ -42,6 +42,22 @@ module Bankai
42
42
  def volume(*volumes)
43
43
  command 'volume', *volumes, mode: :array
44
44
  end
45
+
46
+ def package(*packages, runtime: true)
47
+ Package.use(@name) do |instance|
48
+ instance.add_dependency(*packages, runtime: runtime)
49
+ end
50
+ end
51
+
52
+ def runtime_package(*packages)
53
+ Package.instance.add_runtime_dependency(*packages)
54
+ end
55
+
56
+ def package_command
57
+ return unless Package.any?(@name)
58
+
59
+ "RUN #{Package.command_for @name}"
60
+ end
45
61
  end
46
62
  end
47
63
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bankai
4
+ module Docker
5
+ module DSL
6
+ # Gemfile detect
7
+ module Gemfile
8
+ def pg?
9
+ gemfile.match?(/gem .pg./)
10
+ end
11
+
12
+ def mysql?
13
+ gemfile.match?(/gem .mysql2./)
14
+ end
15
+
16
+ def gem?(name)
17
+ gemfile.match?(/gem .#{name}./)
18
+ end
19
+
20
+ private
21
+
22
+ def gemfile
23
+ @gemfile ||= ::File.read(Rails.root.join('Gemfile'))
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -33,6 +33,7 @@ module Bankai
33
33
  include Singleton
34
34
  include DSL::Name
35
35
  include DSL::Argument
36
+ include DSL::Gemfile
36
37
 
37
38
  attr_reader :stages
38
39
 
@@ -60,12 +61,27 @@ module Bankai
60
61
  @stages[:main] = Stage.new(:main, from: from, &block)
61
62
  end
62
63
 
64
+ def package(stage, *packages, runtime: true)
65
+ Package.use(stage) do |instance|
66
+ instance.add_dependency(*packages, runtime: runtime)
67
+ end
68
+ end
69
+
70
+ def runtime_package(*packages)
71
+ Package.instance.add_runtime_dependency(*packages)
72
+ end
73
+
74
+ def detect_package(name, stage, &block)
75
+ Package.register(name, stage, &block)
76
+ end
77
+
63
78
  def update_name(default = nil)
64
79
  app_name = Rails.app_class.parent_name.demodulize.underscore.dasherize
65
80
  @name = @name || default || "#{`whoami`.chomp}/#{app_name}"
66
81
  end
67
82
 
68
83
  def to_s
84
+ Package.detect
69
85
  root = Bankai::Docker::Generators::Base.default_source_root
70
86
  template = ERB.new(::File.read("#{root}/dockerfile.erb"), nil, '-')
71
87
  template.result(binding)
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Bankai
6
+ module Docker
7
+ # The package manager
8
+ class Package
9
+ class << self
10
+ def use(name, &block)
11
+ instance.switch(name, &block)
12
+ end
13
+
14
+ def register(name, stage, &block)
15
+ instance.register(name, stage, &block)
16
+ end
17
+
18
+ def detect
19
+ instance.detect
20
+ end
21
+
22
+ def any?(name)
23
+ !instance.stages[name.to_sym].nil?
24
+ end
25
+
26
+ def command_for(name)
27
+ format(
28
+ instance.command,
29
+ packages: (instance.stages[name.to_sym] || []).uniq.join(' ')
30
+ )
31
+ end
32
+ end
33
+
34
+ include Singleton
35
+
36
+ attr_reader :stages, :command
37
+
38
+ def initialize
39
+ # TODO: Support more package manager
40
+ @command = 'apk add --no-cache %<packages>s'
41
+ @mutex = Mutex.new
42
+ @stages = {}
43
+ @autos = {}
44
+
45
+ @current_stage = :main
46
+ end
47
+
48
+ def add_dependency(*packages, runtime: true)
49
+ current_stage.concat(packages)
50
+ stage(:main).concat(packages) if runtime
51
+ end
52
+
53
+ def add_runtime_dependency(*packages)
54
+ stage(:main).concat(packages)
55
+ end
56
+
57
+ def switch(stage_name, &_block)
58
+ prev = @current_stage
59
+ @mutex.synchronize do
60
+ @current_stage = stage_name.to_s
61
+ yield self
62
+ @current_stage = prev
63
+ end
64
+ end
65
+
66
+ def current_stage
67
+ stage(@current_stage)
68
+ end
69
+
70
+ def register(name, stage, &block)
71
+ @autos[name.to_sym] = [stage.to_sym, block]
72
+ end
73
+
74
+ def detect
75
+ @autos.each do |_, (stage, fn)|
76
+ switch(stage, &fn)
77
+ end
78
+ end
79
+
80
+ def stage(name)
81
+ name = name.to_sym
82
+ @stages[name] ||= []
83
+ @stages[name]
84
+ end
85
+ end
86
+ end
87
+ end
@@ -25,7 +25,9 @@ module Bankai
25
25
  @index = main? ? 999 : Stage.next_index
26
26
  @commands = []
27
27
  @arguments = {}
28
- instance_eval(&block)
28
+ @block = block
29
+ @mutex = Mutex.new
30
+ @executed = false
29
31
  end
30
32
 
31
33
  def tag
@@ -49,10 +51,32 @@ module Bankai
49
51
  end
50
52
 
51
53
  def to_s
54
+ ensure_executed
52
55
  root = Bankai::Docker::Generators::Base.default_source_root
53
56
  template = ERB.new(::File.read("#{root}/stage.erb"), nil, '-')
54
57
  template.result(binding)
55
58
  end
59
+
60
+ def produce(source, destination = nil)
61
+ Copy.add(@name, source, destination || source)
62
+ end
63
+
64
+ def copies
65
+ return unless main?
66
+
67
+ Copy.to_s
68
+ end
69
+
70
+ private
71
+
72
+ def ensure_executed
73
+ return if @executed
74
+
75
+ @mutex.synchronize do
76
+ instance_eval(&@block)
77
+ @executed = true
78
+ end
79
+ end
56
80
  end
57
81
  end
58
82
  end
@@ -3,6 +3,8 @@
3
3
  require 'bankai/docker'
4
4
  require 'bankai/docker/builder'
5
5
 
6
+ load "#{Bankai::Docker::Generators::Base.default_source_root}/auto_package.rb"
7
+
6
8
  config = Rails.root.join('config', 'docker.rb')
7
9
  if config.exist?
8
10
  load config
@@ -1,5 +1,5 @@
1
1
  module Bankai
2
2
  module Docker
3
- VERSION = '0.1.0'.freeze
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ Bankai::Docker.setup do
4
+ detect_package :database, :gem do |package|
5
+ if pg?
6
+ package.add_dependency 'postgresql-dev', runtime: false
7
+ package.add_runtime_dependency 'postgresql-libs'
8
+ end
9
+
10
+ if mysql?
11
+ package.add_dependency 'mariadb-dev', runtime: false
12
+ package.add_runtime_dependency 'mariadb-client-libs'
13
+ end
14
+
15
+ if gem?('sassc')
16
+ package.add_runtime_dependency 'libstdc++'
17
+ end
18
+ end
19
+ end
@@ -2,9 +2,11 @@
2
2
 
3
3
  # rubocop:disable Metrics/BlockLength
4
4
  Bankai::Docker.setup do
5
+ runtime_package 'tzdata', 'libstdc++', 'git'
6
+
5
7
  stage :gem do
6
- run 'apk add --no-cache build-base ca-certificates zlib-dev',
7
- 'libressl-dev postgresql-dev'
8
+ package 'build-base', 'ca-certificates', 'zlib-dev', 'libressl-dev',
9
+ runtime: false
8
10
 
9
11
  run 'mkdir -p /src/app'
10
12
 
@@ -14,37 +16,30 @@ Bankai::Docker.setup do
14
16
  workdir '/src/app'
15
17
  run 'bundle install --deployment --without development test ' \
16
18
  '--no-cache --clean && rm -rf vendor/bundle/ruby/**/cache'
19
+
20
+ produce '/src/app/vendor/bundle'
21
+ produce '/usr/local/bundle/config'
17
22
  end
18
23
 
19
24
  stage :node, from: 'node:10.15.2-alpine' do
20
25
  run 'mv /opt/yarn-v${YARN_VERSION} /opt/yarn'
26
+
27
+ produce '/usr/local/bin/node'
28
+ produce '/opt/yarn'
21
29
  end
22
30
 
23
31
  main do
24
- # Requirements
25
- # node - libstdc++
26
- # yarn - git
27
- run 'apk add --no-cache tzdata postgresql-libs libstdc++ libcurl git'
28
32
  run 'mkdir -p /src/app'
29
33
 
30
- copy '/usr/local/bin/node', '/usr/local/bin/', from: :node
31
- copy '/opt/yarn', '/opt/yarn', from: :node
32
-
33
34
  env 'PATH=/opt/yarn/bin:/src/app/bin:$PATH'
35
+ env 'RAILS_ENV=production'
34
36
 
35
37
  add '.', '/src/app'
36
-
37
- copy '/src/app/vendor/bundle', '/src/app/vendor/bundle', from: :gem
38
- copy '/usr/local/bundle/config', '/usr/local/bundle/config', from: :gem
39
-
40
38
  workdir '/src/app'
41
39
 
42
- env 'RAILS_ENV=production'
43
- run 'rails app:update:bin && rm -rf /src/app/tmp'
44
-
45
40
  expose 80
46
41
 
47
- entrypoint './bin/docker-entrypoint'
42
+ entrypoint 'docker-entrypoint'
48
43
  cmd 'serve'
49
44
  end
50
45
  end
@@ -1,7 +1,9 @@
1
- FROM <%= @from %><%= @name == :main ? '' : " AS #{@name}" %>
2
- <% @arguments.each do |name, value| -%>
1
+ FROM <%= @from %><%= main? ? '' : " AS #{@name}" %>
2
+ <% @arguments.each do |name, value| %>
3
3
  ARG <%= name %><%= value.nil? ? '' : "=#{value}" %>
4
- <% end %>
5
- <%- @commands.each do |command| -%>
4
+ <% end -%>
5
+ <%= package_command %>
6
+ <%= copies %>
7
+ <% @commands.each do |command| -%>
6
8
  <%= command %>
7
9
  <% end -%>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bankai-docker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 5xRuby
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-03-12 00:00:00.000000000 Z
12
+ date: 2019-03-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -137,7 +137,6 @@ files:
137
137
  - ".travis.yml"
138
138
  - CODE_OF_CONDUCT.md
139
139
  - Gemfile
140
- - Gemfile.lock
141
140
  - README.md
142
141
  - Rakefile
143
142
  - bankai-docker.gemspec
@@ -146,18 +145,22 @@ files:
146
145
  - lib/bankai/docker.rb
147
146
  - lib/bankai/docker/builder.rb
148
147
  - lib/bankai/docker/command.rb
148
+ - lib/bankai/docker/copy.rb
149
149
  - lib/bankai/docker/dsl.rb
150
150
  - lib/bankai/docker/dsl/argument.rb
151
151
  - lib/bankai/docker/dsl/commands.rb
152
+ - lib/bankai/docker/dsl/gemfile.rb
152
153
  - lib/bankai/docker/dsl/name.rb
153
154
  - lib/bankai/docker/file.rb
154
155
  - lib/bankai/docker/generators/base.rb
155
156
  - lib/bankai/docker/generators/install.rb
157
+ - lib/bankai/docker/package.rb
156
158
  - lib/bankai/docker/railtie.rb
157
159
  - lib/bankai/docker/stage.rb
158
160
  - lib/bankai/docker/tasks/all.rake
159
161
  - lib/bankai/docker/tasks/build.rake
160
162
  - lib/bankai/docker/version.rb
163
+ - templates/auto_package.rb
161
164
  - templates/default.rb
162
165
  - templates/dockerfile.erb
163
166
  - templates/entrypoint.erb
@@ -1,182 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- bankai-docker (0.1.0)
5
- bankai
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- actioncable (5.2.2)
11
- actionpack (= 5.2.2)
12
- nio4r (~> 2.0)
13
- websocket-driver (>= 0.6.1)
14
- actionmailer (5.2.2)
15
- actionpack (= 5.2.2)
16
- actionview (= 5.2.2)
17
- activejob (= 5.2.2)
18
- mail (~> 2.5, >= 2.5.4)
19
- rails-dom-testing (~> 2.0)
20
- actionpack (5.2.2)
21
- actionview (= 5.2.2)
22
- activesupport (= 5.2.2)
23
- rack (~> 2.0)
24
- rack-test (>= 0.6.3)
25
- rails-dom-testing (~> 2.0)
26
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
- actionview (5.2.2)
28
- activesupport (= 5.2.2)
29
- builder (~> 3.1)
30
- erubi (~> 1.4)
31
- rails-dom-testing (~> 2.0)
32
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
- activejob (5.2.2)
34
- activesupport (= 5.2.2)
35
- globalid (>= 0.3.6)
36
- activemodel (5.2.2)
37
- activesupport (= 5.2.2)
38
- activerecord (5.2.2)
39
- activemodel (= 5.2.2)
40
- activesupport (= 5.2.2)
41
- arel (>= 9.0)
42
- activestorage (5.2.2)
43
- actionpack (= 5.2.2)
44
- activerecord (= 5.2.2)
45
- marcel (~> 0.3.1)
46
- activesupport (5.2.2)
47
- concurrent-ruby (~> 1.0, >= 1.0.2)
48
- i18n (>= 0.7, < 2)
49
- minitest (~> 5.1)
50
- tzinfo (~> 1.1)
51
- arel (9.0.0)
52
- ast (2.4.0)
53
- bankai (0.2.2)
54
- rails (= 5.2.2)
55
- builder (3.2.3)
56
- bundler-audit (0.6.1)
57
- bundler (>= 1.2.0, < 3)
58
- thor (~> 0.18)
59
- childprocess (0.9.0)
60
- ffi (~> 1.0, >= 1.0.11)
61
- concurrent-ruby (1.1.4)
62
- crass (1.0.4)
63
- diff-lcs (1.3)
64
- docile (1.3.1)
65
- erubi (1.8.0)
66
- ffi (1.10.0)
67
- globalid (0.4.2)
68
- activesupport (>= 4.2.0)
69
- i18n (1.6.0)
70
- concurrent-ruby (~> 1.0)
71
- iniparse (1.4.4)
72
- jaro_winkler (1.5.2)
73
- json (2.2.0)
74
- loofah (2.2.3)
75
- crass (~> 1.0.2)
76
- nokogiri (>= 1.5.9)
77
- mail (2.7.1)
78
- mini_mime (>= 0.1.1)
79
- marcel (0.3.3)
80
- mimemagic (~> 0.3.2)
81
- method_source (0.9.2)
82
- mimemagic (0.3.3)
83
- mini_mime (1.0.1)
84
- mini_portile2 (2.4.0)
85
- minitest (5.11.3)
86
- nio4r (2.3.1)
87
- nokogiri (1.10.1)
88
- mini_portile2 (~> 2.4.0)
89
- overcommit (0.46.0)
90
- childprocess (~> 0.6, >= 0.6.3)
91
- iniparse (~> 1.4)
92
- parallel (1.14.0)
93
- parser (2.6.0.0)
94
- ast (~> 2.4.0)
95
- powerpack (0.1.2)
96
- rack (2.0.6)
97
- rack-test (1.1.0)
98
- rack (>= 1.0, < 3)
99
- rails (5.2.2)
100
- actioncable (= 5.2.2)
101
- actionmailer (= 5.2.2)
102
- actionpack (= 5.2.2)
103
- actionview (= 5.2.2)
104
- activejob (= 5.2.2)
105
- activemodel (= 5.2.2)
106
- activerecord (= 5.2.2)
107
- activestorage (= 5.2.2)
108
- activesupport (= 5.2.2)
109
- bundler (>= 1.3.0)
110
- railties (= 5.2.2)
111
- sprockets-rails (>= 2.0.0)
112
- rails-dom-testing (2.0.3)
113
- activesupport (>= 4.2.0)
114
- nokogiri (>= 1.6)
115
- rails-html-sanitizer (1.0.4)
116
- loofah (~> 2.2, >= 2.2.2)
117
- railties (5.2.2)
118
- actionpack (= 5.2.2)
119
- activesupport (= 5.2.2)
120
- method_source
121
- rake (>= 0.8.7)
122
- thor (>= 0.19.0, < 2.0)
123
- rainbow (3.0.0)
124
- rake (10.5.0)
125
- rspec (3.8.0)
126
- rspec-core (~> 3.8.0)
127
- rspec-expectations (~> 3.8.0)
128
- rspec-mocks (~> 3.8.0)
129
- rspec-core (3.8.0)
130
- rspec-support (~> 3.8.0)
131
- rspec-expectations (3.8.2)
132
- diff-lcs (>= 1.2.0, < 2.0)
133
- rspec-support (~> 3.8.0)
134
- rspec-mocks (3.8.0)
135
- diff-lcs (>= 1.2.0, < 2.0)
136
- rspec-support (~> 3.8.0)
137
- rspec-support (3.8.0)
138
- rubocop (0.60.0)
139
- jaro_winkler (~> 1.5.1)
140
- parallel (~> 1.10)
141
- parser (>= 2.5, != 2.5.1.1)
142
- powerpack (~> 0.1)
143
- rainbow (>= 2.2.2, < 4.0)
144
- ruby-progressbar (~> 1.7)
145
- unicode-display_width (~> 1.4.0)
146
- ruby-progressbar (1.10.0)
147
- simplecov (0.16.1)
148
- docile (~> 1.1)
149
- json (>= 1.8, < 3)
150
- simplecov-html (~> 0.10.0)
151
- simplecov-html (0.10.2)
152
- sprockets (3.7.2)
153
- concurrent-ruby (~> 1.0)
154
- rack (> 1, < 3)
155
- sprockets-rails (3.2.1)
156
- actionpack (>= 4.0)
157
- activesupport (>= 4.0)
158
- sprockets (>= 3.0.0)
159
- thor (0.20.3)
160
- thread_safe (0.3.6)
161
- tzinfo (1.2.5)
162
- thread_safe (~> 0.1)
163
- unicode-display_width (1.4.1)
164
- websocket-driver (0.7.0)
165
- websocket-extensions (>= 0.1.0)
166
- websocket-extensions (0.1.3)
167
-
168
- PLATFORMS
169
- ruby
170
-
171
- DEPENDENCIES
172
- bankai-docker!
173
- bundler (~> 1.17)
174
- bundler-audit
175
- overcommit
176
- rake (~> 10.0)
177
- rspec (~> 3.0)
178
- rubocop (~> 0.60.0)
179
- simplecov
180
-
181
- BUNDLED WITH
182
- 1.17.3