matrixeval-ruby 0.2.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c1fa003fd849f1a5baf6c9393c5210e030e52a14c044308f6732b602c196027
4
- data.tar.gz: 652ab5ecd63138e426a002aae768c7bd61214785ef8a8eaa698cffa2eddda1ae
3
+ metadata.gz: b38c89479923e7c9b607eff588ef02e9b4af821899b45b3a55a995e5673c9a9f
4
+ data.tar.gz: d2984e75182192bf0c80fced8062a019754c6d1ef042b757074e014e2851c6bd
5
5
  SHA512:
6
- metadata.gz: fc85db98392512e7683fbfa96c54e4f34ff2f5c248ab07ceac57bd18a6781cdfac45325b874c1580c068b5a1f56a8653bc0d943baea6fcd391e3baf521038dd4
7
- data.tar.gz: af503dd911a0522975061dfb37696470adedafc4a6868019480e89cfd51c686bb5aa4f5d848683dfe9b52ba94be0d34cb7fbb7478a262c6fe62f8964f0c5c6a1
6
+ metadata.gz: ec71cc460fe724eb580b95da0af5e42e9324a7f789ab951af5082871cf45a8e5906545a3ce60ec31993da95872d76ae8cdae5372ed58b5ecae603e6c4d42ced3
7
+ data.tar.gz: bf8be870710b0f433319e9be0360e69bdd23625d078661d288ef23b3b5c8cef61535e5baca49eff4013abf88a8769ab6030d7840a77f8f74fda92b452c480d7e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2022-02-27
4
+
5
+ - Refactor this gem as a plugin of matrixeval
6
+
7
+ ## [0.3.1] - 2022-02-21
8
+
9
+ - Add `.matrixeval/docker-compose` to `.gitignore`
10
+
11
+ ## [0.3.0] - 2022-02-21
12
+
13
+ - Support add extra docker compose services and volumes
14
+ - Isolate each job with docker compose project name and network
15
+
3
16
  ## [0.2.2] - 2022-02-11
4
17
 
5
18
  - Auto remove containers
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # matrixeval-ruby
2
2
 
