読者です 読者をやめる 読者になる 読者になる
pixiv insideは移転しました! ≫ http://inside.pixiv.blog/

Mac mini増殖中!iOSアプリのビルドをマスター・スレーブ化して時間を短縮する

チームでiOSアプリの開発をしていると、ビルドのコストが肥大化しがちです。ピクシブでは、ビルドとテストをMac mini上に構築したJenkinsで行っているのですが、ビルドキューが詰まり、開発速度が上がりにくくなっていました。

これを改善するため、最近、iOSチームのビルドサーバーを、Mac mini 1台から3台に増やし、マスター・スレーブ環境でビルドできるようにしました。今回、そのための設定について、エンジニアの @anchan から紹介します!

ビルドサーバーの環境設定は、GitHubに公開しています

モチベーション

Xcodeは1台のマシンでビルドを並列化できません。チームメンバーとアプリの数が増えるにつれ、それに比例してJenkinsのビルドキューが詰まるようになりました。

Mac mini 1台では厳しくなってきたので、Mac mini 3台でJenkinsのマスター・スレーブ環境を作る事にしました。既にあったMac miniをマスターのJenkinsサーバーとして使って、新しい2台をスレーブとして使うことにしました。

CIサービスも検証しましたが、ピクシブのiOSチームにはMac miniとJenkinsの方が合うと判断しました。CIサービスだと新しいXcodeのバージョンが出てから使えるまでに時間がかかります。Xcodeのバージョンによって対応しているSwift言語のバージョンが変わるのでCIサービスが新しいXcodeに対応するまでには新しいSwiftも使えません。パフォーマンスとしてもMac miniでビルドする方がCIサービスでビルドするより早いです。価格でもMac miniが負けません。CircleCIのGrowthプランやTravis CIのSmall Businessプランの1年分の価格でハイスペックのMac mini 2台を購入できます。

f:id:devpixiv:20161122105741j:plain

Mac miniで使用したOSについてですが、後述するようにmacOS Sierraでいくつか問題があったので今回の構成にはOS X El Capitanをつかっています。

構成管理システム

マスター・スレーブ環境は1台構成とくらべて問題を起こしがちです。例えば、1台構成だとXcodeをインストールするにはMac miniにリモートデスクトップで繋いで、Xcodeをインストールだけとシンプルです。これが3台だと、1台づつに繋いでXcodeをインストールすることになり、違うディレクトリにインストールしてしまうなどの間違いが起こりやすくなります。そこで、構成管理システムの出番です。

構成管理システムとしてSaltStackを選びました。Salt SSHを使えば、SaltのサーバーもクライアントもMac miniにインストールする必要がなくて便利です。Ansibleでもいいのですが、Saltでの構築経験があったのでSaltをつかうことにしました。

Saltの基本的な使い方については割愛します。ドキュメンテーションを参考にしてください。

ブートストラップスクリプト

SaltStackをつかう前に、基本なツールをインストールします。Homebrewが必要なのですが、インストールにはXcodeかXcode Command Line Toolsが必要です。Xcodeのインストールは自動化しにくいので、Xcode Command Line Toolsをsoftwareupdateでインストールします。

#! /bin/bash

# SSHログインを有効
sudo systemsetup -setremotelogin on

# Xcode Command Line Toolsをインストール
# 参考: https://github.com/Homebrew/install/blob/e28329a14d328b603e00756e99576c75f08e8dd9/install#L230-L237
if [ ! -d /Library/Developer/CommandLineTools ]; then
  COMMAND_LINE_TOOLS_PLACEHOLDER=/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
  sudo touch $COMMAND_LINE_TOOLS_PLACEHOLDER
  COMMAND_LINE_TOOLS_NAME=$(softwareupdate -l | grep -B 1 -E "Command Line (Developer|Tools)" | awk -F"*" '/^ +\\*/ {print $2}' | sed 's/^ *//' | head -n1)
  sudo softwareupdate -i "$COMMAND_LINE_TOOLS_NAME"
  sudo rm $COMMAND_LINE_TOOLS_PLACEHOLDER
fi

# Homebrewをインストール。TRAVISを設定すると確認のためのエンターを押さなくても良くなる。
# 参考: http://brew.sh/
TRAVIS=1 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

これを実行すると、SSHログインが有効になり、Xcode Command Line ToolsとHomebrewがインストールされます。次に、SaltStackの設定を行います。

