kuby-core 0.11.16 → 0.12.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/Gemfile +1 -2
  4. data/kuby-core.gemspec +1 -1
  5. data/lib/kuby.rb +2 -20
  6. data/lib/kuby/commands.rb +13 -67
  7. data/lib/kuby/docker.rb +27 -25
  8. data/lib/kuby/docker/alpine.rb +2 -1
  9. data/lib/kuby/docker/app_image.rb +19 -0
  10. data/lib/kuby/docker/cli.rb +4 -12
  11. data/lib/kuby/docker/docker_uri.rb +18 -7
  12. data/lib/kuby/docker/errors.rb +1 -19
  13. data/lib/kuby/docker/image.rb +115 -0
  14. data/lib/kuby/docker/layer.rb +0 -7
  15. data/lib/kuby/docker/local_tags.rb +9 -10
  16. data/lib/kuby/docker/package_phase.rb +0 -5
  17. data/lib/kuby/docker/packages.rb +1 -0
  18. data/lib/kuby/docker/remote_tags.rb +10 -5
  19. data/lib/kuby/docker/setup_phase.rb +17 -9
  20. data/lib/kuby/docker/spec.rb +29 -62
  21. data/lib/kuby/docker/timestamp_tag.rb +8 -1
  22. data/lib/kuby/docker/timestamped_image.rb +113 -0
  23. data/lib/kuby/environment.rb +1 -10
  24. data/lib/kuby/kubernetes/bare_metal_provider.rb +16 -4
  25. data/lib/kuby/kubernetes/deployer.rb +1 -1
  26. data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
  27. data/lib/kuby/kubernetes/spec.rb +21 -17
  28. data/lib/kuby/plugin.rb +2 -2
  29. data/lib/kuby/plugins/rails_app.rb +1 -0
  30. data/lib/kuby/plugins/rails_app/assets.rb +60 -70
  31. data/lib/kuby/plugins/rails_app/assets_image.rb +55 -0
  32. data/lib/kuby/plugins/rails_app/plugin.rb +53 -236
  33. data/lib/kuby/tasks.rb +30 -69
  34. data/lib/kuby/version.rb +1 -1
  35. data/spec/docker/spec_spec.rb +9 -118
  36. data/spec/docker/timestamped_image_spec.rb +123 -0
  37. data/spec/spec_helper.rb +10 -11
  38. metadata +9 -10
  39. data/lib/kuby/dev_setup.rb +0 -346
  40. data/lib/kuby/docker/dev_spec.rb +0 -202
  41. data/lib/kuby/docker/metadata.rb +0 -90
  42. data/lib/kuby/docker/tags.rb +0 -92
  43. data/lib/kuby/rails_commands.rb +0 -84
  44. data/spec/docker/metadata_spec.rb +0 -73
