menagerie 0.1.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -4
  4. data/CHANGELOG.md +4 -0
  5. data/README.md +137 -1
  6. data/lib/menagerie.rb +9 -0
  7. data/lib/menagerie/artifact.rb +15 -12
  8. data/lib/menagerie/collection.rb +17 -7
  9. data/lib/menagerie/release.rb +27 -13
  10. data/menagerie.gemspec +5 -5
  11. data/spec/examples/empty/.keep +0 -0
  12. data/spec/examples/empty_with_dirs/artifacts/.keep +0 -0
  13. data/spec/examples/empty_with_dirs/releases/.keep +0 -0
  14. data/spec/examples/existing/artifacts/a/0.0.1 +0 -0
  15. data/spec/examples/existing/artifacts/a/0.0.2 +0 -0
  16. data/spec/examples/existing/artifacts/a/0.0.3 +0 -0
  17. data/spec/examples/existing/artifacts/b/1.0.0 +0 -0
  18. data/spec/examples/existing/artifacts/b/1.0.1 +0 -0
  19. data/spec/examples/existing/artifacts/c/0.1.1 +0 -0
  20. data/spec/examples/existing/artifacts/c/0.1.2 +0 -0
  21. data/spec/examples/existing/artifacts/c/0.2.0 +0 -0
  22. data/spec/examples/existing/releases/0/a +0 -0
  23. data/spec/examples/existing/releases/0/b +0 -0
  24. data/spec/examples/existing/releases/0/c +0 -0
  25. data/spec/examples/existing/releases/1/a +0 -0
  26. data/spec/examples/existing/releases/1/b +0 -0
  27. data/spec/examples/existing/releases/1/c +0 -0
  28. data/spec/examples/existing/releases/2/a +0 -0
  29. data/spec/examples/existing/releases/2/b +0 -0
  30. data/spec/examples/existing/releases/2/c +0 -0
  31. data/spec/examples/orphaned/artifacts/a/0.0.1 +0 -0
  32. data/spec/examples/orphaned/artifacts/a/0.0.2 +0 -0
  33. data/spec/examples/orphaned/artifacts/a/0.0.3 +0 -0
  34. data/spec/examples/orphaned/artifacts/b/1.0.0 +0 -0
  35. data/spec/examples/orphaned/artifacts/b/1.0.1 +0 -0
  36. data/spec/examples/orphaned/artifacts/c/0.1.0 +0 -0
  37. data/spec/examples/orphaned/artifacts/c/0.1.1 +0 -0
  38. data/spec/examples/orphaned/artifacts/c/0.1.2 +0 -0
  39. data/spec/examples/orphaned/artifacts/c/0.2.0 +0 -0
  40. data/spec/examples/orphaned/releases/0/a +0 -0
  41. data/spec/examples/orphaned/releases/0/b +0 -0
  42. data/spec/examples/orphaned/releases/0/c +0 -0
  43. data/spec/examples/orphaned/releases/1/a +0 -0
  44. data/spec/examples/orphaned/releases/1/b +0 -0
  45. data/spec/examples/orphaned/releases/1/c +0 -0
  46. data/spec/examples/orphaned/releases/2/a +0 -0
  47. data/spec/examples/orphaned/releases/2/b +0 -0
  48. data/spec/examples/orphaned/releases/2/c +0 -0
  49. data/spec/menagerie/artifact_spec.rb +43 -0
  50. data/spec/menagerie/collection_spec.rb +114 -0
  51. data/spec/menagerie/release_spec.rb +58 -0
  52. data/spec/menagerie_spec.rb +32 -0
  53. metadata +94 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b31c3981add784c6a763079af7377d26bb8a135b
4
- data.tar.gz: 096b3570b9937154565628863f2da67d0b87461e
3
+ metadata.gz: b98fd545ddccb263db3f21023fd0dc02aad05189
4
+ data.tar.gz: 308c931a27d9573b6d70a8be093d6fa6ea7f44ad
5
5
  SHA512:
6
- metadata.gz: 0446dc43e870b7dc802607e50a3bfc5037cff63e35c3878841b9acf9fe3bb8cb57d03a534c4bc3dcbbaebbb4aed89a439132fc518abd212828788b282ab26ade
7
- data.tar.gz: 9cd913b1b8e1fdf235aa5fe6b63a5cc70161f31470235c1cabef5d5d225650dfc51b1db3ba733564a30324da648da5f7799c1ec0c3dfc27b5e79088397c4b617
6
+ metadata.gz: b40e70f809cd646aab6ac63b90e10477f4ffaab400a84e870fb6b93ba339fe1eea51cfd0fe3a870615ac62543e7a55d558e3acc8c033ee3f588b528128b430b2
7
+ data.tar.gz: ff6b695108b03b845701f6aa55d4b267c966d621edea7625fe38827f01c34140d1cf9e76152d621e862203581de942a9e96b635cb1f9d9e24a0ec01fa7e3bfbd
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ coverage/
3
3
  .coveralls.yml
