kuby-core 0.16.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/Gemfile +6 -2
  4. data/Rakefile +5 -3
  5. data/bin/tapioca +29 -0
  6. data/kuby-core.gemspec +9 -11
  7. data/lib/kuby/basic_logger.rb +34 -34
  8. data/lib/kuby/cli_base.rb +43 -43
  9. data/lib/kuby/commands.rb +135 -21
  10. data/lib/kuby/definition.rb +12 -12
  11. data/lib/kuby/dependable.rb +20 -0
  12. data/lib/kuby/dependency.rb +14 -0
  13. data/lib/kuby/docker/alpine.rb +10 -10
  14. data/lib/kuby/docker/app_image.rb +11 -20
  15. data/lib/kuby/docker/app_phase.rb +36 -0
  16. data/lib/kuby/docker/assets_phase.rb +2 -2
  17. data/lib/kuby/docker/bundler_phase.rb +42 -40
  18. data/lib/kuby/docker/cli.rb +72 -37
  19. data/lib/kuby/docker/copy_phase.rb +7 -7
  20. data/lib/kuby/docker/credentials.rb +1 -0
  21. data/lib/kuby/docker/debian.rb +10 -10
  22. data/lib/kuby/docker/distro.rb +13 -13
  23. data/lib/kuby/docker/docker_uri.rb +20 -20
  24. data/lib/kuby/docker/dockerfile.rb +48 -39
  25. data/lib/kuby/docker/image.rb +66 -54
  26. data/lib/kuby/docker/inline_layer.rb +4 -4
  27. data/lib/kuby/docker/layer.rb +6 -6
  28. data/lib/kuby/docker/layer_stack.rb +35 -35
  29. data/lib/kuby/docker/local_tags.rb +16 -16
  30. data/lib/kuby/docker/package_list.rb +16 -16
  31. data/lib/kuby/docker/package_phase.rb +16 -16
  32. data/lib/kuby/docker/packages/managed_package.rb +13 -13
  33. data/lib/kuby/docker/packages/nodejs.rb +5 -5
  34. data/lib/kuby/docker/packages/package.rb +8 -8
  35. data/lib/kuby/docker/packages/simple_managed_package.rb +7 -7
  36. data/lib/kuby/docker/packages/yarn.rb +6 -6
  37. data/lib/kuby/docker/remote_tags.rb +16 -16
  38. data/lib/kuby/docker/setup_phase.rb +18 -20
  39. data/lib/kuby/docker/spec.rb +93 -72
  40. data/lib/kuby/docker/timestamp_tag.rb +16 -11
  41. data/lib/kuby/docker/timestamped_image.rb +64 -38
  42. data/lib/kuby/docker/webserver_phase.rb +20 -20
  43. data/lib/kuby/docker/yarn_phase.rb +29 -5
  44. data/lib/kuby/docker.rb +2 -1
  45. data/lib/kuby/kubernetes/bare_metal_provider.rb +12 -10
  46. data/lib/kuby/kubernetes/deployer.rb +22 -10
  47. data/lib/kuby/kubernetes/docker_config.rb +1 -0
  48. data/lib/kuby/kubernetes/provider.rb +1 -0
  49. data/lib/kuby/kubernetes/spec.rb +63 -13
  50. data/lib/kuby/plugin.rb +23 -1
  51. data/lib/kuby/plugin_registry.rb +15 -0
  52. data/lib/kuby/plugins/nginx_ingress.rb +8 -6
  53. data/lib/kuby/plugins/rails_app/assets.rb +16 -4
  54. data/lib/kuby/plugins/rails_app/assets_image.rb +18 -11
  55. data/lib/kuby/plugins/rails_app/crdb/plugin.rb +473 -0
  56. data/lib/kuby/plugins/rails_app/crdb.rb +9 -0
  57. data/lib/kuby/plugins/rails_app/database.rb +12 -8
  58. data/lib/kuby/plugins/rails_app/generators/kuby.rb +17 -16
  59. data/lib/kuby/plugins/rails_app/plugin.rb +37 -27
  60. data/lib/kuby/plugins/rails_app/sqlite.rb +7 -3
  61. data/lib/kuby/plugins/rails_app/tasks.rake +25 -12
  62. data/lib/kuby/plugins/rails_app.rb +1 -0
  63. data/lib/kuby/plugins/system.rb +16 -0
  64. data/lib/kuby/plugins.rb +1 -0
  65. data/lib/kuby/railtie.rb +31 -1
  66. data/lib/kuby/tasks.rb +91 -10
  67. data/lib/kuby/trailing_hash.rb +2 -2
  68. data/lib/kuby/utils/sem_ver/constraint.rb +68 -0
  69. data/lib/kuby/utils/sem_ver/constraint_set.rb +25 -0
  70. data/lib/kuby/utils/sem_ver/version.rb +49 -0
  71. data/lib/kuby/utils/sem_ver.rb +17 -0
  72. data/lib/kuby/utils/table.rb +35 -0
  73. data/lib/kuby/utils/which.rb +65 -0
  74. data/lib/kuby/utils.rb +11 -0
  75. data/lib/kuby/version.rb +1 -1
  76. data/lib/kuby.rb +37 -2
  77. data/rbi/kuby-core.rbi +2128 -0
  78. data/spec/docker/spec_spec.rb +50 -26
  79. data/spec/docker/timestamped_image_spec.rb +2 -2
  80. data/spec/dummy/app/channels/application_cable/channel.rb +2 -1
  81. data/spec/dummy/app/channels/application_cable/connection.rb +2 -1
  82. data/spec/dummy/app/controllers/application_controller.rb +2 -1
  83. data/spec/dummy/app/jobs/application_job.rb +2 -1
  84. data/spec/dummy/app/mailers/application_mailer.rb +2 -1
  85. data/spec/dummy/app/models/application_record.rb +2 -1
  86. data/spec/dummy/config/application.rb +2 -1
  87. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -1
  88. data/spec/dummy/config/routes.rb +2 -1
  89. data/spec/dummy/test/application_system_test_case.rb +2 -1
  90. data/spec/dummy/test/channels/application_cable/connection_test.rb +2 -1
  91. data/spec/spec_helper.rb +13 -1
  92. metadata +46 -39
  93. data/lib/kuby/plugins/rails_app/mysql.rb +0 -158
  94. data/lib/kuby/plugins/rails_app/postgres.rb +0 -163
