deplomat 0.1.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 +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +78 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/deplomat.gemspec +77 -0
- data/examples/simple_deploy.rb +22 -0
- data/lib/deplomat/directives.rb +25 -0
- data/lib/deplomat/exceptions.rb +4 -0
- data/lib/deplomat/local_node.rb +13 -0
- data/lib/deplomat/node.rb +149 -0
- data/lib/deplomat/remote_node.rb +63 -0
- data/lib/deplomat.rb +8 -0
- data/spec/directives_spec.rb +25 -0
- data/spec/fixtures/cleaning/.keep +0 -0
- data/spec/fixtures/dir1/file1 +0 -0
- data/spec/fixtures/dir1/file2 +0 -0
- data/spec/fixtures/upload1/uploaded_file1 +0 -0
- data/spec/fixtures/upload1/uploaded_file2 +0 -0
- data/spec/local_node_spec.rb +180 -0
- data/spec/node_spec.rb +41 -0
- data/spec/remote_node_spec.rb +132 -0
- data/spec/spec_helper.rb +17 -0
- metadata +141 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 67aed2b09a78c6ff894531f065ba7db0bdc60e20
|
|
4
|
+
data.tar.gz: 848f9cba73c651699f2496b5b871448fbc15fcb6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7723cc145303719df54eb436a43a012b02e5180ab8fc56d08d121fe3ec1ed6a4b1fffa8befa7dd4d4cb22db0178e9d214e480b4884a062ebe4b5a42357f1eb2e
|
|
7
|
+
data.tar.gz: 6298c99481b28878c15763b0aeed01e99697c3621a677f35027cedb50410f9faaae0ab4f526e753be48d6741acbedd56f214d56f8c3422eef3c3252be32684d3
|
data/.document
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: http://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
addressable (2.4.0)
|
|
5
|
+
builder (3.2.2)
|
|
6
|
+
colorize (0.7.7)
|
|
7
|
+
descendants_tracker (0.0.4)
|
|
8
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
9
|
+
diff-lcs (1.2.5)
|
|
10
|
+
faraday (0.9.2)
|
|
11
|
+
multipart-post (>= 1.2, < 3)
|
|
12
|
+
git (1.3.0)
|
|
13
|
+
github_api (0.13.1)
|
|
14
|
+
addressable (~> 2.4.0)
|
|
15
|
+
descendants_tracker (~> 0.0.4)
|
|
16
|
+
faraday (~> 0.8, < 0.10)
|
|
17
|
+
hashie (>= 3.4)
|
|
18
|
+
multi_json (>= 1.7.5, < 2.0)
|
|
19
|
+
oauth2
|
|
20
|
+
hashie (3.4.4)
|
|
21
|
+
highline (1.7.8)
|
|
22
|
+
jeweler (2.1.1)
|
|
23
|
+
builder
|
|
24
|
+
bundler (>= 1.0)
|
|
25
|
+
git (>= 1.2.5)
|
|
26
|
+
github_api
|
|
27
|
+
highline (>= 1.6.15)
|
|
28
|
+
nokogiri (>= 1.5.10)
|
|
29
|
+
rake
|
|
30
|
+
rdoc
|
|
31
|
+
semver
|
|
32
|
+
json (1.8.3)
|
|
33
|
+
jwt (1.5.1)
|
|
34
|
+
mini_portile2 (2.0.0)
|
|
35
|
+
multi_json (1.11.3)
|
|
36
|
+
multi_xml (0.5.5)
|
|
37
|
+
multipart-post (2.0.0)
|
|
38
|
+
nokogiri (1.6.7.2)
|
|
39
|
+
mini_portile2 (~> 2.0.0.rc2)
|
|
40
|
+
oauth2 (1.1.0)
|
|
41
|
+
faraday (>= 0.8, < 0.10)
|
|
42
|
+
jwt (~> 1.0, < 1.5.2)
|
|
43
|
+
multi_json (~> 1.3)
|
|
44
|
+
multi_xml (~> 0.5)
|
|
45
|
+
rack (>= 1.2, < 3)
|
|
46
|
+
rack (1.6.4)
|
|
47
|
+
rake (11.1.2)
|
|
48
|
+
rdoc (4.2.2)
|
|
49
|
+
json (~> 1.4)
|
|
50
|
+
rspec (3.4.0)
|
|
51
|
+
rspec-core (~> 3.4.0)
|
|
52
|
+
rspec-expectations (~> 3.4.0)
|
|
53
|
+
rspec-mocks (~> 3.4.0)
|
|
54
|
+
rspec-core (3.4.4)
|
|
55
|
+
rspec-support (~> 3.4.0)
|
|
56
|
+
rspec-expectations (3.4.0)
|
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
58
|
+
rspec-support (~> 3.4.0)
|
|
59
|
+
rspec-mocks (3.4.1)
|
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
61
|
+
rspec-support (~> 3.4.0)
|
|
62
|
+
rspec-support (3.4.1)
|
|
63
|
+
semver (1.0.1)
|
|
64
|
+
sys-proctable (1.0.0)
|
|
65
|
+
thread_safe (0.3.5)
|
|
66
|
+
|
|
67
|
+
PLATFORMS
|
|
68
|
+
ruby
|
|
69
|
+
|
|
70
|
+
DEPENDENCIES
|
|
71
|
+
bundler (~> 1.0)
|
|
72
|
+
colorize
|
|
73
|
+
jeweler
|
|
74
|
+
rspec
|
|
75
|
+
sys-proctable
|
|
76
|
+
|
|
77
|
+
BUNDLED WITH
|
|
78
|
+
1.12.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2016 Roman Snitko
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'bundler'
|
|
5
|
+
begin
|
|
6
|
+
Bundler.setup(:default, :development)
|
|
7
|
+
rescue Bundler::BundlerError => e
|
|
8
|
+
$stderr.puts e.message
|
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
10
|
+
exit e.status_code
|
|
11
|
+
end
|
|
12
|
+
require 'rake'
|
|
13
|
+
|
|
14
|
+
require 'jeweler'
|
|
15
|
+
Jeweler::Tasks.new do |gem|
|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
|
17
|
+
gem.name = "deplomat"
|
|
18
|
+
gem.homepage = "http://github.com/snitko/deplomat"
|
|
19
|
+
gem.license = "MIT"
|
|
20
|
+
gem.summary = %Q{Stack agnostic deployment system that uses bash and ssh commands}
|
|
21
|
+
gem.description = %Q{Stack agnostic deployment system that uses bash and ssh commands}
|
|
22
|
+
gem.email = "roman.snitko@gmail.com"
|
|
23
|
+
gem.authors = ["Roman Snitko"]
|
|
24
|
+
# dependencies defined in Gemfile
|
|
25
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.1
|
data/deplomat.gemspec
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
# stub: deplomat 0.1.1 ruby lib
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = "deplomat"
|
|
9
|
+
s.version = "0.1.1"
|
|
10
|
+
|
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
|
+
s.require_paths = ["lib"]
|
|
13
|
+
s.authors = ["Roman Snitko"]
|
|
14
|
+
s.date = "2016-07-05"
|
|
15
|
+
s.description = "Stack agnostic deployment system that uses bash and ssh commands"
|
|
16
|
+
s.email = "roman.snitko@gmail.com"
|
|
17
|
+
s.extra_rdoc_files = [
|
|
18
|
+
"LICENSE.txt",
|
|
19
|
+
"README.rdoc"
|
|
20
|
+
]
|
|
21
|
+
s.files = [
|
|
22
|
+
".document",
|
|
23
|
+
".rspec",
|
|
24
|
+
"Gemfile",
|
|
25
|
+
"Gemfile.lock",
|
|
26
|
+
"LICENSE.txt",
|
|
27
|
+
"README.rdoc",
|
|
28
|
+
"Rakefile",
|
|
29
|
+
"VERSION",
|
|
30
|
+
"examples/simple_deploy.rb",
|
|
31
|
+
"lib/deplomat.rb",
|
|
32
|
+
"lib/deplomat/directives.rb",
|
|
33
|
+
"lib/deplomat/exceptions.rb",
|
|
34
|
+
"lib/deplomat/local_node.rb",
|
|
35
|
+
"lib/deplomat/node.rb",
|
|
36
|
+
"lib/deplomat/remote_node.rb",
|
|
37
|
+
"spec/directives_spec.rb",
|
|
38
|
+
"spec/fixtures/cleaning/.keep",
|
|
39
|
+
"spec/fixtures/dir1/file1",
|
|
40
|
+
"spec/fixtures/dir1/file2",
|
|
41
|
+
"spec/fixtures/upload1/uploaded_file1",
|
|
42
|
+
"spec/fixtures/upload1/uploaded_file2",
|
|
43
|
+
"spec/local_node_spec.rb",
|
|
44
|
+
"spec/node_spec.rb",
|
|
45
|
+
"spec/remote_node_spec.rb",
|
|
46
|
+
"spec/spec_helper.rb"
|
|
47
|
+
]
|
|
48
|
+
s.homepage = "http://github.com/snitko/deplomat"
|
|
49
|
+
s.licenses = ["MIT"]
|
|
50
|
+
s.rubygems_version = "2.5.1"
|
|
51
|
+
s.summary = "Stack agnostic deployment system that uses bash and ssh commands"
|
|
52
|
+
|
|
53
|
+
if s.respond_to? :specification_version then
|
|
54
|
+
s.specification_version = 4
|
|
55
|
+
|
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
57
|
+
s.add_runtime_dependency(%q<sys-proctable>, [">= 0"])
|
|
58
|
+
s.add_runtime_dependency(%q<colorize>, [">= 0"])
|
|
59
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
|
60
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
|
61
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
|
62
|
+
else
|
|
63
|
+
s.add_dependency(%q<sys-proctable>, [">= 0"])
|
|
64
|
+
s.add_dependency(%q<colorize>, [">= 0"])
|
|
65
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
|
66
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
67
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
s.add_dependency(%q<sys-proctable>, [">= 0"])
|
|
71
|
+
s.add_dependency(%q<colorize>, [">= 0"])
|
|
72
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
74
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# This is an example of a deployment script. This file is not executable and uses a simple DSL
|
|
2
|
+
# which is implemented using Ruby. Thus, it may contain valid ruby code, but it's not necessary
|
|
3
|
+
# to know Ruby to write deployment scripts - all that's required is to know a few simple common
|
|
4
|
+
# deplomat directives.
|
|
5
|
+
|
|
6
|
+
# executes right away, but only in particular environment
|
|
7
|
+
execute_in env: "staging" do
|
|
8
|
+
$db = new_node host: "0.0.0.0", port: 1234, user: "deploy"
|
|
9
|
+
$app = new_node host: "0.0.0.0", port: 1234, user: "deploy"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# doesn't execute right away and awaits until called with `execute :compile_assets`
|
|
13
|
+
partial 'compile_assets' do
|
|
14
|
+
$db.execute "echo compiling assets..."
|
|
15
|
+
$app.execute "echo compiling assets..."
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
partial 'run_migrations' do |args|
|
|
19
|
+
args[:server].execute "rake db:migrate"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
execute_partial 'run_migrations', node: $app
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
$partials = {}
|
|
2
|
+
|
|
3
|
+
def execute_in(env:)
|
|
4
|
+
yield if $env == env
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def execute_partial(name, args={})
|
|
8
|
+
print_to_terminal $partials[name][:before_message]
|
|
9
|
+
$partials[name][:block].call(args)
|
|
10
|
+
print_to_terminal $partials[name][:after_message]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def partial(name, message: [], &block)
|
|
14
|
+
message = [message] if message.kind_of?(String)
|
|
15
|
+
before_message = message[0]
|
|
16
|
+
after_message = message[1]
|
|
17
|
+
$partials[name] = { block: block, before_message: before_message, after_message: after_message }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def print_to_terminal(message, color: nil, newline: true)
|
|
21
|
+
return unless message
|
|
22
|
+
message += "\n" if newline
|
|
23
|
+
message = message.send(color) if color
|
|
24
|
+
$stdout.print message
|
|
25
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Deplomat
|
|
2
|
+
|
|
3
|
+
class Node
|
|
4
|
+
|
|
5
|
+
attr_accessor :log_to, :raise_exceptions, :logfile
|
|
6
|
+
attr_reader :current_path
|
|
7
|
+
|
|
8
|
+
def initialize(logfile: "#{Dir.pwd}/deplomat.log", log_to: [:stdout])
|
|
9
|
+
@log_to = log_to
|
|
10
|
+
@logfile = logfile
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute(command, path=@current_path, message: [], stdout_source: :stdout, log_command: true)
|
|
14
|
+
|
|
15
|
+
message = [message] if message.kind_of?(String)
|
|
16
|
+
log(message[0] + "\n", color: 'white') unless message.empty? || message.nil?
|
|
17
|
+
|
|
18
|
+
# Respect current_path
|
|
19
|
+
command_to_log = command
|
|
20
|
+
if path
|
|
21
|
+
command_to_log = "#{command}\n(in #{path})"
|
|
22
|
+
command = "cd #{path} && #{command}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
out = ""
|
|
26
|
+
status = nil
|
|
27
|
+
Open3.popen3(command) do |stdin, stdout, stderr, thread|
|
|
28
|
+
|
|
29
|
+
# Sometimes, programs write in stderr, although there are no errors.
|
|
30
|
+
# rake assets:precompile does that, for example.
|
|
31
|
+
stdout_source_object = (stdout_source == :stderr ? stderr : stdout)
|
|
32
|
+
|
|
33
|
+
log("--> " + command_to_log + "\n", color: "white") if log_command
|
|
34
|
+
while line=stdout_source_object.gets do
|
|
35
|
+
out << line
|
|
36
|
+
log(" #{line}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
error_out = ""
|
|
40
|
+
status = thread.value.exitstatus.to_i
|
|
41
|
+
if status > 0
|
|
42
|
+
while o = stderr.gets
|
|
43
|
+
error_out += o
|
|
44
|
+
end
|
|
45
|
+
log(error_out + "\n", color: 'red')
|
|
46
|
+
if @raise_exceptions
|
|
47
|
+
self.close if self.respond_to?(:close)
|
|
48
|
+
raise Deplomat::ExecutionError
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
yield if block_given?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
log(message[1] + "\n", color: 'white') unless message.empty? || message.nil?
|
|
55
|
+
|
|
56
|
+
return { status: status, out: out }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def copy(what, where)
|
|
60
|
+
execute("rsync -ar #{what} #{where}")
|
|
61
|
+
end
|
|
62
|
+
alias :cp :copy
|
|
63
|
+
|
|
64
|
+
def move(what, where)
|
|
65
|
+
execute("mv #{what} #{where}")
|
|
66
|
+
end
|
|
67
|
+
alias :mv :move
|
|
68
|
+
|
|
69
|
+
def remove(what)
|
|
70
|
+
execute("rm -rf #{what}")
|
|
71
|
+
end
|
|
72
|
+
alias :rm :remove
|
|
73
|
+
|
|
74
|
+
def create_file(filename)
|
|
75
|
+
execute("touch #{filename}")
|
|
76
|
+
end
|
|
77
|
+
alias :touch :create_file
|
|
78
|
+
|
|
79
|
+
def create_dir(dirname)
|
|
80
|
+
execute("mkdir #{dirname}")
|
|
81
|
+
end
|
|
82
|
+
alias :mkdir :create_dir
|
|
83
|
+
|
|
84
|
+
def create_symlink(source, target)
|
|
85
|
+
execute("ln -s #{source} #{target}")
|
|
86
|
+
end
|
|
87
|
+
alias :ln :create_symlink
|
|
88
|
+
|
|
89
|
+
def cd(path)
|
|
90
|
+
@current_path = if path =~ /\A[\/~]/ || path.nil?
|
|
91
|
+
path
|
|
92
|
+
else
|
|
93
|
+
File.expand_path("#{@current_path}#{path}")
|
|
94
|
+
end
|
|
95
|
+
raise Deplomat::NoSuchPathError, @current_path if !@current_path.nil? && !path_exist?(@current_path)
|
|
96
|
+
|
|
97
|
+
# Making sure we don't end up with double // at the end of the path
|
|
98
|
+
@current_path = @current_path.chomp("/") + "/"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def git_push(remote="origin", branch="master")
|
|
102
|
+
execute("git push -u #{remote} #{branch}")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def git_pull(remote="origin", branch="master")
|
|
106
|
+
execute("git pull #{remote} #{branch}")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def git_merge(source="origin", target="master")
|
|
110
|
+
execute("git merge #{source} #{target}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def git_checkout(target)
|
|
114
|
+
execute("git checkout #{target}")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def clean(path: @current_path, except: [], leave: [0, :last])
|
|
118
|
+
# Gets us all entries sorted by date, most recent ones first
|
|
119
|
+
entries_by_date = execute("ls -t", path)[:out].split("\n")
|
|
120
|
+
# Don't do anything with entries listed in :except
|
|
121
|
+
entries_by_date = entries_by_date - except
|
|
122
|
+
if leave
|
|
123
|
+
entries_by_date.reverse! if leave[1] == :first
|
|
124
|
+
entries_by_date = entries_by_date[leave[0]..entries_by_date.length]
|
|
125
|
+
end
|
|
126
|
+
entries_by_date.each { |entry| remove("#{path}#{entry}") }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def log(line, color: 'light_black')
|
|
132
|
+
@message_color = color
|
|
133
|
+
# Only calls log methods mentioned in the @log_to property
|
|
134
|
+
@log_to.each { |logger| self.send("log_to_#{logger}", line) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def log_to_file(line)
|
|
138
|
+
if @logfile
|
|
139
|
+
open(@logfile, 'a') { |f| f.puts line }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def log_to_stdout(line)
|
|
144
|
+
print_to_terminal(line, color: @message_color, newline: false)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Deplomat
|
|
2
|
+
class RemoteNode < Node
|
|
3
|
+
|
|
4
|
+
def initialize(host:, port: 22, user: "deploy")
|
|
5
|
+
super()
|
|
6
|
+
|
|
7
|
+
# Create ControlMasters dir, user might not have it
|
|
8
|
+
unless File.exists?("#{ENV['HOME']}/.ssh/controlmasters/")
|
|
9
|
+
Dir.mkdir("#{ENV['HOME']}/.ssh/controlmasters/")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Establish connection
|
|
13
|
+
first_ssh_command = "ssh -MNfS #{ENV['HOME']}/.ssh/controlmasters/%r@%h:%p #{user}@#{host} -p #{port} -o ControlPersist=10m"
|
|
14
|
+
system first_ssh_command
|
|
15
|
+
|
|
16
|
+
# get background process id by the full command name
|
|
17
|
+
Sys::ProcTable.ps.each do |process|
|
|
18
|
+
if process.cmdline.match(first_ssh_command)
|
|
19
|
+
@pid = process.pid.to_i
|
|
20
|
+
puts "Connected with ssh, host #{host}, pid #{@pid}."
|
|
21
|
+
break
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@ssh_command = "ssh -S #{ENV['HOME']}/.ssh/controlmasters/%r@%h:%p #{user}@#{host} -p #{port}"
|
|
26
|
+
|
|
27
|
+
@host = host
|
|
28
|
+
@user = user
|
|
29
|
+
@port = port
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
alias :local_execute :execute
|
|
33
|
+
def execute(command, path=@current_path, message: [], env_vars: '', login_shell: false, stdout_source: :stdout)
|
|
34
|
+
|
|
35
|
+
log("(#{@host}) --> " + command + "\n", color: "white")
|
|
36
|
+
command = "#{env_vars} cd #{path} && #{command}" if path
|
|
37
|
+
command = "bash -l -c \"#{command}\"" if login_shell
|
|
38
|
+
super("#{@ssh_command} '#{command}'", nil, message: message, stdout_source: stdout_source, log_command: false)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def upload(what, where)
|
|
42
|
+
local_execute "rsync -arz #{what} #{@user}@#{@host}:#{where} --port=#{@port}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def close
|
|
46
|
+
begin
|
|
47
|
+
puts "Closing connection to #{@host}."
|
|
48
|
+
Process.kill 'KILL', @pid
|
|
49
|
+
rescue Errno::ESRCH
|
|
50
|
+
puts "WARNING: no process with #{@pid} found, no connection to close!"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def path_exist?(path)
|
|
57
|
+
old_raise_exceptions = @raise_exceptions
|
|
58
|
+
@raise_exceptions = false
|
|
59
|
+
!(execute("ls #{path}", nil){ @raise_exceptions = old_raise_exceptions}[:status] > 0)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/deplomat.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "directives" do
|
|
4
|
+
|
|
5
|
+
it "executes arbitrary ruby code in a particular environment" do
|
|
6
|
+
$env = 'staging'
|
|
7
|
+
expect(self).to receive(:hello).exactly(1).times
|
|
8
|
+
execute_in(env: 'staging') { hello() }
|
|
9
|
+
execute_in(env: 'production') { hello() }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "creates a partial" do
|
|
13
|
+
partial("hello_partial") { hello() }
|
|
14
|
+
expect($partials["hello_partial"][:block]).to be_kind_of(Proc)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "executes a partial" do
|
|
18
|
+
expect(self).to receive(:hello).with("hello").exactly(1).times
|
|
19
|
+
partial("hello_partial") { |args| hello(args[:x]) }
|
|
20
|
+
execute_partial "hello_partial", x: 'hello'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "passes before and after messages"
|
|
24
|
+
|
|
25
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Deplomat::LocalNode do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@fixtures_dir = Dir.pwd + "/spec/fixtures"
|
|
7
|
+
@localhost = Deplomat::LocalNode.new
|
|
8
|
+
@localhost.log_to = []
|
|
9
|
+
@localhost.raise_exceptions = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:each) do
|
|
13
|
+
FileUtils.rm_r(Dir.glob("#{@fixtures_dir}/dir2/*"))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "filesystem operations" do
|
|
17
|
+
|
|
18
|
+
it "copies files to another folder" do
|
|
19
|
+
@localhost.copy("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
20
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file1")).to be_truthy
|
|
21
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file2")).to be_truthy
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "moves files and dirs to another directory" do
|
|
25
|
+
@localhost.move("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
26
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file1")).to be_truthy
|
|
27
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file2")).to be_truthy
|
|
28
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/subdir1")).to be_truthy
|
|
29
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/file1")).to be_falsy
|
|
30
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/file2")).to be_falsy
|
|
31
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/subdir1")).to be_falsy
|
|
32
|
+
|
|
33
|
+
@localhost.move("#{@fixtures_dir}/dir2/*", "#{@fixtures_dir}/dir1/")
|
|
34
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/file1")).to be_truthy
|
|
35
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/file2")).to be_truthy
|
|
36
|
+
expect(File.exist?("#{@fixtures_dir}/dir1/subdir1")).to be_truthy
|
|
37
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file1")).to be_falsy
|
|
38
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file2")).to be_falsy
|
|
39
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/subdir1")).to be_falsy
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "removes files and dirs" do
|
|
43
|
+
@localhost.copy("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
44
|
+
@localhost.remove("#{@fixtures_dir}/dir2/*")
|
|
45
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file1")).to be_falsy
|
|
46
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/file2")).to be_falsy
|
|
47
|
+
expect(File.exist?("#{@fixtures_dir}/dir2/subdir1")).to be_falsy
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "changes current directory" do
|
|
51
|
+
@localhost.cd("/etc")
|
|
52
|
+
expect(@localhost.current_path).to eq("/etc/")
|
|
53
|
+
expect(-> { @localhost.cd("/non-existent-path") }).to raise_exception(Deplomat::NoSuchPathError)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "executes commands from the current directory" do
|
|
57
|
+
@localhost.cd("#{@fixtures_dir}/dir1")
|
|
58
|
+
expect(@localhost.execute("ls")[:out]).to eq("file1\nfile2\nsubdir1\n")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "creates an empty file" do
|
|
62
|
+
@localhost.cd("#{@fixtures_dir}/dir2")
|
|
63
|
+
@localhost.touch("myfile")
|
|
64
|
+
expect(@localhost.execute("ls")[:out]).to have_files("myfile")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "creates a directory" do
|
|
68
|
+
@localhost.cd("#{@fixtures_dir}/dir2")
|
|
69
|
+
@localhost.mkdir("mydir")
|
|
70
|
+
expect(@localhost.execute("ls")[:out]).to have_files("mydir")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "creates a symlink" do
|
|
74
|
+
@localhost.cd("#{@fixtures_dir}/dir2")
|
|
75
|
+
@localhost.create_symlink("../dir1", "./")
|
|
76
|
+
expect(@localhost.execute("ls dir1")[:out]).to have_files("file1", "file2", "subdir1")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "handling status and errors" do
|
|
82
|
+
|
|
83
|
+
it "returns the status" do
|
|
84
|
+
expect(@localhost.execute("ls #{@fixtures_dir}/dir1/")[:status]).to eq(0)
|
|
85
|
+
expect(@localhost.execute("ls #{@fixtures_dir}/non-existent-dir/")[:status]).to eq(2)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "raises execution error when command fails" do
|
|
89
|
+
@localhost.raise_exceptions = true
|
|
90
|
+
expect( -> { @localhost.execute("ls #{@fixtures_dir}/non-existent-dir/")[:status] }).to raise_exception(Deplomat::ExecutionError)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe "cleaning" do
|
|
96
|
+
|
|
97
|
+
before(:each) do
|
|
98
|
+
@localhost.cd("#{@fixtures_dir}/cleaning")
|
|
99
|
+
@localhost.execute("touch file1 file2 file3")
|
|
100
|
+
@localhost.execute("mkdir current")
|
|
101
|
+
sleep(0.01)
|
|
102
|
+
@localhost.execute("mkdir dir1 dir2 dir3")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
after(:each) do
|
|
106
|
+
@localhost.execute("rm -rf #{@fixtures_dir}/cleaning/*")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "cleans all the files and dirs in a given directory" do
|
|
110
|
+
@localhost.clean
|
|
111
|
+
expect(@localhost.execute('ls')[:out]).to be_empty
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "cleans all but last 3 entries in a given directory" do
|
|
115
|
+
@localhost.clean(leave: [3, :last])
|
|
116
|
+
expect(@localhost.execute('ls')[:out]).to eq("dir1\ndir2\ndir3\n")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "cleans all, but :except entries in a given directory" do
|
|
120
|
+
@localhost.clean(except: ["current"])
|
|
121
|
+
expect(@localhost.execute('ls')[:out]).to eq("current\n")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe "git commands" do
|
|
127
|
+
|
|
128
|
+
before(:all) do
|
|
129
|
+
@fixtures_dir = Dir.pwd + "/spec/fixtures"
|
|
130
|
+
@localhost_git = Deplomat::LocalNode.new
|
|
131
|
+
@localhost_git.log_to = [:stdout]
|
|
132
|
+
@localhost_git.cd(@fixtures_dir)
|
|
133
|
+
@localhost_git.mkdir("git_repo")
|
|
134
|
+
@localhost_git.cd("git_repo")
|
|
135
|
+
@localhost_git.touch("file1")
|
|
136
|
+
@localhost_git.execute("git init && git add .")
|
|
137
|
+
@localhost_git.execute("git remote add origin deploy@deplomat-test-node:/home/deploy/deplomat/.git_repo")
|
|
138
|
+
@remote_node = Deplomat::RemoteNode.new(host: "deplomat-test-node", user: "deploy")
|
|
139
|
+
@remote_node.log_to = [:stdout]
|
|
140
|
+
@remote_node.execute("cd ~/deplomat && mkdir .git_repo && cd .git_repo && git --bare init")
|
|
141
|
+
@localhost_git.cd("#{@fixtures_dir}/git_repo")
|
|
142
|
+
@localhost_git.execute("git commit -m \"Initial commit\"")
|
|
143
|
+
|
|
144
|
+
@localhost_git.raise_exceptions = true
|
|
145
|
+
@remote_node.raise_exceptions = true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
after(:all) do
|
|
149
|
+
@remote_node.remove("~/deplomat/.git_repo")
|
|
150
|
+
@localhost_git.remove("#{@fixtures_dir}/git_repo")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "pushes to remote" do
|
|
154
|
+
expect(@localhost_git.git_push[:status]).to eq(0)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "pulls from remote" do
|
|
158
|
+
expect(@localhost_git.git_pull[:status]).to eq(0)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "merges branches" do
|
|
162
|
+
@localhost_git.execute("git checkout -b dev1")
|
|
163
|
+
@localhost_git.touch("file2")
|
|
164
|
+
@localhost_git.execute("git add . && git commit -m \"adding file2\"")
|
|
165
|
+
@localhost_git.execute("git checkout master")
|
|
166
|
+
expect(@localhost_git.git_merge("dev1", "master")[:status]).to eq(0)
|
|
167
|
+
expect(File.exist?("#{@fixtures_dir}/git_repo/file2")).to be_truthy
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "checks out into a branch" do
|
|
171
|
+
@localhost_git.execute("git branch dev2")
|
|
172
|
+
@localhost_git.touch("file3")
|
|
173
|
+
@localhost_git.execute("git add . && git commit -m \"adding file3\"")
|
|
174
|
+
@localhost_git.git_checkout("dev2")
|
|
175
|
+
expect(File.exist?("#{@fixtures_dir}/git_repo/file3")).to be_falsy
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
end
|
data/spec/node_spec.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Deplomat::Node do
|
|
4
|
+
|
|
5
|
+
describe "logging" do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@fixtures_dir = Dir.pwd + "/spec/fixtures"
|
|
9
|
+
@node = Deplomat::Node.new(logfile: "#{@fixtures_dir}/deplomat.log", log_to: [:file, :stdout])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:each) do
|
|
13
|
+
@node.log_to = []
|
|
14
|
+
@node.remove("#{@fixtures_dir}/deplomat.log")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "logs to the stdout" do
|
|
18
|
+
expect($stdout).to receive(:print).exactly(4).times
|
|
19
|
+
@node.execute("ls #{@fixtures_dir}/dir1")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "logs to a log file" do
|
|
23
|
+
@node.log_to = [:file]
|
|
24
|
+
@node.execute("ls #{@fixtures_dir}/dir1")
|
|
25
|
+
log = File.readlines("#{@fixtures_dir}/deplomat.log")
|
|
26
|
+
expect(log.size).to eq(4)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "puts additional messages into the terminal when required" do
|
|
30
|
+
expect($stdout).to receive(:print).once.with("hello\n".white)
|
|
31
|
+
expect($stdout).to receive(:print).once.with("--> ls /home/roman/Dropbox/Work/my_libs/deplomat/spec/fixtures/dir1\n".white)
|
|
32
|
+
expect($stdout).to receive(:print).once.with(" file1\n".light_black)
|
|
33
|
+
expect($stdout).to receive(:print).once.with(" file2\n".light_black)
|
|
34
|
+
expect($stdout).to receive(:print).once.with(" subdir1\n".light_black)
|
|
35
|
+
expect($stdout).to receive(:print).once.with("bye\n".white)
|
|
36
|
+
@node.execute("ls #{@fixtures_dir}/dir1", message: ["hello", "bye"])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Deplomat::RemoteNode do
|
|
4
|
+
|
|
5
|
+
before(:all) do
|
|
6
|
+
@fixtures_dir = "/home/deploy/deplomat"
|
|
7
|
+
@local_fixtures_dir = Dir.pwd + "/spec/fixtures"
|
|
8
|
+
@node = Deplomat::RemoteNode.new(host: "deplomat-test-node", user: "deploy")
|
|
9
|
+
@node.log_to = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
@node.raise_exceptions = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after(:all) do
|
|
17
|
+
@node.close
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
after(:each) do
|
|
21
|
+
@node.raise_exceptions = false
|
|
22
|
+
@node.execute("rm -rf #{@fixtures_dir}/dir2/*")
|
|
23
|
+
@node.execute("rm -rf #{@fixtures_dir}/uploaded1/*")
|
|
24
|
+
@node.execute("rm -rf #{@fixtures_dir}/uploaded2/*")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "filesystem operations" do
|
|
28
|
+
|
|
29
|
+
it "copies files to another folder" do
|
|
30
|
+
@node.copy("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
31
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir2/")[:out]).to have_files("file1", "file2", "subdir1")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "moves files and dirs from one directory into another" do
|
|
35
|
+
@node.move("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
36
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir1/")[:out]).to eq("")
|
|
37
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir2/")[:out]).to have_files("file1", "file2", "subdir1")
|
|
38
|
+
@node.move("#{@fixtures_dir}/dir2/*", "#{@fixtures_dir}/dir1/")
|
|
39
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir2/")[:out]).to eq("")
|
|
40
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir1/")[:out]).to have_files("file1", "file2", "subdir1")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "removes files and dirs" do
|
|
44
|
+
@node.copy("#{@fixtures_dir}/dir1/*", "#{@fixtures_dir}/dir2/")
|
|
45
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir2/")[:out]).to have_files("file1", "file2", "subdir1")
|
|
46
|
+
@node.remove("#{@fixtures_dir}/dir2/*")
|
|
47
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir2/")[:out]).to eq("")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "uploads files from localhost to the node" do
|
|
51
|
+
@node.upload("#{@local_fixtures_dir}/upload1/*", "#{@fixtures_dir}/uploaded1/")
|
|
52
|
+
expect(@node.execute("ls #{@fixtures_dir}/uploaded1/")[:out]).to have_files("uploaded_file1", "uploaded_file2")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "changes current directory" do
|
|
56
|
+
#@node.cd("/etc")
|
|
57
|
+
#expect(@node.current_path).to eq("/etc/")
|
|
58
|
+
expect(-> { @node.cd("/non-existent-path") }).to raise_exception(Deplomat::NoSuchPathError)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "executes commands from the current directory" do
|
|
62
|
+
@node.cd("#{@fixtures_dir}/dir1")
|
|
63
|
+
expect(@node.execute("ls")[:out]).to have_files("file1", "file2", "subdir1")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "creates an empty file" do
|
|
67
|
+
@node.cd("#{@fixtures_dir}/dir2")
|
|
68
|
+
@node.touch("myfile")
|
|
69
|
+
expect(@node.execute("ls")[:out]).to have_files("myfile")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "creates a directory" do
|
|
73
|
+
@node.cd("#{@fixtures_dir}/dir2")
|
|
74
|
+
@node.mkdir("mydir")
|
|
75
|
+
expect(@node.execute("ls")[:out]).to have_files("mydir")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "creates a symlink" do
|
|
79
|
+
@node.cd("#{@fixtures_dir}/dir2")
|
|
80
|
+
@node.create_symlink("../dir1", "./")
|
|
81
|
+
expect(@node.execute("ls dir1")[:out]).to have_files("file1", "file2", "subdir1")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe "handling status and errors" do
|
|
87
|
+
|
|
88
|
+
it "returns the status" do
|
|
89
|
+
@node.raise_exceptions = false
|
|
90
|
+
expect(@node.execute("ls #{@fixtures_dir}/dir1/")[:status]).to eq(0)
|
|
91
|
+
expect(@node.execute("ls #{@fixtures_dir}/non-existent-dir/")[:status]).to eq(2)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "raises execution error when command fails" do
|
|
95
|
+
@node.raise_exceptions = true
|
|
96
|
+
expect( -> { @node.execute("ls #{@fixtures_dir}/non-existent-dir/")[:status] }).to raise_exception(Deplomat::ExecutionError)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "cleaning" do
|
|
102
|
+
|
|
103
|
+
before(:each) do
|
|
104
|
+
@node.cd("#{@fixtures_dir}/cleaning")
|
|
105
|
+
@node.execute("touch file1 file2 file3")
|
|
106
|
+
@node.execute("mkdir current")
|
|
107
|
+
sleep(0.01)
|
|
108
|
+
@node.execute("mkdir dir1 dir2 dir3")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
after(:each) do
|
|
112
|
+
@node.execute("rm -rf #{@fixtures_dir}/cleaning/*")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "cleans all the files and dirs in a given directory" do
|
|
116
|
+
@node.clean
|
|
117
|
+
expect(@node.execute('ls')[:out]).to be_empty
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "cleans all but last 3 entries in a given directory" do
|
|
121
|
+
@node.clean(leave: [3, :last])
|
|
122
|
+
expect(@node.execute('ls')[:out]).to eq("dir1\ndir2\ndir3\n")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "cleans all, but :except entries in a given directory" do
|
|
126
|
+
@node.clean(except: ["current"])
|
|
127
|
+
expect(@node.execute('ls')[:out]).to eq("current\n")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rspec/expectations'
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require_relative "../lib/deplomat"
|
|
4
|
+
|
|
5
|
+
RSpec::Matchers.define :have_files do |*expected|
|
|
6
|
+
match do |actual|
|
|
7
|
+
actual = actual.split("\n")
|
|
8
|
+
actual.pop if actual.last == ""
|
|
9
|
+
expected.each do |e|
|
|
10
|
+
return false unless actual.include?(e)
|
|
11
|
+
end
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
failure_message do |actual|
|
|
15
|
+
"expected these files in the directory:\n\t#{actual.split("\n").inspect}\nwould include all of these:\n\t#{expected.inspect}"
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: deplomat
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Roman Snitko
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-07-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: sys-proctable
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: colorize
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: jeweler
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
description: Stack agnostic deployment system that uses bash and ssh commands
|
|
84
|
+
email: roman.snitko@gmail.com
|
|
85
|
+
executables: []
|
|
86
|
+
extensions: []
|
|
87
|
+
extra_rdoc_files:
|
|
88
|
+
- LICENSE.txt
|
|
89
|
+
- README.rdoc
|
|
90
|
+
files:
|
|
91
|
+
- ".document"
|
|
92
|
+
- ".rspec"
|
|
93
|
+
- Gemfile
|
|
94
|
+
- Gemfile.lock
|
|
95
|
+
- LICENSE.txt
|
|
96
|
+
- README.rdoc
|
|
97
|
+
- Rakefile
|
|
98
|
+
- VERSION
|
|
99
|
+
- deplomat.gemspec
|
|
100
|
+
- examples/simple_deploy.rb
|
|
101
|
+
- lib/deplomat.rb
|
|
102
|
+
- lib/deplomat/directives.rb
|
|
103
|
+
- lib/deplomat/exceptions.rb
|
|
104
|
+
- lib/deplomat/local_node.rb
|
|
105
|
+
- lib/deplomat/node.rb
|
|
106
|
+
- lib/deplomat/remote_node.rb
|
|
107
|
+
- spec/directives_spec.rb
|
|
108
|
+
- spec/fixtures/cleaning/.keep
|
|
109
|
+
- spec/fixtures/dir1/file1
|
|
110
|
+
- spec/fixtures/dir1/file2
|
|
111
|
+
- spec/fixtures/upload1/uploaded_file1
|
|
112
|
+
- spec/fixtures/upload1/uploaded_file2
|
|
113
|
+
- spec/local_node_spec.rb
|
|
114
|
+
- spec/node_spec.rb
|
|
115
|
+
- spec/remote_node_spec.rb
|
|
116
|
+
- spec/spec_helper.rb
|
|
117
|
+
homepage: http://github.com/snitko/deplomat
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata: {}
|
|
121
|
+
post_install_message:
|
|
122
|
+
rdoc_options: []
|
|
123
|
+
require_paths:
|
|
124
|
+
- lib
|
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '0'
|
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">="
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '0'
|
|
135
|
+
requirements: []
|
|
136
|
+
rubyforge_project:
|
|
137
|
+
rubygems_version: 2.5.1
|
|
138
|
+
signing_key:
|
|
139
|
+
specification_version: 4
|
|
140
|
+
summary: Stack agnostic deployment system that uses bash and ssh commands
|
|
141
|
+
test_files: []
|