matrixeval 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/Gemfile +4 -0
- data/README.md +91 -6
- data/Rakefile +1 -1
- data/bin/test +4 -0
- data/exe/matrixeval +5 -0
- data/exe/meval +5 -0
- data/lib/matrixeval/command_line/parse_context_arguments.rb +80 -0
- data/lib/matrixeval/command_line/parse_init_arguments.rb +50 -0
- data/lib/matrixeval/command_line.rb +57 -0
- data/lib/matrixeval/config/yaml.rb +47 -0
- data/lib/matrixeval/config.rb +101 -0
- data/lib/matrixeval/container.rb +13 -0
- data/lib/matrixeval/context/build_docker_compose_extend.rb +41 -0
- data/lib/matrixeval/context/find_by_command_options.rb +65 -0
- data/lib/matrixeval/context.rb +80 -0
- data/lib/matrixeval/docker_compose/extend.rb +19 -0
- data/lib/matrixeval/docker_compose/extend_raw.rb +15 -0
- data/lib/matrixeval/docker_compose/file.rb +122 -0
- data/lib/matrixeval/docker_compose.rb +65 -0
- data/lib/matrixeval/extra_mount_files.rb +21 -0
- data/lib/matrixeval/gitignore.rb +40 -0
- data/lib/matrixeval/runner.rb +207 -0
- data/lib/matrixeval/target.rb +45 -0
- data/lib/matrixeval/templates/matrixeval.yml +70 -0
- data/lib/matrixeval/variant.rb +51 -0
- data/lib/matrixeval/vector.rb +38 -0
- data/lib/matrixeval/version.rb +1 -1
- data/lib/matrixeval.rb +44 -1
- metadata +72 -5
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative "./context/find_by_command_options"
|
2
|
+
require_relative "./context/build_docker_compose_extend"
|
3
|
+
|
4
|
+
module Matrixeval
|
5
|
+
class Context
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def find_by_command_options!(options)
|
10
|
+
FindByCommandOptions.call(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
Config.variant_combinations.map do |variants|
|
15
|
+
Context.new(
|
16
|
+
main_variant: variants.find { |v| v.vector.main? },
|
17
|
+
rest_variants: variants.reject { |v| v.vector.main? }
|
18
|
+
)
|
19
|
+
end.select do |context|
|
20
|
+
Config.exclusions.none? do |exclusion|
|
21
|
+
context.match_exclusion?(exclusion)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :main_variant, :rest_variants
|
29
|
+
|
30
|
+
def initialize(main_variant:, rest_variants:)
|
31
|
+
@main_variant = main_variant
|
32
|
+
@rest_variants = (rest_variants || []).sort do |v1, v2|
|
33
|
+
v1.id <=> v2.id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
variants.map(&:name).join(", ")
|
39
|
+
end
|
40
|
+
|
41
|
+
def id
|
42
|
+
[[main_variant.id] + rest_variants.map(&:id)].join("_")
|
43
|
+
end
|
44
|
+
|
45
|
+
def env
|
46
|
+
rest_variants.map(&:env).reduce({}, &:merge)
|
47
|
+
.merge(main_variant.env)
|
48
|
+
end
|
49
|
+
|
50
|
+
def docker_compose_service_name
|
51
|
+
main_variant.id
|
52
|
+
end
|
53
|
+
|
54
|
+
def docker_compose_file_path
|
55
|
+
Matrixeval.working_dir.join(".matrixeval/docker-compose/#{id}.yml")
|
56
|
+
end
|
57
|
+
|
58
|
+
def variants
|
59
|
+
[main_variant] + rest_variants
|
60
|
+
end
|
61
|
+
|
62
|
+
def match_exclusion?(exclusion)
|
63
|
+
return false if exclusion.empty?
|
64
|
+
|
65
|
+
variants.all? do |variant|
|
66
|
+
vector_key = variant.vector.key
|
67
|
+
if exclusion.key?(vector_key)
|
68
|
+
exclusion[vector_key].to_s == variant.key
|
69
|
+
else
|
70
|
+
true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def docker_compose_extend
|
76
|
+
BuildDockerComposeExtend.call(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Matrixeval
|
2
|
+
class DockerCompose
|
3
|
+
class File
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def create_all
|
7
|
+
FileUtils.mkdir_p folder
|
8
|
+
|
9
|
+
Context.all.each do |context|
|
10
|
+
new(context).create
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def folder
|
17
|
+
Matrixeval.working_dir.join(".matrixeval/docker-compose")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :context
|
22
|
+
|
23
|
+
def initialize(context)
|
24
|
+
@context = context
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
::File.open(docker_compose_file_path, 'w+') do |file|
|
29
|
+
file.puts build_content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def docker_compose_file_path
|
36
|
+
context.docker_compose_file_path
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_content
|
40
|
+
{
|
41
|
+
"version" => "3",
|
42
|
+
"services" => services_json,
|
43
|
+
"volumes" => volumes_json
|
44
|
+
}.to_yaml.sub(/---\n/, "")
|
45
|
+
end
|
46
|
+
|
47
|
+
def services_json
|
48
|
+
services = {}
|
49
|
+
|
50
|
+
services[main_variant.docker_compose_service_name] = {
|
51
|
+
"image" => main_variant.container.image,
|
52
|
+
"volumes" => mounts,
|
53
|
+
"environment" => env,
|
54
|
+
"working_dir" => "/app"
|
55
|
+
}.merge(depends_on)
|
56
|
+
|
57
|
+
services.merge(docker_compose_extend.services)
|
58
|
+
end
|
59
|
+
|
60
|
+
def volumes_json
|
61
|
+
target.volumes(context).merge(
|
62
|
+
docker_compose_extend.volumes
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def env
|
67
|
+
target.env(context).merge(
|
68
|
+
Config.env,
|
69
|
+
main_variant.container.env,
|
70
|
+
context.env
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def depends_on
|
75
|
+
if docker_compose_extend.services.keys.empty?
|
76
|
+
{}
|
77
|
+
else
|
78
|
+
{ "depends_on" => docker_compose_extend.services.keys }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def main_variant
|
83
|
+
context.main_variant
|
84
|
+
end
|
85
|
+
|
86
|
+
def mounts
|
87
|
+
["../..:/app:cached"] + target.mounts(context) + extra_mounts
|
88
|
+
end
|
89
|
+
|
90
|
+
def extra_mounts
|
91
|
+
mounts = Config.mounts + context.variants.map(&:mounts).flatten
|
92
|
+
mounts.map do |mount|
|
93
|
+
local_path, in_docker_path = mount.split(':')
|
94
|
+
next mount if Pathname.new(local_path).absolute?
|
95
|
+
|
96
|
+
local_path = Matrixeval.working_dir.join(local_path)
|
97
|
+
docker_compose_folder_path = Matrixeval.working_dir.join(".matrixeval/docker-compose")
|
98
|
+
local_path = local_path.relative_path_from docker_compose_folder_path
|
99
|
+
|
100
|
+
"#{local_path}:#{in_docker_path}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def docker_compose_extend
|
105
|
+
@docker_compose_extend ||= context.docker_compose_extend
|
106
|
+
end
|
107
|
+
|
108
|
+
def working_dir_name
|
109
|
+
Matrixeval.working_dir.basename
|
110
|
+
end
|
111
|
+
|
112
|
+
def project_name
|
113
|
+
Config.project_name.gsub(/[^A-Za-z0-9-]/,'_').downcase
|
114
|
+
end
|
115
|
+
|
116
|
+
def target
|
117
|
+
Config.target
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative "./docker_compose/file"
|
2
|
+
|
3
|
+
module Matrixeval
|
4
|
+
class DockerCompose
|
5
|
+
|
6
|
+
attr_reader :context
|
7
|
+
|
8
|
+
def initialize(context)
|
9
|
+
@context = context
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(arguments)
|
13
|
+
forward_arguments = arguments.map do |arg|
|
14
|
+
arg.match(/\s/) ? "\"#{arg}\"" : arg
|
15
|
+
end.join(" ")
|
16
|
+
|
17
|
+
no_tty = %w[bash sh zsh dash].include?(arguments[0]) ? '' : '--no-TTY'
|
18
|
+
|
19
|
+
system(
|
20
|
+
<<~DOCKER_COMPOSE_COMMAND
|
21
|
+
#{docker_compose} \
|
22
|
+
run --rm \
|
23
|
+
#{no_tty} \
|
24
|
+
#{context.docker_compose_service_name} \
|
25
|
+
#{forward_arguments}
|
26
|
+
DOCKER_COMPOSE_COMMAND
|
27
|
+
)
|
28
|
+
ensure
|
29
|
+
stop_containers
|
30
|
+
clean_containers_and_anonymous_volumes
|
31
|
+
turn_on_stty_opost
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def stop_containers
|
37
|
+
system("#{docker_compose} stop >> /dev/null 2>&1")
|
38
|
+
end
|
39
|
+
|
40
|
+
def clean_containers_and_anonymous_volumes
|
41
|
+
system("#{docker_compose} rm -v -f >> /dev/null 2>&1")
|
42
|
+
end
|
43
|
+
|
44
|
+
def docker_compose
|
45
|
+
<<~DOCKER_COMPOSE_COMMAND.strip
|
46
|
+
docker --log-level error compose \
|
47
|
+
-f #{yaml_file} \
|
48
|
+
-p matrixeval-#{project_name}-#{context.id}
|
49
|
+
DOCKER_COMPOSE_COMMAND
|
50
|
+
end
|
51
|
+
|
52
|
+
def yaml_file
|
53
|
+
".matrixeval/docker-compose/#{context.id}.yml"
|
54
|
+
end
|
55
|
+
|
56
|
+
def turn_on_stty_opost
|
57
|
+
system("stty opost")
|
58
|
+
end
|
59
|
+
|
60
|
+
def project_name
|
61
|
+
Config.project_name.gsub(/[^A-Za-z0-9-]/,'_').downcase
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Matrixeval
|
2
|
+
class ExtraMountFiles
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def create
|
6
|
+
Config.all_mounts.each do |mount|
|
7
|
+
local_path, _ = mount.split(':')
|
8
|
+
next mount if Pathname.new(local_path).absolute?
|
9
|
+
|
10
|
+
local_path = Matrixeval.working_dir.join(local_path)
|
11
|
+
next if local_path.extname.empty?
|
12
|
+
next if local_path.ascend.none? { |path| path == Matrixeval.working_dir }
|
13
|
+
|
14
|
+
FileUtils.mkdir_p local_path.dirname
|
15
|
+
FileUtils.touch local_path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Matrixeval
|
2
|
+
class Gitignore
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def update
|
6
|
+
ignore_paths.map do |path|
|
7
|
+
ignore(path)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def ignore_paths
|
14
|
+
[docker_compose] + Config.target.gitignore_paths
|
15
|
+
end
|
16
|
+
|
17
|
+
def docker_compose
|
18
|
+
".matrixeval/docker-compose"
|
19
|
+
end
|
20
|
+
|
21
|
+
def ignore(path)
|
22
|
+
return if ignored?(path)
|
23
|
+
|
24
|
+
File.open(gitignore_path, 'a+') do |file|
|
25
|
+
file.puts path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ignored?(path)
|
30
|
+
File.exist?(gitignore_path) &&
|
31
|
+
File.read(gitignore_path).include?(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def gitignore_path
|
35
|
+
Matrixeval.working_dir.join(".gitignore")
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'rainbow'
|
2
|
+
require "concurrent/utility/processor_counter"
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
module Matrixeval
|
6
|
+
class Runner
|
7
|
+
class << self
|
8
|
+
def start(argv)
|
9
|
+
new(argv).start
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :argv, :command
|
14
|
+
|
15
|
+
def initialize(argv)
|
16
|
+
@argv = argv
|
17
|
+
@command = CommandLine.new(argv)
|
18
|
+
@threads ||= []
|
19
|
+
@matrixeval_results ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
validates
|
24
|
+
|
25
|
+
if command.init?
|
26
|
+
init
|
27
|
+
elsif command.all?
|
28
|
+
run_all_contexts
|
29
|
+
else
|
30
|
+
run_a_specific_context
|
31
|
+
end
|
32
|
+
rescue OptionParser::InvalidOption => e
|
33
|
+
puts <<~ERROR
|
34
|
+
#{e.message}
|
35
|
+
See 'matrixeval --help'
|
36
|
+
ERROR
|
37
|
+
exit
|
38
|
+
rescue Config::YAML::MissingError
|
39
|
+
puts "Please run 'matrixeval init' first to generate matrixeval.yml"
|
40
|
+
puts "See 'matrixeval init -h'"
|
41
|
+
exit
|
42
|
+
ensure
|
43
|
+
turn_on_stty_opost
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def validates
|
49
|
+
return if command.valid?
|
50
|
+
|
51
|
+
puts <<~ERROR
|
52
|
+
matrixeval: '#{argv.join(' ')}' is not a MatrixEval command.
|
53
|
+
See 'matrixeval --help'
|
54
|
+
ERROR
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
def init
|
59
|
+
Config::YAML.create_for(command.init_options[:target])
|
60
|
+
Gitignore.update
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_all_contexts
|
64
|
+
load_plugin
|
65
|
+
|
66
|
+
DockerCompose::File.create_all
|
67
|
+
Gitignore.update
|
68
|
+
ExtraMountFiles.create
|
69
|
+
Config.target.create_files
|
70
|
+
|
71
|
+
pull_all_images
|
72
|
+
|
73
|
+
if workers_count == 1
|
74
|
+
run_all_contexts_sequentially
|
75
|
+
else
|
76
|
+
run_all_contexts_in_parallel
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_all_contexts_sequentially
|
81
|
+
Context.all.each do |context|
|
82
|
+
puts Rainbow("[ MatrixEval ] ").blue.bright + Rainbow(" #{context.name} ").white.bright.bg(:blue)
|
83
|
+
puts Rainbow("[ MatrixEval ] Run \"#{command.rest_arguments.join(" ")}\"").blue.bright
|
84
|
+
|
85
|
+
docker_compose = DockerCompose.new(context)
|
86
|
+
success = docker_compose.run(command.rest_arguments)
|
87
|
+
|
88
|
+
@matrixeval_results << [context, !!success]
|
89
|
+
end
|
90
|
+
|
91
|
+
report
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_all_contexts_in_parallel
|
95
|
+
parallel(contexts) do |sub_contexts|
|
96
|
+
Thread.current[:matrixeval_results] = []
|
97
|
+
|
98
|
+
sub_contexts.each do |context|
|
99
|
+
docker_compose = DockerCompose.new(context)
|
100
|
+
success = docker_compose.run(command.rest_arguments)
|
101
|
+
|
102
|
+
Thread.current[:matrixeval_results] << [context, !!success]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
report
|
107
|
+
end
|
108
|
+
|
109
|
+
def run_a_specific_context
|
110
|
+
load_plugin
|
111
|
+
|
112
|
+
DockerCompose::File.create_all
|
113
|
+
Gitignore.update
|
114
|
+
ExtraMountFiles.create
|
115
|
+
Config.target.create_files
|
116
|
+
|
117
|
+
context = Context.find_by_command_options!(command.context_options)
|
118
|
+
|
119
|
+
puts Rainbow("[ MatrixEval ] ").blue.bright + Rainbow(" #{context.name} ").white.bright.bg(:blue)
|
120
|
+
puts Rainbow("[ MatrixEval ] Run \"#{command.rest_arguments.join(" ")}\"").blue.bright
|
121
|
+
|
122
|
+
docker_compose = DockerCompose.new(context)
|
123
|
+
docker_compose.run(command.rest_arguments)
|
124
|
+
end
|
125
|
+
|
126
|
+
def pull_all_images
|
127
|
+
parallel(Config.main_vector_variants) do |sub_variants|
|
128
|
+
sub_variants.each do |variant|
|
129
|
+
puts "Docker image check/pull #{variant.container.image}"
|
130
|
+
image_exists = system %Q{[ -n "$(docker images -q #{variant.container.image})" ]}
|
131
|
+
next if image_exists
|
132
|
+
|
133
|
+
system "docker pull #{variant.container.image}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def report
|
139
|
+
turn_on_stty_opost
|
140
|
+
|
141
|
+
table = Terminal::Table.new(title: Rainbow("MatrixEval").blue.bright + " Summary", alignment: :center) do |table|
|
142
|
+
|
143
|
+
headers = Config.vectors.map(&:key) + ['result']
|
144
|
+
table.add_row headers.map { |value| { value: value, alignment: :center } }
|
145
|
+
table.add_separator
|
146
|
+
|
147
|
+
@matrixeval_results.each do |context, success|
|
148
|
+
success_cell = [success ? Rainbow('Success').green : Rainbow('Failed').red]
|
149
|
+
row = (context.variants.map(&:key) + success_cell).map do |value|
|
150
|
+
{ value: value, alignment: :center }
|
151
|
+
end
|
152
|
+
|
153
|
+
table.add_row row
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
puts table
|
159
|
+
end
|
160
|
+
|
161
|
+
def parallel(collection)
|
162
|
+
@threads = [] unless @threads.empty?
|
163
|
+
@matrixeval_results = [] unless @matrixeval_results.empty?
|
164
|
+
|
165
|
+
collection.each_slice(per_worker_contexts_count) do |sub_collection|
|
166
|
+
@threads << Thread.new do
|
167
|
+
yield sub_collection
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
@threads.each(&:join)
|
172
|
+
|
173
|
+
@threads.each do |thread|
|
174
|
+
@matrixeval_results += (thread[:matrixeval_results] || [])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
def per_worker_contexts_count
|
180
|
+
[(contexts.count / workers_count), 1].max
|
181
|
+
end
|
182
|
+
|
183
|
+
def contexts
|
184
|
+
@contexts ||= Context.all
|
185
|
+
end
|
186
|
+
|
187
|
+
def workers_count
|
188
|
+
count = if Config.parallel_workers == "number_of_processors"
|
189
|
+
Concurrent.physical_processor_count
|
190
|
+
else
|
191
|
+
Integer(Config.parallel_workers)
|
192
|
+
end
|
193
|
+
|
194
|
+
[count, 1].max
|
195
|
+
end
|
196
|
+
|
197
|
+
def turn_on_stty_opost
|
198
|
+
system("stty opost")
|
199
|
+
end
|
200
|
+
|
201
|
+
def load_plugin
|
202
|
+
require "matrixeval/#{Config.target_name}"
|
203
|
+
rescue LoadError
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Matrixeval
|
2
|
+
class Target
|
3
|
+
|
4
|
+
def version
|
5
|
+
Matrixeval::VERSION
|
6
|
+
end
|
7
|
+
|
8
|
+
def matrixeval_yml_template_path
|
9
|
+
Matrixeval.root.join("lib/matrixeval/templates/matrixeval.yml")
|
10
|
+
end
|
11
|
+
|
12
|
+
def vector_key
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def env(context)
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
def mounts(context)
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def volumes(context)
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
def gitignore_paths
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def support_commands
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
|
36
|
+
def cli_example_lines
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_files
|
41
|
+
# Do nothing
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
version: 0.4
|
2
|
+
project_name: REPLACE_ME
|
3
|
+
parallel_workers: number_of_processors
|
4
|
+
# commands:
|
5
|
+
# - ps
|
6
|
+
# - top
|
7
|
+
# - an_additional_command
|
8
|
+
# mounts:
|
9
|
+
# - /a/path/need/to/mount:/a/path/mount/to
|
10
|
+
matrix:
|
11
|
+
ruby:
|
12
|
+
main: true
|
13
|
+
variants:
|
14
|
+
- key: 2.7
|
15
|
+
container:
|
16
|
+
image: ruby:2.7.1
|
17
|
+
- key: 3.0
|
18
|
+
default: true
|
19
|
+
container:
|
20
|
+
image: ruby:3.0.0
|
21
|
+
- key: 3.1
|
22
|
+
container:
|
23
|
+
image: ruby:3.1.0
|
24
|
+
# - key: jruby-9.3
|
25
|
+
# container:
|
26
|
+
# image: jruby:9.3
|
27
|
+
# env:
|
28
|
+
# PATH: "/opt/jruby/bin:/app/bin:/bundle/bin:$PATH"
|
29
|
+
# mounts:
|
30
|
+
# - /a/path/need/to/mount:/a/path/mount/to
|
31
|
+
|
32
|
+
# rails:
|
33
|
+
# variants:
|
34
|
+
# - key: 6.1
|
35
|
+
# default: true
|
36
|
+
# env:
|
37
|
+
# RAILS_VERSION: "~> 6.1.0"
|
38
|
+
# - key: 7.0
|
39
|
+
# env:
|
40
|
+
# RAILS_VERSION: "~> 7.0.0"
|
41
|
+
# another:
|
42
|
+
# variants:
|
43
|
+
# - key: key1
|
44
|
+
# default: true
|
45
|
+
# env:
|
46
|
+
# ENV_KEY: 1
|
47
|
+
# - key: key2
|
48
|
+
# env:
|
49
|
+
# ENV_KEY: 2
|
50
|
+
|
51
|
+
exclude:
|
52
|
+
# - ruby: 3.0
|
53
|
+
# rails: 4.2
|
54
|
+
# - ruby: jruby-9.3
|
55
|
+
# rails: 7.0
|
56
|
+
|
57
|
+
docker-compose-extend:
|
58
|
+
# services:
|
59
|
+
# postgres:
|
60
|
+
# image: postgres:12.8
|
61
|
+
# volumes:
|
62
|
+
# - postgres12:/var/lib/postgresql/data
|
63
|
+
# environment:
|
64
|
+
# POSTGRES_HOST_AUTH_METHOD: trust
|
65
|
+
|
66
|
+
# redis:
|
67
|
+
# image: redis:6.2-alpine
|
68
|
+
|
69
|
+
# volumes:
|
70
|
+
# postgres12:
|