data/lib/kuby/commands.rb CHANGED
@@ -3,9 +3,12 @@
3
3
  require 'kuby/version'
4
4
  require 'gli'
5
5
 
6
+ # run the pre hook for the help command
7
+ GLI::Commands::Help.skips_pre = false
8
+
6
9
  module Kuby
7
10
  class Commands
8
- extend T::Sig
11
+ # extend T::Sig
9
12
  extend GLI::App
10
13
 
11
14
  # GLI doesn't have a wildcard option, so it's impossible to tell it to
@@ -19,12 +22,12 @@ module Kuby
19
22
  # avoid the usual series of cryptic alias_method calls (note that there
20
23
  # is no singleton class version of #prepend in the Ruby language).
21
24
  singleton_class.send(:prepend, Module.new do
22
- extend T::Sig
25
+ # extend T::Sig
23
26
 
24
- sig { params(args: T::Array[String]).void }
27
+ # T::Sig::WithoutRuntime.sig { params(args: T::Array[String]).void }
25
28
  def run(args)
26
29
  if idx = args.index('rails') || idx = args.index('rake')
27
- @rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
30
+ # @rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
28
31
  @rails_options = args[(idx + 1)..-1]
29
32
  super(args[0..idx])
30
33
  else
@@ -34,11 +37,24 @@ module Kuby
34
37
  end
35
38
  end)
36
39
 
37
- sig { returns(Kuby::Tasks) }
40
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Tasks) }
38
41
  def self.tasks
39
42
  Kuby::Tasks.new(Kuby.environment)
40
43
  end
41
44
 