ユーザーとSSH鍵認証

すべてのMac miniに、同一のOS XのユーザーとSSH鍵と鍵認証を設定します。SSH鍵が同じだと、新しいスレーブを追加したり、スレーブからマスターにビルドアーティファクトをコピーしたり、マスターがスレーブにSSH接続するのが楽になります。ユーザーとSSH鍵の情報をSalt Pillarに入れて、SSH鍵と鍵認証を設定するSaltステートを作ります。

# pillar/user/init.sls

user:
  account: pixiv
  password: himitsu
# pillar/ssh/init.sls

ssh:
  public_key: ここに公開鍵を書いてね
  private_key: |
      ここに秘密鍵を書いてね
  authorized_keys:
    - authorized_keysに入れたい公開鍵を書いてね
# salt/ssh/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set ssh_directory = '/Users/' + user + '/.ssh' %}

{{ ssh_directory }}:
  file.directory:
    - user: {{ user }}
    - group: staff
    - mode: 700

# ホスト鍵確認を無視する
{{ ssh_directory }}/config:
  file.managed:
    - user: {{ user }}
    - group: staff
    - mode: 600
    - contents: |
        Host *
          StrictHostKeyChecking no

{{ ssh_directory }}/authorized_keys:
  file.managed:
    - user: {{ user }}
    - group: staff
    - mode: 600
    - contents:
      {% for key in salt['pillar.get']('ssh:authorized_keys') %}
      - {{ key }}
      {% endfor %}

# 公開鍵
{{ ssh_directory }}/id_rsa.pub:
  file.managed:
    - user: {{ user }}
    - group: staff
    - mode: 600
    - contents_pillar: ssh:public_key

# 秘密鍵
{{ ssh_directory }}/id_rsa:
  file.managed:
    - user: {{ user }}
    - group: staff
    - mode: 600
    - contents_pillar: ssh:private_key

fastlaneの設定

ピクシブのiOSプロジェクトは、各種作業の自動化のためにfastlaneを使っています。fastlaneは、Apple IDの管理やプロビジョニングプロファイルの同期のような、全プロジェクトに影響がある作業の自動化に向いているので、システム全体にインストールするのがおすすめです。Xcodeのインストール自動化にも役立ちます。

fastlaneのインストール

OS X El CapitanのシステムRubyだとSSL関係の問題があるので、Homebrewで新しいバージョンのRubyをインストールします。

# salt/ruby/init.sls

ruby:
  pkg.installed

その後、pixivユーザーとしてfastlaneをインストールします。

# salt/fastlane/fastlane/init.sls

fastlane:
  gem.installed:
    - gem_bin: /usr/local/bin/gem
    - runas: {{ salt['pillar.get']('user:account') }}

fastlaneのApple ID管理

fastlaneにApple IDを設定すると、審査提出やプロビジョニングプロファイル管理などができるようになります。ピクシブの場合は、App Storeのアカウントとエンタープライズのアカウント両方があるので、複数アカウントを登録します。

まずはpillarにアカウントを登録してみましょう。

# pillar/apple/init.sls

apple:
  accounts:
    store:
      username: app-store@example.com
      password: himitsu
    enterprise:
      username: enterprise@example.com
      password: naisho

  # Xcodeのダウンロードなどに使うアカウント
  default_account: store

fastlaneにアカウントを追加するにはfastlane-credentials addが使えます。しかし、fastlaneでは既にアカウントが存在しているのかを、知る方法がありません。fastlane-credentialsのコードを見ると、キーチェーンにdeliver.$ACCOUNTとしてアカウントのパスワードを保存しているので、security find-internet-passwordを使えば既に追加されたアカウントかがわかります。

# salt/fastlane/credentials/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set password = salt['pillar.get']('user:password') %}

{% for name, account in salt['pillar.get']('apple:accounts').items() %}
fastlane_credentials_{{ name }}:
  cmd.run:
    - name: |
        security unlock-keychain -p "{{ password }}" /Users/{{ user }}/Library/Keychains/login.keychain
        fastlane-credentials add --username {{ account['username'] }} --password {{ account['password'] }}
    - runas: {{ user }}
    - unless: security find-internet-password -s deliver.{{ account['username'] }}
    - require:
      - gem: fastlane
{% endfor %}

