kuby-core 0.13.0 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/lib/kuby/commands.rb +20 -8
- data/lib/kuby/docker/app_image.rb +13 -7
- data/lib/kuby/docker/bundler_phase.rb +9 -2
- data/lib/kuby/docker/cli.rb +12 -3
- data/lib/kuby/docker/docker_uri.rb +35 -5
- data/lib/kuby/docker/image.rb +32 -5
- data/lib/kuby/docker/spec.rb +10 -1
- data/lib/kuby/docker/timestamped_image.rb +22 -12
- data/lib/kuby/plugins/rails_app/assets.rb +5 -3
- data/lib/kuby/plugins/rails_app/assets_image.rb +6 -4
- data/lib/kuby/plugins/rails_app/generators/kuby.rb +31 -9
- data/lib/kuby/tasks.rb +131 -21
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/timestamped_image_spec.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cbd792443dc33de6a20d02f6969f6d59d535f1b68fb75c9a8945615fd870a28
|
4
|
+
data.tar.gz: 3add1ef85d6758a2590d35d7aa0bfb4fffaeae55ef6567a34cf3b7557a56e55a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d855f57aae01d93c2fa44e285ee417ebcc00f12de6f9c4b989a39626ec9ec13a4659eafe0b8924592de5d860666158376e5adbf69c70373703eb2a9074ca75d
|
7
|
+
data.tar.gz: dc553aa0b22e5ab262fa18fa1a60bebb421862eab7e747e06c63907fefe8f5371be56415d0bde2e959ffa775140b624aee3aa53006f1dbee272122c87bf53cc6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
## 0.16.1
|
2
|
+
* Fix a few bugs caused by leaving `continue-on-error: true` in the GitHub actions config 🤦
|
3
|
+
- Docker builds would fail if no previous images existed in the registry.
|
4
|
+
- The assets image would fail to build because `RAILS_MASTER_KEY` was not propagated correctly.
|
5
|
+
* Use the kuby-prebundler gem in the integration tests instead of a custom layer.
|
6
|
+
|
7
|
+
## 0.16.0
|
8
|
+
* Allow Bundler executable to be customized.
|
9
|
+
- Main use-case is to allow adding in the Prebundler plugin, https://github.com/getkuby/kuby-prebundler.
|
10
|
+
* Print error message and exit on missing Docker build args.
|
11
|
+
- Ignore with `--ignore-missing-args`.
|
12
|
+
|
13
|
+
## 0.15.0
|
14
|
+
* Add an extra parameter to the Docker spec for specifying the URL of the Docker registry index.
|
15
|
+
- 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.
|
16
|
+
* Add config/master.key to the .dockerignore created by the Rails generator.
|
17
|
+
- This was an unfortunate oversight, but such are the perils of using pre-1.0 software.
|
18
|
+
* Print an error message if building images on non-x86 hardware.
|
19
|
+
- 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.
|
20
|
+
- We may need to revisit this in the future if ARM processors become more popular.
|
21
|
+
|
22
|
+
## 0.14.0
|
23
|
+
* Make RAILS_MASTER_KEY available in the assets image (@palkan, #63)
|
24
|
+
* Allow specifying custom docker build options via CLI (@palkan, #65)
|
25
|
+
|
1
26
|
## 0.13.0
|
2
27
|
* Fix handling rails/rake options in remote exec (@palkan, #60)
|
3
28
|
* Add `bundler_phase.gemfiles(*paths)` to allow adding additional gemfiles (@palkan, #61)
|
data/lib/kuby/commands.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: false
|
2
2
|
|
3
3
|
require 'kuby/version'
|
4
4
|
require 'gli'
|
@@ -65,23 +65,32 @@ module Kuby
|
|
65
65
|
desc 'Builds the Docker image.'
|
66
66
|
command :build do |c|
|
67
67
|
c.flag [:a, :arg], required: false, multiple: true
|
68
|
-
c.
|
68
|
+
c.switch [:'ignore-missing-args'], required: false, default: false
|
69
|
+
c.flag [:only], required: false
|
70
|
+
c.flag [:c, :context], required: false
|
71
|
+
c.action do |global_options, options, docker_args|
|
69
72
|
build_args = {}.tap do |build_args|
|
70
73
|
(options[:arg] || []).each do |a|
|
71
|
-
key, value = a.split('=')
|
74
|
+
key, value = a.split('=', 2)
|
72
75
|
value = value[1..-2] if value.start_with?('"') || value.start_with?("'")
|
73
76
|
build_args[key] = value
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
|
-
tasks.build(
|
80
|
+
tasks.build(
|
81
|
+
build_args, docker_args,
|
82
|
+
only: options[:only],
|
83
|
+
ignore_missing_args: options[:'ignore-missing-args'],
|
84
|
+
context: options[:context]
|
85
|
+
)
|
78
86
|
end
|
79
87
|
end
|
80
88
|
|
81
89
|
desc 'Pushes the Docker image to the configured registry.'
|
82
90
|
command :push do |c|
|
91
|
+
c.flag [:only], required: false
|
83
92
|
c.action do |global_options, options, args|
|
84
|
-
tasks.push
|
93
|
+
tasks.push(only: options[:only])
|
85
94
|
end
|
86
95
|
end
|
87
96
|
|
@@ -94,8 +103,9 @@ module Kuby
|
|
94
103
|
|
95
104
|
desc 'Prints the effective Dockerfiles used to build Docker images.'
|
96
105
|
command :dockerfiles do |c|
|
106
|
+
c.flag [:only], required: false
|
97
107
|
c.action do |global_options, options, args|
|
98
|
-
tasks.print_dockerfiles
|
108
|
+
tasks.print_dockerfiles(only: options[:only])
|
99
109
|
end
|
100
110
|
end
|
101
111
|
|
@@ -117,8 +127,10 @@ module Kuby
|
|
117
127
|
|
118
128
|
desc 'Prints the effective Kubernetes resources that will be applied on deploy.'
|
119
129
|
command :resources do |c|
|
130
|
+
c.flag [:K, :kind], required: false
|
131
|
+
c.flag [:N, :name], required: false
|
120
132
|
c.action do |global_options, options, args|
|
121
|
-
tasks.print_resources
|
133
|
+
tasks.print_resources(options[:kind], options[:name])
|
122
134
|
end
|
123
135
|
end
|
124
136
|
|
@@ -165,7 +177,7 @@ module Kuby
|
|
165
177
|
rc.desc 'Runs an arbitrary command inside a running Rails pod.'
|
166
178
|
rc.command :exec do |c|
|
167
179
|
c.action do |global_options, options, args|
|
168
|
-
tasks.remote_exec([*args,
|
180
|
+
tasks.remote_exec([*args, *T.unsafe(@rails_options)])
|
169
181
|
end
|
170
182
|
end
|
171
183
|
|
@@ -6,13 +6,19 @@ module Kuby
|
|
6
6
|
class AppImage < ::Kuby::Docker::TimestampedImage
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
-
sig {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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"
|
16
22
|
end
|
17
23
|
end
|
18
24
|
end
|
@@ -29,6 +29,12 @@ module Kuby
|
|
29
29
|
sig { params(without: T::Array[String]).void }
|
30
30
|
attr_writer :without
|
31
31
|
|
32
|
+
sig { returns(T.nilable(String)) }
|
33
|
+
attr_reader :executable
|
34
|
+
|
35
|
+
sig { params(executable: String).void }
|
36
|
+
attr_writer :executable
|
37
|
+
|
32
38
|
sig { params(environment: Environment).void }
|
33
39
|
def initialize(environment)
|
34
40
|
super
|
@@ -37,6 +43,7 @@ module Kuby
|
|
37
43
|
@gemfile = T.let(@gemfile, T.nilable(String))
|
38
44
|
@gemfiles = T.let([], T::Array[String])
|
39
45
|
@without = T.let(@without, T.nilable(T::Array[String]))
|
46
|
+
@executable = T.let(@executable, T.nilable(String))
|
40
47
|
end
|
41
48
|
|
42
49
|
sig { override.params(dockerfile: Dockerfile).void }
|
@@ -61,14 +68,14 @@ module Kuby
|
|
61
68
|
end
|
62
69
|
|
63
70
|
dockerfile.run(
|
64
|
-
'bundle', 'install',
|
71
|
+
executable || 'bundle', 'install',
|
65
72
|
'--jobs', '$(nproc)',
|
66
73
|
'--retry', '3',
|
67
74
|
'--gemfile', gf
|
68
75
|
)
|
69
76
|
|
70
77
|
# generate binstubs and add the bin directory to our path
|
71
|
-
dockerfile.run('bundle', 'binstubs', '--all')
|
78
|
+
dockerfile.run(executable || 'bundle', 'binstubs', '--all')
|
72
79
|
dockerfile.env("PATH=./bin:$PATH")
|
73
80
|
end
|
74
81
|
|
data/lib/kuby/docker/cli.rb
CHANGED
@@ -53,15 +53,24 @@ module Kuby
|
|
53
53
|
config.fetch('auths', {}).keys
|
54
54
|
end
|
55
55
|
|
56
|
-
sig {
|
57
|
-
|
56
|
+
sig {
|
57
|
+
params(
|
58
|
+
image: Image,
|
59
|
+
build_args: T::Hash[T.any(Symbol, String), String],
|
60
|
+
docker_args: T::Array[String],
|
61
|
+
context: T.nilable(String)
|
62
|
+
).void
|
63
|
+
}
|
64
|
+
def build(image, build_args: {}, docker_args: [], context: nil)
|
58
65
|
cmd = [
|
59
66
|
executable, 'build',
|
60
67
|
*image.tags.flat_map { |tag| ['-t', "#{image.image_url}:#{tag}"] },
|
61
68
|
*build_args.flat_map do |arg, val|
|
62
69
|
['--build-arg', Shellwords.shellescape("#{arg}=#{val}")]
|
63
70
|
end,
|
64
|
-
'-f-',
|
71
|
+
'-f-',
|
72
|
+
*docker_args,
|
73
|
+
context || '.'
|
65
74
|
]
|
66
75
|
|
67
76
|
open3_w(cmd) do |stdin, _wait_threads|
|
@@ -5,11 +5,36 @@ module Kuby
|
|
5
5
|
class DockerURI
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
-
DEFAULT_REGISTRY_HOST = T.let('
|
9
|
-
|
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)
|
10
11
|
|
11
12
|
sig { params(url: String).returns(DockerURI) }
|
12
|
-
def self.
|
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:)
|
13
38
|
if idx = url.index('://')
|
14
39
|
url = url[(idx + 3)..-1] || ''
|
15
40
|
end
|
@@ -17,9 +42,9 @@ module Kuby
|
|
17
42
|
host_port, *path = url.split('/')
|
18
43
|
host, port, *path = if host_port =~ /[.:]/
|
19
44
|
hst, prt = T.must(host_port).split(':')
|
20
|
-
[T.must(hst), prt ||
|
45
|
+
[T.must(hst), prt || default_port, *path]
|
21
46
|
else
|
22
|
-
[
|
47
|
+
[default_host, default_port, host_port, *path]
|
23
48
|
end
|
24
49
|
|
25
50
|
new(host.to_s, port.to_i, (path || []).join('/'))
|
@@ -40,6 +65,11 @@ module Kuby
|
|
40
65
|
@port = port
|
41
66
|
@path = path
|
42
67
|
end
|
68
|
+
|
69
|
+
sig { returns(T::Boolean) }
|
70
|
+
def has_default_port?
|
71
|
+
port == DEFAULT_PORT
|
72
|
+
end
|
43
73
|
end
|
44
74
|
end
|
45
75
|
end
|
data/lib/kuby/docker/image.rb
CHANGED
@@ -5,9 +5,15 @@ module Kuby
|
|
5
5
|
class Image
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
sig { returns(T.nilable(String)) }
|
9
|
+
attr_reader :identifier
|
10
|
+
|
8
11
|
sig { returns(String) }
|
9
12
|
attr_reader :image_url
|
10
13
|
|
14
|
+
sig { returns(T.nilable(String)) }
|
15
|
+
attr_reader :registry_index_url
|
16
|
+
|
11
17
|
sig { returns(Credentials) }
|
12
18
|
attr_reader :credentials
|
13
19
|
|
@@ -22,19 +28,25 @@ module Kuby
|
|
22
28
|
dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
|
23
29
|
image_url: String,
|
24
30
|
credentials: Credentials,
|
31
|
+
registry_index_url: T.nilable(String),
|
25
32
|
main_tag: T.nilable(String),
|
26
33
|
alias_tags: T::Array[String]
|
27
34
|
).void
|
28
35
|
}
|
29
|
-
def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
|
36
|
+
def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
|
30
37
|
@dockerfile = T.let(dockerfile, T.any(Dockerfile, T.proc.returns(Dockerfile)))
|
31
38
|
@image_url = T.let(image_url, String)
|
39
|
+
@registry_index_url = T.let(registry_index_url, T.nilable(String))
|
32
40
|
@credentials = T.let(credentials, Credentials)
|
33
41
|
@main_tag = T.let(main_tag, T.nilable(String))
|
34
42
|
@alias_tags = T.let(alias_tags, T::Array[String])
|
43
|
+
@identifier = T.let(@identifier, T.nilable(String))
|
35
44
|
|
36
45
|
@image_host = T.let(@image_host, T.nilable(String))
|
37
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))
|
38
50
|
@image_repo = T.let(@image_repo, T.nilable(String))
|
39
51
|
@full_image_uri = T.let(@full_image_uri, T.nilable(DockerURI))
|
40
52
|
@docker_cli = T.let(@docker_cli, T.nilable(Docker::CLI))
|
@@ -69,6 +81,16 @@ module Kuby
|
|
69
81
|
@image_host ||= "#{image_uri.host}:#{image_uri.port}"
|
70
82
|
end
|
71
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
|
+
|
72
94
|
sig { returns(String) }
|
73
95
|
def image_hostname
|
74
96
|
@image_hostname ||= image_uri.host
|
@@ -81,7 +103,12 @@ module Kuby
|
|
81
103
|
|
82
104
|
sig { returns(DockerURI) }
|
83
105
|
def image_uri
|
84
|
-
@full_image_uri ||= DockerURI.
|
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)
|
85
112
|
end
|
86
113
|
|
87
114
|
sig { returns(T::Array[String]) }
|
@@ -89,8 +116,8 @@ module Kuby
|
|
89
116
|
[main_tag, *alias_tags].compact
|
90
117
|
end
|
91
118
|
|
92
|
-
sig { params(build_args: T::Hash[String, String]).void }
|
93
|
-
def build(build_args = {})
|
119
|
+
sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String], context: T.nilable(String)).void }
|
120
|
+
def build(build_args = {}, docker_args = [], context: nil)
|
94
121
|
raise NotImplementedError, 'please use a Docker::Image subclass'
|
95
122
|
end
|
96
123
|
|
@@ -108,7 +135,7 @@ module Kuby
|
|
108
135
|
|
109
136
|
sig { params(main_tag: String, alias_tags: T::Array[String]).returns(Image) }
|
110
137
|
def duplicate_with_tags(main_tag, alias_tags)
|
111
|
-
self.class.new(dockerfile, image_url, credentials, main_tag, alias_tags)
|
138
|
+
self.class.new(dockerfile, image_url, credentials, registry_index_url, main_tag, alias_tags)
|
112
139
|
end
|
113
140
|
end
|
114
141
|
end
|
data/lib/kuby/docker/spec.rb
CHANGED
@@ -13,6 +13,9 @@ module Kuby
|
|
13
13
|
sig { returns(T.nilable(String)) }
|
14
14
|
attr_reader :image_url_str
|
15
15
|
|
16
|
+
sig { returns(T.nilable(String)) }
|
17
|
+
attr_reader :registry_index_url_str
|
18
|
+
|
16
19
|
sig { params(environment: Environment).void }
|
17
20
|
def initialize(environment)
|
18
21
|
@environment = environment
|
@@ -31,6 +34,7 @@ module Kuby
|
|
31
34
|
@layer_stack = T.let(@layer_stack, T.nilable(Kuby::Docker::LayerStack))
|
32
35
|
|
33
36
|
@image_url_str = T.let(@image_url_str, T.nilable(String))
|
37
|
+
@registry_index_url_str = T.let(@registry_index_url_str, T.nilable(String))
|
34
38
|
@image = T.let(@image, T.nilable(Docker::AppImage))
|
35
39
|
end
|
36
40
|
|
@@ -96,6 +100,11 @@ module Kuby
|
|
96
100
|
@image_url_str = url
|
97
101
|
end
|
98
102
|
|
103
|
+
sig { params(url: String).void }
|
104
|
+
def registry_index_url(url)
|
105
|
+
@registry_index_url_str = url
|
106
|
+
end
|
107
|
+
|
99
108
|
sig {
|
100
109
|
params(
|
101
110
|
name: Symbol,
|
@@ -148,7 +157,7 @@ module Kuby
|
|
148
157
|
end
|
149
158
|
|
150
159
|
Docker::AppImage.new(
|
151
|
-
dockerfile, T.must(image_url_str), credentials
|
160
|
+
dockerfile, T.must(image_url_str), credentials, registry_index_url_str
|
152
161
|
)
|
153
162
|
end
|
154
163
|
end
|
@@ -12,11 +12,12 @@ module Kuby
|
|
12
12
|
dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
|
13
13
|
image_url: String,
|
14
14
|
credentials: Credentials,
|
15
|
+
registry_index_url_str: T.nilable(String),
|
15
16
|
main_tag: T.nilable(String),
|
16
17
|
alias_tags: T::Array[String]
|
17
18
|
).void
|
18
19
|
}
|
19
|
-
def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
|
20
|
+
def initialize(dockerfile, image_url, credentials, registry_index_url_str = nil, main_tag = nil, alias_tags = [])
|
20
21
|
@new_version = T.let(@new_version, T.nilable(Image))
|
21
22
|
@current_version = T.let(@current_version, T.nilable(Image))
|
22
23
|
@previous_version = T.let(@previous_version, T.nilable(Image))
|
@@ -30,16 +31,18 @@ module Kuby
|
|
30
31
|
|
31
32
|
sig { returns(Image) }
|
32
33
|
def new_version
|
33
|
-
@new_version ||=
|
34
|
-
TimestampTag.new(Time.now).to_s, [Kuby::Docker::LATEST_TAG]
|
35
|
-
)
|
34
|
+
@new_version ||= create_new_version
|
36
35
|
end
|
37
36
|
|
38
37
|
sig { returns(Image) }
|
39
38
|
def current_version
|
40
|
-
@current_version ||=
|
41
|
-
|
42
|
-
|
39
|
+
@current_version ||= begin
|
40
|
+
duplicate_with_tags(
|
41
|
+
latest_timestamp_tag.to_s, [Kuby::Docker::LATEST_TAG]
|
42
|
+
)
|
43
|
+
rescue MissingTagError
|
44
|
+
create_new_version
|
45
|
+
end
|
43
46
|
end
|
44
47
|
|
45
48
|
sig { params(current_tag: T.nilable(String)).returns(Image) }
|
@@ -73,9 +76,9 @@ module Kuby
|
|
73
76
|
tag
|
74
77
|
end
|
75
78
|
|
76
|
-
sig { params(build_args: T::Hash[String, String]).void }
|
77
|
-
def build(build_args = {})
|
78
|
-
docker_cli.build(new_version, build_args: build_args)
|
79
|
+
sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String], context: T.nilable(String)).void }
|
80
|
+
def build(build_args = {}, docker_args = [], context: nil)
|
81
|
+
docker_cli.build(new_version, build_args: build_args, docker_args: docker_args, context: context)
|
79
82
|
@current_version = new_version
|
80
83
|
@new_version = nil
|
81
84
|
end
|
@@ -87,10 +90,17 @@ module Kuby
|
|
87
90
|
|
88
91
|
private
|
89
92
|
|
93
|
+
sig { returns(Image) }
|
94
|
+
def create_new_version
|
95
|
+
duplicate_with_tags(
|
96
|
+
TimestampTag.new(Time.now).to_s, [Kuby::Docker::LATEST_TAG]
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
90
100
|
sig { returns(::Docker::Remote::Client) }
|
91
101
|
def remote_client
|
92
102
|
@remote_client ||= ::Docker::Remote::Client.new(
|
93
|
-
|
103
|
+
registry_index_host, image_repo, credentials.username, credentials.password,
|
94
104
|
)
|
95
105
|
end
|
96
106
|
|
@@ -106,7 +116,7 @@ module Kuby
|
|
106
116
|
|
107
117
|
sig { returns(RemoteTags) }
|
108
118
|
def remote
|
109
|
-
@remote ||= RemoteTags.new(remote_client,
|
119
|
+
@remote ||= RemoteTags.new(remote_client, registry_index_host)
|
110
120
|
end
|
111
121
|
end
|
112
122
|
end
|
@@ -296,7 +296,7 @@ module Kuby
|
|
296
296
|
end
|
297
297
|
|
298
298
|
def image
|
299
|
-
@image ||= RailsApp::AssetsImage.new(docker.image, -> { dockerfile })
|
299
|
+
@image ||= RailsApp::AssetsImage.new(docker.image, -> { dockerfile }, docker.image.registry_index_url)
|
300
300
|
end
|
301
301
|
|
302
302
|
private
|
@@ -317,14 +317,16 @@ module Kuby
|
|
317
317
|
tags.each_cons(2) do |prev_tag, tag|
|
318
318
|
prev_image_name = "#{app_name}-#{prev_tag}"
|
319
319
|
df.from("#{base_image.image_url}:#{prev_tag}", as: prev_image_name)
|
320
|
+
df.arg('RAILS_MASTER_KEY')
|
320
321
|
df.run("mkdir -p #{RAILS_MOUNT_PATH}")
|
321
|
-
df.run("bundle exec rake kuby:rails_app:assets:copy")
|
322
|
+
df.run("env RAILS_MASTER_KEY=$RAILS_MASTER_KEY bundle exec rake kuby:rails_app:assets:copy")
|
322
323
|
|
323
324
|
if tag
|
324
325
|
image_name = "#{app_name}-#{tag}"
|
325
326
|
df.from("#{base_image.image_url}:#{tag}", as: image_name)
|
327
|
+
df.arg('RAILS_MASTER_KEY')
|
326
328
|
df.copy("--from=#{prev_image_name} #{RAILS_MOUNT_PATH}", RAILS_MOUNT_PATH)
|
327
|
-
df.run("bundle exec rake kuby:rails_app:assets:copy")
|
329
|
+
df.run("env RAILS_MASTER_KEY=$RAILS_MASTER_KEY bundle exec rake kuby:rails_app:assets:copy")
|
328
330
|
end
|
329
331
|
end
|
330
332
|
|
@@ -4,9 +4,10 @@ module Kuby
|
|
4
4
|
class AssetsImage < ::Kuby::Docker::Image
|
5
5
|
attr_reader :base_image
|
6
6
|
|
7
|
-
def initialize(base_image, dockerfile, main_tag = nil, alias_tags = [])
|
8
|
-
super(dockerfile, base_image.image_url, base_image.credentials, main_tag, alias_tags)
|
7
|
+
def initialize(base_image, dockerfile, registry_index_url = nil, main_tag = nil, alias_tags = [])
|
8
|
+
super(dockerfile, base_image.image_url, base_image.credentials, registry_index_url, main_tag, alias_tags)
|
9
9
|
@base_image = base_image
|
10
|
+
@identifier = "assets"
|
10
11
|
end
|
11
12
|
|
12
13
|
def new_version
|
@@ -27,8 +28,8 @@ module Kuby
|
|
27
28
|
)
|
28
29
|
end
|
29
30
|
|
30
|
-
def build(build_args = {})
|
31
|
-
docker_cli.build(current_version, build_args: build_args)
|
31
|
+
def build(build_args = {}, docker_args = [], context: nil)
|
32
|
+
docker_cli.build(current_version, build_args: build_args, docker_args: docker_args, context: context)
|
32
33
|
end
|
33
34
|
|
34
35
|
def push(tag)
|
@@ -41,6 +42,7 @@ module Kuby
|
|
41
42
|
self.class.new(
|
42
43
|
base_image,
|
43
44
|
dockerfile,
|
45
|
+
registry_index_url,
|
44
46
|
annotate_tag(image.main_tag),
|
45
47
|
image.alias_tags.map { |at| annotate_tag(at) }
|
46
48
|
)
|
@@ -86,16 +86,38 @@ class KubyGenerator < Rails::Generators::Base
|
|
86
86
|
create_file(
|
87
87
|
'.dockerignore',
|
88
88
|
<<~END
|
89
|
-
.
|
90
|
-
|
91
|
-
|
92
|
-
.
|
93
|
-
|
94
|
-
|
95
|
-
log
|
96
|
-
|
97
|
-
|
89
|
+
.git/
|
90
|
+
|
91
|
+
# Ignore bundler config.
|
92
|
+
.bundle
|
93
|
+
|
94
|
+
# Ignore all logfiles and tempfiles.
|
95
|
+
log/*
|
96
|
+
tmp/*
|
97
|
+
!log/.keep
|
98
|
+
!tmp/.keep
|
99
|
+
|
100
|
+
# Ignore pidfiles, but keep the directory.
|
101
|
+
tmp/pids/*
|
102
|
+
!tmp/pids/
|
103
|
+
!tmp/pids/.keep
|
104
|
+
|
105
|
+
# Ignore uploaded files in development.
|
106
|
+
storage/*
|
107
|
+
!storage/.keep
|
108
|
+
|
98
109
|
public/assets
|
110
|
+
**/.byebug_history
|
111
|
+
|
112
|
+
# Ignore master key for decrypting credentials and more.
|
113
|
+
config/master.key
|
114
|
+
|
115
|
+
public/packs
|
116
|
+
public/packs-test
|
117
|
+
node_modules
|
118
|
+
yarn-error.log
|
119
|
+
**/yarn-debug.log*
|
120
|
+
**/.yarn-integrity
|
99
121
|
END
|
100
122
|
)
|
101
123
|
end
|
data/lib/kuby/tasks.rb
CHANGED
@@ -9,10 +9,13 @@ module Kuby
|
|
9
9
|
@environment = environment
|
10
10
|
end
|
11
11
|
|
12
|
-
def print_dockerfiles
|
12
|
+
def print_dockerfiles(only: nil)
|
13
13
|
kubernetes.docker_images.each do |image|
|
14
|
+
next if only && image.identifier != only
|
15
|
+
|
14
16
|
image = image.current_version
|
15
|
-
|
17
|
+
identifier = image.identifier ? " ##{image.identifier}" : ""
|
18
|
+
Kuby.logger.info("Dockerfile for#{identifier} image #{image.image_url} with tags #{image.tags.join(', ')}")
|
16
19
|
theme = Rouge::Themes::Base16::Solarized.new
|
17
20
|
formatter = Rouge::Formatters::Terminal256.new(theme)
|
18
21
|
lexer = Rouge::Lexers::Docker.new
|
@@ -25,16 +28,31 @@ module Kuby
|
|
25
28
|
environment.kubernetes.setup
|
26
29
|
end
|
27
30
|
|
28
|
-
def build(build_args = {})
|
31
|
+
def build(build_args = {}, docker_args = [], only: nil, ignore_missing_args: false, context: nil)
|
32
|
+
check_platform(docker_args)
|
33
|
+
|
34
|
+
build_args['RAILS_MASTER_KEY'] ||= ENV['RAILS_MASTER_KEY'] || begin
|
35
|
+
master_key_file = File.join('config', 'master.key')
|
36
|
+
File.exist?(master_key_file) ? File.read(master_key_file).strip : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
check_build_args(build_args) unless ignore_missing_args
|
40
|
+
|
29
41
|
kubernetes.docker_images.each do |image|
|
42
|
+
next if only && image.identifier != only
|
43
|
+
|
44
|
+
return unless perform_docker_login_if_necessary(image)
|
45
|
+
|
30
46
|
image = image.new_version
|
31
47
|
Kuby.logger.info("Building image #{image.image_url} with tags #{image.tags.join(', ')}")
|
32
|
-
image.build(build_args)
|
48
|
+
image.build(build_args, docker_args, context: context)
|
33
49
|
end
|
34
50
|
end
|
35
51
|
|
36
|
-
def push
|
52
|
+
def push(only: nil)
|
37
53
|
kubernetes.docker_images.each do |image|
|
54
|
+
next if only && image.identifier != only
|
55
|
+
|
38
56
|
image = image.current_version
|
39
57
|
Kuby.logger.info("Pushing image #{image.image_url} with tags #{image.tags.join(', ')}")
|
40
58
|
push_image(image)
|
@@ -42,21 +60,7 @@ module Kuby
|
|
42
60
|
end
|
43
61
|
|
44
62
|
def push_image(image)
|
45
|
-
|
46
|
-
Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
|
47
|
-
|
48
|
-
begin
|
49
|
-
image.docker_cli.login(
|
50
|
-
url: image.image_host,
|
51
|
-
username: image.credentials.username,
|
52
|
-
password: image.credentials.password
|
53
|
-
)
|
54
|
-
rescue Kuby::Docker::LoginError => e
|
55
|
-
Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
|
56
|
-
Kuby.logger.fatal(e.message)
|
57
|
-
return
|
58
|
-
end
|
59
|
-
end
|
63
|
+
return unless perform_docker_login_if_necessary(image)
|
60
64
|
|
61
65
|
begin
|
62
66
|
image.tags.each { |tag| image.push(tag) }
|
@@ -77,10 +81,16 @@ module Kuby
|
|
77
81
|
environment.kubernetes.rollback
|
78
82
|
end
|
79
83
|
|
80
|
-
def print_resources
|
84
|
+
def print_resources(kind = nil, name_pattern = nil)
|
81
85
|
kubernetes.before_deploy
|
82
86
|
|
87
|
+
name_rxp = Regexp.new(name_pattern) if name_pattern
|
88
|
+
|
83
89
|
kubernetes.resources.each do |res|
|
90
|
+
next if kind && res.kind_sym.to_s != kind
|
91
|
+
|
92
|
+
next if name_rxp && !name_rxp.match?(res.metadata.name)
|
93
|
+
|
84
94
|
puts res.to_resource.serialize.to_yaml
|
85
95
|
end
|
86
96
|
end
|
@@ -132,6 +142,106 @@ module Kuby
|
|
132
142
|
|
133
143
|
private
|
134
144
|
|
145
|
+
def check_platform(docker_args)
|
146
|
+
arch, * = RUBY_PLATFORM.split('-')
|
147
|
+
|
148
|
+
if arch != 'x86_64' && !docker_args.include?('--platform')
|
149
|
+
Kuby.logger.fatal(<<~END)
|
150
|
+
Hey there! It looks like your processor isn't x86-compatible.
|
151
|
+
By default, Docker will try to build images that match the
|
152
|
+
current architecture, in this case #{arch}. Most hosting
|
153
|
+
providers run x86 hardware, meaning Docker images built using
|
154
|
+
this computer's architecture might fail to run when deployed
|
155
|
+
to production. You can fix this by running the build command
|
156
|
+
with a special --platform flag, eg:
|
157
|
+
|
158
|
+
bundle exec kuby -e production build -- --platform linux/amd64
|
159
|
+
|
160
|
+
If you meant to build for the current architecture, you can
|
161
|
+
prevent this error by passing the --platform argument for the
|
162
|
+
current architecture, eg. --platform linux/arm64 for ARM, etc.
|
163
|
+
END
|
164
|
+
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def check_build_args(build_args)
|
170
|
+
required_args = kubernetes.docker_images.flat_map do |image|
|
171
|
+
image.dockerfile.commands.flat_map do |command|
|
172
|
+
case command
|
173
|
+
when Kuby::Docker::Dockerfile::Arg
|
174
|
+
command.args
|
175
|
+
else
|
176
|
+
[]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
required_args.uniq!
|
182
|
+
|
183
|
+
if File.exist?(File.join('config', 'master.key'))
|
184
|
+
required_args.delete('RAILS_MASTER_KEY')
|
185
|
+
end
|
186
|
+
|
187
|
+
missing_args = required_args - build_args.keys
|
188
|
+
|
189
|
+
if missing_args.any?
|
190
|
+
Kuby.logger.fatal(<<~END)
|
191
|
+
The following Docker build arguments are missing: #{missing_args.join(', ')}.
|
192
|
+
Please pass each argument to `kuby build` using the -a or --arg parameter (note
|
193
|
+
that the -a/--arg parameter can be specified multiple times). For example:
|
194
|
+
|
195
|
+
kuby build -a #{missing_args.first}=value ...
|
196
|
+
|
197
|
+
To ignore missing build args, pass the --ignore-missing-args parameter.
|
198
|
+
END
|
199
|
+
|
200
|
+
exit 1
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def perform_docker_login_if_necessary(image)
|
205
|
+
auth_uris = image.docker_cli.auths.map do |url|
|
206
|
+
Kuby::Docker::DockerURI.parse_uri(url)
|
207
|
+
end
|
208
|
+
|
209
|
+
logged_in = image.credentials.username && (
|
210
|
+
auth_uris.any? do |uri|
|
211
|
+
image.image_hostname == uri.host ||
|
212
|
+
image.registry_index_hostname == uri.host
|
213
|
+
end
|
214
|
+
)
|
215
|
+
|
216
|
+
if !logged_in
|
217
|
+
Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
|
218
|
+
|
219
|
+
begin
|
220
|
+
# For some reason, Docker login with a port doesn't work for some
|
221
|
+
# registries (most notably Docker Hub). Since the default is 443 anyway,
|
222
|
+
# it should be fine to omit it.
|
223
|
+
url = if image.image_uri.has_default_port?
|
224
|
+
image.image_hostname # host without port
|
225
|
+
else
|
226
|
+
image.image_host # host with port
|
227
|
+
end
|
228
|
+
|
229
|
+
image.docker_cli.login(
|
230
|
+
url: url,
|
231
|
+
username: image.credentials.username,
|
232
|
+
password: image.credentials.password
|
233
|
+
)
|
234
|
+
rescue Kuby::Docker::LoginError => e
|
235
|
+
Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
|
236
|
+
Kuby.logger.fatal(e.message)
|
237
|
+
|
238
|
+
return false
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
135
245
|
def get_first_pod
|
136
246
|
pods = kubernetes_cli.get_objects(
|
137
247
|
'pods', namespace, match_labels.serialize
|
data/lib/kuby/version.rb
CHANGED
@@ -19,8 +19,8 @@ describe Kuby::Docker::TimestampedImage do
|
|
19
19
|
subject { image.current_version&.main_tag }
|
20
20
|
|
21
21
|
context 'with no local or remote tags' do
|
22
|
-
it '
|
23
|
-
expect
|
22
|
+
it 'creates a new tag' do
|
23
|
+
expect(subject).to match(/\d{14}/)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Dutro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|