choctop 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,15 @@ Given /is configured for custom Applications icon$/ do
8
8
  end
9
9
  end
10
10
 
11
- When /^dmg '(.*)' is mounted as '(.*)'$/ do |dmg, name|
11
+ Given /^is configured for an asset file "([^\"]*)" to be included in dmg$/ do |file|
12
+ in_project_folder do
13
+ append_to_file "Rakefile", <<-RUBY.gsub(/^ /, '')
14
+ $sparkle.add_file "#{file}", :position=> [347, 65]
15
+ RUBY
16
+ end
17
+ end
18
+
19
+ When /^dmg "(.*)" is mounted as "(.*)"$/ do |dmg, name|
12
20
  @stdout = File.expand_path(File.join(@tmp_root, "hdiutil.out"))
13
21
  in_project_folder do
14
22
  @mountpoint = ChocTop.new.mountpoint
@@ -22,19 +30,19 @@ def in_mounted_volume(&block)
22
30
  FileUtils.chdir(@volume_path, &block)
23
31
  end
24
32
 
25
- Then %r{^folder '(.*)' in mounted volume (is|is not) created} do |folder, is|
33
+ Then %r{^folder "(.*)" in mounted volume (is|is not) created} do |folder, is|
26
34
  in_mounted_volume do
27
35
  File.exists?(folder).should(is == 'is' ? be_true : be_false)
28
36
  end
29
37
  end
30
38
 
31
- Then %r{^file '(.*)' in mounted volume (is|is not) created} do |file, is|
39
+ Then %r{^file "(.*)" in mounted volume (is|is not) created} do |file, is|
32
40
  in_mounted_volume do
33
41
  File.exists?(file).should(is == 'is' ? be_true : be_false)
34
42
  end
35
43
  end
36
44
 
37
- Then /^file '(.*)' in mounted volume (is|is not) invisible$/ do |file, is|
45
+ Then /^file "(.*)" in mounted volume (is|is not) invisible$/ do |file, is|
38
46
  in_mounted_volume do
39
47
  `GetFileInfo -aV '#{@volume_path}/#{file}'`.to_i.should_not == (is == 'is' ? 0 : 1)
40
48
  end
@@ -1,4 +1,4 @@
1
- Then /^file '(.*)' in mounted volume has GetFileInfo (.*) '(.*)'/ do |file, file_info_type, value|
1
+ Then /^file "(.*)" in mounted volume has GetFileInfo (.*) "(.*)"/ do |file, file_info_type, value|
2
2
  flags = case file_info_type.to_sym
3
3
  when :type; "-t"
4
4
  when :alias; "-aa"
@@ -9,7 +9,7 @@ Then /^file '(.*)' in mounted volume has GetFileInfo (.*) '(.*)'/ do |file, file
9
9
  end
10
10
  end
11
11
 
12
- Then /^file '(.*)' in mounted volume is aliased to '(.*)'/ do |file, target|
12
+ Then /^file "(.*)" in mounted volume is aliased to "(.*)"/ do |file, target|
13
13
  in_mounted_volume do
14
14
  puts "TODO - how to get applescript to test this?"
15
15
  end
@@ -24,7 +24,7 @@ end
24
24
  Given /Rakefile constants rewired for local rsync/ do
25
25
  end
26
26
 
27
- Given /^a Cocoa app with choctop installed called '(.*)'$/ do |name|
27
+ Given /^a Cocoa app with choctop installed called "(.*)"$/ do |name|
28
28
  Given "a safe folder"
29
29
  @remote_folder = File.expand_path(File.join(@tmp_root, 'website'))
30
30
  FileUtils.rm_rf @remote_folder
@@ -33,7 +33,7 @@ Given /^a Cocoa app with choctop installed called '(.*)'$/ do |name|
33
33
  `cp -r '#{app_path}' #{@tmp_root}/ 2> /dev/null`
34
34
  `rm -rf '#{@tmp_root}/#{name}/build'`
35
35
  setup_active_project_folder name
