MacOSX
Boxen
Puppet

BoxenがどうやってMacアプリ(*.dmg)をインストールしているのか調べてみた

More than 5 years have passed since last update.


はじめに

boxenは便利そうなのですがちょっとへピーです。自分の環境ではうまく動いてくれないしエラーもよく分からず対応もできません。ですがboxenが提供している「dmgファイルからのMacアプリのインストール」は魅力的です。

このdmgファイルからのMacアプリインストールがhomebrewから出来れば、個人的にはそれで満足です。

homebrewで出来るかどうか置いといて、まずはboxenがどうやってこれをやっているのか調べてみました。


boxenで扱うモジュールの場所

http://github.com/boxen の下に、puppet-XXXというリポジトリが多数あります。ここにpuppet-evernoteやpuppet-dropbox等があるので、これを元にアプリをインストールしているのでしょう。

リポジトリの中を見ると、spec/classes/(アプリ名)_spec.rb というファイルがあり、この中でdmgファイルの場所と:provider => 'appdmg'が設定されています。puppet-dropboxのdropbox_spec.rb


:provider => 'appdmg'

なんとなくappdmgというプロバイダ?がインストールをやってくれているようです。これboxenの中を探しても見つかりません。ですが、puppet labsの中にappdmg.rbを見つけました。

puppetとしての処理も入ってるので、インストールしてる箇所を抜き出してみます。

  def self.installapp(source, name, orig_source)

appname = File.basename(source);
#=============
# (4)
#=============
ditto "--rsrc", source, "/Applications/#{appname}"
File.open("/var/db/.puppet_appdmg_installed_#{name}", "w") do |t|
t.print "name: '#{name}'\n"
t.print "source: '#{orig_source}'\n"
end
end

def self.installpkgdmg(source, name)
unless source =~ /\.dmg$/i
self.fail "Mac OS X PKG DMG's must specify a source string ending in .dmg"
end
require 'open-uri'
require 'facter/util/plist'
cached_source = source
tmpdir = Dir.mktmpdir
#=============
# (1)
#=============
begin
if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source
cached_source = File.join(tmpdir, name)
begin
curl "-o", cached_source, "-C", "-", "-k", "-L", "-s", "--url", source
Puppet.debug "Success: curl transfered [#{name}]"
rescue Puppet::ExecutionFailure
Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods."
cached_source = source
end
end

#=============
# (2)
#=============
open(cached_source) do |dmg|
xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-mountrandom", "/tmp", dmg.path
ptable = Plist::parse_xml xml_str
# JJM Filter out all mount-paths into a single array, discard the rest.
mounts = ptable['system-entities'].collect { |entity|
entity['mount-point']
}.select { |mountloc|; mountloc }
begin
mounts.each do |fspath|
#=============
# (3)
#=============
Dir.entries(fspath).select { |f|
f =~ /\.app$/i
}.each do |pkg|
installapp("#{fspath}/#{pkg}", name, source)
end
end
ensure
hdiutil "eject", mounts[0]
end
end
ensure
FileUtils.remove_entry_secure(tmpdir, true)
end
end

rubyは好きですが得意ではありません。感覚で読み進めてみると、

- (1) dmgファイルをダウンロード

- (2) hdiutilでdmgをマウント

- (3) 末尾が.appなパスを選択

- (4) dittoで/Application/以下にコピー

となっているようです。 hdiutilやdittoはMac固有のコマンドだったと思うので、これが動くのはMacRubyだけ?とか思いますがよくわかりません。意外に単純ですね。


homebrewにポーティングするとしたら

homebrewのspecfileに、appdmg.rbと同等の動きをするロジックを組み込んで、boxenの(puppetの) xxxx_spec.rbを読み込ませれば良さそうです。そのうちやってみますが誰かやってたら教えてください。