forked from capistrano/capistrano
/
callbacks.rb
132 lines (118 loc) · 5.05 KB
/
callbacks.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
127
128
129
130
131
132
require 'capistrano/callback'
module Capistrano
class Configuration
module Callbacks
def self.included(base) #:nodoc:
%w(initialize invoke_task_directly).each do |method|
base.send :alias_method, "#{method}_without_callbacks", method
base.send :alias_method, method, "#{method}_with_callbacks"
end
end
# The hash of callbacks that have been registered for this configuration
attr_reader :callbacks
def initialize_with_callbacks(*args) #:nodoc:
initialize_without_callbacks(*args)
@callbacks = {}
end
def invoke_task_directly_with_callbacks(task) #:nodoc:
trigger :before, task
result = invoke_task_directly_without_callbacks(task)
trigger :after, task
return result
end
# Defines a callback to be invoked before the given task. You must
# specify the fully-qualified task name, both for the primary task, and
# for the task(s) to be executed before. Alternatively, you can pass a
# block to be executed before the given task.
#
# before "deploy:update_code", :record_difference
# before :deploy, "custom:log_deploy"
# before :deploy, :this, "then:this", "and:then:this"
# before :some_task do
# puts "an anonymous hook!"
# end
#
# This just provides a convenient interface to the more general #on method.
def before(task_name, *args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
args << options.merge(:only => task_name)
on :before, *args, &block
end
# Defines a callback to be invoked after the given task. You must
# specify the fully-qualified task name, both for the primary task, and
# for the task(s) to be executed after. Alternatively, you can pass a
# block to be executed after the given task.
#
# after "deploy:update_code", :log_difference
# after :deploy, "custom:announce"
# after :deploy, :this, "then:this", "and:then:this"
# after :some_task do
# puts "an anonymous hook!"
# end
#
# This just provides a convenient interface to the more general #on method.
def after(task_name, *args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
args << options.merge(:only => task_name)
on :after, *args, &block
end
# Defines one or more callbacks to be invoked in response to some event.
# Capistrano currently understands the following events:
#
# * :before, triggered before a task is invoked
# * :after, triggered after a task is invoked
# * :start, triggered before a top-level task is invoked via the command-line
# * :finish, triggered when a top-level task completes
# * :load, triggered after all recipes have loaded
# * :exit, triggered after all tasks have completed
#
# Specify the (fully-qualified) task names that you want invoked in
# response to the event. Alternatively, you can specify a block to invoke
# when the event is triggered. You can also pass a hash of options as the
# last parameter, which may include either of two keys:
#
# * :only, should specify an array of task names. Restricts this callback
# so that it will only fire when the event applies to those tasks.
# * :except, should specify an array of task names. Restricts this callback
# so that it will never fire when the event applies to those tasks.
#
# Usage:
#
# on :before, "some:hook", "another:hook", :only => "deploy:update"
# on :after, "some:hook", :except => "deploy:symlink"
# on :before, "global:hook"
# on :after, :only => :deploy do
# puts "after deploy here"
# end
def on(event, *args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
callbacks[event] ||= []
if args.empty? && block.nil?
raise ArgumentError, "please specify either a task name or a block to invoke"
elsif args.any? && block
raise ArgumentError, "please specify only a task name or a block, but not both"
elsif block
callbacks[event] << ProcCallback.new(block, options)
else
args.each do |name|
if name =~ /^(?:deploy\:symlink|symlink)$/
Kernel.warn("[API Change] deploy:symlink no longer exists, please use deploy:create_symlink")
end
callbacks[event] << TaskCallback.new(self, name, options)
end
end
end
# Trigger the named event for the named task. All associated callbacks
# will be fired, in the order they were defined.
def trigger(event, task=nil)
pending = Array(callbacks[event]).select { |c| c.applies_to?(task) }
if pending.any?
msg = "triggering #{event} callbacks"
msg << " for `#{task.fully_qualified_name}'" if task
logger.trace(msg)
pending.each { |callback| callback.call }
end
end
end
end
end