36
- Given "I run local executable 'install_choctop' with arguments '.'"
36
+ Given %Q{I run local executable "install_choctop" with arguments "."}
37
37
  Given "Rakefile wired to use development code instead of installed RubyGem"
38
38
  Given "Rakefile constants rewired for local rsync"
39
39
  ENV['NO_FINDER'] = 'YES' # disable Finder during tests
@@ -1,10 +1,10 @@
1
- Then %r{^remote folder '(.*)' is created} do |folder|
1
+ Then %r{^remote folder "(.*)" is created} do |folder|
2
2
  FileUtils.chdir @remote_folder do
3
3
  File.exists?(folder).should be_true
4
4
  end
5
5
  end
6
6
 
7
- Then %r{^remote file '(.*)' (is|is not) created} do |file, is|
7
+ Then %r{^remote file "(.*)" (is|is not) created} do |file, is|
8
8
  FileUtils.chdir @remote_folder do
9
9
  File.exists?(file).should(is == 'is' ? be_true : be_false)
10
10
  end
@@ -1,4 +1,4 @@
1
- Then /^current xcode project version is '(.*)'$/ do |version|
1
+ Then /^current xcode project version is "(.*)"$/ do |version|
2
2
  in_project_folder do
3
3
  ChocTop.new.version.to_s.should == version
4
4
  end
@@ -0,0 +1,44 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_home_folder(&block)
12
+ FileUtils.chdir(@home_path, &block)
13
+ end
14
+
15
+ def force_local_lib_override(project_name = @project_name)
16
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
17
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
18
+ f << "$:.unshift('#{@lib_path}')\n"
19
+ f << rakefile
20
+ end
21
+ end
22
+
23
+ def setup_active_project_folder project_name
24
+ @active_project_folder = File.join(@tmp_root, project_name)
25
+ FileUtils.mkdir_p(@active_project_folder)
26
+ @project_name = project_name
27
+ end
28
+
29
+ def prepend_to_file(filename, text)
30
+ file = File.read(filename)
31
+ File.open(filename, "w+") do |f|
32
+ f << text + "\n"
33
+ f << file
34
+ end
35
+ end
36
+
37
+ def append_to_file(filename, text)
38
+ File.open(filename, "a") do |f|
39
+ f << text + "\n"
40
+ end
41
+ end
42
+ end
43
+
44
+ World(CommonHelpers)
File without changes
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
@@ -5,12 +5,17 @@ require "fileutils"
5
5
  require "yaml"
6
6
  require "builder"
7
7
  require "erb"
8
+ require "uri"
8
9
  require "osx/cocoa"
9
10
  require "active_support"
10
11
  require "RedCloth"
11
12
 
12
13
  class ChocTop
13
- VERSION = '0.9.6'
14
+ VERSION = '0.10.0'
15
+
16
+ # Path to the Info.plist
17
+ # Default: "Info.plist"
18
+ attr_accessor :info_plist_path
14
19
 
15
20
  # The name of the Cocoa application
16
21
  # Default: info_plist['CFBundleExecutable'] or project folder name if "${EXECUTABLE_NAME}"
@@ -23,20 +28,29 @@ class ChocTop
23
28
  # The target name of the distributed DMG file
24
29
  # Default: #{name}.app
25
30
  attr_accessor :target
31
+
32
+ # The build type of the distributed DMG file
33
+ # Default: Release
34
+ attr_accessor :build_type
35
+
36
+ # The Sparkle feed URL
37
+ # Default: info_plist['SUFeedURL']
38
+ attr_accessor :su_feed_url
26
39
 
27
40
  # The host name, e.g. some-domain.com
41
+ # Default: host from base_url
28
42
  attr_accessor :host
29
43
 
30
44
  # The url from where the xml + dmg files will be downloaded
31
- # Default: http://#{host}
32
- attr_writer :base_url
33
- def base_url
34
- @base_url ||= "http://#{host}"
35
- end
45
+ # Default: dir path from appcast_filename
46
+ attr_accessor :base_url
36
47
 
