4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

はじめに

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を読み込ませれば良さそうです。そのうちやってみますが誰かやってたら教えてください。

4
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?