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 ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Include rake in Gemfile so that `bundle exec rake` doesn't raise an error
4
+ gem 'rake', :group => :test
5
+
6
+ # Specify your gem's dependencies in capistrano_deploy_lock.gemspec
7
+ gemspec
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,3 @@
1
+ module CapistranoDeployLock
2
+ VERSION = "1.0.0"
3
+ 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: []