vx-common 0.2.0.pre28

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ # from activesupport
2
+
3
+ require 'logger'
4
+
5
+ module Vx
6
+ module Common
7
+ module TaggedLogging
8
+
9
+ class Formatter < ::Logger::Formatter
10
+ Format = "[%s] %1s : %s\n"
11
+
12
+ def call(severity, time, progname, msg)
13
+ Format % [format_datetime(time),
14
+ severity[0...1],
15
+ msg2str("#{tags_text}#{msg}")]
16
+ end
17
+
18
+ def thread_id
19
+ Thread.current.object_id
20
+ end
21
+
22
+ def tagged(*tags)
23
+ new_tags = push_tags(*tags)
24
+ yield self
25
+ ensure
26
+ pop_tags(new_tags.size)
27
+ end
28
+
29
+ def push_tags(*tags)
30
+ tags.flatten.reject{|i| i.to_s.strip.empty? }.tap do |new_tags|
31
+ current_tags.concat new_tags
32
+ end
33
+ end
34
+
35
+ def pop_tags(size = 1)
36
+ current_tags.pop size
37
+ end
38
+
39
+ def clear_tags!
40
+ current_tags.clear
41
+ end
42
+
43
+ def current_tags
44
+ Thread.current[:activesupport_tagged_logging_tags] ||= []
45
+ end
46
+
47
+ private
48
+
49
+ def tags_text
50
+ tags = current_tags
51
+ if tags.any?
52
+ tags.collect { |tag| "[#{tag}] " }.join
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ def self.new(logger)
59
+ # Ensure we set a default formatter so we aren't extending nil!
60
+ logger.formatter = Formatter.new
61
+ logger.extend(self)
62
+ end
63
+
64
+ %w{ push_tags pop_tags clear_tags! }.each do |m|
65
+ define_method m do
66
+ formatter.send(m)
67
+ end
68
+ end
69
+
70
+ def tagged(*tags)
71
+ formatter.tagged(*tags) { yield self }
72
+ end
73
+
74
+ def flush
75
+ clear_tags!
76
+ super if defined?(super)
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,5 @@
1
+ module Vx
2
+ module Common
3
+ VERSION = "0.2.0.pre28"
4
+ end
5
+ end
data/lib/vx/common.rb ADDED
@@ -0,0 +1,20 @@
1
+ require File.expand_path("../common/version", __FILE__)
2
+
3
+ module Vx
4
+ module Common
5
+ module Helper
6
+ autoload :Shell, File.expand_path("../common/helper/shell", __FILE__)
7
+ autoload :Middlewares, File.expand_path("../common/helper/middlewares", __FILE__)
8
+ autoload :UploadShCommand, File.expand_path("../common/helper/upload_sh_command", __FILE__)
9
+ end
10
+
11
+ autoload :OutputBuffer, File.expand_path("../common/output_buffer", __FILE__)
12
+ autoload :EnvFile, File.expand_path("../common/env_file", __FILE__)
13
+ autoload :ErrorNotifier, File.expand_path("../common/error_notifier", __FILE__)
14
+ end
15
+
16
+ module SCM
17
+ autoload :Git, File.expand_path("../scm/git", __FILE__)
18
+ end
19
+
20
+ end
@@ -0,0 +1,58 @@
1
+ module Vx
2
+ module SCM
3
+
4
+ class Git
5
+
6
+ class GitSSH
7
+
8
+ include Common::Helper::Shell
9
+
10
+ attr_reader :deploy_key
11
+
12
+ def initialize(deploy_key)
13
+ @deploy_key = deploy_key
14
+ end
15
+
16
+ def open
17
+ begin
18
+ yield create
19
+ ensure
20
+ destroy
21
+ end
22
+ end
23
+
24
+ def create
25
+ key_location
26
+ location
27
+ end
28
+
29
+ def destroy
30
+ key_location.unlink if key_location
31
+ location.unlink
32
+ @location = nil
33
+ @key_location = nil
34
+ end
35
+
36
+ def location
37
+ @location ||= write_tmp_file 'git', self.class.template(key_location && key_location.path), 0700
38
+ end
39
+
40
+ def key_location
41
+ if deploy_key
42
+ @key_location ||= write_tmp_file 'key', deploy_key, 0600
43
+ end
44
+ end
45
+
46
+ class << self
47
+ def template(key_location)
48
+ key = key_location ? "-i #{key_location}" : ""
49
+ out = ['#!/bin/sh']
50
+ out << "exec /usr/bin/ssh -A -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null #{key} $@"
51
+ out.join "\n"
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/vx/scm/git.rb ADDED
@@ -0,0 +1,105 @@
1
+ require 'ostruct'
2
+ require File.expand_path("../git/git_ssh", __FILE__)
3
+
4
+ module Vx
5
+ module SCM
6
+
7
+ class Git
8
+
9
+ include Common::Helper::Shell
10
+
11
+ COMMIT_RE = /^(.*) -:- (.*) \((.*)\) -:- (.*)$/
12
+
13
+ attr_reader :src, :sha, :path, :logger, :git_ssh, :branch
14
+
15
+ def initialize(src, sha, path, options = {}, &block)
16
+ @src = src
17
+ @sha = sha
18
+ @path = path
19
+ @branch = options[:branch]
20
+ @git_ssh = GitSSH.new options[:deploy_key]
21
+ @logger = block
22
+ end
23
+
24
+ def open
25
+ git_ssh.open do
26
+ yield if block_given?
27
+ end
28
+ end
29
+
30
+ def fetch
31
+ open do
32
+ run_git make_fetch_command
33
+ end
34
+ end
35
+
36
+ def self.make_export_command(from, to)
37
+ %{ (cd '#{from}' && git checkout-index -a -f --prefix='#{to}/') }.strip
38
+ end
39
+
40
+ def make_fetch_command(options = {})
41
+ depth = options.key?(:depth) ? options[:depth] : 50
42
+ clone_branch = " --branch=#{branch}" if branch
43
+ clone_cmd = "git clone -q --depth=#{depth}#{clone_branch} #{src} #{path}"
44
+ checkout_cmd = "git checkout -qf #{sha}"
45
+ fetch_cmd = "git fetch -q origin"
46
+ cmd = %{
47
+ if [ -d #{path}/.git ] ; then
48
+ echo "$ #{fetch_cmd}" &&
49
+ cd #{path} &&
50
+ #{fetch_cmd} || exit $? ;
51
+ else
52
+ echo "$ #{clone_cmd}" &&
53
+ #{clone_cmd} || exit $? ;
54
+ fi ;
55
+ echo "$ #{checkout_cmd}" &&
56
+ cd #{path} &&
57
+ #{checkout_cmd}
58
+ }.gsub("\n", ' ').gsub(/\ +/, ' ').strip
59
+ cmd
60
+ end
61
+
62
+ def commit_info
63
+ rs = {}
64
+ if str = commit_info_string
65
+ if m = str.match(COMMIT_RE)
66
+ rs.merge!(
67
+ sha: m[1],
68
+ author: m[2],
69
+ email: m[3],
70
+ message: m[4]
71
+ )
72
+ end
73
+ end
74
+ OpenStruct.new rs
75
+ end
76
+
77
+ private
78
+
79
+ def commit_info_string
80
+ output = ""
81
+ code = spawn commit_info_cmd, chdir: path do |io|
82
+ output << io
83
+ end
84
+ if code == 0
85
+ output.strip
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ def commit_info_cmd
92
+ %{git log -1 --pretty=format:'%H -:- %cn (%ce) -:- %s'}
93
+ end
94
+
95
+ def run_git(cmd, options = {})
96
+ env = {
97
+ 'GIT_SSH' => git_ssh.location.path
98
+ }
99
+ spawn(env, cmd, options, &logger)
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ TestFirstMiddleware = Struct.new(:app) do
4
+ def call(env)
5
+ env << :first
6
+ app.call env
7
+ end
8
+ end
9
+
10
+ TestLastMiddleware = Struct.new(:app) do
11
+ def call(env)
12
+ env << :last
13
+ app.call env
14
+ end
15
+ end
16
+
17
+ class MiddlewaresTest
18
+ include Vx::Common::Helper::Middlewares
19
+
20
+ middlewares do
21
+ use TestFirstMiddleware
22
+ use TestLastMiddleware
23
+ end
24
+
25
+ def run
26
+ run_middlewares([]) do |env|
27
+ env << :app
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ describe Vx::Common::Helper::Middlewares do
34
+ let(:klass) { MiddlewaresTest }
35
+ let(:object) { klass.new }
36
+ subject { object }
37
+
38
+ it "should run defined middlewares" do
39
+
40
+ expect(object.run).to eq [:first, :last, :app]
41
+ end
42
+
43
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vx::Common::Helper::Shell do
4
+
5
+ let(:klass) { Class.new.tap{|i| i.send :include, described_class } }
6
+ let(:object) { klass.new }
7
+
8
+ subject { object }
9
+
10
+ context "path" do
11
+ it "should create Pathname" do
12
+ expect_method(:path, '/tmp').to eq Pathname.new('/tmp')
13
+ end
14
+ end
15
+
16
+ context "mkdir" do
17
+
18
+ after { FileUtils.rm_rf '/tmp/.a/' }
19
+
20
+ it "should create directories" do
21
+ expect_method(:mkdir, '/tmp/.a/b/c')
22
+ expect(File.directory? '/tmp/.a/b/c')
23
+ end
24
+ end
25
+
26
+ context "rm" do
27
+ before { FileUtils.mkdir_p '/tmp/.a/b/c' }
28
+ after { FileUtils.rm_rf '/tmp/.a' }
29
+
30
+ it "should force remove" do
31
+ expect_method :rm, '/tmp/.a'
32
+ expect(File.exists? '/tmp/.a').to be_false
33
+ end
34
+ end
35
+
36
+ context "recreate" do
37
+ before { FileUtils.mkdir_p '/tmp/.a/b/c' }
38
+ after { FileUtils.rm_rf '/tmp/.a' }
39
+
40
+ it "should remove and create directory" do
41
+ expect_method :recreate, '/tmp/.a/b'
42
+ expect(File.exists? '/tmp/.a/b/c').to be_false
43
+ end
44
+ end
45
+
46
+ context "write_file" do
47
+ let(:fname) { '/tmp/.a' }
48
+ after { FileUtils.rm_f fname }
49
+
50
+ it "should write content to file" do
51
+ expect_method :write_file, fname, 'content', 0611
52
+ expect(File.readable? fname).to be_true
53
+ expect(File.read fname).to eq 'content'
54
+ end
55
+ end
56
+
57
+ context "write_tmp_file" do
58
+ let(:tmp_file) { object.send :write_tmp_file, 'fname', 'content', 0611 }
59
+
60
+ after { FileUtils.rm_f tmp_file.path }
61
+
62
+ it "should create tmp file and write content" do
63
+ expect(tmp_file).to be
64
+ expect(File.readable? tmp_file.path).to be_true
65
+ expect(File.read tmp_file.path).to eq 'content'
66
+ end
67
+ end
68
+
69
+ context "read_file" do
70
+ let(:fname) { '/tmp/.a' }
71
+ before do
72
+ File.open fname, 'w' do |io|
73
+ io << "content"
74
+ end
75
+ end
76
+ after { FileUtils.rm_f fname }
77
+
78
+ it "should read file" do
79
+ expect_method(:read_file, fname).to eq 'content'
80
+ end
81
+
82
+ context "when file does not exists" do
83
+ it "should return nil" do
84
+ expect_method(:read_file, 'not_exists').to be_nil
85
+ end
86
+ end
87
+ end
88
+
89
+ context "bash" do
90
+ let(:output) { '' }
91
+
92
+ context "when command is string" do
93
+ it "should spawn bash command and return exit code" do
94
+ expect_method(:bash, "echo $HOME", &method(:add_to_output)).to eq 0
95
+ expect(output).to eq ENV['HOME'] + "\n"
96
+ end
97
+ end
98
+
99
+ context "when command is a file" do
100
+ let(:fname) { '/tmp/.a' }
101
+
102
+ before do
103
+ File.open fname, 'w' do |io|
104
+ io << "echo $HOME"
105
+ end
106
+ end
107
+
108
+ after { FileUtils.rm_f fname }
109
+
110
+ it "should spawn bash, execute file and return exit code" do
111
+ expect_method(:bash, file: fname, &method(:add_to_output)).to eq 0
112
+ end
113
+ end
114
+
115
+ context "when :ssh options passed" do
116
+ let(:ssh) { 'ssh' }
117
+
118
+ before do
119
+ mock(ssh).spawn("/usr/bin/env -i HOME=${HOME} bash -c command", {}) { 0 }
120
+ end
121
+
122
+ it "should execute command thougth :ssh" do
123
+ expect_method(:bash, 'command', ssh: ssh, &method(:add_to_output)).to eq 0
124
+ end
125
+
126
+ end
127
+
128
+ def add_to_output(out)
129
+ output << out
130
+ end
131
+ end
132
+
133
+ def expect_method(name, *args, &block)
134
+ expect(object.send(name, *args, &block))
135
+ end
136
+
137
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'vx/common/spawn'
5
+ require 'fileutils'
6
+
7
+ describe Vx::Common::Helper::UploadShCommand do
8
+ let(:object) { Object.new }
9
+ subject { object }
10
+
11
+ before do
12
+ object.extend described_class
13
+ object.extend Vx::Common::Spawn
14
+ end
15
+
16
+ it { should be_respond_to(:upload_sh_command) }
17
+
18
+ context "#upload_sh_command" do
19
+ let(:file) { '/tmp/.test' }
20
+ let(:content) { 'Дима' }
21
+ let(:cmd) { object.upload_sh_command file, content }
22
+
23
+ before { FileUtils.rm_rf file }
24
+ after { FileUtils.rm_rf file }
25
+
26
+ it "should be successful" do
27
+ object.spawn cmd do |out|
28
+ puts " ===> #{out}"
29
+ end
30
+
31
+ expect(File).to be_readable(file)
32
+ expect(File.read file).to eq content
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vx::Common::OutputBuffer do
4
+ let(:collected) { "" }
5
+ let(:write) { ->(str) { collected << str } }
6
+ let(:buffer) { described_class.new(0.5, &write) }
7
+
8
+ after do
9
+ buffer.close
10
+ end
11
+
12
+ it { should be }
13
+
14
+ it "should add string to buffer" do
15
+ buffer << "1"
16
+ buffer << "2"
17
+ expect(collected).to eq ''
18
+ sleep 1
19
+ expect(collected).to eq '12'
20
+
21
+ buffer << '3'
22
+ expect(collected).to eq '12'
23
+ sleep 1
24
+ expect(collected).to eq '123'
25
+ end
26
+
27
+ it "should raise error when add to closed buffer" do
28
+ buffer.close
29
+ expect {
30
+ buffer << "1"
31
+ }.to raise_error
32
+ end
33
+
34
+ it "should flush buffer" do
35
+ buffer << "1"
36
+ sleep 0.1
37
+ expect(collected).to eq ''
38
+ buffer.flush
39
+ expect(collected).to eq '1'
40
+ end
41
+
42
+ end
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+ require 'vx/message/testing'
3
+
4
+ describe Vx::SCM::Git do
5
+ let(:path) { '/tmp/.test/repo' }
6
+ let(:message) { Vx::Message::PerformBuild.test_message }
7
+ let(:src) { message.src }
8
+ let(:sha) { message.sha }
9
+ let(:deploy_key) { message.deploy_key }
10
+ let(:output) { "" }
11
+ let(:options) { {} }
12
+ let(:git) {
13
+ described_class.new src, sha, path, options, &method(:add_to_output)
14
+ }
15
+
16
+ subject { git }
17
+
18
+ before do
19
+ FileUtils.rm_rf path
20
+ FileUtils.mkdir_p path
21
+ end
22
+
23
+ after { FileUtils.rm_rf path }
24
+
25
+ context "just created" do
26
+ its(:src) { should eq src }
27
+ its(:sha) { should eq sha }
28
+ its(:path) { should eq path }
29
+ its(:branch) { should be_nil }
30
+ its("git_ssh.deploy_key") { should be_nil }
31
+ end
32
+
33
+ context "assign branch" do
34
+ let(:options) { { branch: 'master' } }
35
+ its(:branch) { should eq 'master' }
36
+ end
37
+
38
+ context "assign deploy_key" do
39
+ let(:options) { { deploy_key: deploy_key } }
40
+ its("git_ssh.deploy_key") { should eq deploy_key }
41
+ end
42
+
43
+ context "fetch" do
44
+ let(:options) { { deploy_key: deploy_key } }
45
+ subject { git.fetch }
46
+
47
+ it { should eq 0 }
48
+
49
+ it "should create nessesary directories and checkout sha" do
50
+ subject
51
+ expect(File.directory? path).to be
52
+ expect(File.directory? "#{path}/.git").to be
53
+ Dir.chdir path do
54
+ expect((`git rev-parse HEAD`).strip).to eq sha
55
+ end
56
+ end
57
+
58
+ it "should capture output" do
59
+ subject
60
+ expect(output).to match(Regexp.escape "$ git clone -q --depth=50 #{src} #{path}")
61
+ end
62
+
63
+ context "twice" do
64
+ before { git.fetch }
65
+ it { should eq 0 }
66
+ end
67
+
68
+ context "with error" do
69
+ let(:src) { "/not-exists-repo.git" }
70
+
71
+ it "should return 128 exitstatus and add error to output" do
72
+ expect(subject).to eq 128
73
+ expect(output).to match('does not exist')
74
+ end
75
+ end
76
+ end
77
+
78
+ context "make_fetch_command" do
79
+ include Vx::Common::Spawn
80
+
81
+ let(:options) { { deploy_key: deploy_key, branch: "master" } }
82
+ let(:run) do
83
+ git.open do
84
+ spawn(git_ssh_env, git.make_fetch_command, &method(:add_to_output))
85
+ end
86
+ end
87
+ subject { git.make_fetch_command }
88
+
89
+ before do
90
+ run
91
+ end
92
+
93
+ it { should be }
94
+
95
+ it "should be success" do
96
+ expect(run).to eq 0
97
+ end
98
+
99
+ it "should create nessesary directories and checkout sha" do
100
+ expect(File.directory? path).to be
101
+ expect(File.directory? "#{path}/.git").to be
102
+ Dir.chdir path do
103
+ expect((`git rev-parse HEAD`).strip).to eq sha
104
+ end
105
+ end
106
+
107
+ context "twice" do
108
+ it "should be" do
109
+ code = git.open do
110
+ spawn(git_ssh_env, git.make_fetch_command, &method(:add_to_output))
111
+ end
112
+ expect(code).to eq 0
113
+ end
114
+ end
115
+
116
+ context "with error" do
117
+ let(:src) { "/not-exists-repo.git" }
118
+
119
+ it "should return 128 exitstatus and add error to output" do
120
+ expect(run).to eq 128
121
+ end
122
+ end
123
+
124
+ def git_ssh_env
125
+ { 'GIT_SSH' => git.git_ssh.location.path }
126
+ end
127
+ end
128
+
129
+ context ".make_export_command" do
130
+ let(:options) { { deploy_key: deploy_key } }
131
+ let(:from) { path }
132
+ let(:to) { '/tmp/.test/export' }
133
+ let(:expected) { "(cd '#{ from }' && git checkout-index -a -f --prefix='#{ to}/')" }
134
+ subject { described_class.make_export_command from, to}
135
+ it { should eq expected }
136
+
137
+ context "run" do
138
+ before do
139
+ git.fetch
140
+ system subject
141
+ end
142
+
143
+ it "should be success" do
144
+ expect($?.to_i).to eq 0
145
+ end
146
+
147
+ it "should export repo" do
148
+ expect(File.readable? "#{to}/Gemfile").to be_true
149
+ end
150
+ end
151
+ end
152
+
153
+ context "commit_info" do
154
+ let(:options) { { deploy_key: deploy_key } }
155
+ subject { git.commit_info }
156
+ before { git.fetch }
157
+
158
+ it "should be" do
159
+ expect(subject.sha).to eq 'b665f90239563c030f1b280a434b3d84daeda1bd'
160
+ expect(subject.author).to eq "Dmitry Galinsky"
161
+ expect(subject.email).to eq 'dima.exe@gmail.com'
162
+ expect(subject.message).to eq 'first release'
163
+ end
164
+ end
165
+
166
+ def add_to_output(out)
167
+ puts "==> #{out}"
168
+ output << out
169
+ end
170
+ end