cape 1.0.0
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.
- data/.gemtest +0 -0
- data/.gitignore +6 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/Gemfile +18 -0
- data/History.markdown +7 -0
- data/MIT-LICENSE.markdown +10 -0
- data/README.markdown +157 -0
- data/Rakefile +49 -0
- data/cape.gemspec +36 -0
- data/features/dsl/each_rake_task/with_defined_namespace_argument.feature +43 -0
- data/features/dsl/each_rake_task/with_defined_task_argument.feature +37 -0
- data/features/dsl/each_rake_task/with_undefined_argument.feature +31 -0
- data/features/dsl/each_rake_task/without_arguments.feature +81 -0
- data/features/dsl/mirror_rake_tasks/inside_capistrano_namespace/with_defined_namespace_argument.feature +93 -0
- data/features/dsl/mirror_rake_tasks/inside_capistrano_namespace/with_defined_task_argument.feature +66 -0
- data/features/dsl/mirror_rake_tasks/inside_capistrano_namespace/with_undefined_argument.feature +39 -0
- data/features/dsl/mirror_rake_tasks/inside_capistrano_namespace/without_arguments.feature +263 -0
- data/features/dsl/mirror_rake_tasks/with_defined_namespace_argument.feature +85 -0
- data/features/dsl/mirror_rake_tasks/with_defined_task_argument.feature +60 -0
- data/features/dsl/mirror_rake_tasks/with_undefined_argument.feature +35 -0
- data/features/dsl/mirror_rake_tasks/without_arguments.feature +243 -0
- data/features/step_definitions.rb +33 -0
- data/features/support/env.rb +1 -0
- data/features/task_invocation/nonparameterized.feature +69 -0
- data/features/task_invocation/parameterized.feature +70 -0
- data/lib/cape.rb +22 -0
- data/lib/cape/capistrano.rb +86 -0
- data/lib/cape/core_ext.rb +10 -0
- data/lib/cape/core_ext/hash.rb +24 -0
- data/lib/cape/core_ext/symbol.rb +25 -0
- data/lib/cape/dsl.rb +81 -0
- data/lib/cape/rake.rb +60 -0
- data/lib/cape/strings.rb +25 -0
- data/lib/cape/version.rb +6 -0
- data/spec/cape/capistrano_spec.rb +0 -0
- data/spec/cape/core_ext/hash_spec.rb +12 -0
- data/spec/cape/core_ext/symbol_spec.rb +7 -0
- data/spec/cape/dsl_spec.rb +128 -0
- data/spec/cape/rake_spec.rb +0 -0
- data/spec/cape/strings_spec.rb +44 -0
- data/spec/cape/task_spec.rb +0 -0
- data/spec/cape/version_spec.rb +6 -0
- data/spec/cape_spec.rb +5 -0
- metadata +192 -0
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
Feature: Invoking parameterless Rake tasks via Capistrano
|
2
|
+
|
3
|
+
In order to invoke Rake tasks via Capistrano,
|
4
|
+
As a developer using Cape,
|
5
|
+
I want to use the `cap` command.
|
6
|
+
|
7
|
+
Scenario: invoke Rake task 'with_period'
|
8
|
+
Given a full-featured Rakefile
|
9
|
+
And a file named "Capfile" with:
|
10
|
+
"""
|
11
|
+
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
12
|
+
|
13
|
+
# Uncomment if you are using Rails' asset pipeline
|
14
|
+
# load 'deploy/assets'
|
15
|
+
|
16
|
+
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
|
17
|
+
|
18
|
+
load 'config/deploy' # remove this line to skip loading any of the default tasks
|
19
|
+
require 'cape'
|
20
|
+
|
21
|
+
Cape do
|
22
|
+
mirror_rake_tasks
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
And a file named "config/deploy.rb" with:
|
26
|
+
"""
|
27
|
+
require 'cape'
|
28
|
+
|
29
|
+
Cape do
|
30
|
+
mirror_rake_tasks
|
31
|
+
end
|
32
|
+
"""
|
33
|
+
When I run `cap with_period`
|
34
|
+
Then the output should contain:
|
35
|
+
"""
|
36
|
+
* executing `with_period'
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: invoke Rake task 'my_namespace:in_a_namespace'
|
40
|
+
Given a full-featured Rakefile
|
41
|
+
And a file named "Capfile" with:
|
42
|
+
"""
|
43
|
+
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
44
|
+
|
45
|
+
# Uncomment if you are using Rails' asset pipeline
|
46
|
+
# load 'deploy/assets'
|
47
|
+
|
48
|
+
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
|
49
|
+
|
50
|
+
load 'config/deploy' # remove this line to skip loading any of the default tasks
|
51
|
+
require 'cape'
|
52
|
+
|
53
|
+
Cape do
|
54
|
+
mirror_rake_tasks
|
55
|
+
end
|
56
|
+
"""
|
57
|
+
And a file named "config/deploy.rb" with:
|
58
|
+
"""
|
59
|
+
require 'cape'
|
60
|
+
|
61
|
+
Cape do
|
62
|
+
mirror_rake_tasks
|
63
|
+
end
|
64
|
+
"""
|
65
|
+
When I run `cap my_namespace:in_a_namespace`
|
66
|
+
Then the output should contain:
|
67
|
+
"""
|
68
|
+
* executing `my_namespace:in_a_namespace'
|
69
|
+
"""
|
@@ -0,0 +1,70 @@
|
|
1
|
+
Feature: Invoking parameterized Rake tasks via Capistrano
|
2
|
+
|
3
|
+
In order to invoke Rake tasks via Capistrano,
|
4
|
+
As a developer using Cape,
|
5
|
+
I want to use the `cap` command.
|
6
|
+
|
7
|
+
Scenario: invoke Rake task 'with_one_arg' without an argument
|
8
|
+
Given a full-featured Rakefile
|
9
|
+
And a file named "Capfile" with:
|
10
|
+
"""
|
11
|
+
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
12
|
+
|
13
|
+
# Uncomment if you are using Rails' asset pipeline
|
14
|
+
# load 'deploy/assets'
|
15
|
+
|
16
|
+
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
|
17
|
+
|
18
|
+
load 'config/deploy' # remove this line to skip loading any of the default tasks
|
19
|
+
require 'cape'
|
20
|
+
|
21
|
+
Cape do
|
22
|
+
mirror_rake_tasks
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
And a file named "config/deploy.rb" with:
|
26
|
+
"""
|
27
|
+
require 'cape'
|
28
|
+
|
29
|
+
Cape do
|
30
|
+
mirror_rake_tasks
|
31
|
+
end
|
32
|
+
"""
|
33
|
+
When I run `cap with_one_arg`
|
34
|
+
Then the output should contain:
|
35
|
+
"""
|
36
|
+
* executing `with_one_arg'
|
37
|
+
"""
|
38
|
+
And the output should contain "Environment variable THE_ARG must be set (RuntimeError)"
|
39
|
+
|
40
|
+
Scenario: invoke Rake task 'with_one_arg' with its argument
|
41
|
+
Given a full-featured Rakefile
|
42
|
+
And a file named "Capfile" with:
|
43
|
+
"""
|
44
|
+
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
45
|
+
|
46
|
+
# Uncomment if you are using Rails' asset pipeline
|
47
|
+
# load 'deploy/assets'
|
48
|
+
|
49
|
+
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
|
50
|
+
|
51
|
+
load 'config/deploy' # remove this line to skip loading any of the default tasks
|
52
|
+
require 'cape'
|
53
|
+
|
54
|
+
Cape do
|
55
|
+
mirror_rake_tasks
|
56
|
+
end
|
57
|
+
"""
|
58
|
+
And a file named "config/deploy.rb" with:
|
59
|
+
"""
|
60
|
+
require 'cape'
|
61
|
+
|
62
|
+
Cape do
|
63
|
+
mirror_rake_tasks
|
64
|
+
end
|
65
|
+
"""
|
66
|
+
When I run `cap with_one_arg THE_ARG="the arg goes here"`
|
67
|
+
Then the output should contain:
|
68
|
+
"""
|
69
|
+
* executing `with_one_arg'
|
70
|
+
"""
|
data/lib/cape.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Dir.glob( File.expand_path( 'cape/*.rb', File.dirname( __FILE__ ))) do |f|
|
2
|
+
require "cape/#{File.basename f, '.rb'}"
|
3
|
+
end
|
4
|
+
|
5
|
+
# Contains the implementation of Cape.
|
6
|
+
module Cape
|
7
|
+
|
8
|
+
extend DSL
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
# The method used to group Cape statements in a block.
|
13
|
+
def Cape(&block)
|
14
|
+
Cape.module_eval do
|
15
|
+
@outer_self = block.binding.eval('self', __FILE__, __LINE__)
|
16
|
+
if 0 < block.arity
|
17
|
+
block.call self
|
18
|
+
else
|
19
|
+
module_eval(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'cape/strings'
|
2
|
+
|
3
|
+
module Cape
|
4
|
+
|
5
|
+
# An abstraction of the Capistrano installation.
|
6
|
+
class Capistrano
|
7
|
+
|
8
|
+
# Defines the specified _task_ as a Capistrano task, provided a Binding
|
9
|
+
# named argument +:binding+ and a Cape::Rake named argument +:rake+. Any
|
10
|
+
# parameters the task has are converted to environment variables, since
|
11
|
+
# Capistrano does not have the concept of task parameters.
|
12
|
+
#
|
13
|
+
# The _task_ argument must be a Hash of the form:
|
14
|
+
#
|
15
|
+
# {:name => <String>,
|
16
|
+
# :parameters => <String Array or nil>,
|
17
|
+
# :description => <String>}
|
18
|
+
def define(task, named_arguments={})
|
19
|
+
unless (binding = named_arguments[:binding])
|
20
|
+
raise ::ArgumentError, ':binding named argument is required'
|
21
|
+
end
|
22
|
+
unless (rake = named_arguments[:rake])
|
23
|
+
raise ::ArgumentError, ':rake named argument is required'
|
24
|
+
end
|
25
|
+
roles = named_arguments[:roles] || :app
|
26
|
+
|
27
|
+
capistrano = binding.eval('self', __FILE__, __LINE__)
|
28
|
+
if (description = build_capistrano_description(task))
|
29
|
+
capistrano.desc description
|
30
|
+
end
|
31
|
+
implement task, roles, capistrano, rake
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def build_capistrano_description(task)
|
38
|
+
return nil unless task[:description]
|
39
|
+
|
40
|
+
description = [task[:description]]
|
41
|
+
description << '.' unless task[:description].end_with?('.')
|
42
|
+
unless (parameters = Array(task[:parameters])).empty?
|
43
|
+
noun = Strings.pluralize('variable', parameters.length)
|
44
|
+
parameters_list = Strings.to_list_phrase(parameters.collect(&:upcase))
|
45
|
+
description << <<-end_description
|
46
|
+
|
47
|
+
|
48
|
+
You must set environment #{noun} #{parameters_list}.
|
49
|
+
end_description
|
50
|
+
end
|
51
|
+
description.join
|
52
|
+
end
|
53
|
+
|
54
|
+
def implement(task, roles, capistrano_context, rake)
|
55
|
+
name = task[:name].split(':')
|
56
|
+
# Define the task.
|
57
|
+
block = lambda { |context|
|
58
|
+
context.task name.last, :roles => roles do
|
59
|
+
arguments = Array(task[:parameters]).collect do |a|
|
60
|
+
unless (value = ENV[a.upcase])
|
61
|
+
fail "Environment variable #{a.upcase} must be set"
|
62
|
+
end
|
63
|
+
value
|
64
|
+
end
|
65
|
+
if arguments.empty?
|
66
|
+
arguments = nil
|
67
|
+
else
|
68
|
+
arguments = "[#{arguments.join ','}]"
|
69
|
+
end
|
70
|
+
context.run "cd #{context.current_path} && " +
|
71
|
+
"#{rake.remote_executable} #{name.join ':'}#{arguments}"
|
72
|
+
end
|
73
|
+
}
|
74
|
+
# Nest the task inside its containing namespaces.
|
75
|
+
name[0...-1].reverse.each do |namespace_token|
|
76
|
+
inner_block = block
|
77
|
+
block = lambda { |context|
|
78
|
+
context.namespace(namespace_token, &inner_block)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
block.call capistrano_context
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cape
|
2
|
+
|
3
|
+
module CoreExt
|
4
|
+
|
5
|
+
# Contains extensions to the Hash core class.
|
6
|
+
module Hash
|
7
|
+
|
8
|
+
# Returns a copy of the Hash containing values only for the specified
|
9
|
+
# _keys_.
|
10
|
+
def slice(*keys)
|
11
|
+
::Hash[select { |key, value| keys.include? key }]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
unless Hash.instance_methods.collect(&:to_s).include?('slice')
|
21
|
+
Hash.class_eval do
|
22
|
+
include Cape::CoreExt::Hash
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cape
|
2
|
+
|
3
|
+
module CoreExt
|
4
|
+
|
5
|
+
# Contains extensions to the Symbol core class.
|
6
|
+
module Symbol
|
7
|
+
|
8
|
+
# Returns +0+ if the Symbol is equal to _other_, +-1+ if it is
|
9
|
+
# alphabetically lesser than _other_, and +1+ if it is alphabetically
|
10
|
+
# greater than _other_.
|
11
|
+
def <=>(other)
|
12
|
+
to_s <=> other.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
unless Symbol.instance_methods.collect(&:to_s).include?('<=>')
|
22
|
+
Symbol.class_eval do
|
23
|
+
include Cape::CoreExt::Symbol
|
24
|
+
end
|
25
|
+
end
|
data/lib/cape/dsl.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'cape/capistrano'
|
2
|
+
require 'cape/rake'
|
3
|
+
|
4
|
+
module Cape
|
5
|
+
|
6
|
+
# Provides methods for integrating Capistrano and Rake.
|
7
|
+
module DSL
|
8
|
+
|
9
|
+
# Yields each available Rake task to a block. The optional _task_expression_
|
10
|
+
# argument limits the list to a single task or a namespace containing
|
11
|
+
# multiple tasks.
|
12
|
+
#
|
13
|
+
# Tasks are yielded as Hash objects of the form:
|
14
|
+
#
|
15
|
+
# {:name => <String>,
|
16
|
+
# :parameters => <String Array or nil>,
|
17
|
+
# :description => <String>}
|
18
|
+
def each_rake_task(task_expression=nil, &block)
|
19
|
+
rake.each_task(task_expression, &block)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the command used to run Rake on the local computer. Defaults to
|
24
|
+
# Rake::DEFAULT_EXECUTABLE.
|
25
|
+
def local_rake_executable
|
26
|
+
rake.local_executable
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets the command used to run Rake on the local computer.
|
30
|
+
def local_rake_executable=(value)
|
31
|
+
rake.local_executable = value
|
32
|
+
end
|
33
|
+
|
34
|
+
# Defines each available Rake task as a Capistrano task. Any
|
35
|
+
# parameters the tasks have are converted to environment variables, since
|
36
|
+
# Capistrano does not have the concept of task parameters. The optional
|
37
|
+
# _task_expression_ argument limits the list to a single task or a namespace
|
38
|
+
# containing multiple tasks.
|
39
|
+
def mirror_rake_tasks(task_expression=nil)
|
40
|
+
d = nil
|
41
|
+
rake.each_task task_expression do |t|
|
42
|
+
(d ||= deployment_library).define t, :binding => binding, :rake => rake
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the command used to run Rake on remote computers. Defaults to
|
48
|
+
# Rake::DEFAULT_EXECUTABLE.
|
49
|
+
def remote_rake_executable
|
50
|
+
rake.remote_executable
|
51
|
+
end
|
52
|
+
|
53
|
+
# Sets the command used to run Rake on remote computers.
|
54
|
+
def remote_rake_executable=(value)
|
55
|
+
rake.remote_executable = value
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def deployment_library
|
61
|
+
raise_unless_capistrano
|
62
|
+
Capistrano.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_missing(method, *args, &block)
|
66
|
+
@outer_self.send(method, *args, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def raise_unless_capistrano
|
70
|
+
if @outer_self.method(:task).owner.name !~ /^Capistrano::/
|
71
|
+
raise 'Use this in the context of Capistrano recipes'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def rake
|
76
|
+
@rake ||= Rake.new
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/lib/cape/rake.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Cape
|
2
|
+
|
3
|
+
# An abstraction of the Rake installation and available tasks.
|
4
|
+
class Rake
|
5
|
+
|
6
|
+
# The default command used to run Rake.
|
7
|
+
DEFAULT_EXECUTABLE = '/usr/bin/env rake'.freeze
|
8
|
+
|
9
|
+
# Sets the command used to run Rake on the local computer.
|
10
|
+
attr_writer :local_executable
|
11
|
+
|
12
|
+
# Sets the command used to run Rake on remote computers.
|
13
|
+
attr_writer :remote_executable
|
14
|
+
|
15
|
+
# Constructs a new Rake object with the specified _attributes_.
|
16
|
+
def initialize(attributes={})
|
17
|
+
attributes.each do |name, value|
|
18
|
+
send "#{name}=", value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Yields each available Rake task to a block. The optional _task_expression_
|
23
|
+
# argument limits the list to a single task or a namespace containing
|
24
|
+
# multiple tasks.
|
25
|
+
#
|
26
|
+
# Tasks are yielded as Hash objects of the form:
|
27
|
+
#
|
28
|
+
# {:name => <String>,
|
29
|
+
# :parameters => <String Array or nil>,
|
30
|
+
# :description => <String>}
|
31
|
+
def each_task(task_expression=nil)
|
32
|
+
task_expression = " #{task_expression}" if task_expression
|
33
|
+
command = "#{local_executable} --tasks #{task_expression}"
|
34
|
+
`#{command}`.each_line do |l|
|
35
|
+
matches = l.chomp.match(/^rake (.+?)(?:\[(.+?)\])?\s+# (.+)/)
|
36
|
+
task = {}.tap do |t|
|
37
|
+
t[:name] = matches[1].strip
|
38
|
+
t[:parameters] = matches[2].split(',') if matches[2]
|
39
|
+
t[:description] = matches[3]
|
40
|
+
end
|
41
|
+
yield task
|
42
|
+
end
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the command used to run Rake on the local computer. Defaults to
|
47
|
+
# DEFAULT_EXECUTABLE.
|
48
|
+
def local_executable
|
49
|
+
@local_executable ||= DEFAULT_EXECUTABLE
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the command used to run Rake on remote computers. Defaults to
|
53
|
+
# DEFAULT_EXECUTABLE.
|
54
|
+
def remote_executable
|
55
|
+
@remote_executable ||= DEFAULT_EXECUTABLE
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|