kuby-core 0.12.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +2 -1
- data/lib/kuby/commands.rb +20 -9
- data/lib/kuby/docker/app_image.rb +18 -3
- data/lib/kuby/docker/bundler_phase.rb +19 -2
- data/lib/kuby/docker/cli.rb +5 -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 +7 -6
- data/lib/kuby/plugins/rails_app/assets.rb +5 -3
- data/lib/kuby/plugins/rails_app/assets_image.rb +10 -4
- data/lib/kuby/plugins/rails_app/generators/kuby.rb +31 -9
- data/lib/kuby/tasks.rb +125 -21
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/spec_spec.rb +12 -0
- metadata +3 -6
- data/spec/dummy/Gemfile.lock +0 -223
- data/spec/dummy/config/master.key +0 -1
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bb1eba2c179a03610f3f478f595c43638e692b41ebad53c7d6292ef5699000a
|
4
|
+
data.tar.gz: b9003940ecba163e1f9c08100223d6059920f7f9a2d8ebff0c038e1fd5f335ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fad183f5c697c6f6a06da8984dff19e0feac91b1996ae79a6605cafb612c77590461f80807ac87854124a97eef31e2999b39a64f997ef4f0fc77564365d81602
|
7
|
+
data.tar.gz: 1df3f9153d1595d085511329a5c1110117fa626632438a635f162c38cbe9c48d53df3098b28300408b89021365298b9972d07bdb9abd17b01bbc236e711ed3b8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## 0.16.0
|
2
|
+
* Allow Bundler executable to be customized.
|
3
|
+
- Main use-case is to allow adding in the Prebundler plugin, https://github.com/getkuby/kuby-prebundler.
|
4
|
+
* Print error message and exit on missing Docker build args.
|
5
|
+
- Ignore with `--ignore-missing-args`.
|
6
|
+
|
7
|
+
## 0.15.0
|
8
|
+
* Add an extra parameter to the Docker spec for specifying the URL of the Docker registry index.
|
9
|
+
- 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.
|
10
|
+
* Add config/master.key to the .dockerignore created by the Rails generator.
|
11
|
+
- This was an unfortunate oversight, but such are the perils of using pre-1.0 software.
|
12
|
+
* Print an error message if building images on non-x86 hardware.
|
13
|
+
- 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.
|
14
|
+
- We may need to revisit this in the future if ARM processors become more popular.
|
15
|
+
|
16
|
+
## 0.14.0
|
17
|
+
* Make RAILS_MASTER_KEY available in the assets image (@palkan, #63)
|
18
|
+
* Allow specifying custom docker build options via CLI (@palkan, #65)
|
19
|
+
|
20
|
+
## 0.13.0
|
21
|
+
* Fix handling rails/rake options in remote exec (@palkan, #60)
|
22
|
+
* Add `bundler_phase.gemfiles(*paths)` to allow adding additional gemfiles (@palkan, #61)
|
23
|
+
|
1
24
|
## 0.12.0
|
2
25
|
* Fix issue causing volume mount errors when k8s tries to schedule asset pods on multiple nodes (fixes #42).
|
3
26
|
- Persistent volumes can only be mounted on a single physical node.
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
## Kuby
|
2
2
|
|
3
|
-
|
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/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'
|
@@ -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
|
@@ -64,23 +65,30 @@ module Kuby
|
|
64
65
|
desc 'Builds the Docker image.'
|
65
66
|
command :build do |c|
|
66
67
|
c.flag [:a, :arg], required: false, multiple: true
|
67
|
-
c.
|
68
|
+
c.switch [:'ignore-missing-args'], required: false, default: false
|
69
|
+
c.flag [:only], required: false
|
70
|
+
c.action do |global_options, options, docker_args|
|
68
71
|
build_args = {}.tap do |build_args|
|
69
72
|
(options[:arg] || []).each do |a|
|
70
|
-
key, value = a.split('=')
|
73
|
+
key, value = a.split('=', 2)
|
71
74
|
value = value[1..-2] if value.start_with?('"') || value.start_with?("'")
|
72
75
|
build_args[key] = value
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
76
|
-
tasks.build(
|
79
|
+
tasks.build(
|
80
|
+
build_args, docker_args,
|
81
|
+
only: options[:only],
|
82
|
+
ignore_missing_args: options[:'ignore-missing-args']
|
83
|
+
)
|
77
84
|
end
|
78
85
|
end
|
79
86
|
|
80
87
|
desc 'Pushes the Docker image to the configured registry.'
|
81
88
|
command :push do |c|
|
89
|
+
c.flag [:only], required: false
|
82
90
|
c.action do |global_options, options, args|
|
83
|
-
tasks.push
|
91
|
+
tasks.push(only: options[:only])
|
84
92
|
end
|
85
93
|
end
|
86
94
|
|
@@ -93,8 +101,9 @@ module Kuby
|
|
93
101
|
|
94
102
|
desc 'Prints the effective Dockerfiles used to build Docker images.'
|
95
103
|
command :dockerfiles do |c|
|
104
|
+
c.flag [:only], required: false
|
96
105
|
c.action do |global_options, options, args|
|
97
|
-
tasks.print_dockerfiles
|
106
|
+
tasks.print_dockerfiles(only: options[:only])
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
@@ -116,8 +125,10 @@ module Kuby
|
|
116
125
|
|
117
126
|
desc 'Prints the effective Kubernetes resources that will be applied on deploy.'
|
118
127
|
command :resources do |c|
|
128
|
+
c.flag [:K, :kind], required: false
|
129
|
+
c.flag [:N, :name], required: false
|
119
130
|
c.action do |global_options, options, args|
|
120
|
-
tasks.print_resources
|
131
|
+
tasks.print_resources(options[:kind], options[:name])
|
121
132
|
end
|
122
133
|
end
|
123
134
|
|
@@ -164,7 +175,7 @@ module Kuby
|
|
164
175
|
rc.desc 'Runs an arbitrary command inside a running Rails pod.'
|
165
176
|
rc.command :exec do |c|
|
166
177
|
c.action do |global_options, options, args|
|
167
|
-
tasks.remote_exec(args)
|
178
|
+
tasks.remote_exec([*args, *T.unsafe(@rails_options)])
|
168
179
|
end
|
169
180
|
end
|
170
181
|
|
@@ -6,13 +6,28 @@ module Kuby
|
|
6
6
|
class AppImage < ::Kuby::Docker::TimestampedImage
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
-
sig {
|
10
|
-
|
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 = [])
|
11
26
|
unless ENV.fetch('RAILS_MASTER_KEY', '').empty?
|
12
27
|
build_args['RAILS_MASTER_KEY'] = T.must(ENV['RAILS_MASTER_KEY'])
|
13
28
|
end
|
14
29
|
|
15
|
-
super(build_args)
|
30
|
+
super(build_args, docker_args)
|
16
31
|
end
|
17
32
|
end
|
18
33
|
end
|
@@ -29,13 +29,21 @@ 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
|
35
41
|
|
36
42
|
@version = T.let(@version, T.nilable(String))
|
37
43
|
@gemfile = T.let(@gemfile, T.nilable(String))
|
44
|
+
@gemfiles = T.let([], T::Array[String])
|
38
45
|
@without = T.let(@without, T.nilable(T::Array[String]))
|
46
|
+
@executable = T.let(@executable, T.nilable(String))
|
39
47
|
end
|
40
48
|
|
41
49
|
sig { override.params(dockerfile: Dockerfile).void }
|
@@ -51,22 +59,31 @@ module Kuby
|
|
51
59
|
dockerfile.copy(gf, '.')
|
52
60
|
dockerfile.copy(lf, '.')
|
53
61
|
|
62
|
+
@gemfiles.each do |file|
|
63
|
+
dockerfile.copy(file, file)
|
64
|
+
end
|
65
|
+
|
54
66
|
unless wo.empty?
|
55
67
|
dockerfile.env("BUNDLE_WITHOUT='#{wo.join(' ')}'")
|
56
68
|
end
|
57
69
|
|
58
70
|
dockerfile.run(
|
59
|
-
'bundle', 'install',
|
71
|
+
executable || 'bundle', 'install',
|
60
72
|
'--jobs', '$(nproc)',
|
61
73
|
'--retry', '3',
|
62
74
|
'--gemfile', gf
|
63
75
|
)
|
64
76
|
|
65
77
|
# generate binstubs and add the bin directory to our path
|
66
|
-
dockerfile.run('bundle', 'binstubs', '--all')
|
78
|
+
dockerfile.run(executable || 'bundle', 'binstubs', '--all')
|
67
79
|
dockerfile.env("PATH=./bin:$PATH")
|
68
80
|
end
|
69
81
|
|
82
|
+
sig { params(paths: String).void }
|
83
|
+
def gemfiles(*paths)
|
84
|
+
@gemfiles.concat(paths)
|
85
|
+
end
|
86
|
+
|
70
87
|
private
|
71
88
|
|
72
89
|
sig { returns(String) }
|
data/lib/kuby/docker/cli.rb
CHANGED
@@ -53,15 +53,17 @@ module Kuby
|
|
53
53
|
config.fetch('auths', {}).keys
|
54
54
|
end
|
55
55
|
|
56
|
-
sig { params(image: Image, build_args: T::Hash[T.any(Symbol, String), String]).void }
|
57
|
-
def build(image, 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: [])
|
58
58
|
cmd = [
|
59
59
|
executable, 'build',
|
60
60
|
*image.tags.flat_map { |tag| ['-t', "#{image.image_url}:#{tag}"] },
|
61
61
|
*build_args.flat_map do |arg, val|
|
62
62
|
['--build-arg', Shellwords.shellescape("#{arg}=#{val}")]
|
63
63
|
end,
|
64
|
-
'-f-',
|
64
|
+
'-f-',
|
65
|
+
*docker_args,
|
66
|
+
'.'
|
65
67
|
]
|
66
68
|
|
67
69
|
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]).void }
|
120
|
+
def build(build_args = {}, docker_args = [])
|
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))
|
@@ -73,9 +74,9 @@ module Kuby
|
|
73
74
|
tag
|
74
75
|
end
|
75
76
|
|
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)
|
77
|
+
sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String]).void }
|
78
|
+
def build(build_args = {}, docker_args = [])
|
79
|
+
docker_cli.build(new_version, build_args: build_args, docker_args: docker_args)
|
79
80
|
@current_version = new_version
|
80
81
|
@new_version = nil
|
81
82
|
end
|
@@ -90,7 +91,7 @@ module Kuby
|
|
90
91
|
sig { returns(::Docker::Remote::Client) }
|
91
92
|
def remote_client
|
92
93
|
@remote_client ||= ::Docker::Remote::Client.new(
|
93
|
-
|
94
|
+
registry_index_host, image_repo, credentials.username, credentials.password,
|
94
95
|
)
|
95
96
|
end
|
96
97
|
|
@@ -106,7 +107,7 @@ module Kuby
|
|
106
107
|
|
107
108
|
sig { returns(RemoteTags) }
|
108
109
|
def remote
|
109
|
-
@remote ||= RemoteTags.new(remote_client,
|
110
|
+
@remote ||= RemoteTags.new(remote_client, registry_index_host)
|
110
111
|
end
|
111
112
|
end
|
112
113
|
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,12 @@ module Kuby
|
|
27
28
|
)
|
28
29
|
end
|
29
30
|
|
30
|
-
def build(build_args = {})
|
31
|
-
|
31
|
+
def build(build_args = {}, docker_args = [])
|
32
|
+
unless ENV.fetch('RAILS_MASTER_KEY', '').empty?
|
33
|
+
build_args['RAILS_MASTER_KEY'] = ENV['RAILS_MASTER_KEY']
|
34
|
+
end
|
35
|
+
|
36
|
+
docker_cli.build(current_version, build_args: build_args, docker_args: docker_args)
|
32
37
|
end
|
33
38
|
|
34
39
|
def push(tag)
|
@@ -41,6 +46,7 @@ module Kuby
|
|
41
46
|
self.class.new(
|
42
47
|
base_image,
|
43
48
|
dockerfile,
|
49
|
+
registry_index_url,
|
44
50
|
annotate_tag(image.main_tag),
|
45
51
|
image.alias_tags.map { |at| annotate_tag(at) }
|
46
52
|
)
|
@@ -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,25 @@ 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)
|
32
|
+
check_platform(docker_args)
|
33
|
+
check_build_args(build_args) unless ignore_missing_args
|
34
|
+
|
29
35
|
kubernetes.docker_images.each do |image|
|
36
|
+
next if only && image.identifier != only
|
37
|
+
|
38
|
+
return unless perform_docker_login_if_necessary(image)
|
39
|
+
|
30
40
|
image = image.new_version
|
31
41
|
Kuby.logger.info("Building image #{image.image_url} with tags #{image.tags.join(', ')}")
|
32
|
-
image.build(build_args)
|
42
|
+
image.build(build_args, docker_args)
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
36
|
-
def push
|
46
|
+
def push(only: nil)
|
37
47
|
kubernetes.docker_images.each do |image|
|
48
|
+
next if only && image.identifier != only
|
49
|
+
|
38
50
|
image = image.current_version
|
39
51
|
Kuby.logger.info("Pushing image #{image.image_url} with tags #{image.tags.join(', ')}")
|
40
52
|
push_image(image)
|
@@ -42,21 +54,7 @@ module Kuby
|
|
42
54
|
end
|
43
55
|
|
44
56
|
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
|
57
|
+
return unless perform_docker_login_if_necessary(image)
|
60
58
|
|
61
59
|
begin
|
62
60
|
image.tags.each { |tag| image.push(tag) }
|
@@ -77,10 +75,16 @@ module Kuby
|
|
77
75
|
environment.kubernetes.rollback
|
78
76
|
end
|
79
77
|
|
80
|
-
def print_resources
|
78
|
+
def print_resources(kind = nil, name_pattern = nil)
|
81
79
|
kubernetes.before_deploy
|
82
80
|
|
81
|
+
name_rxp = Regexp.new(name_pattern) if name_pattern
|
82
|
+
|
83
83
|
kubernetes.resources.each do |res|
|
84
|
+
next if kind && res.kind_sym.to_s != kind
|
85
|
+
|
86
|
+
next if name_rxp && !name_rxp.match?(res.metadata.name)
|
87
|
+
|
84
88
|
puts res.to_resource.serialize.to_yaml
|
85
89
|
end
|
86
90
|
end
|
@@ -132,6 +136,106 @@ module Kuby
|
|
132
136
|
|
133
137
|
private
|
134
138
|
|
139
|
+
def check_platform(docker_args)
|
140
|
+
arch, * = RUBY_PLATFORM.split('-')
|
141
|
+
|
142
|
+
if arch != 'x86_64' && !docker_args.include?('--platform')
|
143
|
+
Kuby.logger.fatal(<<~END)
|
144
|
+
Hey there! It looks like your processor isn't x86-compatible.
|
145
|
+
By default, Docker will try to build images that match the
|
146
|
+
current architecture, in this case #{arch}. Most hosting
|
147
|
+
providers run x86 hardware, meaning Docker images built using
|
148
|
+
this computer's architecture might fail to run when deployed
|
149
|
+
to production. You can fix this by running the build command
|
150
|
+
with a special --platform flag, eg:
|
151
|
+
|
152
|
+
bundle exec kuby -e production build -- --platform linux/amd64
|
153
|
+
|
154
|
+
If you meant to build for the current architecture, you can
|
155
|
+
prevent this error by passing the --platform argument for the
|
156
|
+
current architecture, eg. --platform linux/arm64 for ARM, etc.
|
157
|
+
END
|
158
|
+
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def check_build_args(build_args)
|
164
|
+
required_args = kubernetes.docker_images.flat_map do |image|
|
165
|
+
image.dockerfile.commands.flat_map do |command|
|
166
|
+
case command
|
167
|
+
when Kuby::Docker::Dockerfile::Arg
|
168
|
+
command.args
|
169
|
+
else
|
170
|
+
[]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
required_args.uniq!
|
176
|
+
|
177
|
+
if File.exist?(File.join('config', 'master.key'))
|
178
|
+
required_args.delete('RAILS_MASTER_KEY')
|
179
|
+
end
|
180
|
+
|
181
|
+
missing_args = required_args - build_args.keys
|
182
|
+
|
183
|
+
if missing_args.any?
|
184
|
+
Kuby.logger.fatal(<<~END)
|
185
|
+
The following Docker build arguments are missing: #{missing_args.join(', ')}.
|
186
|
+
Please pass each argument to `kuby build` using the -a or --arg parameter (note
|
187
|
+
that the -a/--arg parameter can be specified multiple times). For example:
|
188
|
+
|
189
|
+
kuby build -a #{missing_args.first}=value ...
|
190
|
+
|
191
|
+
To ignore missing build args, pass the --ignore-missing-args parameter.
|
192
|
+
END
|
193
|
+
|
194
|
+
exit 1
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def perform_docker_login_if_necessary(image)
|
199
|
+
auth_uris = image.docker_cli.auths.map do |url|
|
200
|
+
Kuby::Docker::DockerURI.parse_uri(url)
|
201
|
+
end
|
202
|
+
|
203
|
+
logged_in = image.credentials.username && (
|
204
|
+
auth_uris.any? do |uri|
|
205
|
+
image.image_hostname == uri.host ||
|
206
|
+
image.registry_index_hostname == uri.host
|
207
|
+
end
|
208
|
+
)
|
209
|
+
|
210
|
+
if !logged_in
|
211
|
+
Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
|
212
|
+
|
213
|
+
begin
|
214
|
+
# For some reason, Docker login with a port doesn't work for some
|
215
|
+
# registries (most notably Docker Hub). Since the default is 443 anyway,
|
216
|
+
# it should be fine to omit it.
|
217
|
+
url = if image.image_uri.has_default_port?
|
218
|
+
image.image_hostname # host without port
|
219
|
+
else
|
220
|
+
image.image_host # host with port
|
221
|
+
end
|
222
|
+
|
223
|
+
image.docker_cli.login(
|
224
|
+
url: url,
|
225
|
+
username: image.credentials.username,
|
226
|
+
password: image.credentials.password
|
227
|
+
)
|
228
|
+
rescue Kuby::Docker::LoginError => e
|
229
|
+
Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
|
230
|
+
Kuby.logger.fatal(e.message)
|
231
|
+
|
232
|
+
return false
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
true
|
237
|
+
end
|
238
|
+
|
135
239
|
def get_first_pod
|
136
240
|
pods = kubernetes_cli.get_objects(
|
137
241
|
'pods', namespace, match_labels.serialize
|
data/lib/kuby/version.rb
CHANGED
data/spec/docker/spec_spec.rb
CHANGED
@@ -107,6 +107,18 @@ describe Kuby::Docker::Spec do
|
|
107
107
|
expect(subject).to match(/RUN bundle install .* --gemfile foo\/bar\/Gemfile/)
|
108
108
|
end
|
109
109
|
end
|
110
|
+
|
111
|
+
context 'when multiple gemfiles are specified' do
|
112
|
+
before { spec.bundler_phase.gemfiles('gemfiles/a.gemfile', 'gemfiles/b.gemfile') }
|
113
|
+
|
114
|
+
it 'uses all gemfiles including the default one' do
|
115
|
+
expect(subject).to include("COPY Gemfile .\n")
|
116
|
+
expect(subject).to include("COPY Gemfile.lock .\n")
|
117
|
+
expect(subject).to include("COPY gemfiles/a.gemfile gemfiles/a.gemfile\n")
|
118
|
+
expect(subject).to include("COPY gemfiles/b.gemfile gemfiles/b.gemfile\n")
|
119
|
+
expect(subject).to match(/RUN bundle install .* --gemfile Gemfile/)
|
120
|
+
end
|
121
|
+
end
|
110
122
|
end
|
111
123
|
|
112
124
|
describe '#package' do
|
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.0
|
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-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -290,7 +290,6 @@ files:
|
|
290
290
|
- spec/docker/timestamp_tag_spec.rb
|
291
291
|
- spec/docker/timestamped_image_spec.rb
|
292
292
|
- spec/dummy/Gemfile
|
293
|
-
- spec/dummy/Gemfile.lock
|
294
293
|
- spec/dummy/README.md
|
295
294
|
- spec/dummy/Rakefile
|
296
295
|
- spec/dummy/app/assets/config/manifest.js
|
@@ -334,7 +333,6 @@ files:
|
|
334
333
|
- spec/dummy/config/initializers/mime_types.rb
|
335
334
|
- spec/dummy/config/initializers/wrap_parameters.rb
|
336
335
|
- spec/dummy/config/locales/en.yml
|
337
|
-
- spec/dummy/config/master.key
|
338
336
|
- spec/dummy/config/puma.rb
|
339
337
|
- spec/dummy/config/routes.rb
|
340
338
|
- spec/dummy/config/spring.rb
|
@@ -351,7 +349,6 @@ files:
|
|
351
349
|
- spec/dummy/test/application_system_test_case.rb
|
352
350
|
- spec/dummy/test/channels/application_cable/connection_test.rb
|
353
351
|
- spec/dummy/test/test_helper.rb
|
354
|
-
- spec/dummy/tmp/cache/bootsnap-load-path-cache
|
355
352
|
- spec/spec_helper.rb
|
356
353
|
- spec/support/docker/fake_cli.rb
|
357
354
|
- spec/support/docker/remote/fake_client.rb
|
@@ -374,7 +371,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
374
371
|
- !ruby/object:Gem::Version
|
375
372
|
version: '0'
|
376
373
|
requirements: []
|
377
|
-
rubygems_version: 3.
|
374
|
+
rubygems_version: 3.2.22
|
378
375
|
signing_key:
|
379
376
|
specification_version: 4
|
380
377
|
summary: Deploy your Rails app onto Kubernetes the easy way.
|
data/spec/dummy/Gemfile.lock
DELETED
@@ -1,223 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: https://rubygems.org/
|
3
|
-
specs:
|
4
|
-
actioncable (6.0.3.2)
|
5
|
-
actionpack (= 6.0.3.2)
|
6
|
-
nio4r (~> 2.0)
|
7
|
-
websocket-driver (>= 0.6.1)
|
8
|
-
actionmailbox (6.0.3.2)
|
9
|
-
actionpack (= 6.0.3.2)
|
10
|
-
activejob (= 6.0.3.2)
|
11
|
-
activerecord (= 6.0.3.2)
|
12
|
-
activestorage (= 6.0.3.2)
|
13
|
-
activesupport (= 6.0.3.2)
|
14
|
-
mail (>= 2.7.1)
|
15
|
-
actionmailer (6.0.3.2)
|
16
|
-
actionpack (= 6.0.3.2)
|
17
|
-
actionview (= 6.0.3.2)
|
18
|
-
activejob (= 6.0.3.2)
|
19
|
-
mail (~> 2.5, >= 2.5.4)
|
20
|
-
rails-dom-testing (~> 2.0)
|
21
|
-
actionpack (6.0.3.2)
|
22
|
-
actionview (= 6.0.3.2)
|
23
|
-
activesupport (= 6.0.3.2)
|
24
|
-
rack (~> 2.0, >= 2.0.8)
|
25
|
-
rack-test (>= 0.6.3)
|
26
|
-
rails-dom-testing (~> 2.0)
|
27
|
-
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
28
|
-
actiontext (6.0.3.2)
|
29
|
-
actionpack (= 6.0.3.2)
|
30
|
-
activerecord (= 6.0.3.2)
|
31
|
-
activestorage (= 6.0.3.2)
|
32
|
-
activesupport (= 6.0.3.2)
|
33
|
-
nokogiri (>= 1.8.5)
|
34
|
-
actionview (6.0.3.2)
|
35
|
-
activesupport (= 6.0.3.2)
|
36
|
-
builder (~> 3.1)
|
37
|
-
erubi (~> 1.4)
|
38
|
-
rails-dom-testing (~> 2.0)
|
39
|
-
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
40
|
-
activejob (6.0.3.2)
|
41
|
-
activesupport (= 6.0.3.2)
|
42
|
-
globalid (>= 0.3.6)
|
43
|
-
activemodel (6.0.3.2)
|
44
|
-
activesupport (= 6.0.3.2)
|
45
|
-
activerecord (6.0.3.2)
|
46
|
-
activemodel (= 6.0.3.2)
|
47
|
-
activesupport (= 6.0.3.2)
|
48
|
-
activestorage (6.0.3.2)
|
49
|
-
actionpack (= 6.0.3.2)
|
50
|
-
activejob (= 6.0.3.2)
|
51
|
-
activerecord (= 6.0.3.2)
|
52
|
-
marcel (~> 0.3.1)
|
53
|
-
activesupport (6.0.3.2)
|
54
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
55
|
-
i18n (>= 0.7, < 2)
|
56
|
-
minitest (~> 5.1)
|
57
|
-
tzinfo (~> 1.1)
|
58
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
59
|
-
addressable (2.7.0)
|
60
|
-
public_suffix (>= 2.0.2, < 5.0)
|
61
|
-
bindex (0.8.1)
|
62
|
-
bootsnap (1.4.8)
|
63
|
-
msgpack (~> 1.0)
|
64
|
-
builder (3.2.4)
|
65
|
-
byebug (11.1.3)
|
66
|
-
capybara (3.33.0)
|
67
|
-
addressable
|
68
|
-
mini_mime (>= 0.1.3)
|
69
|
-
nokogiri (~> 1.8)
|
70
|
-
rack (>= 1.6.0)
|
71
|
-
rack-test (>= 0.6.3)
|
72
|
-
regexp_parser (~> 1.5)
|
73
|
-
xpath (~> 3.2)
|
74
|
-
childprocess (3.0.0)
|
75
|
-
concurrent-ruby (1.1.7)
|
76
|
-
crass (1.0.6)
|
77
|
-
erubi (1.9.0)
|
78
|
-
ffi (1.13.1)
|
79
|
-
globalid (0.4.2)
|
80
|
-
activesupport (>= 4.2.0)
|
81
|
-
i18n (1.8.5)
|
82
|
-
concurrent-ruby (~> 1.0)
|
83
|
-
jbuilder (2.10.0)
|
84
|
-
activesupport (>= 5.0.0)
|
85
|
-
listen (3.2.1)
|
86
|
-
rb-fsevent (~> 0.10, >= 0.10.3)
|
87
|
-
rb-inotify (~> 0.9, >= 0.9.10)
|
88
|
-
loofah (2.7.0)
|
89
|
-
crass (~> 1.0.2)
|
90
|
-
nokogiri (>= 1.5.9)
|
91
|
-
mail (2.7.1)
|
92
|
-
mini_mime (>= 0.1.1)
|
93
|
-
marcel (0.3.3)
|
94
|
-
mimemagic (~> 0.3.2)
|
95
|
-
method_source (1.0.0)
|
96
|
-
mimemagic (0.3.5)
|
97
|
-
mini_mime (1.0.2)
|
98
|
-
mini_portile2 (2.4.0)
|
99
|
-
minitest (5.14.1)
|
100
|
-
msgpack (1.3.3)
|
101
|
-
nio4r (2.5.2)
|
102
|
-
nokogiri (1.10.10)
|
103
|
-
mini_portile2 (~> 2.4.0)
|
104
|
-
public_suffix (4.0.5)
|
105
|
-
puma (4.3.5)
|
106
|
-
nio4r (~> 2.0)
|
107
|
-
rack (2.2.3)
|
108
|
-
rack-proxy (0.6.5)
|
109
|
-
rack
|
110
|
-
rack-test (1.1.0)
|
111
|
-
rack (>= 1.0, < 3)
|
112
|
-
rails (6.0.3.2)
|
113
|
-
actioncable (= 6.0.3.2)
|
114
|
-
actionmailbox (= 6.0.3.2)
|
115
|
-
actionmailer (= 6.0.3.2)
|
116
|
-
actionpack (= 6.0.3.2)
|
117
|
-
actiontext (= 6.0.3.2)
|
118
|
-
actionview (= 6.0.3.2)
|
119
|
-
activejob (= 6.0.3.2)
|
120
|
-
activemodel (= 6.0.3.2)
|
121
|
-
activerecord (= 6.0.3.2)
|
122
|
-
activestorage (= 6.0.3.2)
|
123
|
-
activesupport (= 6.0.3.2)
|
124
|
-
bundler (>= 1.3.0)
|
125
|
-
railties (= 6.0.3.2)
|
126
|
-
sprockets-rails (>= 2.0.0)
|
127
|
-
rails-dom-testing (2.0.3)
|
128
|
-
activesupport (>= 4.2.0)
|
129
|
-
nokogiri (>= 1.6)
|
130
|
-
rails-html-sanitizer (1.3.0)
|
131
|
-
loofah (~> 2.3)
|
132
|
-
railties (6.0.3.2)
|
133
|
-
actionpack (= 6.0.3.2)
|
134
|
-
activesupport (= 6.0.3.2)
|
135
|
-
method_source
|
136
|
-
rake (>= 0.8.7)
|
137
|
-
thor (>= 0.20.3, < 2.0)
|
138
|
-
rake (13.0.1)
|
139
|
-
rb-fsevent (0.10.4)
|
140
|
-
rb-inotify (0.10.1)
|
141
|
-
ffi (~> 1.0)
|
142
|
-
regexp_parser (1.7.1)
|
143
|
-
rubyzip (2.3.0)
|
144
|
-
sass-rails (6.0.0)
|
145
|
-
sassc-rails (~> 2.1, >= 2.1.1)
|
146
|
-
sassc (2.4.0)
|
147
|
-
ffi (~> 1.9)
|
148
|
-
sassc-rails (2.1.2)
|
149
|
-
railties (>= 4.0.0)
|
150
|
-
sassc (>= 2.0)
|
151
|
-
sprockets (> 3.0)
|
152
|
-
sprockets-rails
|
153
|
-
tilt
|
154
|
-
selenium-webdriver (3.142.7)
|
155
|
-
childprocess (>= 0.5, < 4.0)
|
156
|
-
rubyzip (>= 1.2.2)
|
157
|
-
spring (2.1.1)
|
158
|
-
spring-watcher-listen (2.0.1)
|
159
|
-
listen (>= 2.7, < 4.0)
|
160
|
-
spring (>= 1.2, < 3.0)
|
161
|
-
sprockets (4.0.2)
|
162
|
-
concurrent-ruby (~> 1.0)
|
163
|
-
rack (> 1, < 3)
|
164
|
-
sprockets-rails (3.2.1)
|
165
|
-
actionpack (>= 4.0)
|
166
|
-
activesupport (>= 4.0)
|
167
|
-
sprockets (>= 3.0.0)
|
168
|
-
sqlite3 (1.4.2)
|
169
|
-
thor (1.0.1)
|
170
|
-
thread_safe (0.3.6)
|
171
|
-
tilt (2.0.10)
|
172
|
-
turbolinks (5.2.1)
|
173
|
-
turbolinks-source (~> 5.2)
|
174
|
-
turbolinks-source (5.2.0)
|
175
|
-
tzinfo (1.2.7)
|
176
|
-
thread_safe (~> 0.1)
|
177
|
-
web-console (4.0.4)
|
178
|
-
actionview (>= 6.0.0)
|
179
|
-
activemodel (>= 6.0.0)
|
180
|
-
bindex (>= 0.4.0)
|
181
|
-
railties (>= 6.0.0)
|
182
|
-
webdrivers (4.4.1)
|
183
|
-
nokogiri (~> 1.6)
|
184
|
-
rubyzip (>= 1.3.0)
|
185
|
-
selenium-webdriver (>= 3.0, < 4.0)
|
186
|
-
webpacker (4.3.0)
|
187
|
-
activesupport (>= 4.2)
|
188
|
-
rack-proxy (>= 0.6.1)
|
189
|
-
railties (>= 4.2)
|
190
|
-
websocket-driver (0.7.3)
|
191
|
-
websocket-extensions (>= 0.1.0)
|
192
|
-
websocket-extensions (0.1.5)
|
193
|
-
xpath (3.2.0)
|
194
|
-
nokogiri (~> 1.8)
|
195
|
-
zeitwerk (2.4.0)
|
196
|
-
|
197
|
-
PLATFORMS
|
198
|
-
ruby
|
199
|
-
|
200
|
-
DEPENDENCIES
|
201
|
-
bootsnap (>= 1.4.2)
|
202
|
-
byebug
|
203
|
-
capybara (>= 2.15)
|
204
|
-
jbuilder (~> 2.7)
|
205
|
-
listen (~> 3.2)
|
206
|
-
puma (~> 4.1)
|
207
|
-
rails (~> 6.0.3, >= 6.0.3.2)
|
208
|
-
sass-rails (>= 6)
|
209
|
-
selenium-webdriver
|
210
|
-
spring
|
211
|
-
spring-watcher-listen (~> 2.0.0)
|
212
|
-
sqlite3 (~> 1.4)
|
213
|
-
turbolinks (~> 5)
|
214
|
-
tzinfo-data
|
215
|
-
web-console (>= 3.3.0)
|
216
|
-
webdrivers
|
217
|
-
webpacker (~> 4.0)
|
218
|
-
|
219
|
-
RUBY VERSION
|
220
|
-
ruby 2.5.8p224
|
221
|
-
|
222
|
-
BUNDLED WITH
|
223
|
-
2.1.4
|
@@ -1 +0,0 @@
|
|
1
|
-
e02c0a6ae84e2533f0f5a02962d0ac35
|
Binary file
|