vx-builder 0.0.2

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.
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