gemika 0.3.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +114 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +91 -0
- data/{gemfiles/Gemfile.3.2.mysql2 → Gemfile.3.2.mysql2} +4 -3
- data/{gemfiles/Gemfile.3.2.mysql2.lock → Gemfile.3.2.mysql2.lock} +19 -5
- data/{gemfiles/Gemfile.4.2.mysql2 → Gemfile.4.2.mysql2} +3 -2
- data/{gemfiles/Gemfile.4.2.mysql2.lock → Gemfile.4.2.mysql2.lock} +11 -5
- data/{gemfiles/Gemfile.4.2.pg → Gemfile.4.2.pg} +2 -1
- data/{gemfiles/Gemfile.4.2.pg.lock → Gemfile.4.2.pg.lock} +9 -3
- data/{gemfiles/Gemfile.5.0.mysql2 → Gemfile.5.2.mysql2} +4 -3
- data/{gemfiles/Gemfile.5.0.mysql2.lock → Gemfile.5.2.mysql2.lock} +28 -21
- data/{gemfiles/Gemfile.5.0.pg → Gemfile.5.2.pg} +3 -2
- data/{gemfiles/Gemfile.5.0.pg.lock → Gemfile.5.2.pg.lock} +26 -19
- data/{gemfiles/Gemfile.5.0.sqlite3 → Gemfile.5.2.sqlite3} +3 -2
- data/{gemfiles/Gemfile.5.0.sqlite3.lock → Gemfile.5.2.sqlite3.lock} +20 -14
- data/Gemfile.6.1.pg +17 -0
- data/Gemfile.6.1.pg.lock +66 -0
- data/README.md +117 -49
- data/gemika.gemspec +2 -2
- data/lib/gemika/database.rb +9 -3
- data/lib/gemika/env.rb +15 -1
- data/lib/gemika/errors.rb +2 -0
- data/lib/gemika/github_actions_generator.rb +150 -0
- data/lib/gemika/matrix.rb +38 -33
- data/lib/gemika/matrix/github_actions_config.rb +61 -0
- data/lib/gemika/matrix/travis_config.rb +42 -0
- data/lib/gemika/tasks.rb +1 -0
- data/lib/gemika/tasks/gemika.rb +14 -0
- data/lib/gemika/tasks/matrix.rb +4 -4
- data/lib/gemika/version.rb +1 -1
- data/spec/fixtures/github_actions_yml/Gemfile_without_gemika +1 -0
- data/spec/fixtures/github_actions_yml/excludes.yml +13 -0
- data/spec/fixtures/github_actions_yml/gemfile_without_gemika.yml +8 -0
- data/spec/fixtures/github_actions_yml/includes.yml +20 -0
- data/spec/fixtures/github_actions_yml/invalid.yml +8 -0
- data/spec/fixtures/github_actions_yml/missing_gemfile.yml +8 -0
- data/spec/fixtures/github_actions_yml/multiple_jobs.yml +16 -0
- data/spec/fixtures/github_actions_yml/two_by_two.yml +10 -0
- data/spec/fixtures/migrate/expected_github_actions.yml +129 -0
- data/spec/fixtures/migrate/travis.yml +66 -0
- data/spec/fixtures/travis_yml/includes.yml +12 -0
- data/spec/gemika/matrix_spec.rb +116 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/database.github.yml +13 -0
- metadata +35 -21
- data/.travis.yml +0 -62
- data/Gemfile.lock +0 -63
- data/gemfiles/Gemfile.2.3.mysql2 +0 -17
- data/gemfiles/Gemfile.2.3.mysql2.lock +0 -34
- data/spec/support/database.travis.yml +0 -9
data/gemika.gemspec
CHANGED
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.description = s.summary
|
12
12
|
s.license = 'MIT'
|
13
13
|
|
14
|
-
s.files = `git ls-files`.split("\n").reject { |path| File.lstat(path).symlink? }
|
15
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").reject { |path| File.lstat(path).symlink? }
|
14
|
+
s.files = `git ls-files`.split("\n").reject { |path| !File.exists?(path) || File.lstat(path).symlink? }
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").reject { |path| !File.exists?(path) || File.lstat(path).symlink? }
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
end
|
data/lib/gemika/database.rb
CHANGED
@@ -14,7 +14,13 @@ module Gemika
|
|
14
14
|
|
15
15
|
def initialize(options = {})
|
16
16
|
yaml_config_folder = options.fetch(:config_folder, 'spec/support')
|
17
|
-
yaml_config_filename = Env.travis?
|
17
|
+
yaml_config_filename = if Env.travis?
|
18
|
+
'database.travis.yml'
|
19
|
+
elsif Env.github?
|
20
|
+
'database.github.yml'
|
21
|
+
else
|
22
|
+
'database.yml'
|
23
|
+
end
|
18
24
|
yaml_config_path = File.join(yaml_config_folder, yaml_config_filename)
|
19
25
|
if File.exists?(yaml_config_path)
|
20
26
|
@yaml_config = YAML.load_file(yaml_config_path)
|
@@ -30,7 +36,7 @@ module Gemika
|
|
30
36
|
#
|
31
37
|
def connect
|
32
38
|
unless @connected
|
33
|
-
ActiveRecord::Base.establish_connection(adapter_config)
|
39
|
+
ActiveRecord::Base.establish_connection(**adapter_config)
|
34
40
|
@connected = true
|
35
41
|
end
|
36
42
|
end
|
@@ -102,7 +108,7 @@ module Gemika
|
|
102
108
|
else
|
103
109
|
raise UnknownAdapter, "Unknown database type. Either 'pg', 'mysql2', or 'sqlite3' gem should be in your current bundle."
|
104
110
|
end
|
105
|
-
default_config.merge(user_config)
|
111
|
+
default_config.merge(user_config).symbolize_keys
|
106
112
|
end
|
107
113
|
|
108
114
|
private
|
data/lib/gemika/env.rb
CHANGED
@@ -35,7 +35,15 @@ module Gemika
|
|
35
35
|
# process, regardless of what's in ENV temporarily
|
36
36
|
@gemfile_changed = true
|
37
37
|
@process_gemfile = ENV['BUNDLE_GEMFILE']
|
38
|
-
|
38
|
+
|
39
|
+
# .with_clean_env is deprecated since Bundler ~> 2.
|
40
|
+
bundler_method = if Gemika::Env.gem?('bundler', '< 2')
|
41
|
+
:with_clean_env
|
42
|
+
else
|
43
|
+
:with_unbundled_env
|
44
|
+
end
|
45
|
+
|
46
|
+
Bundler.send(bundler_method) do
|
39
47
|
ENV['BUNDLE_GEMFILE'] = path
|
40
48
|
block.call(*args)
|
41
49
|
end
|
@@ -87,6 +95,12 @@ module Gemika
|
|
87
95
|
!!ENV['TRAVIS']
|
88
96
|
end
|
89
97
|
|
98
|
+
##
|
99
|
+
# Return whether this process is running within a Github Actions build.
|
100
|
+
def github?
|
101
|
+
ENV.key?('GITHUB_WORKFLOW')
|
102
|
+
end
|
103
|
+
|
90
104
|
##
|
91
105
|
# Creates an hash that enumerates entries in order of insertion.
|
92
106
|
#
|
data/lib/gemika/errors.rb
CHANGED
@@ -0,0 +1,150 @@
|
|
1
|
+
module Gemika
|
2
|
+
class GithubActionsGenerator
|
3
|
+
TYPES = {
|
4
|
+
test_sqlite: {
|
5
|
+
gemfile_filter: /\.sqlite/,
|
6
|
+
},
|
7
|
+
test_pg: {
|
8
|
+
gemfile_filter: /\.pg/,
|
9
|
+
database_setup: [
|
10
|
+
'sudo apt-get install -y postgresql-client',
|
11
|
+
"PGPASSWORD=postgres psql -c 'create database test;' -U postgres -p 5432 -h localhost",
|
12
|
+
],
|
13
|
+
services: {
|
14
|
+
'postgres' => {
|
15
|
+
'image' => 'postgres',
|
16
|
+
'env' => {
|
17
|
+
'POSTGRES_PASSWORD' => 'postgres'
|
18
|
+
},
|
19
|
+
'options' => '--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5',
|
20
|
+
'ports' => ['5432:5432'],
|
21
|
+
},
|
22
|
+
},
|
23
|
+
},
|
24
|
+
test_mysql: {
|
25
|
+
gemfile_filter: /\.mysql/,
|
26
|
+
database_setup: [
|
27
|
+
'sudo apt-get install -y mysql-client libmariadbclient-dev',
|
28
|
+
"mysql -e 'create database IF NOT EXISTS test;' -u root --password=password -P 3306 -h 127.0.0.1",
|
29
|
+
],
|
30
|
+
services: {
|
31
|
+
'mysql' => {
|
32
|
+
'image' => 'mysql:5.6',
|
33
|
+
'env' => {
|
34
|
+
'MYSQL_ROOT_PASSWORD' => 'password',
|
35
|
+
},
|
36
|
+
'options' => '--health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5',
|
37
|
+
'ports' => ['3306:3306'],
|
38
|
+
},
|
39
|
+
},
|
40
|
+
},
|
41
|
+
test: {
|
42
|
+
gemfile_filter: //,
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
def initialize(bundler_version:)
|
47
|
+
@bundler_version = bundler_version
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate(rows)
|
51
|
+
rows_by_type = split_rows_by_gemfile(rows)
|
52
|
+
jobs = {}
|
53
|
+
rows_by_type.each do |type, type_rows|
|
54
|
+
jobs[type.to_s] = job_by_type(type, type_rows)
|
55
|
+
end
|
56
|
+
full_config(jobs)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def split_rows_by_gemfile(rows)
|
62
|
+
rows.group_by do |row|
|
63
|
+
TYPES.detect do |type, type_definition|
|
64
|
+
row.gemfile =~ type_definition[:gemfile_filter]
|
65
|
+
end.first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def job_by_type(type, rows)
|
70
|
+
matrix = full_matrix(rows) || include_matrix(rows)
|
71
|
+
type_definition = TYPES[type]
|
72
|
+
|
73
|
+
steps = [{
|
74
|
+
'uses' => 'actions/checkout@v2',
|
75
|
+
}, {
|
76
|
+
'name' => 'Install ruby',
|
77
|
+
'uses' => 'ruby/setup-ruby@v1',
|
78
|
+
'with' => {'ruby-version' => '${{ matrix.ruby }}'},
|
79
|
+
}]
|
80
|
+
|
81
|
+
if (database_setup = type_definition[:database_setup])
|
82
|
+
steps << {
|
83
|
+
'name' => 'Setup database',
|
84
|
+
'run' => database_setup.join("\n") + "\n",
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
steps += [{
|
89
|
+
'name' => 'Bundle',
|
90
|
+
'run' => "gem install bundler:#{@bundler_version}\nbundle install --no-deployment\n",
|
91
|
+
}, {
|
92
|
+
'name' => 'Run tests',
|
93
|
+
'run' => 'bundle exec rspec',
|
94
|
+
}]
|
95
|
+
|
96
|
+
job = {}
|
97
|
+
job['runs-on'] = 'ubuntu-20.04'
|
98
|
+
if (services = type_definition[:services])
|
99
|
+
job['services'] = services
|
100
|
+
end
|
101
|
+
job['strategy'] = {
|
102
|
+
'fail-fast' => false,
|
103
|
+
'matrix' => matrix,
|
104
|
+
}
|
105
|
+
job['env'] = {
|
106
|
+
'BUNDLE_GEMFILE' => '${{ matrix.gemfile }}',
|
107
|
+
}
|
108
|
+
job['steps'] = steps
|
109
|
+
|
110
|
+
job
|
111
|
+
end
|
112
|
+
|
113
|
+
def full_matrix(rows)
|
114
|
+
rubies = rows.map(&:ruby)
|
115
|
+
gemfiles = rows.map(&:gemfile)
|
116
|
+
if rubies.size * gemfiles.size == rows.size
|
117
|
+
{
|
118
|
+
'ruby' => rubies,
|
119
|
+
'gemfile' => gemfiles,
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def include_matrix(rows)
|
125
|
+
{
|
126
|
+
'include' => rows.map do |row|
|
127
|
+
{
|
128
|
+
'ruby' => row.ruby,
|
129
|
+
'gemfile' => row.gemfile,
|
130
|
+
}
|
131
|
+
end,
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def full_config(jobs)
|
136
|
+
{
|
137
|
+
'name' => 'Tests',
|
138
|
+
'on' => {
|
139
|
+
'push' => {
|
140
|
+
'branches' => ['master'],
|
141
|
+
},
|
142
|
+
'pull_request' => {
|
143
|
+
'branches' => ['master'],
|
144
|
+
},
|
145
|
+
},
|
146
|
+
'jobs' => jobs,
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/gemika/matrix.rb
CHANGED
@@ -1,43 +1,12 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'gemika/errors'
|
3
3
|
require 'gemika/env'
|
4
|
+
require 'gemika/matrix/travis_config'
|
5
|
+
require 'gemika/matrix/github_actions_config'
|
4
6
|
|
5
7
|
module Gemika
|
6
8
|
class Matrix
|
7
9
|
|
8
|
-
##
|
9
|
-
# Load `.travis.yml` files.
|
10
|
-
#
|
11
|
-
# @!visibility private
|
12
|
-
#
|
13
|
-
class TravisConfig
|
14
|
-
class << self
|
15
|
-
|
16
|
-
def load_rows(options)
|
17
|
-
path = options.fetch(:path, '.travis.yml')
|
18
|
-
travis_yml = YAML.load_file(path)
|
19
|
-
rubies = travis_yml.fetch('rvm')
|
20
|
-
gemfiles = travis_yml.fetch('gemfile')
|
21
|
-
matrix_options = travis_yml.fetch('matrix', {})
|
22
|
-
excludes = matrix_options.fetch('exclude', [])
|
23
|
-
rows = []
|
24
|
-
rubies.each do |ruby|
|
25
|
-
gemfiles.each do |gemfile|
|
26
|
-
row = { 'rvm' => ruby, 'gemfile' => gemfile }
|
27
|
-
rows << row unless excludes.include?(row)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
rows = rows.map { |row| convert_row(row) }
|
31
|
-
rows
|
32
|
-
end
|
33
|
-
|
34
|
-
def convert_row(travis_row)
|
35
|
-
Row.new(:ruby => travis_row['rvm'], :gemfile => travis_row['gemfile'])
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
10
|
##
|
42
11
|
# A row in the test matrix
|
43
12
|
#
|
@@ -125,6 +94,25 @@ module Gemika
|
|
125
94
|
print_summary
|
126
95
|
end
|
127
96
|
|
97
|
+
|
98
|
+
##
|
99
|
+
# Builds a {Matrix} from a `.travis.yml` file, or falls back to a Github Action .yml file
|
100
|
+
#
|
101
|
+
# @param [Hash] options
|
102
|
+
# @option options [String] Path to the `.travis.yml` file.
|
103
|
+
#
|
104
|
+
def self.from_ci_config
|
105
|
+
travis_location = '.travis.yml'
|
106
|
+
workflow_location = '.github/workflows/test.yml'
|
107
|
+
if File.exists?(travis_location)
|
108
|
+
from_travis_yml(:path => travis_location)
|
109
|
+
elsif File.exists?(workflow_location)
|
110
|
+
from_github_actions_yml(:path => workflow_location)
|
111
|
+
else
|
112
|
+
raise MissingMatrixDefinition, "expected either a #{travis_location} or a #{workflow_location}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
128
116
|
##
|
129
117
|
# Builds a {Matrix} from the given `.travis.yml` file.
|
130
118
|
#
|
@@ -136,8 +124,25 @@ module Gemika
|
|
136
124
|
new(options.merge(:rows => rows))
|
137
125
|
end
|
138
126
|
|
127
|
+
##
|
128
|
+
# Builds a {Matrix} from the given Github Action workflow definition
|
129
|
+
#
|
130
|
+
# @param [Hash] options
|
131
|
+
# @option options [String] Path to the `.yml` file.
|
132
|
+
#
|
133
|
+
def self.from_github_actions_yml(options = {})
|
134
|
+
rows = GithubActionsConfig.load_rows(options)
|
135
|
+
new(options.merge(:rows => rows))
|
136
|
+
end
|
137
|
+
|
139
138
|
attr_reader :rows, :current_ruby
|
140
139
|
|
140
|
+
def self.generate_github_actions_workflow(options= {})
|
141
|
+
require 'gemika/github_actions_generator'
|
142
|
+
rows = TravisConfig.load_rows(options)
|
143
|
+
GithubActionsGenerator.new(bundler_version: Bundler::VERSION).generate(rows)
|
144
|
+
end
|
145
|
+
|
141
146
|
private
|
142
147
|
|
143
148
|
def puts(*args)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Gemika
|
2
|
+
class Matrix
|
3
|
+
|
4
|
+
##
|
5
|
+
# Load Github Action `.yml` files.
|
6
|
+
#
|
7
|
+
# @!visibility private
|
8
|
+
#
|
9
|
+
class GithubActionsConfig
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def load_rows(options)
|
13
|
+
path = options.fetch(:path, '.github/workflows/test.yml')
|
14
|
+
workflow_yml = YAML.load_file(path)
|
15
|
+
|
16
|
+
matrices = workflow_yml.fetch('jobs', {}).values.map do |job|
|
17
|
+
job.fetch('strategy', {})['matrix']
|
18
|
+
end.reject(&:nil?)
|
19
|
+
|
20
|
+
matrices.map do |matrix|
|
21
|
+
matrix_to_rows(matrix)
|
22
|
+
end.flatten(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def matrix_to_rows(matrix)
|
28
|
+
if (!matrix['ruby'] || !matrix['gemfile']) && (!matrix['include'])
|
29
|
+
raise InvalidMatrixDefinition, 'matrix must use the keys "ruby" and "gemfile"'
|
30
|
+
end
|
31
|
+
|
32
|
+
rubies = matrix.fetch('ruby', [])
|
33
|
+
gemfiles = matrix.fetch('gemfile', [])
|
34
|
+
|
35
|
+
includes = matrix.fetch('include', [])
|
36
|
+
excludes = matrix.fetch('exclude', [])
|
37
|
+
|
38
|
+
rows = []
|
39
|
+
rubies.each do |ruby|
|
40
|
+
gemfiles.each do |gemfile|
|
41
|
+
row = { 'ruby' => ruby, 'gemfile' => gemfile }
|
42
|
+
rows << row unless excludes.include?(row)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
rows = rows + includes
|
47
|
+
rows.map { |row| convert_row(row) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def convert_row(row_hash)
|
51
|
+
if !row_hash['ruby'] || !row_hash['gemfile']
|
52
|
+
raise InvalidMatrixDefinition, 'matrix must use the keys "ruby" and "gemfile"'
|
53
|
+
end
|
54
|
+
Row.new(:ruby => row_hash['ruby'], :gemfile => row_hash['gemfile'])
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Gemika
|
2
|
+
class Matrix
|
3
|
+
|
4
|
+
##
|
5
|
+
# Load `.travis.yml` files.
|
6
|
+
#
|
7
|
+
# @!visibility private
|
8
|
+
#
|
9
|
+
class TravisConfig
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def load_rows(options)
|
13
|
+
path = options.fetch(:path, '.travis.yml')
|
14
|
+
travis_yml = YAML.load_file(path)
|
15
|
+
rubies = travis_yml.fetch('rvm', [])
|
16
|
+
gemfiles = travis_yml.fetch('gemfile', [])
|
17
|
+
matrix_options = travis_yml.fetch('matrix', {})
|
18
|
+
includes = matrix_options.fetch('include', [])
|
19
|
+
excludes = matrix_options.fetch('exclude', [])
|
20
|
+
|
21
|
+
rows = []
|
22
|
+
rubies.each do |ruby|
|
23
|
+
gemfiles.each do |gemfile|
|
24
|
+
row = { 'rvm' => ruby, 'gemfile' => gemfile }
|
25
|
+
rows << row unless excludes.include?(row)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
rows = rows + includes
|
30
|
+
rows = rows.map { |row| convert_row(row) }
|
31
|
+
rows
|
32
|
+
end
|
33
|
+
|
34
|
+
def convert_row(travis_row)
|
35
|
+
Row.new(:ruby => travis_row['rvm'], :gemfile => travis_row['gemfile'])
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/gemika/tasks.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'gemika/env'
|
2
|
+
require 'gemika/matrix'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Rake tasks to run commands for each compatible row in the test matrix.
|
6
|
+
#
|
7
|
+
namespace :gemika do
|
8
|
+
|
9
|
+
desc "Generate a github action workflow from a .travis.yml"
|
10
|
+
task :generate_github_actions_workflow do
|
11
|
+
puts Gemika::Matrix.generate_github_actions_workflow.to_yaml
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|