capistrano-conditional 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -15
- data/lib/capistrano-conditional/deploy.rb +92 -59
- data/lib/capistrano-conditional/integration.rb +11 -17
- data/lib/capistrano-conditional/unit.rb +1 -1
- data/lib/capistrano-conditional/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64ab0159258e31d984ab66b427d33caf82dfaa23
|
4
|
+
data.tar.gz: 9b5fbe11ff09c04be713d8fba77b3a28c921a5bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f36f346973da57b1d9a0abaedc40a81d6ec131e93890b6c5aa07ed0fe9e2d8ecc38ec76e7b604d126a123511630e642ea8c18306508df472067924625d81bda
|
7
|
+
data.tar.gz: 070d4a2781d1e1b1a85294f56339d44c1574c23afd3071b236963fe45187a096b33acada75e183f51031445cd18afc4c86d8640793bf4211944c418d0c350cc9
|
data/README.md
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# capistrano-conditional
|
2
2
|
|
3
|
-
This gem extends capistrano deployments to allow certain tasks to only be run under certain conditions -- i.e. conditionally.
|
3
|
+
This gem extends capistrano v2 deployments to allow certain tasks to only be run under certain conditions -- i.e. conditionally.
|
4
4
|
|
5
5
|
It hasn't yet been extended to work with Capistrano 3; pull requests welcomed!
|
6
6
|
|
7
|
+
## BREAKING UPDATES
|
8
|
+
|
9
|
+
* v. 0.1.0 pulls the branch to be deployed from capistrano multistage, rather than assuming it's whatever you currently have checked out in your working directory (among other things, this allows tweaking the deploy recipe multiple times without having to commit those changes).
|
10
|
+
|
7
11
|
## Installation
|
8
12
|
|
9
13
|
Add to your Gemfile:
|
@@ -22,7 +26,7 @@ Your application must already be using capistrano for deployments, and (for now
|
|
22
26
|
|
23
27
|
## Usage Instructions
|
24
28
|
|
25
|
-
<code>capistrano-conditional</code> adds logic to be run before <code>cap deploy</code> or <code>cap deploy:migrations</code> that compares the
|
29
|
+
<code>capistrano-conditional</code> adds logic to be run before <code>cap deploy</code> or <code>cap deploy:migrations</code> that compares the to-be-deployed code with the existing remote (currently deployed) code and lists all files that will be updated by the current deploy. It then checks the list of conditional statements that you've provided and runs any that you want run -- e.g. if you're using [whenever](https://github.com/javan/whenever) and you only want to run the <code>deploy:update_crontab</code> task if <code>config/schedule.rb</code> has been changed, you'd add a block like this to your deploy.rb:
|
26
30
|
|
27
31
|
ConditionalDeploy.register :whenever, :watchlist => 'config/schedule.rb' do
|
28
32
|
after "deploy:symlink", "deploy:update_crontab"
|
@@ -30,6 +34,10 @@ Your application must already be using capistrano for deployments, and (for now
|
|
30
34
|
|
31
35
|
This example registers a conditional named "whenever" (names aren't programmatically important, but they're used to report what's going to be run at the beginning of each deploy). The contents of the block will be run only if the list of changed files includes a path that matches <code>config/schedule.rb</code>.
|
32
36
|
|
37
|
+
### Setting branches
|
38
|
+
|
39
|
+
By default, capistrano-conditional pics up the branch-to-be-deployed from the `:branch` setting used by capistrano multistage, or defaults to `HEAD`. To specify a different branch manually: `set :git_deploying, 'some/other/branch/name'`.
|
40
|
+
|
33
41
|
### Available Conditions
|
34
42
|
|
35
43
|
There are currently four logic conditions available (well, five, but <code>:watchlist</code> is just an alias for <code>:any_match</code>):
|
@@ -38,7 +46,7 @@ There are currently four logic conditions available (well, five, but <code>:watc
|
|
38
46
|
* <code>:none_match</code> => file_list
|
39
47
|
* <code>:if</code> => Proc
|
40
48
|
* <code>:unless</code> => Proc
|
41
|
-
|
49
|
+
|
42
50
|
Where file_list is either a string or an array of strings which will be <em>matched</em> against the list of changed filenames from git (so <code>:any_match => ['db/migrate']</code> would be true if ANY migration file was added, modified, or deleted).
|
43
51
|
|
44
52
|
<code>:any_match</code> (aliased as <code>:watchlist</code>) executes the block if ANY of the provided strings match ANY of file paths git reports changed.
|
@@ -66,17 +74,17 @@ Only run the rake task to update the crontab if the schedule has changed:
|
|
66
74
|
Only restart the sphinx daemon if our database or schema has changed. Otherwise, just copy the generated sphinx config from the previous release:
|
67
75
|
|
68
76
|
SPHINX_WATCHLIST = ['db/schema.rb', 'db/migrate', 'sphinx.yml', 'app/indices']
|
69
|
-
|
77
|
+
|
70
78
|
ConditionalDeploy.register :sphinx, :watchlist => SPHINX_WATCHLIST do
|
71
79
|
before "deploy:update_code", "thinking_sphinx:stop"
|
72
80
|
before "deploy:start", "thinking_sphinx:start"
|
73
81
|
before "deploy:restart", "thinking_sphinx:start"
|
74
82
|
end
|
75
|
-
|
83
|
+
|
76
84
|
ConditionalDeploy.register :no_sphinx, :none_match => SPHINX_WATCHLIST do
|
77
85
|
after "deploy:update_code", "sphinx:copy_config"
|
78
86
|
end
|
79
|
-
|
87
|
+
|
80
88
|
namespace :sphinx do
|
81
89
|
desc 'Copy the config file from previous release, if available, or else rerun configuration'
|
82
90
|
task :copy_config, :roles => :app do
|
@@ -97,7 +105,7 @@ For pre-asset-pipeline versions of Rails, this snippet will reprocess your asset
|
|
97
105
|
#### Migrations
|
98
106
|
|
99
107
|
I've got <code>cap deploy</code> in muscle memory, and I used to find myself forgetting to run <code>cap deploy:migrations</code> until after I tested the new changes and found staging wasn't working right. I now add the following code to my apps, so I never have to worry about it again:
|
100
|
-
|
108
|
+
|
101
109
|
if ARGV.any?{|v| v['deploy:migrations']} # If running deploy:migrations
|
102
110
|
# If there weren't any changes to migrations or the schema file, then abort the deploy
|
103
111
|
ConditionalDeploy.register :unneeded_migrations, :none_match => ['db/schema.rb', 'db/migrate'] do
|
@@ -107,19 +115,15 @@ I've got <code>cap deploy</code> in muscle memory, and I used to find myself for
|
|
107
115
|
# If there were changes to migration files, run migrations as part of the deployment
|
108
116
|
ConditionalDeploy.register :forgotten_migrations, :any_match => ['db/schema.rb', 'db/migrate'], :msg => "Forgot to run migrations? It's cool, we'll do it for you." do
|
109
117
|
after "deploy:update_code", "deploy:migrate"
|
110
|
-
end
|
118
|
+
end
|
111
119
|
end
|
112
120
|
|
113
|
-
Since I use it on every project, I've wrapped that logic up
|
121
|
+
Since I use it on every project, I've wrapped that logic up into this gem. To enable, just add `set :monitor_migrations, true`.
|
114
122
|
|
115
|
-
ConditionalDeploy.monitor_migrations(self)
|
116
|
-
|
117
123
|
## Advanced Usage
|
118
124
|
|
119
|
-
|
120
|
-
|
121
|
-
If you need to force a particular conditional to run, you can also do that via the environment. Given the examples above, if you want to run the conditional named <code>whenever</code> even though config/schedule.rb hasn't been changed, just run <code>cap deploy RUN_WHENEVER=1</code>. Similarly, if you needed to skip the <code>whenever</code> conditional which would otherwise be run, you can use <code>cap deploy SKIP_WHENEVER=1</code>.
|
125
|
+
If you need to force a particular conditional to run, you can do so via the environment. Given the examples above, if you want to run the conditional named <code>whenever</code> even though config/schedule.rb hasn't been changed, just run <code>cap deploy RUN_WHENEVER=1</code>. Similarly, if you needed to skip the <code>whenever</code> conditional which would otherwise be run, you can use <code>cap deploy SKIP_WHENEVER=1</code>.
|
122
126
|
|
123
127
|
## License
|
124
128
|
|
125
|
-
Copyright ©
|
129
|
+
Copyright © 2014 [Deviantech, Inc.](http://www.deviantech.com) and released under the MIT license.
|
@@ -12,6 +12,10 @@ class ConditionalDeploy
|
|
12
12
|
@@conditionals << Capistrano::Conditional::Unit.new(name, opts, block)
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.configure
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
15
19
|
def self.monitor_migrations(context)
|
16
20
|
if ARGV.any?{|v| v['deploy:migrations']} # If running deploy:migrations
|
17
21
|
# If there weren't any changes to migrations or the schema file, then abort the deploy
|
@@ -22,80 +26,109 @@ class ConditionalDeploy
|
|
22
26
|
# If there were changes to migration files, run migrations as part of the deployment
|
23
27
|
ConditionalDeploy.register :forgotten_migrations, :any_match => ['db/schema.rb', 'db/migrate'], :msg => "Forgot to run migrations? It's cool, we'll do it for you." do
|
24
28
|
context.after "deploy:update_code", "deploy:migrate"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.apply_conditions!(deployed)
|
30
|
-
conditional = self.new(deployed)
|
31
|
-
conditional.ensure_local_up_to_date
|
32
|
-
conditional.screen_conditionals
|
33
|
-
conditional.report_plan
|
34
|
-
conditional.run_conditionals
|
29
|
+
end
|
30
|
+
end
|
35
31
|
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
def initialize(compare_to = 'HEAD^')
|
33
|
+
def initialize(current, deploying)
|
40
34
|
@logger = Capistrano::Logger.new(:output => STDOUT)
|
41
35
|
@logger.level = Capistrano::Logger::MAX_LEVEL
|
42
|
-
|
36
|
+
|
43
37
|
@verbose = true
|
44
|
-
@git
|
45
|
-
@
|
46
|
-
@
|
47
|
-
@
|
48
|
-
|
38
|
+
@git = Git.open('.')
|
39
|
+
@working = get_object 'HEAD'
|
40
|
+
@current = get_object current, 'currently deployed'
|
41
|
+
@deploying = get_object deploying, 'about to be deployed'
|
42
|
+
|
43
|
+
@diff = @git.diff(current, deploying)
|
44
|
+
@changed = @diff.stats[:files].keys.compact.sort
|
45
|
+
@to_run = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def apply_conditions!
|
49
|
+
screen_conditionals
|
50
|
+
report_plan
|
51
|
+
run_conditionals
|
49
52
|
end
|
50
53
|
|
51
|
-
|
52
|
-
return true if ENV['ALLOW_UNCOMMITTED']
|
53
|
-
s = @git.status
|
54
|
-
no_changes = %w(changed added deleted).all? { |attrib| s.send(attrib).empty? }
|
54
|
+
protected
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
def get_object(name, desc=nil)
|
57
|
+
@git.object(name)
|
58
|
+
rescue Git::GitExecuteError => e
|
59
|
+
msg = desc ? "(#{desc}) #{name}" : name
|
60
|
+
abort "Unable to find git object for #{msg}. Is your local repository up to date?\n\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
def report_plan
|
64
|
+
@plan = []
|
65
|
+
set_report_header
|
66
|
+
set_report_files
|
67
|
+
set_report_runlist
|
68
|
+
log_plan @plan
|
58
69
|
end
|
59
|
-
end
|
60
70
|
|
61
|
-
|
62
|
-
|
63
|
-
|
71
|
+
def screen_conditionals
|
72
|
+
@@conditionals.each do |job|
|
73
|
+
force = job.name && ENV["RUN_#{job.name.to_s.upcase}"]
|
74
|
+
skip = job.name && ENV["SKIP_#{job.name.to_s.upcase}"]
|
75
|
+
next unless force || job.applies?(@changed)
|
76
|
+
next if skip
|
77
|
+
@to_run << job
|
78
|
+
end
|
64
79
|
end
|
65
|
-
|
66
|
-
|
67
|
-
log "Conditional Deployment Report:"
|
68
|
-
log
|
69
|
-
log "\tLast deployed commit: #{@last_deployed.message}"
|
70
|
-
log
|
71
|
-
log "\tFiles Modified:"
|
72
|
-
@changed.each {|f| log "\t\t- #{f}"}
|
73
|
-
log
|
74
|
-
log "\tConditional Runlist:"
|
75
|
-
if @to_run.empty?
|
76
|
-
log "\t\t* No conditional tasks have been added"
|
77
|
-
else
|
80
|
+
|
81
|
+
def run_conditionals
|
78
82
|
@to_run.each do |job|
|
79
|
-
|
80
|
-
log "\t\t* Running #{out}"
|
83
|
+
job.block.call
|
81
84
|
end
|
82
85
|
end
|
83
|
-
log
|
84
|
-
end
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@
|
87
|
+
def set_report_header
|
88
|
+
@plan << ''
|
89
|
+
@plan << 'Conditional Deployment Report:'
|
90
|
+
@plan << ''
|
91
|
+
@plan << "\tCurrently deployed: #{commit_details @current}"
|
92
|
+
@git.log.between(@current, @deploying).each{|l| @plan << "\t\t* #{commit_details l}"}
|
93
|
+
@plan << "\tPreparing to deploy: #{commit_details @deploying}"
|
94
|
+
@plan << ''
|
93
95
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
|
97
|
+
def set_report_files
|
98
|
+
if @changed.length == 0
|
99
|
+
@plan << "\tNo files were modified."
|
100
|
+
else
|
101
|
+
@plan << "\tFiles Modified:"
|
102
|
+
@changed.each do |file|
|
103
|
+
@plan << "\t\t- #{file}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@plan << ''
|
99
107
|
end
|
100
|
-
|
108
|
+
|
109
|
+
def set_report_runlist
|
110
|
+
@plan << "\tConditional Runlist:"
|
111
|
+
@plan << ''
|
112
|
+
if @to_run.empty?
|
113
|
+
@plan << "\t\t* No conditional tasks have been added"
|
114
|
+
else
|
115
|
+
@to_run.each do |job|
|
116
|
+
out = job.message ? "#{job.name} (#{job.message})" : job.name
|
117
|
+
@plan << "\t\t* Running #{out}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
@plan << ''
|
121
|
+
end
|
122
|
+
|
123
|
+
def commit_details(c)
|
124
|
+
# extra = "(#{c.author.name} at #{c.date.strftime("%H:%M %Z on %B %e")})"
|
125
|
+
"#{c.sha} #{c.message.split("\n").first}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def log_plan(lines = "\n", level = Capistrano::Logger::TRACE)
|
129
|
+
Array(lines).each do |line|
|
130
|
+
@logger.log(level, ': ' + line, "Conditional")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
101
134
|
end
|
@@ -2,33 +2,27 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
2
2
|
log_formatter([
|
3
3
|
{ :match => /^Conditional$/, :color => :cyan, :style => :dim, :priority => 10 }
|
4
4
|
])
|
5
|
-
|
6
|
-
|
5
|
+
|
7
6
|
abort "\ncapistrano-conditional is not compatible with Capistrano 1.x\n" unless respond_to?(:namespace)
|
8
7
|
abort "\nGit is not defined (are you in a git repository, with the Git gem installed?)\n" unless defined?(Git)
|
9
8
|
|
9
|
+
# By default, assume using multi-stage deployment setting :branch variable, and that the local branch is up to date
|
10
|
+
# (although don't require being on that branch to deploy).
|
11
|
+
set :git_deploying, -> { fetch(:branch).blank? ? 'HEAD' : "origin/#{fetch(:branch)}"}
|
12
|
+
set :git_currently_deployed, -> { capture("cat #{current_path}/REVISION").strip }
|
13
|
+
set :monitor_migrations, -> { false }
|
14
|
+
|
10
15
|
namespace :conditional do
|
11
16
|
desc "Initializes the conditional deployment functionality"
|
12
17
|
task :apply do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
desc "Tests to be sure that the newest local and remote git commits match"
|
18
|
-
task :ensure_latest_git do
|
19
|
-
remote = capture("cd #{shared_path}/cached-copy && git log --format=oneline -n 1", :pty => false)
|
20
|
-
local = run_locally("git log --format=oneline -n 1")
|
21
|
-
|
22
|
-
unless local == remote
|
23
|
-
abort("\nLocal and remote git repositories have different HEADs:\n Local: #{local} Remote: #{remote}\n Make sure you've committed your latest changes, or else pull down the remote updates and try again\n")
|
24
|
-
end
|
18
|
+
ConditionalDeploy.monitor_migrations(self) if monitor_migrations
|
19
|
+
|
20
|
+
@deploy = ConditionalDeploy.new(git_currently_deployed, git_deploying)
|
21
|
+
@deploy.apply_conditions!
|
25
22
|
end
|
26
23
|
end
|
27
24
|
|
28
25
|
# Ensure deploys apply conditional elements before running the rest of the tasks
|
29
26
|
before 'deploy', 'conditional:apply'
|
30
27
|
before 'deploy:migrations', 'conditional:apply'
|
31
|
-
|
32
|
-
# Abort deployment if mismatch between local and remote git repositories
|
33
|
-
after 'deploy:update_code', 'conditional:ensure_latest_git'
|
34
28
|
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capistrano-conditional
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kali Donovan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: capistrano
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '2.5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.5'
|
41
41
|
description: Allows making tasks for git-based projects conditional based on the specific
|
@@ -46,7 +46,7 @@ executables: []
|
|
46
46
|
extensions: []
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
|
-
- .gitignore
|
49
|
+
- ".gitignore"
|
50
50
|
- CHANGELOG.md
|
51
51
|
- Gemfile
|
52
52
|
- README.md
|
@@ -66,17 +66,17 @@ require_paths:
|
|
66
66
|
- lib
|
67
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- -
|
69
|
+
- - ">="
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
78
|
rubyforge_project: capistrano-conditional
|
79
|
-
rubygems_version: 2.
|
79
|
+
rubygems_version: 2.3.0
|
80
80
|
signing_key:
|
81
81
|
specification_version: 4
|
82
82
|
summary: Adds support for conditional deployment tasks in capistrano
|