kuby-core 0.9.0 → 0.11.1

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/Gemfile +1 -0
  4. data/README.md +4 -2
  5. data/kuby-core.gemspec +2 -1
  6. data/lib/kuby-core.rb +1 -0
  7. data/lib/kuby.rb +14 -0
  8. data/lib/kuby/basic_logger.rb +1 -0
  9. data/lib/kuby/cli_base.rb +53 -4
  10. data/lib/kuby/commands.rb +29 -4
  11. data/lib/kuby/definition.rb +1 -0
  12. data/lib/kuby/dev_setup.rb +1 -0
  13. data/lib/kuby/docker.rb +3 -0
  14. data/lib/kuby/docker/alpine.rb +16 -12
  15. data/lib/kuby/docker/assets_phase.rb +5 -0
  16. data/lib/kuby/docker/bundler_phase.rb +36 -2
  17. data/lib/kuby/docker/cli.rb +47 -2
  18. data/lib/kuby/docker/copy_phase.rb +11 -3
  19. data/lib/kuby/docker/credentials.rb +1 -0
  20. data/lib/kuby/docker/debian.rb +14 -10
  21. data/lib/kuby/docker/dev_spec.rb +82 -18
  22. data/lib/kuby/docker/distro.rb +43 -0
  23. data/lib/kuby/docker/dockerfile.rb +67 -19
  24. data/lib/kuby/docker/errors.rb +9 -0
  25. data/lib/kuby/docker/inline_layer.rb +7 -0
  26. data/lib/kuby/docker/layer.rb +11 -0
  27. data/lib/kuby/docker/layer_stack.rb +43 -6
  28. data/lib/kuby/docker/local_tags.rb +23 -2
  29. data/lib/kuby/docker/metadata.rb +35 -7
  30. data/lib/kuby/docker/package_list.rb +22 -3
  31. data/lib/kuby/docker/package_phase.rb +25 -5
  32. data/lib/kuby/docker/packages.rb +1 -0
  33. data/lib/kuby/docker/packages/managed_package.rb +20 -2
  34. data/lib/kuby/docker/packages/nodejs.rb +8 -0
  35. data/lib/kuby/docker/packages/package.rb +15 -1
  36. data/lib/kuby/docker/packages/simple_managed_package.rb +10 -1
  37. data/lib/kuby/docker/packages/yarn.rb +14 -0
  38. data/lib/kuby/docker/remote_tags.rb +21 -2
  39. data/lib/kuby/docker/setup_phase.rb +35 -4
  40. data/lib/kuby/docker/spec.rb +89 -13
  41. data/lib/kuby/docker/tags.rb +37 -2
  42. data/lib/kuby/docker/timestamp_tag.rb +14 -2
  43. data/lib/kuby/docker/webserver_phase.rb +41 -6
  44. data/lib/kuby/docker/yarn_phase.rb +5 -0
  45. data/lib/kuby/environment.rb +4 -0
  46. data/lib/kuby/kubernetes.rb +1 -0
  47. data/lib/kuby/kubernetes/deploy_task.rb +1 -0
  48. data/lib/kuby/kubernetes/deployer.rb +8 -4
  49. data/lib/kuby/kubernetes/docker_config.rb +1 -0
  50. data/lib/kuby/kubernetes/docker_desktop_provider.rb +1 -0
  51. data/lib/kuby/kubernetes/errors.rb +1 -0
  52. data/lib/kuby/kubernetes/manifest.rb +1 -0
  53. data/lib/kuby/kubernetes/provider.rb +1 -0
  54. data/lib/kuby/kubernetes/registry_secret.rb +1 -0
  55. data/lib/kuby/kubernetes/spec.rb +1 -0
  56. data/lib/kuby/middleware.rb +1 -0
  57. data/lib/kuby/middleware/health_check.rb +1 -0
  58. data/lib/kuby/plugin.rb +1 -0
  59. data/lib/kuby/plugin_registry.rb +1 -0
  60. data/lib/kuby/plugins.rb +1 -0
  61. data/lib/kuby/plugins/nginx_ingress.rb +1 -0
  62. data/lib/kuby/plugins/rails_app.rb +1 -0
  63. data/lib/kuby/plugins/rails_app/asset_copy_task.rb +1 -0
  64. data/lib/kuby/plugins/rails_app/assets.rb +1 -0
  65. data/lib/kuby/plugins/rails_app/database.rb +10 -9
  66. data/lib/kuby/plugins/rails_app/generators/kuby.rb +45 -32
  67. data/lib/kuby/plugins/rails_app/mysql.rb +1 -3
  68. data/lib/kuby/plugins/rails_app/plugin.rb +13 -4
  69. data/lib/kuby/plugins/rails_app/postgres.rb +1 -0
  70. data/lib/kuby/plugins/rails_app/rewrite_db_config.rb +1 -0
  71. data/lib/kuby/plugins/rails_app/sqlite.rb +1 -0
  72. data/lib/kuby/rails_commands.rb +14 -19
  73. data/lib/kuby/railtie.rb +1 -0
  74. data/lib/kuby/tasks.rb +3 -1
  75. data/lib/kuby/trailing_hash.rb +5 -3
  76. data/lib/kuby/version.rb +3 -1
  77. data/spec/docker/metadata_spec.rb +1 -0
  78. data/spec/docker/spec_spec.rb +37 -0
  79. data/spec/docker/timestamp_tag_spec.rb +1 -0
  80. data/spec/dummy/app/channels/application_cable/channel.rb +1 -0
  81. data/spec/dummy/app/channels/application_cable/connection.rb +1 -0
  82. data/spec/dummy/app/controllers/application_controller.rb +1 -0
  83. data/spec/dummy/app/helpers/application_helper.rb +1 -0
  84. data/spec/dummy/app/jobs/application_job.rb +1 -0
  85. data/spec/dummy/app/mailers/application_mailer.rb +1 -0
  86. data/spec/dummy/app/models/application_record.rb +1 -0
  87. data/spec/dummy/config/application.rb +1 -0
  88. data/spec/dummy/config/boot.rb +1 -0
  89. data/spec/dummy/config/environment.rb +1 -0
  90. data/spec/dummy/config/environments/development.rb +1 -0
  91. data/spec/dummy/config/environments/production.rb +1 -0
  92. data/spec/dummy/config/environments/test.rb +1 -0
  93. data/spec/dummy/config/initializers/application_controller_renderer.rb +1 -0
  94. data/spec/dummy/config/initializers/assets.rb +1 -0
  95. data/spec/dummy/config/initializers/backtrace_silencers.rb +1 -0
  96. data/spec/dummy/config/initializers/content_security_policy.rb +1 -0
  97. data/spec/dummy/config/initializers/cookies_serializer.rb +1 -0
  98. data/spec/dummy/config/initializers/filter_parameter_logging.rb +1 -0
  99. data/spec/dummy/config/initializers/inflections.rb +1 -0
  100. data/spec/dummy/config/initializers/mime_types.rb +1 -0
  101. data/spec/dummy/config/initializers/wrap_parameters.rb +1 -0
  102. data/spec/dummy/config/puma.rb +1 -0
  103. data/spec/dummy/config/routes.rb +1 -0
  104. data/spec/dummy/config/spring.rb +1 -0
  105. data/spec/dummy/db/seeds.rb +1 -0
  106. data/spec/dummy/test/application_system_test_case.rb +1 -0
  107. data/spec/dummy/test/channels/application_cable/connection_test.rb +1 -0
  108. data/spec/dummy/test/test_helper.rb +1 -0
  109. data/spec/spec_helper.rb +10 -0
  110. data/spec/support/docker/fake_cli.rb +1 -0
  111. data/spec/support/docker/remote/fake_client.rb +1 -0
  112. data/spec/trailing_hash_spec.rb +2 -0
  113. metadata +20 -4