プロビジョニングプロファイルの同期

fastlaneのsighで、プロビジョニングプロファイルの管理ができます。各Apple IDの最新プロビジョニングプロファイルを同期します。

# salt/fastlane/sigh/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set password = salt['pillar.get']('user:password') %}

{% for name, account in salt['pillar.get']('apple:accounts').items() %}
fastlane_sigh_{{ name }}:
  cmd.run:
    - name: |
        security unlock-keychain -p "{{ password }}" /Users/{{ user }}/Library/Keychains/login.keychain
        sigh download_all
    - runas: {{ user }}
    - cwd: /tmp
    - env:
      - FASTLANE_USER: {{ account['username'] }}
    - require:
      - gem: fastlane
{% endfor %}

Xcodeのバージョン管理

ピクシブはプロジェクトによって、使っているXcodeのバージョンに差があります。複数Xcodeのバージョンをインストールして簡単に切り替えれるようにします。

Xcodeのインストールは複雑で自動化しにくいです。Xcodeをダウンロードするにはログインが必要な上、インストール後も初回起動時には利用規約に同意する操作が求められます。またXcodeのバージョンによって、.dmgファイルもあれば.xipファイルのものもあります。

そこでxcode-installを使います。xcode-installはfastlaneの開発者KrauseFxが作った、Xcodeのインストールツールです。fastlaneはxcode-installに依存しているので、既にインストールされています。これを使うと複数のXcodeのバージョンを簡単にインストールできます。

Pillarにインストールしたいバージョンを記述します。

# pillar/xcode/init.sls

xcode:
  versions:
    - 7.3.1
    - 8
    - 8.1

  # xcode-selectで設定するバージョン
  default_version: 8.0

ステートは以下のとおり。

# salt/xcode/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set password = salt['pillar.get']('user:password') %}
{% set fastlane_user = salt['pillar.get']('apple:accounts')[salt['pillar.get']('apple:default_account')]['username'] %}

# 各バージョンをインストール
{% for version in salt['pillar.get']('xcode:versions') %}
xcversion_install_{{ version }}:
  cmd.run:
    - name: |
        security unlock-keychain -p "{{ password }}" /Users/{{ user }}/Library/Keychains/login.keychain
        xcversion update
        xcversion install {{ version }}
        xcodebuild -license accept
    - unless: xcversion installed | grep {{ version }}
    - env:
      - FASTLANE_USER: {{ fastlane_user }}
    - require:
      - gem: fastlane
{% endfor %}

# デフォルトXcodeを設定
{% set default_version = salt['pillar.get']('xcode:default_version') %}
xcversion_select:
  cmd.run:
    - name: |
          xcversion select {{ default_version }}
          xcodebuild -license accept
    - unless: xcversion selected | grep {{ default_version }}
    - require:
      - gem: fastlane

これで、Xcodeのバージョンを簡単にDEVELOPER_DIRの環境変数で切り替えできます

Xcodeのファイルサイズが大きいので(4~5 GB)、Mac mini 3台が同時に3つのバージョンのXcodeをダウンロードすると、全体で45 GB程度のダウンロードサイズになり、ネットワークの帯域を圧迫します。しかし、~/Library/Caches/XcodeInstallに、各バージョンごとのXcodeのdmgxipを置くとダウンロードが発生しません。事前に各Mac miniにXcodeのファイルを置くとよいでしょう。

コードサイニングの秘密鍵・証明書管理

開発中のiOSアプリを社内配布するには、コードサイニングの秘密鍵と証明書が必要になります。Ad hocとエンタープライズのビルド両方やっているので、複数のコードサイニングの秘密鍵と証明書をKeychainにインポートしなくてはいけません。

キーチェーンアクセスから、証明書と秘密鍵を書き出してpillarにbase64(base64 -b64 file.p12)のデータとして設定します。

# pillar/keychain/init.sls

