harrison 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 0.2.0
2
+ -------------------
3
+ - Implemented the ability to purge old releases after a successful deploy.
4
+
1
5
  0.1.0
2
6
  -------------------
3
7
  - Renamed --pkg_dir option of 'package' task to --destination
data/IDEAS CHANGED
@@ -1,5 +1,6 @@
1
1
  Ideas:
2
2
  ------
3
+ [ ] Split deploy into distinct stages (e.g. pre-deploy, deploy, post-deploy) and ensure stage succeeds on each host before attempting next stage.
3
4
  [ ] Allow more elaborate hosts config, e.g.: h.hosts = [ { host: '10.16.18.207', tags: %w(util migrate) } ]
4
5
  [ ] Some kind of --dry-run option.
5
6
  [ ] Allow deploy_via to include alternate user/connection options.
@@ -9,4 +10,3 @@ Ideas:
9
10
  [ ] Something like a "status" command that shows what commit is live for env (on each host?)
10
11
  [ ] Move artifacts out of pkg/ (and into like pkg/deployed) once they have been deployed? (Some sort of flag for the significant env?)
11
12
  [ ] Include branch name in artifact file name.
12
- [ ] Make --purge the default behavior for the package step.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Simple artifact-based deployment for web applications.
4
4
 
5
- [![Build Status](https://travis-ci.org/scotje/harrison.svg?branch=master)](https://travis-ci.org/scotje/harrison)
5
+ [![Build Status](https://travis-ci.org/puppetlabs/harrison.svg?branch=master)](https://travis-ci.org/puppetlabs/harrison)
6
6
 
7
7
  ## Installation
8
8
 
@@ -26,7 +26,7 @@ First, create a Harrisonfile in the root of your project. Here's an example:
26
26
  # Project-wide Config
27
27
  Harrison.config do |h|
28
28
  h.project = 'harrison'
29
- h.git_src = "git@github.com:scotje/harrison.git"
29
+ h.git_src = "git@github.com:puppetlabs/harrison.git"
30
30
  end
31
31
 
32
32
  Harrison.package do |h|
@@ -157,12 +157,16 @@ else
157
157
  end
158
158
  ```
159
159
 
160
+ You can use the `--keep` option (or set it in the deploy section of your Harrisonfile) to specify the total number of
161
+ deploys you want to retain on each server after a successful deployment. The default is to keep all previous deploys
162
+ around indefinitely.
163
+
160
164
  There are some additional options available, run `harrison deploy --help` to see everything available.
161
165
 
162
166
 
163
167
  ## Contributing
164
168
 
165
- 1. Fork it ( https://github.com/scotje/harrison/fork )
169
+ 1. Fork it ( https://github.com/puppetlabs/harrison/fork )
166
170
  2. Create your feature branch (`git checkout -b my-new-feature`)
167
171
  3. Commit your changes (`git commit -am 'Add some feature'`)
168
172
  4. Push to the branch (`git push origin my-new-feature`)
data/harrison.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Jesse Scott"]
10
10
  spec.email = ["jesse@puppetlabs.com"]
11
11
  spec.summary = %q{Simple artifact-based deployment for web applications.}
12
- spec.homepage = "https://github.com/scotje/harrison"
12
+ spec.homepage = "https://github.com/puppetlabs/harrison"
13
13
  spec.license = "Apache 2.0"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
@@ -11,11 +11,13 @@ module Harrison
11
11
  self.class.option_helper(:env)
12
12
  self.class.option_helper(:base_dir)
13
13
  self.class.option_helper(:deploy_via)
14
+ self.class.option_helper(:keep)
14
15
 
15
16
  # Command line opts for this action. Will be merged with common opts.
16
17
  arg_opts = [
17
18
  [ :hosts, "List of remote hosts to deploy to. Can also be specified in Harrisonfile.", :type => :strings ],
18
19
  [ :env, "Environment to deploy to. This can be examined in your Harrisonfile to calculate target hosts.", :type => :string ],
20
+ [ :keep, "Number of recent deploys to keep after a successful deploy. (Including the most recent deploy.) Defaults to keeping all deploys forever.", :type => :integer ],
19
21
  ]
20
22
 
21
23
  super(arg_opts, opts)
@@ -88,12 +90,42 @@ module Harrison
88
90
  # Run user supplied deploy code to restart server or whatever.
89
91
  super
90
92
 
93
+ # Cleanup old releases if a keep value is set.
94
+ if (self.keep)
95
+ cleanup_deploys(self.keep)
96
+ cleanup_releases
97
+ end
98
+
91
99
  close(self.host)
92
100
  end
93
101
 
94
102
  puts "Sucessfully deployed #{artifact} to #{hosts.join(', ')}."
95
103
  end
96
104
 
105
+ def cleanup_deploys(limit)
106
+ # Grab a list of deploys to be removed.
107
+ purge_deploys = self.deploys.sort.reverse.slice(limit..-1) || []
108
+
109
+ if purge_deploys.size > 0
110
+ puts "Purging #{purge_deploys.size} old deploys on #{self.host}, keeping #{limit}..."
111
+
112
+ purge_deploys.each do |stale_deploy|
113
+ remote_exec("cd deploys && rm -f #{stale_deploy}")
114
+ end
115
+ end
116
+ end
117
+
118
+ def cleanup_releases
119
+ # Figure out which releases need to be kept.
120
+ keep_releases = self.active_releases
121
+
122
+ self.releases.each do |release|
123
+ unless keep_releases.include?(release)
124
+ remote_exec("cd releases && rm -rf #{release}")
125
+ end
126
+ end
127
+ end
128
+
97
129
  def close(host=nil)
98
130
  if host
99
131
  @_conns[host].close if @_conns && @_conns[host]
@@ -114,5 +146,20 @@ module Harrison
114
146
  def remote_project_dir
115
147
  "#{base_dir}/#{project}"
116
148
  end
149
+
150
+ # Return a sorted list of deploys, unsorted.
151
+ def deploys
152
+ remote_exec("cd deploys && ls -1").split("\n")
153
+ end
154
+
155
+ # Return a list of all releases, unsorted.
156
+ def releases
157
+ remote_exec("cd releases && ls -1").split("\n")
158
+ end
159
+
160
+ # Return a list of releases with at least 1 deploy pointing to them, unsorted.
161
+ def active_releases
162
+ self.deploys.collect { |deploy| remote_exec("cd deploys && basename `readlink #{deploy}`") }.uniq
163
+ end
117
164
  end
118
165
  end
@@ -1,3 +1,3 @@
1
1
  module Harrison
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -121,6 +121,17 @@ describe Harrison::Deploy do
121
121
  expect(output).to include('host1', 'host2', 'host3')
122
122
  end
123
123
 
124
+ it 'should clean up old releases if passed a --keep option' do
125
+ instance.keep = 3
126
+
127
+ expect(instance).to receive(:cleanup_deploys).with(3)
128
+ expect(instance).to receive(:cleanup_releases)
129
+
130
+ output = capture(:stdout) do
131
+ instance.run
132
+ end
133
+ end
134
+
124
135
  context 'when deploying from a remote artifact source' do
125
136
  before(:each) do
126
137
  instance.artifact = 'test_user@test_host1:/tmp/test_artifact.tar.gz'
@@ -150,6 +161,39 @@ describe Harrison::Deploy do
150
161
  end
151
162
  end
152
163
 
164
+ describe '#cleanup_deploys' do
165
+ before(:each) do
166
+ allow(instance).to receive(:deploys).and_return([ 'deploy_1', 'deploy_2', 'deploy_3', 'deploy_4', 'deploy_5' ])
167
+ end
168
+
169
+ it 'should remove deploys beyond the passed in limit' do
170
+ expect(instance).to receive(:remote_exec).with(/rm -f deploy_2/).and_return('')
171
+ expect(instance).to receive(:remote_exec).with(/rm -f deploy_1/).and_return('')
172
+
173
+ output = capture(:stdout) do
174
+ instance.cleanup_deploys(3)
175
+ end
176
+
177
+ expect(output).to include('purging', 'deploys', 'keeping 3')
178
+ end
179
+ end
180
+
181
+ describe '#cleanup_releases' do
182
+ before(:each) do
183
+ allow(instance).to receive(:releases).and_return([ 'release_1', 'release_2', 'release_3', 'release_4', 'release_5' ])
184
+ allow(instance).to receive(:active_releases).and_return([ 'release_3', 'release_4', 'release_5' ])
185
+ end
186
+
187
+ it 'should remove inactive releases' do
188
+ expect(instance).to receive(:remote_exec).with(/rm -rf release_1/).and_return('')
189
+ expect(instance).to receive(:remote_exec).with(/rm -rf release_2/).and_return('')
190
+
191
+ capture(:stdout) do
192
+ instance.cleanup_releases
193
+ end
194
+ end
195
+ end
196
+
153
197
  describe '#close' do
154
198
  before(:each) do
155
199
  @test_host1_ssh = double(:ssh, 'closed?' => false)
@@ -204,5 +248,72 @@ describe Harrison::Deploy do
204
248
  expect(instance.send(:remote_project_dir)).to include('/test_base_dir', 'test_project')
205
249
  end
206
250
  end
251
+
252
+ describe '#deploys' do
253
+ it 'should invoke ls in the correct directory on the remote server' do
254
+ expect(instance).to receive(:remote_exec).with(/deploys.*ls -1/).and_return('')
255
+
256
+ instance.send(:deploys)
257
+ end
258
+
259
+ it 'should return an array of deploys' do
260
+ expect(instance).to receive(:remote_exec).and_return("deploy_1\ndeploy_2\ndeploy_3\n")
261
+
262
+ deploys = instance.send(:deploys)
263
+
264
+ expect(deploys).to respond_to(:size)
265
+ expect(deploys.size).to be 3
266
+ end
267
+ end
268
+
269
+ describe '#releases' do
270
+ it 'should invoke ls in the correct directory on the remote server' do
271
+ expect(instance).to receive(:remote_exec).with(/releases.*ls -1/).and_return('')
272
+
273
+ instance.send(:releases)
274
+ end
275
+
276
+ it 'should return an array of releases' do
277
+ expect(instance).to receive(:remote_exec).and_return("release_1\nrelease_2\nrelease_3\n")
278
+
279
+ releases = instance.send(:releases)
280
+
281
+ expect(releases).to respond_to(:size)
282
+ expect(releases.size).to be 3
283
+ end
284
+ end
285
+
286
+ describe '#active_releases' do
287
+ before(:each) do
288
+ allow(instance).to receive(:remote_exec).with(/readlink/) do |cmd|
289
+ "release_" + /`readlink deploy_([0-9]+)`/.match(cmd).captures[0]
290
+ end
291
+ end
292
+
293
+ it 'should return an array of releases' do
294
+ expect(instance).to receive(:deploys).and_return([ 'deploy_3', 'deploy_4', 'deploy_5' ])
295
+
296
+ active_releases = instance.send(:active_releases)
297
+
298
+ expect(active_releases).to respond_to(:size)
299
+ expect(active_releases.size).to be 3
300
+ end
301
+
302
+ it 'should only return distinct releases' do
303
+ expect(instance).to receive(:deploys).and_return([ 'deploy_3', 'deploy_4', 'deploy_5', 'deploy_3' ])
304
+
305
+ active_releases = instance.send(:active_releases)
306
+
307
+ expect(active_releases.size).to be 3
308
+ end
309
+
310
+ it 'should return releases corresponding to given deploys' do
311
+ expect(instance).to receive(:deploys).and_return([ 'deploy_3' ])
312
+
313
+ active_releases = instance.send(:active_releases)
314
+
315
+ expect(active_releases).to include('release_3')
316
+ end
317
+ end
207
318
  end
208
319
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harrison
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-08-08 00:00:00.000000000 Z
12
+ date: 2014-09-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -174,7 +174,7 @@ files:
174
174
  - spec/unit/harrison/package_spec.rb
175
175
  - spec/unit/harrison/ssh_spec.rb
176
176
  - spec/unit/harrison_spec.rb
177
- homepage: https://github.com/scotje/harrison
177
+ homepage: https://github.com/puppetlabs/harrison
178
178
  licenses:
179
179
  - Apache 2.0
180
180
  post_install_message:
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
195
  version: '0'
196
196
  segments:
197
197
  - 0
198
- hash: -1749031995180641465
198
+ hash: 1433589896306598119
199
199
  requirements: []
200
200
  rubyforge_project:
201
201
  rubygems_version: 1.8.29