Panduan - CI/CD

GitLab CI/CD Runner di Mac Mini M4: Panduan Lengkap

Instal dan konfigurasi runner GitLab CI/CD pada Mac Mini M4 khusus. Bangun aplikasi iOS secara native di Apple Silicon, jalankan pengujian pada simulator sungguhan, dan deploy ke TestFlight -- semuanya dari pipeline GitLab Anda.

Baca 30 menit Diperbarui Maret 2026

1. Mengapa GitLab Runner Self-Hosted di Mac?

GitLab menyediakan shared runner di Linux, tetapi membangun aplikasi iOS memerlukan macOS yang berjalan di perangkat keras Apple. Shared runner macOS milik GitLab sendiri terbatas dan mahal. Runner Mac Mini M4 self-hosted memberi Anda:

Menit CI/CD Tanpa Batas

Paket gratis GitLab mencakup 400 menit CI/CD pada shared runner. Pada self-hosted, tidak ada batas.

Apple Silicon Native

Bangun pada chip M4 yang sama dengan yang dijalankan perangkat pengguna Anda. Tanpa overhead terjemahan Rosetta.

Kontrol Penuh atas Lingkungan

Instal versi Xcode, simulator, alat, dan dependensi apa pun yang Anda butuhkan.

Cache Persisten

Cache DerivedData, paket SPM, dan CocoaPods tetap ada di antara setiap eksekusi pipeline.

2. Instal GitLab Runner

SSH ke Mac Mini M4 Anda dan instal GitLab Runner menggunakan Homebrew:

# Connect to your Mac Mini M4
ssh admin@your-server-ip

# Install Homebrew (if not already installed)
/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 GitLab Runner
brew install gitlab-runner

# Verify installation
gitlab-runner --version
# Version:      17.7.0
# Git revision:  ...
# Git branch:    17-7-stable
# GO version:    go1.22.10
# Built:         ...
# OS/Arch:       darwin/arm64

Instal sebagai Layanan macOS

# Install the runner as a launchd service
# This ensures it starts automatically on boot
brew services start gitlab-runner

# Verify the service is running
brew services list | grep gitlab-runner
# gitlab-runner started admin ~/Library/LaunchAgents/homebrew.mxcl.gitlab-runner.plist

# Check runner status
gitlab-runner status
# gitlab-runner: Service is running

3. Daftarkan Runner

Buka proyek (atau grup) GitLab Anda dan navigasikan ke Settings > CI/CD > Runners > New project runner. Salin token registrasi.

Daftarkan dengan Alur Registrasi Runner Baru (GitLab 16+)

# Register the runner using the authentication token from GitLab UI
# (GitLab 16+ uses authentication tokens instead of registration tokens)
gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --token "YOUR_RUNNER_AUTHENTICATION_TOKEN" \
  --executor "shell" \
  --description "mac-mini-m4-runner" \
  --tag-list "macos,apple-silicon,m4,ios,xcode"

Daftarkan dengan Token Registrasi Lama (GitLab 15 dan sebelumnya)

# For older GitLab instances using registration tokens
gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --registration-token "YOUR_REGISTRATION_TOKEN" \
  --executor "shell" \
  --description "mac-mini-m4-runner" \
  --tag-list "macos,apple-silicon,m4,ios,xcode" \
  --run-untagged="false"

Verifikasi Konfigurasi Runner

# View the runner config file
cat ~/.gitlab-runner/config.toml

# Expected output:
# concurrent = 2
# check_interval = 0
#
# [session_server]
#   session_timeout = 1800
#
# [[runners]]
#   name = "mac-mini-m4-runner"
#   url = "https://gitlab.com/"
#   token = "..."
#   executor = "shell"
#   [runners.cache]
#     MaxUploadedArchiveSize = 0

# Adjust concurrency based on your hardware:
# Mac Mini M4 (16GB): concurrent = 2
# Mac Mini M4 Pro (24GB): concurrent = 3
# Mac Mini M4 Pro (48GB): concurrent = 4

Edit ~/.gitlab-runner/config.toml untuk menyesuaikan konkurensi:

# Edit the config
nano ~/.gitlab-runner/config.toml

# Set concurrent to match your hardware capacity
concurrent = 2

# Restart the runner to apply changes
gitlab-runner restart

Runner sekarang seharusnya muncul sebagai Online di proyek GitLab Anda pada Settings > CI/CD > Runners.

4. Buat .gitlab-ci.yml untuk iOS

Buat file .gitlab-ci.yml di root repositori Anda. Pipeline lengkap ini membangun, menguji, dan men-deploy aplikasi iOS Anda:

# .gitlab-ci.yml

stages:
  - setup
  - build
  - test
  - deploy

