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.
@@ -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
-