box-release 0.0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +28 -0
- data/Rakefile +5 -0
- data/autotest/discover.rb +1 -0
- data/bin/box-release +11 -0
- data/box-release.gemspec +26 -0
- data/lib/box/release/base.rb +195 -0
- data/lib/box/release/cli.rb +89 -0
- data/lib/box/release/downloader.rb +74 -0
- data/lib/box/release/loader.rb +31 -0
- data/lib/box/release/memory.rb +31 -0
- data/lib/box/release/version.rb +5 -0
- data/lib/box/release.rb +48 -0
- data/spec/box/release/base_spec.rb +118 -0
- data/spec/box/release/cli_spec.rb +235 -0
- data/spec/box/release/loader_spec.rb +50 -0
- data/spec/box/release/memory_spec.rb +8 -0
- data/spec/fixtures/box-update-20100421-0846.tar +0 -0
- data/spec/fixtures/current.yml +3 -0
- data/spec/fixtures/latest.yml +5 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/tasks/rspec.rake +36 -0
- metadata +140 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
box-release (0.0.1)
|
5
|
+
activesupport
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (3.0.7)
|
11
|
+
diff-lcs (1.1.2)
|
12
|
+
rcov (0.9.9)
|
13
|
+
rspec (2.5.0)
|
14
|
+
rspec-core (~> 2.5.0)
|
15
|
+
rspec-expectations (~> 2.5.0)
|
16
|
+
rspec-mocks (~> 2.5.0)
|
17
|
+
rspec-core (2.5.1)
|
18
|
+
rspec-expectations (2.5.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.5.0)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
box-release!
|
27
|
+
rcov
|
28
|
+
rspec
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/bin/box-release
ADDED
data/box-release.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "box/release/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "box-release"
|
7
|
+
s.version = Box::Release::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Alban Peignier", "Florent Peyraud"]
|
10
|
+
s.email = ["alban@tryphon.eu", "florent@tryphon.eu"]
|
11
|
+
s.homepage = "http://projects.tryphon.eu/box-release"
|
12
|
+
s.summary = %q{Check, download and install Tryphon boxes releases}
|
13
|
+
s.description = %q{}
|
14
|
+
|
15
|
+
s.rubyforge_project = "box-release"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency("activesupport")
|
23
|
+
|
24
|
+
s.add_development_dependency('rspec')
|
25
|
+
s.add_development_dependency('rcov')
|
26
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'box/release/downloader'
|
2
|
+
require 'digest'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module Box
|
6
|
+
module Release
|
7
|
+
module Base
|
8
|
+
|
9
|
+
# after_save :change_current
|
10
|
+
|
11
|
+
# def after_initialize
|
12
|
+
# self.status_updated_at ||= Time.now
|
13
|
+
# self.status ||= :available
|
14
|
+
# end
|
15
|
+
|
16
|
+
# @@latest_url = nil
|
17
|
+
# cattr_accessor :latest_url
|
18
|
+
|
19
|
+
# def self.check_latest
|
20
|
+
# Loader.save_release_at(latest_url) do |release|
|
21
|
+
# release.status = :available
|
22
|
+
# release.newer?(last)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
|
26
|
+
# @@current_url = nil
|
27
|
+
# cattr_accessor :current_url
|
28
|
+
|
29
|
+
# def self.check_current
|
30
|
+
# Loader.save_release_at(current_url) do |release|
|
31
|
+
# release.status = :installed
|
32
|
+
# true
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
|
36
|
+
# def self.check
|
37
|
+
# check_current
|
38
|
+
# check_latest
|
39
|
+
# end
|
40
|
+
|
41
|
+
# def self.latest
|
42
|
+
# latest = last
|
43
|
+
# latest.reset_download_status if latest and latest.newer?(current)
|
44
|
+
# end
|
45
|
+
|
46
|
+
# def self.current
|
47
|
+
# find_by_status("installed")
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def human_name
|
51
|
+
# name.gsub("_"," ").capitalize.gsub("box","Box")
|
52
|
+
# end
|
53
|
+
|
54
|
+
# def status
|
55
|
+
# status = read_attribute :status
|
56
|
+
# status ? ActiveSupport::StringInquirer.new(status) : nil
|
57
|
+
# end
|
58
|
+
|
59
|
+
# def status=(status, update_timestamp = true)
|
60
|
+
# write_attribute :status, status ? status.to_s : nil
|
61
|
+
# self.status_updated_at = Time.now unless new_record?
|
62
|
+
# end
|
63
|
+
|
64
|
+
def file
|
65
|
+
"/tmp/release.tar"
|
66
|
+
end
|
67
|
+
|
68
|
+
def newer?(other)
|
69
|
+
other.nil? or (self.name and self.name > other.name)
|
70
|
+
end
|
71
|
+
|
72
|
+
# def presenter
|
73
|
+
# @presenter ||= ReleasePresenter.new self
|
74
|
+
# end
|
75
|
+
|
76
|
+
def download
|
77
|
+
logger.info "Download #{url} into #{file}"
|
78
|
+
# return if self.status.downloaded?
|
79
|
+
|
80
|
+
# update_attribute :status, :download_pending
|
81
|
+
|
82
|
+
File.open(file, "w") do |file|
|
83
|
+
Box::Release::Downloader.open(url) do |data, download_size, url_size|
|
84
|
+
file.write data
|
85
|
+
|
86
|
+
# self.url_size ||= url_size
|
87
|
+
# self.download_size = download_size
|
88
|
+
# save! if 10.seconds.ago > self.updated_at
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
raise "Invalid checksum after download" unless valid_checksum?
|
93
|
+
# self.status = :downloaded
|
94
|
+
ensure
|
95
|
+
# self.status = :download_failed unless self.status.downloaded?
|
96
|
+
# save!
|
97
|
+
end
|
98
|
+
|
99
|
+
# def reset_download_status
|
100
|
+
# if %w{download_pending downloaded}.include?(status) and
|
101
|
+
# status_updated_at < 30.seconds.ago and
|
102
|
+
# not file_exists?
|
103
|
+
|
104
|
+
# update_attributes :status => :download_failed, :url_size => nil, :download_size => nil
|
105
|
+
# end
|
106
|
+
|
107
|
+
# self
|
108
|
+
# end
|
109
|
+
|
110
|
+
def install(options = {})
|
111
|
+
options = {
|
112
|
+
:install_command => install_command
|
113
|
+
}.merge(options)
|
114
|
+
|
115
|
+
logger.info "Install #{name}"
|
116
|
+
Box::Release.execute! "#{options[:install_command]} #{file} #{yaml_file}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def yaml_file
|
120
|
+
@yaml_file ||= Tempfile.new("release").tap do |yaml_file|
|
121
|
+
begin
|
122
|
+
yaml_file.write to_yaml
|
123
|
+
ensure
|
124
|
+
yaml_file.close
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
@yaml_file.path
|
129
|
+
end
|
130
|
+
|
131
|
+
def valid_checksum?
|
132
|
+
checksum.nil? or checksum == file_checksum
|
133
|
+
end
|
134
|
+
|
135
|
+
def file_checksum
|
136
|
+
Digest::SHA256.file(file).hexdigest if file_exists?
|
137
|
+
end
|
138
|
+
|
139
|
+
def file_exists?
|
140
|
+
File.exists?(file)
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_yaml
|
144
|
+
{ "name" => name, "description_url" => description_url, "status_updated_at" => status_updated_at }.to_yaml
|
145
|
+
end
|
146
|
+
|
147
|
+
# def change_current
|
148
|
+
# return unless status.installed?
|
149
|
+
|
150
|
+
# (Release.find_all_by_status("installed") - [self]).each do |old_release|
|
151
|
+
# old_release.update_attribute :status, :old
|
152
|
+
# end
|
153
|
+
# end
|
154
|
+
|
155
|
+
# class Loader
|
156
|
+
|
157
|
+
# attr_reader :url
|
158
|
+
|
159
|
+
# def initialize(url)
|
160
|
+
# @url = url
|
161
|
+
# end
|
162
|
+
|
163
|
+
# def self.release_at(url)
|
164
|
+
# new(url).release if url
|
165
|
+
# end
|
166
|
+
|
167
|
+
# def self.save_release_at(url)
|
168
|
+
# if release = release_at(url)
|
169
|
+
# release.save unless block_given? and not yield release
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
|
173
|
+
# def attributes
|
174
|
+
# @attributes ||= YAML.load open(url,&:read)
|
175
|
+
# rescue => e
|
176
|
+
# Rails.logger.error "Can't load attributes from #{url} : #{e}"
|
177
|
+
# {}
|
178
|
+
# end
|
179
|
+
|
180
|
+
# def supported_attributes
|
181
|
+
# attributes.reject do |attribute, _|
|
182
|
+
# not Release.column_names.include? attribute.to_s
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
|
186
|
+
# def release
|
187
|
+
# Release.find_or_create_by_name supported_attributes["name"], supported_attributes
|
188
|
+
# end
|
189
|
+
|
190
|
+
# end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Box
|
4
|
+
module Release
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
attr_accessor :url, :current_definition, :install_command, :before_download_command
|
8
|
+
|
9
|
+
def initialize(arguments = [])
|
10
|
+
@install_command = "/usr/local/sbin/box-upgrade"
|
11
|
+
|
12
|
+
parse_options(arguments)
|
13
|
+
end
|
14
|
+
|
15
|
+
def usage(output = $stdout)
|
16
|
+
output.puts option_parser
|
17
|
+
end
|
18
|
+
|
19
|
+
def option_parser
|
20
|
+
@option_parser ||= OptionParser.new do |options|
|
21
|
+
options.banner = <<-BANNER.gsub(/^ /,'')
|
22
|
+
Box Release CLI : update your box in command line
|
23
|
+
|
24
|
+
Usage: #{File.basename($0)} [options]
|
25
|
+
|
26
|
+
Options are:
|
27
|
+
BANNER
|
28
|
+
options.separator ""
|
29
|
+
options.on("-c", "--current=file/name", String,
|
30
|
+
"The current.yml file or the current name") { |arg| self.current_definition = arg }
|
31
|
+
options.on("-i", "--install=command", String,
|
32
|
+
"The install command used to install the release") { |arg| self.install_command = arg }
|
33
|
+
options.on("-b", "--before-download=command", String,
|
34
|
+
"The command executed before download starts") { |arg| self.before_download_command = arg }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_options(arguments)
|
39
|
+
option_parser.parse!(arguments)
|
40
|
+
@url = arguments.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
def latest
|
44
|
+
@latest ||= Box::Release::Loader.release_at url
|
45
|
+
end
|
46
|
+
|
47
|
+
def current
|
48
|
+
@current ||=
|
49
|
+
if File.exists?(current_definition)
|
50
|
+
Box::Release::Loader.release_at current_definition
|
51
|
+
else
|
52
|
+
Box::Release::Memory.new :name => current_definition
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?
|
57
|
+
current_definition
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate!(output = $stdout)
|
61
|
+
unless valid?
|
62
|
+
usage(output)
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def before_download
|
70
|
+
Box::Release.execute! before_download_command if before_download_command
|
71
|
+
end
|
72
|
+
|
73
|
+
def upgrade
|
74
|
+
if latest and latest.newer?(current)
|
75
|
+
Box::Release.logger.info "New release : #{latest}"
|
76
|
+
before_download
|
77
|
+
latest.download
|
78
|
+
Box::Release.logger.info "Replace #{current} with #{latest}"
|
79
|
+
latest.install :install_command => install_command
|
80
|
+
Box::Release.logger.info "Successfully installed #{latest}"
|
81
|
+
else
|
82
|
+
Box::Release.logger.info "Keep current release : #{current} (latest is #{latest})"
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Box
|
4
|
+
module Release
|
5
|
+
class Downloader
|
6
|
+
|
7
|
+
attr_reader :url
|
8
|
+
|
9
|
+
def initialize(url)
|
10
|
+
@url = URI.parse url
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.open(url, &block)
|
14
|
+
new(url).open(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def open(&block)
|
18
|
+
if url.scheme == "http"
|
19
|
+
open_http &block
|
20
|
+
else
|
21
|
+
open_file &block
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def open_file(&block)
|
26
|
+
content_length = File.size(url.path)
|
27
|
+
File.open(url.path, "r") do |file|
|
28
|
+
downloaded_size = 0
|
29
|
+
while data = file.read(1024)
|
30
|
+
downloaded_size += data.size
|
31
|
+
yield data, downloaded_size, content_length
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def open_http(&block)
|
37
|
+
send_request do |response|
|
38
|
+
content_length = nil
|
39
|
+
if response.key?('Content-Length')
|
40
|
+
content_length = response['Content-Length'].to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
downloaded_size = 0
|
44
|
+
response.read_body do |data|
|
45
|
+
downloaded_size += data.size
|
46
|
+
yield data, downloaded_size, content_length
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_request(target = url, redirection_count = 0, &block)
|
52
|
+
raise "Too many redirections, last one was #{target}" if redirection_count > 10
|
53
|
+
|
54
|
+
Net::HTTP.start(target.host, target.port) do |http|
|
55
|
+
request = Net::HTTP::Get.new target.path
|
56
|
+
http.request(request) do |response|
|
57
|
+
case response
|
58
|
+
when Net::HTTPSuccess
|
59
|
+
yield response
|
60
|
+
when Net::HTTPMovedPermanently, # 301
|
61
|
+
Net::HTTPFound, # 302
|
62
|
+
Net::HTTPSeeOther, # 303
|
63
|
+
Net::HTTPTemporaryRedirect # 307
|
64
|
+
send_request URI.parse(response['location']), redirection_count+1, &block
|
65
|
+
else
|
66
|
+
response.error!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module Box
|
5
|
+
module Release
|
6
|
+
class Loader
|
7
|
+
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
def initialize(url)
|
11
|
+
@url = url
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.release_at(url)
|
15
|
+
new(url).release if url
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
@attributes ||= YAML.load open(url,&:read)
|
20
|
+
rescue => e
|
21
|
+
Box::Release.logger.error "Can't load attributes from #{url} : #{e}"
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
|
25
|
+
def release
|
26
|
+
Box::Release::Memory.new attributes
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'box/release/base'
|
2
|
+
#require 'active_support/string_inquirer'
|
3
|
+
|
4
|
+
module Box
|
5
|
+
module Release
|
6
|
+
class Memory
|
7
|
+
include Box::Release::Base
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
attributes.each_pair do |k,v|
|
11
|
+
send "#{k}=", v
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :name, :description_url, :status_updated_at
|
16
|
+
attr_accessor :url, :checksum
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def logger
|
23
|
+
Box::Release.logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def install_command
|
27
|
+
Box::Release.install_command
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/box/release.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'syslog_logger'
|
6
|
+
rescue LoadError
|
7
|
+
$stderr.puts "SyslogLogger isn't available"
|
8
|
+
end
|
9
|
+
|
10
|
+
module Box
|
11
|
+
module Release
|
12
|
+
|
13
|
+
def self.default_logger
|
14
|
+
if defined?(SyslogLogger)
|
15
|
+
SyslogLogger.new("box-release")
|
16
|
+
else
|
17
|
+
Logger.new($stdout)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@@logger = default_logger
|
22
|
+
mattr_accessor :logger
|
23
|
+
|
24
|
+
@@install_command = nil
|
25
|
+
mattr_accessor :install_command
|
26
|
+
|
27
|
+
class ExecutionError < StandardError; end
|
28
|
+
|
29
|
+
def self.execute!(command)
|
30
|
+
logger.debug "execute '#{command}'"
|
31
|
+
output = `#{command} 2>&1`
|
32
|
+
logger.debug output unless output.empty?
|
33
|
+
|
34
|
+
if $? != 0
|
35
|
+
raise ExecutionError.new("Command failed: #{command} (error code #{$?})")
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'box/release/loader'
|
45
|
+
require 'box/release/downloader'
|
46
|
+
require 'box/release/base'
|
47
|
+
require 'box/release/memory'
|
48
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
describe Box::Release::Base do
|
6
|
+
|
7
|
+
let(:update_tar) { File.expand_path "../../../fixtures/box-update-20100421-0846.tar", __FILE__ }
|
8
|
+
|
9
|
+
subject { Box::Release::Memory.new }
|
10
|
+
|
11
|
+
its(:file) { should == "/tmp/release.tar" }
|
12
|
+
|
13
|
+
describe "download" do
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
FileUtils.rm_f(subject.file)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should download a local file" do
|
20
|
+
subject.url = __FILE__
|
21
|
+
subject.download
|
22
|
+
IO.read(subject.file).should == IO.read(__FILE__)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should raise an error if checksum is invalid" do
|
26
|
+
subject.stub :valid_checksum? => false
|
27
|
+
lambda { subject.download }.should raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "valid_checksum? " do
|
33
|
+
|
34
|
+
before(:each) do
|
35
|
+
subject.stub :file => update_tar
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return true if checksum is SHA256 digest of downloaded file" do
|
39
|
+
subject.checksum = "9eeb495a5b273c7dd88aa8dd741df8ecf10b5a34c422b0fe6b7a1b053a518369"
|
40
|
+
subject.should be_valid_checksum
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return false if checksum is not the SHA256 digest of downloaded file" do
|
44
|
+
subject.checksum = "dummy"
|
45
|
+
subject.should_not be_valid_checksum
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return true if checksum is nil" do
|
49
|
+
subject.checksum = nil
|
50
|
+
subject.should be_valid_checksum
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return false if download file isn't found" do
|
54
|
+
subject.checksum = "dummy"
|
55
|
+
subject.stub!(:file_checksum)
|
56
|
+
subject.should_not be_valid_checksum
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "to_yaml" do
|
62
|
+
|
63
|
+
def self.it_shoud_include(attribute)
|
64
|
+
attribute = attribute.to_s
|
65
|
+
it "should the Release #{attribute}" do
|
66
|
+
subject.send("#{attribute}=", "dummy")
|
67
|
+
YAML.load(subject.to_yaml).should include(attribute => "dummy")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it_shoud_include :name
|
72
|
+
it_shoud_include :description_url
|
73
|
+
it_shoud_include :status_updated_at
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "yaml_file" do
|
78
|
+
|
79
|
+
it "should return the path of a file which contains Release#to_yaml" do
|
80
|
+
subject.stub :to_yaml => "to_yaml"
|
81
|
+
IO.read(subject.yaml_file).should == "to_yaml"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#install" do
|
87
|
+
|
88
|
+
before(:each) do
|
89
|
+
subject.stub :yaml_file => "/path/to/tempfile"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should execute install command with release file and temp file (with attributes) in argument" do
|
93
|
+
subject.stub :install_command => "/usr/bin/dummy", :file => "/tmp/test.tar"
|
94
|
+
Box::Release.should_receive(:execute!).with("/usr/bin/dummy /tmp/test.tar /path/to/tempfile").and_return(true)
|
95
|
+
subject.install
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#newer? ' do
|
101
|
+
|
102
|
+
it "should be true when other release is nil" do
|
103
|
+
subject.should be_newer(nil)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should be true when name is greather than the other one" do
|
107
|
+
subject.name = "version-2"
|
108
|
+
subject.should be_newer(stub(:name => "version-1"))
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should be false when name is nil" do
|
112
|
+
subject.name = nil
|
113
|
+
subject.should_not be_newer(stub(:name => "version-1"))
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'box/release/cli'
|
4
|
+
|
5
|
+
describe Box::Release::CLI do
|
6
|
+
|
7
|
+
describe "#current_definition" do
|
8
|
+
|
9
|
+
it "should be nil by default" do
|
10
|
+
subject.current_definition.should be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be the value specified with --current" do
|
14
|
+
subject.parse_options %w{--current dummy}
|
15
|
+
subject.current_definition.should == "dummy"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#install_command" do
|
21
|
+
|
22
|
+
it "should be /usr/local/sbin/box-upgrade by default" do
|
23
|
+
subject.install_command.should == "/usr/local/sbin/box-upgrade"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use the value specified with --install" do
|
27
|
+
subject.parse_options %w{--install dummy}
|
28
|
+
subject.install_command.should == "dummy"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#url" do
|
34
|
+
|
35
|
+
it "should be nil by default" do
|
36
|
+
subject.url.should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should use the valud specified in argument (after options)" do
|
40
|
+
subject.parse_options %w{dummy}
|
41
|
+
subject.url.should == "dummy"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#before_download_command" do
|
47
|
+
|
48
|
+
it "should be nil by default" do
|
49
|
+
subject.before_download_command.should be_nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should use the valud specified with --before-download" do
|
53
|
+
subject.parse_options %w{--before-download dummy}
|
54
|
+
subject.before_download_command.should == "dummy"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#valid?" do
|
60
|
+
|
61
|
+
subject {
|
62
|
+
Box::Release::CLI.new.tap do |cli|
|
63
|
+
cli.current_definition = "dummy"
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
it "should be true when current_definition is nil" do
|
68
|
+
subject.current_definition = nil
|
69
|
+
subject.should_not be_valid
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "validate!" do
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
subject.stub :exit => true
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when valid" do
|
81
|
+
|
82
|
+
before(:each) do
|
83
|
+
subject.stub :valid? => true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should return the CLI instance" do
|
87
|
+
subject.validate!.should == subject
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when invalid" do
|
93
|
+
|
94
|
+
before(:each) do
|
95
|
+
subject.stub :valid? => false
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should display usage" do
|
99
|
+
subject.should_receive(:usage)
|
100
|
+
subject.validate!
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should exit with code 1" do
|
104
|
+
subject.should_receive(:exit).with(1)
|
105
|
+
subject.validate!(StringIO.new)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#latest" do
|
113
|
+
|
114
|
+
before(:each) do
|
115
|
+
subject.url = given_url
|
116
|
+
end
|
117
|
+
|
118
|
+
let(:given_url) { "given_url" }
|
119
|
+
let(:loaded_release) { mock(Box::Release::Memory) }
|
120
|
+
|
121
|
+
it "should load release described by the given url" do
|
122
|
+
Box::Release::Loader.should_receive(:release_at).with(given_url).and_return(loaded_release)
|
123
|
+
subject.latest.should == loaded_release
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "current" do
|
129
|
+
|
130
|
+
context "when current_defintion is a existing file" do
|
131
|
+
|
132
|
+
before(:each) do
|
133
|
+
subject.current_definition = existing_file
|
134
|
+
end
|
135
|
+
|
136
|
+
let(:existing_file) { File.expand_path("../../../fixtures/current.yml", __FILE__) }
|
137
|
+
let(:loaded_release) { mock(Box::Release::Memory) }
|
138
|
+
|
139
|
+
it "should load release described by the given file" do
|
140
|
+
Box::Release::Loader.should_receive(:release_at).with(subject.current_definition).and_return(loaded_release)
|
141
|
+
subject.current.should == loaded_release
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when current_definition is an existing file" do
|
147
|
+
|
148
|
+
before(:each) do
|
149
|
+
subject.current_definition = "dummy"
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should create a Box::Release with the given name" do
|
153
|
+
subject.current.name.should == "dummy"
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "upgrade" do
|
161
|
+
|
162
|
+
before(:each) do
|
163
|
+
subject.current_definition = "dummy"
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when latest release can't be loaded" do
|
167
|
+
|
168
|
+
before(:each) do
|
169
|
+
subject.stub :latest => nil
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should return false" do
|
173
|
+
subject.upgrade.should be_false
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when latest release isn't newer than current" do
|
179
|
+
|
180
|
+
before(:each) do
|
181
|
+
subject.stub :latest => mock(:newer? => false)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should return false" do
|
185
|
+
subject.upgrade.should be_false
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
context "when latest release is new than current" do
|
191
|
+
|
192
|
+
before(:each) do
|
193
|
+
subject.stub :latest => latest
|
194
|
+
end
|
195
|
+
|
196
|
+
let(:latest) { mock :name => "latest", :newer? => true, :download => true, :install => true }
|
197
|
+
|
198
|
+
it "should invoke before_download ... before downloading latest release" do
|
199
|
+
subject.should_receive(:before_download).ordered
|
200
|
+
subject.latest.should_receive(:download).ordered
|
201
|
+
subject.upgrade
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should download the latest release" do
|
205
|
+
subject.latest.should_receive(:download)
|
206
|
+
subject.upgrade
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should install the latest release (after downloading it ...)" do
|
210
|
+
subject.latest.should_receive(:download).ordered
|
211
|
+
subject.latest.should_receive(:install).ordered
|
212
|
+
subject.upgrade
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should install by using the given install_command" do
|
216
|
+
subject.install_command = "dummy"
|
217
|
+
subject.latest.should_receive(:install).with(:install_command => subject.install_command)
|
218
|
+
subject.upgrade
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "before_download" do
|
226
|
+
|
227
|
+
it "should execute the before_download_command if given" do
|
228
|
+
subject.before_download_command = "dummy"
|
229
|
+
Box::Release.should_receive(:execute!).with("dummy")
|
230
|
+
subject.before_download
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'box/release/loader'
|
4
|
+
|
5
|
+
describe Box::Release::Loader do
|
6
|
+
|
7
|
+
describe "#attributes" do
|
8
|
+
|
9
|
+
let(:loader) { Box::Release::Loader.new("dummy") }
|
10
|
+
|
11
|
+
it "should load attributes found in url" do
|
12
|
+
Tempfile.open("release-loader") do |file|
|
13
|
+
file.puts "dummy: true"
|
14
|
+
file.close
|
15
|
+
|
16
|
+
Box::Release::Loader.new(file.path).attributes.should == { "dummy" => true }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be empty if url isn't reachable" do
|
21
|
+
loader.stub(:open).and_raise(SocketError)
|
22
|
+
loader.attributes.should be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#release" do
|
28
|
+
|
29
|
+
let(:loader) { Box::Release::Loader.new("dummy") }
|
30
|
+
|
31
|
+
it "should create a new Release with attributes" do
|
32
|
+
loader.stub :attributes => { "checksum" => "dummy" }
|
33
|
+
loader.release.checksum.should == "dummy"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".release_at" do
|
39
|
+
|
40
|
+
let(:url) { "dummy://url" }
|
41
|
+
let(:loaded_release) { Box::Release::Memory.new }
|
42
|
+
|
43
|
+
it "should create a new Loader and load release" do
|
44
|
+
Box::Release::Loader.should_receive(:new).with(url).and_return(stub(:release => loaded_release))
|
45
|
+
Box::Release::Loader.release_at(url).should == loaded_release
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
Binary file
|
@@ -0,0 +1,5 @@
|
|
1
|
+
name: playbox-20110424-1616
|
2
|
+
url: spec/fixtures/box-update-20100421-0846.tar
|
3
|
+
checksum: 9eeb495a5b273c7dd88aa8dd741df8ecf10b5a34c422b0fe6b7a1b053a518369
|
4
|
+
status_updated_at: Sun Apr 24 16:19:08 +0200 2011
|
5
|
+
description_url: http://www.tryphon.eu/release/playbox-20110424-1616
|
data/spec/rcov.opts
ADDED
data/spec/spec_helper.rb
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require 'rspec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'rspec'
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
rescue LoadError
|
11
|
+
puts <<-EOS
|
12
|
+
To use rspec for testing you must install rspec gem:
|
13
|
+
gem install rspec
|
14
|
+
EOS
|
15
|
+
exit(0)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run the specs under spec"
|
19
|
+
RSpec::Core::RakeTask.new do |t|
|
20
|
+
t.pattern = 'spec/**/*_spec.rb'
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :spec do
|
24
|
+
desc "Run all specs in spec directory with RCov (excluding plugin specs)"
|
25
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
26
|
+
t.rcov = true
|
27
|
+
t.pattern = "./spec/**/*_spec.rb"
|
28
|
+
t.rcov_opts = '--exclude /gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-,spec/*'
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Print Specdoc for all specs (excluding plugin specs)"
|
32
|
+
RSpec::Core::RakeTask.new(:doc) do |t|
|
33
|
+
t.rspec_opts = ["--format", "specdoc", "--dry-run"]
|
34
|
+
t.pattern = "./spec/**/*_spec.rb"
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: box-release
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alban Peignier
|
14
|
+
- Florent Peyraud
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-05-10 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rcov
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: ""
|
64
|
+
email:
|
65
|
+
- alban@tryphon.eu
|
66
|
+
- florent@tryphon.eu
|
67
|
+
executables:
|
68
|
+
- box-release
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- .gitignore
|
75
|
+
- Gemfile
|
76
|
+
- Gemfile.lock
|
77
|
+
- Rakefile
|
78
|
+
- autotest/discover.rb
|
79
|
+
- bin/box-release
|
80
|
+
- box-release.gemspec
|
81
|
+
- lib/box/release.rb
|
82
|
+
- lib/box/release/base.rb
|
83
|
+
- lib/box/release/cli.rb
|
84
|
+
- lib/box/release/downloader.rb
|
85
|
+
- lib/box/release/loader.rb
|
86
|
+
- lib/box/release/memory.rb
|
87
|
+
- lib/box/release/version.rb
|
88
|
+
- spec/box/release/base_spec.rb
|
89
|
+
- spec/box/release/cli_spec.rb
|
90
|
+
- spec/box/release/loader_spec.rb
|
91
|
+
- spec/box/release/memory_spec.rb
|
92
|
+
- spec/fixtures/box-update-20100421-0846.tar
|
93
|
+
- spec/fixtures/current.yml
|
94
|
+
- spec/fixtures/latest.yml
|
95
|
+
- spec/rcov.opts
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
- tasks/rspec.rake
|
98
|
+
homepage: http://projects.tryphon.eu/box-release
|
99
|
+
licenses: []
|
100
|
+
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
version: "0"
|
124
|
+
requirements: []
|
125
|
+
|
126
|
+
rubyforge_project: box-release
|
127
|
+
rubygems_version: 1.7.2
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: Check, download and install Tryphon boxes releases
|
131
|
+
test_files:
|
132
|
+
- spec/box/release/base_spec.rb
|
133
|
+
- spec/box/release/cli_spec.rb
|
134
|
+
- spec/box/release/loader_spec.rb
|
135
|
+
- spec/box/release/memory_spec.rb
|
136
|
+
- spec/fixtures/box-update-20100421-0846.tar
|
137
|
+
- spec/fixtures/current.yml
|
138
|
+
- spec/fixtures/latest.yml
|
139
|
+
- spec/rcov.opts
|
140
|
+
- spec/spec_helper.rb
|