45
+ # T::Sig::WithoutRuntime.sig {
46
+ # params(
47
+ # global_options: T::Hash[T.any(String, Symbol), T.any(String, Integer)]
48
+ # ).void
49
+ # }
50
+ def self.load_kuby_config!(global_options)
51
+ return if @kuby_config_loaded
52
+
53
+ Kuby.env = global_options[:environment] if global_options[:environment]
54
+ Kuby.load!(global_options[:config])
55
+ @kuby_config_loaded = true
56
+ end
57
+
42
58
  program_desc 'Kuby command-line interface. Kuby is a convention '\
43
59
  'over configuration approach for running Rails apps in Kubernetes.'
44
60
 
@@ -54,19 +70,59 @@ module Kuby
54
70
  default_value './kuby.rb'
55
71
  flag [:c, :config]
56
72
 
73
+ command_missing do |command_name, global_options|
74
+ load_kuby_config!(global_options)
75
+ cmd = nil
76
+
77
+ # command_name is also the name of the plugin
78
+ if plugin_klass = Kuby.plugins.find(command_name)
79
+ if plugin_klass.respond_to?(:install_commands)
80
+ desc "Run commands for the #{command_name} plugin."
81
+ cmd = command(command_name) do |c|
82
+ # the plugin now defines its own commands on c
83
+ plugin_klass.install_commands(c)
84
+ end
85
+ end
86
+ end
87
+
88
+ cmd
89
+ end
90
+
57
91
  pre do |global_options, options, args|
58
- Kuby.env = global_options[:environment] if global_options[:environment]
59
- Kuby.load!(global_options[:config])
92
+ load_kuby_config!(global_options)
93
+
94
+ Kuby.plugins.each do |plugin_name, plugin_klass|
95
+ if plugin_klass.respond_to?(:commands) && !@commands[plugin_name]
96
+ desc "Run commands for the #{plugin_name} plugin."
97
+ command plugin_name.to_sym do |c|
98
+ plugin_klass.commands(c)
99
+ end
100
+ end
101
+ end
60
102
 
61
103
  # GLI will abort unless this block returns a truthy value
62
104
  true
63
105
  end
64
106
 
65
- desc 'Builds the Docker image.'
107
+ desc 'Builds Docker images.'
66
108
  command :build do |c|
109
+ c.desc 'Docker build argument.'
67
110
  c.flag [:a, :arg], required: false, multiple: true
68
- c.switch [:'ignore-missing-args'], required: false, default: false
69
- c.flag [:only], required: false
111
+
112
+ c.desc 'When enabled, ignores missing build arguments.'
113
+ c.switch [:'ignore-missing-args'], required: false, default_value: false
114
+
115
+ c.desc 'Build only the images associated with the specified identifier(s). '\
116
+ 'Run `kuby images` for a list of all valid identifiers (note that '\
117
+ 'identifiers can be associated with more than one image).'
118
+ c.flag [:only], required: false, multiple: true
119
+
120
+ c.desc 'The directory to use as the Docker build context.'
121
+ c.flag [:c, :context], required: false
122
+
123
+ c.desc 'Pull the latest images from the registry and reuse any previously built layers.'
124
+ c.switch [:l, :'cache-from-latest'], required: false, default_value: true
125
+
70
126
  c.action do |global_options, options, docker_args|
71
127
  build_args = {}.tap do |build_args|
72
128
  (options[:arg] || []).each do |a|
@@ -79,14 +135,19 @@ module Kuby
79
135
  tasks.build(
80
136
  build_args, docker_args,
81
137
  only: options[:only],
82
- ignore_missing_args: options[:'ignore-missing-args']
138
+ ignore_missing_args: options[:'ignore-missing-args'],
139
+ context: options[:context],
140
+ cache_from_latest: options[:'cache-from-latest']
83
141
  )
84
142
  end
85
143
  end
86
144
 
87
- desc 'Pushes the Docker image to the configured registry.'
145
+ desc 'Pushes Docker images to their associated registries.'
88
146
  command :push do |c|
89
- c.flag [:only], required: false
147
+ c.desc 'Push only the images associated with the specified identifier(s). '\
148
+ 'Run `kuby images` for a list of all valid identifiers (note that '\
149
+ 'identifiers can be associated with more than one image).'
150
+ c.flag [:only], required: false, multiple: true
90
151
  c.action do |global_options, options, args|
