vx-builder 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +15 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +40 -0
  9. data/lib/vx/builder/configuration.rb +20 -0
  10. data/lib/vx/builder/helper/config.rb +14 -0
  11. data/lib/vx/builder/helper/logger.rb +14 -0
  12. data/lib/vx/builder/helper/trace_sh_command.rb +16 -0
  13. data/lib/vx/builder/script/env.rb +21 -0
  14. data/lib/vx/builder/script/prepare.rb +59 -0
  15. data/lib/vx/builder/script/ruby.rb +59 -0
  16. data/lib/vx/builder/script/script.rb +22 -0
  17. data/lib/vx/builder/script/services.rb +25 -0
  18. data/lib/vx/builder/script/webdav_cache.rb +83 -0
  19. data/lib/vx/builder/script.rb +98 -0
  20. data/lib/vx/builder/source/constants.rb +11 -0
  21. data/lib/vx/builder/source/matrix.rb +113 -0
  22. data/lib/vx/builder/source/serializable.rb +43 -0
  23. data/lib/vx/builder/source.rb +106 -0
  24. data/lib/vx/builder/task.rb +27 -0
  25. data/lib/vx/builder/version.rb +5 -0
  26. data/lib/vx/builder.rb +38 -0
  27. data/spec/fixtures/travis.yml +5 -0
  28. data/spec/fixtures/travis_bug_1.yml +13 -0
  29. data/spec/fixtures/travis_bug_2.yml +18 -0
  30. data/spec/lib/builder/configuration_spec.rb +7 -0
  31. data/spec/lib/builder/script_spec.rb +29 -0
  32. data/spec/lib/builder/source_matrix_spec.rb +215 -0
  33. data/spec/lib/builder/source_spec.rb +183 -0
  34. data/spec/lib/builder/task_spec.rb +25 -0
  35. data/spec/lib/builder_spec.rb +17 -0
  36. data/spec/spec_helper.rb +15 -0
  37. data/spec/support/create.rb +14 -0
  38. data/spec/support/fixture.rb +7 -0
  39. data/vx-builder.gemspec +29 -0
  40. metadata +192 -0
