nogara-capistrano-conditional 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MWMyZTk1ZGVkMDhlNTgyYTMzODRlMThjOGQwMTMzYmQwMmE2OGY0Mw==
5
+ data.tar.gz: !binary |-
6
+ ZGJlYzRjZDcxZjk3ZTZkYjc1OGE3ZDg4NDFhY2E5Njc0MDcwYWJmMA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YjY3MTY4N2RiMDI0M2NmOWJjNzllYTcxN2Q1MjY3NjQ1ZTkxMDllMmMwYjU4
10
+ MTU0NWM0MDc3MTM0NTdhODgxZDQ5NTQ2NzdjNWM4YTUwNDg2M2VkYTIyZDEw
11
+ ZTc3ZWE3MDBmNmFiYjI1MWY1NDkyYWZmNzg1MTJlZjgwYzBiOTE=
12
+ data.tar.gz: !binary |-
13
+ OTUyNjQ1M2E1MTUxODUyZTVjMTYzNTE4ZjA2NzMxYTYxMTYzYzNjZWZiZDc2
14
+ OTgyZjZjMDYyODY3YjA0NTY3YTYxMTMyMDg5NTU1N2Y5MWQxMzZiNTEwN2Ri
15
+ ZjRhOGE5MGFhMmZlY2RiODYwZTJhOTI0NjZlNWI1MmY0M2M1MmQ=
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in capistrano-conditional.gemspec
4
+ gemspec
@@ -0,0 +1,99 @@
1
+ # capistrano-conditional
2
+
3
+ This gem extends capistrano deployments to allow certain tasks to only be run under certain conditions -- i.e. conditionally.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ group :development do
10
+ gem 'capistrano-conditional', :require => false # <-- This is important!
11
+ end
12
+
13
+ And then modify your deploy.rb to include this at the top:
14
+
15
+ require "capistrano-conditional"
16
+
17
+ ## Requirements
18
+
19
+ Your application must already be using capistrano for deployments, and (for now at least) you need to be using git.
20
+
21
+ ## Usage Instructions
22
+
23
+ <code>capistrano-conditional</code> adds logic to be run before <code>cap deploy</code> or <code>cap deploy:migrations</code> that compares the local (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:
24
+
25
+ ConditionalDeploy.register :whenever, :watchlist => 'config/schedule.rb' do
26
+ after "deploy:symlink", "deploy:update_crontab"
27
+ end
28
+
29
+ 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>.
30
+
31
+ ### Available Conditions
32
+
33
+ There are currently four logic conditions available (well, five, but <code>:watchlist</code> is just an alias for <code>:any_match</code>):
34
+
35
+ * <code>:any_match</code> => file_list
36
+ * <code>:none_match</code> => file_list
37
+ * <code>:if</code> => Proc
38
+ * <code>:unless</code> => Proc
39
+
40
+ 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).
41
+
42
+ <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.
43
+
44
+ <code>:none_match</code> executes the block if NONE of the provided strings match ANY of file paths git reports changed.
45
+
46
+ If you need more custom control, <code>:if</code> and <code>:unless</code> expect a Proc (which will be passed the list of changed files, if one argument is expected, or the list of changes and the git object itself, if two arguments are expected and you really want to dive into things yourself).
47
+
48
+ ## Example Usage
49
+
50
+ ConditionalDeploy.register :whenever, :watchlist => 'config/schedule.rb' do
51
+ after "deploy:symlink", "deploy:update_crontab"
52
+ end
53
+
54
+ ConditionalDeploy.register :sphinx, :watchlist => ['db/schema.rb', 'db/migrate'] do
55
+ before "deploy:update_code", "thinking_sphinx:stop"
56
+ before "deploy:start", "thinking_sphinx:start"
57
+ before "deploy:restart", "thinking_sphinx:start"
58
+ end
59
+
60
+ ConditionalDeploy.register :jammit, :watchlist => ['public/images/embed', 'public/stylesheets', 'public/javascripts', 'public/assets', 'config/assets.yml'] do
61
+ after 'deploy:symlink', 'deploy:rebuild_assets'
62
+ end
63
+
64
+ # Restart the resque workers unless the only changes were to static assets, views, or controllers.
65
+ ConditionalDeploy.register(:resque, :unless => lambda { |changed| changed.all?{|f| f['public/'] || f['app/controllers/'] || f['app/views/'] } }) do
66
+ before "deploy:restart", "resque:workers:restart"
67
+ end
68
+
69
+ # ... note that you still have to actually DEFINE the tasks laid out above (e.g. deploy:update_crontab)
70
+
71
+
72
+ 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:
73
+
74
+ if ARGV.any?{|v| v['deploy:migrations']} # If running deploy:migrations
75
+ # If there weren't any changes to migrations or the schema file, then abort the deploy
76
+ ConditionalDeploy.register :unneeded_migrations, :none_match => ['db/schema.rb', 'db/migrate'] do
77
+ abort "You're running migrations, but it doesn't look like you need to!"
78
+ end
79
+ else # If NOT running deploy:migrations
80
+ # If there were changes to migration files, run migrations as part of the deployment
81
+ 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
82
+ after "deploy:update_code", "deploy:migrate"
83
+ end
84
+ end
85
+
86
+ Since I use it on every project, I've wrapped that logic up in a single command:
87
+
88
+ ConditionalDeploy.monitor_migrations(self)
89
+
90
+ ## Advanced Usage
91
+
92
+ By default <code>capistrano-conditional</code> will abort the deployment if you have uncommited changes in your working directory. You can skip this check on an individual run by setting the ALLOW_UNCOMMITED environment variable (e.g. <code>cap deploy ALLOW_UNCOMMITTED=1</code>).
93
+
94
+ 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>.
95
+
96
+ ## License
97
+
98
+ Copyright &copy; 2011 [Deviantech, Inc.](http://www.deviantech.com) and released under the MIT license.
99
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "capistrano-conditional/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nogara-capistrano-conditional"
7
+ s.version = Capistrano::Conditional::VERSION
8
+ s.authors = ["Kali Donovan"]
9
+ s.email = ["kali@deviantech.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Adds support for conditional deployment tasks in capistrano}
12
+ s.description = %q{Allows making tasks for git-based projects conditional based on the specific files to be deployed.}
13
+
14
+ s.rubyforge_project = "capistrano-conditional"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "git"
24
+ s.add_runtime_dependency "capistrano"
25
+ end
@@ -0,0 +1,5 @@
1
+ require "capistrano-conditional/version"
2
+ require 'git'
3
+ require "capistrano-conditional/unit"
4
+ require "capistrano-conditional/deploy"
5
+ require "capistrano-conditional/integration"
@@ -0,0 +1,101 @@
1
+ # This class handles the logic associated with checking if each conditional
2
+ # statement applies to a given deploy and, if so, applying them.
3
+ #
4
+ # The only publicly-useful method is <em>ConditionalDeploy.register</em>, which
5
+ # is used in deploy.rb to add conditional elements (see README for details).
6
+ class ConditionalDeploy
7
+
8
+ @@conditionals = []
9
+
10
+ def self.register(name, opts, &block)
11
+ raise("Already added a conditional with that name") if @@conditionals.any?{|c| c.name == name}
12
+ @@conditionals << Capistrano::Conditional::Unit.new(name, opts, block)
13
+ end
14
+
15
+ def self.monitor_migrations(context)
16
+ if ARGV.any?{|v| v['deploy:migrations']} # If running deploy:migrations
17
+ # If there weren't any changes to migrations or the schema file, then abort the deploy
18
+ ConditionalDeploy.register :unneeded_migrations, :none_match => ['db/schema.rb', 'db/migrate'] do
19
+ context.send :abort, "You're running migrations, but it doesn't look like you need to!"
20
+ end
21
+ else # If NOT running deploy:migrations
22
+ # If there were changes to migration files, run migrations as part of the deployment
23
+ 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
+ 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
35
+ end
36
+
37
+
38
+
39
+ def initialize(compare_to = 'HEAD^')
40
+ @logger = Capistrano::Logger.new(:output => STDOUT)
41
+ @logger.level = Capistrano::Logger::MAX_LEVEL
42
+
43
+ @verbose = true
44
+ @git = Git.open('.')
45
+ @last_deployed = @git.object(compare_to)
46
+ @diff = @git.diff('HEAD', compare_to)
47
+ @changed = @diff.stats[:files].keys.sort
48
+ @to_run = []
49
+ end
50
+
51
+ def ensure_local_up_to_date
52
+ return true if ENV['ALLOW_UNCOMMITTED']
53
+ s = @git.status
54
+ no_changes = %w(changed added deleted).all? { |attrib| s.send(attrib).empty? }
55
+
56
+ unless no_changes
57
+ abort "\nYour working copy contains local changes not yet committed to git. \nPlease commit all changes before deploying.\n\n"
58
+ end
59
+ end
60
+
61
+ def report_plan
62
+ def log(text = "\n", level = Capistrano::Logger::TRACE)
63
+ @logger.log(level, text, "Conditional")
64
+ end
65
+
66
+ log
67
+ log "Conditional Deployment Report:", Capistrano::Logger::IMPORTANT
68
+ log
69
+ log "\tLast deployed commit: #{@last_deployed.message}", Capistrano::Logger::DEBUG
70
+ log
71
+ log "\tFiles Modified:", Capistrano::Logger::DEBUG
72
+ @changed.each {|f| log "\t\t- #{f}"}
73
+ log
74
+ log "\tConditional Runlist:", Capistrano::Logger::DEBUG
75
+ if @to_run.empty?
76
+ log "\t\t* No conditional tasks have been added"
77
+ else
78
+ @to_run.each do |job|
79
+ out = job.message ? "#{job.name} (#{job.message})" : job.name
80
+ log "\t\t* Running #{out}"
81
+ end
82
+ end
83
+ log
84
+ end
85
+
86
+ def screen_conditionals
87
+ @@conditionals.each do |job|
88
+ force = job.name && ENV["RUN_#{job.name.to_s.upcase}"]
89
+ skip = job.name && ENV["SKIP_#{job.name.to_s.upcase}"]
90
+ next unless force || job.applies?(@changed)
91
+ next if skip
92
+ @to_run << job
93
+ end
94
+ end
95
+
96
+ def run_conditionals
97
+ @to_run.each do |job|
98
+ job.block.call
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,29 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ abort "\ncapistrano-conditional is not compatible with Capistrano 1.x\n" unless respond_to?(:namespace)
3
+ abort "\nGit is not defined (are you in a git repository, with the Git gem installed?)\n" unless defined?(Git)
4
+
5
+ namespace :conditional do
6
+ desc "Initializes the conditional deployment functionality"
7
+ task :apply do
8
+ deployed_hash = capture("cat #{current_path}/REVISION").strip
9
+ ConditionalDeploy.apply_conditions!( deployed_hash )
10
+ end
11
+
12
+ desc "Tests to be sure that the newest local and remote git commits match"
13
+ task :ensure_latest_git do
14
+ remote = capture("cd #{shared_path}/cached-copy && git log --format=oneline -n 1", :pty => false)
15
+ local = run_locally("git log --format=oneline -n 1")
16
+
17
+ unless local == remote
18
+ 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")
19
+ end
20
+ end
21
+ end
22
+
23
+ # Ensure deploys apply conditional elements before running the rest of the tasks
24
+ before 'deploy', 'conditional:apply'
25
+ before 'deploy:migrations', 'conditional:apply'
26
+
27
+ # Abort deployment if mismatch between local and remote git repositories
28
+ after 'deploy:update_code', 'conditional:ensure_latest_git'
29
+ end
@@ -0,0 +1,71 @@
1
+ module Capistrano
2
+ module Conditional
3
+ # Stores the actual conditionals added by the user, including under
4
+ # what conditions the conditional should be applied (<em>conditions</em>)
5
+ # and what to do if that's the case (<em>block</em>).
6
+ #
7
+ # Created from <em>ConditionalDeploy.register</em>, the end user should
8
+ # never need to interact with it directly.
9
+ class Unit
10
+ attr_accessor :name, :message, :conditions, :block
11
+
12
+ def initialize(name, opts, block)
13
+ @name = name
14
+ @message = opts.delete(:msg)
15
+ @block = block
16
+ @conditions = {}
17
+ opts.each do |k,v|
18
+ @conditions[k] = v
19
+ end
20
+ end
21
+
22
+ # Currently supported options: any_match (aliased to watchlist), none_match, if, unless
23
+ def applies?(changed)
24
+ @changed = changed
25
+ any_match_applies? && none_match_applies? && if_applies? && unless_applies?
26
+ end
27
+
28
+ protected
29
+
30
+ def any_match_applies?
31
+ any_files_match?(:any_match) && any_files_match?(:watchlist)
32
+ end
33
+
34
+ def none_match_applies?
35
+ Array(conditions[:none_match]).all? do |watched|
36
+ !@changed.any? { |path| path[watched] }
37
+ end
38
+ end
39
+
40
+ def if_applies?
41
+ return true if conditions[:if].nil?
42
+ condition_true?(:if)
43
+ end
44
+
45
+ def unless_applies?
46
+ return true if conditions[:unless].nil?
47
+ !condition_true?(:unless)
48
+ end
49
+
50
+
51
+
52
+ def any_files_match?(key)
53
+ return true unless conditions[key]
54
+ Array(conditions[key]).any? do |watched|
55
+ @changed.any? { |path| path[watched] }
56
+ end
57
+ end
58
+
59
+ def condition_true?(label)
60
+ c = conditions[label]
61
+ case c.arity
62
+ when 0 then c.call
63
+ when 1 then c.call(@changed)
64
+ else 2
65
+ c.call(@changed, @git)
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Conditional
3
+ VERSION = "0.0.3"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nogara-capistrano-conditional
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Kali Donovan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: git
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: capistrano
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Allows making tasks for git-based projects conditional based on the specific
42
+ files to be deployed.
43
+ email:
44
+ - kali@deviantech.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - Gemfile
51
+ - README.md
52
+ - Rakefile
53
+ - capistrano-conditional.gemspec
54
+ - lib/capistrano-conditional.rb
55
+ - lib/capistrano-conditional/deploy.rb
56
+ - lib/capistrano-conditional/integration.rb
57
+ - lib/capistrano-conditional/unit.rb
58
+ - lib/capistrano-conditional/version.rb
59
+ homepage: ''
60
+ licenses: []
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: capistrano-conditional
78
+ rubygems_version: 2.0.3
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Adds support for conditional deployment tasks in capistrano
82
+ test_files: []