मुख्य सामग्री पर जाएं

Jenkins Mac Build Agent: संपूर्ण सेटअप गाइड

एक समर्पित Mac Mini M4 पर Jenkins build agent कॉन्फ़िगर करें। Xcode बिल्ड स्वचालित करें, iOS टेस्ट चलाएं और अपने बिल्ड इन्फ्रास्ट्रक्चर पर पूर्ण नियंत्रण के साथ TestFlight पर डिप्लॉय करें।

CI/CD 35 मिनट पढ़ें अक्टूबर 2026 में अपडेट किया गया

Jenkins के लिए एक समर्पित Mac क्यों?

Jenkins सबसे व्यापक रूप से उपयोग किया जाने वाला CI/CD प्लेटफ़ॉर्म है, जो 50% से अधिक एंटरप्राइज़ बिल्ड पाइपलाइनों को चलाता है। हालांकि, Jenkins के साथ iOS ऐप्स बनाने के लिए macOS की आवश्यकता होती है, और macOS केवल Apple हार्डवेयर पर ही कानूनी रूप से चल सकता है। एक समर्पित Mac Mini M4 आपको देता है:

  • सुसंगत बिल्ड एनवायरनमेंट

    हर बिल्ड में समान हार्डवेयर, समान OS, समान टूल्स। एनवायरनमेंट ड्रिफ्ट के कारण कोई अस्थिर बिल्ड नहीं।

  • पूर्ण Root एक्सेस

    कोई भी टूल्स इंस्टॉल करें, सिस्टम सेटिंग्स कॉन्फ़िगर करें, कोड साइनिंग के लिए कीचेन प्रबंधित करें।

  • Apple Silicon प्रदर्शन

    M4 चिप, Intel Mac Minis की तुलना में 2-3x तेज़ Xcode बिल्ड प्रदान करती है।

  • स्थायी कैश

    DerivedData, CocoaPods और SPM कैश बिल्ड के बीच बने रहते हैं।

पूर्वापेक्षाएं

  • My Remote Mac से एक समर्पित Mac mini M4 बिल्ड सर्वर ($85/माह से, USD, कर अतिरिक्त)
  • एक Jenkins controller (Linux, Docker या किसी भी क्लाउड VM पर हो सकता है)
  • आपके Mac Mini तक SSH एक्सेस
  • Mac Mini पर Xcode इंस्टॉल किया गया (Xcode इंस्टॉलेशन चरणों के लिए हमारी GitHub Actions गाइड देखें)

चरण 1: macOS पर Java JDK इंस्टॉल करें

Jenkins agents को एक Java Runtime Environment की आवश्यकता होती है। Jenkins की मौजूदा रिलीज़ Java 21 और Java 25 को सपोर्ट करती हैं (Java 17 अब सपोर्टेड नहीं है), इसलिए आसान प्रबंधन के लिए हम Homebrew के माध्यम से JDK 21 (LTS) इंस्टॉल करते हैं।

# Install Homebrew if not already present
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

# Install Java JDK 21 (LTS): current Jenkins releases support Java 21 and 25
brew install openjdk@21

# Create the symlink for system Java wrappers
sudo ln -sfn /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk \
  /Library/Java/JavaVirtualMachines/openjdk-21.jdk

# Add to PATH
echo 'export PATH="/opt/homebrew/opt/openjdk@21/bin:$PATH"' >> ~/.zprofile
source ~/.zprofile

# Verify Java installation (should report version 21)
java -version

# Tools used by the pipeline in Step 3 (CocoaPods only if you use a Podfile)
brew install xcbeautify cocoapods

चरण 2: Jenkins Agent कॉन्फ़िगर करें

एक Jenkins agent को कनेक्ट करने के दो प्राथमिक तरीके हैं: inbound agent (agent, controller से कनेक्ट करता है; पहले इसे JNLP कहा जाता था) और SSH (controller, agent से कनेक्ट करता है)। हम दोनों को कवर करते हैं।

विधि A: SSH Agent (अनुशंसित)

यह सबसे विश्वसनीय तरीका है। Jenkins, SSH के माध्यम से Mac से कनेक्ट होता है और agent प्रक्रिया को दूरस्थ रूप से प्रबंधित करता है।

# On the Mac mini: create a dedicated, non-admin Jenkins user
sudo sysadminctl -addUser jenkins -fullName "Jenkins Agent" -password "STRONG_PASSWORD_HERE"

# Create its home folder from the macOS template (includes ~/Library)
sudo createhomedir -c -u jenkins

# If Remote Login is limited to some users (System Settings > General >
# Sharing > Remote Login), allow the jenkins user to connect over SSH
sudo dseditgroup -o edit -a jenkins -t user com.apple.access_ssh