variables:
  SCHEME: "MyApp"
  WORKSPACE: "MyApp.xcworkspace"
  DESTINATION: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2"
  DERIVED_DATA: "${CI_PROJECT_DIR}/DerivedData"

# Only run on our Mac runner
default:
  tags:
    - macos
    - m4

# ---- SETUP ----

setup:
  stage: setup
  script:
    - sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
    - xcodebuild -version
    - swift --version
    # Install CocoaPods if using Podfile
    - |
      if [ -f "Podfile" ]; then
        pod install --repo-update
      fi
  cache:
    key: pods-${CI_COMMIT_REF_SLUG}
    paths:
      - Pods/
      - .spm-cache/

# ---- BUILD ----

build:
  stage: build
  needs: ["setup"]
  script:
    - |
      xcodebuild build \
        -workspace "${WORKSPACE}" \
        -scheme "${SCHEME}" \
        -destination "${DESTINATION}" \
        -derivedDataPath "${DERIVED_DATA}" \
        -clonedSourcePackagesDirPath ".spm-cache" \
        CODE_SIGNING_ALLOWED=NO \
        | xcbeautify
  cache:
    key: derived-data-${CI_COMMIT_REF_SLUG}
    paths:
      - DerivedData/
      - .spm-cache/
  artifacts:
    paths:
      - DerivedData/
    expire_in: 1 hour

# ---- TEST ----

unit_tests:
  stage: test
  needs: ["build"]
  script:
    - |
      xcodebuild test \
        -workspace "${WORKSPACE}" \
        -scheme "${SCHEME}" \
        -destination "${DESTINATION}" \
        -derivedDataPath "${DERIVED_DATA}" \
        -resultBundlePath "TestResults.xcresult" \
        -parallel-testing-enabled YES \
        | xcbeautify
  artifacts:
    when: always
    paths:
      - TestResults.xcresult/
    reports:
      junit: TestResults.xcresult/report.junit
    expire_in: 7 days
  after_script:
    - xcrun simctl shutdown all 2>/dev/null || true

# ---- DEPLOY ----

deploy_testflight:
  stage: deploy
  needs: ["unit_tests"]
  only:
    - main
  script:
    - |
      # Install or update Fastlane
      which fastlane || brew install fastlane

      # Run Fastlane beta lane
      fastlane beta
  environment:
    name: testflight
  variables:
    MATCH_PASSWORD: ${MATCH_PASSWORD}
    APP_STORE_CONNECT_API_KEY_ID: ${APP_STORE_KEY_ID}
    APP_STORE_CONNECT_API_ISSUER_ID: ${APP_STORE_ISSUER_ID}
    APP_STORE_CONNECT_API_KEY_CONTENT: ${APP_STORE_KEY_CONTENT}

Tambahkan Variabel CI/CD di GitLab

Navigasikan ke Settings > CI/CD > Variables di proyek GitLab Anda dan tambahkan variabel-variabel ini sebagai "Masked" dan "Protected":

  • MATCH_PASSWORD - Kata sandi untuk enkripsi Fastlane Match
  • APP_STORE_KEY_ID - ID Kunci API App Store Connect
  • APP_STORE_ISSUER_ID - Issuer ID App Store Connect
  • APP_STORE_KEY_CONTENT - Isi file kunci .p8

5. Optimalkan Performa

Konfigurasi Cache GitLab

GitLab Runner mendukung caching lokal untuk shell executor. Karena runner Anda persisten, cache lokal sangat efisien:

# In .gitlab-ci.yml, configure cache per branch:
cache:
  key: "${CI_COMMIT_REF_SLUG}"
  paths:
    - DerivedData/
    - .spm-cache/
    - Pods/
  policy: pull-push

# For test jobs that don't modify cache, use pull-only:
unit_tests:
  cache:
    key: "${CI_COMMIT_REF_SLUG}"
    paths:
      - DerivedData/
    policy: pull

Gunakan Artifact untuk Data Antar-Stage

# Pass build artifacts between stages efficiently
build:
  artifacts:
    paths:
      - DerivedData/Build/Products/
    expire_in: 2 hours

# The test stage receives the built products without rebuilding
test:
  needs: ["build"]  # only download artifacts from the build job
  script:
    - xcodebuild test-without-building \
        -scheme "${SCHEME}" \
        -destination "${DESTINATION}" \
        -derivedDataPath "${DERIVED_DATA}"

Eksekusi Pengujian Paralel

# Split tests across parallel jobs using GitLab's parallel keyword
unit_tests:
  stage: test
  parallel: 2
  script:
    - |
      # Use test plan partitioning or custom splitting
      xcodebuild test \
        -workspace "${WORKSPACE}" \
        -scheme "${SCHEME}" \
        -destination "${DESTINATION}" \
        -derivedDataPath "${DERIVED_DATA}" \
        -parallel-testing-enabled YES \
        -maximum-parallel-testing-workers 4

