Class: Harrison::Deploy
Defined Under Namespace
Classes: Phase
Instance Attribute Summary collapse
-
#artifact ⇒ Object
Returns the value of attribute artifact.
-
#deploy_link ⇒ Object
Returns the value of attribute deploy_link.
-
#host ⇒ Object
The specific host among –hosts that we are currently working on.
-
#phases ⇒ Object
Returns the value of attribute phases.
-
#release_dir ⇒ Object
Returns the value of attribute release_dir.
-
#rollback ⇒ Object
Returns the value of attribute rollback.
Attributes inherited from Base
Instance Method Summary collapse
- #add_phase(name, &block) ⇒ Object
- #cleanup_deploys(limit) ⇒ Object
- #cleanup_releases ⇒ Object
- #close(host = nil) ⇒ Object
- #current_symlink ⇒ Object
-
#initialize(opts = {}) ⇒ Deploy
constructor
A new instance of Deploy.
- #invoke_user_block ⇒ Object
- #parse(args) ⇒ Object
- #remote_exec(cmd) ⇒ Object
- #revert_current_symlink ⇒ Object
- #run ⇒ Object
- #update_current_symlink ⇒ Object
Methods inherited from Base
#download, #exec, option_helper, #upload
Constructor Details
#initialize(opts = {}) ⇒ Deploy
Returns a new instance of Deploy.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/harrison/deploy.rb', line 15 def initialize(opts={}) # Config helpers for Harrisonfile. self.class.option_helper(:hosts) self.class.option_helper(:env) self.class.option_helper(:base_dir) self.class.option_helper(:deploy_via) self.class.option_helper(:keep) self.class.option_helper(:confirm) # Command line opts for this action. Will be merged with common opts. arg_opts = [ [ :hosts, "List of remote hosts to deploy to. Can also be specified in Harrisonfile.", :type => :strings ], [ :env, "Environment to deploy to. This can be examined in your Harrisonfile to calculate target hosts.", :type => :string ], [ :keep, "Number of recent deploys to keep after a successful deploy. (Including the most recent deploy.) Defaults to keeping all deploys forever.", :type => :integer ], [ :confirm, "Whether to interactively confirm the list of target hosts for deployment.", :type => :flag, :default => true ], ] super(arg_opts, opts) self.add_default_phases end |
Instance Attribute Details
#artifact ⇒ Object
Returns the value of attribute artifact.
5 6 7 |
# File 'lib/harrison/deploy.rb', line 5 def artifact @artifact end |
#deploy_link ⇒ Object
Returns the value of attribute deploy_link.
8 9 10 |
# File 'lib/harrison/deploy.rb', line 8 def deploy_link @deploy_link end |
#host ⇒ Object
The specific host among –hosts that we are currently working on.
6 7 8 |
# File 'lib/harrison/deploy.rb', line 6 def host @host end |
#phases ⇒ Object
Returns the value of attribute phases.
11 12 13 |
# File 'lib/harrison/deploy.rb', line 11 def phases @phases end |
#release_dir ⇒ Object
Returns the value of attribute release_dir.
7 8 9 |
# File 'lib/harrison/deploy.rb', line 7 def release_dir @release_dir end |
#rollback ⇒ Object
Returns the value of attribute rollback.
10 11 12 |
# File 'lib/harrison/deploy.rb', line 10 def rollback @rollback end |
Instance Method Details
#add_phase(name, &block) ⇒ Object
51 52 53 54 55 |
# File 'lib/harrison/deploy.rb', line 51 def add_phase(name, &block) @_phases ||= Hash.new @_phases[name] = Harrison::Deploy::Phase.new(name, &block) end |
#cleanup_deploys(limit) ⇒ Object
173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/harrison/deploy.rb', line 173 def cleanup_deploys(limit) # Grab a list of deploys to be removed. purge_deploys = self.deploys.sort.reverse.slice(limit..-1) || [] if purge_deploys.size > 0 puts "[#{self.host}] Purging #{purge_deploys.size} old deploys. (Keeping #{limit}...)" purge_deploys.each do |stale_deploy| remote_exec("cd deploys && rm -f #{stale_deploy}") end end end |
#cleanup_releases ⇒ Object
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/harrison/deploy.rb', line 186 def cleanup_releases # Figure out which releases need to be kept. keep_releases = self.active_releases self.releases.each do |release| unless keep_releases.include?(release) remote_exec("cd releases && rm -rf #{release}") end end end |
#close(host = nil) ⇒ Object
197 198 199 200 201 202 203 204 205 |
# File 'lib/harrison/deploy.rb', line 197 def close(host=nil) if host @_conns[host].close if @_conns && @_conns[host] elsif @_conns @_conns.keys.each do |host| @_conns[host].close unless @_conns[host].closed? end end end |
#current_symlink ⇒ Object
61 62 63 |
# File 'lib/harrison/deploy.rb', line 61 def current_symlink "#{self.remote_project_dir}/current" end |
#invoke_user_block ⇒ Object
13 |
# File 'lib/harrison/deploy.rb', line 13 alias :invoke_user_block :run |
#parse(args) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/harrison/deploy.rb', line 37 def parse(args) super # Preserve argv hosts if it's been passed. @_argv_hosts = self.hosts.dup if self.hosts self.rollback = args[0] == 'rollback' unless self.rollback # Make sure they passed an artifact. self.artifact = args[1] || abort("ERROR: You must specify the artifact to be deployed as an argument to this command.") end end |
#remote_exec(cmd) ⇒ Object
57 58 59 |
# File 'lib/harrison/deploy.rb', line 57 def remote_exec(cmd) super("cd #{remote_project_dir} && #{cmd}") end |
#revert_current_symlink ⇒ Object
74 75 76 77 78 79 |
# File 'lib/harrison/deploy.rb', line 74 def revert_current_symlink # Restore current symlink to previous if set. if @_old_current self.remote_exec("ln -sfn #{@_old_current} #{self.current_symlink}") end end |
#run ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/harrison/deploy.rb', line 81 def run # Override Harrisonfile hosts if it was passed on argv. self.hosts = @_argv_hosts if @_argv_hosts if self.hosts.respond_to?(:call) resolved_hosts = self.hosts.call(self) self.hosts = resolved_hosts end # Require at least one host. if !self.hosts || self.hosts.empty? abort("ERROR: You must specify one or more hosts to deploy/rollback on, either in your Harrisonfile or via --hosts.") end if self.confirm self.hosts.each { |h| puts " - #{h}" } exit unless HighLine.new.agree("\nProceed with above-listed hosts?") puts "" end # Default to just built in deployment phases. self.phases ||= [ :upload, :extract, :link, :cleanup ] # Default base_dir. self.base_dir ||= '/opt' if self.rollback puts "Rolling back \"#{project}\" to previously deployed release on #{hosts.size} hosts...\n\n" # Find the prior deploy on the first host. self.host = hosts[0] last_deploy = self.deploys.sort.reverse[1] || abort("ERROR: No previous deploy to rollback to.") self.release_dir = remote_exec("cd deploys && readlink -vn #{last_deploy}") # No need to upload or extract for rollback. self.phases.delete(:upload) self.phases.delete(:extract) # Don't cleanup old deploys either. self.phases.delete(:cleanup) else puts "Deploying #{artifact} for \"#{project}\" onto #{hosts.size} hosts...\n\n" self.release_dir = "#{remote_project_dir}/releases/" + File.basename(artifact, '.tar.gz') end self.deploy_link = "#{remote_project_dir}/deploys/" + Time.new.utc.strftime('%Y-%m-%d_%H%M%S') progress_stack = [] failed = catch(:failure) do self.phases.each do |phase_name| phase = @_phases[phase_name] || abort("ERROR: Could not resolve \"#{phase_name}\" as a deployment phase.") self.hosts.each do |host| self.host = host phase._run(self) # Track what phases we have completed on which hosts, in a stack. progress_stack << { host: host, phase: phase_name } end end # We want "failed" to be false if nothing was caught. false end if failed print "\n" progress_stack.reverse.each do |progress| self.host = progress[:host] phase = @_phases[progress[:phase]] # Don't let failures interrupt the rest of the process. catch(:failure) do phase._fail(self) end end abort "\nDeployment failed, previously completed deployment actions have been reverted." else if self.rollback puts "\nSucessfully rolled back #{project} on #{hosts.join(', ')}." else puts "\nSucessfully deployed #{artifact} to #{hosts.join(', ')}." end end end |
#update_current_symlink ⇒ Object
65 66 67 68 69 70 71 72 |
# File 'lib/harrison/deploy.rb', line 65 def update_current_symlink # Conditional assignment here makes this idempotent. @_old_current ||= self.remote_exec("if [ -L #{current_symlink} ]; then readlink -vn #{current_symlink}; fi") @_old_current = nil if @_old_current.empty? # Symlink current to new deploy. self.remote_exec("ln -sfn #{self.deploy_link} #{self.current_symlink}") end |