@@ -1,346 +0,0 @@
1
- # typed: strict
2
-
3
- module Kuby
4
- class Spinner
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)
18
- yield new(message)
19
- end
20
-
21
- sig { returns(String) }
22
- attr_reader :message
23
-
24
- sig { returns(Symbol) }
25
- attr_reader :status
26
-
27
- sig { params(message: String).void }
28
- def initialize(message)
29
- @message = T.let(message, String)
30
- @status = T.let(:running, Symbol)
31
- @thread = T.let(Thread.new do
32
- counter = 0
33
-
34
- while true
35
- case status
36
- when :running
37
- piece = PIECES[counter % PIECES.size]
38
- STDOUT.write "\r[#{piece}] #{message}"
39
- sleep INTERVAL
40
- counter += 1
41
- when :success
42
- STDOUT.write("\r[+] #{message}")
43
- break
44
- when :failure
45
- STDOUT.write("\r[×] #{message}")
46
- break
47
- end
48
- end
49
-
50
- puts
51
- end, Thread)
52
- end
53
-
54
- sig { void }
55
- def success
56
- @status = :success
57
- @thread.join
58
- end
59
-
60
- sig { void }
61
- def failure
62
- @status = :failure
63
- @thread.join
64
- end
65
- end
66
-
67
- class SetupTask
68
- extend T::Sig
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 }
77
- def initialize(message, callable)
78
- @message = message
79
- @callable = callable
80
- end
81
-
82
- sig { void }
83
- def run
84
- callable.call
85
- end
86
- end
87
-
88
- class Pipe
89
- extend T::Sig
90
-
91
- sig { returns(Symbol) }
92
- attr_reader :name
93
-
94
- sig { returns(T.untyped) }
95
- attr_reader :cli
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 }
104
- def initialize(name, cli)
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)
109
- end
110
-
111
- sig { params(block: T.proc.void).void }
112
- def wrap(&block)
113
- cli.with_pipes(out, err) do
114
- block.call
115
- end
116
- end
117
-
118
- sig { returns(T::Boolean) }
119
- def success?
120
- cli.last_status.nil? || cli.last_status.success?
121
- end
122
- end
123
-
124
- class Pipes
125
- extend T::Sig
126
- extend T::Generic
127
-
128
- include Enumerable
129
-
130
- Elem = type_member(fixed: Pipe)
131
-
132
- sig { returns(T::Array[Pipe]) }
133
- attr_reader :pipes
134
-
135
- sig { returns(T.nilable(StandardError)) }
136
- attr_reader :ex
137
-
138
- sig { params(clis: T::Hash[Symbol, T.untyped]).returns(Pipes) }
139
- def self.build(clis)
140
- new(clis.map { |name, cli| Pipe.new(name, cli) })
141
- end
142
-
143
- sig { params(pipes: T::Array[Pipe]).void }
144
- def initialize(pipes)
145
- @ex = T.let(@ex, T.nilable(StandardError))
146
- @pipes = T.let(pipes, T::Array[Pipe])
147
- end
148
-
149
- sig {
150
- override.params(
151
- block: T.proc.params(package: Pipe).void
152
- )
153
- .void
154
- }
155
- def each(&block)
156
- pipes.each(&block)
157
- end
158
-
159
- sig { params(block: T.proc.void).void }
160
- def wrap(&block)
161
- do_wrap(pipes, &block)
162
- end
163
-
164
- sig { returns(T::Boolean) }
165
- def success?
166
- pipes.all?(&:success?) && !ex
167
- end
168
-
169
- private
170
-
171
- sig {
172
- params(
173
- remaining_pipes: T::Array[Pipe],
174
- block: T.proc.void
175
- )
176
- .void
177
- }
178
- def do_wrap(remaining_pipes, &block)
179
- if remaining_pipes.empty?
180
- begin
181
- yield
182
- rescue => e
183
- @ex = e
184
- end
185
-
186
- return
187
- end
188
-
189
- T.must(remaining_pipes[0]).wrap do
190
- do_wrap(T.must(remaining_pipes[1..-1]), &block)
191
- end
192
- end
193
- end
194
-
195
- class SetupTaskList
196
- extend T::Sig
197
-
198
- sig { returns(T::Array[SetupTask]) }
199
- attr_reader :tasks
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 }
205
- def initialize(tasks, clis)
206
- @tasks = tasks
207
- @clis = clis
208
- end
209
-
210
- sig { void }
211
- def run
212
- tasks.each do |task|
213
- pipes = Pipes.build(clis)
214
-
215
- Spinner.spin(task.message) do |spinner|
216
- pipes.wrap { task.run }
217
-
218
- if pipes.success?
219
- spinner.success
220
- else
221
- spinner.failure
222
- print_error(pipes.ex)
223
-
224
- pipes.each do |pipe|
225
- print_streams(pipe)
226
- end
227
-
228
- return false
229
- end
230
- end
231
- end
232
-
233
- true
234
- end
235
-
236
- private
237
-
238
- sig { params(pipe: Pipe).void }
239
- def print_streams(pipe)
240
- unless pipe.out.string.strip.empty?
241
- puts("========= #{pipe.name.upcase} STDOUT ========")
242
- puts pipe.out.string
243
- end
244
-
245
- unless pipe.err.string.strip.empty?
246
- puts("========= #{pipe.name.upcase} STDERR ========")
247
- puts pipe.err.string
248
- end
249
- end
250
-
251
- sig { params(ex: T.nilable(StandardError)).void }
252
- def print_error(ex)
253
- return unless ex
254
- puts("========= RUBY ERROR ========")
255
- puts(ex.message)
256
- puts(T.must(ex.backtrace).join("\n"))
257
- end
258
- end
259
-
260
- class DevSetup
261
- extend T::Sig
262
-
263
- sig { returns(Environment) }
264
- attr_reader :environment
265
-
266
- sig { params(environment: Environment).void }
267
- def initialize(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))
272
- end
273
-
274
- sig { void }
275
- def run
276
- SetupTaskList.new(setup_tasks, clis).run
277
- end
278
-
279
- private
280
-
281
- sig { returns(T::Array[SetupTask]) }
282
- def setup_tasks
283
- @setup_tasks ||= [
284
- SetupTask.new(
285
- 'Building dev Docker image', -> { tasks.build }
286
- ),
287
-
288
- SetupTask.new(
289
- 'Setting up local Kubernetes cluster', -> { tasks.setup }
290
- ),
291
-
292
- SetupTask.new(
293
- 'Deploying dev environment', -> { tasks.deploy }
294
- ),
295
-
296
- SetupTask.new(
297
- 'Installing bundler', -> {
298
- tasks.remote_system("gem install bundler -v #{Bundler::VERSION}")
299
- }
300
- ),
301
-
302
- SetupTask.new(
303
- 'Installing bundled gems', -> { tasks.remote_system('bundle install') }
304
- ),
305
-
306
- SetupTask.new(
307
- 'Installing Javascript packages', -> { tasks.remote_system('yarn install') }
308
- ),
309
-
310
- SetupTask.new(
311
- 'Creating database', -> { tasks.remote_system('bundle exec rake db:create') }
312
- ),
313
-
314
- SetupTask.new(
315
- 'Migrating database', -> { tasks.remote_system('bundle exec rake db:migrate') }
316
- )
317
- ]
318
- end
319
-
320
- sig { returns(Kubernetes::Spec) }
321
- def kubernetes
322
- environment.kubernetes
323
- end
324
-
325
- sig { returns(Docker::Spec) }
326
- def docker
327
- environment.docker
328
- end
329
-
330
- sig { returns T::Hash[Symbol, T.untyped] }
331
- def clis
332
- @clis ||= {
333
- kubectl: kubernetes.provider.kubernetes_cli,
334
- helm: kubernetes.provider.helm_cli,
335
- krane: kubernetes.provider.deployer,
336
- docker: docker.cli,
337
- kuby: Kuby.logger
338
- }
339
- end
340
-
341
- sig { returns(Tasks) }
342
- def tasks
343
- @tasks ||= Kuby::Tasks.new(environment)
344
- end
345
- end
346
- end
@@ -1,202 +0,0 @@
1
- # typed: strict
2
-
3
- module Kuby
4
- module Docker
5
- class WebserverDevPhase < Layer
6
- extend T::Sig
7
-
8
- DEFAULT_PORT = T.let(3000, Integer)
9
-
10
- sig { params(port: Integer).void }
11
- attr_writer :port
12
-
13
- sig { params(environment: Environment).void }
14
- def initialize(environment)
15
- super
16
-
17
- @port = T.let(@port, T.nilable(Integer))
18
- end
19
-
20
- sig { override.params(dockerfile: Dockerfile).void }
21
- def apply_to(dockerfile)
22
- dockerfile.expose(port)
23
- end
24
-
25
- sig { returns(Integer) }
26
- def port
27
- @port || DEFAULT_PORT
28
- end
29
- end
30
-
31
- class DevSpec
32
- extend T::Sig
33
-
34
- sig { returns(Environment) }
35
- attr_reader :environment
36
-
37
- sig { params(environment: Environment).void }
38
- def initialize(environment)
39
- @environment = environment
40
-
41
- @setup_phase = T.let(@setup_phase, T.nilable(SetupPhase))
42
- @package_phase = T.let(@package_phase, T.nilable(PackagePhase))
43
- @webserver_phase = T.let(@webserver_phase, T.nilable(WebserverDevPhase))
44
- @metadata = T.let(@metadata, T.nilable(Metadata))
45
- @distro_spec = T.let(@distro_spec, T.nilable(Distro))
46
- @cli = T.let(@cli, T.nilable(CLI))
47
- @tags = T.let(@tags, T.nilable(LocalTags))
48
- @layer_stack = T.let(@layer_stack, T.nilable(Docker::LayerStack))
49
- end
50
-
51
- sig { params(dir: String).void }
52
- def working_dir(dir)
53
- setup_phase.working_dir = dir
54
- end
55
-
56
- sig { params(env: String).void }
57
- def rails_env(env)
58
- setup_phase.rails_env = env
59
- end
60
-
61
- sig {
62
- params(
63
- package_name: Symbol,
64
- version: T.nilable(String)
65
- )
66
- .void
67
- }
68
- def package(package_name, version = nil)
69
- package_phase.add(package_name, version)
70
- end
71
-
72
- sig { params(distro_name: Symbol).void }
73
- def distro(distro_name)
74
- metadata.distro = distro_name
75
- @distro_spec = nil
76
- end
77
-
78
- sig { params(port: Integer).void }
79
- def port(port)
80
- webserver_phase.port = port
81
- end
82
-
83
- sig {
84
- params(
85
- name: Symbol,
86
- layer: T.nilable(Layer),
87
- block: T.nilable(T.proc.params(df: Dockerfile).void)
88
- )
89
- .void
90
- }
91
- def use(name, layer = nil, &block)
92
- layer_stack.use(name, layer, &block)
93
- end
94
-
95
- sig {
96
- params(
97
- name: Symbol,
98
- layer: T.nilable(T.any(Layer, T::Hash[Symbol, T.untyped])),
99
- options: T::Hash[Symbol, T.untyped],
100
- block: T.nilable(T.proc.params(df: Dockerfile).void)
101
- )
102
- .void
103
- }
104
- def insert(name, layer = nil, options = {}, &block)
105
- layer_stack.insert(name, layer, options, &block)
106
- end
107
-
108
- sig { params(name: Symbol).void }
109
- def delete(name)
110
- layer_stack.delete(name)
111
- end
112
-
113
- sig { params(name: Symbol).returns(T::Boolean) }
114
- def exists?(name)
115
- layer_stack.includes?(name)
116
- end
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
-
125
- sig { returns(Dockerfile) }
126
- def to_dockerfile
127
- Dockerfile.new.tap do |df|
128
- layer_stack.each { |layer| layer.apply_to(df) }
129
- df.cmd("#{distro_spec.shell_exe} -c 'while test 1; do sleep 5; done'")
130
- end
131
- end
132
-
133
- sig { returns(SetupPhase) }
134
- def setup_phase
135
- @setup_phase ||= SetupPhase.new(environment)
136
- end
137
-
138
- sig { returns(PackagePhase) }
139
- def package_phase
140
- @package_phase ||= PackagePhase.new(environment)
141
- end
142
-
143
- sig { returns(WebserverDevPhase) }
144
- def webserver_phase
145
- @webserver_phase ||= WebserverDevPhase.new(environment)
146
- end
147
-
148
- sig { returns(Metadata) }
149
- def metadata
150
- @metadata ||= Metadata.new(environment)
151
- end
152
-
153
- sig { returns(Distro) }
154
- def distro_spec
155
- @distro_spec ||= if distro_klass = Kuby.distros[metadata.distro]
156
- distro_klass.new(self)
157
- else
158
- raise MissingDistroError, "distro '#{metadata.distro}' hasn't been registered"
159
- end
160
- end
161
-
162
- sig { returns(String) }
163
- def tag
164
- t = ENV.fetch('KUBY_DOCKER_TAG') do
165
- tags.latest_timestamp_tag
166
- end
167
-
168
- unless t
169
- raise MissingTagError, 'could not find latest timestamped tag'
170
- end
171
-
172
- t.to_s
173
- end
174
-
175
- sig { params(current_tag: String).returns(String) }
176
- def previous_tag(current_tag)
177
- raise MissingTagError, 'cannot roll back in the development environment'
178
- end
179
-
180
- sig { returns(CLI) }
181
- def cli
182
- @cli ||= Docker::CLI.new
183
- end
184
-
185
- private
186
-
187
- sig { returns(LocalTags) }
188
- def tags
189
- @tags ||= LocalTags.new(cli, metadata)
190
- end
191
-
192
- sig { returns(Docker::LayerStack) }
193
- def layer_stack
194
- @layer_stack ||= Docker::LayerStack.new.tap do |stack|
195
- stack.use(:setup_phase, setup_phase)
196
- stack.use(:package_phase, package_phase)
197
- stack.use(:webserver_phase, webserver_phase)
198
- end
199
- end
200
- end
201
- end
202
- end