vx-router 0.2.0.pre28
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +276 -0
- data/Rakefile +46 -0
- data/bin/vx-router +5 -0
- data/fixtures/jobs_publisher +13 -0
- data/fixtures/travis.yml +5 -0
- data/lib/vx/router.rb +158 -0
- data/lib/vx/router/build.rb +80 -0
- data/lib/vx/router/cli.rb +53 -0
- data/lib/vx/router/configuration.rb +27 -0
- data/lib/vx/router/consumers/build_logs_consumer.rb +13 -0
- data/lib/vx/router/consumers/build_status_consumer.rb +13 -0
- data/lib/vx/router/consumers/builds_consumer.rb +27 -0
- data/lib/vx/router/consumers/job_status_consumer.rb +13 -0
- data/lib/vx/router/consumers/jobs_consumer.rb +13 -0
- data/lib/vx/router/ext/array.rb +5 -0
- data/lib/vx/router/ext/string.rb +9 -0
- data/lib/vx/router/helper/config.rb +11 -0
- data/lib/vx/router/helper/logger.rb +11 -0
- data/lib/vx/router/helper/trace_sh_command.rb +12 -0
- data/lib/vx/router/initializers/amqp.rb +3 -0
- data/lib/vx/router/script_builder.rb +98 -0
- data/lib/vx/router/script_builder/databases.rb +28 -0
- data/lib/vx/router/script_builder/env.rb +21 -0
- data/lib/vx/router/script_builder/prepare.rb +58 -0
- data/lib/vx/router/script_builder/ruby.rb +68 -0
- data/lib/vx/router/script_builder/script.rb +22 -0
- data/lib/vx/router/script_builder/webdav_cache.rb +80 -0
- data/lib/vx/router/version.rb +5 -0
- data/spec/lib/build_spec.rb +123 -0
- data/spec/lib/configuration_spec.rb +23 -0
- data/spec/lib/router_spec.rb +80 -0
- data/spec/lib/script_builder/prepare_spec.rb +44 -0
- data/spec/lib/script_builder/webdav_cache_spec.rb +80 -0
- data/spec/lib/script_builder_spec.rb +36 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/create.rb +40 -0
- data/spec/support/fixture.rb +7 -0
- data/spec/support/last_build_log_message.rb +3 -0
- data/spec/support/shared_examples/update_build_status_message.rb +5 -0
- data/vx-router.gemspec +32 -0
- metadata +225 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'vx/common'
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
class Router
|
5
|
+
class ScriptBuilder
|
6
|
+
|
7
|
+
Prepare = Struct.new(:app) do
|
8
|
+
|
9
|
+
include Helper::TraceShCommand
|
10
|
+
include Common::Helper::UploadShCommand
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
name = env.build.message.name
|
14
|
+
deploy_key = env.build.message.deploy_key
|
15
|
+
|
16
|
+
repo_path = "code/#{name}"
|
17
|
+
data_path = "data/#{name}"
|
18
|
+
key_file = "#{data_path}/key"
|
19
|
+
git_ssh_file = "#{data_path}/git_ssh"
|
20
|
+
|
21
|
+
sha = env.build.message.sha
|
22
|
+
scm = build_scm(env, sha, repo_path)
|
23
|
+
git_ssh = scm.git_ssh.class.template(deploy_key && "$PWD/#{key_file}")
|
24
|
+
|
25
|
+
env.init.tap do |i|
|
26
|
+
i << "mkdir -p #{data_path}"
|
27
|
+
i << "mkdir -p #{repo_path}"
|
28
|
+
|
29
|
+
if deploy_key
|
30
|
+
i << "echo instaling keys"
|
31
|
+
i << upload_sh_command(key_file, deploy_key)
|
32
|
+
i << "chmod 0600 #{key_file}"
|
33
|
+
end
|
34
|
+
|
35
|
+
i << upload_sh_command(git_ssh_file, git_ssh)
|
36
|
+
i << "chmod 0750 #{git_ssh_file}"
|
37
|
+
|
38
|
+
i << "export GIT_SSH=$PWD/#{git_ssh_file}"
|
39
|
+
i << scm.make_fetch_command
|
40
|
+
i << "unset GIT_SSH"
|
41
|
+
end
|
42
|
+
|
43
|
+
app.call env
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def build_scm(env, sha, path)
|
49
|
+
SCM::Git.new(env.build.message.src,
|
50
|
+
sha,
|
51
|
+
"$PWD/#{path}",
|
52
|
+
branch: env.build.message.branch)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Vx
|
2
|
+
class Router
|
3
|
+
class ScriptBuilder
|
4
|
+
|
5
|
+
Ruby = Struct.new(:app) do
|
6
|
+
|
7
|
+
ALIASES = {
|
8
|
+
'1.8.7' => "system"
|
9
|
+
}
|
10
|
+
|
11
|
+
include Helper::TraceShCommand
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
if rvm env
|
15
|
+
env.cache_key << "rvm-#{rvm env}"
|
16
|
+
|
17
|
+
env.before_install.tap do |i|
|
18
|
+
i << 'eval "$(rbenv init -)" || true'
|
19
|
+
i << "rbenv shell #{make_rbenv_version_command env}"
|
20
|
+
i << 'export BUNDLE_GEMFILE=${PWD}/Gemfile'
|
21
|
+
i << 'export GEM_HOME=$HOME/cached/rubygems'
|
22
|
+
end
|
23
|
+
|
24
|
+
env.announce.tap do |a|
|
25
|
+
a << trace_sh_command("ruby --version")
|
26
|
+
a << trace_sh_command("gem --version")
|
27
|
+
a << trace_sh_command("bundle --version")
|
28
|
+
end
|
29
|
+
|
30
|
+
env.install.tap do |i|
|
31
|
+
i << trace_sh_command("bundle install")
|
32
|
+
i << trace_sh_command("bundle clean --force")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
app.call(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def rvm(env)
|
42
|
+
env.configuration.rvm.first
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_rbenv_version_command(env)
|
46
|
+
select_rbenv_version_by_aliases(env) ||
|
47
|
+
select_rbenv_version(env)
|
48
|
+
end
|
49
|
+
|
50
|
+
def select_rbenv_version_by_aliases(env)
|
51
|
+
ALIASES[rvm(env)]
|
52
|
+
end
|
53
|
+
|
54
|
+
def select_rbenv_version(env)
|
55
|
+
%{
|
56
|
+
$(rbenv versions |
|
57
|
+
sed -e 's/^\*/ /' |
|
58
|
+
awk '{print $1}' |
|
59
|
+
grep -v 'system' |
|
60
|
+
grep '#{rvm env}' |
|
61
|
+
tail -n1)
|
62
|
+
}.compact
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vx
|
2
|
+
class Router
|
3
|
+
class ScriptBuilder
|
4
|
+
|
5
|
+
Script = Struct.new(:app) do
|
6
|
+
|
7
|
+
include Helper::TraceShCommand
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
env.configuration.before_script.each do |c|
|
11
|
+
env.before_script << trace_sh_command(c)
|
12
|
+
end
|
13
|
+
env.configuration.script.each do |c|
|
14
|
+
env.script << trace_sh_command(c)
|
15
|
+
end
|
16
|
+
app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Vx
|
2
|
+
class Router
|
3
|
+
class ScriptBuilder
|
4
|
+
|
5
|
+
WebdavCache = Struct.new(:app) do
|
6
|
+
|
7
|
+
include Helper::Config
|
8
|
+
include Helper::Logger
|
9
|
+
|
10
|
+
CASHER_URL = "https://raw.github.com/travis-ci/casher/production/bin/casher"
|
11
|
+
CASHER_BIN = "$HOME/.casher/bin/casher"
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
rs = app.call env
|
15
|
+
|
16
|
+
if config.webdav_cache_url
|
17
|
+
assign_url_to_env(env)
|
18
|
+
prepare(env)
|
19
|
+
fetch(env)
|
20
|
+
add(env)
|
21
|
+
push(env)
|
22
|
+
end
|
23
|
+
|
24
|
+
rs
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def assign_url_to_env(env)
|
30
|
+
urls = []
|
31
|
+
branch = env.build.message.branch
|
32
|
+
if branch != 'master'
|
33
|
+
urls << url_for(env, branch)
|
34
|
+
end
|
35
|
+
puts urls.inspect
|
36
|
+
urls << url_for(env, 'master')
|
37
|
+
|
38
|
+
env.webdav_fetch_url = urls
|
39
|
+
env.webdav_push_url = url_for(env, branch)
|
40
|
+
env
|
41
|
+
end
|
42
|
+
|
43
|
+
def url_for(env, branch)
|
44
|
+
name = env.build.message.name.dup + "/" + branch
|
45
|
+
|
46
|
+
key = env.cache_key.join("-").gsub(/[^a-z0-9_\-.]/, '-')
|
47
|
+
"#{config.webdav_cache_url}/#{name}/#{key}.tgz"
|
48
|
+
end
|
49
|
+
|
50
|
+
def prepare(env)
|
51
|
+
cmd = %{
|
52
|
+
export CASHER_DIR=$HOME/.casher &&
|
53
|
+
( mkdir -p $CASHER_DIR/bin &&
|
54
|
+
curl #{CASHER_URL} -s -o #{CASHER_BIN} &&
|
55
|
+
chmod +x #{CASHER_BIN} ) ||
|
56
|
+
true
|
57
|
+
}.compact
|
58
|
+
env.init << cmd
|
59
|
+
end
|
60
|
+
|
61
|
+
def fetch(env)
|
62
|
+
urls = env.webdav_fetch_url.join(" ")
|
63
|
+
env.init << "#{CASHER_BIN} fetch #{urls} || true"
|
64
|
+
end
|
65
|
+
|
66
|
+
def add(env)
|
67
|
+
env.init << "#{CASHER_BIN} add $HOME/cached || true"
|
68
|
+
env.init << "unset CASHER_DIR"
|
69
|
+
end
|
70
|
+
|
71
|
+
def push(env)
|
72
|
+
if env.webdav_push_url
|
73
|
+
env.after_script << "#{CASHER_BIN} push #{env.webdav_push_url}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vx/common'
|
3
|
+
|
4
|
+
describe Vx::Router::Build do
|
5
|
+
let(:msg) { create :message, 'PerformBuild' }
|
6
|
+
let(:build) { described_class.new msg }
|
7
|
+
|
8
|
+
subject { build }
|
9
|
+
|
10
|
+
context "just created" do
|
11
|
+
its(:message) { should eq msg }
|
12
|
+
its(:output) { should be_an_instance_of(Vx::Common::OutputBuffer)}
|
13
|
+
its(:output_counter) { should eq 0 }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "publish_build_log_message" do
|
17
|
+
let(:data) { 'log' }
|
18
|
+
subject { build.publish_build_log_message data }
|
19
|
+
|
20
|
+
it { should be_an_instance_of(Vx::Message::BuildLog) }
|
21
|
+
its(:build_id) { should eq build.message.id }
|
22
|
+
its(:tm) { should eq 1 }
|
23
|
+
its(:log) { should eq data }
|
24
|
+
|
25
|
+
it "should increment counter" do
|
26
|
+
expect {
|
27
|
+
subject
|
28
|
+
}.to change(build, :output_counter).by(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should delivery message" do
|
32
|
+
expect {
|
33
|
+
subject
|
34
|
+
}.to change(Vx::Router::BuildLogsConsumer.messages, :count).by(1)
|
35
|
+
expect(last_build_log_message.log).to eq data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "to_build_status_message" do
|
40
|
+
subject { build.to_build_status_message Vx::Router::Build::STARTED }
|
41
|
+
|
42
|
+
before do
|
43
|
+
build.jobs_count = 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it { should be_an_instance_of(Vx::Message::BuildStatus) }
|
47
|
+
its(:build_id) { should eq build.message.id }
|
48
|
+
its(:status) { should eq 2 }
|
49
|
+
its(:tm) { should be }
|
50
|
+
its(:jobs_count) { should eq 1 }
|
51
|
+
end
|
52
|
+
|
53
|
+
context "add_to_output" do
|
54
|
+
let(:data) { 'data' }
|
55
|
+
let(:messages) { Vx::Router::BuildLogsConsumer.messages }
|
56
|
+
subject do
|
57
|
+
build.add_to_output(data)
|
58
|
+
build.output.flush
|
59
|
+
build
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should delivery message" do
|
63
|
+
expect {
|
64
|
+
subject
|
65
|
+
}.to change(messages, :size).by(1)
|
66
|
+
expect(last_build_log_message.log).to eq data
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should increment output_counter" do
|
70
|
+
expect {
|
71
|
+
subject
|
72
|
+
}.to change(build, :output_counter).by(1)
|
73
|
+
expect(last_build_log_message.tm).to eq 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "add_command_to_output" do
|
78
|
+
let(:data) { 'data' }
|
79
|
+
let(:messages) { Vx::Router::BuildLogsConsumer.messages }
|
80
|
+
subject do
|
81
|
+
build.add_command_to_output(data)
|
82
|
+
build.output.flush
|
83
|
+
build
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should delivery message" do
|
87
|
+
expect {
|
88
|
+
subject
|
89
|
+
}.to change(messages, :size).by(1)
|
90
|
+
expect(last_build_log_message.log).to eq "$ #{data}\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should increment output_counter" do
|
94
|
+
expect {
|
95
|
+
subject
|
96
|
+
}.to change(build, :output_counter).by(1)
|
97
|
+
expect(last_build_log_message.tm).to eq 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context ".to_perform_job_message" do
|
102
|
+
let(:config) { create :configuration }
|
103
|
+
let(:job_id) { 2 }
|
104
|
+
subject { build.to_perform_job_message config, job_id }
|
105
|
+
|
106
|
+
it { should be_an_instance_of Vx::Message::PerformJob }
|
107
|
+
|
108
|
+
context "created message" do
|
109
|
+
its(:id) { should eq build.message.id }
|
110
|
+
its(:name) { should eq build.message.name }
|
111
|
+
its(:job_id) { should eq job_id }
|
112
|
+
its(:before_script) { should be }
|
113
|
+
its(:script) { should be }
|
114
|
+
its(:after_script) { should be }
|
115
|
+
its(:matrix_keys) { should eq ['rvm:2.0.0'] }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "scm_class" do
|
120
|
+
subject { build.scm_class }
|
121
|
+
it { should eq Vx::SCM::Git }
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vx::Router::Configuration do
|
4
|
+
let(:config) { Vx::Router.config }
|
5
|
+
subject { config }
|
6
|
+
|
7
|
+
before { Vx::Router.reset_config! }
|
8
|
+
|
9
|
+
its(:timeout) { should eq 1800 }
|
10
|
+
its(:amqp_url) { should be_nil }
|
11
|
+
|
12
|
+
context ".configure" do
|
13
|
+
subject {
|
14
|
+
Vx::Router.configure do |c|
|
15
|
+
c.timeout = 1
|
16
|
+
c.amqp_url = "2"
|
17
|
+
end
|
18
|
+
}
|
19
|
+
its(:timeout) { should eq 1 }
|
20
|
+
its(:amqp_url) { should eq "2" }
|
21
|
+
its(:workers) { should eq 1 }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
describe Vx::Router do
|
6
|
+
let(:build) { create :build }
|
7
|
+
let(:router) { described_class.new build }
|
8
|
+
subject { router }
|
9
|
+
|
10
|
+
context "just created" do
|
11
|
+
its(:build) { should eq build }
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#perform" do
|
15
|
+
subject { router.perform }
|
16
|
+
it { should be_true }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "#update_build_status" do
|
20
|
+
let(:messages) { Vx::Router::BuildStatusConsumer.messages }
|
21
|
+
let(:first_message) { messages.first }
|
22
|
+
let(:last_message) { messages.last }
|
23
|
+
|
24
|
+
it "should delivery STARTED and FINISHED when success" do
|
25
|
+
router.update_build_status { true }
|
26
|
+
expect(messages).to have(2).items
|
27
|
+
expect(first_message.status).to eq Vx::Router::Build::STARTED
|
28
|
+
expect(last_message.status).to eq Vx::Router::Build::FINISHED
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should delivery STARTED and FAILED when failed" do
|
32
|
+
router.update_build_status { false }
|
33
|
+
expect(messages).to have(2).items
|
34
|
+
expect(first_message.status).to eq Vx::Router::Build::STARTED
|
35
|
+
expect(last_message.status).to eq Vx::Router::Build::FAILED
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should delivery STARTED and FAILED when exception raised" do
|
39
|
+
router.update_build_status { raise "Ignore Me" }
|
40
|
+
expect(messages).to have(2).items
|
41
|
+
expect(first_message.status).to eq Vx::Router::Build::STARTED
|
42
|
+
expect(last_message.status).to eq Vx::Router::Build::FAILED
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "#load_configuration" do
|
47
|
+
subject { router.load_configuration }
|
48
|
+
|
49
|
+
it "should build new BuildConfiguration instance from message" do
|
50
|
+
expect(subject).to be_true
|
51
|
+
expect(router.configuration).to be
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "#create_and_delivery_build_matrix" do
|
56
|
+
let(:messages) { Vx::Router::JobsConsumer.messages }
|
57
|
+
let(:message) { messages.first }
|
58
|
+
subject { router.create_and_delivery_build_matrix }
|
59
|
+
before do
|
60
|
+
stub(router).configuration { create :configuration }
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should create build matrix from source configuration and delivery its to jobs" do
|
64
|
+
expect(subject).to be_true
|
65
|
+
expect(messages).to have(1).item
|
66
|
+
expect(message.job_id).to eq 1
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should publish job_status message with status = INITIALIZED" do
|
70
|
+
expect {
|
71
|
+
subject
|
72
|
+
}.to change(Vx::Router::JobStatusConsumer.messages, :count).by(1)
|
73
|
+
message = Vx::Router::JobStatusConsumer.messages.last
|
74
|
+
expect(message.build_id).to eq build.message.id
|
75
|
+
expect(message.job_id).to eq 1
|
76
|
+
expect(message.status).to eq 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|