choctop 0.9.6 → 0.10.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.
@@ -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