keychain:
  keys:
    store_adhoc:
      passphrase: himitsu
      contents_base64: |
          44GT44GT44Gr56eY5a+G6Y2144Go6Ki85piO5pu444KS5YWl44KM44Gm44Gt44CC
          44Kt44O844OB44Kn44Oz44Ki44Kv44K744K544GL44KJcDEy44Go44GX44Gm5pu4
          44GN5Ye644GX44Gm44Gt44CC5a6f6Zqb44Gr44GT44KM44KI44KK5YWo54S26ZW3
          44GP44Gq44KL44Gv44Ga44CC

    store_developer:
      passphrase: himitsu
      contents_base64: |
          44GT44GT44Gr56eY5a+G6Y2144Go6Ki85piO5pu444KS5YWl44KM44Gm44Gt44CC
          44Kt44O844OB44Kn44Oz44Ki44Kv44K744K544GL44KJcDEy44Go44GX44Gm5pu4
          44GN5Ye644GX44Gm44Gt44CC5a6f6Zqb44Gr44GT44KM44KI44KK5YWo54S26ZW3
          44GP44Gq44KL44Gv44Ga44CC

ステートの方でbase64にデコードしてキーチェーンにインポートします。security import-T /usr/bin/codesignのパラメーターを渡すとcodesignに鍵の使用を許可することができます。許可をしないと、ビルド時に許可のポップアップがでてしまいます。

# salt/keychain/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set password = salt['pillar.get']('user:password') %}
{% set keychain = '/Users/' + user + '/Library/Keychains/login.keychain' %}
{% set key_dir = '/Users/' + user + '/.keys' %}

{{ key_dir }}:
  file.directory:
    - user: {{ user }}
    - group: staff
    - mode: 700

# base64デコード
{% for name, key in salt['pillar.get']('keychain:keys').items() %}
keychain_key_file_{{ name }}:
  cmd.run:
    - name: echo {{ key['contents_base64'] | replace('\n', '') }} | base64 -D > {{ key_dir }}/{{ name }}.p12
    - runas: {{ user }}
    - creates: {{ key_dir }}/{{ name }}.p12
    - require:
      - file: {{ key_dir }}

# キーチェーンにインポート
keychain_key_import_{{ name }}:
  cmd.run:
    - name: |
        security unlock-keychain -p "{{ password }}" {{ keychain }}
        security import {{ key_dir }}/{{ name }}.p12 -P "{{ key['passphrase'] }}" -k {{ keychain }} -T /usr/bin/codesign
        touch {{ key_dir }}/{{ name }}.p12.imported
    - runas: {{ user }}
    - creates: {{ key_dir }}/{{ name }}.p12.imported
    - require:
      - cmd: keychain_key_file_{{ name }}
{% endfor %}

Java依存問題の解決

JenkinsはJavaに依存しています。スレーブもJavaが無いと動きません。簡単に自動でインストールするにはHomebrew Caskを使います。

まずはHomebrew Caskをインストールするステートを作ります。事前にCaskroomのディレクトリを作っておかないと、初めてbrew caskを実行する時にHomebrew Caskの設定を行ってエンターキーを押さないと進めなくなってしまいます。ディレクトリを事前に作れば、brew caskの設定が終了したと認識されるため、自動化できるようになります。

# salt/tools/brew-cask/init.sls

{% set user = salt['pillar.get']('user:account') %}

brew_cask_install:
  cmd.run:
    - name: brew tap caskroom/cask
    - runas: {{ user }}
    - unless: brew tap | grep caskroom/cask

/usr/local/Caskroom:
  file.directory:
    - user: {{ user }}
    - group: staff
    - mode: 775

Homebrew Caskをインストールすると、Javaを簡単にインストールできます。

# salt/tools/java/init.sls

java_install:
  cmd.run:
    - name: brew cask install java
    - unless: brew cask list | grep -x java
    - require:
      - cmd: brew_cask_install

OS Xのセッションとキーチェーンについて

マスターにJenkinsをインストールする前に、OS Xのセッションとキーチェーンの仕組みを理解する必要があります。ここでも簡単に紹介しておきましょう。

セッションとは

セッションはOS Xのプロセスの分け方です。あるセッション内のプロセスは、他のセッション内のプロセスの存在を知ることができません。詳しくはAppleのドキュメンテーションをご覧ください。

OS Xには、ルートセッションとユーザーセッションの2種類のセッションがあります。ルートセッションは、mDNSRespondersshdなど、ユーザーと関係無いプロセスが動いています。ユーザーセッションは、ユーザーがUIにログインしてから作られ、ログインしたユーザーのためにプロセスが動くセッションです。

キーチェーンとは

OS Xはパスワード・鍵・証明書などをキーチェーンに保存しています。デフォルトでシステムのキーチェーンとユーザーのログインキーチェーンがあります。

