harrison 0.1.0 → 0.2.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/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