@@ -1,3 +1,5 @@
1
+ # typed: strict
2
+
1
3
  require 'json'
2
4
  require 'open3'
3
5
  require 'shellwords'
@@ -5,22 +7,29 @@ require 'shellwords'
5
7
  module Kuby
6
8
  module Docker
7
9
  class CLI < CLIBase
10
+ extend T::Sig
11
+
12
+ sig { returns(String) }
8
13
  attr_reader :executable
9
14
 
15
+ sig { params(executable: T.nilable(String)).void }
10
16
  def initialize(executable = nil)
11
- @executable = executable || `which docker`.strip
17
+ @executable = T.let(executable || `which docker`.strip, String)
12
18
  end
13
19
 
20
+ sig { returns(T.nilable(String)) }
14
21
  def config_file
15
22
  if File.exist?(default_config_file)
16
23
  default_config_file
17
24
  end
18
25
  end
19
26
 
27
+ sig { returns(String) }
20
28
  def default_config_file
21
29
  File.join(Dir.home, '.docker', 'config.json')
22
30
  end
23
31
 
32
+ sig { params(url: String, username: String, password: String).void }
24
33
  def login(url:, username:, password:)
25
34
  cmd = [
26
35
  executable, 'login', url, '--username', username, '--password-stdin'
@@ -36,13 +45,23 @@ module Kuby
36
45
  end
37
46
  end
38
47
 
48
+ sig { returns(T::Array[String]) }
39
49
  def auths
40
50
  return [] unless config_file
41
51
 
42
- config = JSON.parse(File.read(config_file))
52
+ config = JSON.parse(File.read(T.must(config_file)))
43
53
  config.fetch('auths', {}).keys
44
54
  end
45
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
+ }
46
65
  def build(dockerfile:, image_url:, tags:, build_args: {})
