caplock 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 'rake/rdoctask'
47
- Rake::RDocTask.new do |rdoc|
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.2.0
1
+ 0.3.1
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{caplock}
8
- s.version = "0.2.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 = [%q{Dru Ibarra}]
12
- s.date = %q{2012-03-09}
13
- s.description = %q{Adds a lock file to Capistrano deployments to prevent concurrent deployments.}
14
- s.email = %q{Druwerd@gmail.com}
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 = %q{http://github.com/Druwerd/caplock}
31
- s.require_paths = [%q{lib}]
32
- s.rubygems_version = %q{1.8.6}
33
- s.summary = %q{Adds a lock file to Capistrano deployments}
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
@@ -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
- abort "\n\n\n\e[0;31m A Deployment is already in progress\n Remove #{deploy_to}/#{lockfile} to unlock \e[0m\n\n\n"
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
@@ -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
- hash: 23
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
- date: 2012-03-09 00:00:00 Z
19
- dependencies:
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
- prerelease: false
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
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: !ruby/object:Gem::Requirement
40
26
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 3
45
- segments:
46
- - 0
47
- version: "0"
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
- version_requirements: *id002
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
- hash: 3
83
- segments:
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
- hash: 3
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.6
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
-