ガイド

iOSビルド用にMacをJenkinsのエージェントとして追加する方法

MacをJenkinsに追加するには、コントローラーに接続する永続ノードを作成します。MacにはJava 21をインストールし、-webSocketオプション付きでagent.jarを実行します。このコマンドをLaunchAgentで包み、自動ログインをオンにします。これで再起動後もエージェントが再接続します。最後にノードにmacosのラベルを付け、iOSのステージをそこに送ります。

最初に必要なもの

  • MacからHTTPSで到達できるJenkinsのコントローラー。
  • 管理者権限、Homebrew、XcodeのあるApple silicon Mac。
  • 適切なJava。2026年10月に読んだJenkinsのJavaサポートポリシーでは、LTS 2.555.1以降はJava 21または25が必要です。このルールはコントローラーだけでなくエージェントにも当てはまります。
  • 最後の手順のための、画面共有によるMacのデスクトップセッション。

1. MacにJavaをインストールする

brew install openjdk@21
/opt/homebrew/opt/openjdk@21/bin/java -version

HomebrewはこのJDKをkeg-onlyでインストールするので、PATHには入りません。どこでも上のフルパスを使ってください。そうすれば、あとで新しいJDKが入っても、エージェントはJava 21のままです。

2. コントローラーでノードを作成する

  • 「Jenkinsの管理」、「ノード」、「新規ノード」の順に開き、「Permanent Agent」を選びます。
  • 同時ビルド数:1。Jenkins自身のドキュメントが、ノードあたり1つのexecutorを最も安全な設定としています。Xcodeのビルドは、それだけで全コアを使います。
  • リモートFSルート:/Users/YOUR-USER/jenkins。
  • ラベル:macos xcode。
  • 用途:このノードに一致するラベル式のジョブだけをビルドする。これでLinuxのジョブがMacに来なくなります。
  • 起動方法:コントローラーに接続してエージェントを起動する。

保存します。ノードのページに、実行コマンド、エージェント名、長い16進数のシークレットが表示されます。シークレットはエージェント名に結びついています。漏れた場合、Jenkinsはその名前を再利用しないよう指示しています。

3. agent.jarをダウンロードして手動でテストする

Jenkinsは、コントローラーに合ったagent.jarを/jnlpJars/agent.jarで配布しています。シークレットはファイルに保存し、@付きで渡します。こうすればプロセス一覧に表示されません。

mkdir -p ~/jenkins && cd ~/jenkins
curl -sO https://jenkins.example.com/jnlpJars/agent.jar
echo 'PASTE-THE-SECRET' > secret-file
chmod 600 secret-file
/opt/homebrew/opt/openjdk@21/bin/java -jar agent.jar \
  -url https://jenkins.example.com/ \
  -name mac-mini-1 \
  -secret @secret-file \
  -workDir "$HOME/jenkins" \
  -webSocket

ノードのページが接続済みに変わるはずです。Control Cで止めます。-webSocketを付けると、エージェントはHTTPS接続を1本だけ使います。付けない場合は、コントローラーの別のインバウンドTCPポートも必要です。

4. LaunchAgentからエージェントを動かす

launchdはログイン時にエージェントを起動し、終了したら再起動します。これはMacのデスクトップ上のターミナルから実行してください。ヒアドキュメントがホームフォルダのパスを埋めます。

mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/local.jenkins-agent.plist <<EOF
<?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>local.jenkins-agent</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/opt/openjdk@21/bin/java</string>
    <string>-jar</string><string>$HOME/jenkins/agent.jar</string>
    <string>-url</string><string>https://jenkins.example.com/</string>
    <string>-name</string><string>mac-mini-1</string>
    <string>-secret</string><string>@$HOME/jenkins/secret-file</string>
    <string>-workDir</string><string>$HOME/jenkins</string>
    <string>-webSocket</string>
  </array>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key><string>/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    <key>LANG</key><string>en_US.UTF-8</string>
  </dict>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>ThrottleInterval</key><integer>30</integer>
  <key>StandardOutPath</key><string>$HOME/jenkins/agent.log</string>
  <key>StandardErrorPath</key><string>$HOME/jenkins/agent.log</string>
</dict>
</plist>
EOF
plutil -lint ~/Library/LaunchAgents/local.jenkins-agent.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.jenkins-agent.plist
launchctl print gui/$(id -u)/local.jenkins-agent | head -20

このplistはplutil -lintで確認済みです。なぜLaunchDaemonではなくLaunchAgentなのでしょうか。デーモンはどのログインセッションの外でも動きます。シミュレータとログインキーチェーンはセッションの中にあります。GitLabとBuildkiteも、自社のMac用エージェントについて同じ理由を挙げています。