# Create SSH directory and add the Jenkins controller's public key
sudo mkdir -p /Users/jenkins/.ssh
sudo sh -c 'echo "ssh-rsa YOUR_JENKINS_CONTROLLER_PUBLIC_KEY" > /Users/jenkins/.ssh/authorized_keys'
sudo chmod 700 /Users/jenkins/.ssh
sudo chmod 600 /Users/jenkins/.ssh/authorized_keys
sudo chown -R jenkins:staff /Users/jenkins/.ssh

# Create a workspace directory
sudo mkdir -p /Users/jenkins/workspace
sudo chown jenkins:staff /Users/jenkins/workspace

अब Jenkins में agent कॉन्फ़िगर करें:

  1. Manage Jenkins > Nodes > New Node पर जाएं
  2. नाम mac-mini-m4 पर सेट करें
  3. Remote root directory को /Users/jenkins/workspace पर सेट करें
  4. Labels: mac macos apple-silicon m4 ios
  5. Launch method: Launch agents via SSH
  6. Host: आपके Mac Mini का IP पता
  7. Credentials: Mac पर authorized_keys से मेल खाने वाली SSH private key जोड़ें
  8. Host Key Verification Strategy: Known hosts file Verification Strategy। पहली बार लॉन्च करने से पहले, controller से उस user के रूप में एक बार कनेक्ट करें जो Jenkins चलाता है (ssh jenkins@YOUR_MAC_IP), ताकि Mac उसकी ~/.ssh/known_hosts में जुड़ जाए

विधि B: Inbound Agent (WebSocket)

इस विधि का उपयोग तब करें जब Jenkins controller, Mac तक सीधे नहीं पहुंच सकता (उदाहरण के लिए, Mac एक फ़ायरवॉल के पीछे है)। agent कनेक्शन शुरू करता है। -webSocket विकल्प के साथ यह केवल controller के वेब पोर्ट (HTTP या HTTPS) का उपयोग करता है, किसी अलग TCP agent पोर्ट का नहीं।

# First, create the node in Jenkins UI:
# Manage Jenkins > Nodes > New Node
# Launch method: "Launch agent by connecting it to the controller"
# Note the secret token from the node configuration page

# On the Mac mini, download the agent JAR as the jenkins user
# (download it again after each Jenkins controller upgrade)
sudo -u jenkins mkdir -p /Users/jenkins/jenkins-agent
sudo -u jenkins curl -fsSL -o /Users/jenkins/jenkins-agent/agent.jar \
  https://your-jenkins-url/jnlpJars/agent.jar

# Test the agent connection. -webSocket goes through the controller's
# HTTP(S) port: no separate TCP agent port to open
sudo -u jenkins /opt/homebrew/opt/openjdk@21/bin/java \
  -jar /Users/jenkins/jenkins-agent/agent.jar \
  -url https://your-jenkins-url \
  -secret YOUR_AGENT_SECRET \
  -name "mac-mini-m4" \
  -webSocket \
  -workDir "/Users/jenkins/workspace"

Inbound Agent को एक launchd सर्विस के रूप में चलाएं

agent, jenkins user के LaunchAgent के रूप में चलता है, इसलिए यह बूट पर नहीं, बल्कि उस user के लॉगिन पर शुरू होता है। हेडलेस Mac पर, jenkins user के लिए Automatic login सक्षम करें (System Settings > Users & Groups; FileVault चालू रहते उपलब्ध नहीं), ताकि रीबूट के बाद agent फिर से ऑनलाइन आ जाए। यही लॉग-इन सत्र Simulators, UI tests और login keychain को वह user environment भी देता है जिसकी उन्हें ज़रूरत होती है।

# Create the LaunchAgent in the jenkins user's own Library
sudo -u jenkins mkdir -p /Users/jenkins/Library/LaunchAgents
sudo -u jenkins tee /Users/jenkins/Library/LaunchAgents/io.jenkins.agent.plist > /dev/null <<'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>io.jenkins.agent</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/opt/openjdk@21/bin/java</string>
        <string>-jar</string>
        <string>/Users/jenkins/jenkins-agent/agent.jar</string>
        <string>-url</string>
        <string>https://your-jenkins-url</string>
        <string>-secret</string>
        <string>YOUR_AGENT_SECRET</string>
        <string>-name</string>
        <string>mac-mini-m4</string>
        <string>-webSocket</string>
        <string>-workDir</string>
        <string>/Users/jenkins/workspace</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/jenkins/jenkins-agent/stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/jenkins/jenkins-agent/stderr.log</string>
</dict>
</plist>
EOF

# The agent loads at every login of the jenkins user. On a headless Mac,
# connect over VNC as an admin, open System Settings > Users & Groups and set
# "Automatically log in as" to jenkins (unavailable while FileVault is on),
# then restart:
sudo shutdown -r now

