Module: ChocTop::Dmg

Included in:
Configuration
Defined in:
lib/choctop/dmg.rb

Instance Method Summary collapse

Instance Method Details

#add_eulaObject



185
186
187
188
189
190
191
192
# File 'lib/choctop/dmg.rb', line 185

def add_eula
  # TODO support EULA
  # hdiutil unflatten $@
	# /Developer/Tools/DeRez -useDF SLAResources.rsrc > build/temp/sla.r
	# /Developer/Tools/Rez -a build/temp/sla.r -o $@
	# hdiutil flatten $@
	
end

#add_file_to_dmg_src_folder(path, options) ⇒ Object



194
195
196
197
198
199
200
201
# File 'lib/choctop/dmg.rb', line 194

def add_file_to_dmg_src_folder(path, options)
  target = File.join(tmp_dmg_src_folder, options[:name])
  sh ::Escape.shell_command(['cp', '-r', path, target])
  if options[:exclude]
    exclude_list = options[:exclude].is_a?(Array) ? options[:exclude] : [options[:exclude].to_s]
    exclude_list.each { |exclude| sh ::Escape.shell_command(['rm', '-rf', File.join(target, exclude)]) }
  end
end


203
204
205
206
207
208
209
210
# File 'lib/choctop/dmg.rb', line 203

def add_link_to_dmg_src_folder(path, options)
  plist_name   = options[:name].gsub(/\.webloc$/, '')
  plist_target = File.join(tmp_dmg_src_folder, plist_name)
  target       = File.join(tmp_dmg_src_folder, options[:name])
  sh ::Escape.shell_command(['defaults', 'write', plist_target, 'URL', options[:url]])
  sh ::Escape.shell_command(['plutil', '-convert', 'xml1', '-o', target, "#{plist_target}.plist"])
  sh ::Escape.shell_command(['rm', "#{plist_target}.plist"])
end

#background_boundsObject



73
74
75
76
77
# File 'lib/choctop/dmg.rb', line 73

def background_bounds
  return [400, 300] unless background_file
  background = OSX::NSImage.alloc.initByReferencingFile(background_file).size.to_a
  [background.first, background.last + statusbar_height]
end

#configure_applications_iconObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/choctop/dmg.rb', line 151

def configure_applications_icon
  run_applescript <<-SCRIPT.gsub(/^      /, ''), "apps_icon_script"
    tell application "Finder"
      set applications_folder to displayed name of (path to applications folder) -- i18n
      set dest to disk "#{name}"
      set src to folder applications_folder of startup disk
      make new alias at dest to src
    end tell
  SCRIPT
  if applications_icon
    applications_path = "#{volume_path}/Applications"
    OSX::NSApplicationLoad()
    image = OSX::NSImage.alloc.initWithContentsOfFile(applications_icon)
    OSX::NSWorkspace.sharedWorkspace.setIcon_forFile_options(image, applications_path, nil)
  end
end

#configure_dmg_windowObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/choctop/dmg.rb', line 88

