ey-deploy 0.2.7 → 0.3.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/lib/ey-deploy.rb +4 -1
- data/lib/ey-deploy/cli.rb +43 -1
- data/lib/ey-deploy/configuration.rb +26 -4
- data/lib/ey-deploy/deploy.rb +5 -37
- data/lib/ey-deploy/deploy_hook.rb +52 -0
- data/lib/ey-deploy/server.rb +2 -2
- data/lib/ey-deploy/task.rb +10 -2
- data/lib/ey-deploy/version.rb +1 -1
- data/spec/deploy_hook_spec.rb +95 -0
- data/spec/spec_helper.rb +2 -0
- metadata +22 -7
- data/spec/ey-deploy_spec.rb +0 -7
data/lib/ey-deploy.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'escape'
|
2
|
+
|
1
3
|
$LOAD_PATH.push(File.expand_path("ey-deploy", File.dirname(__FILE__)))
|
2
4
|
|
3
5
|
require 'version'
|
@@ -6,6 +8,7 @@ require 'strategies/git'
|
|
6
8
|
require 'task'
|
7
9
|
require 'server'
|
8
10
|
require 'deploy'
|
11
|
+
require 'deploy_hook'
|
9
12
|
require 'cli'
|
10
13
|
require 'configuration'
|
11
14
|
|
@@ -13,4 +16,4 @@ module EY
|
|
13
16
|
def self.node
|
14
17
|
@node ||= JSON.parse(`sudo cat /etc/chef/dna.json`)
|
15
18
|
end
|
16
|
-
end
|
19
|
+
end
|
data/lib/ey-deploy/cli.rb
CHANGED
@@ -26,9 +26,22 @@ module EY
|
|
26
26
|
|
27
27
|
desc "deploy", "Deploy code from /data/<app>"
|
28
28
|
def deploy(default_task=:deploy)
|
29
|
+
invoke :propagate
|
29
30
|
EY::Deploy.run(options.merge("default_task" => default_task))
|
30
31
|
end
|
31
32
|
|
33
|
+
method_option :app, :type => :string,
|
34
|
+
:required => true,
|
35
|
+
:desc => "Which application's hooks to run",
|
36
|
+
:aliases => ["-a"]
|
37
|
+
|
38
|
+
method_option :release_path, :type => :string,
|
39
|
+
:desc => "Value for #release_path in hooks (mostly for internal coordination)",
|
40
|
+
:aliases => ["-r"]
|
41
|
+
desc "hook [NAME]", "Run a particular deploy hook"
|
42
|
+
def hook(hook_name)
|
43
|
+
EY::DeployHook.new(options).run(hook_name)
|
44
|
+
end
|
32
45
|
|
33
46
|
desc "check", "Check whether the client gem is compatible with the server gem"
|
34
47
|
def check(client_version, server_requirement)
|
@@ -54,5 +67,34 @@ module EY
|
|
54
67
|
exit 3
|
55
68
|
end
|
56
69
|
end
|
70
|
+
|
71
|
+
desc "propagate", "Propagate the ey-deploy gem to the other instances in the cluster. This will install exactly version #{VERSION} and remove other versions if found."
|
72
|
+
def propagate
|
73
|
+
config = EY::Deploy::Configuration.new
|
74
|
+
gem_filename = "ey-deploy-#{VERSION}.gem"
|
75
|
+
local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
|
76
|
+
remote_gem_file = File.join(Dir.tmpdir, gem_filename)
|
77
|
+
gem_binary = File.join(Gem.default_bindir, 'gem')
|
78
|
+
|
79
|
+
EY::Server.config = config
|
80
|
+
|
81
|
+
EY::Server.all.find_all do |server|
|
82
|
+
!server.local? # of course this machine has it
|
83
|
+
end.find_all do |server|
|
84
|
+
has_gem_cmd = "/usr/local/ey_resin/ruby/bin/gem list ey-deploy | grep -q '(#{VERSION})'"
|
85
|
+
!server.run(has_gem_cmd) # doesn't have only this exact version
|
86
|
+
end.each do |server|
|
87
|
+
puts "~> Installing ey-deploy on #{server.hostname}"
|
88
|
+
|
89
|
+
system(Escape.shell_command([
|
90
|
+
'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
|
91
|
+
"-o", "StrictHostKeyChecking=no",
|
92
|
+
local_gem_file,
|
93
|
+
"#{config.user}@#{server.hostname}:#{remote_gem_file}",
|
94
|
+
]))
|
95
|
+
server.run("sudo #{gem_binary} uninstall -a -x ey-deploy 2>/dev/null")
|
96
|
+
server.run("sudo #{gem_binary} install '#{remote_gem_file}'")
|
97
|
+
end
|
98
|
+
end
|
57
99
|
end
|
58
|
-
end
|
100
|
+
end
|
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'thor'
|
2
3
|
|
3
4
|
module EY
|
4
5
|
class Deploy::Configuration
|
5
|
-
DEFAULT_CONFIG = {
|
6
|
+
DEFAULT_CONFIG = Thor::CoreExt::HashWithIndifferentAccess.new({
|
6
7
|
"branch" => "master",
|
7
|
-
"copy_exclude" => ".git",
|
8
8
|
"strategy" => "Git",
|
9
|
-
}
|
9
|
+
})
|
10
10
|
|
11
11
|
attr_reader :configuration
|
12
12
|
alias :c :configuration
|
13
13
|
|
14
14
|
def initialize(opts={})
|
15
|
+
@release_path = opts[:release_path]
|
15
16
|
config = JSON.parse(opts["config"] || "{}")
|
16
17
|
@configuration = DEFAULT_CONFIG.merge(config).merge(opts)
|
17
18
|
end
|
@@ -20,14 +21,35 @@ module EY
|
|
20
21
|
def method_missing(meth, *args, &blk)
|
21
22
|
c.key?(meth.to_s) ? c[meth.to_s] : super
|
22
23
|
end
|
23
|
-
|
24
|
+
|
25
|
+
def respond_to?(meth, include_private=false)
|
24
26
|
c.key?(meth.to_s) ? true : super
|
25
27
|
end
|
26
28
|
|
29
|
+
def [](key)
|
30
|
+
if respond_to?(key.to_sym)
|
31
|
+
send(key.to_sym)
|
32
|
+
else
|
33
|
+
c[key]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_key?(key)
|
38
|
+
if respond_to?(key.to_sym)
|
39
|
+
true
|
40
|
+
else
|
41
|
+
c.has_key?(key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
27
45
|
def node
|
28
46
|
EY.node
|
29
47
|
end
|
30
48
|
|
49
|
+
def revision
|
50
|
+
IO.read(File.join(latest_release, 'REVISION'))
|
51
|
+
end
|
52
|
+
|
31
53
|
def repository_cache
|
32
54
|
configuration['repository_cache'] || File.join(deploy_to, "/shared/cached-copy")
|
33
55
|
end
|
data/lib/ey-deploy/deploy.rb
CHANGED
@@ -91,7 +91,7 @@ module EY
|
|
91
91
|
# task
|
92
92
|
def copy_repository_cache
|
93
93
|
puts "~> copying to #{c.release_path}"
|
94
|
-
sudo("mkdir -p #{c.release_path} && rsync -aq #{c.exclusions} #{c.repository_cache}
|
94
|
+
sudo("mkdir -p #{c.release_path} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
|
95
95
|
|
96
96
|
puts "~> ensuring proper ownership"
|
97
97
|
sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
|
@@ -126,46 +126,14 @@ module EY
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
|
130
|
-
# before_restart
|
131
|
-
|
132
|
-
class CallbackContext
|
133
|
-
def initialize(deploy)
|
134
|
-
@deploy = deploy
|
135
|
-
end
|
136
|
-
|
137
|
-
def method_missing(meth, *args, &blk)
|
138
|
-
if @deploy.respond_to?(meth)
|
139
|
-
@deploy.send(meth, *args, &blk)
|
140
|
-
elsif @deploy.config.respond_to?(meth)
|
141
|
-
@deploy.config.send(meth, *args, &blk)
|
142
|
-
else
|
143
|
-
super
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def respond_to(meth)
|
148
|
-
if @deploy.respond_to?(meth) || @deploy.config.respond_to?(meth)
|
149
|
-
true
|
150
|
-
else
|
151
|
-
super
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def callback_context
|
157
|
-
@context ||= CallbackContext.new(self)
|
158
|
-
end
|
159
|
-
|
160
|
-
def callback(what)
|
129
|
+
def callback(what, roles=@roles)
|
161
130
|
if File.exist?("#{c.latest_release}/deploy/#{what}.rb")
|
162
|
-
|
163
|
-
|
164
|
-
|
131
|
+
eysd_path = $0 # invoke others just like we were invoked
|
132
|
+
EY::Server.from_roles(roles).each do |server|
|
133
|
+
server.run("#{eysd_path} hook '#{what}' --app '#{config.app}' --release-path #{config.release_path}")
|
165
134
|
end
|
166
135
|
end
|
167
136
|
end
|
168
|
-
|
169
137
|
end
|
170
138
|
|
171
139
|
class Deploy < DeployBase
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'ey-deploy/verbose_system'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
class DeployHook < Task
|
5
|
+
include VerboseSystem
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
super(EY::Deploy::Configuration.new(options))
|
9
|
+
end
|
10
|
+
|
11
|
+
def callback_context
|
12
|
+
@context ||= CallbackContext.new(self, config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(hook)
|
16
|
+
if File.exist?("#{c.latest_release}/deploy/#{hook}.rb")
|
17
|
+
Dir.chdir(c.latest_release) do
|
18
|
+
puts "~> running deploy hook: deploy/#{hook}.rb"
|
19
|
+
callback_context.instance_eval(IO.read("#{c.latest_release}/deploy/#{hook}.rb"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class CallbackContext
|
25
|
+
def initialize(hook_runner, config)
|
26
|
+
@hook_runner, @configuration = hook_runner, config
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(meth, *args, &blk)
|
30
|
+
if @configuration.respond_to?(meth)
|
31
|
+
@configuration.send(meth, *args, &blk)
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def respond_to?(meth, include_private=false)
|
38
|
+
@configuration.respond_to?(meth, include_private) || super
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(cmd)
|
42
|
+
system(@hook_runner.prepare_run(cmd))
|
43
|
+
end
|
44
|
+
|
45
|
+
def sudo(cmd)
|
46
|
+
system(@hook_runner.prepare_sudo(cmd))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
data/lib/ey-deploy/server.rb
CHANGED
@@ -73,9 +73,9 @@ module EY
|
|
73
73
|
|
74
74
|
def run(command)
|
75
75
|
if local?
|
76
|
-
system(command
|
76
|
+
system(command)
|
77
77
|
else
|
78
|
-
system(
|
78
|
+
system(ssh_command + " " + Escape.shell_command(["#{config.user}@#{hostname}", command]))
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
data/lib/ey-deploy/task.rb
CHANGED
@@ -29,14 +29,22 @@ module EY
|
|
29
29
|
|
30
30
|
def run(cmd)
|
31
31
|
EY::Server.from_roles(@roles).each do |server|
|
32
|
-
server.run
|
32
|
+
server.run prepare_run(cmd)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
def sudo(cmd)
|
37
37
|
EY::Server.from_roles(@roles).each do |server|
|
38
|
-
server.run
|
38
|
+
server.run prepare_sudo(cmd)
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
def prepare_run(command)
|
43
|
+
Escape.shell_command ["sh", "-l", "-c", command]
|
44
|
+
end
|
45
|
+
|
46
|
+
def prepare_sudo(command)
|
47
|
+
Escape.shell_command ["sudo", "sh", "-l", "-c", command]
|
48
|
+
end
|
41
49
|
end
|
42
50
|
end
|
data/lib/ey-deploy/version.rb
CHANGED
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "deploy hook's context" do
|
4
|
+
before(:each) do
|
5
|
+
@hook_runner = EY::DeployHook.new(options)
|
6
|
+
@callback_context = EY::DeployHook::CallbackContext.new(@hook_runner, @hook_runner.config)
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_hook(options={}, &blk)
|
10
|
+
raise ArgumentError unless block_given?
|
11
|
+
options.each do |k, v|
|
12
|
+
@hook_runner.config.configuration[k] = v # ugh
|
13
|
+
end
|
14
|
+
|
15
|
+
# The hooks on the filesystem are run by passing a string to
|
16
|
+
# context.instance_eval, not a block. However, using a block
|
17
|
+
# should allow us to get the same degree of test coverage and
|
18
|
+
# still let us have things like syntax checking work on this spec
|
19
|
+
# file.
|
20
|
+
@callback_context.instance_eval(&blk)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "the #run method" do
|
24
|
+
it "is available" do
|
25
|
+
run_hook { respond_to?(:run) }.should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "runs commands like the shell does" do
|
29
|
+
ENV['COUNT'] = 'Chocula'
|
30
|
+
File.unlink("/tmp/deploy_hook_spec.the_count") rescue nil
|
31
|
+
|
32
|
+
run_hook { run("echo $COUNT > /tmp/deploy_hook_spec.the_count") }
|
33
|
+
|
34
|
+
IO.read("/tmp/deploy_hook_spec.the_count").strip.should == "Chocula"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns true/false to indicate the command's success" do
|
38
|
+
run_hook { run("true") }.should be_true
|
39
|
+
run_hook { run("false") }.should be_false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "the #sudo method" do
|
44
|
+
it "is available" do
|
45
|
+
run_hook { respond_to?(:sudo) }.should be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "runs things with sudo" do
|
49
|
+
@callback_context.should_receive(:system).with("sudo sh -l -c 'do it as root'").and_return(true)
|
50
|
+
|
51
|
+
run_hook { sudo("do it as root") }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "capistrano-ish methods" do
|
56
|
+
it "has them" do
|
57
|
+
run_hook { respond_to?(:latest_release) }.should be_true
|
58
|
+
run_hook { respond_to?(:previous_release) }.should be_true
|
59
|
+
run_hook { respond_to?(:all_releases) }.should be_true
|
60
|
+
run_hook { respond_to?(:current_path) }.should be_true
|
61
|
+
run_hook { respond_to?(:shared_path) }.should be_true
|
62
|
+
run_hook { respond_to?(:release_dir) }.should be_true
|
63
|
+
run_hook { respond_to?(:release_path) }.should be_true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "the @configuration ivar" do
|
68
|
+
it "is available" do
|
69
|
+
run_hook { @configuration.nil? }.should be_false
|
70
|
+
end
|
71
|
+
|
72
|
+
it "has the configuration in it" do
|
73
|
+
run_hook('bert' => 'ernie') { @configuration.bert }.should == 'ernie'
|
74
|
+
end
|
75
|
+
|
76
|
+
it "can be accessed with method calls, with [:symbols], or ['strings']" do
|
77
|
+
run_hook('bert' => 'ernie') { @configuration.bert }.should == 'ernie'
|
78
|
+
run_hook('bert' => 'ernie') { @configuration[:bert] }.should == 'ernie'
|
79
|
+
run_hook('bert' => 'ernie') { @configuration['bert'] }.should == 'ernie'
|
80
|
+
end
|
81
|
+
|
82
|
+
[:repository_cache,
|
83
|
+
:release_path,
|
84
|
+
:branch,
|
85
|
+
:shared_path,
|
86
|
+
:deploy_to,
|
87
|
+
:user,
|
88
|
+
:revision,
|
89
|
+
:environment].each do |attribute|
|
90
|
+
it "has the #{attribute.inspect} attribute for compatibility with chef-deploy" do
|
91
|
+
run_hook { @configuration.has_key?(attribute) }.should be_true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- EY Cloud Team
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-12 00:00:00 -07:00
|
18
18
|
default_executable: eysd
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,16 +31,30 @@ dependencies:
|
|
31
31
|
version_requirements: *id001
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
33
|
prerelease: false
|
34
|
-
name:
|
34
|
+
name: escape
|
35
35
|
requirement: &id002 !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
segments:
|
40
40
|
- 0
|
41
|
-
|
41
|
+
- 0
|
42
|
+
- 4
|
43
|
+
version: 0.0.4
|
42
44
|
type: :runtime
|
43
45
|
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
prerelease: false
|
48
|
+
name: json
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
type: :runtime
|
57
|
+
version_requirements: *id003
|
44
58
|
description:
|
45
59
|
email: cloud@engineyard.com
|
46
60
|
executables:
|
@@ -55,6 +69,7 @@ files:
|
|
55
69
|
- lib/ey-deploy/compatibility.rb
|
56
70
|
- lib/ey-deploy/configuration.rb
|
57
71
|
- lib/ey-deploy/deploy.rb
|
72
|
+
- lib/ey-deploy/deploy_hook.rb
|
58
73
|
- lib/ey-deploy/server.rb
|
59
74
|
- lib/ey-deploy/strategies/git.rb
|
60
75
|
- lib/ey-deploy/task.rb
|
@@ -128,5 +143,5 @@ signing_key:
|
|
128
143
|
specification_version: 3
|
129
144
|
summary: A gem that deploys ruby applications on EY Cloud instances
|
130
145
|
test_files:
|
131
|
-
- spec/
|
146
|
+
- spec/deploy_hook_spec.rb
|
132
147
|
- spec/spec_helper.rb
|