# After the restart, verify it's running
sudo launchctl print gui/$(id -u jenkins)/io.jenkins.agent | grep state

चरण 3: iOS Build Pipeline बनाएं

अपने रिपॉजिटरी रूट में एक Jenkinsfile बनाएं। यह डिक्लेरेटिव पाइपलाइन आपके iOS ऐप को बनाती, टेस्ट करती और वैकल्पिक रूप से आर्काइव करती है।

pipeline {
    agent { label 'mac && m4' }

    environment {
        // Pick the Xcode for every stage without sudo (path from: xcodes installed)
        DEVELOPER_DIR = '/Applications/Xcode-27.0.0.app/Contents/Developer'
        SCHEME        = 'MyApp'
        // Not WORKSPACE: Jenkins already uses it for the job's workspace path
        XC_WORKSPACE  = 'MyApp.xcworkspace'
        // Any device listed by: xcrun simctl list devices available
        DESTINATION   = 'platform=iOS Simulator,name=iPhone 17,OS=latest'
        // Relative to the job workspace, kept between builds
        DERIVED_DATA  = 'DerivedData'
    }

    options {
        timeout(time: 30, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Install Dependencies') {
            steps {
                sh '''
                    # Homebrew tools are not on the PATH of a non-interactive SSH session
                    export PATH="/opt/homebrew/bin:$PATH"

                    # Xcode comes from DEVELOPER_DIR: no sudo xcode-select in a pipeline
                    xcodebuild -version

                    # Install CocoaPods dependencies
                    if [ -f "Podfile" ]; then
                        pod install --repo-update
                    fi
                '''
            }
        }

        stage('Build') {
            steps {
                sh '''
                    # Jenkins runs sh -xe: without pipefail, xcbeautify's exit status would hide a failed build
                    set -o pipefail
                    export PATH="/opt/homebrew/bin:$PATH"
                    xcodebuild build \
                        -workspace "${XC_WORKSPACE}" \
                        -scheme "${SCHEME}" \
                        -destination "${DESTINATION}" \
                        -derivedDataPath "${DERIVED_DATA}" \
                        CODE_SIGNING_ALLOWED=NO \
                        | xcbeautify
                '''
            }
        }

        stage('Test') {
            steps {
                sh '''
                    set -o pipefail
                    export PATH="/opt/homebrew/bin:$PATH"
                    rm -rf TestResults.xcresult
                    xcodebuild test \
                        -workspace "${XC_WORKSPACE}" \
                        -scheme "${SCHEME}" \
                        -destination "${DESTINATION}" \
                        -derivedDataPath "${DERIVED_DATA}" \
                        -resultBundlePath "TestResults.xcresult" \
                        -parallel-testing-enabled YES \
                        | xcbeautify --report junit --report-path build/reports
                '''
            }
            post {
                always {
                    junit allowEmptyResults: true, testResults: 'build/reports/junit.xml'
                    archiveArtifacts artifacts: 'TestResults.xcresult/**', allowEmptyArchive: true
                }
            }
        }

        stage('Archive') {
            when {
                branch 'main'
            }
            steps {
                sh '''
                    set -o pipefail
                    export PATH="/opt/homebrew/bin:$PATH"
                    # Unsigned archive: checks that the Release build compiles.
                    # Fastlane signs and uploads the real build in Step 4.
                    xcodebuild archive \
                        -workspace "${XC_WORKSPACE}" \
                        -scheme "${SCHEME}" \
                        -archivePath "${DERIVED_DATA}/MyApp.xcarchive" \
                        -destination "generic/platform=iOS" \
                        CODE_SIGNING_ALLOWED=NO \
                        | xcbeautify
                '''
            }
        }
    }

    post {
        success {
            echo 'Build and tests passed!'
        }
        failure {
            echo 'Build or tests failed.'
        }
        cleanup {
            sh 'xcrun simctl shutdown all 2>/dev/null || true'
        }
    }
}

चरण 4: डिप्लॉयमेंट के लिए Fastlane एकीकृत करें

Fastlane, कोड साइनिंग और TestFlight डिप्लॉयमेंट को सरल बनाता है। इसे Mac पर एक बार इंस्टॉल करें:

# Install Fastlane on the Mac mini (one-time setup)
brew install fastlane

अपनी Jenkinsfile में एक deploy stage जोड़ें:

        stage('Deploy to TestFlight') {
            when {
                branch 'main'
            }
            environment {
                APP_STORE_CONNECT_API_KEY_ID     = credentials('app-store-key-id')
                APP_STORE_CONNECT_API_ISSUER_ID  = credentials('app-store-issuer-id')
                APP_STORE_CONNECT_API_KEY_CONTENT = credentials('app-store-key-content')
                MATCH_PASSWORD                   = credentials('match-password')
            }
            steps {
                sh '''
                    export PATH="/opt/homebrew/bin:$PATH"
                    fastlane beta
                '''
            }
        }

