kuby-core 0.11.16 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/Gemfile +1 -2
  4. data/README.md +2 -1
  5. data/kuby-core.gemspec +1 -1
  6. data/lib/kuby/commands.rb +25 -73
  7. data/lib/kuby/docker/alpine.rb +2 -1
  8. data/lib/kuby/docker/app_image.rb +34 -0
  9. data/lib/kuby/docker/bundler_phase.rb +10 -0
  10. data/lib/kuby/docker/cli.rb +7 -13
  11. data/lib/kuby/docker/docker_uri.rb +50 -9
  12. data/lib/kuby/docker/errors.rb +1 -19
  13. data/lib/kuby/docker/image.rb +142 -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 +38 -62
  21. data/lib/kuby/docker/timestamp_tag.rb +8 -1
  22. data/lib/kuby/docker/timestamped_image.rb +114 -0
  23. data/lib/kuby/docker.rb +27 -25
  24. data/lib/kuby/environment.rb +1 -10
  25. data/lib/kuby/kubernetes/bare_metal_provider.rb +16 -4
  26. data/lib/kuby/kubernetes/deployer.rb +1 -1
  27. data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
  28. data/lib/kuby/kubernetes/spec.rb +21 -17
  29. data/lib/kuby/plugin.rb +2 -2
  30. data/lib/kuby/plugins/rails_app/assets.rb +62 -70
  31. data/lib/kuby/plugins/rails_app/assets_image.rb +57 -0
  32. data/lib/kuby/plugins/rails_app/generators/kuby.rb +31 -9
  33. data/lib/kuby/plugins/rails_app/plugin.rb +53 -236
  34. data/lib/kuby/plugins/rails_app.rb +1 -0
  35. data/lib/kuby/tasks.rb +98 -69
  36. data/lib/kuby/version.rb +1 -1
  37. data/lib/kuby.rb +2 -20
  38. data/spec/docker/spec_spec.rb +21 -118
  39. data/spec/docker/timestamped_image_spec.rb +123 -0
  40. data/spec/spec_helper.rb +10 -11
  41. metadata +10 -14
  42. data/lib/kuby/dev_setup.rb +0 -346
  43. data/lib/kuby/docker/dev_spec.rb +0 -202
  44. data/lib/kuby/docker/metadata.rb +0 -90
  45. data/lib/kuby/docker/tags.rb +0 -92
  46. data/lib/kuby/rails_commands.rb +0 -84
  47. data/spec/docker/metadata_spec.rb +0 -73
  48. data/spec/dummy/Gemfile.lock +0 -223
  49. data/spec/dummy/config/master.key +0 -1
  50. data/spec/dummy/tmp/cache/bootsnap-load-path-cache +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01cc5428a8bc4bfc44143f4aa2c2e949405d3c7231831da50f33efa5a314fc29
4
- data.tar.gz: c4e440506c9ad072449f3382bcfa7dd1f80c757e64426b5c49f1e20540c57ae8
3
+ metadata.gz: 0a5f265cee423b7487de7e4c209b26cc9fef63cf584455bc4164ecd7e6384377
4
+ data.tar.gz: e48304bf9397b30ceed7025039d074e55423ced190f734a416a311e412bd609d
5
5
  SHA512:
6
- metadata.gz: 5db95c1958618d58354ba4c87411a0cd52894ede6ba0fbd622479e66e327397967314979c7496806577115d6b22571582e17f76c720a8472c9568cb009682555
7
- data.tar.gz: 2c1c1bd58fcbc7d29380e5e2ef2cf71b68f7073dbff26ca5d73333734b326dccc42243a559b3272502b4a6628db0c7513ea9b6586b5475e44108ba4a490734f5
6
+ metadata.gz: d4cbd6c2fbf6de8f621d4b2412bb4c1921eff7e5ba3c90b5afefac3c0d216aff676772b0582dab70a688c3c1dc7ce55a48f9c42f3b178ad25f31d489a3f33421
7
+ data.tar.gz: b9b3f808ed6184c73edb8652a958cd53b12337c8a48bf2f19d188b07c1c4d56283f7bfbfffd2a9338dc522a2376f8665e9c792571b4fdb609bf14b0181efcc16
data/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## 0.15.0
2
+ * Add an extra parameter to the Docker spec for specifying the URL of the Docker registry index.
3
+ - In most cases, the registry and registry index URLs will be the same. However Docker Hub, the default registry, uses index.docker.io for API requests (catalog, tags, etc) but only allows pushes to docker.io.
4
+ * Add config/master.key to the .dockerignore created by the Rails generator.
5
+ - This was an unfortunate oversight, but such are the perils of using pre-1.0 software.
6
+ * Print an error message if building images on non-x86 hardware.
7
+ - People with M1 Macs are running into mismatches between their laptop's arch and the arch of their hosting provider's hardware. The error message asks you to explicitly pass the `--platform` flag to Docker.
8
+ - We may need to revisit this in the future if ARM processors become more popular.
9
+
10
+ ## 0.14.0
11
+ * Make RAILS_MASTER_KEY available in the assets image (@palkan, #63)
12
+ * Allow specifying custom docker build options via CLI (@palkan, #65)
13
+
14
+ ## 0.13.0
15
+ * Fix handling rails/rake options in remote exec (@palkan, #60)
16
+ * Add `bundler_phase.gemfiles(*paths)` to allow adding additional gemfiles (@palkan, #61)
17
+
18
+ ## 0.12.0
19
+ * Fix issue causing volume mount errors when k8s tries to schedule asset pods on multiple nodes (fixes #42).
20
+ - Persistent volumes can only be mounted on a single physical node.
21
+ - Kuby now creates a separate assets image powered by nginx.
22
+ - Ingress routes requests to either the Rails service or assets service depending on the URL (i.e. /assets).
23
+ - Since the ingress layer is now essential, the Docker Desktop provider no longer deletes ingresses or monkeys with service objects. You'll have to either 1) add an entry for your hostname to your hosts file, or 2) set the `Host` header when making requests to your app.
24
+ * Plugins can now specify additional Dockerfiles to build during `kuby build`.
25
+ * Switch from TravisCI to Github Actions.
26
+ * Remove support for development environments.
27
+ - Proved to be too difficult to maintain.
28
+ - Kuby is a _deployment_ tool anyway, not a dev tool.
29
+ - Maybe we can turn the dev stuff into a gem at some point.
30
+ * Refactor Docker image logic.
31
+ - Introduced the `Image` class and friends.
32
+ - Deleted the `Kuby::Docker::Metadata` class.
33
+ * Add missing git dependency to Alpine distro.
34
+ * Add a bare metal provider (fixes #10).
35
+
1
36
  ## 0.11.16
2
37
  * Fix yarn phase
3
38
  - Apparently you have to copy at least one file, TIL.
data/Gemfile CHANGED
@@ -6,10 +6,9 @@ group :development, :test do
6
6
  gem 'pry-byebug'
7
7
  gem 'rake'
8
8
  # lock to a specific version to prevent breaking CI when new versions come out
9
- gem 'sorbet', '= 0.5.6397'
9
+ gem 'sorbet', '= 0.5.6433'
10
10
  end
11
11
 
12
12
  group :test do
13
13
  gem 'rspec', '~> 3.0'
14
- gem 'timecop', '~> 0.9'
15
14
  end
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  ## Kuby
2
2
 
3
- [![Build Status](https://travis-ci.com/getkuby/kuby-core.svg?branch=master)](https://travis-ci.com/getkuby/kuby-core)
3
+ ![Unit Tests](https://github.com/getkuby/kuby-core/actions/workflows/unit_tests.yml/badge.svg?branch=master)
4
+ ![Integration Tests](https://github.com/getkuby/kuby-core/actions/workflows/integration_tests.yml/badge.svg?branch=master)
4
5
 
5
6
  Deploy your Rails app the easy way.
6
7
 
data/kuby-core.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.platform = Gem::Platform::RUBY
14
14
 
15
15
  s.add_dependency 'colorize', '~> 0.8'
16
- s.add_dependency 'docker-remote', '~> 0.5'
16
+ s.add_dependency 'docker-remote', '~> 0.6'
17
17
  s.add_dependency 'gli', '~> 2.0'
18
18
  s.add_dependency 'helm-cli', '~> 0.3'
19
19
  # See: https://github.com/Shopify/krane/pull/720
data/lib/kuby/commands.rb CHANGED
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: false
2
2
 
3
3
  require 'kuby/version'
4
4
  require 'gli'
@@ -25,9 +25,10 @@ module Kuby
25
25
  def run(args)
26
26
  if idx = args.index('rails') || idx = args.index('rake')
27
27
  @rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
28
- @rails_options = args[idx..-1]
28
+ @rails_options = args[(idx + 1)..-1]
29
29
  super(args[0..idx])
30
30
  else
31
+ @rails_options = []
31
32
  super
32
33
  end
33
34
  end
@@ -38,13 +39,6 @@ module Kuby
38
39
  Kuby::Tasks.new(Kuby.environment)
39
40
  end
40
41
 
41
- sig { void }
42
- def self.must_be_dev_env!
43
- unless Kuby.environment.development?
44
- fail "Command not supported in the '#{Kuby.environment.name}' environment"
45
- end
46
- end
47
-
48
42
  program_desc 'Kuby command-line interface. Kuby is a convention '\
49
43
  'over configuration approach for running Rails apps in Kubernetes.'
50
44
 
@@ -68,66 +62,28 @@ module Kuby
68
62
  true
69
63
  end
70
64
 
71
- # These are only stubs included to fill out the help screens. Rails
72
- # commands are handled by the RailsCommands class.
73
- desc 'Runs a Rails command.'
74
- command :rails do |rc|
75
- rc.action do |global_options, options, args|
76
- must_be_dev_env!
77
- exit 1 unless tasks.dev_deployment_ok
78
- @rails_options = T.let(@rails_options, T.nilable(T::Array[String]))
79
- Kuby::RailsCommands.run(@rails_options)
80
- end
81
-
82
- rc.desc 'Runs the rails server (run `rails server --help` for options)'
83
- rc.command [:server, :s] do |c|
84
- c.action do |global_options, options, args|
85
- must_be_dev_env!
86
- exit 1 unless tasks.dev_deployment_ok
87
- Kuby::RailsCommands.run(@rails_options)
88
- end
89
- end
90
-
91
- rc.desc 'Runs a script in the Rails environment (run `rails runner --help` for options)'
92
- rc.command [:runner, :r] do |c|
93
- c.action do |global_options, options, args|
94
- must_be_dev_env!
95
- exit 1 unless tasks.dev_deployment_ok
96
- Kuby::RailsCommands.run(@rails_options)
97
- end
98
- end
99
-
100
- rc.desc 'Starts an interactive Ruby console with the Rails environment loaded '\
101
- '(run `rails console --help` for options)'
102
- rc.command [:console, :c] do |c|
103
- c.action do |global_options, options, args|
104
- must_be_dev_env!
105
- exit 1 unless tasks.dev_deployment_ok
106
- Kuby::RailsCommands.run(@rails_options)
107
- end
108
- end
109
- end
110
-
111
- desc 'Runs a rake task.'
112
- command :rake do |rc|
113
- rc.action do |global_options, options, args|
114
- must_be_dev_env!
115
- exit 1 unless tasks.dev_deployment_ok
116
- Kuby::RailsCommands.run(@rails_options)
117
- end
118
- end
119
-
120
65
  desc 'Builds the Docker image.'
121
66
  command :build do |c|
122
- c.action do |global_options, options, args|
123
- tasks.build
67
+ c.flag [:a, :arg], required: false, multiple: true
68
+ c.flag [:only], required: false
69
+ c.action do |global_options, options, docker_args|
70
+ build_args = {}.tap do |build_args|
71
+ (options[:arg] || []).each do |a|
72
+ key, value = a.split('=', 2)
73
+ value = value[1..-2] if value.start_with?('"') || value.start_with?("'")
74
+ build_args[key] = value
75
+ end
76
+ end
77
+
78
+ tasks.build(build_args, docker_args, options[:only])
124
79
  end
125
80
  end
126
81
 
127
82
  desc 'Pushes the Docker image to the configured registry.'
128
83
  command :push do |c|
84
+ c.flag [:only], required: false
129
85
  c.action do |global_options, options, args|
130
- tasks.push
86
+ tasks.push(options[:only])
131
87
  end
132
88
  end
133
89
 
@@ -138,10 +94,11 @@ module Kuby
138
94
  end
139
95
  end
140
96
 
141
- desc 'Prints the effective Dockerfile used to build the Docker image.'
142
- command :dockerfile do |c|
97
+ desc 'Prints the effective Dockerfiles used to build Docker images.'
98
+ command :dockerfiles do |c|
99
+ c.flag [:only], required: false
143
100
  c.action do |global_options, options, args|
144
- tasks.print_dockerfile
101
+ tasks.print_dockerfiles(options[:only])
145
102
  end
146
103
  end
147
104
 
@@ -163,8 +120,10 @@ module Kuby
163
120
 
164
121
  desc 'Prints the effective Kubernetes resources that will be applied on deploy.'
165
122
  command :resources do |c|
123
+ c.flag [:K, :kind], required: false
124
+ c.flag [:N, :name], required: false
166
125
  c.action do |global_options, options, args|
167
- tasks.print_resources
126
+ tasks.print_resources(options[:kind], options[:name])
168
127
  end
169
128
  end
170
129
 
@@ -197,7 +156,6 @@ module Kuby
197
156
  rc.desc 'Tails (i.e. continuously streams) the Rails log from your running application.'
198
157
  rc.command :logs do |c|
199
158
  c.action do |global_options, options, args|
200
- exit 1 unless tasks.dev_deployment_ok
201
159
  tasks.remote_logs
202
160
  end
203
161
  end
@@ -205,7 +163,6 @@ module Kuby
205
163
  rc.desc 'Lists running Kubernetes pods.'
206
164
  rc.command :status do |c|
207
165
  c.action do |global_options, options, args|
208
- exit 1 unless tasks.dev_deployment_ok
209
166
  tasks.remote_status
210
167
  end
211
168
  end
@@ -213,15 +170,13 @@ module Kuby
213
170
  rc.desc 'Runs an arbitrary command inside a running Rails pod.'
214
171
  rc.command :exec do |c|
215
172
  c.action do |global_options, options, args|
216
- exit 1 unless tasks.dev_deployment_ok
217
- tasks.remote_exec(args)
173
+ tasks.remote_exec([*args, *T.unsafe(@rails_options)])
218
174
  end
219
175
  end
220
176
 
221
177
  rc.desc 'Establishes a shell inside a running Rails pod.'
222
178
  rc.command :shell do |c|
223
179
  c.action do |global_options, options, args|
224
- exit 1 unless tasks.dev_deployment_ok
225
180
  tasks.remote_shell
226
181
  end
227
182
  end
@@ -229,7 +184,6 @@ module Kuby
229
184
  rc.desc 'Establishes a Rails console inside a running Rails pod.'
230
185
  rc.command :console do |c|
231
186
  c.action do |global_options, options, args|
232
- exit 1 unless tasks.dev_deployment_ok
233
187
  tasks.remote_console
234
188
  end
235
189
  end
@@ -237,7 +191,6 @@ module Kuby
237
191
  rc.desc 'Establishes a database console inside a running Rails pod.'
238
192
  rc.command :dbconsole do |c|
239
193
  c.action do |global_options, options, args|
240
- exit 1 unless tasks.dev_deployment_ok
241
194
  tasks.remote_dbconsole
242
195
  end
243
196
  end
@@ -245,7 +198,6 @@ module Kuby
245
198
  rc.desc "Restarts the Rails app's web pods."
246
199
  rc.command :restart do |c|
247
200
  c.action do |global_options, options, args|
248
- exit 1 unless tasks.dev_deployment_ok
249
201
  tasks.remote_restart
250
202
  end
251
203
  end
@@ -10,7 +10,8 @@ module Kuby
10
10
  [:nodejs, '12.14.1'],
11
11
  [:yarn, '1.21.1'],
12
12
  [:c_toolchain, nil],
13
- [:tzdata, nil]
13
+ [:tzdata, nil],
14
+ [:git, nil]
14
15
  ].freeze, T::Array[[Symbol, T.nilable(String)]])
15
16
 
16
17
  sig { returns(Layer) }
@@ -0,0 +1,34 @@
1
+
2
+ # typed: strict
3
+
4
+ module Kuby
5
+ module Docker
6
+ class AppImage < ::Kuby::Docker::TimestampedImage
7
+ extend T::Sig
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
+ }
19
+ def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
20
+ super
21
+ @identifier = "app"
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
+ end
33
+ end
34
+ end
@@ -35,6 +35,7 @@ module Kuby
35
35
 
36
36
  @version = T.let(@version, T.nilable(String))
37
37
  @gemfile = T.let(@gemfile, T.nilable(String))
38
+ @gemfiles = T.let([], T::Array[String])
38
39
  @without = T.let(@without, T.nilable(T::Array[String]))
39
40
  end
40
41
 
@@ -51,6 +52,10 @@ module Kuby
51
52
  dockerfile.copy(gf, '.')
52
53
  dockerfile.copy(lf, '.')
53
54
 
55
+ @gemfiles.each do |file|
56
+ dockerfile.copy(file, file)
57
+ end
58
+
54
59
  unless wo.empty?
55
60
  dockerfile.env("BUNDLE_WITHOUT='#{wo.join(' ')}'")
56
61
  end
@@ -67,6 +72,11 @@ module Kuby
67
72
  dockerfile.env("PATH=./bin:$PATH")
68
73
  end
69
74
 
75
+ sig { params(paths: String).void }
76
+ def gemfiles(*paths)
77
+ @gemfiles.concat(paths)
78
+ end
79
+
70
80
  private
71
81
 
72
82
  sig { returns(String) }
@@ -53,27 +53,21 @@ module Kuby
53
53
  config.fetch('auths', {}).keys
54
54
  end
55
55
 
56
- sig {
57
- params(
58
- dockerfile: Dockerfile,
59
- image_url: String,
60
- tags: T::Array[String],
61
- build_args: T::Hash[T.any(Symbol, String), String]
62
- )
63
- .void
64
- }
65
- def build(dockerfile:, image_url:, tags:, build_args: {})
56
+ sig { params(image: Image, build_args: T::Hash[T.any(Symbol, String), String], docker_args: T::Array[String]).void }
57
+ def build(image, build_args: {}, docker_args: [])
66
58
  cmd = [
67
59
  executable, 'build',
68
- *tags.flat_map { |tag| ['-t', "#{image_url}:#{tag}"] },
60
+ *image.tags.flat_map { |tag| ['-t', "#{image.image_url}:#{tag}"] },
69
61
  *build_args.flat_map do |arg, val|
70
62
  ['--build-arg', Shellwords.shellescape("#{arg}=#{val}")]
71
63
  end,
72
- '-f-', '.'
64
+ '-f-',
65
+ *docker_args,
66
+ '.'
73
67
  ]
74
68
 
75
69
  open3_w(cmd) do |stdin, _wait_threads|
76
- stdin.puts(dockerfile.to_s)
70
+ stdin.puts(image.dockerfile.to_s)
77
71
  end
78
72
 
79
73
  unless T.must(last_status).success?
@@ -1,34 +1,75 @@
1
- # typed: false
1
+ # typed: strict
2
2
 
3
3
  module Kuby
4
4
  module Docker
5
5
  class DockerURI
6
- DEFAULT_REGISTRY_HOST = 'index.docker.io'.freeze
7
- DEFAULT_REGISTRY_PORT = 443
6
+ extend T::Sig
8
7
 
9
- def self.parse(url)
8
+ DEFAULT_REGISTRY_HOST = T.let('docker.io'.freeze, String)
9
+ DEFAULT_REGISTRY_INDEX_HOST = T.let('index.docker.io'.freeze, String)
10
+ DEFAULT_PORT = T.let(443, Integer)
11
+
12
+ sig { params(url: String).returns(DockerURI) }
13
+ def self.parse_uri(url)
14
+ parse(
15
+ url,
16
+ default_host: DEFAULT_REGISTRY_HOST,
17
+ default_port: DEFAULT_PORT
18
+ )
19
+ end
20
+
21
+ sig { params(url: String).returns(DockerURI) }
22
+ def self.parse_index_uri(url)
23
+ parse(
24
+ url,
25
+ default_host: DEFAULT_REGISTRY_INDEX_HOST,
26
+ default_port: DEFAULT_PORT
27
+ )
28
+ end
29
+
30
+ sig {
31
+ params(
32
+ url: String,
33
+ default_host: T.nilable(String),
34
+ default_port: T.nilable(Integer)
35
+ ).returns(DockerURI)
36
+ }
37
+ def self.parse(url, default_host:, default_port:)
10
38
  if idx = url.index('://')
11
39
  url = url[(idx + 3)..-1] || ''
12
40
  end
13
41
 
14
42
  host_port, *path = url.split('/')
15
43
  host, port, *path = if host_port =~ /[.:]/
16
- hst, prt = host_port.split(':')
17
- [hst, prt || DEFAULT_REGISTRY_PORT, *path]
44
+ hst, prt = T.must(host_port).split(':')
45
+ [T.must(hst), prt || default_port, *path]
18
46
  else
19
- [DEFAULT_REGISTRY_HOST, DEFAULT_REGISTRY_PORT, host_port, *path]
47
+ [default_host, default_port, host_port, *path]
20
48
  end
21
49
 
22
- new(host, port.to_i, path.join('/'))
50
+ new(host.to_s, port.to_i, (path || []).join('/'))
23
51
  end
24
52
 
25
- attr_reader :host, :port, :path
53
+ sig { returns(String) }
54
+ attr_reader :host
26
55
 
56
+ sig { returns(Integer) }
57
+ attr_reader :port
58
+
59
+ sig { returns(String) }
60
+ attr_reader :path
61
+
62
+ sig { params(host: String, port: Integer, path: String).void }
27
63
  def initialize(host, port, path)
28
64
  @host = host
29
65
  @port = port
30
66
  @path = path
31
67
  end
68
+
69
+ sig { returns(T::Boolean) }
70
+ def has_default_port?
71
+ port == DEFAULT_PORT
72
+ end
32
73
  end
33
74
  end
34
75
  end
@@ -6,25 +6,7 @@ module Kuby
6
6
  class PushError < StandardError; end
7
7
  class PullError < StandardError; end
8
8
  class LoginError < StandardError; end
9
-
10
- class MissingTagError < StandardError
11
- extend T::Sig
12
-
13
- sig { returns(String) }
14
- attr_reader :tag
15
-
16
- sig { params(tag: String).void }
17
- def initialize(tag)
18
- @tag = tag
19
- @message = T.let(@message, T.nilable(String))
20
- end
21
-
22
- sig { returns(String) }
23
- def message
24
- @message ||= "Could not find tag '#{tag}'."
25
- end
26
- end
27
-
9
+ class MissingTagError < StandardError; end
28
10
  class UnsupportedDistroError < StandardError; end
29
11
  class MissingPackageError < StandardError; end
30
12
  class MissingDistroError < StandardError; end
@@ -0,0 +1,142 @@
1
+ # typed: strict
2
+
3
+ module Kuby
4
+ module Docker
5
+ class Image
6
+ extend T::Sig
7
+
8
+ sig { returns(T.nilable(String)) }
9
+ attr_reader :identifier
10
+
11
+ sig { returns(String) }
12
+ attr_reader :image_url
13
+
14
+ sig { returns(T.nilable(String)) }
15
+ attr_reader :registry_index_url
16
+
17
+ sig { returns(Credentials) }
18
+ attr_reader :credentials
19
+
20
+ sig { returns(T.nilable(String)) }
21
+ attr_reader :main_tag
22
+
23
+ sig { returns T::Array[String] }
24
+ attr_reader :alias_tags
25
+
26
+ sig {
27
+ params(
28
+ dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
29
+ image_url: String,
30
+ credentials: Credentials,
31
+ registry_index_url: T.nilable(String),
32
+ main_tag: T.nilable(String),
33
+ alias_tags: T::Array[String]
34
+ ).void
35
+ }
36
+ def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
37
+ @dockerfile = T.let(dockerfile, T.any(Dockerfile, T.proc.returns(Dockerfile)))
38
+ @image_url = T.let(image_url, String)
39
+ @registry_index_url = T.let(registry_index_url, T.nilable(String))
40
+ @credentials = T.let(credentials, Credentials)
41
+ @main_tag = T.let(main_tag, T.nilable(String))
42
+ @alias_tags = T.let(alias_tags, T::Array[String])
43
+ @identifier = T.let(@identifier, T.nilable(String))
44
+
45
+ @image_host = T.let(@image_host, T.nilable(String))
46
+ @image_hostname = T.let(@image_hostname, T.nilable(String))
47
+ @registry_index_host = T.let(@registry_index_host, T.nilable(String))
48
+ @registry_index_hostname = T.let(@registry_index_hostname, T.nilable(String))
49
+ @registry_index_uri = T.let(@registry_index_uri, T.nilable(DockerURI))
50
+ @image_repo = T.let(@image_repo, T.nilable(String))
51
+ @full_image_uri = T.let(@full_image_uri, T.nilable(DockerURI))
52
+ @docker_cli = T.let(@docker_cli, T.nilable(Docker::CLI))
53
+ end
54
+
55
+ sig { returns(Image) }
56
+ def new_version
57
+ raise NotImplementedError, 'please use a Docker::Image subclass'
58
+ end
59
+
60
+ sig { returns(Image) }
61
+ def current_version
62
+ raise NotImplementedError, 'please use a Docker::Image subclass'
63
+ end
64
+
65
+ sig { params(current_tag: T.nilable(String)).returns(Image) }
66
+ def previous_version(current_tag = nil)
67
+ raise NotImplementedError, 'please use a Docker::Image subclass'
68
+ end
69
+
70
+ sig { returns(Dockerfile) }
71
+ def dockerfile
72
+ if @dockerfile.respond_to?(:call)
73
+ T.cast(@dockerfile, T.proc.returns(Dockerfile)).call
74
+ else
75
+ T.cast(@dockerfile, Dockerfile)
76
+ end
77
+ end
78
+
79
+ sig { returns(String) }
80
+ def image_host
81
+ @image_host ||= "#{image_uri.host}:#{image_uri.port}"
82
+ end
83
+
84
+ sig { returns(String) }
85
+ def registry_index_host
86
+ @registry_index_host ||= "#{registry_index_uri.host}:#{registry_index_uri.port}"
87
+ end
88
+
89
+ sig { returns(String) }
90
+ def registry_index_hostname
91
+ @registry_index_hostname ||= registry_index_uri.host
92
+ end
93
+
94
+ sig { returns(String) }
95
+ def image_hostname
96
+ @image_hostname ||= image_uri.host
97
+ end
98
+
99
+ sig { returns(String) }
100
+ def image_repo
101
+ @image_repo ||= image_uri.path
102
+ end
103
+
104
+ sig { returns(DockerURI) }
105
+ def image_uri
106
+ @full_image_uri ||= DockerURI.parse_uri(image_url)
107
+ end
108
+
109
+ sig { returns(DockerURI) }
110
+ def registry_index_uri
111
+ @registry_index_uri ||= DockerURI.parse_index_uri(registry_index_url || image_url)
112
+ end
113
+
114
+ sig { returns(T::Array[String]) }
115
+ def tags
116
+ [main_tag, *alias_tags].compact
117
+ end
118
+
119
+ sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String]).void }
120
+ def build(build_args = {}, docker_args = [])
121
+ raise NotImplementedError, 'please use a Docker::Image subclass'
122
+ end
123
+
124
+ sig { params(tag: String).void }
125
+ def push(tag)
126
+ raise NotImplementedError, 'please use a Docker::Image subclass'
127
+ end
128
+
129
+ sig { returns(Docker::CLI) }
130
+ def docker_cli
131
+ @docker_cli ||= Docker::CLI.new
132
+ end
133
+
134
+ private
135
+
136
+ sig { params(main_tag: String, alias_tags: T::Array[String]).returns(Image) }
137
+ def duplicate_with_tags(main_tag, alias_tags)
138
+ self.class.new(dockerfile, image_url, credentials, registry_index_url, main_tag, alias_tags)
139
+ end
140
+ end
141
+ end
142
+ end
@@ -21,13 +21,6 @@ module Kuby
21
21
  raise NotImplementedError,
22
22
  "#{__method__} must be defined in derived classes"
23
23
  end
24
-
25
- private
26
-
27
- sig { returns(Metadata) }
28
- def metadata
29
- environment.docker.metadata
30
- end
31
24
  end
32
25
  end
33
26
  end