plistは~/Library/LaunchAgentsに置いてください。macOS 27では、/Library/LaunchAgentsにplistがあると、起動のたびに自動ログインが壊れることを確認しました。詳しくはヘッドレスMacでの自動ログインにあります。

5. 再起動後も動くようにする

「システム設定」の「ユーザとグループ」で、エージェントのアカウントの自動ログインをオンにします。Macがスリープしないようにします。そして再起動し、ノードのページを確認します。

sudo pmset -a sleep 0
sudo shutdown -r now
# then, after it is back:
tail -n 20 ~/jenkins/agent.log

6. iOSのステージを含むJenkinsfile

pipeline {
    agent { label 'macos' }
    options { timeout(time: 30, unit: 'MINUTES') }
    stages {
        stage('Test') {
            steps {
                sh 'xcodebuild -version'
                sh 'rm -rf build/TestResults.xcresult'
                sh "xcodebuild test -project MyApp.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.5' -resultBundlePath build/TestResults.xcresult"
            }
        }
    }
    post {
        always {
            sh 'ditto -c -k --keepParent build/TestResults.xcresult build/TestResults.xcresult.zip || true'
            archiveArtifacts artifacts: 'build/TestResults.xcresult.zip', fingerprint: true
        }
    }
}

result bundleはフォルダなので、アーカイブの前にdittoでzipにします。どのMacでもzipを開き、bundleをダブルクリックすればXcodeで見られます。

1つのエージェントに2つのバージョンのXcodeがある場合は、DEVELOPER_DIRでパイプラインごとに選びます。Appleのxcode-selectのmanページによると、これはシステム全体の選択を変えずに上書きします。environmentブロックで設定すれば、Mac上のほかのジョブに影響しません。

environment {
    DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer'
}

このブロックはpipelineの中、agentの隣に置きます。sudo xcode-select -sでグローバル設定を変えても動きますが、マシン上のすべてのジョブが一度に変わります。

よくあるエラーと対処法

  • エージェントは接続するのに、再起動後に消える。誰もログインしていないので、LaunchAgentが起動していません。自動ログインを確認してください。
  • SSH経由だとlaunchctl bootstrapがドメインのエラーで失敗する。デスクトップのターミナルから実行してください。GitLabとBuildkiteも、自社のLaunchAgentについて同じエラーを記載しています。
  • エージェントが起動を拒否し、Javaに言及する。Jenkinsは起動時にJavaのバージョンを確認します。サポートポリシーのバージョンに合わせてください。
  • xcodebuild: error: Existing file at -resultBundlePath。ワークスペースはビルドの間も残ります。Jenkinsfileのように、先に古いbundleを削除してください。

専用Macが役立つ理由

永続エージェントは、ワークスペースとXcodeのDerivedDataをビルドの間も保持します。その見返りが差分ビルドです。当社のベンチマークでは、Xcode 26.6でWikipediaのiOSアプリを使い、3回の中央値を取りました。小さな変更後の再ビルドは、ウォームなM6で27秒でした。新規のGitHubホスト型macos-26 runnerでは、同じジョブに269秒かかりました。

これが不要な場合

まだJenkinsを使っていないなら、iOSアプリ1つのために始めるのはやめましょう。Mac runnerのあるホスト型サービスのほうが保守は少なく済みます。GitHubのmacOSの単価$0.062/分(2026年9月に確認)なら、月額固定$139のMacは月2,242分ほどで元が取れます。それより少なければ、従量課金のほうが安くなります。また、コントローラーが固定IPからのエージェントしか受け付けないなら、MacRunは向きません。当社には固定IPがありません。

MacRunのMacでのJenkinsは、その他のCIシステムのページで簡潔に説明しています。ステージに何を入れるかは、iOS CI/CDパイプラインのガイドで扱っています。

よくある質問

JenkinsのMacエージェントはSSHとインバウンドのどちらで起動すべきですか?

+

Macからコントローラーには届くが、逆方向には届かない場合、インバウンドが使えます。-webSocketを付ければ、コントローラーへのHTTPSだけで済みます。

macOS上のJenkinsエージェントにはどのJavaが必要ですか?

+

コントローラーと同じ系統です。2026年10月に読んだJenkinsのサポートポリシーでは、LTS 2.555.1以降はJava 21または25が必要です。

Macエージェントのexecutorはいくつにすべきですか?

+

まずは1つにしてください。Jenkinsはノードあたり1つのexecutorを最も安全な設定としています。Xcodeのビルドは、それだけで全コアを使います。

再起動後にJenkinsのMacエージェントが再接続しないのはなぜですか?

+

LaunchAgentは、そのユーザーがログインしないと動きません。そのアカウントの自動ログインをオンにし、plistは~/Library/LaunchAgentsに置いてください。

関連ガイド