37
48
  # The file name for generated release notes for the latest release
38
49
  # Default: release_notes.html
39
50
  attr_accessor :release_notes
51
+
52
+ # List of files/bundles to be packaged into the DMG
53
+ attr_accessor :files
40
54
 
41
55
  # The path for an HTML template into which the release_notes.txt are inserted
42
56
  # after conversion to HTML
@@ -60,6 +74,13 @@ class ChocTop
60
74
  # Default: -aCv
61
75
  attr_accessor :rsync_args
62
76
 
77
+ # Folder from where all files will be copied into the DMG
78
+ # Files are copied here if specified with +add_file+ before DMG creation
79
+ attr_accessor :src_folder
80
+ def src_folder
81
+ @src_folder ||= "build/#{build_type}/dmg"
82
+ end
83
+
63
84
  # Generated filename for a distribution, from name, version and .dmg
64
85
  # e.g. MyApp-1.0.0.dmg
65
86
  def pkg_name
@@ -136,41 +157,61 @@ class ChocTop
136
157
  end
137
158
 
138
159
  def info_plist
139
- @info_plist ||= OSX::NSDictionary.dictionaryWithContentsOfFile(File.expand_path('Info.plist')) || {}
160
+ @info_plist ||= OSX::NSDictionary.dictionaryWithContentsOfFile(File.expand_path(info_plist_path)) || {}
161
+ end
162
+
163
+ # Add an explicit file/bundle/folder into the DMG
164
+ # Required option:
165
+ # +:position+ - two item array [x, y] window position
166
+ def add_file(path, options)
167
+ throw "add_files #{path}, :position => [x,y] option is missing" unless options[:position]
168
+ self.files ||= {}
169
+ files[path] = options
140
170
  end
141
171
 
142
172
  def initialize
143
173
  $sparkle = self # define a global variable for this object
144
174
 
175
+ yield self if block_given?
176
+
145
177
  # Defaults
146
- @name = info_plist['CFBundleExecutable']
147
- @name = File.basename(File.expand_path(".")) if name == "${EXECUTABLE_NAME}"
148
- @version = info_plist['CFBundleVersion']
149
- @target = "#{name}.app"
150
- @appcast_filename = info_plist['SUFeedURL'] ? File.basename(info_plist['SUFeedURL']) : 'linker_appcast.xml'
151
- @release_notes = 'release_notes.html'
152
- @release_notes_template = "release_notes_template.html.erb"
153
- @rsync_args = '-aCv --progress'
178
+ @info_plist_path ||= 'Info.plist'
179
+ @name ||= info_plist['CFBundleExecutable']
180
+ @name = File.basename(File.expand_path(".")) if name.to_s == "${EXECUTABLE_NAME}"
181
+ @version ||= info_plist['CFBundleVersion']
182
+ @target ||= "#{name}.app"
183
+ @build_type = ENV['BUILD_TYPE'] || 'Release'
154
184
 
155
- @background_file = File.dirname(__FILE__) + "/../assets/sky_background.jpg"
156
- @app_icon_position = [175, 65]
157
- @applications_icon_position = [347, 270]
158
- @volume_icon = File.dirname(__FILE__) + "/../assets/DefaultVolumeIcon.icns"
159
- @icon_size = 104
160
- @icon_text_size = 12
185
+ if @su_feed_url = info_plist['SUFeedURL']
186
+ @appcast_filename ||= File.basename(su_feed_url)
187
+ @base_url ||= File.dirname(su_feed_url)
188
+ end
189
+ if @base_url
190
+ @host ||= URI.parse(base_url).host
191
+ end
192
+ @release_notes ||= 'release_notes.html'
193
+ @release_notes_template ||= "release_notes_template.html.erb"
194
+ @rsync_args ||= '-aCv --progress'
161
195
 
162
- yield self if block_given?
196
+ @background_file ||= File.dirname(__FILE__) + "/../assets/sky_background.jpg"
197
+ @app_icon_position ||= [175, 65]
198
+ @applications_icon_position ||= [347, 270]
199
+ @volume_icon ||= File.dirname(__FILE__) + "/../assets/DefaultVolumeIcon.icns"
200
+ @icon_size ||= 104
201
+ @icon_text_size ||= 12
163
202
 