91
152
  tasks.push(only: options[:only])
92
153
  end
@@ -94,14 +155,18 @@ module Kuby
94
155
 
95
156
  desc 'Gets your Kubernetes cluster ready to run your Rails app.'
96
157
  command :setup do |c|
158
+ c.desc 'Run the setup routines for only the specified plugin identifier(s).'
159
+ c.flag [:only], required: false, multiple: true
97
160
  c.action do |global_options, options, args|
98
- tasks.setup
161
+ tasks.setup(only: options[:only])
99
162
  end
100
163
  end
101
164
 
102
165
  desc 'Prints the effective Dockerfiles used to build Docker images.'
103
166
  command :dockerfiles do |c|
104
- c.flag [:only], required: false
167
+ c.desc 'Print Dockerfiles for only the images associated with the specified '\
168
+ 'identifier(s).'
169
+ c.flag [:only], required: false, multiple: true
105
170
  c.action do |global_options, options, args|
106
171
  tasks.print_dockerfiles(only: options[:only])
107
172
  end
@@ -116,7 +181,7 @@ module Kuby
116
181
  end
117
182
  end
118
183
 
119
- desc 'Rolls back to the previous Docker tag.'
184
+ desc 'Rolls back to the previous release.'
120
185
  command :rollback do |c|
121
186
  c.action do |global_options, options, args|
122
187
  tasks.rollback
@@ -125,26 +190,38 @@ module Kuby
125
190
 
126
191
  desc 'Prints the effective Kubernetes resources that will be applied on deploy.'
127
192
  command :resources do |c|
193
+ c.desc 'Only print resources of the given kind.'
128
194
  c.flag [:K, :kind], required: false
195
+
196
+ c.desc 'Only print resources that match the given name.'
129
197
  c.flag [:N, :name], required: false
198
+
130
199
  c.action do |global_options, options, args|
131
200
  tasks.print_resources(options[:kind], options[:name])
132
201
  end
133
202
  end
134
203
 
135
- desc 'Prints out the contents of the kubeconfig Kuby is using to communicate with your cluster.'
204
+ desc 'Prints out the contents of the kubeconfig file Kuby is using to communicate '\
205
+ 'with your cluster.'
136
206
  command :kubeconfig do |c|
137
207
  c.action do |global_options, options, args|
138
208
  tasks.print_kubeconfig
139
209
  end
140
210
  end
141
211
 
212
+ desc 'Prints out the URLs to the latest Docker images in the Docker registry.'
213
+ command :images do |c|
214
+ c.action do |global_options, options, args|
215
+ tasks.print_images
216
+ end
217
+ end
218
+
142
219
  desc 'Runs an arbitrary kubectl command.'
143
220
  command :kubectl do |c|
144
221
  c.desc 'Prefixes the kubectl command with the namespace associated with '\
145
- 'the current environment. For example, if the Kuby env is "production", '\
146
- 'this option will prefix the kubectl command with "-n myapp-production".'
147
- c.switch [:N, :namespaced], default: false
222
+ 'the current environment. For example, if the Kuby env is "production", '\
223
+ 'this option will prefix the kubectl command with "-n myapp-production".'
224
+ c.switch [:N, :namespaced], default_value: false
148
225
  c.action do |global_options, options, args|
149
226
  if options[:namespaced]
150
227
  # sorry Demeter
@@ -156,6 +233,43 @@ module Kuby
156
233
  end
157
234
  end
158
235
 