システムキーチェーン(/Library/Keychains/System.keychain)はどのセッションからも、Read Onlyとしてアクセスできます。

ログインキーチェーン($HOME/Library/Keychains/login.keychain)はユーザー毎のキーチェーンになります。ユーザーセッションから自身のキーチェーンにのみアクセスできます。

iOS開発との関係

ルートセッションからiOSのシミュレータを起動できなかったり、ログインキーチェーンにアクセスできなかったりする事があります。対策はありますが、OS Xのバージョンや、Xcodeのバージョンによって動かなかったりと、問題は複雑です。可能な限りユーザーセッションで動くと楽になります。

マスターにJenkinsをインストール

Jenkinsのジョブからシミュレーターを起動したり、ユーザーのキーチェーンをアクセスするためにユーザーセッションの中でJenkinsを起動します。そのためにJenkinsをpixivユーザーのLaunchAgentsに入れます。

# salt/jenkins/init.sls

{% set user = salt['pillar.get']('user:account') %}
{% set jenkins_startup_file = '/Users/' + user + '/Library/LaunchAgents/net.pixiv.jenkins.plist' %}

jenkins:
  pkg.installed

jenkins_startup:
  # LaunchAgentを作る
  file.managed:
    - name: {{ jenkins_startup_file }}
    - user: {{ user }}
    - group: staff
    - makedirs: True
    - contents: |
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
          <dict>
            <key>Label</key>
            <string>net.pixiv.jenkins</string>
            <key>ProgramArguments</key>
            <array>
              <string>/usr/bin/java</string>
              <string>-Dmail.smtp.starttls.enable=true</string>
              <string>-jar</string>
              <string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
              <string>--httpListenAddress=0.0.0.0</string>
              <string>--httpPort=8080</string>
            </array>
            <key>RunAtLoad</key>
            <true/>
          </dict>
        </plist>
  # launchdに登録
  cmd.run:
    - name: launchctl load -w {{ jenkins_startup_file }}
    - runas: {{ user }}
    - onchanges:
      - file: jenkins_startup

これでpixivユーザーがログインしたら、Jenkinsはポート8080で起動されます。

Jenkinsの環境変数

Jenkinsの管理画面で全体的な環境変数を設定する必要があります。

まずはOS Xのデフォルトlocaleには問題があり、そのままではいろんなツールがうまく動きません。

Anchans-MacBook-Pro:~ anchan$ locale
LANG=
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

対策としてLC_CTYPEen_US.UTF-8に設定します。

また、/usr/local/binPATHに入れないとHomebrewでインストールしたパッケージを実行できないのでPATHを設定します。

f:id:devpixiv:20161125174144p:plain

スレーブの設定

Jenkinsマスターは、スレーブにSSHで接続してJenkinsのSlave Agentを起動します。問題はsshdがルートセッションで動いているので、スレーブでシミュレーターを起動したり、ログインキーチェーンをアクセスしたり、といったことができません。

Appleのドキュメンテーションによってユーザーセッション(このドキュメンテーションでは「Aqua session」と呼ばれています)が存在している、つまりユーザーはUIにログインしていれば、SSHで接続する時はユーザーセッションに接続します。ただEl Capitanだとルートセッションになるので、そのための対策が必要でした。

対策として、ユーザーセッションの中でユーザーとしてもう一つのsshdを動かします。Jenkinsはこのユーザーセッションのsshdに接続して、Slave Agentをユーザーセッションの中に起動するので、シミュレーターとログインキーチェーンを問題なく使うことができます。

ユーザー権限でsshdを実行する方法について情報はあまりみかけませんが、2008年のCygwinのメールリストのポストを見つけました。これを参考にしてユーザーsshdステートを作ります。

{% set user = salt['pillar.get']('user:account') %}
{% set sshd_directory = '/Users/' + user + '/.sshd' %}
{% set key_types = ['rsa', 'dsa', 'ecdsa', 'ed25519'] %}
{% set sshd_startup_file = '/Users/' + user + '/Library/LaunchAgents/net.pixiv.sshd.plist' %}