और एक मेल खाती fastlane/Fastfile बनाएं:

default_platform(:ios)

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    setup_ci

    # Fetch code signing certificates via match
    match(type: "appstore", readonly: true)

    # Increment build number
    increment_build_number(
      build_number: ENV["BUILD_NUMBER"]
    )

    # Build the app
    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: "DerivedData"
    )

    # Upload to TestFlight
    upload_to_testflight(
      skip_waiting_for_build_processing: true,
      api_key: app_store_connect_api_key(
        key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
        issuer_id: ENV["APP_STORE_CONNECT_API_ISSUER_ID"],
        key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"]
      )
    )
  end
end

सुरक्षा सर्वोत्तम अभ्यास

  • एक समर्पित Jenkins उपयोगकर्ता का उपयोग करें

    Jenkins agent को कभी भी root के रूप में न चलाएं। न्यूनतम परमिशन के साथ एक समर्पित उपयोगकर्ता बनाएं।

  • सीक्रेट्स को Jenkins Credentials में स्टोर करें

    Jenkinsfiles में कभी भी सर्टिफिकेट, API keys या पासवर्ड हार्डकोड न करें। Jenkins Credentials plugin का उपयोग करें।

  • केवल SSH key-आधारित प्रमाणीकरण सक्षम करें

    SSH के लिए पासवर्ड प्रमाणीकरण अक्षम करें। Ed25519 या RSA 4096-bit keys का उपयोग करें।

  • macOS फ़ायरवॉल का उपयोग करें

    इनकमिंग कनेक्शन को केवल Jenkins controller IP तक सीमित करें। My Remote Mac, API एक्सेस के साथ एक मैनेज्ड फ़ायरवॉल प्रदान करता है।

  • macOS और Xcode को अपडेट रखें

    सुरक्षा पैच तुरंत लागू करें। अपडेट की जांच के लिए softwareupdate -l का उपयोग करें।

सामान्य प्रश्न

क्या मैं Jenkins controller को उसी Mac पर चला सकता हूं?
हां, लेकिन हम उन्हें अलग करने की अनुशंसा करते हैं। controller को एक Linux VM या Docker कंटेनर पर चलाएं, और Mac का उपयोग विशेष रूप से एक build agent के रूप में करें। इससे भारी बिल्ड के दौरान संसाधन प्रतिस्पर्धा से बचा जा सकता है।
एक Mac Mini M4 कितने समवर्ती बिल्ड संभाल सकता है?
16GB RAM वाला एक Mac Mini M4 आराम से 2 समवर्ती Xcode बिल्ड संभाल सकता है। 24GB के साथ, आप 3 तक बढ़ा सकते हैं। 48GB वाला M4 Pro आसानी से 4+ समवर्ती बिल्ड संभालता है। अपनी Jenkins node सेटिंग्स में तदनुसार "Number of executors" कॉन्फ़िगर करें।
क्या मुझे iOS सिम्युलेटर के लिए एक GUI सत्र की आवश्यकता है?
नहीं। iOS सिम्युलेटर SSH पर हेडलेस मोड में काम करते हैं। हालांकि, यदि आपको एक GUI की आवश्यकता है (उदाहरण के लिए, UI टेस्टिंग स्क्रीनशॉट के लिए), तो Mac में लॉग इन करने के लिए VNC का उपयोग करें और सुनिश्चित करें कि एक GUI सत्र सक्रिय है।
मैं एक हेडलेस सर्वर पर कोड साइनिंग कैसे संभालूं?
एक Git repo या क्लाउड स्टोरेज से सर्टिफिकेट सिंक करने के लिए Fastlane Match का उपयोग करें। वैकल्पिक रूप से, सर्टिफिकेट को macOS कीचेन में इम्पोर्ट करें और अपनी पाइपलाइन में security unlock-keychain का उपयोग करके इसे अनलॉक करें।

अपने Jenkins बिल्ड को सुपरचार्ज करने के लिए तैयार हैं?

अपनी Jenkins CI/CD पाइपलाइन के लिए एक समर्पित Mac Mini M4 प्राप्त करें। $85/माह से शुरू।

My Remote Mac की कीमतें USD में, कर अतिरिक्त।

और जानकारी चाहिए?

चरण-दर-चरण सेटअप, कॉन्फ़िगरेशन संदर्भ और समस्या-निवारण के लिए संपूर्ण दस्तावेज़ देखें।

दस्तावेज़ खोलें →