236
+ desc 'Provides information about plugins.'
237
+ command :plugin do |rc|
238
+ rc.desc "Run a plugin's remove routine, i.e. uninstall it's resources from your cluster."
239
+ rc.command :remove do |c|
240
+ c.desc 'The plugin to remove. Run `kuby plugin list` for a list of valid plugin '\
241
+ 'identifiers.'
242
+ c.flag [:p, :plugin], required: true
243
+ c.action do |global_options, options, args|
244
+ tasks.remove_plugin(options[:plugin])
245
+ end
246
+ end
247
+
248
+ rc.desc 'List plugins.'
249
+ rc.command :list do |c|
250
+ c.desc 'Show all available plugins, not just the ones in use.'
251
+ c.switch [:a, :all], default_value: false
252
+ c.action do |global_options, options, args|
253
+ tasks.list_plugins(all: options[:all])
254
+ end
255
+ end
256
+
257
+ rc.desc "Run one of a plugin's rake tasks. Omit task names to print all tasks."
258
+ rc.arg_name 'task_name', [:multiple, :optional]
259
+ rc.command :rake do |c|
260
+ c.action do |global_options, options, args|
261
+ # Args come through as @rails_options here because of the monkeypatch
262
+ # at the top of this file. Should revisit the patch at some point because
263
+ # I think GLI's arg concept can replace it.
264
+ if @rails_options.empty?
265
+ tasks.list_rake_tasks
266
+ else
267
+ tasks.run_rake_tasks(@rails_options)
268
+ end
269
+ end
270
+ end
271
+ end
272
+
159
273
  desc 'Runs commands, etc against the Kubernetes cluster.'
160
274
  command :remote do |rc|
161
275
  rc.desc 'Tails (i.e. continuously streams) the Rails log from your running application.'
@@ -175,7 +289,7 @@ module Kuby
175
289
  rc.desc 'Runs an arbitrary command inside a running Rails pod.'
176
290
  rc.command :exec do |c|
177
291
  c.action do |global_options, options, args|
178
- tasks.remote_exec([*args, *T.unsafe(@rails_options)])
292
+ tasks.remote_exec([*args, *@rails_options])
179
293
  end
180
294
  end
181
295
 
@@ -2,23 +2,23 @@
2
2
 
3
3
  module Kuby
4
4
  class Definition
5
- extend T::Sig
5
+ # extend T::Sig
6
6
 
7
- sig { returns(String) }
7
+ # T::Sig::WithoutRuntime.sig { returns(String) }
8
8
  attr_reader :app_name
9
9
 
10
- sig { params(app_name: String, block: T.nilable(T.proc.void)).void }
10
+ # T::Sig::WithoutRuntime.sig { params(app_name: String, block: T.nilable(T.proc.void)).void }
11
11
  def initialize(app_name, &block)
12
12
  @app_name = app_name
13
- @environments = T.let(@environments, T.nilable(T::Hash[Symbol, Environment]))
13
+ # @environments = T.let(@environments, T.nilable(T::Hash[Symbol, Environment]))
14
14
  end
15
15
 
16
- sig {
17
- params(
18
- name: Symbol,
19
- block: T.nilable(T.proc.void)
20
- ).returns(Environment)
21
- }
16
+ # T::Sig::WithoutRuntime.sig {
17
+ # params(
18
+ # name: Symbol,
19
+ # block: T.nilable(T.proc.void)
20
+ # ).returns(Kuby::Environment)
21
+ # }
22
22
  def environment(name = Kuby.env, &block)
23
23
  name = name.to_s
24
24
 
@@ -28,10 +28,10 @@ module Kuby
28
28
  environments[name].instance_eval(&block)
29
29
  end
30
30
 
31
- T.must(environments[name])
31
+ environments[name]
32
32
  end
33
33
 
34
- sig { returns(T::Hash[Symbol, Environment]) }
34
+ # T::Sig::WithoutRuntime.sig { returns(T::Hash[Symbol, Kuby::Environment]) }
35
35
  def environments
36
36
  @environments ||= {}
37
37
  end