# sshd_configを作る
{{ sshd_directory }}/config:
  file.managed:
    - user: {{ user }}
    - group: staff
    - makedirs: True
    - contents: |
        Port 2222
        {% for key_type in key_types %}
        HostKey {{ sshd_directory }}/ssh_host_{{ key_type }}_key
        {% endfor %}

        UsePrivilegeSeparation no
        PidFile {{ sshd_directory }}/sshd.pid

        SyslogFacility AUTHPRIV
        AuthorizedKeysFile      .ssh/authorized_keys
        AcceptEnv LANG LC_*
        Subsystem       sftp    /usr/libexec/sftp-server

# 各種類のホストキーを作る
{% for key_type in key_types %}
user_sshd_host_key_{{ key_type }}:
  cmd.run:
    - name: ssh-keygen -t {{ key_type }} -f {{ sshd_directory }}/ssh_host_{{ key_type }}_key -N ""
    - runas: {{ user }}
    - creates: {{ sshd_directory }}/ssh_host_{{ key_type }}_key
{% endfor %}

user_sshd_startup:
  # LaunchAgentを作る
  file.managed:
    - name: {{ sshd_startup_file }}
    - user: {{ user }}
    - group: staff
    - makedirs: True
    - contents: |
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
          <dict>
            <key>Label</key>
            <string>net.pixiv.sshd</string>
            <key>ProgramArguments</key>
            <array>
              <string>/usr/sbin/sshd</string>
              <string>-f</string>
              <string>{{ sshd_directory }}/config</string>
              <string>-D</string>
            </array>
            <key>RunAtLoad</key>
            <true/>
          </dict>
        </plist>
  # launchdに登録
  cmd.run:
    - name: launchctl load -w {{ sshd_startup_file }}
    - runas: {{ user }}
    - onchanges:
      - file: user_sshd_startup

# コンフィグファイルが変わったらsshdを再起動
user_sshd_restart:
  cmd.run:
    - name: |
        launchctl unload {{ sshd_startup_file }}
        launchctl load {{ sshd_startup_file }}
    - runas: {{ user }}
    - onchanges:
      - file: {{ sshd_directory }}/config

これでユーザーセッション内でsshdがポート2222で起動されます。

Jenkinsの管理画面にスレーブを登録

Jenkinsの「Manage Nodes」の管理画面からこのスレーブを追加できるようになりました。追加する時は「Launch slave agents on Unix machines via SSH」を選んで「Advanced」から「Port」を「2222」に設定します。

f:id:devpixiv:20161125174533p:plain

これでマスターがスレーブに接続して設定が終わりました!

macOS Sierraを使わなかった理由

今後XcodeはSierraしか対応しなくなるので、最初はSierraを使おうとしていましたが、いくつかの越えられない壁が出てEl Capitanを使う事にしました。その理由を、いくつかご紹介します。

キーチェーンを切り替えられなかった

El Capitanだとsshでログインするとログインとシステムのキーチェーン両方が見えます

$ security list-keychains
    "/Users/pixiv/Library/Keychains/login.keychain"
    "/Library/Keychains/System.keychain"

security unlock-keychain ~/Library/Keychains/login.keychainをするとすぐログインキーチェーンを使う事ができます。

Sierraの場合は

$ security list-keychains
    "/Library/Keychains/System.keychain"
    "/Library/Keychains/System.keychain"

ログインキーチェーンをサーチリストに追加しようとしても

$ security list-keychains -s ~/Library/Keychains/login.keychain
$ security list-keychains
    "/Library/Keychains/System.keychain"
    "/Library/Keychains/System.keychain"

変わりません…!追加ができないようです。

キーチェーンに許可済み状態として鍵をインポートできない

鍵のp12ファイルをキーチェーンに以下のようにインポートしようとすると

$ security import key.p12 -P passphrase -k ~/Library/Keychains/login.keychain -T /usr/bin/codesign

/usr/bin/codesignにその鍵を読む許可をあげています。El Capitanではうまく動きますが、Sierraだとキーチェーンアクセスで見るとcodesignに許可があるにもかかわらずビルド時に許可ダイアログが出てしまいます。これでは自動化できません。

まとめ

ピクシブで利用しているiOSアプリのビルドのJenkinsのマスター・スレーブ環境の設定を紹介しました。Mac mini 1台から3台にしてからというもの、ビルドキューが詰まることもなく、アプリチームメンバーは幸せです!

細かい話は飛ばしたので詳しくはGitHubのリポジトリSaltStackのドキュメンテーション、そしてJenkinsのマスター・スレーブドキュメンテーションを参考にしてください。