203
+ add_file "build/#{build_type}/#{target}", :position => app_icon_position
204
+
164
205
  define_tasks
165
206
  end
166
207
 
167
208
  def define_tasks
168
209
  return unless Object.const_defined?("Rake")
169
210
 
170
- desc "Build Xcode Release"
171
- task :build => "build/Release/#{target}/Contents/Info.plist"
211
+ desc "Build Xcode #{build_type}"
212
+ task :build => "build/#{build_type}/#{target}/Contents/Info.plist"
172
213
 
173
- task "build/Release/#{target}/Contents/Info.plist" do
214
+ task "build/#{build_type}/#{target}/Contents/Info.plist" do
174
215
  make_build
175
216
  end
176
217
 
@@ -195,9 +236,6 @@ class ChocTop
195
236
  upload_appcast
196
237
  end
197
238
 
198
- desc "Create dmg, update appcast file, and upload to host"
199
- task :appcast => %w[force_build dmg force_feed upload]
200
-
201
239
  task :detach_dmg do
202
240
  detach_dmg
203
241
  end
@@ -3,7 +3,7 @@ module ChocTop::Appcast
3
3
  if ENV['NO_BUILD']
4
4
  puts "Skipping build task..."
5
5
  else
6
- sh "xcodebuild -configuration Release"
6
+ sh "xcodebuild -configuration #{build_type}"
7
7
  end
8
8
  end
9
9
 
@@ -1,9 +1,18 @@
1
1
  module ChocTop::Dmg
2
+ def prepare_files
3
+ FileUtils.mkdir_p(src_folder)
4
+ files.each do |file|
5
+ path, options = file
6
+ FileUtils.cp_r(path, src_folder)
7
+ end
8
+ end
9
+
2
10
  def make_dmg
11
+ prepare_files
3
12
  FileUtils.rm_rf build_path
4
13
  FileUtils.mkdir_p build_path
5
14
  FileUtils.mkdir_p mountpoint # TODO can we remove random mountpoints?
6
- sh "hdiutil create -format UDRW -quiet -volname '#{name}' -srcfolder 'build/Release/#{target}' '#{pkg}'"
15
+ sh "hdiutil create -format UDRW -quiet -volname '#{name}' -srcfolder '#{src_folder}' '#{pkg}'"
7
16
  sh "hdiutil attach '#{pkg}' -mountpoint '#{volume_path}' -noautoopen -quiet"
8
17
  sh "bless --folder '#{volume_path}' --openfolder '#{volume_path}'"
9
18
  sh "sleep 1"
@@ -47,7 +56,7 @@ module ChocTop::Dmg
47
56
  FileUtils.mkdir_p(File.dirname(target_background))
48
57
  FileUtils.cp(background_file, target_background)
49
58
  end
50
- run_applescript <<-SCRIPT.gsub(/^ /, '')
59
+ script = <<-SCRIPT.gsub(/^ /, '')
51
60
  tell application "Finder"
52
61
  set mountpoint to POSIX file ("#{volume_path}" as string) as alias
53
62
  tell folder mountpoint
@@ -63,7 +72,7 @@ module ChocTop::Dmg
63
72
  set icon size of the icon view options of container window to #{icon_size}
64
73
  set text size of the icon view options of container window to #{icon_text_size}
65
74
  set arrangement of the icon view options of container window to not arranged
