avodeploy 0.4.2 → 0.5
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 +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,10 +17,10 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
module AvoDeploy
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
class CommandExecutionResult
|
21
|
+
attr_accessor :stdin
|
22
|
+
attr_accessor :stdout
|
23
|
+
attr_accessor :stderr
|
24
|
+
attr_accessor :retval
|
25
|
+
end
|
26
26
|
end
|
data/lib/avodeploy/config.rb
CHANGED
@@ -17,103 +17,124 @@
|
|
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
|
-
|
20
|
+
class Config
|
21
|
+
|
22
|
+
attr_reader :config
|
23
|
+
attr_reader :stages
|
24
|
+
attr_reader :targets
|
25
|
+
attr_reader :loaded_stage
|
26
|
+
|
27
|
+
# Intializes the config object
|
28
|
+
def initialize
|
29
|
+
@config = setup_config_defaults
|
30
|
+
@stages = {}
|
31
|
+
@targets = {}
|
32
|
+
@loaded_stage = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets a configuration item
|
36
|
+
#
|
37
|
+
# @param key [Symbol] configuration key
|
38
|
+
# @param value [mixed] configuration value
|
39
|
+
def set(key, value)
|
40
|
+
# compatibility with releases < 0.5
|
41
|
+
if key == :strategy
|
42
|
+
inherit_strategy(value)
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
@config[key] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Loads a strategy. Because the strategy files are loaded in
|
50
|
+
# the specified order, it's possible to override tasks.
|
51
|
+
# This is how inheritance of strategies is realized.
|
52
|
+
#
|
53
|
+
# @param strategy [Symbol] the strategy to load
|
54
|
+
def inherit_strategy(strategy)
|
55
|
+
AvoDeploy::Deployment.instance.log.debug "Loading deployment strategy #{strategy.to_s}..."
|
56
|
+
|
57
|
+
strategy_file_path = File.dirname(__FILE__) + "/strategy/#{strategy.to_s}.rb"
|
58
|
+
|
59
|
+
if File.exist?(strategy_file_path)
|
60
|
+
require strategy_file_path
|
61
|
+
else
|
62
|
+
raise RuntimeError, "The requested strategy '#{strategy.to_s}' does not exist"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a configuration item if set
|
67
|
+
#
|
68
|
+
# @param key [Symbol] configuration key
|
69
|
+
# @return [mixed] configuration value
|
70
|
+
def get(key)
|
71
|
+
raise ArgumentError, "key #{key} is not set" unless @config.has_key?(key)
|
72
|
+
|
73
|
+
@config[key]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Defines a task
|
77
|
+
#
|
78
|
+
# @param name [Symbol] task name
|
79
|
+
# @param options [Hash] task options
|
80
|
+
# @param block [Block] the code to be executed when the task is started
|
81
|
+
def task(name, options = {}, &block)
|
82
|
+
AvoDeploy::Deployment.instance.log.debug "registering task #{name}..."
|
83
|
+
AvoDeploy::Deployment.instance.task_manager.add_task(name, options, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Defines a stage
|
87
|
+
#
|
88
|
+
# @param name [Symbol] stage name
|
89
|
+
# @param options [Hash] stage options
|
90
|
+
# @param block [Block] the stage configuration
|
91
|
+
def setup_stage(name, options = {}, &block)
|
92
|
+
stages[name] = ''
|
93
|
+
|
94
|
+
if options.has_key?(:desc)
|
95
|
+
stages[name] = options[:desc]
|
96
|
+
end
|
97
|
+
|
98
|
+
if name.to_s == get(:stage).to_s
|
99
|
+
@loaded_stage = name
|
100
|
+
|
101
|
+
instance_eval(&block)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Defines a deployment target
|
106
|
+
#
|
107
|
+
# @param name [Symbol] the deployment targets' name
|
108
|
+
# @param options [Hash] target options
|
109
|
+
def target(name, options = {})
|
110
|
+
@targets[name] = AvoDeploy::Target.new(name, options)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Merges the configuration with another config hash
|
114
|
+
#
|
115
|
+
# @param other_config [Hash] configuration hash
|
116
|
+
# @return [Hash] the merged hash
|
117
|
+
def merge(other_config)
|
118
|
+
@config.merge(other_config)
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
# Sets up configuration defaults
|
123
|
+
#
|
124
|
+
# @return [Hash] configuration defaults
|
125
|
+
def setup_config_defaults
|
126
|
+
{
|
127
|
+
:project_name => '',
|
128
|
+
:scm => :git,
|
129
|
+
:repo_url => '',
|
130
|
+
:branch => :master,
|
131
|
+
:stage => :production,
|
132
|
+
:strategy => nil,
|
133
|
+
:ignore_files => [],
|
134
|
+
:log_level => Logger::WARN,
|
135
|
+
:force_tag => false,
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
119
140
|
end
|
@@ -19,27 +19,27 @@
|
|
19
19
|
class String
|
20
20
|
|
21
21
|
# Paints the string red on CLI
|
22
|
-
|
22
|
+
def red
|
23
23
|
"\033[31m#{self}\033[0m"
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# Paints the string green on CLI
|
27
27
|
def green
|
28
|
-
"\033[32m#{self}\033[0m"
|
28
|
+
"\033[32m#{self}\033[0m"
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Paints the string cyan on CLI
|
32
32
|
def cyan
|
33
|
-
"\033[36m#{self}\033[0m"
|
33
|
+
"\033[36m#{self}\033[0m"
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Paints the string yellow on CLI
|
37
37
|
def yellow
|
38
|
-
"\e[33m#{self}\e[0m"
|
38
|
+
"\e[33m#{self}\e[0m"
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Paints the string gray on CLI
|
42
42
|
def gray
|
43
|
-
"\033[37m#{self}\033[0m"
|
43
|
+
"\033[37m#{self}\033[0m"
|
44
44
|
end
|
45
45
|
end
|
data/lib/avodeploy/deployment.rb
CHANGED
@@ -17,60 +17,57 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
module AvoDeploy
|
20
|
-
|
20
|
+
class Deployment
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
attr_reader :log_file
|
22
|
+
attr_accessor :config
|
23
|
+
attr_accessor :task_manager
|
24
|
+
attr_reader :log
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
# Initializes the deployment
|
27
|
+
def initialize
|
28
|
+
@stages = {}
|
29
|
+
@task_manager = AvoDeploy::Task::TaskManager.new
|
30
|
+
@config = AvoDeploy::Config.new
|
32
31
|
|
33
|
-
|
34
|
-
|
32
|
+
@log = ::Logger.new(STDOUT)
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
# Configures the deployment
|
36
|
+
#
|
37
|
+
# @param block [Block] configuration block
|
38
|
+
def self.configure(&block)
|
39
|
+
@instance = self.instance
|
40
|
+
@instance.config.instance_eval(&block)
|
42
41
|
|
43
|
-
|
44
|
-
|
42
|
+
# @todo check config and throw exception
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
# Returns the deployment instance
|
46
|
+
#
|
47
|
+
# @return [Deployment] the deployment instance
|
48
|
+
def self.instance
|
49
|
+
if @instance.nil?
|
50
|
+
@instance = self.new
|
51
|
+
end
|
53
52
|
|
54
|
-
|
55
|
-
|
53
|
+
@instance
|
54
|
+
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
# Handles exceptions
|
57
|
+
#
|
58
|
+
# @param e [Exception] the exception to handle
|
59
|
+
def handle_abort(e)
|
60
|
+
if log.level == ::Logger::DEBUG
|
61
|
+
raise e
|
62
|
+
elsif e.class != SystemExit
|
63
|
+
@log.error e.message.red
|
64
|
+
@log.info 'cleaning up...'
|
64
65
|
|
65
|
-
|
66
|
+
task_manager.invoke_task_oneshot(:cleanup_local)
|
67
|
+
end
|
68
|
+
|
69
|
+
Kernel.exit(true)
|
70
|
+
end
|
66
71
|
|
67
|
-
|
68
|
-
# task_manager.invoke_task_oneshot(:cleanup_remote)
|
69
|
-
#end
|
70
|
-
|
71
|
-
Kernel.exit(true)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
72
|
+
end
|
76
73
|
end
|
data/lib/avodeploy/multi_io.rb
CHANGED
@@ -17,24 +17,24 @@
|
|
17
17
|
=end
|
18
18
|
|
19
19
|
module AvoDeploy
|
20
|
-
|
20
|
+
class MultiIO
|
21
21
|
# Initializes the MultiIO with various target
|
22
22
|
#
|
23
23
|
# @param targets [Array] targets to handle
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
def initialize(*targets)
|
25
|
+
@targets = targets
|
26
|
+
end
|
27
27
|
|
28
28
|
# Writes to all targets
|
29
29
|
#
|
30
30
|
# @param args [mixed] arguments
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
def write(*args)
|
32
|
+
@targets.each { |t| t.write(*args) }
|
33
|
+
end
|
34
34
|
|
35
35
|
# Closes the targets
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def close
|
37
|
+
@targets.each(&:close)
|
38
|
+
end
|
39
|
+
end
|
40
40
|
end
|
@@ -0,0 +1,111 @@
|
|
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
|
+
module AvoDeploy
|
20
|
+
module ScmProvider
|
21
|
+
class BzrScmProvider < ScmProvider
|
22
|
+
|
23
|
+
# Initializes the provider
|
24
|
+
#
|
25
|
+
# @param env [TaskExecutionEnvironment] Environment for the commands to be executed in
|
26
|
+
def initialize(env)
|
27
|
+
super(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Checks out repository code from a system and switches to the given branch
|
31
|
+
#
|
32
|
+
# @param url [String] the repository location
|
33
|
+
# @param local_dir [String] path to the working copy
|
34
|
+
# @param branch [String] the branch to check out, not used here because branches are encoded in the bazaar urls
|
35
|
+
# @param tag [String] tag to check out
|
36
|
+
def checkout_from_remote(url, local_dir, branch, tag = nil)
|
37
|
+
cmd = "bzr co --lightweight #{url} #{local_dir}"
|
38
|
+
|
39
|
+
if tag.nil? == false
|
40
|
+
cmd += " -r tag:#{tag}"
|
41
|
+
end
|
42
|
+
|
43
|
+
res = @env.command(cmd)
|
44
|
+
raise RuntimeError, "Could not checkout from bzr url #{url}" unless res.retval == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the current revision of the working copy
|
48
|
+
#
|
49
|
+
# @return [String] the current revision of the working copy
|
50
|
+
def revision
|
51
|
+
res = @env.command('bzr revno --tree')
|
52
|
+
|
53
|
+
res.stdout.gsub("\n", '')
|
54
|
+
end
|
55
|
+
|
56
|
+
# Finds files that differ between two revisions and returns them
|
57
|
+
# as a array
|
58
|
+
#
|
59
|
+
# @param rev1 [String] sha1
|
60
|
+
# @param rev2 [String] sha1
|
61
|
+
#
|
62
|
+
# @return [Array]
|
63
|
+
def diff_files_between_revisions(rev1, rev2)
|
64
|
+
# same revision, nothing changed
|
65
|
+
if rev1 == rev2
|
66
|
+
return []
|
67
|
+
end
|
68
|
+
|
69
|
+
# exclude rev1 itself
|
70
|
+
rev1 = rev1.to_i + 1
|
71
|
+
|
72
|
+
res = @env.command("bzr log -r#{rev1}..#{rev2} -v --short")
|
73
|
+
|
74
|
+
files = []
|
75
|
+
|
76
|
+
res.stdout.lines.each do |line|
|
77
|
+
line.strip!
|
78
|
+
next unless line.start_with?('A') || line.start_with?('M')
|
79
|
+
|
80
|
+
files << line[3, line.length]
|
81
|
+
end
|
82
|
+
|
83
|
+
files
|
84
|
+
end
|
85
|
+
|
86
|
+
# Finds files unknown file in the working directory and returns them
|
87
|
+
# as a array
|
88
|
+
#
|
89
|
+
# @return [Array]
|
90
|
+
def unknown_files_in_workdir
|
91
|
+
res = @env.command("bzr status --short | grep '^? ' | awk '/^? (.*)$/ {print $2}'")
|
92
|
+
res.stdout.lines
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns scm files to be executed in the deployment process
|
96
|
+
#
|
97
|
+
# @return [Array] array of scm control files
|
98
|
+
def scm_files
|
99
|
+
['.bzr', '.bzrignore']
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the scm tools that have to be installed on specific systems
|
103
|
+
#
|
104
|
+
# @return [Array] array of utilities
|
105
|
+
def cli_utils
|
106
|
+
['bzr']
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|