47
66
  cmd = [
48
67
  executable, 'build',
@@ -63,6 +82,15 @@ module Kuby
63
82
  end
64
83
  end
65
84
 
85
+ sig {
86
+ params(
87
+ image_url: String,
88
+ tag: String,
89
+ env: T::Hash[T.any(Symbol, String), String],
90
+ ports: T::Array[T.any(String, Integer)]
91
+ )
92
+ .void
93
+ }
66
94
  def run(image_url:, tag: 'latest', env: {}, ports: [])
67
95
  cmd = [
68
96
  executable, 'run',
@@ -76,6 +104,7 @@ module Kuby
76
104
  execc(cmd)
77
105
  end
78
106
 
107
+ sig { params(image_url: String).returns(T::Array[T::Hash[Symbol, String]]) }
79
108
  def images(image_url)
80
109
  cmd = [
81
110
  executable, 'images', image_url,
@@ -89,6 +118,7 @@ module Kuby
89
118
  end
90
119
  end
91
120
 
121
+ sig { params(image_url: String, tag: String).void }
92
122
  def push(image_url, tag)
93
123
  systemm([
94
124
  executable, 'push', "#{image_url}:#{tag}"
@@ -100,14 +130,29 @@ module Kuby
100
130
  end
101
131
  end
102
132
 
133
+ sig { params(image_url: String, tag: String).void }
134
+ def pull(image_url, tag)
135
+ systemm([
136
+ executable, 'pull', "#{image_url}:#{tag}"
137
+ ])
138
+
139
+ unless last_status.success?
140
+ raise PullError, 'pull failed: docker command exited with '\
141
+ "status code #{last_status.exitstatus}"
142
+ end
143
+ end
144
+
145
+ sig { returns(Symbol) }
103
146
  def status_key
104
147
  :kuby_docker_cli_last_status
105
148
  end
106
149
 
150
+ sig { returns(Symbol) }
107
151
  def stdout_key
108
152
  :kuby_docker_stdout
109
153
  end
110
154
 
155
+ sig { returns(Symbol) }
111
156
  def stderr_key
112
157
  :kuby_docker_stderr
113
158
  end
@@ -1,19 +1,27 @@
1
+ # typed: strict
2
+
1
3
  module Kuby
2
4
  module Docker
3
5
  class CopyPhase < Layer
4
- DEFAULT_PATHS = ['./'].freeze
6
+ extend T::Sig
7
+
8
+ DEFAULT_PATHS = T.let(['./'].freeze, T::Array[String])
5
9
 
10
+ sig { returns(T::Array[String]) }
6
11
  attr_reader :paths
7
12
 
8
- def initialize(*args)
13
+ sig { params(environment: Environment).void }
14
+ def initialize(environment)
9
15
  super
10
- @paths = []
16
+ @paths = T.let([], T::Array[String])
11
17
  end
12
18
 
19
+ sig { params(path: String).void }
13
20
  def <<(path)
14
21
  paths << path
15
22
  end
16
23
 
24
+ sig { params(dockerfile: Dockerfile).void }
17
25
  def apply_to(dockerfile)
18
26
  to_copy = paths.empty? ? DEFAULT_PATHS : paths
19
27
  to_copy.each { |path| dockerfile.copy(path, '.') }
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  require 'kube-dsl'
2
3
 
3
4
  module Kuby
@@ -1,38 +1,41 @@
1
+ # typed: strict
2
+
1
3
  module Kuby
2
4
  module Docker
3
- class Debian
4
- SHELL_EXE = '/bin/bash'
5
+ class Debian < Distro
6
+ SHELL_EXE = T.let('/bin/bash'.freeze, String)
5
7
 
6
- DEFAULT_PACKAGES = [
7
- [:ca_certificates],
8
+ DEFAULT_PACKAGES = T.let([
9
+ [:ca_certificates, nil],
8
10
  [:nodejs, '12.14.1'],
9
11
  [:yarn, '1.21.1']
10
- ].freeze
12
+ ].freeze, T::Array[[Symbol, T.nilable(String)]])
11
13
 
14
+ sig { returns(Layer) }
12
15
  attr_reader :phase
13
16
 
14
- def initialize(phase)
15
- @phase = phase
16
- end
17
-
17
+ sig { override.params(packages: T::Array[Distro::PackageImpl], into: Dockerfile).void }
18
18
  def install(packages, into:)
19
19
  dockerfile = into
20
20
  install_managed(packages, dockerfile)
21
21
  install_unmanaged(packages, dockerfile)
22
22
  end
23
23
 
24
+ sig { override.returns(T::Array[[Symbol, T.nilable(String)]]) }
24
25
  def default_packages
25
26
  DEFAULT_PACKAGES
26
27
  end
27
28
 
29
+ sig { override.returns(String) }
28
30
  def shell_exe
29
31
  SHELL_EXE
30
32
  end
31
33
 
32
34
  private
33
35
 
36
+ sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
34
37
  def install_managed(packages, dockerfile)
35
- pkgs = packages.select(&:managed?)
38
+ pkgs = T.cast(packages.select(&:managed?), T::Array[Distro::ManagedPackageImpl])
36
39
 
37
40
  pkg_cmd = <<~CMD.strip
38
41
  apt-get update -qq && \\
@@ -49,6 +52,7 @@ module Kuby
49
52
  dockerfile.run(pkg_cmd)
50
53
  end
51
54
 
55
+ sig { params(packages: T::Array[Distro::PackageImpl], dockerfile: Dockerfile).void }
52
56
  def install_unmanaged(packages, dockerfile)
53
57
  packages
54
58
  .reject(&:managed?)
@@ -1,63 +1,121 @@
1
+ # typed: strict
2
+
1
3
  module Kuby
2
4
  module Docker
3
5
  class WebserverDevPhase < Layer
4
- DEFAULT_PORT = 3000
6
+ extend T::Sig
7
+
8
+ DEFAULT_PORT = T.let(3000, Integer)
9
+
10
+ sig { params(port: Integer).void }
11
+ attr_writer :port
5
12
 
6
- attr_accessor :port
13
+ sig { params(environment: Environment).void }
14
+ def initialize(environment)
15
+ super
16
+
17
+ @port = T.let(@port, T.nilable(Integer))
18
+ end
7
19
 
20
+ sig { override.params(dockerfile: Dockerfile).void }
8
21
  def apply_to(dockerfile)
9
22
  dockerfile.expose(port)
10
23
  end
11
24
 
25
+ sig { returns(Integer) }
12
26
  def port
13
27
  @port || DEFAULT_PORT
14
28
  end
15
29
  end
16
30
 
17
31
  class DevSpec
32
+ extend T::Sig
33
+
34
+ sig { returns(Environment) }
18
35
  attr_reader :environment
19
36
 
37
+ sig { params(environment: Environment).void }
20
38
  def initialize(environment)
21
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))
22
49
  end
23
50
 
51
+ sig { params(dir: String).void }
24
52
  def working_dir(dir)
25
53
  setup_phase.working_dir = dir
26
54
  end
27
55
 
56
+ sig { params(env: String).void }
28
57
  def rails_env(env)
29
58
  setup_phase.rails_env = env
30
59
  end
31
60
 
32
- def package(*args)
33
- package_phase.add(*args)
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)
34
70
  end
35
71
 
72
+ sig { params(distro_name: Symbol).void }
36
73
  def distro(distro_name)
37
74
  metadata.distro = distro_name
38
75
  @distro_spec = nil
39
76
  end
40
77
 
78
+ sig { params(port: Integer).void }
41
79
  def port(port)
42
80
  webserver_phase.port = port
43
81
  end
44
82
 
45
- def use(*args, &block)
46
- layer_stack.use(*args, &block)
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)
47
93
  end
48
94
 
49
- def insert(*args, &block)
50
- layer_stack.insert(*args, &block)
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)
51
106
  end
