roasted 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 ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in roasted.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Mikko Kokkonen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Roasted
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'roasted'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install roasted
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/roasted ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'roasted/cli'
6
+ Roasted::CLI.start
@@ -0,0 +1,18 @@
1
+ require 'thor'
2
+ require 'roasted/roaster'
3
+
4
+ module Roasted
5
+ class CLI < Thor
6
+ default_task :bootstrap
7
+
8
+ desc "bootstrap", "Bootstraps current workstation with given roasted description"
9
+ method_option :config, :type => :string, :default => "roastedrc"
10
+ def bootstrap()
11
+ say "Starting bootstrapping..."
12
+ raise "Cannot find roastedrc" unless File.exists?(options[:config])
13
+
14
+ # Load config file
15
+ Roaster.new(options)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ source "http://www.appzapper.com/downloads/appzapper.dmg"
2
+ checksum "7d32f5a8f98d379ababe56f8444277b08666bc36"
3
+ accept_eula
4
+ license :preferences, :key => "Registration Code", :name => "Registration Name"
5
+ defaults "com.appzapper.AppZapper"
@@ -0,0 +1,2 @@
1
+ source "https://ddr3luum8vl5r.cloudfront.net/Dropbox%201.4.7.dmg"
2
+ checksum "1d4e149fc60b3d83c078a99675b3826321403b56"
@@ -0,0 +1,2 @@
1
+ source "http://bpxl-kaleidoscope.s3.amazonaws.com/Kaleidoscope-1.1.6.zip"
2
+ checksum "e3335724f5d80df07eb3bb84477e66be1af351bc"
@@ -0,0 +1,3 @@
1
+ source "http://appldnld.apple.com/MessagesBeta/041-5086.20120216.z5km/MessagesBeta.dmg"
2
+ checksum "59d475b429b51c2bf735d98bd18ac2477c7aac52"
3
+ installs "iChat", :version => "6.1"
@@ -0,0 +1,3 @@
1
+ # Skype
2
+ source "http://download.skype.com/macosx/Skype_5.7.0.1130.dmg"
3
+ checksum "e6ec3071e2c177bb68fcd0ed1a4d2ed63af1ef6e"
@@ -0,0 +1,72 @@
1
+ # Textmate 1
2
+ # app "TextMate", :license => { :key => 'XXX', :name => 'XXX' } do
3
+ # plugin "url_to_plugin"
4
+ # bundle "url_to_bundle"
5
+ # theme "url_to_theme"
6
+ # end
7
+ source "http://dl-origin.macromates.com/TextMate_1.5.10_r1631.zip"
8
+ checksum "4f3f167750b14d6dd1be4e4852f60581a617a47d"
9
+ license :preferences, :key => "OakRegistrationLicenseKey", :name => "OakRegistrationOwner"
10
+ defaults "com.macromates.textmate"
11
+
12
+ after_install do
13
+ FileUtils.rm "/usr/local/bin/mate" if File.exists?("/usr/local/bin/mate")
14
+ FileUtils.ln_s "/Applications/TextMate.app/Contents/SharedSupport/Support/bin/mate", "/usr/local/bin/mate"
15
+ end
16
+
17
+ def plugin(source, options = {})
18
+ download source, "~/Library/Application Support/TextMate/PlugIns", options
19
+ end
20
+
21
+ def bundle(source, options = {})
22
+ download source, "~/Library/Application Support/TextMate/Bundles", options
23
+ end
24
+
25
+ def theme(source, options = {})
26
+ download source, "~/Library/Application Support/TextMate/Themes", options
27
+ end
28
+
29
+ def download(source, target, options = {})
30
+ type = case source
31
+ when /^http/
32
+ :http
33
+ when /^git/
34
+ :git
35
+ end
36
+ self.send("download_#{type}", source, target, options)
37
+ end
38
+
39
+ def download_http(source, target, options = {})
40
+ target = File.expand_path(target)
41
+
42
+ # Prepare target directory
43
+ FileUtils.mkdir_p target
44
+
45
+ tmp = Tempfile.new File.basename(source)
46
+ system "curl -o '#{tmp.path}' '#{source}'"
47
+
48
+ if options[:checksum]
49
+ checksum = Digest::SHA1.hexdigest(File.read(tmp.path))
50
+ raise "Checksum mismatch, expected #{options[:checksum]}" if checksum != options[:checksum]
51
+ end
52
+
53
+ case File.extname(source)[1..-1]
54
+ when "zip"
55
+ system "ditto -x -k '#{tmp.path}' '#{target}'"
56
+ else
57
+ raise "Unknown download type: #{source}"
58
+ end
59
+ end
60
+
61
+ def download_git(source, target, options = {})
62
+ target = File.expand_path(target)
63
+
64
+ # Prepare target directory
65
+ FileUtils.mkdir_p target
66
+
67
+ options[:name] ||= File.basename(source).gsub(".git", "")
68
+
69
+ Dir.chdir target do
70
+ system "git clone '#{source}' '#{options[:name]}'"
71
+ end
72
+ end
@@ -0,0 +1,2 @@
1
+ source "https://macapps.fournova.com/tower1-1060/download", :type => "zip"
2
+ checksum "30ddeeb1d4a664a9ff44daeb9ec4dd9459375be7"
@@ -0,0 +1,7 @@
1
+ # Transmit
2
+ # app "Transmit", :license => { :key => 'XXXX' }
3
+
4
+ source "http://www.panic.com/transmit/d/Transmit%204.1.9.zip"
5
+ checksum "a0dca3bba7c66a30b380abcd3383f0ed403620bb"
6
+ license :preferences, :key => "SerialNumber2"
7
+ defaults "com.panic.Transmit"
@@ -0,0 +1,3 @@
1
+ # Versions installation
2
+ source "http://bpxl-versions.s3.amazonaws.com/Versions-1.1.10.zip"
3
+ checksum "e07e6a1dd18579eedc1d5015ba2bc057370116d3"
@@ -0,0 +1,239 @@
1
+ require 'tempfile'
2
+ require 'plist'
3
+ require 'digest'
4
+
5
+ module Roasted
6
+ class Provider
7
+ class App
8
+ attr_accessor :app
9
+ attr_accessor :action
10
+ attr_accessor :options
11
+
12
+ attr_accessor :source
13
+ attr_accessor :type
14
+ attr_accessor :checksum
15
+ attr_accessor :accept_eula
16
+ attr_accessor :installs
17
+ attr_accessor :license
18
+ attr_accessor :domain
19
+ attr_accessor :hooks
20
+
21
+ def initialize(action, app, options)
22
+ @app = app
23
+ @action = action
24
+ @options = options
25
+ @installs = { :app => @app }
26
+ @hooks = {}
27
+
28
+ path = File.expand_path(File.join(File.dirname(__FILE__), "app", "#{app.downcase}.rb"))
29
+ @parser = Parser.new(self)
30
+ @parser.instance_eval(File.read(path), path)
31
+ end
32
+
33
+ def self.create(options)
34
+ action = options.delete(:action)
35
+ app = options.delete(:app)
36
+
37
+ # Replace ourself with custom class
38
+ path = File.expand_path(File.join(File.dirname(__FILE__), "app", "#{app.downcase}.rb"))
39
+ raise "Cannot find app provider #{app}" unless File.exists?(path)
40
+
41
+ new(action, app, options)
42
+ end
43
+
44
+ # Actions, at this point only install
45
+ def install
46
+ if File.exists?("/Applications/#{@installs[:app]}.app")
47
+ if @installs[:version]
48
+ # Check version
49
+ info = Plist.parse_xml(File.read("/Applications/#{@installs[:app]}.app/Contents/Info.plist"))
50
+ return if info["CFBundleShortVersionString"] >= @installs[:version]
51
+ else
52
+ # No version requested, installs app exists, skipping installation
53
+ return
54
+ end
55
+ end
56
+
57
+ puts "Installing #{@app}"
58
+
59
+ self.send("install_#{self.type}")
60
+
61
+ # Handle license
62
+ if @options[:license] and @license
63
+ puts "Setting up license"
64
+ if @license[:block]
65
+ @license[:block].call(@options[:license])
66
+ else
67
+ self.send("license_#{@license[:type]}", @options[:license])
68
+ end
69
+ end
70
+
71
+ # Handle custom functionality
72
+ if @options[:block]
73
+ @parser.instance_eval &@options[:block]
74
+ end
75
+
76
+ # Hooks
77
+ if @hooks[:after_install]
78
+ @hooks[:after_install].call
79
+ end
80
+ end
81
+
82
+ def install_zip
83
+ tmp = download_source
84
+
85
+ # Simple and easy, just extract zip
86
+ system "ditto -x -k '#{tmp.path}' '/Applications/'"
87
+
88
+ # Cleanup
89
+ tmp.unlink
90
+ end
91
+
92
+ def install_tbz
93
+ tmp = download_source
94
+
95
+ # Simple and easy, just extract bzip tar
96
+ system "tar -C '/Applications' -jxf '#{tmp.path}'"
97
+
98
+ # Cleanup
99
+ tmp.unlink
100
+ end
101
+
102
+ def install_dmg
103
+ tmp = download_source
104
+
105
+ # Attach it
106
+ needs_eula = system("hdiutil imageinfo #{tmp.path} | grep -q 'Software License Agreement: true'")
107
+ raise "Requires EULA Acceptance; add 'accept_eula' to application resource" if needs_eula and !@accept_eula
108
+ accept_eula_cmd = @accept_eula ? "yes |" : ""
109
+ # require 'pry';binding.pry if needs_eula
110
+ system "#{accept_eula_cmd} hdiutil attach '#{tmp.path}' > /dev/null"
111
+
112
+ # Get volume path
113
+ hdi = Plist.parse_xml(`hdiutil info -plist`)
114
+ image = hdi["images"].select{|i| i["image-path"] == tmp.path}.first
115
+ mntinfo = image["system-entities"].select{|i| i.has_key?("mount-point")}.first
116
+ mount_point = mntinfo["mount-point"]
117
+ disk = mntinfo["dev-entry"]
118
+
119
+ # Find app
120
+ Dir["#{mount_point}/*#{@app}*.{app,pkg,mpkg}"].each do |entry|
121
+ type = @apptype || File.extname(entry)[1..-1].to_sym
122
+ case type
123
+ when :app
124
+ system "rsync -aH '#{entry}' '/Applications/'"
125
+ when :pkg
126
+ when :mpkg
127
+ system "sudo installer -pkg '#{entry}' -target /"
128
+ else
129
+ puts "Don't know how to handle entry #{entry}, type #{type}!"
130
+ end
131
+ end
132
+
133
+ # Detach
134
+ system("hdiutil detach -quiet '#{mount_point}'")
135
+
136
+ # Remove image
137
+ tmp.unlink
138
+ end
139
+
140
+ def license_preferences(license)
141
+ domain_exists = system("defaults domains | grep #{@domain} >/dev/null")
142
+
143
+ if domain_exists
144
+ @license[:options].each do |key, name|
145
+ system "defaults write #{@domain} '#{name}' '#{license[key]}'"
146
+ end
147
+ else
148
+ plist = @license[:options].collect {|key, name| "\"#{name}\" = \"#{license[key]}\";"}.join(" ")
149
+ system "defaults write #{@domain} '{#{plist}}'"
150
+ end
151
+ end
152
+
153
+ def type
154
+ @type ||= case self.source
155
+ when /zip$/
156
+ :zip
157
+ when /tbz$/
158
+ :tbz
159
+ when /dmg$/
160
+ :dmg
161
+ end
162
+ end
163
+
164
+ def to_s
165
+ "#{self.class}: #{self.options.inspect}"
166
+ end
167
+
168
+ private
169
+ def download_source
170
+ # Create temporary path for image
171
+ tmp = Tempfile.new(@app)
172
+
173
+ # Download image
174
+ system("curl -o #{tmp.path} '#{@source}'")
175
+
176
+ # Calculate checksum
177
+ checksum = Digest::SHA1.hexdigest(File.read(tmp.path))
178
+ if @checksum != checksum
179
+ puts "Checksum mismatch"
180
+ puts "Source checksum: #{checksum}"
181
+ puts " Expected: #{@checksum}"
182
+ raise "Checksum mismatch"
183
+ end
184
+
185
+ return tmp
186
+ end
187
+
188
+ class Parser
189
+ def initialize(app)
190
+ @app = app
191
+ end
192
+
193
+ # DSL actions
194
+ def source(source, options = {})
195
+ # version = options[:version] || :default
196
+ @app.source = source
197
+ @app.type = options.delete(:type)
198
+ end
199
+
200
+ def checksum(checksum)
201
+ @app.checksum = checksum
202
+ end
203
+
204
+ def accept_eula
205
+ @app.accept_eula = true
206
+ end
207
+
208
+ def installs(appname, options = {})
209
+ @app.installs = options.merge(:app => appname)
210
+ end
211
+
212
+ def license(type, options = {}, &block)
213
+ @app.license = {:type => type, :options => options, :block => block}
214
+ end
215
+
216
+ def defaults(domain)
217
+ @app.domain = domain
218
+ end
219
+
220
+ def after_install(&block)
221
+ @app.hooks[:after_install] = block
222
+ end
223
+
224
+ def preferences(prefs)
225
+ domain_exists = system("defaults domains | grep #{@app.domain} >/dev/null")
226
+
227
+ if domain_exists
228
+ prefs.each do |key, value|
229
+ system "defaults write #{@app.domain} '#{key}' '#{value}'"
230
+ end
231
+ else
232
+ plist = prefs.collect {|key, value| "\"#{key}\" = \"#{value}\";"}.join(" ")
233
+ system "defaults write #{@app.domain} '{#{plist}}'"
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,33 @@
1
+ module Roasted
2
+ class Provider
3
+ class Brew
4
+ attr_accessor :formula
5
+ attr_accessor :action
6
+ attr_accessor :options
7
+
8
+ def self.create(options)
9
+ return new(options)
10
+ end
11
+
12
+ def initialize(options)
13
+ @action = options.delete(:action)
14
+ @formula = options.delete(:formula)
15
+
16
+ @options = options
17
+ end
18
+
19
+ def install
20
+ # Simple, check if we have brew installed
21
+ install_brew unless File.executable?("/usr/local/bin/brew")
22
+
23
+ unless File.directory?("/usr/local/Cellar/#{@formula}")
24
+ system("/usr/local/bin/brew install #{@formula}")
25
+ end
26
+ end
27
+
28
+ def install_brew
29
+ system '/usr/bin/ruby -e "$(/usr/bin/curl -fsSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)"'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'roasted/provider/brew'
2
+ require 'roasted/provider/app'
3
+
4
+ module Roasted
5
+ class Roaster
6
+ attr_reader :options
7
+
8
+ def initialize(options)
9
+ @options = options
10
+
11
+ # Load DSL
12
+ parser = Parser.new
13
+ parser.instance_eval(File.read(options[:config]), options[:config])
14
+
15
+ parser.runlist.each do |run|
16
+ # Only action at this point, install
17
+ run.install
18
+ end
19
+ end
20
+
21
+ class Parser
22
+ attr_reader :runlist
23
+
24
+ def initialize
25
+ @runlist = []
26
+ end
27
+
28
+ def brew(formula)
29
+ @runlist << ::Roasted::Provider::Brew.create(:formula => formula)
30
+ end
31
+
32
+ def app(appname, options = {}, &block)
33
+ @runlist << ::Roasted::Provider::App.create(options.merge({:app => appname, :block => block}))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Roasted
2
+ VERSION = "0.1.0"
3
+ end
data/lib/roasted.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "roasted/version"
2
+
3
+ module Roasted
4
+ # Your code goes here...
5
+ end
data/roasted.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/roasted/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Mikko Kokkonen"]
6
+ gem.email = ["mikko@owlforestry.com"]
7
+ gem.description = %q{
8
+ Roasted is a simple bootstrapping library to install
9
+ all necessary applications and requirements to freshly
10
+ installed OS X. Roasted install not only applications
11
+ but also preferences and licenses if possible.}
12
+ gem.summary = %q{Simple application installation and configuration library for OS X}
13
+ gem.homepage = "http://www.github.com/owlforestry/roasted"
14
+
15
+ gem.files = `git ls-files`.split($\)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.name = "roasted"
19
+ gem.require_paths = ["lib"]
20
+ gem.version = Roasted::VERSION
21
+
22
+ gem.add_dependency 'thor'
23
+ gem.add_dependency 'plist'
24
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roasted
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mikko Kokkonen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: plist
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ! "\n Roasted is a simple bootstrapping library to install\n all
47
+ necessary applications and requirements to freshly\n installed OS X. Roasted
48
+ install not only applications\n but also preferences and licenses if possible."
49
+ email:
50
+ - mikko@owlforestry.com
51
+ executables:
52
+ - roasted
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - Gemfile
58
+ - LICENSE
59
+ - README.md
60
+ - Rakefile
61
+ - bin/roasted
62
+ - lib/roasted.rb
63
+ - lib/roasted/cli.rb
64
+ - lib/roasted/provider/app.rb
65
+ - lib/roasted/provider/app/appzapper.rb
66
+ - lib/roasted/provider/app/dropbox.rb
67
+ - lib/roasted/provider/app/kaleidoscope.rb
68
+ - lib/roasted/provider/app/messages.rb
69
+ - lib/roasted/provider/app/skype.rb
70
+ - lib/roasted/provider/app/textmate.rb
71
+ - lib/roasted/provider/app/tower.rb
72
+ - lib/roasted/provider/app/transmit.rb
73
+ - lib/roasted/provider/app/versions.rb
74
+ - lib/roasted/provider/brew.rb
75
+ - lib/roasted/roaster.rb
76
+ - lib/roasted/version.rb
77
+ - roasted.gemspec
78
+ homepage: http://www.github.com/owlforestry/roasted
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.23
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Simple application installation and configuration library for OS X
102
+ test_files: []