@@ -0,0 +1,20 @@
1
+ module Kuby
2
+ class Dependable
3
+ attr_reader :name, :version_or_callable
4
+
5
+ def initialize(name, version_or_callable)
6
+ @name = name
7
+ @version_or_callable = version_or_callable
8
+ end
9
+
10
+ def version
11
+ @version ||= Kuby::Utils::SemVer.parse_version(
12
+ if version_or_callable.respond_to?(:call)
13
+ version_or_callable.call
14
+ else
15
+ version_or_callable
16
+ end
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module Kuby
2
+ class Dependency
3
+ attr_reader :name, :constraints
4
+
5
+ def initialize(name, *constraints)
6
+ @name = name
7
+ @constraints = Kuby::Utils::SemVer.parse_constraints(*constraints)
8
+ end
9
+
10
+ def satisfied_by?(dependable)
11
+ constraints.satisfied_by?(dependable.version)
12
+ end
13
+ end
14
+ end
@@ -3,42 +3,42 @@
3
3
  module Kuby
4
4
  module Docker
5
5
  class Alpine < Distro
6
- SHELL_EXE = T.let('/bin/sh'.freeze, String)
6
+ SHELL_EXE = '/bin/sh'.freeze
7
7
 
8
- DEFAULT_PACKAGES = T.let([
8
+ DEFAULT_PACKAGES = [
9
9
  [:ca_certificates, nil],
10
10
  [:nodejs, '12.14.1'],
11
11
  [:yarn, '1.21.1'],
12
12
  [:c_toolchain, nil],
13
13
  [:tzdata, nil],
14
14
  [:git, nil]
15
- ].freeze, T::Array[[Symbol, T.nilable(String)]])
15
+ ].freeze
16
16
 
17
- sig { returns(Layer) }
17
+ # T::Sig::WithoutRuntime.sig { returns(Layer) }
18
18
  attr_reader :phase
19
19
 
20
- sig { override.params(packages: T::Array[Distro::PackageImpl], into: Dockerfile).void }
20
+ # T::Sig::WithoutRuntime.sig { override.params(packages: T::Array[Distro::PackageImpl], into: Dockerfile).void }
21
21
  def install(packages, into:)
22
22
  dockerfile = into
23
23
  install_managed(packages, dockerfile)
24
24
  install_unmanaged(packages, dockerfile)
25
25
  end
26
26
 
27
- sig { override.returns(T::Array[[Symbol, T.nilable(String)]]) }
27
+ # T::Sig::WithoutRuntime.sig { override.returns(T::Array[[Symbol, T.nilable(String)]]) }
28
28
  def default_packages
29
29
  DEFAULT_PACKAGES
30
30
  end
31
31
 
32
- sig { override.returns(String) }
32
+ # T::Sig::WithoutRuntime.sig { override.returns(String) }
33
33
  def shell_exe
34
34
  SHELL_EXE
35
35
  end
36
36
 
37
37
  private
38
38
 
39
- sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
39
+ # T::Sig::WithoutRuntime.sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
40
40
  def install_managed(packages, dockerfile)
41
- pkgs = T.cast(packages.select(&:managed?), T::Array[Distro::ManagedPackageImpl])
41
+ pkgs = packages.select(&:managed?)
42
42
 
43
43
  unless pkgs.empty?
44
44
  package_names = pkgs.map { |pkg| pkg.package_name_for(:alpine) }
@@ -48,7 +48,7 @@ module Kuby
48
48
  end
49
49
  end
50
50
 
51
- sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
51
+ # T::Sig::WithoutRuntime.sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
52
52
  def install_unmanaged(packages, dockerfile)
53
53
  packages
54
54
  .reject(&:managed?)
@@ -4,31 +4,22 @@
4
4
  module Kuby
5
5
  module Docker
6
6
  class AppImage < ::Kuby::Docker::TimestampedImage
7
- extend T::Sig
7
+ # extend T::Sig
8
8
 
9
- sig {
10
- params(
11
- dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
12
- image_url: String,
13
- credentials: Credentials,
14
- registry_index_url: T.nilable(String),
15
- main_tag: T.nilable(String),
16
- alias_tags: T::Array[String]
17
- ).void
18
- }
9
+ # T::Sig::WithoutRuntime.sig {
10
+ # params(
11
+ # dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
12
+ # image_url: String,
13
+ # credentials: Credentials,
14
+ # registry_index_url: T.nilable(String),
15
+ # main_tag: T.nilable(String),
16
+ # alias_tags: T::Array[String]
17
+ # ).void
18
+ # }
19
19
  def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
20
20
  super
21
21
  @identifier = "app"
22
22
  end
23
-
24
- sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String]).returns(AppImage) }
25
- def build(build_args = {}, docker_args = [])
26
- unless ENV.fetch('RAILS_MASTER_KEY', '').empty?
27
- build_args['RAILS_MASTER_KEY'] = T.must(ENV['RAILS_MASTER_KEY'])
28
- end
29
-
30
- super(build_args, docker_args)
31
- end
32
23
  end