52
107
 
53
- def delete(*args)
54
- layer_stack.delete(*args)
108
+ sig { params(name: Symbol).void }
109
+ def delete(name)
110
+ layer_stack.delete(name)
55
111
  end
56
112
 
57
- def exists?(*args)
58
- layer_stack.includes?(*args)
113
+ sig { params(name: Symbol).returns(T::Boolean) }
114
+ def exists?(name)
115
+ layer_stack.includes?(name)
59
116
  end
60
117
 
118
+ sig { returns(Dockerfile) }
61
119
  def to_dockerfile
62
120
  Dockerfile.new.tap do |df|
63
121
  layer_stack.each { |layer| layer.apply_to(df) }
@@ -65,26 +123,27 @@ module Kuby
65
123
  end
66
124
  end
67
125
 
126
+ sig { returns(SetupPhase) }
68
127
  def setup_phase
69
128
  @setup_phase ||= SetupPhase.new(environment)
70
129
  end
71
130
 
131
+ sig { returns(PackagePhase) }
72
132
  def package_phase
73
133
  @package_phase ||= PackagePhase.new(environment)
74
134
  end
75
135
 
136
+ sig { returns(WebserverDevPhase) }
76
137
  def webserver_phase
77
138
  @webserver_phase ||= WebserverDevPhase.new(environment)
