caplock 0.2.0 → 0.3.1
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/README.rdoc +7 -0
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/caplock.gemspec +10 -10
- data/lib/caplock.rb +15 -8
- data/test/test_caplock.rb +64 -1
- metadata +46 -61
data/README.rdoc
CHANGED
@@ -15,6 +15,13 @@ Include in your Capfile
|
|
15
15
|
Define the name of your lock file in your deploy.rb
|
16
16
|
set :lockfile, "my.lock" # defaults to "cap.lock"
|
17
17
|
|
18
|
+
The `lock:check` task is executed automatically before `deploy:update_code` (i.e. before deploy).
|
19
|
+
If you have overridden the default deploy task, you may need to add the task with:
|
20
|
+
before "deploy", "lock:check"
|
21
|
+
|
22
|
+
If the lockfile becomes stale (because you interrupted the deployment with CTRL-C for example)
|
23
|
+
cap lock:release # or "cap production lock:release" with multistage
|
24
|
+
|
18
25
|
== Note on Patches/Pull Requests
|
19
26
|
|
20
27
|
* Fork the project.
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ begin
|
|
9
9
|
gem.description = %Q{Adds a lock file to Capistrano deployments to prevent concurrent deployments.}
|
10
10
|
gem.email = "Druwerd@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/Druwerd/caplock"
|
12
|
-
gem.authors = ["Dru Ibarra"]
|
12
|
+
gem.authors = ["Dru Ibarra", "Giorgio Premi"]
|
13
13
|
gem.add_dependency "capistrano", '>= 2.9.0'
|
14
14
|
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
15
15
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
@@ -43,9 +43,9 @@ task :test => :check_dependencies
|
|
43
43
|
|
44
44
|
task :default => :test
|
45
45
|
|
46
|
-
require '
|
47
|
-
|
48
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
require 'rdoc/task'
|
47
|
+
RDoc::Task.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION').strip : ""
|
49
49
|
|
50
50
|
rdoc.rdoc_dir = 'rdoc'
|
51
51
|
rdoc.title = "caplock #{version}"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
data/caplock.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.
|
7
|
+
s.name = "caplock"
|
8
|
+
s.version = "0.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
11
|
+
s.authors = ["Dru Ibarra", "Giorgio Premi"]
|
12
|
+
s.date = "2014-07-08"
|
13
|
+
s.description = "Adds a lock file to Capistrano deployments to prevent concurrent deployments."
|
14
|
+
s.email = "Druwerd@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
17
|
"README.rdoc"
|
@@ -27,10 +27,10 @@ Gem::Specification.new do |s|
|
|
27
27
|
"test/helper.rb",
|
28
28
|
"test/test_caplock.rb"
|
29
29
|
]
|
30
|
-
s.homepage =
|
31
|
-
s.require_paths = [
|
32
|
-
s.rubygems_version =
|
33
|
-
s.summary =
|
30
|
+
s.homepage = "http://github.com/Druwerd/caplock"
|
31
|
+
s.require_paths = ["lib"]
|
32
|
+
s.rubygems_version = "1.8.23"
|
33
|
+
s.summary = "Adds a lock file to Capistrano deployments"
|
34
34
|
|
35
35
|
if s.respond_to? :specification_version then
|
36
36
|
s.specification_version = 3
|
data/lib/caplock.rb
CHANGED
@@ -2,11 +2,13 @@ require 'capistrano'
|
|
2
2
|
|
3
3
|
module Capistrano
|
4
4
|
module Caplock
|
5
|
-
|
5
|
+
|
6
|
+
class LockedDeployError < RuntimeError ; end
|
7
|
+
|
6
8
|
# Returns Boolean indicating the result of +filetest+ on +full_path+ on the server, evaluated by shell on
|
7
9
|
# the server (usually bash or something roughly compatible).
|
8
10
|
def remote_filetest_passes?(filetest, full_path)
|
9
|
-
'true' == capture("if [ #{filetest} #{full_path} ]; then echo 'true'; fi").strip
|
11
|
+
'true' == top.capture("if [ #{filetest} #{full_path} ]; then echo 'true'; fi").strip
|
10
12
|
end
|
11
13
|
|
12
14
|
# Checks if a symlink exists on the remote machine.
|
@@ -23,7 +25,7 @@ module Capistrano
|
|
23
25
|
# is equivalent to content by checking whether or not the MD5 of the remote content is the same as the
|
24
26
|
# MD5 of the String in +content+.
|
25
27
|
def remote_file_content_same_as?(full_path, content)
|
26
|
-
Digest::MD5.hexdigest(content) == capture("md5sum #{full_path} | awk '{ print $1 }'").strip
|
28
|
+
Digest::MD5.hexdigest(content) == top.capture("md5sum #{full_path} | awk '{ print $1 }'").strip
|
27
29
|
end
|
28
30
|
|
29
31
|
# Returns Boolean indicating whether the remote file is present and has the same contents as
|
@@ -35,12 +37,16 @@ module Capistrano
|
|
35
37
|
def self.load_into(configuration)
|
36
38
|
configuration.load do
|
37
39
|
set :lockfile, "cap.lock"
|
38
|
-
|
40
|
+
|
41
|
+
# internal
|
42
|
+
set :keep_lock, false
|
43
|
+
|
39
44
|
namespace :lock do
|
40
45
|
desc "check lock"
|
41
46
|
task :check, :roles => :app do
|
42
47
|
if caplock.remote_file_exists?("#{deploy_to}/#{lockfile}")
|
43
|
-
|
48
|
+
keep_lock = true
|
49
|
+
raise LockedDeployError, "\n\n\n\e[0;31m A Deployment is already in progress\n Remove #{deploy_to}/#{lockfile} to unlock \e[0m\n\n\n"
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
@@ -49,16 +55,17 @@ module Capistrano
|
|
49
55
|
timestamp = Time.now.strftime("%m/%d/%Y %H:%M:%S %Z")
|
50
56
|
lock_message = "Deploy started at #{timestamp} in progress"
|
51
57
|
put lock_message, "#{deploy_to}/#{lockfile}", :mode => 0644
|
58
|
+
on_rollback { find_and_execute_task("lock:release") }
|
52
59
|
end
|
53
60
|
|
54
61
|
desc "release lock"
|
55
62
|
task :release, :roles => :app do
|
56
|
-
run "rm -f #{deploy_to}/#{lockfile}"
|
63
|
+
run "rm -f #{deploy_to}/#{lockfile}" unless keep_lock
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
67
|
# Deployment
|
61
|
-
before "deploy", "lock:check"
|
68
|
+
before "deploy:update_code", "lock:check"
|
62
69
|
after "lock:check", "lock:create"
|
63
70
|
after "deploy", "lock:release"
|
64
71
|
|
@@ -76,4 +83,4 @@ end
|
|
76
83
|
|
77
84
|
if Capistrano::Configuration.instance
|
78
85
|
Capistrano::Caplock.load_into(Capistrano::Configuration.instance)
|
79
|
-
end
|
86
|
+
end
|
data/test/test_caplock.rb
CHANGED
@@ -1,13 +1,44 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
+
class StubLogger
|
4
|
+
def close ; end
|
5
|
+
def log(level, message, line_prefix=nil) ; end
|
6
|
+
def important(message, line_prefix=nil) ; end
|
7
|
+
def info(message, line_prefix=nil) ; end
|
8
|
+
def debug(message, line_prefix=nil) ; end
|
9
|
+
def trace(message, line_prefix=nil); end
|
10
|
+
def format(message, color, style, nl = "\n") ; end
|
11
|
+
end
|
12
|
+
|
3
13
|
class TestCaplock < Test::Unit::TestCase
|
14
|
+
class TestCaplockError < RuntimeError ; end
|
15
|
+
|
4
16
|
def setup
|
17
|
+
@update_code_raise = false;
|
5
18
|
@config = Capistrano::Configuration.new
|
19
|
+
@config.load do
|
20
|
+
namespace :deploy do
|
21
|
+
task :default do
|
22
|
+
transaction do
|
23
|
+
update_code
|
24
|
+
end
|
25
|
+
end
|
26
|
+
task :update_code do
|
27
|
+
raise TestCaplockError, "update_code simulated exception" if caplock_update_code_raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@config.logger = StubLogger.new
|
32
|
+
#@config.logger = Capistrano::Logger.new :level => Capistrano::Logger::MAX_LEVEL
|
6
33
|
Capistrano::Caplock.load_into(@config)
|
7
34
|
@config.set :deploy_to, "/tmp"
|
35
|
+
@config.set :caplock_update_code_raise, false
|
8
36
|
@config.role :app, "localhost"
|
37
|
+
|
38
|
+
# ensure clean test
|
39
|
+
File.unlink("/tmp/cap.lock") if File.exists?("/tmp/cap.lock")
|
9
40
|
end
|
10
|
-
|
41
|
+
|
11
42
|
should "use default lockfile name 'cap.lock'" do
|
12
43
|
assert_equal @config.lockfile, 'cap.lock'
|
13
44
|
end
|
@@ -32,4 +63,36 @@ class TestCaplock < Test::Unit::TestCase
|
|
32
63
|
assert_nil @config.lock.release
|
33
64
|
assert_nil @config.lock.check
|
34
65
|
end
|
66
|
+
|
67
|
+
should "check for lock and abort" do
|
68
|
+
assert_nil @config.lock.create
|
69
|
+
assert_raises(Capistrano::Caplock::LockedDeployError) { @config.lock.check }
|
70
|
+
end
|
71
|
+
|
72
|
+
should "check if remote file exists" do
|
73
|
+
@config.set :lockfile, 'test.lock'
|
74
|
+
assert_nil @config.lock.create
|
75
|
+
assert @config.caplock.remote_file_exists?("/tmp/test.lock")
|
76
|
+
end
|
77
|
+
|
78
|
+
should "remove lock on rollback" do
|
79
|
+
@config.set :caplock_update_code_raise, true
|
80
|
+
assert_raises(TestCaplockError) { @config.deploy.default }
|
81
|
+
assert_nil @config.lock.check
|
82
|
+
end
|
83
|
+
|
84
|
+
should "not remove lock owned by other process" do
|
85
|
+
File.open("/tmp/cap.lock", "w") { |file| file.write("Simulate cap.lock from another deploy process") }
|
86
|
+
assert File.exists?("/tmp/cap.lock")
|
87
|
+
assert_raises(Capistrano::Caplock::LockedDeployError) { @config.deploy.default }
|
88
|
+
# rollback should not have removed the file, so it is still locked
|
89
|
+
assert_raises(Capistrano::Caplock::LockedDeployError) { @config.lock.check }
|
90
|
+
end
|
91
|
+
|
92
|
+
should "remove lock owned by other process on manual release" do
|
93
|
+
File.open("/tmp/cap.lock", "w") { |file| file.write("Simulate cap.lock from another deploy process") }
|
94
|
+
assert File.exists?("/tmp/cap.lock")
|
95
|
+
assert_nil @config.lock.release
|
96
|
+
assert_nil @config.lock.check
|
97
|
+
end
|
35
98
|
end
|
metadata
CHANGED
@@ -1,62 +1,57 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: caplock
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 0
|
10
|
-
version: 0.2.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Dru Ibarra
|
9
|
+
- Giorgio Premi
|
14
10
|
autorequire:
|
15
11
|
bindir: bin
|
16
12
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2014-07-08 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
21
16
|
name: capistrano
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
24
18
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 43
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 9
|
32
|
-
- 0
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
33
22
|
version: 2.9.0
|
34
23
|
type: :runtime
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: thoughtbot-shoulda
|
38
24
|
prerelease: false
|
39
|
-
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
26
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 2.9.0
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: thoughtbot-shoulda
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
48
39
|
type: :development
|
49
|
-
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
50
47
|
description: Adds a lock file to Capistrano deployments to prevent concurrent deployments.
|
51
48
|
email: Druwerd@gmail.com
|
52
49
|
executables: []
|
53
|
-
|
54
50
|
extensions: []
|
55
|
-
|
56
|
-
extra_rdoc_files:
|
51
|
+
extra_rdoc_files:
|
57
52
|
- LICENSE
|
58
53
|
- README.rdoc
|
59
|
-
files:
|
54
|
+
files:
|
60
55
|
- .document
|
61
56
|
- LICENSE
|
62
57
|
- README.rdoc
|
@@ -68,36 +63,26 @@ files:
|
|
68
63
|
- test/test_caplock.rb
|
69
64
|
homepage: http://github.com/Druwerd/caplock
|
70
65
|
licenses: []
|
71
|
-
|
72
66
|
post_install_message:
|
73
67
|
rdoc_options: []
|
74
|
-
|
75
|
-
require_paths:
|
68
|
+
require_paths:
|
76
69
|
- lib
|
77
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
71
|
none: false
|
79
|
-
requirements:
|
80
|
-
- -
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
|
83
|
-
|
84
|
-
- 0
|
85
|
-
version: "0"
|
86
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
77
|
none: false
|
88
|
-
requirements:
|
89
|
-
- -
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
|
92
|
-
segments:
|
93
|
-
- 0
|
94
|
-
version: "0"
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
95
82
|
requirements: []
|
96
|
-
|
97
83
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.8.
|
84
|
+
rubygems_version: 1.8.23
|
99
85
|
signing_key:
|
100
86
|
specification_version: 3
|
101
87
|
summary: Adds a lock file to Capistrano deployments
|
102
88
|
test_files: []
|
103
|
-
|