33
24
  end
34
25
  end
@@ -0,0 +1,36 @@
1
+ # typed: strict
2
+
3
+ module Kuby
4
+ module Docker
5
+ class AppPhase < Layer
6
+ # extend T::Sig
7
+
8
+ # T::Sig::WithoutRuntime.sig { params(environment: Environment).void }
9
+ def initialize(environment)
10
+ super
11
+
12
+ @env_vars = {}
13
+ end
14
+
15
+ # T::Sig::WithoutRuntime.sig { override.params(dockerfile: Dockerfile).void }
16
+ def apply_to(dockerfile)
17
+ @env_vars.each_pair do |key, value|
18
+ dockerfile.env("#{key}='#{value}'")
19
+ end
20
+
21
+ absolute_app_root = Pathname(dockerfile.current_workdir)
22
+ .join(environment.docker.app_root_path)
23
+ .to_s
24
+
25
+ if dockerfile.current_workdir != absolute_app_root
26
+ dockerfile.workdir(absolute_app_root)
27
+ end
28
+ end
29
+
30
+ # T::Sig::WithoutRuntime.sig { params(key: String, value: String).void }
31
+ def env(key, value)
32
+ @env_vars[key] = value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -3,9 +3,9 @@
3
3
  module Kuby
4
4
  module Docker
5
5
  class AssetsPhase < Layer
6
- extend T::Sig
6
+ # extend T::Sig
7
7
 
8
- sig { override.params(dockerfile: Dockerfile).void }
8
+ # T::Sig::WithoutRuntime.sig { override.params(dockerfile: Dockerfile).void }
9
9
  def apply_to(dockerfile)
10
10
  dockerfile.run(
11
11
  'bundle', 'exec', 'rake', 'assets:precompile'
@@ -5,101 +5,103 @@ require 'pathname'
5
5
  module Kuby
6
6
  module Docker
7
7
  class BundlerPhase < Layer
8
- extend T::Sig
8
+ # extend T::Sig
9
9
 
10
- DEFAULT_WITHOUT = T.let(
11
- ['development', 'test', 'deploy'].freeze, T::Array[String]
12
- )
10
+ DEFAULT_GEMFILE = 'Gemfile'.freeze
11
+ DEFAULT_WITHOUT =
12
+ ['development', 'test', 'deploy'].freeze
13
+
13
14
 
14
- sig { returns(T.nilable(String)) }
15
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(String)) }
15
16
  attr_reader :version
16
17
 
17
- sig { params(version: String).void }
18
+ # T::Sig::WithoutRuntime.sig { params(version: String).returns(String) }
18
19
  attr_writer :version
19
20
 
20
- sig { returns(T.nilable(String)) }
21
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(String)) }
21
22
  attr_reader :gemfile
22
23
 
23
- sig { params(gemfile: String).void }
24
+ # T::Sig::WithoutRuntime.sig { params(gemfile: String).returns(String) }
24
25
  attr_writer :gemfile
25
26
 
26
- sig { returns(T.nilable(T::Array[String])) }
27
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(T::Array[String])) }
27
28
  attr_reader :without
28
29
 
29
- sig { params(without: T::Array[String]).void }
30
+ # T::Sig::WithoutRuntime.sig { params(without: T::Array[String]).returns(T::Array[String]) }
30
31
  attr_writer :without
31
32
 
32
- sig { returns(T.nilable(String)) }
33
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(String)) }
33
34
  attr_reader :executable
34
35
 
35
- sig { params(executable: String).void }
36
+ # T::Sig::WithoutRuntime.sig { params(executable: String).returns(String) }
36
37
  attr_writer :executable
37
38
 
