geoffrey 0.0.6 → 0.1.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/.gitignore +1 -0
- data/Gemfile.lock +9 -1
- data/README.md +24 -18
- data/Rakefile +4 -2
- data/bin/geoffrey +8 -0
- data/geoffrey.gemspec +4 -1
- data/lib/geoffrey.rb +8 -0
- data/lib/geoffrey/cli.rb +58 -0
- data/lib/geoffrey/package.rb +91 -30
- data/lib/geoffrey/plist.rb +20 -6
- data/lib/geoffrey/reporter.rb +42 -0
- data/lib/geoffrey/version.rb +1 -1
- data/spec/cli_spec.rb +67 -0
- data/spec/geoffrey_spec.rb +15 -0
- data/spec/package_spec.rb +54 -17
- data/spec/plist_spec.rb +4 -6
- data/spec/reporter_spec.rb +79 -0
- data/spec/spec_helper.rb +6 -3
- data/spec/static/test.dmg b/data/spec/static/test → 1.dmg +0 -0
- data/spec/static/test.txt.gz.123 +0 -0
- data/spec/support/helpers.rb +17 -0
- data/spec/templates/growl.rb +3 -0
- data/spec/templates/simbl.rb +0 -0
- data/templates/adium.rb +4 -0
- data/templates/growl.rb +4 -0
- data/templates/skype.rb +4 -0
- data/templates/unrarx.rb +4 -0
- metadata +67 -23
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
geoffrey (0.0
|
4
|
+
geoffrey (0.1.0)
|
5
5
|
plist4r
|
6
|
+
progressbar
|
7
|
+
thor
|
6
8
|
|
7
9
|
GEM
|
8
10
|
remote: http://rubygems.org/
|
9
11
|
specs:
|
12
|
+
autotest (4.3.2)
|
10
13
|
bluecloth (2.0.7)
|
11
14
|
haml (3.0.18)
|
12
15
|
libxml-ruby (1.1.4)
|
@@ -18,18 +21,23 @@ GEM
|
|
18
21
|
haml
|
19
22
|
libxml-ruby
|
20
23
|
libxml4r
|
24
|
+
progressbar (0.9.0)
|
21
25
|
rake (0.8.7)
|
22
26
|
rspec (1.3.0)
|
27
|
+
thor (0.14.0)
|
23
28
|
yard (0.6.0)
|
24
29
|
|
25
30
|
PLATFORMS
|
26
31
|
ruby
|
27
32
|
|
28
33
|
DEPENDENCIES
|
34
|
+
autotest
|
29
35
|
bluecloth (~> 2.0.7)
|
30
36
|
bundler (~> 1.0.0.rc.5)
|
31
37
|
geoffrey!
|
32
38
|
mocha (~> 0.9.8)
|
33
39
|
plist4r
|
40
|
+
progressbar
|
34
41
|
rspec (~> 1.3.0)
|
42
|
+
thor
|
35
43
|
yard (~> 0.6.0)
|
data/README.md
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
geoffrey
|
2
2
|
========
|
3
3
|
|
4
|
-
## ALERT!
|
5
|
-
|
6
|
-
This is very early alpha code, use with precaution. It's a spinoff of myself willing to
|
7
|
-
improve my [dotfiles project](http://github.com/mrsimo/dotfiles) a step further. All
|
8
|
-
code examples in this README come from it.
|
9
|
-
|
10
4
|
## Links
|
11
5
|
|
12
6
|
* [Documentation](http://rubydoc.info/gems/geoffrey/frames) (this README is better viewed there ;)
|
@@ -14,10 +8,13 @@ code examples in this README come from it.
|
|
14
8
|
## Introduction
|
15
9
|
|
16
10
|
Geoffrey's your little helper, that who just wants to makes your life easier.
|
11
|
+
It helps you install applications into your OS X system.
|
12
|
+
|
13
|
+
It will help you in two ways, with a command line application, and an API to
|
14
|
+
let you create your own recipes.
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
you'll know it's a pain in the ass to keep everything up to date.
|
16
|
+
If, like me, you use different computers at work, home, or even when
|
17
|
+
travelling, you'll know it's a pain in the ass to keep everything up to date.
|
21
18
|
|
22
19
|
Inspired in the numerous dotfiles projects, specially
|
23
20
|
[holman's](http://github.com/holman/dotfiles),
|
@@ -27,8 +24,24 @@ the intention is to bring customization to a bigger extend.
|
|
27
24
|
|
28
25
|
$ [sudo] gem install geoffrey
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
Command line
|
28
|
+
============
|
29
|
+
|
30
|
+
Type `geoffrey` to see help with the commands. Right now you can list the
|
31
|
+
provided recipes, and install them.
|
32
|
+
|
33
|
+
$ geoffrey list
|
34
|
+
adium growl skype unrarx
|
35
|
+
|
36
|
+
$ geoffrey install adium
|
37
|
+
>> Adium is a free instant messaging application for Mac OS X that can connect to AIM, MSN, Jabber, Yahoo, and more.
|
38
|
+
>> Downloading http://adiumx.cachefly.net/Adium_1.3.10.dmg
|
39
|
+
: 100% |oooooooooooooooooooooooo| 21.3MB 451.6KB/s ETA: 00:00:00
|
40
|
+
>> Moving file to /Applications
|
41
|
+
>> Install successful
|
42
|
+
|
43
|
+
API
|
44
|
+
===
|
32
45
|
|
33
46
|
Geoffrey has many talents, and thus you'll find he can help you in one of these areas:
|
34
47
|
|
@@ -67,10 +80,3 @@ up and running with Pro scheme, Menlo Font 14pt, and a size 520x100.
|
|
67
80
|
end
|
68
81
|
|
69
82
|
See {Geoffrey::plist} for extended documentation.
|
70
|
-
|
71
|
-
## A few words
|
72
|
-
|
73
|
-
This is work done in my free hours, it's not yet finished. The code is dirty and mainly undocumented. It all started on #whyday,
|
74
|
-
but I'm so lazy that I've needed a lot of time to do just this.
|
75
|
-
|
76
|
-
Thanks!
|
data/Rakefile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'mocha'
|
3
5
|
|
4
6
|
desc "Execute specs"
|
5
|
-
|
6
|
-
|
7
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
8
|
+
t.spec_files = FileList['spec/**/*.rb'].reject{|f| f =~ /templates/ }
|
7
9
|
end
|
8
10
|
|
9
11
|
desc "Generate docs"
|
data/bin/geoffrey
ADDED
data/geoffrey.gemspec
CHANGED
@@ -16,13 +16,16 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_development_dependency "bundler", "~> 1.0.0.rc.5"
|
18
18
|
s.add_development_dependency "rspec", "~> 1.3.0"
|
19
|
+
s.add_development_dependency "autotest"
|
19
20
|
s.add_development_dependency "mocha", "~> 0.9.8"
|
20
21
|
s.add_development_dependency "yard", "~> 0.6.0"
|
21
22
|
s.add_development_dependency "bluecloth", "~> 2.0.7"
|
22
23
|
|
23
24
|
s.add_dependency 'plist4r'
|
25
|
+
s.add_dependency 'progressbar'
|
26
|
+
s.add_dependency 'thor'
|
24
27
|
|
25
28
|
s.files = `git ls-files`.split("\n")
|
26
|
-
s.executables =
|
29
|
+
s.executables = ['geoffrey']
|
27
30
|
s.require_path = 'lib'
|
28
31
|
end
|
data/lib/geoffrey.rb
CHANGED
@@ -5,6 +5,13 @@ module Geoffrey
|
|
5
5
|
|
6
6
|
class << self
|
7
7
|
|
8
|
+
def environment=(value)
|
9
|
+
@@environment = value if value
|
10
|
+
end
|
11
|
+
def environment
|
12
|
+
@@environment ||= :production
|
13
|
+
end
|
14
|
+
|
8
15
|
# Define a package installation from the URL specified. It will download
|
9
16
|
# the file, and if it's compressed, it will decompress it.It currently
|
10
17
|
# supports zip, tar.gz, .gz and .dmg files. Once extracted it will look for
|
@@ -18,6 +25,7 @@ module Geoffrey
|
|
18
25
|
# * +:if+ inverse of inverse
|
19
26
|
# * +:file+ what file has to be installed from all of the ones that came in
|
20
27
|
# the compressed file.
|
28
|
+
# * +:filename+ set the name of the file you are downloading.
|
21
29
|
#
|
22
30
|
# If you don't want to install a .pkg but do something else, you can just
|
23
31
|
# override the install method. See the examples
|
data/lib/geoffrey/cli.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Geoffrey
|
4
|
+
class CLI < Thor
|
5
|
+
|
6
|
+
attr_accessor :package
|
7
|
+
|
8
|
+
desc "list", "list of all the available templates"
|
9
|
+
def list(filter = '')
|
10
|
+
names = if filter && filter != ''
|
11
|
+
list_of_template_names.select{ |name| name.downcase.include?(filter.downcase) }
|
12
|
+
else
|
13
|
+
list_of_template_names
|
14
|
+
end
|
15
|
+
puts names.join(' ')
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "install [template]", "install the specified template"
|
19
|
+
method_options :verbose => :boolean
|
20
|
+
def install(name)
|
21
|
+
if file = file_for_template(name)
|
22
|
+
@package = Geoffrey::Package.new
|
23
|
+
@package.verbose(true) if options[:verbose]
|
24
|
+
@package.instance_eval File.read(file)
|
25
|
+
@package.download_and_decompress
|
26
|
+
@package.install
|
27
|
+
else
|
28
|
+
puts "Template not found"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
#--
|
36
|
+
# TODO Might be useful for the future to have multiple dirs for templates.
|
37
|
+
def templates_dir
|
38
|
+
File.dirname(__FILE__) + '/../../templates'
|
39
|
+
end
|
40
|
+
|
41
|
+
def list_of_templates
|
42
|
+
Dir[templates_dir + '/*']
|
43
|
+
end
|
44
|
+
|
45
|
+
def list_of_template_names
|
46
|
+
list_of_templates.map{ |file| name_from_file(file) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def file_for_template(name)
|
50
|
+
list_of_templates.select{ |file| name_from_file(file) == name }.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def name_from_file(file)
|
54
|
+
file.split("/").last.split(".").first
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/geoffrey/package.rb
CHANGED
@@ -2,6 +2,8 @@ require 'tmpdir'
|
|
2
2
|
require 'tempfile'
|
3
3
|
require 'open-uri'
|
4
4
|
require 'fileutils'
|
5
|
+
require 'progressbar'
|
6
|
+
require 'geoffrey/reporter'
|
5
7
|
|
6
8
|
module Geoffrey
|
7
9
|
|
@@ -10,43 +12,49 @@ module Geoffrey
|
|
10
12
|
|
11
13
|
# This class helps installing packages and other apps into your system.
|
12
14
|
class Package
|
13
|
-
|
15
|
+
|
16
|
+
include Geoffrey::Reporter
|
17
|
+
|
18
|
+
attr_accessor :url, :description, :options, :format, :filename, :dir, :file
|
14
19
|
|
15
20
|
def initialize
|
16
21
|
@options = {}
|
17
22
|
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def options(val = nil)
|
26
|
-
@options = val if val
|
27
|
-
@options
|
24
|
+
[:url,:name,:description,:options].each do |attrib|
|
25
|
+
class_eval <<-GEOF
|
26
|
+
def #{attrib}(val=nil)
|
27
|
+
val.nil? ? @#{attrib} : @#{attrib} = val
|
28
|
+
end
|
29
|
+
GEOF
|
28
30
|
end
|
29
31
|
|
30
32
|
# Uses the url provided to download the file.
|
31
33
|
# Checks the options first to see if it shouldn't be done.
|
32
|
-
#--
|
33
|
-
# TODO add some option to define the file name for weird url's
|
34
34
|
def download_and_decompress
|
35
|
+
echo "URL not specified" unless @url && @url != ""
|
36
|
+
|
37
|
+
get_filename and debug("filename detected: #{@filename}")
|
38
|
+
|
35
39
|
return unless should_install
|
36
40
|
|
41
|
+
echo @description if @description && @description != ""
|
42
|
+
|
37
43
|
begin
|
38
|
-
@file = Tempfile.new("geoffrey")
|
39
|
-
|
44
|
+
@file = Tempfile.new("geoffrey") and debug("downloading file into #{@file.path}")
|
45
|
+
|
46
|
+
read_with_progress_bar
|
40
47
|
@file.close
|
41
48
|
|
42
|
-
|
43
|
-
get_format
|
49
|
+
get_format and debug("format detected: #{@format}")
|
44
50
|
|
45
51
|
|
46
|
-
@dir = "#{Dir.tmpdir}/#{filename}"
|
52
|
+
@dir = "#{Dir.tmpdir}/#{filename}" and debug("will put files into temporary dir: #{@dir}")
|
53
|
+
|
47
54
|
FileUtils.rm_rf dir if File.exist?(dir)
|
48
55
|
FileUtils.mkdir_p dir
|
49
|
-
rescue
|
56
|
+
rescue Exception => e
|
57
|
+
echo "Can't find file #{@url}"
|
50
58
|
raise NotFoundError
|
51
59
|
end
|
52
60
|
|
@@ -64,10 +72,13 @@ module Geoffrey
|
|
64
72
|
|
65
73
|
case File.extname file_to_install
|
66
74
|
when ".pkg"
|
75
|
+
echo "Installing package"
|
67
76
|
execute "installer -pkg #{file_to_install} -target /"
|
68
77
|
when ".app"
|
69
|
-
|
78
|
+
echo "Moving file to /Applications"
|
79
|
+
FileUtils.mv file_to_install, "/Applications"
|
70
80
|
end
|
81
|
+
echo "Install successful"
|
71
82
|
end
|
72
83
|
|
73
84
|
# If it was specified through options, then get that, otherwise
|
@@ -87,13 +98,19 @@ module Geoffrey
|
|
87
98
|
private
|
88
99
|
|
89
100
|
def should_install
|
90
|
-
if @
|
91
|
-
|
92
|
-
|
93
|
-
@options[:if].call
|
101
|
+
ok_to_go = if @url && @url != ""
|
102
|
+
if @options[:unless] && @options[:unless].respond_to?(:call)
|
103
|
+
!@options[:unless].call
|
104
|
+
elsif @options[:if] && @options[:if].respond_to?(:call)
|
105
|
+
@options[:if].call
|
106
|
+
else
|
107
|
+
true
|
108
|
+
end
|
94
109
|
else
|
95
|
-
|
110
|
+
false
|
96
111
|
end
|
112
|
+
echo "#{filename} is already installed." unless ok_to_go
|
113
|
+
ok_to_go
|
97
114
|
end
|
98
115
|
|
99
116
|
# Simple wrapper to execute things, using sudo or not as per defiend in the
|
@@ -103,12 +120,29 @@ module Geoffrey
|
|
103
120
|
opts = @options.merge(opts)
|
104
121
|
command = "sudo #{command}" if opts[:sudo]
|
105
122
|
command = "#{command} > /dev/null 2>&1" unless opts[:verbose]
|
106
|
-
|
123
|
+
debug "-- executing: #{command}"
|
124
|
+
returned = `#{command}`
|
125
|
+
debug "-- returned:"
|
126
|
+
debug returned
|
127
|
+
returned
|
107
128
|
end
|
108
129
|
end
|
109
130
|
|
131
|
+
def read_with_progress_bar
|
132
|
+
pbar = nil
|
133
|
+
open(@url, :content_length_proc => lambda { |t|
|
134
|
+
if t && t > 0
|
135
|
+
echo "Downloading #{@url}"
|
136
|
+
pbar = ProgressBar.new("",t)
|
137
|
+
pbar.file_transfer_mode
|
138
|
+
end
|
139
|
+
}, :progress_proc => lambda { |s|
|
140
|
+
pbar.set s if pbar
|
141
|
+
}) { |f| @file.write f.read }
|
142
|
+
end
|
143
|
+
|
110
144
|
def get_format
|
111
|
-
@format = case File.extname @
|
145
|
+
@format = case File.extname @filename
|
112
146
|
when '.zip' then :zip
|
113
147
|
when '.tar' then :tar
|
114
148
|
when '.tgz' then :tgz
|
@@ -118,16 +152,23 @@ module Geoffrey
|
|
118
152
|
end
|
119
153
|
end
|
120
154
|
|
155
|
+
def get_filename
|
156
|
+
@filename = @options[:filename]
|
157
|
+
@filename ||= begin
|
158
|
+
escape_whitespaces(File.basename(@url).split("?").first) if @url && @url != ""
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
121
162
|
# Actual decompressing of the file. Does so into a directory we created in
|
122
163
|
# +#{Dir.tmpdir}/#{filename}+
|
123
164
|
#--
|
124
|
-
# TODO control errors. Maybe it's .zip but just doesn't work.
|
125
165
|
# TODO better isolate each download inside the tmpdir? When debugging it's
|
126
166
|
# misleading to have a folder with the file name and extension.
|
127
167
|
# * Timestamp if clash?
|
128
168
|
# * Random string?
|
129
169
|
def decompress
|
130
|
-
|
170
|
+
debug "decompressing #{file.path}"
|
171
|
+
case format
|
131
172
|
when :zip
|
132
173
|
execute "unzip -o #{file.path} -d #{dir}", :sudo => false
|
133
174
|
when :tar
|
@@ -144,21 +185,41 @@ module Geoffrey
|
|
144
185
|
|
145
186
|
# Might be that the decompression failed
|
146
187
|
raise DecompressionError unless $?.success?
|
188
|
+
|
189
|
+
debug "Files after decompression:"
|
190
|
+
debug `ls -la #{dir}`
|
147
191
|
end
|
148
192
|
|
149
193
|
# Mount the dmg file, copy contents to tmpdir, unmount, remove dmg
|
194
|
+
#--
|
195
|
+
# TODO change system calls with FileUtils maybe?
|
150
196
|
def extract_from_dmg
|
151
197
|
execute("mv #{file.path} #{dir}.dmg")
|
152
198
|
str = execute("hdiutil attach #{dir}.dmg 2> /dev/null", :verbose => true)
|
153
199
|
if $?.success?
|
154
200
|
info = str.split("\t")
|
155
|
-
|
201
|
+
|
202
|
+
dmg_dir = escape_whitespaces(info.last.chomp)
|
203
|
+
debug "the dmg has been mounted here: #{dmg_dir}"
|
204
|
+
|
156
205
|
device = info.first.strip.split("/").last
|
157
|
-
|
206
|
+
debug "the attached device is: #{device}"
|
207
|
+
|
208
|
+
debug "These are the files inside the dmg:"
|
209
|
+
debug `ls -la #{dmg_dir}`
|
210
|
+
|
211
|
+
execute("cp -r #{escape_whitespaces(dmg_dir)}/** #{dir}")
|
212
|
+
|
213
|
+
debug "Now these in the target"
|
214
|
+
debug `ls -la #{dir}`
|
215
|
+
|
158
216
|
execute("hdiutil detach #{device}")
|
159
217
|
execute("rm -f #{dir}.dmg")
|
160
218
|
end
|
161
219
|
end
|
162
220
|
|
221
|
+
def escape_whitespaces(path)
|
222
|
+
path.gsub(' ','\ ')
|
223
|
+
end
|
163
224
|
end
|
164
225
|
end
|
data/lib/geoffrey/plist.rb
CHANGED
@@ -10,7 +10,11 @@ module Geoffrey
|
|
10
10
|
:terminal => "#{ENV["HOME"]}/Library/Preferences/com.apple.Terminal.plist"
|
11
11
|
}
|
12
12
|
|
13
|
-
attr_accessor :data, :file, :process
|
13
|
+
attr_accessor :data, :file, :process, :changes
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@changes = {}
|
17
|
+
end
|
14
18
|
|
15
19
|
# Set the file you'll edit. if you pass a symbol then it'll use the file
|
16
20
|
# defined in the FILES hash.
|
@@ -30,7 +34,7 @@ module Geoffrey
|
|
30
34
|
end
|
31
35
|
|
32
36
|
def options
|
33
|
-
|
37
|
+
self
|
34
38
|
end
|
35
39
|
|
36
40
|
def [](key)
|
@@ -38,12 +42,12 @@ module Geoffrey
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def []=(key,value)
|
45
|
+
old_value = data[key]
|
41
46
|
data[key] = value
|
47
|
+
@changes[key] = value if old_value != value
|
42
48
|
end
|
43
49
|
|
44
50
|
# Saves the changes defined into the file.
|
45
|
-
#--
|
46
|
-
# TODO don't reopen the process if it wasn't opened to begin with
|
47
51
|
def save
|
48
52
|
kill
|
49
53
|
data.save
|
@@ -53,11 +57,21 @@ module Geoffrey
|
|
53
57
|
private
|
54
58
|
|
55
59
|
def kill
|
56
|
-
|
60
|
+
unless @process.nil? || @changes.keys.size.zero?
|
61
|
+
`ps aux | grep #{@process}`.split("\n").each do |row|
|
62
|
+
unless row.include?("grep #{@process}")
|
63
|
+
columns = row.split(" ")
|
64
|
+
@pid = columns[1].to_i
|
65
|
+
Process.kill("TERM", @pid) rescue nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
57
69
|
end
|
58
70
|
|
59
71
|
def open
|
60
|
-
|
72
|
+
unless @process.nil? || @changes.keys.size.zero? || @pid.nil?
|
73
|
+
system "open '#{@process}'"
|
74
|
+
end
|
61
75
|
end
|
62
76
|
|
63
77
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
#--
|
4
|
+
# H4X!
|
5
|
+
# Changing logger format, didn't like the other one
|
6
|
+
class Logger
|
7
|
+
def format_message(level, time, progname, msg)
|
8
|
+
">> #{msg}\n"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
# /H4X
|
12
|
+
|
13
|
+
module Geoffrey
|
14
|
+
|
15
|
+
# This module is intended to be used in real action classes so that they can
|
16
|
+
# report what they're doing. The idea is to abstract the functionality in
|
17
|
+
# order to test it better and help the future children.
|
18
|
+
module Reporter
|
19
|
+
|
20
|
+
def logger
|
21
|
+
unless @logger
|
22
|
+
@logger = Logger.new($stdout)
|
23
|
+
@logger.level = Logger::INFO
|
24
|
+
end
|
25
|
+
@logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def echo(msg)
|
29
|
+
logger.info msg
|
30
|
+
end
|
31
|
+
|
32
|
+
def debug(msg)
|
33
|
+
logger.debug msg
|
34
|
+
end
|
35
|
+
|
36
|
+
def verbose(mode)
|
37
|
+
@logger = Logger.new($stdout)
|
38
|
+
@logger.level = (mode ? Logger::DEBUG : Logger::INFO)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/geoffrey/version.rb
CHANGED
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'geoffrey/cli'
|
4
|
+
|
5
|
+
describe Geoffrey::CLI do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@app = Geoffrey::CLI.new
|
9
|
+
@app.stubs(:templates_dir).returns(File.dirname(__FILE__) + '/templates')
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'list' do
|
13
|
+
|
14
|
+
it "prints a list of templates" do
|
15
|
+
output = capturing_output { @app.list }
|
16
|
+
output.should include("simbl")
|
17
|
+
output.should include("growl")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "filters through items in the list" do
|
21
|
+
output = capturing_output { @app.list("gr") }
|
22
|
+
output.should include("growl")
|
23
|
+
output.should_not include("simbl")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "filters through items in the list ignoring case" do
|
27
|
+
output = capturing_output { @app.list("Growl") }
|
28
|
+
output.should include("growl")
|
29
|
+
output.should_not include("simbl")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'install' do
|
35
|
+
|
36
|
+
# Can't really test something's installed for real, so we just expect
|
37
|
+
# that it will call the proper methods on the Geoffrey::Package object.
|
38
|
+
it "knows what to install" do
|
39
|
+
Geoffrey::Package.any_instance.expects(:url).with("http://some.fic/tional/url.dmg").once
|
40
|
+
Geoffrey::Package.any_instance.expects(:description).with("This package should do awesome stuff").once
|
41
|
+
Geoffrey::Package.any_instance.expects(:options).with(:sudo => true).once.returns(nil)
|
42
|
+
Geoffrey::Package.any_instance.expects(:download_and_decompress).once.returns(nil)
|
43
|
+
Geoffrey::Package.any_instance.expects(:install).once
|
44
|
+
@app.install("growl")
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
#--
|
50
|
+
# This doesn't really have to be tested, right? It's not public api, we
|
51
|
+
# should test the public methods that depend on these. Or not?
|
52
|
+
context 'finding templates' do
|
53
|
+
|
54
|
+
it "knows about template files" do
|
55
|
+
@app.send(:list_of_template_names).should == ['growl','simbl']
|
56
|
+
@app.send(:list_of_templates).each do |file|
|
57
|
+
File.exists?(file).should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "knows how to find the file a template is defined it" do
|
62
|
+
@app.send(:file_for_template,'growl').should == File.dirname(__FILE__) + '/templates/growl.rb'
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'geoffrey'
|
3
|
+
|
4
|
+
describe "Geoffrey" do
|
5
|
+
|
6
|
+
context "environment" do
|
7
|
+
|
8
|
+
it "has an environment attribute" do
|
9
|
+
Geoffrey.should respond_to(:"environment=")
|
10
|
+
Geoffrey.should respond_to(:"environment")
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/spec/package_spec.rb
CHANGED
@@ -3,10 +3,18 @@ require 'geoffrey/package'
|
|
3
3
|
|
4
4
|
describe Geoffrey::Package do
|
5
5
|
|
6
|
-
it "
|
6
|
+
it "receives different options" do
|
7
|
+
[:url, :name, :description, :options].each do |opt|
|
8
|
+
package = Geoffrey::Package.new
|
9
|
+
package.send(opt, 'foo')
|
10
|
+
package.send(opt).should == 'foo'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "allows for a description to be specified" do
|
7
15
|
package = Geoffrey::Package.new
|
8
|
-
package.
|
9
|
-
package.
|
16
|
+
package.description 'foo'
|
17
|
+
package.description.should == 'foo'
|
10
18
|
end
|
11
19
|
|
12
20
|
it "allows for some options to be specified" do
|
@@ -33,19 +41,23 @@ describe Geoffrey::Package do
|
|
33
41
|
end
|
34
42
|
|
35
43
|
it "doesnt install the thing if a :unless option is provided" do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
44
|
+
capturing_output do
|
45
|
+
package = Geoffrey::Package.new
|
46
|
+
package.url("whatever")
|
47
|
+
package.options :unless => Proc.new { true }
|
48
|
+
package.download_and_decompress.should == nil
|
49
|
+
package.install.should == nil
|
50
|
+
end
|
41
51
|
end
|
42
52
|
|
43
53
|
it "inverse if passed a :if option" do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
54
|
+
capturing_output do
|
55
|
+
package = Geoffrey::Package.new
|
56
|
+
package.url("whatever")
|
57
|
+
package.options :if => Proc.new { false }
|
58
|
+
package.download_and_decompress.should == nil
|
59
|
+
package.install.should == nil
|
60
|
+
end
|
49
61
|
end
|
50
62
|
|
51
63
|
end
|
@@ -55,9 +67,11 @@ describe Geoffrey::Package do
|
|
55
67
|
context 'package downloading/uncompressing' do
|
56
68
|
|
57
69
|
it "raises an error if the file can't be found/read" do
|
58
|
-
|
59
|
-
|
60
|
-
|
70
|
+
capturing_output do
|
71
|
+
package = Geoffrey::Package.new
|
72
|
+
package.url 'really/impossible/to/exist.dmg'
|
73
|
+
lambda { package.download_and_decompress }.should raise_exception(Geoffrey::NotFoundError)
|
74
|
+
end
|
61
75
|
end
|
62
76
|
|
63
77
|
it "raises an error if the file has bad format" do
|
@@ -75,6 +89,13 @@ describe Geoffrey::Package do
|
|
75
89
|
File.read(package.file_to_install).chomp.should == "foo"
|
76
90
|
end
|
77
91
|
|
92
|
+
it "removes dirty from url's (but only from http/ftp" do
|
93
|
+
package = Geoffrey::Package.new
|
94
|
+
package.url(File.dirname(__FILE__) + "/static/test.txt?foo=bar&baz=jar")
|
95
|
+
package.send(:get_filename)
|
96
|
+
package.filename.should == "test.txt"
|
97
|
+
end
|
98
|
+
|
78
99
|
it "smartly unzips the file if it's a zip" do
|
79
100
|
package = Geoffrey::Package.new
|
80
101
|
package.url(File.dirname(__FILE__) + "/static/test.txt.zip")
|
@@ -96,9 +117,25 @@ describe Geoffrey::Package do
|
|
96
117
|
File.read(package.file_to_install).chomp.should == "foo"
|
97
118
|
end
|
98
119
|
|
120
|
+
# Testing with a dmg that has a whitespace as a name because testing dmg's
|
121
|
+
# is too slow, so we do it all in one.
|
122
|
+
#--
|
123
|
+
# TODO this test fails :( but it does work in real examples.
|
99
124
|
it "also supports .dmg files" do
|
100
125
|
package = Geoffrey::Package.new
|
101
|
-
package.url(File.dirname(__FILE__) + "/static/test.dmg")
|
126
|
+
package.url(File.dirname(__FILE__) + "/static/test 1.dmg")
|
127
|
+
package.download_and_decompress
|
128
|
+
File.read(package.file_to_install).chomp.should == "foo"
|
129
|
+
end
|
130
|
+
|
131
|
+
# test.txt.gz.123 is test.txt.gz copied and renamed. This way geoffrey
|
132
|
+
# knows what file format to use.
|
133
|
+
# TODO maybe would be better to just set the strategy to use when
|
134
|
+
# extracting
|
135
|
+
it "allows for filename to be configured" do
|
136
|
+
package = Geoffrey::Package.new
|
137
|
+
package.url(File.dirname(__FILE__) + "/static/test.txt.gz.123")
|
138
|
+
package.options :filename => 'test.txt.gz'
|
102
139
|
package.download_and_decompress
|
103
140
|
File.read(package.file_to_install).chomp.should == "foo"
|
104
141
|
end
|
data/spec/plist_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe Geoffrey::Plist do
|
|
17
17
|
it "also accepts symbol values for already known values" do
|
18
18
|
plist = Geoffrey::Plist.new
|
19
19
|
plist.file :terminal
|
20
|
-
plist.file.should include("com.apple.Terminal.plist")
|
20
|
+
plist.file.should include("com.apple.Terminal.plist")
|
21
21
|
end
|
22
22
|
|
23
23
|
it "parses correct plist items" do
|
@@ -26,18 +26,16 @@ describe Geoffrey::Plist do
|
|
26
26
|
plist.data.class.should == Plist4r::Plist
|
27
27
|
end
|
28
28
|
|
29
|
-
it "
|
29
|
+
it "doesn't restart applications if no changes where really made" do
|
30
30
|
plist = Geoffrey::Plist.new
|
31
31
|
plist.file(@plist_file.path)
|
32
32
|
plist.affects "/some/process"
|
33
|
-
|
34
|
-
# Since I don't know how to test this without starting and shutting down processes
|
35
|
-
# I'll just suppose that some internal functions are called
|
36
|
-
plist.expects(:system).twice
|
33
|
+
plist.expects(:system).never
|
37
34
|
plist.save
|
38
35
|
end
|
39
36
|
|
40
37
|
context "plist modification" do
|
38
|
+
|
41
39
|
it "allows adding new plist item" do
|
42
40
|
plist = Geoffrey::Plist.new
|
43
41
|
plist.file(@plist_file.path)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'geoffrey'
|
4
|
+
|
5
|
+
|
6
|
+
class Foo
|
7
|
+
include Geoffrey::Reporter
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Geoffrey::Reporter do
|
11
|
+
|
12
|
+
it "gives classes the ability to echo" do
|
13
|
+
foo = Foo.new
|
14
|
+
capturing_output { foo.echo "Hola" }.should include("Hola")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "gives classes the ability to print debug information" do
|
18
|
+
foo = Foo.new
|
19
|
+
capturing_output { foo.debug "useful" }.should_not include("useful")
|
20
|
+
capturing_output { foo.verbose(true); foo.debug "useful" }.should include("useful")
|
21
|
+
capturing_output { foo.verbose(false); foo.debug "useful" }.should_not include("useful")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Geoffrey::Package do
|
27
|
+
|
28
|
+
before(:each) do
|
29
|
+
@package = Geoffrey::Package.new
|
30
|
+
end
|
31
|
+
|
32
|
+
it "prints proper error when no url specified" do
|
33
|
+
capturing_output { @package.download_and_decompress }.should include("URL not specified")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "prints proper error when file can't be downloaded for some reason" do
|
37
|
+
capturing_output do
|
38
|
+
@package.url("/nothing/to/see/here.txt")
|
39
|
+
lambda { @package.download_and_decompress }.should raise_error
|
40
|
+
end.should include("Can't find file /nothing/to/see/here.txt")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "prints the description when given" do
|
44
|
+
capturing_output do
|
45
|
+
@package.url(File.dirname(__FILE__) + "/static/test.txt")
|
46
|
+
@package.description 'Something useful'
|
47
|
+
@package.download_and_decompress
|
48
|
+
end.should include("Something useful")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "prints proper message when the conditions don't apply" do
|
52
|
+
capturing_output do
|
53
|
+
@package.url(File.dirname(__FILE__) + "/static/test.txt")
|
54
|
+
@package.options :if => Proc.new { false }
|
55
|
+
@package.download_and_decompress
|
56
|
+
end.strip.should include("test.txt is already installed.")
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe Geoffrey::Plist do
|
62
|
+
|
63
|
+
it "prints proper error when file is not found or has bad format" do
|
64
|
+
pending
|
65
|
+
end
|
66
|
+
|
67
|
+
it "prints amount of changes done" do
|
68
|
+
pending
|
69
|
+
end
|
70
|
+
|
71
|
+
it "prints nothing if no values where changed" do
|
72
|
+
pending
|
73
|
+
end
|
74
|
+
|
75
|
+
it "prints information about process being restarted" do
|
76
|
+
pending
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
Bundler.setup(:default,:test)
|
3
|
-
require 'geoffrey'
|
4
1
|
require 'tempfile'
|
2
|
+
require 'stringio'
|
3
|
+
require 'geoffrey'
|
4
|
+
require 'support/helpers'
|
5
|
+
|
6
|
+
Geoffrey.environment = :test
|
5
7
|
|
6
8
|
Spec::Runner.configure do |config|
|
7
9
|
config.mock_with :mocha
|
10
|
+
config.include Helpers
|
8
11
|
end
|
File without changes
|
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Helpers
|
2
|
+
|
3
|
+
# Small helper to capture output oft hings happening inside a block
|
4
|
+
# @example
|
5
|
+
# out = capturing_output do
|
6
|
+
# puts "Hello!"
|
7
|
+
# end
|
8
|
+
# out # => "Hello\n"
|
9
|
+
def capturing_output(&block)
|
10
|
+
$stdout = StringIO.new
|
11
|
+
yield
|
12
|
+
out = $stdout.string
|
13
|
+
$stdout = STDOUT
|
14
|
+
return out
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
File without changes
|
data/templates/adium.rb
ADDED
data/templates/growl.rb
ADDED
data/templates/skype.rb
ADDED
data/templates/unrarx.rb
ADDED
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geoffrey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 19
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
7
|
+
- 1
|
8
8
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.6
|
9
|
+
version: 0.1.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Albert Llop
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-
|
17
|
+
date: 2010-09-10 00:00:00 +02:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,7 +25,6 @@ dependencies:
|
|
26
25
|
requirements:
|
27
26
|
- - ~>
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 15424063
|
30
28
|
segments:
|
31
29
|
- 1
|
32
30
|
- 0
|
@@ -44,7 +42,6 @@ dependencies:
|
|
44
42
|
requirements:
|
45
43
|
- - ~>
|
46
44
|
- !ruby/object:Gem::Version
|
47
|
-
hash: 27
|
48
45
|
segments:
|
49
46
|
- 1
|
50
47
|
- 3
|
@@ -53,72 +50,107 @@ dependencies:
|
|
53
50
|
type: :development
|
54
51
|
version_requirements: *id002
|
55
52
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
53
|
+
name: autotest
|
57
54
|
prerelease: false
|
58
55
|
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: mocha
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
59
69
|
none: false
|
60
70
|
requirements:
|
61
71
|
- - ~>
|
62
72
|
- !ruby/object:Gem::Version
|
63
|
-
hash: 43
|
64
73
|
segments:
|
65
74
|
- 0
|
66
75
|
- 9
|
67
76
|
- 8
|
68
77
|
version: 0.9.8
|
69
78
|
type: :development
|
70
|
-
version_requirements: *
|
79
|
+
version_requirements: *id004
|
71
80
|
- !ruby/object:Gem::Dependency
|
72
81
|
name: yard
|
73
82
|
prerelease: false
|
74
|
-
requirement: &
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
75
84
|
none: false
|
76
85
|
requirements:
|
77
86
|
- - ~>
|
78
87
|
- !ruby/object:Gem::Version
|
79
|
-
hash: 7
|
80
88
|
segments:
|
81
89
|
- 0
|
82
90
|
- 6
|
83
91
|
- 0
|
84
92
|
version: 0.6.0
|
85
93
|
type: :development
|
86
|
-
version_requirements: *
|
94
|
+
version_requirements: *id005
|
87
95
|
- !ruby/object:Gem::Dependency
|
88
96
|
name: bluecloth
|
89
97
|
prerelease: false
|
90
|
-
requirement: &
|
98
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
91
99
|
none: false
|
92
100
|
requirements:
|
93
101
|
- - ~>
|
94
102
|
- !ruby/object:Gem::Version
|
95
|
-
hash: 1
|
96
103
|
segments:
|
97
104
|
- 2
|
98
105
|
- 0
|
99
106
|
- 7
|
100
107
|
version: 2.0.7
|
101
108
|
type: :development
|
102
|
-
version_requirements: *
|
109
|
+
version_requirements: *id006
|
103
110
|
- !ruby/object:Gem::Dependency
|
104
111
|
name: plist4r
|
105
112
|
prerelease: false
|
106
|
-
requirement: &
|
113
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
107
114
|
none: false
|
108
115
|
requirements:
|
109
116
|
- - ">="
|
110
117
|
- !ruby/object:Gem::Version
|
111
|
-
hash: 3
|
112
118
|
segments:
|
113
119
|
- 0
|
114
120
|
version: "0"
|
115
121
|
type: :runtime
|
116
|
-
version_requirements: *
|
122
|
+
version_requirements: *id007
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: progressbar
|
125
|
+
prerelease: false
|
126
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
134
|
+
type: :runtime
|
135
|
+
version_requirements: *id008
|
136
|
+
- !ruby/object:Gem::Dependency
|
137
|
+
name: thor
|
138
|
+
prerelease: false
|
139
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
type: :runtime
|
148
|
+
version_requirements: *id009
|
117
149
|
description: Geoffrey is a little helper to aid in the installation of packages and configuration of various Mac OSX applications.
|
118
150
|
email:
|
119
151
|
- mrsimo@gmail.com
|
120
|
-
executables:
|
121
|
-
|
152
|
+
executables:
|
153
|
+
- geoffrey
|
122
154
|
extensions: []
|
123
155
|
|
124
156
|
extra_rdoc_files: []
|
@@ -129,17 +161,23 @@ files:
|
|
129
161
|
- Gemfile.lock
|
130
162
|
- README.md
|
131
163
|
- Rakefile
|
164
|
+
- bin/geoffrey
|
132
165
|
- geoffrey.gemspec
|
133
166
|
- lib/geoffrey.rb
|
167
|
+
- lib/geoffrey/cli.rb
|
134
168
|
- lib/geoffrey/package.rb
|
135
169
|
- lib/geoffrey/plist.rb
|
170
|
+
- lib/geoffrey/reporter.rb
|
136
171
|
- lib/geoffrey/version.rb
|
172
|
+
- spec/cli_spec.rb
|
173
|
+
- spec/geoffrey_spec.rb
|
137
174
|
- spec/package_spec.rb
|
138
175
|
- spec/plist_spec.rb
|
176
|
+
- spec/reporter_spec.rb
|
139
177
|
- spec/spec.opts
|
140
178
|
- spec/spec_helper.rb
|
141
179
|
- spec/static/example.plist
|
142
|
-
- spec/static/test.dmg
|
180
|
+
- spec/static/test 1.dmg
|
143
181
|
- spec/static/test.error.dmg
|
144
182
|
- spec/static/test.error.gz
|
145
183
|
- spec/static/test.error.tar.gz
|
@@ -148,7 +186,15 @@ files:
|
|
148
186
|
- spec/static/test.tgz
|
149
187
|
- spec/static/test.txt
|
150
188
|
- spec/static/test.txt.gz
|
189
|
+
- spec/static/test.txt.gz.123
|
151
190
|
- spec/static/test.txt.zip
|
191
|
+
- spec/support/helpers.rb
|
192
|
+
- spec/templates/growl.rb
|
193
|
+
- spec/templates/simbl.rb
|
194
|
+
- templates/adium.rb
|
195
|
+
- templates/growl.rb
|
196
|
+
- templates/skype.rb
|
197
|
+
- templates/unrarx.rb
|
152
198
|
has_rdoc: true
|
153
199
|
homepage:
|
154
200
|
licenses: []
|
@@ -163,7 +209,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
209
|
requirements:
|
164
210
|
- - ">="
|
165
211
|
- !ruby/object:Gem::Version
|
166
|
-
hash: 3
|
167
212
|
segments:
|
168
213
|
- 0
|
169
214
|
version: "0"
|
@@ -172,7 +217,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
217
|
requirements:
|
173
218
|
- - ">="
|
174
219
|
- !ruby/object:Gem::Version
|
175
|
-
hash: 23
|
176
220
|
segments:
|
177
221
|
- 1
|
178
222
|
- 3
|