Pembersihan Cache Terjadwal

# On the Mac Mini, set up a weekly cleanup cron job
crontab -e

# Add these lines:
# Clean DerivedData older than 7 days every Sunday at 3 AM
0 3 * * 0 find ~/builds/*/DerivedData -maxdepth 0 -mtime +7 -exec rm -rf {} + 2>/dev/null

# Clean old GitLab Runner builds older than 14 days
0 4 * * 0 find ~/builds -maxdepth 2 -mtime +14 -type d -exec rm -rf {} + 2>/dev/null

# Clean Homebrew cache monthly
0 5 1 * * /opt/homebrew/bin/brew cleanup --prune=30 2>/dev/null

6. Pemecahan Masalah

Runner menampilkan "offline" di GitLab

Periksa status layanan runner dan log-nya:

# Check service status
brew services list | grep gitlab-runner

# View logs
cat /usr/local/var/log/gitlab-runner.log

# Restart the service
brew services restart gitlab-runner

# Verify connectivity to GitLab
gitlab-runner verify

Error permission denied saat build

Runner mungkin memerlukan akses ke direktori developer Xcode:

# Ensure the runner user has Xcode access
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
sudo xcodebuild -license accept

# If using simulators, ensure the user can access them
xcrun simctl list devices

Cache tidak dipulihkan

Pastikan cache key konsisten dan path-nya ada:

# Check cache directory permissions
ls -la ~/builds/

# The shell executor stores caches locally by default
# Verify the cache directory in config.toml:
cat ~/.gitlab-runner/config.toml

# Ensure [runners.cache] section has the right settings
# For local caching (most efficient for persistent runners):
# [runners.cache]
#   Type = ""  # empty = local cache

Build Xcode macet atau timeout

Ini sering disebabkan oleh prompt akses keychain atau masalah simulator:

# Unlock the keychain before builds
security unlock-keychain -p "YOUR_PASSWORD" ~/Library/Keychains/login.keychain-db

# Kill stuck simulators
xcrun simctl shutdown all
pkill -f "Simulator.app" 2>/dev/null || true

# Set a build timeout in .gitlab-ci.yml
build:
  timeout: 30 minutes

7. FAQ

Bisakah saya menggunakan Docker executor di macOS?

Docker di macOS menjalankan container Linux dalam VM, yang tidak dapat mengakses API macOS, Xcode, atau simulator iOS. Untuk build iOS, Anda harus menggunakan shell executor. Docker cocok untuk Swift sisi server atau tugas berbasis Linux lainnya yang berjalan bersama runner Mac Anda.

Bagaimana cara mendaftarkan runner untuk grup GitLab?

Buka Settings > CI/CD > Runners > New group runner pada grup GitLab Anda. Gunakan token group runner alih-alih token proyek. Ini membuat runner tersedia untuk semua proyek dalam grup tersebut.

Haruskah saya menggunakan shell executor atau SSH executor?

Gunakan shell executor. Ia menjalankan perintah langsung di Mac, yang memberikan akses penuh ke Xcode, simulator, dan keychain. SSH executor ditujukan untuk mesin jarak jauh, yang tidak diperlukan ketika runner sudah berada di Mac.

Bisakah saya menjalankan runner GitLab dan GitHub Actions di Mac yang sama?

Ya. Kedua runner ringan dan dapat berdampingan di Mac Mini M4 yang sama. Cukup pastikan untuk memperhitungkan penggunaan sumber daya gabungan saat menetapkan tingkat konkurensi untuk masing-masing runner.

Bagaimana cara memperbarui GitLab Runner?

# Update via Homebrew
brew upgrade gitlab-runner

# Restart the service
brew services restart gitlab-runner

# Verify the new version
gitlab-runner --version

Mencari GitLab Runner yang dikelola sepenuhnya? Coba Cloud-Runner

Lewati proses pengaturan sepenuhnya. Cloud-Runner menyediakan GitLab Runner khusus yang telah dikonfigurasi sebelumnya pada perangkat keras Mac — tanpa instalasi atau pemeliharaan.

Panduan Terkait

Siap Memberdayakan Pipeline GitLab Anda?

Dapatkan Mac Mini M4 khusus untuk runner GitLab CI/CD Anda. Build tanpa batas mulai dari $85/bulan.

Butuh detail lebih lanjut?

Jelajahi dokumentasi lengkap untuk panduan langkah demi langkah, referensi konfigurasi, dan pemecahan masalah.

Buka dokumentasi →