38
- sig { params(environment: Environment).void }
39
+ # T::Sig::WithoutRuntime.sig { params(environment: Environment).void }
39
40
  def initialize(environment)
40
41
  super
41
42
 
42
- @version = T.let(@version, T.nilable(String))
43
- @gemfile = T.let(@gemfile, T.nilable(String))
44
- @gemfiles = T.let([], T::Array[String])
45
- @without = T.let(@without, T.nilable(T::Array[String]))
46
- @executable = T.let(@executable, T.nilable(String))
43
+ # @version = T.let(@version, T.nilable(String))
44
+ # @gemfile = T.let(@gemfile, T.nilable(String))
45
+ @gemfiles = []
46
+ # @without = T.let(@without, T.nilable(T::Array[String]))
47
+ # @executable = T.let(@executable, T.nilable(String))
47
48
  end
48
49
 
49
- sig { override.params(dockerfile: Dockerfile).void }
50
+ # T::Sig::WithoutRuntime.sig { override.params(dockerfile: Dockerfile).void }
50
51
  def apply_to(dockerfile)
51
- gf = gemfile || default_gemfile
52
+ gf = gemfile || DEFAULT_GEMFILE
52
53
  lf = "#{gf}.lock"
53
54
  v = version || default_version
54
55
  wo = without || DEFAULT_WITHOUT
55
56
 
56
- dockerfile.run('gem', 'install', 'bundler', '-v', v)
57
+ host_path = Pathname(environment.docker.app_root_path)
58
+ container_path = Pathname(dockerfile.current_workdir).join(environment.docker.app_root_path)
57
59
 
58
- # bundle install
59
- dockerfile.copy(gf, '.')
60
- dockerfile.copy(lf, '.')
60
+ dockerfile.run('gem', 'install', 'bundler', '-v', v)
61
61
 
62
+ dockerfile.copy(host_path.join(gf), container_path.join(gf))
63
+ dockerfile.copy(host_path.join(lf), container_path.join(lf))
62
64
  @gemfiles.each do |file|
63
- dockerfile.copy(file, file)
65
+ dockerfile.copy(host_path.join(file), container_path.join(file))
66
+ extra_lf = host_path.join("#{file}.lock")
67
+
68
+ if extra_lf.exist?
69
+ dockerfile.copy(extra_lf, container_path.join("#{file}.lock"))
70
+ end
64
71
  end
65
72
 
73
+ dockerfile.env("BUNDLE_GEMFILE=#{container_path.join(gf)}")
74
+
66
75
  unless wo.empty?
67
76
  dockerfile.env("BUNDLE_WITHOUT='#{wo.join(' ')}'")
68
77
  end
69
78
 
79
+ dockerfile.run(
80
+ executable || 'bundle', 'lock', '--lockfile', container_path.join(lf)
81
+ )
82
+
70
83
  dockerfile.run(
71
84
  executable || 'bundle', 'install',
72
85
  '--jobs', '$(nproc)',
73
- '--retry', '3',
74
- '--gemfile', gf
86
+ '--retry', '3'
75
87
  )
76
88
 
77
89
  # generate binstubs and add the bin directory to our path
78
90
  dockerfile.run(executable || 'bundle', 'binstubs', '--all')
79
- dockerfile.env("PATH=./bin:$PATH")
91
+ dockerfile.env("PATH=#{container_path.join('bin')}:$PATH")
80
92
  end
81
93
 
82
- sig { params(paths: String).void }
94
+ # T::Sig::WithoutRuntime.sig { params(paths: String).void }
83
95
  def gemfiles(*paths)
84
96
  @gemfiles.concat(paths)
85
97
  end
86
98
 
87
99
  private
88
100
 
89
- sig { returns(String) }
101
+ # T::Sig::WithoutRuntime.sig { returns(String) }
90
102
  def default_version
91
103
  Bundler::VERSION
92
104
  end
93
-
94
- sig { returns(String) }
95
- def default_gemfile
96
- Bundler
97
- .definition
98
- .gemfiles
99
- .first
100
- .relative_path_from(Pathname(Dir.getwd))
101
- .to_s
102
- end
103
105
  end
104
106
  end
105
107
  end