avodeploy 0.4.2 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/README.md +1 -1
- data/avodeploy.gemspec +16 -14
- data/bin/avo +101 -104
- data/lib/avodeploy.rb +1 -0
- data/lib/avodeploy/bootstrap.rb +65 -73
- data/lib/avodeploy/command_execution_result.rb +6 -6
- data/lib/avodeploy/config.rb +120 -99
- data/lib/avodeploy/core_ext/string_colors.rb +9 -9
- data/lib/avodeploy/deployment.rb +43 -46
- data/lib/avodeploy/multi_io.rb +11 -11
- data/lib/avodeploy/scm_provider/bzr_scm_provider.rb +111 -0
- data/lib/avodeploy/scm_provider/git_scm_provider.rb +66 -45
- data/lib/avodeploy/scm_provider/scm_provider.rb +57 -43
- data/lib/avodeploy/skel/manifest_template.rb.erb +127 -116
- data/lib/avodeploy/strategy/base.rb +7 -7
- data/lib/avodeploy/strategy/local_copy.rb +99 -79
- data/lib/avodeploy/strategy/local_copy_partial.rb +86 -0
- data/lib/avodeploy/target.rb +32 -31
- data/lib/avodeploy/task/local_task_execution_environment.rb +112 -107
- data/lib/avodeploy/task/remote_task_execution_environment.rb +90 -94
- data/lib/avodeploy/task/task.rb +53 -42
- data/lib/avodeploy/task/task_dependency.rb +5 -5
- data/lib/avodeploy/task/task_execution_environment.rb +70 -59
- data/lib/avodeploy/task/task_manager.rb +187 -165
- data/lib/avodeploy/version.rb +1 -1
- metadata +7 -5
@@ -17,13 +17,13 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
AvoDeploy::Deployment.configure do
|
20
|
-
|
21
|
-
task :deploy, visibility: :public do
|
22
|
-
# stub for cli calls
|
23
|
-
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
task :deploy, visibility: :public do
|
22
|
+
# stub for cli calls
|
23
|
+
end
|
24
|
+
|
25
|
+
task :check_ssh_connection, before: :deploy, scope: :remote, visibility: :private do
|
26
|
+
log.info 'checking ssh connection'
|
27
|
+
end
|
28
28
|
|
29
29
|
end
|
@@ -18,84 +18,104 @@
|
|
18
18
|
|
19
19
|
AvoDeploy::Deployment.configure do
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
21
|
+
inherit_strategy :base
|
22
|
+
|
23
|
+
task :check_targets, before: :deploy do
|
24
|
+
raise RuntimeError, 'No target systems are configured' if targets.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
task :check_local_tools, before: :deploy do
|
28
|
+
check_util_availability ['tar'].concat(@scm.cli_utils)
|
29
|
+
end
|
30
|
+
|
31
|
+
task :check_remote_system, before: :deploy, scope: :remote do
|
32
|
+
res = command 'uname -a'
|
33
|
+
|
34
|
+
if res.stdout.include?('Linux') == false
|
35
|
+
raise RuntimeError, 'Only linux target systems are supported right now.'
|
36
|
+
end
|
37
|
+
|
38
|
+
check_util_availability ['tar']
|
39
|
+
end
|
40
|
+
|
41
|
+
task :check_temp_existance, after: :deploy do
|
42
|
+
if File.exist?('.avocado-tmp')
|
43
|
+
raise RuntimeError, 'The avocado tmp directory (.avocado-tmp) does already exist. That may indicate that another deployment is already running.'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
task :switch_to_temp_dir, after: :check_temp_existance do
|
48
|
+
command 'mkdir .avocado-tmp/'
|
49
|
+
chdir '.avocado-tmp/'
|
50
|
+
end
|
51
|
+
|
52
|
+
task :checkout_from_scm, after: :switch_to_temp_dir do
|
53
|
+
tag = nil
|
54
|
+
|
55
|
+
if @options.nil? == false && @options[:tag].nil? == false
|
56
|
+
tag = @options[:tag]
|
57
|
+
end
|
58
|
+
|
59
|
+
if get(:force_tag) && tag.nil?
|
60
|
+
raise RuntimeError, 'Deployment requires a tag but none was submitted'
|
61
|
+
end
|
62
|
+
|
63
|
+
@scm.checkout_from_remote(get(:repo_url), 'working-copy', get(:branch), tag)
|
64
|
+
end
|
65
|
+
|
66
|
+
task :chdir_to_working_copy, after: :checkout_from_scm do
|
67
|
+
chdir 'working-copy/'
|
68
|
+
end
|
69
|
+
|
70
|
+
task :get_scm_info, after: :chdir_to_working_copy do
|
71
|
+
set :revision, @scm.revision
|
72
|
+
end
|
73
|
+
|
74
|
+
task :create_revision_file, after: :get_scm_info do
|
75
|
+
command "echo '#{get(:revision)}' > REVISION"
|
76
|
+
end
|
77
|
+
|
78
|
+
task :create_deployment_tarball, after: :create_revision_file do
|
79
|
+
files_to_delete = ['Avofile'].concat(get(:ignore_files)).concat(@scm.scm_files)
|
80
|
+
|
81
|
+
exclude_param = ''
|
82
|
+
|
83
|
+
files_to_delete.each do |file|
|
84
|
+
exclude_param += " --exclude='^#{file}$'"
|
85
|
+
end
|
86
|
+
|
87
|
+
command "tar cfz ../deploy.tar.gz #{exclude_param} ."
|
88
|
+
end
|
89
|
+
|
90
|
+
task :switch_to_parent_dir, after: :create_deployment_tarball do
|
91
|
+
chdir '../'
|
92
|
+
end
|
93
|
+
|
94
|
+
task :upload, after: :switch_to_parent_dir do
|
95
|
+
targets.each_pair do |key, target|
|
96
|
+
copy_to_target(target, 'deploy.tar.gz', '/tmp/deploy.tar.gz')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
task :create_deploy_dir, after: :upload, scope: :remote do
|
101
|
+
command "if [ ! -d '#{get(:deploy_dir)}' ]; then mkdir -p #{get(:deploy_dir)}; fi"
|
102
|
+
end
|
103
|
+
|
104
|
+
task :unpack, after: :create_deploy_dir, scope: :remote do
|
105
|
+
command "tar xvfz /tmp/deploy.tar.gz -C #{get(:deploy_dir)}/"
|
106
|
+
end
|
107
|
+
|
108
|
+
task :cleanup_remote, after: :unpack, scope: :remote do
|
109
|
+
command 'rm /tmp/deploy.tar.gz'
|
110
|
+
end
|
111
|
+
|
112
|
+
task :log_deployment, after: :cleanup_remote, scope: :remote do
|
113
|
+
command "echo '[#{Time.now.strftime('%Y-%m-%d %H-%M-%S')}] revision #{get(:revision)} deployed by #{ENV['USER']}' >> #{get(:log_file)}"
|
114
|
+
end
|
115
|
+
|
116
|
+
task :cleanup_local, after: :cleanup_remote do
|
117
|
+
chdir '../'
|
118
|
+
command 'rm -rf .avocado-tmp'
|
119
|
+
end
|
100
120
|
|
101
121
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
=begin
|
2
|
+
AVOCADO
|
3
|
+
The flexible and easy to use deployment framework for web applications
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it and/or
|
6
|
+
modify it under the terms of the GNU General Public License Version 2
|
7
|
+
as published by the Free Software Foundation.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program; if not, write to the Free Software
|
16
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
=end
|
18
|
+
|
19
|
+
AvoDeploy::Deployment.configure do
|
20
|
+
|
21
|
+
inherit_strategy :local_copy
|
22
|
+
|
23
|
+
task :get_deployed_revision, after: :chdir_to_working_copy, scope: :remote do
|
24
|
+
res = command "cat #{get(:deploy_dir)}/REVISION"
|
25
|
+
|
26
|
+
if get(:deployed_revisions).nil?
|
27
|
+
deployed_revisions = {}
|
28
|
+
else
|
29
|
+
deployed_revisions = get(:deployed_revisions)
|
30
|
+
end
|
31
|
+
|
32
|
+
revision = nil
|
33
|
+
|
34
|
+
if res.retval == 0
|
35
|
+
# file exists
|
36
|
+
revision = res.stdout.lines.first.strip
|
37
|
+
end
|
38
|
+
|
39
|
+
deployed_revisions[get(:name)] = revision
|
40
|
+
set(:deployed_revisions, deployed_revisions)
|
41
|
+
end
|
42
|
+
|
43
|
+
task :create_deployment_tarball, after: :create_revision_file do
|
44
|
+
new_revision = get(:revision)
|
45
|
+
|
46
|
+
get(:deployed_revisions).each_pair do |target_name, deployed_revision|
|
47
|
+
# determine, which files to exclude
|
48
|
+
files_to_delete = ['Avofile'].concat(get(:ignore_files)).concat(@scm.scm_files)
|
49
|
+
|
50
|
+
exclude_param = ''
|
51
|
+
|
52
|
+
files_to_delete.each do |file|
|
53
|
+
exclude_param += " --exclude='^#{file}$'"
|
54
|
+
end
|
55
|
+
|
56
|
+
# create deployment archive
|
57
|
+
if deployed_revision.nil?
|
58
|
+
# create full deployment archive
|
59
|
+
command "tar -czvf ../deploy_#{target_name.to_s}.tar.gz #{exclude_param} ."
|
60
|
+
else
|
61
|
+
# create partial deployment archive
|
62
|
+
diff_files = @scm.diff_files_between_revisions(deployed_revision, new_revision)
|
63
|
+
|
64
|
+
# always include the revision file
|
65
|
+
# UPDATE: not longer needed because REVISION is in the unknown_files array anyway
|
66
|
+
#diff_files << 'REVISION'
|
67
|
+
|
68
|
+
# include unknown files
|
69
|
+
unknown_files = @scm.unknown_files_in_workdir
|
70
|
+
|
71
|
+
File.open("../files_#{target_name.to_s}.txt", 'w') do |f|
|
72
|
+
f << diff_files.concat(unknown_files).join("\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
command "tar cvfz ../deploy_#{target_name.to_s}.tar.gz -T ../files_#{target_name.to_s}.txt #{exclude_param}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
task :upload, after: :switch_to_parent_dir do
|
81
|
+
targets.each_pair do |key, target|
|
82
|
+
copy_to_target(target, "deploy_#{target.name.to_s}.tar.gz", '/tmp/deploy.tar.gz')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/avodeploy/target.rb
CHANGED
@@ -17,35 +17,36 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
module AvoDeploy
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
20
|
+
class Target
|
21
|
+
|
22
|
+
attr_reader :name
|
23
|
+
attr_reader :config
|
24
|
+
|
25
|
+
# Initializes the deployment target
|
26
|
+
#
|
27
|
+
# @param name [Symbol] target name
|
28
|
+
# @param config [Hash] target config
|
29
|
+
def initialize(name, config)
|
30
|
+
@name = name
|
31
|
+
@config = default_config.merge(config)
|
32
|
+
@config[:name] = name
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# Sets up the config defaults
|
37
|
+
#
|
38
|
+
# @return [Hash] config defaults
|
39
|
+
def default_config
|
40
|
+
{
|
41
|
+
:name => '',
|
42
|
+
:host => nil,
|
43
|
+
:port => 22,
|
44
|
+
:user => 'root',
|
45
|
+
:auth => :pubkey,
|
46
|
+
:deploy_dir => '/var/www/',
|
47
|
+
:log_file => '/var/www/deploy.log',
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
51
52
|
end
|
@@ -17,111 +17,116 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
module AvoDeploy
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
20
|
+
module Task
|
21
|
+
class LocalTaskExecutionEnvironment < TaskExecutionEnvironment
|
22
|
+
|
23
|
+
# Initialized the environment
|
24
|
+
#
|
25
|
+
# @param config [Hash] deployment configuration
|
26
|
+
def initialize(config)
|
27
|
+
super
|
28
|
+
|
29
|
+
@dir = Dir.pwd
|
30
|
+
end
|
31
|
+
|
32
|
+
# Checks, if all utilities are available for the deployment process
|
33
|
+
# to be executed
|
34
|
+
#
|
35
|
+
# @param utils [Array] array with utilities to check
|
36
|
+
def check_util_availability(utils)
|
37
|
+
super(utils, 'locally')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Changes the directory for commands to be executed in
|
41
|
+
#
|
42
|
+
# @param dir [String] the directory to change to
|
43
|
+
def chdir(dir)
|
44
|
+
log.debug "changing directory [#{dir.yellow}] " + "locally".cyan
|
45
|
+
|
46
|
+
Dir.chdir(dir)
|
47
|
+
@dir = Dir.pwd
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the current working directory
|
51
|
+
#
|
52
|
+
# @return [String] current working directory
|
53
|
+
def cwd
|
54
|
+
@dir
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns all target systems to deploy to
|
58
|
+
#
|
59
|
+
# @return [Hash] hash of target systems
|
60
|
+
def targets
|
61
|
+
AvoDeploy::Deployment.instance.config.targets
|
62
|
+
end
|
63
|
+
|
64
|
+
# Copies a file to a remote system (= target)
|
65
|
+
#
|
66
|
+
# @param target [Target] the target system to deploy to
|
67
|
+
# @param file [String] the local file to upload
|
68
|
+
# @param remote [String] path on the remote system
|
69
|
+
def copy_to_target(target, file, remote)
|
70
|
+
log = AvoDeploy::Deployment.instance.log
|
71
|
+
|
72
|
+
log.info "started upload of file #{file} to #{target.name}"
|
73
|
+
|
74
|
+
Net::SSH.start(
|
75
|
+
target.config[:host],
|
76
|
+
target.config[:user],
|
77
|
+
{
|
78
|
+
:port => target.config[:port]
|
79
|
+
}
|
80
|
+
) do |session|
|
81
|
+
session.scp.upload!(file, remote, :recursive => true) do |ch, name, sent, total|
|
82
|
+
percentage = 0
|
83
|
+
|
84
|
+
begin
|
85
|
+
percentage = (sent.to_f * 100 / total.to_f).to_i
|
86
|
+
rescue Exception => e
|
87
|
+
AvoDeploy::Deployment.instance.handle_abort(e)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
log.info "upload completed"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Executes a command locally in the current directory
|
96
|
+
#
|
97
|
+
# @param cmd [String] the command to execute
|
98
|
+
# @return [CommandExecutionResult] result of the command exection
|
99
|
+
def command(cmd)
|
100
|
+
log = AvoDeploy::Deployment.instance.log
|
101
|
+
|
102
|
+
log.info "Executing [" + cmd.yellow + "] " + "locally".cyan
|
103
|
+
|
104
|
+
result = AvoDeploy::CommandExecutionResult.new
|
105
|
+
|
106
|
+
begin
|
107
|
+
stdout, stderr, status = ::Open3.capture3(cmd, :chdir => cwd())
|
108
|
+
|
109
|
+
result.stdin = cmd
|
110
|
+
result.stdout = stdout
|
111
|
+
result.stderr = stderr
|
112
|
+
result.retval = status.exitstatus
|
113
|
+
|
114
|
+
if result.stdout.nil? == false && result.stdout.empty? == false
|
115
|
+
log.debug 'Stdout: ' + result.stdout.green
|
116
|
+
end
|
117
|
+
|
118
|
+
if result.stderr.nil? == false && result.stderr.empty? == false
|
119
|
+
log.debug 'Stderr: ' + result.stderr.red
|
120
|
+
end
|
121
|
+
|
122
|
+
log.debug 'Retval: ' + result.retval.to_s
|
123
|
+
rescue Exception => e
|
124
|
+
handle_abort e
|
125
|
+
end
|
126
|
+
|
127
|
+
result
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
127
132
|
end
|