4
4
  .bundle
5
5
  Gemfile.lock
6
+ spec/examples/scratch/
@@ -1,11 +1,15 @@
1
1
  language: ruby
2
2
  cache: bundler
3
+ sudo: false
3
4
  rvm:
4
- - 2.1.0
5
- - 2.0.0
6
- - 1.9.3
5
+ - 2.2.0
6
+ - 2.1.5
7
+ - 2.0.0-p598
8
+ - 1.9.3-p551
7
9
  notifications:
8
10
  email: false
9
11
  irc:
10
12
  template:
11
- - '%{repository}/%{branch}/%{build_number}: %{message} -- %{build_url}'
13
+ - '%{repository}/%{branch}/%{build_number}: %{message} -- %{build_url}'
14
+ channels:
15
+ - irc.oftc.net#akerl
@@ -0,0 +1,4 @@
1
+ # 1.0.0 / 2015-02-14
2
+
3
+ * [ENHANCEMENT] Stabilized API
4
+
data/README.md CHANGED
@@ -8,10 +8,146 @@ menagerie
8
8
  [![Build Status](https://img.shields.io/travis/akerl/menagerie.svg)](https://travis-ci.org/akerl/menagerie)
9
9
  [![MIT Licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://tldrlegal.com/license/mit-license)
10
10
 
11
- Simple release management tool for controlling versioned artifacts on an instance.
11
+ Simple release management tool for controlling versioned artifacts
12
12
 
13
13
  ## Usage
14
14
 
15
+ Menagerie manages Collections of Releases, as well as the Artifacts that are used in those Releases. A quick tl;dr:
16
+
17
+ * Artifact -- a downloaded asset: a compiled Linux kernel, the python package, a picture of your cat, etc
18
+ * Release -- a set of Artifacts at specific versions: "Python 2.7, Linux kernel 3.18.1, and the 3rd revision of your cat's picture"
19
+ * Collection - all the Releases currently downloaded, including the "latest" release as well any previous releases
20
+
21
+ Also relevant are "Orphans"; these are artifacts that are not needed by any currently tracked releases.
22
+
23
+ The gem provides a simple script that will print out the contents of an existing collection:
24
+
25
+ ```
26
+ Release: 0
27
+ initrd: 0.0.36
28
+ kernel: 3.19-rc7_1
29
+ vm_root: 0.0.73
30
+ Release: 1
31
+ initrd: 0.0.36
32
+ kernel: 3.19-rc7_1
33
+ vm_root: 0.0.71
34
+ Release: 2
35
+ initrd: 0.0.36
36
+ kernel: 3.19-rc6_1
37
+ vm_root: 0.0.69
38
+
39
+ Orphans:
40
+ kernel
41
+ 3.19-rc5_1
42
+ ```
43
+
44
+ To start using Menagerie, create a collection:
45
+
46
+ ```
47
+ require 'menagerie'
48
+ my_collection = Menagerie.new
49
+ ```
50
+
51
+ Initially, your collection has no releases. To add a release, call `.add_release` with the details for the artifacts you want to install:
52
+
53
+ ```
54
+ new_artifacts = [
55
+ {
56
+ name: 'kernel',
57
+ version: '3.19-rc7_1',
58
+ url: 'https://github.com/dock0/kernel/releases/download/3.19-rc7_1/vmlinuz'
59
+ },
60
+ {
61
+ name: 'initrd',
62
+ version: '0.0.36',
63
+ url: 'https://github.com/dock0/initrd/releases/download/0.0.36/initrd.img'
64
+ }
65
+ ]
66
+ my_collection.add_release(new_artifacts)
67
+ ```
68
+
69
+ This will download the new artifacts into `./artifacts` and symlink them into `./releases/0`, with `./latest` linked to `./releases/0`:
70
+
71
+ ```
72
+ # tree
73
+ .
74
+ ├── artifacts
75
+ │   ├── initrd
76
+ │   │   └── 0.0.36
77
+ │   └── kernel
78
+ │   └── 3.19-rc7_1
79
+ ├── latest -> releases/0
80
+ └── releases
81
+ └── 0
82
+ ├── initrd -> ../../artifacts/initrd/0.0.36
83
+ └── kernel -> ../../artifacts/kernel/3.19-rc7_1
84
+ ```
85
+
86
+ Now lets say you update your kernel package, so you want to make a new releaes with your updated 3.19_1 kernel:
87
+
88
+ ```
89
+ new_artifacts = [
90
+ {
91
+ name: 'kernel',
92
+ version: '3.19_1',
93
+ url: 'https://github.com/dock0/kernel/releases/download/3.19-rc7_1/vmlinuz'
94
+ },
95
+ {
96
+ name: 'initrd',
97
+ version: '0.0.36',
98
+ url: 'https://github.com/dock0/initrd/releases/download/0.0.36/initrd.img'
99
+ }
100
+ ]
101
+ my_collection.add_release(new_artifacts)
102
+ ```
103
+
104
+ Menagerie will rotate the '0' release to '1', download the new kernel, and link the correct assets into '0'. Note that it doesn't download a new copy of the initrd asset, as the version didn't change:
105
+
106
+ ```
107
+ # tree
108
+ .
109
+ ├── artifacts
110
+ │   ├── initrd
111
+ │   │   └── 0.0.36
112
+ │   └── kernel
113
+ │   ├── 3.19_1
114
+ │   └── 3.19-rc7_1
115
+ ├── latest -> releases/0
116
+ └── releases
117
+ ├── 0
118
+ │   ├── initrd -> ../../artifacts/initrd/0.0.36
119
+ │   └── kernel -> ../../artifacts/kernel/3.19_1
120
+ └── 1
121
+ ├── initrd -> ../../artifacts/initrd/0.0.36
122
+ └── kernel -> ../../artifacts/kernel/3.19-rc7_1
123
+ ```
124
+
125
+ Menagerie will retain 5 old releases by default (for a total of 6, including the current version). Older releases will be removed, and orphaned artifacts (those no longer used in any releases) will be deleted. To change the retention or disable reaping of orphans, pass the desired options to Menagerie when you create your collection:
126
+
127
+ ```
128
+ larger_collection = Menagerie.new(options: { retention: 20 }) # this will retain 20 old releases
129
+
130
+ permanent_collection = Menagerie.new(options: { reap: false }) # this will not reap orphaned artifacts
131
+ ```
132
+
133
+ By default, Menagerie will log to STDOUT when it manipulates releases and artifacts. You can disable this via the `verbose` option:
134
+
135
+ ```
136
+ quiet_collection = Menagerie.new(options: { verbose: false })
137
+ ```
138
+
139
+ You can also override the paths used for artifacts/releases and the "latest" symlink, via the `paths` parameter:
140
+
141
+ ```
142
+ custom_paths = {
143
+ artifacts: '/srv/artifacts',
144
+ releases: '/opt/releases',
145
+ latest: '/var/latest_release'
146
+ }
147
+
148
+ my_collection = Menagerie.new(paths: custom_paths)
149
+ ```
150
+
15
151
  ## Installation
16
152
 
17
153
  gem install menagerie
@@ -1,3 +1,5 @@
1
+ require 'logger'
2
+
1
3
  ##
2
4
  # This module provides release collection management
3
5
  module Menagerie
@@ -8,6 +10,13 @@ module Menagerie
8
10
  def new(*args)
9
11
  self::Collection.new(*args)
10
12
  end
13
+
14
+ def get_logger(verbose = true)
15
+ logger = Logger.new(STDOUT)
16
+ logger.level = verbose ? Logger::DEBUG : Logger::WARN
17
+ logger.progname = 'menagerie'
18
+ logger
19
+ end
11
20
  end
12
21
  end
13
22
 
@@ -1,31 +1,34 @@
1
- require 'pathname'
2
1
  require 'open-uri'
3
2
 
4
3
  module Menagerie
5
4
  ##
6
5
  # Artifacts are a stored file for a particular version of a project
7
6
  class Artifact
8
- attr_reader :version, :name, :path
7
+ attr_reader :name, :version
9
8
 
10
9
  def initialize(params = {})
11
- @config = params
12
- @config[:artifact] ? create : parse
10
+ @options = params
11
+ @logger = @options[:logger] || Menagerie.get_logger
12
+ @options[:artifact] ? create : parse
13
+ end
14
+
15
+ def path
16
+ @path ||= "#{@options[:paths][:artifacts]}/#{@name}/#{@version}"
13
17
  end
14
18
 
15
19
  private
16
20
 
17
21
  def create
18
- @name, @version, url = @config[:artifact].values_at :name, :version, :url
19
- @path = "#{@config[:paths][:artifacts]}/#{@name}/#{@version}"
20
- download url, path unless File.exist? path
21
- File.chmod(@config[:artifact][:mode], path) if @config[:artifact][:mode]
22
+ artifact = @options[:artifact]
23
+ @name, @version = artifact.values_at(:name, :version)
24
+ @logger.info "Downloading artifact: #{path}"
25
+ download(artifact[:url], path) unless File.exist? path
26
+ File.chmod(artifact[:mode], path) if artifact[:mode]
22
27
  end
23
28
 
24
29
  def parse
25
- link_path = @config[:path]
26
- @name = Pathname.new(link_path).basename
27
- @version = File.basename File.readlink(link_path)
28
- @path = "#{@config[:paths][:artifacts]}/#{@name}/#{@version}"
30
+ @name = File.basename @options[:path]
31
+ @version = File.basename File.readlink(@options[:path])
29
32
  end
30
33
 
31
34
  def download(url, path)
@@ -5,16 +5,17 @@ module Menagerie
5
5
  ##
6
6
  # Connection object that contains releases
7
7
  class Collection
8
- def initialize(params = nil)
8
+ def initialize(params = {})
9
9
  params = Cymbal.symbolize(params || {})
10
10
  @paths = default_paths.merge(params[:paths] || {})
11
11
  @options = default_options.merge(params[:options] || {})
12
+ @logger = Menagerie.get_logger(@options[:verbosity])
12
13
  end
13
14
 
14
15
  def releases
15
16
  Dir.glob("#{@paths[:releases]}/*").map do |x|
16
- Release.new path: x, paths: @paths
17
- end
17
+ Release.new path: x, paths: @paths, logger: @logger
18
+ end.sort
18
19
  end
19
20
 
20
21
  def orphans
@@ -24,9 +25,13 @@ module Menagerie
24
25
  end
25
26
  end
26
27
 
27
- def create(artifacts)
28
+ def add_release(artifacts)
28
29
  rotate
29
- Release.new artifacts: Cymbal.symbolize(artifacts), paths: @paths
30
+ Release.new(
31
+ artifacts: Cymbal.symbolize(artifacts),
32
+ paths: @paths,
33
+ logger: @logger
34
+ )
30
35
  reap if @options[:reap]
31
36
  link_latest
32
37
  end
@@ -41,12 +46,16 @@ module Menagerie
41
46
  end
42
47
 
43
48
  def reap
44
- orphans.each { |orphan| FileUtils.rm_f orphan }
49
+ orphans.each do |orphan|
50
+ @logger.info "Reaping orphan: #{orphan}"
51
+ FileUtils.rm_f orphan
52
+ end
45
53
  end
46
54
 
47
55
  def link_latest
48
56
  FileUtils.rm_f @paths[:latest]
49
57
  FileUtils.ln_sf releases.sort.first.path, @paths[:latest]
58
+ @logger.debug "Linked latest release to #{@paths[:latest]}"
50
59
  end
51
60
 
52
61
  def default_paths
@@ -60,7 +69,8 @@ module Menagerie
60
69
  def default_options
61
70
  {
62
71
  retention: 5,
63
- reap: true
72
+ reap: true,
73
+ verbose: true
64
74
  }
65
75
  end
66
76
  end
@@ -8,14 +8,15 @@ module Menagerie
8
8
  attr_reader :id, :base, :path
9
9
 
10
10
  def initialize(params = {})
11
- @config = params
12
- @path = @config[:path] || create
13
- @base, @id = Pathname.new(@path).split.map(&:to_s)
11
+ @options = params
12
+ @logger = @options[:logger] || Menagerie.get_logger
13
+ @path = @options[:path] || create
14
+ parse_path
14
15
  end
15
16
 
16
17
  def artifacts
17
18
  Dir.glob("#{@path}/*").map do |x|
18
- Artifact.new(path: x, paths: @config[:paths])
19
+ Artifact.new path: x, paths: @options[:paths], logger: @logger
19
20
  end
20
21
  end
21
22
 
@@ -24,11 +25,28 @@ module Menagerie
24
25
  @id.to_i <=> other.id.to_i
25
26
  end
26
27
 
28
+ def rotate
29
+ old_path = @path
30
+ @path = "#{@base}/#{@id.to_i + 1}"
31
+ FileUtils.mv old_path, @path
32
+ parse_path
33
+ end
34
+
35
+ def delete
36
+ @logger.info "Deleting release: #{@path}"
37
+ FileUtils.rm_r @path
38
+ end
39
+
40
+ private
41
+
27
42
  def create
28
- path = "#{@config[:paths][:releases]}/0"
43
+ path = "#{@options[:paths][:releases]}/0"
44
+ @logger.info "Creating release: #{path}"
29
45
  FileUtils.mkdir_p path
30
- @config[:artifacts].each do |x|
31
- artifact = Artifact.new artifact: x, paths: @config[:paths]
46
+ @options[:artifacts].each do |x|
47
+ artifact = Artifact.new(
48
+ artifact: x, paths: @options[:paths], logger: @logger
49
+ )
32
50
  link artifact.path, "#{path}/#{x[:name]}"
33
51
  end
34
52
  path
@@ -40,12 +58,8 @@ module Menagerie
40
58
  FileUtils.ln_s relative_source, target
41
59
  end
42
60
 
43
- def rotate
44
- FileUtils.mv @path, "#{@base}/#{@id.to_i + 1}"
45
- end
46
-
47
- def delete
48
- FileUtils.rm_r @path
61
+ def parse_path
62
+ @base, @id = Pathname.new(@path).split.map(&:to_s)
49
63
  end
50
64
  end
51
65
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'menagerie'
3
- s.version = '0.1.3'
3
+ s.version = '1.0.0'
4
4
  s.date = Time.now.strftime("%Y-%m-%d")
5
5
 
6
6
  s.summary = ''
@@ -14,11 +14,11 @@ Gem::Specification.new do |s|
14
14
  s.test_files = `git ls-files spec/*`.split
15
15
  s.executables = ['menagerie']
16
16
 
17
- s.add_dependency 'cymbal', '~> 0.0.1'
17
+ s.add_dependency 'cymbal', '~> 1.0.0'
18
18
 
19
- s.add_development_dependency 'rubocop', '~> 0.26.0'
20
- s.add_development_dependency 'rake', '~> 10.3.2'
19
+ s.add_development_dependency 'rubocop', '~> 0.29.0'
20
+ s.add_development_dependency 'rake', '~> 10.4.0'
21
21
  s.add_development_dependency 'coveralls', '~> 0.7.1'
22
- s.add_development_dependency 'rspec', '~> 3.1.0'
22
+ s.add_development_dependency 'rspec', '~> 3.2.0'
23
23
  s.add_development_dependency 'fuubar', '~> 2.0.0'
24
24
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Menagerie::Artifact do
4
+ before(:each) do
5
+ FileUtils.rm_rf 'spec/examples/scratch'
6
+ FileUtils.cp_r 'spec/examples/existing', 'spec/examples/scratch'
7
+ end
8
+
9
+ let(:base_path) { 'spec/examples/scratch' }
10
+ let(:paths) do
11
+ {
12
+ artifacts: "#{base_path}/artifacts",
13
+ releases: "#{base_path}/releases",
14
+ latest: "#{base_path}/latest"
15
+ }
16
+ end
17
+ let(:new_artifact) do
18
+ { name: 'a', version: '2.0.0', url: 'https://goo.gl/pWew5I' }
19
+ end
20
+ let(:produce_output) do
21
+ output(/Downloading artifact/).to_stdout_from_any_process
22
+ end
23
+ let(:artifact) { Menagerie.new(paths: paths).releases.first.artifacts.first }
24
+
25
+ it 'has a name attribute' do
26
+ expect(artifact.name).to eql 'a'
27
+ end
28
+
29
+ it 'has a version attribute' do
30
+ expect(artifact.version).to eql '0.0.3'
31
+ end
32
+
33
+ it 'has a path attribute' do
34
+ expect(artifact.path).to eql "#{base_path}/artifacts/a/0.0.3"
35
+ end
36
+
37
+ it 'is created if it does not exist' do
38
+ expect(File.exist? "#{base_path}/artifacts/a/2.0.0").to be_falsey
39
+ params = { paths: paths, artifact: new_artifact }
40
+ expect { Menagerie::Artifact.new params }.to produce_output
41
+ expect(File.exist? "#{base_path}/artifacts/a/2.0.0").to be_truthy
42
+ end
43
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ require 'fileutils'
4
+
5
+ describe Menagerie::Collection do
6
+ let(:examples) { 'spec/examples' }
7
+ let(:collection) { Menagerie.new }
8
+
9
+ describe '#releases' do
10
+ it 'parses releases in a collection' do
11
+ Dir.chdir("#{examples}/existing") do
12
+ expect(collection.releases.size).to eql 3
13
+ expect(collection.releases.first).to be_a Menagerie::Release
14
+ end
15
+ end
16
+
17
+ it 'parses an empty collection as empty' do
18
+ Dir.chdir("#{examples}/empty") do
19
+ expect(collection.releases.size).to eql 0
20
+ end
21
+ Dir.chdir("#{examples}/empty_with_dirs") do
22
+ expect(collection.releases.size).to eql 0
23
+ end
24
+ end
25
+
26
+ it 'supports an alternate path for releases' do
27
+ x = Menagerie.new paths: { releases: 'spec/examples/existing/releases' }
28
+ expect(x.releases.size).to eql 3
29
+ end
30
+ end
31
+
32
+ describe '#orphans' do
33
+ it 'identifies orphaned artifacts' do
34
+ Dir.chdir("#{examples}/orphaned") do
35
+ expect(collection.orphans.size).to eql 1
36
+ expect(collection.orphans.first).to be_a String
37
+ end
38
+ end
39
+
40
+ it 'returns an empty list if there are no orphans' do
41
+ Dir.chdir("#{examples}/existing") do
42
+ expect(collection.orphans.size).to eql 0
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#add_release' do
48
+ before(:each) do
49
+ FileUtils.rm_rf 'spec/examples/scratch'
50
+ FileUtils.cp_r 'spec/examples/existing', 'spec/examples/scratch'
51
+ end
52
+
53
+ let(:base_path) { 'spec/examples/scratch' }
54
+ let(:paths) do
55
+ {
56
+ artifacts: "#{base_path}/artifacts",
57
+ releases: "#{base_path}/releases",
58
+ latest: "#{base_path}/latest"
59
+ }
60
+ end
61
+ let(:artifacts) do
62
+ [
63
+ { name: 'a', version: '2.0.0', url: 'https://goo.gl/pWew5I' },
64
+ { name: 'b', version: '3.0.0', url: 'https://goo.gl/fkNplq' }
65
+ ]
66
+ end
67
+ let(:default_collection) { Menagerie.new(paths: paths) }
68
+
69
+ it 'rotates existing releases' do
70
+ expect(default_collection.releases.size).to eql 3
71
+ default_collection.add_release(artifacts)
72
+ expect(default_collection.releases.size).to eql 4
73
+ end
74
+
75
+ it 'retains a set number of old releases' do
76
+ 10.times { default_collection.add_release(artifacts) }
77
+ expect(default_collection.releases.size).to eql 6
78
+ end
79
+
80
+ it 'can be configured to allow a differnet number of releases' do
81
+ collection = Menagerie.new(paths: paths, options: { retention: 8 })
82
+ 10.times { collection.add_release(artifacts) }
83
+ expect(collection.releases.size).to eql 9
84
+ end
85
+
86
+ it 'creates a new release' do
87
+ default_collection.add_release(artifacts)
88
+ expect(default_collection.releases.first.artifacts.size).to eql 2
89
+ end
90
+
91
+ it 'reaps orphaned artifacts' do
92
+ FileUtils.touch "#{base_path}/artifacts/a/0.0.4"
93
+ FileUtils.mkdir "#{base_path}/artifacts/d"
94
+ FileUtils.touch "#{base_path}/artifacts/d/0.0.4"
95
+ expect(File.exist? "#{base_path}/artifacts/a/0.0.4").to be_truthy
96
+ expect(File.exist? "#{base_path}/artifacts/d/0.0.4").to be_truthy
97
+ default_collection.add_release(artifacts)
98
+ expect(File.exist? "#{base_path}/artifacts/a/0.0.4").to be_falsey
99
+ expect(File.exist? "#{base_path}/artifacts/d/0.0.4").to be_falsey
100
+ end
101
+
102
+ it 'can be configure to not reap orphaned artifacts' do
103
+ FileUtils.touch "#{base_path}/artifacts/a/0.0.4"
104
+ FileUtils.mkdir "#{base_path}/artifacts/d"
105
+ FileUtils.touch "#{base_path}/artifacts/d/0.0.4"
106
+ expect(File.exist? "#{base_path}/artifacts/a/0.0.4").to be_truthy
107
+ expect(File.exist? "#{base_path}/artifacts/d/0.0.4").to be_truthy
108
+ collection = Menagerie.new(paths: paths, options: { reap: false })
109
+ collection.add_release(artifacts)
110
+ expect(File.exist? "#{base_path}/artifacts/a/0.0.4").to be_truthy
111
+ expect(File.exist? "#{base_path}/artifacts/d/0.0.4").to be_truthy
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Menagerie::Release do
4
+ before(:each) do
5
+ FileUtils.rm_rf 'spec/examples/scratch'
6
+ FileUtils.cp_r 'spec/examples/existing', 'spec/examples/scratch'
7
+ end
8
+
9
+ let(:base_path) { 'spec/examples/scratch' }
10
+ let(:paths) do
11
+ {
12
+ artifacts: "#{base_path}/artifacts",
13
+ releases: "#{base_path}/releases",
14
+ latest: "#{base_path}/latest"
15
+ }
16
+ end
17
+ let(:release) { Menagerie.new(paths: paths).releases.first }
18
+
19
+ it 'has an id attribute' do
20
+ expect(release.id).to eql '0'
21
+ end
22
+
23
+ it 'has a base attribute' do
24
+ expect(release.base).to eql "#{base_path}/releases"
25
+ end
26
+
27
+ it 'has a path attribute' do
28
+ expect(release.path).to eql "#{release.base}/#{release.id}"
29
+ end
30
+
31
+ it 'sorts' do
32
+ other_release = Menagerie.new(paths: paths).releases.last
33
+ expect(release <=> other_release).to eql(-1)
34
+ end
35
+
36
+ describe '#artifacts' do
37
+ it 'is an array of Artifacts' do
38
+ expect(release.artifacts).to all(be_a Menagerie::Artifact)
39
+ end
40
+ end
41
+
42
+ describe '#rotate' do
43
+ it 'rotates the release path' do
44
+ last_release = Menagerie.new(paths: paths).releases.last
45
+ expect(last_release.id).to eql '2'
46
+ last_release.rotate
47
+ expect(last_release.id).to eql '3'
48
+ end
49
+ end
50
+
51
+ describe '#delete' do
52
+ it 'deletes the release' do
53
+ expect(Dir.exist? release.path).to be_truthy
54
+ release.delete
55
+ expect(Dir.exist? release.path).to be_falsey
56
+ end
57
+ end
58
+ end
@@ -1 +1,33 @@
1
1
  require 'spec_helper'
2
+
3
+ describe Menagerie do
4
+ describe '#new' do
5
+ it 'creates collection objects' do
6
+ expect(Menagerie.new).to be_an_instance_of Menagerie::Collection
7
+ end
8
+ end
9
+
10
+ describe '#get_logger' do
11
+ let(:logger) { Menagerie.get_logger }
12
+ let(:quiet_logger) { Menagerie.get_logger(false) }
13
+
14
+ let(:produce_output) { output(/hello/).to_stdout_from_any_process }
15
+
16
+ it 'returns a logger object' do
17
+ expect(logger).to be_an_instance_of Logger
18
+ end
19
+
20
+ it 'writes to stdout' do
21
+ expect { logger.warn('hello') }.to produce_output
22
+ end
23
+
24
+ it 'shows debugging by default' do
25
+ expect { logger.info('hello') }.to produce_output
26
+ end
27
+
28
+ it 'allows silencing debug output' do
29
+ expect { quiet_logger.info('hello') }.to_not produce_output
30
+ expect { quiet_logger.warn('hello') }.to produce_output
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: menagerie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Les Aker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-26 00:00:00.000000000 Z
11
+ date: 2015-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cymbal
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.1
19
+ version: 1.0.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
- version: 0.0.1
26
+ version: 1.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubocop
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.26.0
33
+ version: 0.29.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.26.0
40
+ version: 0.29.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 10.3.2
47
+ version: 10.4.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 10.3.2
54
+ version: 10.4.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: coveralls
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.1.0
75
+ version: 3.2.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.1.0
82
+ version: 3.2.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: fuubar
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -105,6 +105,7 @@ files:
105
105
  - ".rspec"
106
106
  - ".rubocop.yml"
107
107
  - ".travis.yml"
108
+ - CHANGELOG.md
108
109
  - Gemfile
109
110
  - LICENSE
110
111
  - README.md
@@ -115,6 +116,47 @@ files:
115
116
  - lib/menagerie/collection.rb
116
117
  - lib/menagerie/release.rb
117
118
  - menagerie.gemspec
119
+ - spec/examples/empty/.keep
120
+ - spec/examples/empty_with_dirs/artifacts/.keep
121
+ - spec/examples/empty_with_dirs/releases/.keep
122
+ - spec/examples/existing/artifacts/a/0.0.1
123
+ - spec/examples/existing/artifacts/a/0.0.2
124
+ - spec/examples/existing/artifacts/a/0.0.3
125
+ - spec/examples/existing/artifacts/b/1.0.0
126
+ - spec/examples/existing/artifacts/b/1.0.1
127
+ - spec/examples/existing/artifacts/c/0.1.1
128
+ - spec/examples/existing/artifacts/c/0.1.2
129
+ - spec/examples/existing/artifacts/c/0.2.0
130
+ - spec/examples/existing/releases/0/a
131
+ - spec/examples/existing/releases/0/b
132
+ - spec/examples/existing/releases/0/c
133
+ - spec/examples/existing/releases/1/a
134
+ - spec/examples/existing/releases/1/b
135
+ - spec/examples/existing/releases/1/c
136
+ - spec/examples/existing/releases/2/a
137
+ - spec/examples/existing/releases/2/b
138
+ - spec/examples/existing/releases/2/c
139
+ - spec/examples/orphaned/artifacts/a/0.0.1
140
+ - spec/examples/orphaned/artifacts/a/0.0.2
141
+ - spec/examples/orphaned/artifacts/a/0.0.3
142
+ - spec/examples/orphaned/artifacts/b/1.0.0
143
+ - spec/examples/orphaned/artifacts/b/1.0.1
144
+ - spec/examples/orphaned/artifacts/c/0.1.0
145
+ - spec/examples/orphaned/artifacts/c/0.1.1
146
+ - spec/examples/orphaned/artifacts/c/0.1.2
147
+ - spec/examples/orphaned/artifacts/c/0.2.0
148
+ - spec/examples/orphaned/releases/0/a
149
+ - spec/examples/orphaned/releases/0/b
150
+ - spec/examples/orphaned/releases/0/c
151
+ - spec/examples/orphaned/releases/1/a
152
+ - spec/examples/orphaned/releases/1/b
153
+ - spec/examples/orphaned/releases/1/c
154
+ - spec/examples/orphaned/releases/2/a
155
+ - spec/examples/orphaned/releases/2/b
156
+ - spec/examples/orphaned/releases/2/c
157
+ - spec/menagerie/artifact_spec.rb
158
+ - spec/menagerie/collection_spec.rb
159
+ - spec/menagerie/release_spec.rb
118
160
  - spec/menagerie_spec.rb
119
161
  - spec/spec_helper.rb
120
162
  homepage: https://github.com/akerl/menagerie
@@ -137,10 +179,51 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
179
  version: '0'
138
180
  requirements: []
139
181
  rubyforge_project:
140
- rubygems_version: 2.2.2
182
+ rubygems_version: 2.4.5
141
183
  signing_key:
142
184
  specification_version: 4
143
185
  summary: ''
144
186
  test_files:
187
+ - spec/examples/empty/.keep
188
+ - spec/examples/empty_with_dirs/artifacts/.keep
189
+ - spec/examples/empty_with_dirs/releases/.keep
190
+ - spec/examples/existing/artifacts/a/0.0.1
191
+ - spec/examples/existing/artifacts/a/0.0.2
192
+ - spec/examples/existing/artifacts/a/0.0.3
193
+ - spec/examples/existing/artifacts/b/1.0.0
194
+ - spec/examples/existing/artifacts/b/1.0.1
195
+ - spec/examples/existing/artifacts/c/0.1.1
196
+ - spec/examples/existing/artifacts/c/0.1.2
197
+ - spec/examples/existing/artifacts/c/0.2.0
198
+ - spec/examples/existing/releases/0/a
199
+ - spec/examples/existing/releases/0/b
200
+ - spec/examples/existing/releases/0/c
201
+ - spec/examples/existing/releases/1/a
202
+ - spec/examples/existing/releases/1/b
203
+ - spec/examples/existing/releases/1/c
204
+ - spec/examples/existing/releases/2/a
205
+ - spec/examples/existing/releases/2/b
206
+ - spec/examples/existing/releases/2/c
207
+ - spec/examples/orphaned/artifacts/a/0.0.1
208
+ - spec/examples/orphaned/artifacts/a/0.0.2
209
+ - spec/examples/orphaned/artifacts/a/0.0.3
210
+ - spec/examples/orphaned/artifacts/b/1.0.0
211
+ - spec/examples/orphaned/artifacts/b/1.0.1
212
+ - spec/examples/orphaned/artifacts/c/0.1.0
213
+ - spec/examples/orphaned/artifacts/c/0.1.1
214
+ - spec/examples/orphaned/artifacts/c/0.1.2
215
+ - spec/examples/orphaned/artifacts/c/0.2.0
216
+ - spec/examples/orphaned/releases/0/a
217
+ - spec/examples/orphaned/releases/0/b
218
+ - spec/examples/orphaned/releases/0/c
219
+ - spec/examples/orphaned/releases/1/a
220
+ - spec/examples/orphaned/releases/1/b
221
+ - spec/examples/orphaned/releases/1/c
222
+ - spec/examples/orphaned/releases/2/a
223
+ - spec/examples/orphaned/releases/2/b
224
+ - spec/examples/orphaned/releases/2/c
225
+ - spec/menagerie/artifact_spec.rb
226
+ - spec/menagerie/collection_spec.rb
227
+ - spec/menagerie/release_spec.rb
145
228
  - spec/menagerie_spec.rb
146
229
  - spec/spec_helper.rb