capistrano_deploy_lock 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/.gitignore +17 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +1 -0
- data/capistrano_deploy_lock.gemspec +20 -0
- data/lib/capistrano/deploy_lock.rb +138 -0
- data/lib/capistrano_deploy_lock/version.rb +3 -0
- data/lib/capistrano_deploy_lock.rb +1 -0
- metadata +62 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Nathan Broadbent
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Capistrano Deploy Lock
|
2
|
+
|
3
|
+
Lock a server during deploy, to prevent people from deploying at the same time.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'capistrano_deploy_lock'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Add this line to your `config/deploy.rb`:
|
17
|
+
|
18
|
+
require 'capistrano/deploy_lock'
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Your deploys will now be protected by a lock. Simply run `cap deploy` as usual.
|
23
|
+
However, if someone else trys to deploy at the same time, their deploy will abort
|
24
|
+
with an error like this:
|
25
|
+
|
26
|
+
```
|
27
|
+
*** Deploy locked 3 minutes ago by 'ndbroadbent'
|
28
|
+
*** Message: Deploying master branch
|
29
|
+
*** Expires in 12 minutes
|
30
|
+
.../capistrano/deploy_lock.rb:132:in `block (3 levels) in <top (required)>': Capistrano::DeployLockedError (Capistrano::DeployLockedError)
|
31
|
+
```
|
32
|
+
|
33
|
+
The default 'deploy' lock will expire after 15 minutes, assuming that your deploys will not take more time than this.
|
34
|
+
This is so that crashed or interrupted deploys don't leave a stale lock for the next developer to deal with.
|
35
|
+
This default expiry time can be configured with:
|
36
|
+
|
37
|
+
set :default_lock_expiry, (20 * 60) # Sets the default expiry to 20 minutes
|
38
|
+
|
39
|
+
Anyone can remove a lock by running:
|
40
|
+
|
41
|
+
cap deploy:unlock
|
42
|
+
|
43
|
+
The lock file will be created at `#{shared_path}/capistrano.lock.yml` by default. You can configure this with:
|
44
|
+
|
45
|
+
set :deploy_lockfile, "path/to/deploy/lock/file"
|
46
|
+
|
47
|
+
|
48
|
+
## Manual locks
|
49
|
+
|
50
|
+
You can explicitly set a deploy lock by running:
|
51
|
+
|
52
|
+
cap deploy:lock
|
53
|
+
|
54
|
+
You will receive two prompts:
|
55
|
+
|
56
|
+
* Lock Message:
|
57
|
+
|
58
|
+
Type the reason for the lock. This message will be displayed to any developers who attempt to deploy.
|
59
|
+
|
60
|
+
* Expire lock at? (optional):
|
61
|
+
|
62
|
+
Set an expiry time for the lock. Leave this blank to make the lock last until someone removes it with `cap deploy:unlock`.
|
63
|
+
|
64
|
+
If the [chronic](https://github.com/mojombo/chronic) gem is available, you can type
|
65
|
+
natural language times like `2 hours`, or `tomorrow at 6am`. If not, you must type times in a format that `DateTime.parse()` can handle,
|
66
|
+
such as `06:30:00` or `2012-12-12 00:00:00`.
|
67
|
+
|
68
|
+
The `cap deploy:check_lock` task will automatically delete any expired locks.
|
69
|
+
|
70
|
+
## Thanks
|
71
|
+
|
72
|
+
Special thanks to [David Bock](https://github.com/bokmann), who wrote the [deploy_lock.rb](https://github.com/bokmann/dunce-cap/blob/master/recipes/deploy_lock.rb)
|
73
|
+
script that this is based on.
|
74
|
+
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
1. Fork it
|
79
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
80
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
81
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
82
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'capistrano_deploy_lock/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "capistrano_deploy_lock"
|
8
|
+
gem.version = CapistranoDeployLock::VERSION
|
9
|
+
gem.authors = ["Nathan Broadbent"]
|
10
|
+
gem.email = ["nathan.f77@gmail.com"]
|
11
|
+
gem.description = %q{Lock a server during deploy, to prevent people from deploying at the same time.}
|
12
|
+
gem.summary = %q{Capistrano Deploy Lock}
|
13
|
+
gem.homepage = "https://github.com/ndbroadbent/capistrano_deploy_lock"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# Capistrano Deploy Lock
|
2
|
+
#
|
3
|
+
# Add "require 'capistrano/deploy_lock'" in your Capistrano deploy.rb.
|
4
|
+
# Based on deploy_lock.rb from https://github.com/bokmann/dunce-cap
|
5
|
+
|
6
|
+
require 'time'
|
7
|
+
# Provide advanced expiry time parsing via Chronic, if available
|
8
|
+
begin; require 'chronic'; rescue LoadError; end
|
9
|
+
begin
|
10
|
+
# Use Rails distance_of_time_in_words_to_now helper if available
|
11
|
+
require 'action_view'
|
12
|
+
module Capistrano
|
13
|
+
class DateHelper
|
14
|
+
class << self
|
15
|
+
include ActionView::Helpers::DateHelper
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
end
|
21
|
+
|
22
|
+
Capistrano::DeployLockedError = Class.new(StandardError)
|
23
|
+
|
24
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
25
|
+
before "deploy", "deploy:check_lock"
|
26
|
+
before "deploy", "deploy:create_lock"
|
27
|
+
after "deploy", "deploy:unlock"
|
28
|
+
|
29
|
+
# Default lock expiry of 15 minutes (in case deploy crashes or is interrupted)
|
30
|
+
_cset :default_lock_expiry, (15 * 60)
|
31
|
+
_cset(:deploy_lockfile) { "#{shared_path}/capistrano.lock.yml" }
|
32
|
+
|
33
|
+
# Show lock message as bright red
|
34
|
+
log_formatter(:match => /Deploy locked/, :color => :red, :style => :bright, :priority => 20)
|
35
|
+
|
36
|
+
namespace :deploy do
|
37
|
+
# Set deploy lock with a custom lock message and expiry time
|
38
|
+
task :lock do
|
39
|
+
set :lock_message, Capistrano::CLI.ui.ask("Lock Message: ")
|
40
|
+
|
41
|
+
while self[:lock_expiry].nil?
|
42
|
+
expiry_str = Capistrano::CLI.ui.ask("Expire lock at? (optional): ")
|
43
|
+
if expiry_str == ""
|
44
|
+
# Never expire an explicit lock if no time given
|
45
|
+
set :lock_expiry, false
|
46
|
+
else
|
47
|
+
parsed_expiry = nil
|
48
|
+
if defined?(Chronic)
|
49
|
+
parsed_expiry = (Chronic.parse(expiry_str) || Chronic.parse("#{expiry_str} from now"))
|
50
|
+
else
|
51
|
+
if dt = (DateTime.parse(expiry_str) rescue nil)
|
52
|
+
parsed_expiry = dt.to_time
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if parsed_expiry
|
57
|
+
set :lock_expiry, parsed_expiry.utc
|
58
|
+
else
|
59
|
+
logger.info "'#{expiry_str}' could not be parsed. Please try again."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
create_lock
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Creates a lock file, so that futher deploys will be prevented."
|
68
|
+
task :create_lock do
|
69
|
+
if self[:lock_message].nil?
|
70
|
+
set :lock_message, "Deploying #{branch} branch"
|
71
|
+
end
|
72
|
+
if self[:lock_expiry].nil?
|
73
|
+
set :lock_expiry, (Time.now + default_lock_expiry).utc
|
74
|
+
end
|
75
|
+
|
76
|
+
lock = {
|
77
|
+
:created_at => Time.now.utc,
|
78
|
+
:username => ENV['USER'],
|
79
|
+
:expire_at => self[:lock_expiry],
|
80
|
+
:message => self[:lock_message]
|
81
|
+
}
|
82
|
+
put lock.to_yaml, deploy_lockfile, :mode => 0777
|
83
|
+
end
|
84
|
+
|
85
|
+
desc "Unlocks the server for deployment."
|
86
|
+
task :unlock do
|
87
|
+
run "rm -f #{deploy_lockfile}"
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "Checks for a deploy lock. If present, deploy is aborted and message is displayed. Any expired locks are deleted."
|
91
|
+
task :check_lock do
|
92
|
+
lock_file = capture("[ -e #{deploy_lockfile} ] && cat #{deploy_lockfile} || true").strip
|
93
|
+
|
94
|
+
if lock_file != ""
|
95
|
+
lock = YAML.load(lock_file)
|
96
|
+
|
97
|
+
if lock[:expire_at] && lock[:expire_at] < Time.now
|
98
|
+
logger.info "Deleting expired deploy lock..."
|
99
|
+
unlock
|
100
|
+
else
|
101
|
+
if defined?(Capistrano::DateHelper)
|
102
|
+
locked_ago = Capistrano::DateHelper.distance_of_time_in_words_to_now lock[:created_at].localtime
|
103
|
+
message = "Deploy locked #{locked_ago} ago"
|
104
|
+
else
|
105
|
+
message = "Deploy locked at #{lock[:created_at].localtime}"
|
106
|
+
end
|
107
|
+
|
108
|
+
message << " by '#{lock[:username]}'\nMessage: #{lock[:message]}"
|
109
|
+
|
110
|
+
if lock[:expire_at]
|
111
|
+
if defined?(Capistrano::DateHelper)
|
112
|
+
expires_in = Capistrano::DateHelper.distance_of_time_in_words_to_now lock[:expire_at].localtime
|
113
|
+
message << "\nExpires in #{expires_in}"
|
114
|
+
else
|
115
|
+
message << "\nExpires at #{lock[:expire_at].localtime.strftime("%H:%M:%S")}"
|
116
|
+
end
|
117
|
+
else
|
118
|
+
message << "\nLock must be manually removed with: cap #{stage} deploy:unlock"
|
119
|
+
end
|
120
|
+
|
121
|
+
logger.important message
|
122
|
+
|
123
|
+
# Don't raise exception if current user owns the lock.
|
124
|
+
# Just sleep so they have a chance to Ctrl-C
|
125
|
+
if lock[:username] == ENV['USER']
|
126
|
+
4.downto(1) do |i|
|
127
|
+
Kernel.print "\rDeploy lock was created by you (#{ENV['USER']}). Continuing deploy in #{i}..."
|
128
|
+
sleep 1
|
129
|
+
end
|
130
|
+
puts
|
131
|
+
else
|
132
|
+
raise Capistrano::DeployLockedError
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "capistrano_deploy_lock/version"
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capistrano_deploy_lock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nathan Broadbent
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-15 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Lock a server during deploy, to prevent people from deploying at the
|
15
|
+
same time.
|
16
|
+
email:
|
17
|
+
- nathan.f77@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE.txt
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- capistrano_deploy_lock.gemspec
|
28
|
+
- lib/capistrano/deploy_lock.rb
|
29
|
+
- lib/capistrano_deploy_lock.rb
|
30
|
+
- lib/capistrano_deploy_lock/version.rb
|
31
|
+
homepage: https://github.com/ndbroadbent/capistrano_deploy_lock
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
hash: 1879672784853574210
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
hash: 1879672784853574210
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.8.24
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Capistrano Deploy Lock
|
62
|
+
test_files: []
|