78
139
  end
79
140
 
141
+ sig { returns(Metadata) }
80
142
  def metadata
81
143
  @metadata ||= Metadata.new(environment)
82
144
  end
83
145
 
84
- def cli
85
- @cli ||= Docker::CLI.new
86
- end
87
-
146
+ sig { returns(Distro) }
88
147
  def distro_spec
89
148
  @distro_spec ||= if distro_klass = Kuby.distros[metadata.distro]
90
149
  distro_klass.new(self)
@@ -93,6 +152,7 @@ module Kuby
93
152
  end
94
153
  end
95
154
 
155
+ sig { returns(String) }
96
156
  def tag
97
157
  t = ENV.fetch('KUBY_DOCKER_TAG') do
98
158
  tags.latest_timestamp_tag
@@ -105,22 +165,26 @@ module Kuby
105
165
  t.to_s
106
166
  end
107
167
 
108
- def previous_tag(*)
168
+ sig { params(current_tag: String).returns(String) }
169
+ def previous_tag(current_tag)
109
170
  raise MissingTagError, 'cannot roll back in the development environment'
110
171
  end
111
172
 
173
+ sig { returns(CLI) }
112
174
  def cli
113
175
  @cli ||= Docker::CLI.new
114
176
  end
115
177
 
116
178
  private
117
179
 
180
+ sig { returns(LocalTags) }
118
181
  def tags
119
182
  @tags ||= LocalTags.new(cli, metadata)
120
183
  end
121
184
 
185
+ sig { returns(Docker::LayerStack) }
122
186
  def layer_stack
123
- @layer_stack ||= LayerStack.new.tap do |stack|
187
+ @layer_stack ||= Docker::LayerStack.new.tap do |stack|
124
188
  stack.use(:setup_phase, setup_phase)
125
189
  stack.use(:package_phase, package_phase)
126
190
  stack.use(:webserver_phase, webserver_phase)
@@ -0,0 +1,43 @@
1
+ # typed: strict
2
+
3
+ module Kuby
4
+ module Docker
5
+ class Distro
6
+ extend T::Sig
7
+ extend T::Helpers
8
+
9
+ abstract!
10
+
11
+ PackageImpl = T.type_alias {
12
+ T.any(Packages::Package, Packages::ManagedPackage, Packages::SimpleManagedPackage)
13
+ }
14
+
15
+ ManagedPackageImpl = T.type_alias {
16
+ T.any(Packages::ManagedPackage, Packages::SimpleManagedPackage)
17
+ }
18
+
19
+ sig { params(phase: Layer).void }
20
+ def initialize(phase)
21
+ @phase = phase
22
+ end
23
+
24
+ sig { params(packages: T::Array[PackageImpl], into: Dockerfile).void }
25
+ def install(packages, into:)
26
+ raise NotImplementedError,
27
+ "#{__method__} must be defined in derived classes"
28
+ end
29
+
30
+ sig { returns(T::Array[[Symbol, T.nilable(String)]]) }
31
+ def default_packages
32
+ raise NotImplementedError,
33
+ "#{__method__} must be defined in derived classes"
34
+ end
35
+
36
+ sig { returns(String) }
37
+ def shell_exe
38
+ raise NotImplementedError,
39
+ "#{__method__} must be defined in derived classes"
40
+ end
41
+ end
42
+ end
43
+ end