66
- set position of item "#{target}" to {#{app_icon_position.join(", ")}}
75
+ #{set_position_of_files}
67
76
  set position of item "Applications" to {#{applications_icon_position.join(", ")}}
68
77
  set the bounds of the container window to {#{window_bounds.join(", ")}}
69
78
  set background picture of the icon view options of container window to file "#{volume_background.gsub(/\//,':')}"
@@ -75,9 +84,19 @@ module ChocTop::Dmg
75
84
  delay 5
76
85
  end tell
77
86
  SCRIPT
87
+ run_applescript(script)
78
88
  sh "SetFile -a V '#{target_background}'" if background_file
79
89
  end
80
90
 
91
+ def set_position_of_files
92
+ files.map do |file_options|
93
+ path, options = file_options
94
+ target = File.basename(path)
95
+ position = options[:position].join(", ")
96
+ %Q{set position of item "#{target}" to {#{position}}}
97
+ end.join("\n")
98
+ end
99
+
81
100
  def configure_applications_icon
82
101
  run_applescript <<-SCRIPT.gsub(/^ /, ''), "apps_icon_script"
83
102
  tell application "Finder"
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Time to add your specs!
4
+ # http://rspec.info/
5
+ describe ChocTop do
6
+ attr_reader :choctop
7
+
8
+ describe "default" do
9
+ before(:each) do
10
+ FileUtils.chdir(File.dirname(__FILE__) + "/../features/fixtures/SampleApp") do
11
+ @choctop = ChocTop.new
12
+ end
13
+ end
14
+
15
+ it "should get name from Info.plist['CFBundleExecutable']" do
16
+ choctop.name.should == 'SampleApp'
17
+ end
18
+
19
+ it "should get version from Info.plist['CFBundleVersion']" do
20
+ choctop.version.should == '0.1.0'
21
+ end
22
+
23
+ it "should derive host from Info.plist['SUFeedURL']" do
24
+ choctop.host.should == 'mocra.com'
25
+ end
26
+
27
+ it "should derive base_url from Info.plist['SUFeedURL']" do
28
+ choctop.base_url.should == 'http://mocra.com/sample_app'
29
+ end
30
+
31
+ it "should derive appcast_filename from Info.plist['SUFeedURL']" do
32
+ choctop.appcast_filename.should == 'my_feed.xml'
33
+ end
34
+ end
35
+
36
+ describe "add_files" do
37
+ before(:each) do
38
+ FileUtils.chdir(File.dirname(__FILE__) + "/../features/fixtures/SampleApp") do
39
+ @choctop = ChocTop.new
40
+ @choctop.add_file "README.txt", :position => [50, 100]
41
+ end
42
+ end
43
+
44
+ it "should have build/Release/SampleApp.app as a file/bundle" do
45
+ @choctop.files.keys.should be_include('build/Release/SampleApp.app')
46
+ end
47
+
48
+ it "should have README.txt as a file" do
49
+ @choctop.files.keys.include?('README.txt')
50
+ end
51
+
52
+ it "should have README.txt position" do
53
+ @choctop.files['README.txt'][:position].should == [50, 100]
54
+ end
55
+
56
+ describe "+ prepare_files" do
57
+ before(:each) do
58
+ FileUtils.chdir(File.dirname(__FILE__) + "/../features/fixtures/SampleApp") do
59
+ @choctop.prepare_files
60
+ end
61
+ end
62
+
63
+ it "should have SampleApp.app in build/Release/dmg ready for inclusion in DMG" do
64
+ FileUtils.chdir(File.dirname(__FILE__) + "/../features/fixtures/SampleApp") do
65
+ File.should be_exists('build/Release/dmg/SampleApp.app')
66
+ end
67
+ end
68
+
69
+ it "should position SampleApp.app at [175, 65]" do
70
+ @choctop.set_position_of_files.should =~ /set position of item "SampleApp.app" to \{175, 65\}/
71
+ end
72
+
73
+ it "should have README.txt in build/Release ready for inclusion in DMG" do
74
+ FileUtils.chdir(File.dirname(__FILE__) + "/../features/fixtures/SampleApp") do
75
+ File.should be_exists('build/Release/dmg/README.txt')
76
+ end
77
+ end
78
+
79
+ it "should position README.txt at [50, 100]" do
80
+ @choctop.set_position_of_files.should =~ /set position of item "README.txt" to \{50, 100\}/
81
+ end
82
+ end
83
+
84
+ end
85
+ end