def configure_dmg_window
  if background_file
    target_background = "#{volume_path}/#{volume_background}"
    FileUtils.mkdir_p(File.dirname(target_background))
    FileUtils.cp(background_file, target_background) 
  end
  script = <<-SCRIPT.gsub(/^      /, '')
    tell application "Finder"
       set applications_folder to displayed name of (path to applications folder) -- i18n
       set mountpoint to POSIX file ("#{volume_path}" as string) as alias
       tell folder mountpoint
           open
           tell container window
              set toolbar visible to false
              set statusbar visible to false -- doesn't do anything at DMG open time
              set current view to icon view
              delay 1 -- Sync
              set the bounds to {#{window_bounds.join(", ")}}
           end tell
           delay 1 -- Sync
           set icon size of the icon view options of container window to #{icon_size}
           set text size of the icon view options of container window to #{icon_text_size}
           set arrangement of the icon view options of container window to not arranged
           #{set_position_of_files}
           #{set_position_of_shortcuts}
           close
           open
           set the bounds of the container window to {#{window_bounds.join(", ")}}
           set background picture of the icon view options of container window to file "#{volume_background.gsub(/\//,':')}"
           update without registering applications
           delay 5 -- Sync
           close
       end tell
       -- Sync
       delay 5
    end tell
  SCRIPT
  puts "script: #{script}"
  run_applescript(script)
  sh "SetFile -a V '#{target_background}'" if background_file
end

#configure_volume_iconObject



81
82
83
84
85
86
# File 'lib/choctop/dmg.rb', line 81

def configure_volume_icon
  if volume_icon
    FileUtils.cp(volume_icon, "#{volume_path}/.VolumeIcon.icns")
    sh "SetFile -a C '#{volume_path}'"
  end
end

#convert_dmg_readonlyObject



179
180
181
182
183
# File 'lib/choctop/dmg.rb', line 179

def convert_dmg_readonly
  tmp_path = "/tmp/rw.dmg"
  FileUtils.mv(pkg, tmp_path)
  sh "hdiutil convert '#{tmp_path}' -format UDZO -imagekey zlib-level=9 -o '#{pkg}'"
end

#copy_filesObject

Two-phase copy: first to a tmp folder (to prevent recursion); then tmp folder to dmg_src_folder



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/choctop/dmg.rb', line 28

def copy_files
  FileUtils.mkdir_p(tmp_dmg_src_folder)
  files.each do |path, options|
    if options[:link]
      add_link_to_dmg_src_folder(path, options)
    else
      add_file_to_dmg_src_folder(path, options)
    end
  end
  FileUtils.rm_r(dmg_src_folder) if File.exists? dmg_src_folder
  FileUtils.mkdir_p(dmg_src_folder)
  Dir["#{tmp_dmg_src_folder}/*"].each { |f| FileUtils.cp_r(f, dmg_src_folder) }
end

#detach_dmgObject



168
169
170
171
172
173
174
175
176
177
# File 'lib/choctop/dmg.rb', line 168

def detach_dmg
  mounted_paths = `hdiutil info | grep '#{volume_path}' | grep "Apple_HFS"`.split("\n").map { |e| e.split(" ").first }
  mounted_paths.each do |path|
    begin
      sh "hdiutil detach '#{path}' -quiet -force"
    rescue StandardError => e
      p e
    end
  end
end

#include_applications_icon?Boolean

Returns:

  • (Boolean)


147
148
149
# File 'lib/choctop/dmg.rb', line 147

def include_applications_icon?
  target =~ /.app$/
end

#make_dmgObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/choctop/dmg.rb', line 42

def make_dmg
  prepare_files
  copy_files
  FileUtils.mkdir_p build_path
  FileUtils.mkdir_p mountpoint # TODO can we remove random mountpoints?
  FileUtils.rm_f(pkg) # make sure destination pkg doesn't already exist, or hdiutil will barf
  sh "hdiutil create -format UDRW -quiet -volname '#{name}' -srcfolder '#{dmg_src_folder}' '#{pkg}'"
  sh "hdiutil attach '#{pkg}' -mountpoint '#{volume_path}' -noautoopen -quiet"
  sh "bless --folder '#{volume_path}' --openfolder '#{volume_path}'"
  sh "sleep 1"

  puts "volume_icon: #{volume_icon.inspect}"
  puts "include_applications_icon?: #{include_applications_icon?.inspect}"
  configure_volume_icon
  configure_applications_icon if include_applications_icon?
  configure_dmg_window
end

#prepare_filesObject



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/choctop/dmg.rb', line 3

def prepare_files
  self.files = files.inject({}) do |files, file|
    path_or_helper, options = file
    path = case path_or_helper
      when Symbol
        send path_or_helper
      when Proc
        path_or_helper.call
      else
        path_or_helper
    end
    if path && File.exists?(path)
      files[path] = options 
      options[:name] ||= File.basename(path)
    end
    if path =~ %r{\.webloc$}
      files[path] = options 
      options[:name] ||= File.basename(path)
      options[:link] = true
    end
    files
  end
end

#run_applescript(applescript, tmp_file = "choctop-script") ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/choctop/dmg.rb', line 212

def run_applescript(applescript, tmp_file = "choctop-script")
  File.open(scriptfile = "/tmp/#{tmp_file}", "w") do |f|
    f << applescript
  end
  sh("osascript #{scriptfile}") do |ok, res|
    if ! ok
      p res
      puts volume_path
      exit 1
    end
  end
  applescript
end

#set_position_of_filesObject



130
131
132
133
134
135
136
137
# File 'lib/choctop/dmg.rb', line 130

def set_position_of_files
  files.map do |file_options|
    path, options = file_options
    target        = options[:name]
    position      = options[:position].join(", ")
    %Q{set position of item "#{target}" to {#{position}}}
  end.join("\n")
end

#set_position_of_shortcutsObject



139
140
141
142
143
144
145
# File 'lib/choctop/dmg.rb', line 139

def set_position_of_shortcuts
  if include_applications_icon?
    %Q{set position of item applications_folder to {#{applications_icon_position.join(", ")}}}
  else
    ""
  end
end

#statusbar_heightObject



79
# File 'lib/choctop/dmg.rb', line 79

def statusbar_height; 20; end

#tmp_dmg_src_folderObject



226
227
228
229
230
231
# File 'lib/choctop/dmg.rb', line 226

def tmp_dmg_src_folder
  @tmp_dmg_src_folder ||= begin
    require 'tmpdir'
    File.join(Dir.tmpdir, Time.now.to_i.to_s) # probably unique folder
  end
end

#volume_backgroundObject



60
61
62
# File 'lib/choctop/dmg.rb', line 60

def volume_background
  ".background/background#{File.extname(background_file)}"
end

#window_boundsObject



68
69
70
71
# File 'lib/choctop/dmg.rb', line 68

def window_bounds
  window_position + 
  window_position.zip(background_bounds).map { |w, b| w + b }
end

#window_positionObject



64
65
66
# File 'lib/choctop/dmg.rb', line 64

def window_position
  [50, 100]
end