3
- Test your ruby code against multiple versions of dependencies like Ruby, Rails, Env ...
3
+ It's a plugin of [matrixeval](https://github.com/MatrixEval/matrixeval-ruby) for Ruby. Test your ruby code against multiple versions of dependencies like Ruby, Rails, Env ...
4
+
5
+ ![](https://raw.githubusercontent.com/MatrixEval/assets/main/screenshots/summary.png)
6
+
7
+ ## Features
8
+
9
+ - Parallel test your ruby code against multiple versions of dependencies combinations.
10
+ - Test your ruby code against a specific dependencies combination.
11
+ - Choose any docker image you like for each job.
12
+ - Easy to use CLI to speed up your development efficiency
4
13
  ## Installation
5
14
 
6
15
  Add this line to your application's Gemfile:
@@ -22,7 +31,7 @@ Or install it yourself as:
22
31
  Initialize
23
32
 
24
33
  ```bash
25
- matrixeval init
34
+ matrixeval init -t ruby
26
35
  ```
27
36
 
28
37
  Customize `matrixeval.yml` file and run commands like:
@@ -36,6 +45,97 @@ matrixeval bash
36
45
  ```
37
46
  Run `matrixeval --help` for more details
38
47
 
48
+ ![](https://raw.githubusercontent.com/MatrixEval/assets/main/screenshots/help.png)
49
+
50
+ ### Configuration Example
51
+
52
+ Here is the configuration file `matrixeval.yml` which will auto created by `matrixeval init`
53
+
54
+ ```yaml
55
+ version: 0.4
56
+ target: ruby
57
+ project_name: REPLACE_ME
58
+ parallel_workers: number_of_processors
59
+ # commands:
60
+ # - ps
61
+ # - top
62
+ # - an_additional_command
63
+ # mounts:
64
+ # - /a/path/need/to/mount:/a/path/mount/to
65
+ matrix:
66
+ ruby:
67
+ variants:
68
+ - key: 2.7
69
+ container:
70
+ image: ruby:2.7.1
71
+ - key: 3.0
72
+ default: true
73
+ container:
74
+ image: ruby:3.0.0
75
+ - key: 3.1
76
+ container:
77
+ image: ruby:3.1.0
78
+ # - key: jruby-9.3
79
+ # container:
80
+ # image: jruby:9.3
81
+ # env:
82
+ # PATH: "/opt/jruby/bin:/app/bin:/bundle/bin:$PATH"
83
+ # mounts:
84
+ # - /a/path/need/to/mount:/a/path/mount/to
85
+
86
+ # rails:
87
+ # variants:
88
+ # - key: 6.1
89
+ # default: true
90
+ # env:
91
+ # RAILS_VERSION: "~> 6.1.0"
92
+ # - key: 7.0
93
+ # env:
94
+ # RAILS_VERSION: "~> 7.0.0"
95
+ # another:
96
+ # variants:
97
+ # - key: key1
98
+ # default: true
99
+ # env:
100
+ # ENV_KEY: 1
101
+ # - key: key2
102
+ # env:
103
+ # ENV_KEY: 2
104
+
105
+ exclude:
106
+ # - ruby: 3.0
107
+ # rails: 4.2
108
+ # - ruby: jruby-9.3
109
+ # rails: 7.0
110
+
111
+ docker-compose-extend:
112
+ # services:
113
+ # postgres:
114
+ # image: postgres:12.8
115
+ # volumes:
116
+ # - postgres12:/var/lib/postgresql/data
117
+ # environment:
118
+ # POSTGRES_HOST_AUTH_METHOD: trust
119
+
120
+ # redis:
121
+ # image: redis:6.2-alpine
122
+
123
+ # volumes:
124
+ # postgres12:
125
+ ```
126
+
127
+ ### Gemfile configuration example
128
+
129
+ Here is an example from [ruby-trello](https://github.com/jeremytregunna/ruby-trello)
130
+
131
+ ```ruby
132
+ if active_model_version = ENV['ACTIVE_MODEL_VERSION']
133
+ gem 'activemodel', active_model_version
134
+ end
135
+ ```
136
+
137
+ You can also check its corresponding [`matrixeval.yml`](https://github.com/jeremytregunna/ruby-trello/blob/master/matrixeval.yml)
138
+
39
139
  ## Development
40
140
 
41
141
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,87 @@
1
+ module Matrixeval
2
+ module Ruby
3
+ class Target < Matrixeval::Target
4
+
5
+ def version
6
+ Matrixeval::Ruby::VERSION
7
+ end
8
+
9
+ def matrixeval_yml_template_path
10
+ Matrixeval::Ruby.root.join("lib/matrixeval/ruby/templates/matrixeval.yml")
11
+ end
12
+
13
+ def vector_key
14
+ "ruby"
15
+ end
16
+
17
+ def env(context)
18
+ {
19
+ "BUNDLE_PATH" => "/bundle",
20
+ "GEM_HOME" => "/bundle",
21
+ "BUNDLE_APP_CONFIG" => "/bundle",
22
+ "BUNDLE_BIN" => "/bundle/bin",
23
+ "PATH" => "/app/bin:/bundle/bin:$PATH"
24
+ }
25
+ end
26
+
27
+ def mounts(context)
28
+ bundle_volume = bundle_volume(context)
29
+
30
+ [
31
+ "#{bundle_volume}:/bundle",
32
+ "../gemfile_locks/#{context.id}:/app/Gemfile.lock"
33
+ ]
34
+ end
35
+
36
+ def volumes(context)
37
+ bundle_volume = bundle_volume(context)
38
+
39
+ {
40
+ bundle_volume => {
41
+ "name" => bundle_volume
42
+ }
43
+ }
44
+ end
45
+
46
+ def gitignore_paths
47
+ [
48
+ ".matrixeval/gemfile_locks"
49
+ ]
50
+ end
51
+
52
+ def support_commands
53
+ [
54
+ 'ruby', 'rake', 'rails', 'rspec', 'bundle',
55
+ 'bin/rake', 'bin/rails', 'bin/rspec', 'bin/test'
56
+ ]
57
+ end
58
+
59
+ def cli_example_lines
60
+ [
61
+ "",
62
+ "Example:",
63
+ " matrixeval --all bundle install",
64
+ " matrixeval --ruby 3.0 rspec a_spec.rb",
65
+ " matrixeval --ruby 3.1 --active_model 7.0 rake test",
66
+ " matrixeval bash"
67
+ ]
68
+ end
69
+
70
+ def create_files
71
+ gemfile_lock_folder = Matrixeval.working_dir.join(".matrixeval/gemfile_locks")
72
+ FileUtils.mkdir_p gemfile_lock_folder
73
+
74
+ Context.all.each do |context|
75
+ FileUtils.touch gemfile_lock_folder.join(context.id)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def bundle_volume(context)
82
+ docker_image = context.main_variant.container.image
83
+ "bundle_#{docker_image.gsub(/[^A-Za-z0-9]/,'_')}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,14 +1,15 @@
1
- version: 0.2
1
+ version: 0.4
2
2
  target: ruby
3
+ project_name: REPLACE_ME
3
4
  parallel_workers: number_of_processors
4
5
  # commands:
5
6
  # - ps
6
7
  # - top
7
8
  # - an_additional_command
9
+ # mounts:
10
+ # - /a/path/need/to/mount:/a/path/mount/to
8
11
  matrix:
9
12
  ruby:
10
- # mounts:
11
- # - /a/path/need/to/mount:/a/path/mount/to
12
13
  variants:
13
14
  - key: 2.7
14
15
  container:
@@ -23,8 +24,10 @@ matrix:
23
24
  # - key: jruby-9.3
24
25
  # container:
25
26
  # image: jruby:9.3
26
- # env:
27
+ # env:
27
28
  # PATH: "/opt/jruby/bin:/app/bin:/bundle/bin:$PATH"
29
+ # mounts:
30
+ # - /a/path/need/to/mount:/a/path/mount/to
28
31
 
29
32
  # rails:
30
33
  # variants:
@@ -50,3 +53,18 @@ exclude:
50
53
  # rails: 4.2
51
54
  # - ruby: jruby-9.3
52
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:
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Matrixeval
4
2
  module Ruby
5
- VERSION = "0.2.2"
3
+ VERSION = '0.4.0'
6
4
  end
7
5
  end
@@ -1,29 +1,14 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ruby/version"
4
- require 'rainbow'
5
- require 'matrixeval/ruby/docker_compose'
6
- require 'matrixeval/ruby/context'
7
- require 'matrixeval/ruby/gemfile_locks'
8
- require 'matrixeval/ruby/runner'
9
- require 'matrixeval/ruby/gitignore'
1
+ require 'matrixeval'
2
+ require_relative 'ruby/version'
3
+ require_relative 'ruby/target'
10
4
 
11
5
  module Matrixeval
12
6
  module Ruby
13
- class Error < StandardError; end
14
-
15
7
  module_function
16
8
  def root
17
9
  Pathname.new("#{__dir__}/../..")
18
10
  end
19
11
  end
20
-
21
- module_function
22
- def start(argv)
23
- Ruby::Runner.start(argv)
24
- end
25
-
26
- def working_dir
27
- Pathname.new(Dir.getwd)
28
- end
29
12
  end
13
+
14
+ Matrixeval.register_target(:ruby, Matrixeval::Ruby::Target)
metadata CHANGED
@@ -1,63 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matrixeval-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hopper Gee
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-11 00:00:00.000000000 Z
11
+ date: 2022-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rainbow
14
+ name: matrixeval
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.1'
19
+ version: 0.4.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.1'
27
- - !ruby/object:Gem::Dependency
28
- name: concurrent-ruby
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: terminal-table
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
26
+ version: 0.4.2
55
27
  description: MatrixEval-Ruby
56
28
  email:
57
29
  - hopper.gee@hey.com
58
- executables:
59
- - matrixeval
60
- - meval
30
+ executables: []
61
31
  extensions: []
62
32
  extra_rdoc_files: []
63
33
  files:
@@ -70,24 +40,9 @@ files:
70
40
  - bin/console
71
41
  - bin/setup
72
42
  - bin/test
73
- - exe/matrixeval
74
- - exe/meval
75
43
  - lib/matrixeval/ruby.rb
76
- - lib/matrixeval/ruby/command_line.rb
77
- - lib/matrixeval/ruby/command_line/parse_context_arguments.rb
78
- - lib/matrixeval/ruby/config.rb
79
- - lib/matrixeval/ruby/config/yaml.rb
80
- - lib/matrixeval/ruby/container.rb
81
- - lib/matrixeval/ruby/context.rb
82
- - lib/matrixeval/ruby/context/find_by_command_options.rb
83
- - lib/matrixeval/ruby/docker_compose.rb
84
- - lib/matrixeval/ruby/docker_compose/yaml.rb
85
- - lib/matrixeval/ruby/gemfile_locks.rb
86
- - lib/matrixeval/ruby/gitignore.rb
87
- - lib/matrixeval/ruby/runner.rb
44
+ - lib/matrixeval/ruby/target.rb
88
45
  - lib/matrixeval/ruby/templates/matrixeval.yml
89
- - lib/matrixeval/ruby/variant.rb
90
- - lib/matrixeval/ruby/vector.rb
91
46
  - lib/matrixeval/ruby/version.rb
92
47
  homepage: https://github.com/MatrixEval/matrixeval-ruby
93
48
  licenses:
data/exe/matrixeval DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'matrixeval/ruby'
4
-
5
- Matrixeval.start(ARGV)
data/exe/meval DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'matrixeval/ruby'
4
-
5
- Matrixeval.start(ARGV)
@@ -1,85 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class CommandLine
4
- class ParseContextArguments
5
- class << self
6
- def call(context_arguments)
7
- new(context_arguments).call
8
- end
9
- end
10
-
11
- attr_reader :context_arguments, :options
12
-
13
- def initialize(context_arguments)
14
- @context_arguments = context_arguments
15
- @options = {}
16
- end
17
-
18
- def call
19
- parse!
20
- options
21
- end
22
-
23
- private
24
-
25
- def parse!
26
- OptionParser.new do |opts|
27
- opts.version = Matrixeval::Ruby::VERSION
28
- opts.program_name = ""
29
- opts.banner = <<~USAGE
30
- Usage:
31
- matrixeval(meval) [OPTIONS] COMMAND
32
- USAGE
33
-
34
- opts.separator ""
35
- opts.separator "Options:"
36
-
37
- opts.on "-a", "--all", "# Run the COMMAND against all matrix combinations"
38
-
39
- Config.vectors.each do |vector|
40
- # short = "-#{vector.short_key}"
41
- long = "--#{vector.key} [VERSION]"
42
- desc = [
43
- "# Run the COMMAND against a specific #{vector.key} version",
44
- "# Options: #{vector.variants.map(&:key).join("/")}",
45
- "# Default: #{vector.default_variant.key}",
46
- "# Customizable"
47
- ]
48
- opts.separator ""
49
- opts.on(long, *desc)
50
- end
51
-
52
- opts.separator ""
53
- opts.separator "Commands: #{Config.commands.join("/")} (Customizable)"
54
-
55
- opts.separator ""
56
- opts.separator "MatrixEval Options:"
57
-
58
- opts.on("-h", "--help", "# Show help") do
59
- puts opts.help
60
- exit
61
- end
62
-
63
- opts.on("-v", "--version", "# Show version") do
64
- puts opts.version
65
- exit
66
- end
67
-
68
- opts.separator ""
69
- opts.separator "Customizations:"
70
- opts.separator " You can customize all options in matrixeval.yml"
71
-
72
- opts.separator ""
73
- opts.separator "Example:"
74
- opts.separator " matrixeval --all bundle install"
75
- opts.separator " matrixeval --ruby 3.0 rspec a_spec.rb"
76
- opts.separator " matrixeval --ruby 3.1 --active_model 7.0 rake test"
77
- opts.separator " matrixeval bash"
78
-
79
- end.parse!(context_arguments, into: options)
80
- end
81
-
82
- end
83
- end
84
- end
85
- end
@@ -1,53 +0,0 @@
1
- require_relative "./command_line/parse_context_arguments"
2
-
3
- module Matrixeval
4
- module Ruby
5
- COMMANDS = ['rake', 'rspec', 'bundle', 'bash']
6
-
7
- class CommandLine
8
-
9
- attr_reader :argv
10
-
11
- def initialize(argv)
12
- @argv = argv
13
- end
14
-
15
- def valid?
16
- init? ||
17
- !context_options.empty? ||
18
- !seperator_index.nil?
19
- end
20
-
21
- def init?
22
- @argv[0] == 'init'
23
- end
24
-
25
- def all?
26
- context_options[:all]
27
- end
28
-
29
- def context_options
30
- @context_options ||= ParseContextArguments.call(context_arguments)
31
- end
32
-
33
- def context_arguments
34
- arguments = @argv[0...seperator_index]
35
- arguments << "-h" if @argv.empty?
36
- arguments
37
- end
38
-
39
- def rest_arguments
40
- @argv[seperator_index..-1]
41
- end
42
-
43
- private
44
-
45
- def seperator_index
46
- @argv.index do |argument|
47
- Config.commands.include?(argument)
48
- end
49
- end
50
-
51
- end
52
- end
53
- end
@@ -1,40 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class Config
4
- class YAML
5
-
6
- class MissingError < StandardError; end
7
-
8
- class << self
9
-
10
- def create
11
- return if File.exist?(path)
12
-
13
- FileUtils.cp(template_path, path)
14
- end
15
-
16
- def template_path
17
- Matrixeval::Ruby.root.join(
18
- "lib/matrixeval/ruby/templates/matrixeval.yml"
19
- )
20
- end
21
-
22
- def path
23
- Matrixeval.working_dir.join("matrixeval.yml")
24
- end
25
-
26
- def [](key)
27
- yaml[key]
28
- end
29
-
30
- def yaml
31
- raise MissingError unless File.exist?(path)
32
-
33
- ::YAML.load File.read(path)
34
- end
35
-
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,60 +0,0 @@
1
- require 'yaml'
2
- require_relative "./vector"
3
- require_relative "./config/yaml"
4
-
5
- module Matrixeval
6
- module Ruby
7
- class Config
8
- class << self
9
-
10
- def version
11
- YAML["version"]
12
- end
13
-
14
- def target
15
- YAML["target"]
16
- end
17
-
18
- def vectors
19
- @vectors = YAML["matrix"].map do |key, vector_config|
20
- Vector.new(key, vector_config)
21
- end
22
- end
23
-
24
- def main_vector
25
- vectors.find(&:main?)
26
- end
27
-
28
- def rest_vectors
29
- vectors.reject(&:main?)
30
- end
31
-
32
- def variant_combinations
33
- main_vector_variants.product(*rest_vector_variants_matrix)
34
- end
35
-
36
- def main_vector_variants
37
- main_vector.variants
38
- end
39
-
40
- def rest_vector_variants_matrix
41
- rest_vectors.map(&:variants)
42
- end
43
-
44
- def exclusions
45
- YAML["exclude"] || []
46
- end
47
-
48
- def parallel_workers
49
- YAML["parallel_workers"] || "number_of_processors"
50
- end
51
-
52
- def commands
53
- cmds = YAML["commands"] || []
54
- COMMANDS + cmds
55
- end
56
-
57
- end
58
- end
59
- end
60
- end
@@ -1,15 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class Container
4
-
5
- attr_reader :image, :env
6
-
7
- def initialize(options)
8
- options ||= {}
9
- @image = options["image"]
10
- @env = options["env"] || {}
11
- end
12
-
13
- end
14
- end
15
- end
@@ -1,67 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class Context
4
- class FindByCommandOptions
5
- class << self
6
- def call(options)
7
- new(options).call
8
- end
9
- end
10
-
11
- attr_reader :options
12
-
13
- def initialize(options)
14
- @options = options
15
- end
16
-
17
- def call
18
- context = Context.all.find do |context|
19
- context.main_variant == main_variant &&
20
- context.rest_variants == rest_variants
21
- end
22
-
23
- raise Error.new("Can't find a corresponding matrix") if context.nil?
24
-
25
- context
26
- end
27
-
28
- private
29
-
30
- def main_variant
31
- dig_variant Config.main_vector
32
- end
33
-
34
- def rest_variants
35
- Config.rest_vectors.map do |vector|
36
- dig_variant vector
37
- end.sort do |v1, v2|
38
- v1.id <=> v2.id
39
- end
40
- end
41
-
42
- def dig_variant(vector)
43
- if option_key?(vector.key)
44
- find_variant(vector)
45
- else
46
- vector.default_variant
47
- end
48
- end
49
-
50
- def find_variant(vector)
51
- vector.variants.find do |variant|
52
- option(vector.key) == variant.key
53
- end
54
- end
55
-
56
- def option(key)
57
- options[key.to_sym] || options[key.to_s]
58
- end
59
-
60
- def option_key?(key)
61
- options.key?(key.to_sym) || options.key?(key.to_s)
62
- end
63
-
64
- end
65
- end
66
- end
67
- end
@@ -1,77 +0,0 @@
1
- require_relative "./context/find_by_command_options"
2
-
3
- module Matrixeval
4
- module Ruby
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 gemfile_lock_path
55
- Matrixeval.working_dir.join(".matrixeval/Gemfile.lock.#{id}")
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
- end
76
- end
77
- end
@@ -1,77 +0,0 @@
1
- require "erb"
2
-
3
- module Matrixeval
4
- module Ruby
5
- class DockerCompose
6
- class YAML
7
- class << self
8
-
9
- def create
10
- FileUtils.mkdir_p dot_matrixeval_folder
11
-
12
- File.open(path, 'w+') do |file|
13
- file.puts build_content
14
- end
15
- end
16
-
17
- private
18
-
19
- def build_content
20
- {
21
- "version" => "3",
22
- "services" => services_json,
23
- "volumes" => volumes_json
24
- }.to_yaml.sub(/---\n/, "")
25
- end
26
-
27
- def services_json
28
- services = {}
29
-
30
- Config.main_vector_variants.map do |variant|
31
- services[variant.docker_compose_service_name] = {
32
- "image" => variant.container.image,
33
- "volumes" => mounts(variant),
34
- "environment" => {
35
- "BUNDLE_PATH" => "/bundle",
36
- "GEM_HOME" => "/bundle",
37
- "BUNDLE_APP_CONFIG" => "/bundle",
38
- "BUNDLE_BIN" => "/bundle/bin",
39
- "PATH" => "/app/bin:/bundle/bin:$PATH"
40
- }.merge(variant.container.env),
41
- "working_dir" => "/app"
42
- }
43
- end
44
-
45
- services
46
- end
47
-
48
- def volumes_json
49
- bundle_volumes.map do |volume|
50
- [volume, {"name" => volume}]
51
- end.to_h
52
- end
53
-
54
- def bundle_volumes
55
- Config.main_vector_variants.map(&:bundle_volume_name)
56
- end
57
-
58
- def mounts(variant)
59
- [
60
- "..:/app:cached",
61
- "#{variant.bundle_volume_name}:/bundle",
62
- ] + Config.main_vector.mounts
63
- end
64
-
65
- def path
66
- dot_matrixeval_folder.join("docker-compose.yml")
67
- end
68
-
69
- def dot_matrixeval_folder
70
- Matrixeval.working_dir.join(".matrixeval")
71
- end
72
-
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,53 +0,0 @@
1
-
2
- require_relative "./docker_compose/yaml"
3
-
4
- module Matrixeval
5
- module Ruby
6
- class DockerCompose
7
-
8
- class << self
9
- def clean_containers
10
- system("docker compose -f .matrixeval/docker-compose.yml rm --all -f >> /dev/null 2>&1")
11
- end
12
- end
13
-
14
- attr_reader :context
15
-
16
- def initialize(context)
17
- @context = context
18
- end
19
-
20
- def run(arguments)
21
- forward_arguments = arguments.join(" ")
22
-
23
- system(
24
- <<~DOCKER_COMPOSE_COMMAND
25
- docker compose -f .matrixeval/docker-compose.yml \
26
- run --rm \
27
- #{env} \
28
- #{gemfile_mount} \
29
- #{docker_compose_service_name} \
30
- #{forward_arguments}
31
- DOCKER_COMPOSE_COMMAND
32
- )
33
- end
34
-
35
- private
36
-
37
- def env
38
- context.env.map do |k, v|
39
- "-e #{k}='#{v}'"
40
- end.join(" ")
41
- end
42
-
43
- def gemfile_mount
44
- "-v ./.matrixeval/Gemfile.lock.#{context.id}:/app/Gemfile.lock"
45
- end
46
-
47
- def docker_compose_service_name
48
- context.docker_compose_service_name
49
- end
50
-
51
- end
52
- end
53
- end
@@ -1,23 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class GemfileLocks
4
- class << self
5
-
6
- def create
7
- FileUtils.mkdir_p dot_matrixeval_folder
8
-
9
- Context.all.each do |context|
10
- FileUtils.touch context.gemfile_lock_path
11
- end
12
- end
13
-
14
- private
15
-
16
- def dot_matrixeval_folder
17
- Matrixeval.working_dir.join(".matrixeval")
18
- end
19
-
20
- end
21
- end
22
- end
23
- end
@@ -1,54 +0,0 @@
1
- module Matrixeval
2
- module Ruby
3
- class Gitignore
4
- class << self
5
-
6
- def update
7
- add_docker_compose
8
- add_gemfile_locks
9
- end
10
-
11
- private
12
-
13
- def add_docker_compose
14
- return if docker_compose_included?
15
-
16
- File.open(gitignore_path, 'a+') do |file|
17
- file.puts docker_compose_yaml
18
- end
19
- end
20
-
21
- def add_gemfile_locks
22
- return if gemfile_locks_included?
23
-
24
- File.open(gitignore_path, 'a+') do |file|
25
- file.puts gemfile_locks
26
- end
27
- end
28
-
29
- def docker_compose_included?
30
- File.exist?(gitignore_path) &&
31
- File.read(gitignore_path).include?(docker_compose_yaml)
32
- end
33
-
34
- def gemfile_locks_included?
35
- File.exist?(gitignore_path) &&
36
- File.read(gitignore_path).include?(gemfile_locks)
37
- end
38
-
39
- def docker_compose_yaml
40
- ".matrixeval/docker-compose.yml"
41
- end
42
-
43
- def gemfile_locks
44
- ".matrixeval/Gemfile.lock.*"
45
- end
46
-
47
- def gitignore_path
48
- Matrixeval.working_dir.join(".gitignore")
49
- end
50
-
51
- end
52
- end
53
- end
54
- end
@@ -1,204 +0,0 @@
1
- require 'optparse'
2
- require 'pathname'
3
- require 'fileutils'
4
- require 'matrixeval/ruby/config'
5
- require 'matrixeval/ruby/command_line'
6
- require "concurrent/utility/processor_counter"
7
- require 'terminal-table'
8
-
9
- module Matrixeval
10
- module Ruby
11
- class Runner
12
- class << self
13
- def start(argv)
14
- new(argv).start
15
- end
16
- end
17
-
18
- attr_reader :argv, :command
19
-
20
- def initialize(argv)
21
- @argv = argv
22
- @command = CommandLine.new(argv)
23
- @threads ||= []
24
- @matrixeval_results ||= []
25
- end
26
-
27
- def start
28
- validates
29
-
30
- if command.init?
31
- init
32
- elsif command.all?
33
- run_all_contexts
34
- else
35
- run_a_specific_context
36
- end
37
- rescue OptionParser::InvalidOption => e
38
- puts <<~ERROR
39
- #{e.message}
40
- See 'matrixeval --help'
41
- ERROR
42
- exit
43
- rescue Config::YAML::MissingError
44
- puts "Please run 'matrixeval init' first to generate matrixeval.yml"
45
- exit
46
- ensure
47
- turn_on_stty_opost
48
- DockerCompose.clean_containers
49
- end
50
-
51
- private
52
-
53
- def validates
54
- return if command.valid?
55
-
56
- puts <<~ERROR
57
- matrixeval: '#{argv.join(' ')}' is not a MatrixEval command.
58
- See 'matrixeval --help'
59
- ERROR
60
- exit
61
- end
62
-
63
- def init
64
- Config::YAML.create
65
- Gitignore.update
66
- end
67
-
68
- def run_all_contexts
69
- Config::YAML.create
70
- DockerCompose::YAML.create
71
- GemfileLocks.create
72
- Gitignore.update
73
-
74
- pull_all_images
75
-
76
- if workers_count == 1
77
- run_all_contexts_sequentially
78
- else
79
- run_all_contexts_in_parallel
80
- end
81
- end
82
-
83
- def run_all_contexts_sequentially
84
- Context.all.each do |context|
85
- puts Rainbow("[ MatrixEval ] ").blue.bright + Rainbow(" #{context.name} ").white.bright.bg(:blue)
86
- puts Rainbow("[ MatrixEval ] Run \"#{command.rest_arguments.join(" ")}\"").blue.bright
87
-
88
- docker_compose = DockerCompose.new(context)
89
- success = docker_compose.run(command.rest_arguments)
90
-
91
- @matrixeval_results << [context, !!success]
92
- end
93
-
94
- report
95
- end
96
-
97
- def run_all_contexts_in_parallel
98
- parallel(contexts) do |sub_contexts|
99
- Thread.current[:matrixeval_results] = []
100
-
101
- sub_contexts.each do |context|
102
- docker_compose = DockerCompose.new(context)
103
- success = docker_compose.run(command.rest_arguments)
104
-
105
- Thread.current[:matrixeval_results] << [context, !!success]
106
- end
107
- end
108
-
109
- report
110
- end
111
-
112
- def run_a_specific_context
113
- Config::YAML.create
114
- DockerCompose::YAML.create
115
- GemfileLocks.create
116
- Gitignore.update
117
-
118
- context = Context.find_by_command_options!(command.context_options)
119
-
120
- puts Rainbow("[ MatrixEval ] ").blue.bright + Rainbow(" #{context.name} ").white.bright.bg(:blue)
121
- puts Rainbow("[ MatrixEval ] Run \"#{command.rest_arguments.join(" ")}\"").blue.bright
122
-
123
- docker_compose = DockerCompose.new(context)
124
- docker_compose.run(command.rest_arguments)
125
- end
126
-
127
- def pull_all_images
128
- parallel(Config.main_vector_variants) do |sub_variants|
129
- sub_variants.each do |variant|
130
- puts "Docker image check/pull #{variant.container.image}"
131
- image_exists = system %Q{[ -n "$(docker images -q #{variant.container.image})" ]}
132
- next if image_exists
133
-
134
- system "docker pull #{variant.container.image}"
135
- end
136
- end
137
- end
138
-
139
- def report
140
- turn_on_stty_opost
141
-
142
- table = Terminal::Table.new(title: Rainbow("MatrixEval").blue.bright + " Summary", alignment: :center) do |table|
143
-
144
- headers = Config.vectors.map(&:key) + ['result']
145
- table.add_row headers.map { |value| { value: value, alignment: :center } }
146
- table.add_separator
147
-
148
- @matrixeval_results.each do |context, success|
149
- success_cell = [success ? Rainbow('Success').green : Rainbow('Failed').red]
150
- row = (context.variants.map(&:key) + success_cell).map do |value|
151
- { value: value, alignment: :center }
152
- end
153
-
154
- table.add_row row
155
- end
156
-
157
- end
158
-
159
- puts table
160
- end
161
-
162
- def parallel(collection)
163
- @threads = [] unless @threads.empty?
164
- @matrixeval_results = [] unless @matrixeval_results.empty?
165
-
166
- collection.each_slice(per_worker_contexts_count) do |sub_collection|
167
- @threads << Thread.new do
168
- yield sub_collection
169
- end
170
- end
171
-
172
- @threads.each(&:join)
173
-
174
- @threads.each do |thread|
175
- @matrixeval_results += (thread[:matrixeval_results] || [])
176
- end
177
- end
178
-
179
-
180
- def per_worker_contexts_count
181
- [(contexts.count / workers_count), 1].max
182
- end
183
-
184
- def contexts
185
- @contexts ||= Context.all
186
- end
187
-
188
- def workers_count
189
- count = if Config.parallel_workers == "number_of_processors"
190
- Concurrent.physical_processor_count
191
- else
192
- Integer(Config.parallel_workers)
193
- end
194
-
195
- [count, 1].max
196
- end
197
-
198
- def turn_on_stty_opost
199
- system("stty opost")
200
- end
201
-
202
- end
203
- end
204
- end
@@ -1,58 +0,0 @@
1
- require_relative "./container"
2
-
3
- module Matrixeval
4
- module Ruby
5
- class Variant
6
- class << self
7
- def default(key, vector)
8
- self.new({"key" => key}, vector)
9
- end
10
- end
11
-
12
- attr_reader :key, :env, :vector, :default, :container
13
-
14
- def initialize(config = {}, vector)
15
- raise Error.new("Variant#key is missing") if config["key"].nil?
16
-
17
- @vector = vector
18
- @key = config["key"].to_s
19
- @container = Container.new(config["container"])
20
- @env = config["env"] || {}
21
- @default = config["default"] || false
22
- end
23
-
24
- def name
25
- "#{vector.key}: #{key}"
26
- end
27
-
28
- def bundle_volume_name
29
- "bundle_#{container.image.gsub(/[^A-Za-z0-9]/,'_')}"
30
- end
31
-
32
- def id
33
- "#{vector.id}_#{key.to_s.gsub(/[^A-Za-z0-9]/,'_')}"
34
- end
35
-
36
- def docker_compose_service_name
37
- id
38
- end
39
-
40
- def pathname
41
- id
42
- end
43
-
44
- def default?
45
- default
46
- end
47
-
48
- def match_command_options?(options)
49
- options[vector.key] == key.to_s
50
- end
51
-
52
- def ==(variant)
53
- vector.key == variant.vector.key &&
54
- key == variant.key
55
- end
56
- end
57
- end
58
- end
@@ -1,40 +0,0 @@
1
- require_relative "./variant"
2
-
3
- module Matrixeval
4
- module Ruby
5
- class Vector
6
- attr_reader :key, :variants, :mounts
7
-
8
- def initialize(key, config)
9
- @key = key.to_s
10
- @mounts = config["mounts"] || []
11
- @variants = (config["variants"] || []).map do |variant_config|
12
- config = if variant_config.is_a?(Hash)
13
- variant_config
14
- else
15
- { "key" => variant_config.to_s }
16
- end
17
-
18
- Variant.new(config, self)
19
- end
20
- end
21
-
22
- def main?
23
- key == "ruby"
24
- end
25
-
26
- def id
27
- "#{key.to_s.gsub(/[^A-Za-z0-9]/,'_')}"
28
- end
29
-
30
- def default_variant
31
- variant = variants.find(&:default?)
32
- if variant.nil?
33
- raise Error.new("Please set a default variant for matrix #{key}")
34
- end
35
-
36
- variant
37
- end
38
- end
39
- end
40
- end