@@ -0,0 +1,113 @@
1
+ module Vx
2
+ module Builder
3
+ class Source
4
+ class Matrix
5
+
6
+ KEYS = (Source::LANGS + %w{ matrix_env:env }).freeze
7
+ NOT_MATRIX_KEYS = %w{ script before_script services }
8
+
9
+ attr_reader :source
10
+
11
+ def initialize(build_configuration)
12
+ @source = build_configuration
13
+ end
14
+
15
+ def keys
16
+ extract_pair_of_key_and_values.map(&:first).sort
17
+ end
18
+
19
+ def configurations
20
+ attributes_for_new_configurations_with_merged_env.map do |attrs|
21
+ attrs = attrs.merge(
22
+ NOT_MATRIX_KEYS.inject({}) do |a,v|
23
+ a[v] = source.public_send(v)
24
+ a
25
+ end
26
+ )
27
+ Source.new attrs
28
+ end
29
+ end
30
+
31
+ def attributes_for_new_configurations_with_merged_env
32
+ attrs = attributes_for_new_configurations
33
+ attrs = [{}] if attrs.empty?
34
+ attrs.map do |a|
35
+ e = a["env"]
36
+ a["env"] = {
37
+ "global" => Array(e) + source.global_env,
38
+ "matrix" => e
39
+ }
40
+ a
41
+ end
42
+ attrs
43
+ end
44
+
45
+ def attributes_for_new_configurations
46
+ permutate_and_build_values.inject([]) do |ac, values|
47
+ ac << values.inject({}) do |a,val|
48
+ a[val.key] = val.value
49
+ a
50
+ end
51
+ ac
52
+ end
53
+ end
54
+
55
+ def permutate_and_build_values
56
+ values = extract_pair_of_key_and_values.map do |key, vals|
57
+ vals.map{|it| Value.new(key, it) }
58
+ end
59
+ if matrix_values?(values)
60
+ array_permutations(values).map do |it|
61
+ if it.is_a?(Array)
62
+ it.flatten
63
+ else
64
+ [it]
65
+ end
66
+ end
67
+ else
68
+ [values.flatten]
69
+ end.sort_by(&:to_s)
70
+ end
71
+
72
+ def extract_pair_of_key_and_values
73
+ KEYS.map.inject([]) do |a, k|
74
+ k_method, k_name = k.split(":")
75
+ k_name ||= k_method
76
+
77
+ if (val = source[k_method]) && !val.empty?
78
+ a << [k_name, val]
79
+ end
80
+ a
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def matrix_values?(values)
87
+ !values.all?{|i| i.size == 1 }
88
+ end
89
+
90
+ def array_permutations array, index=0
91
+ # index is 0 by default : start at the beginning, more elegant.
92
+ return array[-1] if index == array.size - 1 # Return last element if at end.
93
+
94
+ result = []
95
+
96
+ array[index].each do |element| # For each array
97
+ array_permutations(array, index + 1).each do |x| # Permute permute permute
98
+ result << [element, x]
99
+ end
100
+ end
101
+ result
102
+ end
103
+
104
+ Value = Struct.new(:key, :value) do
105
+ def to_s
106
+ [key, value].join(":")
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,43 @@
1
+ require 'yaml'
2
+ require 'json'
3
+
4
+ module Vx
5
+ module Builder
6
+ class Source
7
+
8
+ module Serializable
9
+
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ end
13
+
14
+ def to_yaml
15
+ YAML.dump(attributes)
16
+ end
17
+
18
+ def to_hash
19
+ attributes
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ def from_file(file)
25
+ if File.readable? file
26
+ from_yaml File.read(file)
27
+ end
28
+ end
29
+
30
+ def from_yaml(yaml)
31
+ from_attributes YAML.load(yaml)
32
+ end
33
+
34
+ def from_attributes(attrs)
35
+ Source.new attrs
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,106 @@
1
+ require File.expand_path("../source/constants", __FILE__)
2
+ require File.expand_path("../source/matrix", __FILE__)
3
+ require File.expand_path("../source/serializable", __FILE__)
4
+
5
+ module Vx
6
+ module Builder
7
+ class Source
8
+
9
+ include Source::Serializable
10
+
11
+ attr_reader :attributes
12
+ alias_method :to_hash, :attributes
13
+
14
+ def initialize(attrs = {})
15
+ @attributes = normalize_attributes attrs
16
+ end
17
+
18
+ def [](val)
19
+ public_send(val)
20
+ end
21
+
22
+ def matrix_keys
23
+ @matrix_keys ||=
24
+ Matrix::KEYS.inject({}) do |a,k|
25
+ k_method, k_name = k.split(":")
26
+ k_name ||= k_method
27
+ val = send(k_method)
28
+ unless val.empty?
29
+ a[k_name] = val.first
30
+ end
31
+ a
32
+ end
33
+ end
34
+
35
+ def to_matrix_s
36
+ @to_matrix_s ||= matrix_keys.map{|k,v| "#{k}:#{v}" }.sort.join(", ")
37
+ end
38
+
39
+ def to_script_builder(build)
40
+ ScriptBuilder.new(build, self)
41
+ end
42
+
43
+ def env
44
+ attributes["env"]
45
+ end
46
+
47
+ def matrix_env
48
+ attributes["env"]["matrix"]
49
+ end
50
+
51
+ def global_env
52
+ attributes["env"]["global"]
53
+ end
54
+
55
+ AS_ARRAY.each do |m|
56
+ define_method m do
57
+ @attributes[m] || []
58
+ end
59
+ end
60
+
61
+ def merge(attrs = {})
62
+ self.class.from_attributes self.attributes.merge(attrs)
63
+ end
64
+
65
+ private
66
+
67
+ def normalize_attributes(attributes)
68
+ attributes = attributes.inject({}) do |a,row|
69
+ k,v = row
70
+ if AS_ARRAY.include?(k.to_s)
71
+ v = Array(v)
72
+ end
73
+ a[k.to_s] = v
74
+ a
75
+ end
76
+ normalize_env_attribute attributes
77
+ end
78
+
79
+ def normalize_env_attribute(attributes)
80
+ env = (attributes['env'] || {}) .dup
81
+ case env
82
+ when Hash
83
+ attributes["env"] = {
84
+ "matrix" => Array(env['matrix']),
85
+ "global" => Array(env['global'])
86
+ }
87
+ else
88
+ attributes['env'] = {
89
+ "matrix" => Array(env).map(&:to_s),
90
+ "global" => []
91
+ }
92
+ end
93
+ freeze_normalized_attributes attributes
94
+ end
95
+
96
+ def freeze_normalized_attributes(attributes)
97
+ attributes.freeze
98
+ attributes['env'].freeze
99
+ attributes['env']['global'].freeze
100
+ attributes['env']['matrix'].freeze
101
+ attributes
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,27 @@
1
+ module Vx
2
+ module Builder
3
+ class Task
4
+
5
+ attr_reader :name, :src, :sha, :deploy_key, :branch, :pull_request_id
6
+
7
+ def initialize(name, src, sha, options = {})
8
+ @name = name
9
+ @src = src
10
+ @sha = sha
11
+ @deploy_key = options[:deploy_key]
12
+ @branch = options[:branch]
13
+ @pull_request_id = options[:pull_request_id]
14
+
15
+ validate!
16
+ end
17
+
18
+ private
19
+
20
+ def validate!
21
+ (name && src && sha && deploy_key && branch) or
22
+ raise(MissingKeys)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module Vx
2
+ module Builder
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
data/lib/vx/builder.rb ADDED
@@ -0,0 +1,38 @@
1
+ require File.expand_path("../builder/version", __FILE__)
2
+
3
+ module Vx
4
+ module Builder
5
+ autoload :Source, File.expand_path("../builder/source", __FILE__)
6
+ autoload :Script, File.expand_path("../builder/script", __FILE__)
7
+ autoload :Task, File.expand_path("../builder/task", __FILE__)
8
+ autoload :Configuration, File.expand_path("../builder/configuration", __FILE__)
9
+
10
+ module Helper
11
+ autoload :Config, File.expand_path("../builder/helper/config", __FILE__)
12
+ autoload :Logger, File.expand_path("../builder/helper/logger", __FILE__)
13
+ autoload :TraceShCommand, File.expand_path("../builder/helper/trace_sh_command", __FILE__)
14
+ end
15
+
16
+ class MissingKeys < Exception ; end
17
+
18
+ extend self
19
+
20
+ def logger
21
+ config.logger
22
+ end
23
+
24
+ def configure
25
+ yield config if block_given?
26
+ config
27
+ end
28
+
29
+ def config
30
+ @config ||= Configuration.new
31
+ end
32
+
33
+ def reset_config!
34
+ @config = nil
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 2.0.0
3
+ before_script:
4
+ - "echo before_script"
5
+ script: "RAILS_ENV=test ls -1 && echo DONE!"
@@ -0,0 +1,13 @@
1
+ rvm:
2
+ - "2.0.0"
3
+
4
+ env:
5
+ - DB=postgresql
6
+
7
+ before_script:
8
+ - cp config/samples/secret_token.rb config/initializers/secret_token.rb
9
+ - cp config/database.travis.yml config/database.yml
10
+ - bundle exec rake db:setup
11
+
12
+ script:
13
+ - bundle exec rake
@@ -0,0 +1,18 @@
1
+ # bug: ignore services
2
+ rvm:
3
+ - "2.0.0"
4
+
5
+ services:
6
+ - elasticsearch
7
+
8
+ env:
9
+ - DB=postgresql
10
+
11
+ before_script:
12
+ - cp config/samples/secret_token.rb config/initializers/secret_token.rb
13
+ - cp config/database.travis.yml config/database.yml
14
+ - bundle exec rake db:setup
15
+ - bundle exec rake db:test:prepare
16
+
17
+ script:
18
+ - bundle exec rspec
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vx::Builder::Configuration do
4
+ its(:logger) { should be }
5
+ its(:webdav_cache_url) { should be_nil }
6
+ its(:casher_ruby) { should be }
7
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vx::Builder::Script do
4
+ let(:task) { create :task }
5
+ let(:source) { create :source }
6
+ let(:script) { described_class.new task, source }
7
+ subject { script }
8
+
9
+ context "just created" do
10
+ its(:source) { should eq source }
11
+ its(:task) { should eq task }
12
+ end
13
+
14
+ context "#to_before_script" do
15
+ subject { script.to_before_script }
16
+ it { should be }
17
+ end
18
+
19
+ context "#to_after_script" do
20
+ subject { script.to_after_script }
21
+ it { should be }
22
+ end
23
+
24
+ context "#to_script" do
25
+ subject { script.to_script }
26
+ it { should be }
27
+ end
28
+
29
+ end
@@ -0,0 +1,215 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ describe Vx::Builder::Source::Matrix do
5
+ let(:attributes) { {
6
+ env: %w{ FOO=1 BAR=2 },
7
+ rvm: %w{ 1.8.7 1.9.3 2.0.0 },
8
+ scala: %w{ 2.9.2 2.10.1 },
9
+ before_script: "echo before_script",
10
+ script: "echo script"
11
+ } }
12
+ let(:config) { Vx::Builder::Source.from_attributes attributes }
13
+ let(:matrix) { described_class.new config }
14
+
15
+ subject { matrix }
16
+
17
+ context "just created" do
18
+ its(:source) { should eq config }
19
+ end
20
+
21
+ context "keys" do
22
+ subject { matrix.keys }
23
+ it { should eq %w{ env rvm scala } }
24
+ context "without matrix" do
25
+ let(:attributes) { {} }
26
+
27
+ it { should eq [] }
28
+ end
29
+ end
30
+
31
+ context 'configurations' do
32
+ subject { matrix.configurations }
33
+
34
+ it "should copy script from source" do
35
+ expect(subject.map(&:script).uniq).to eq [["echo script"]]
36
+ end
37
+
38
+ it "should copy before_script from source" do
39
+ expect(subject.map(&:before_script).uniq).to eq [["echo before_script"]]
40
+ end
41
+
42
+ context "without any matrix keys" do
43
+ let(:attributes) { {
44
+ script: %w{ /bin/true },
45
+ } }
46
+
47
+ it { should have(1).item }
48
+ its("first.attributes") { should eq(
49
+ "env" => {
50
+ "matrix" => [],
51
+ "global" => []
52
+ },
53
+ "script" =>["/bin/true"],
54
+ "before_script" =>[],
55
+ "services" => []
56
+ )}
57
+ end
58
+
59
+ context "with one env and one rvm key" do
60
+ let(:config) { Vx::Builder::Source.from_file fixture_path("travis_bug_1.yml") }
61
+
62
+ it{ should have(1).item }
63
+
64
+ context "attributes" do
65
+ subject { matrix.configurations.map(&:to_matrix_s) }
66
+
67
+ it { should eq ["env:DB=postgresql, rvm:2.0.0"] }
68
+ end
69
+ end
70
+
71
+ context "with services key" do
72
+ let(:config) { Vx::Builder::Source.from_file fixture_path("travis_bug_2.yml") }
73
+ it { should have(1).item }
74
+
75
+ it "should have services" do
76
+ expect(matrix.configurations.first.services).to eq %w{ elasticsearch }
77
+ end
78
+
79
+ end
80
+
81
+ context "values" do
82
+
83
+ it { should have(12).items }
84
+
85
+ context "attributes" do
86
+ subject { matrix.configurations.map(&:to_matrix_s) }
87
+
88
+ it do
89
+ should eq [
90
+ "env:BAR=2, rvm:1.8.7, scala:2.10.1",
91
+ "env:FOO=1, rvm:1.8.7, scala:2.10.1",
92
+ "env:BAR=2, rvm:1.8.7, scala:2.9.2",
93
+ "env:FOO=1, rvm:1.8.7, scala:2.9.2",
94
+ "env:BAR=2, rvm:1.9.3, scala:2.10.1",
95
+ "env:FOO=1, rvm:1.9.3, scala:2.10.1",
96
+ "env:BAR=2, rvm:1.9.3, scala:2.9.2",
97
+ "env:FOO=1, rvm:1.9.3, scala:2.9.2",
98
+ "env:BAR=2, rvm:2.0.0, scala:2.10.1",
99
+ "env:FOO=1, rvm:2.0.0, scala:2.10.1",
100
+ "env:BAR=2, rvm:2.0.0, scala:2.9.2",
101
+ "env:FOO=1, rvm:2.0.0, scala:2.9.2"
102
+ ]
103
+ end
104
+
105
+ context "without matrix" do
106
+ let(:attributes) { {
107
+ rvm: %w{ 2.0.0 },
108
+ } }
109
+
110
+ it { should eq ['rvm:2.0.0'] }
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+
117
+ context "attributes_for_new_confgurations_with_merged_env" do
118
+ subject { matrix.attributes_for_new_configurations_with_merged_env }
119
+
120
+ before do
121
+ attributes.merge!(
122
+ env: {
123
+ "global" => "FOO=1",
124
+ "matrix" => %w{ BAR=1 BAR=2 }
125
+ }
126
+ )
127
+ end
128
+
129
+ it { should have(12).items }
130
+
131
+ it "should merge matrix env to global env" do
132
+ expect(subject.map{|i| i["env"]["global"] }.uniq.sort).to eq([["BAR=1", "FOO=1"], ["BAR=2", "FOO=1"]])
133
+ end
134
+ end
135
+
136
+ context 'attributes_for_new_configurations' do
137
+ subject { matrix.attributes_for_new_configurations }
138
+
139
+ it { should have(12).items }
140
+
141
+ its(:first) { should eq("rvm" => "1.8.7",
142
+ "scala" => "2.10.1",
143
+ "env" => "BAR=2") }
144
+ its(:last) { should eq("rvm" => "2.0.0",
145
+ "scala" => "2.9.2",
146
+ "env" => "FOO=1") }
147
+ end
148
+
149
+ context 'extract_pair_of_key_and_values' do
150
+ subject { matrix.extract_pair_of_key_and_values }
151
+ it {
152
+ should eq [
153
+ ["rvm", %w{ 1.8.7 1.9.3 2.0.0 }],
154
+ ["scala", %w{ 2.9.2 2.10.1 }],
155
+ ["env", %w{ FOO=1 BAR=2 }]
156
+ ]
157
+ }
158
+ end
159
+
160
+ context "permutate_and_build_values" do
161
+ subject { format_values matrix.permutate_and_build_values }
162
+ let(:expected) { [
163
+ %w{env:BAR=2 rvm:1.8.7 scala:2.10.1},
164
+ %w{env:BAR=2 rvm:1.8.7 scala:2.9.2},
165
+ %w{env:BAR=2 rvm:1.9.3 scala:2.10.1},
166
+ %w{env:BAR=2 rvm:1.9.3 scala:2.9.2},
167
+ %w{env:BAR=2 rvm:2.0.0 scala:2.10.1},
168
+ %w{env:BAR=2 rvm:2.0.0 scala:2.9.2},
169
+ %w{env:FOO=1 rvm:1.8.7 scala:2.10.1},
170
+ %w{env:FOO=1 rvm:1.8.7 scala:2.9.2},
171
+ %w{env:FOO=1 rvm:1.9.3 scala:2.10.1},
172
+ %w{env:FOO=1 rvm:1.9.3 scala:2.9.2},
173
+ %w{env:FOO=1 rvm:2.0.0 scala:2.10.1},
174
+ %w{env:FOO=1 rvm:2.0.0 scala:2.9.2},
175
+ ] }
176
+
177
+ it { should eq expected }
178
+
179
+ context "with empty keys" do
180
+ let(:attributes) { {
181
+ env: %w{ FOO=1 BAR=2 },
182
+ rvm: %w{ 1.8.7 1.9.3 2.0.0 },
183
+ scala: %w{ 2.9.2 2.10.1 },
184
+ java: [],
185
+ go: nil
186
+ } }
187
+ it { should eq expected }
188
+ end
189
+
190
+ context "with one key" do
191
+ let(:attributes) { {
192
+ rvm: %w{ 1.9.3 2.0.0 },
193
+ } }
194
+ let(:expected) {[
195
+ %w{ rvm:1.9.3 },
196
+ %w{ rvm:2.0.0 }
197
+ ]}
198
+ it { should eq expected }
199
+ end
200
+
201
+ context "without matrix" do
202
+ let(:attributes) { {
203
+ rvm: %w{ 2.0.0 },
204
+ } }
205
+ let(:expected) {[
206
+ %w{ rvm:2.0.0 }
207
+ ]}
208
+ it { should eq expected }
209
+ end
210
+
211
+ def format_values(values)
212
+ values.map{|i| i.map(&:to_s).sort }.sort
213
+ end
214
+ end
215
+ end