iOS için CI/CD: Pratik Bir Rehber
iOS için CI/CD: Fastlane, GitHub Actions ve Apple servisleriyle yayıma giden tüm hattın gerçekçi bir kurulumu.
1. Giriş: iOS’ta CI/CD Neden Zordur?
Sürekli entegrasyon ve sürekli teslim (CI/CD), her commit’in otomatik olarak derlenmesini, test edilmesini ve dağıtılmasını sağlar. Web veya backend projelerinde bu görece kolaydır. iOS’ta ise üç özel zorluk vardır: kod imzalama (code signing) Apple’ın sertifika ve profil sistemine bağlıdır, derleme yalnızca macOS makinelerinde yapılabilir ve derleme süreleri uzundur. Bu rehber, bu zorlukları pratik biçimde aşan bir pipeline kurmayı anlatır.
2. Pipeline’ın Aşamaları
Tipik bir iOS pipeline’ı şu sırayı izler:
- Checkout: Kodu çek ve bağımlılıkları çöz.
- Lint: Statik analiz çalıştır (SwiftLint).
- Test: Birim ve UI testlerini çalıştır.
- Build: Uygulamayı imzalayıp arşivle (
.ipa). - Deploy: TestFlight veya App Store Connect’e yükle.
Her aşama başarısız olursa pipeline durur. Bu sayede bozuk kod hiçbir zaman dağıtım aşamasına ulaşmaz.
3. Derleme Otomasyonu: Fastlane
Ham xcodebuild komutları güçlüdür ama uzun ve okunması zordur. Fastlane bu komutları okunabilir “lane”lere sarar. Aşağıda bir Fastfile örneği vardır:
# fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Testleri çalıştır"
lane :test do
run_tests(
scheme: "MyApp",
devices: ["iPhone 15"],
clean: true
)
end
desc "TestFlight'a beta yükle"
lane :beta do
setup_ci
match(type: "appstore", readonly: true)
build_app(
scheme: "MyApp",
export_method: "app-store"
)
upload_to_testflight(skip_waiting_for_build_processing: true)
end
end
Karşılığında ham xcodebuild ile aynı işi yapmak şöyle görünürdü:
xcodebuild test \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-resultBundlePath ./TestResults \
CODE_SIGNING_ALLOWED=NO
4. Kod İmzalama: En Kritik Adım
CI’da en çok sorun çıkaran konu imzalamadır. Sertifikaları her geliştiricinin makinesinde elle yönetmek sürdürülemez. Fastlane’in match aracı, sertifika ve profilleri şifreli bir git deposunda saklar ve CI dahil her makineye aynısını dağıtır. Tek bir doğruluk kaynağı oluşturur.
# İlk kurulum (bir kez çalıştırılır)
fastlane match init
fastlane match appstore
# CI üzerinde (readonly modda)
fastlane match appstore --readonly
CI ortamında geçici bir anahtarlık (keychain) oluşturmak için setup_ci çağrılır. Bu, sertifikaların geçici ve izole bir alana yüklenmesini sağlar:
lane :beta do
setup_ci # geçici keychain oluşturur
match(type: "appstore", readonly: true)
# ...
end
5. GitHub Actions ile Tam Bir İş Akışı
Aşağıdaki workflow, bir PR açıldığında testleri, main dalına push yapıldığında ise TestFlight dağıtımını tetikler:
# .github/workflows/ios.yml
name: iOS CI
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
jobs:
test:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Bağımlılıkları önbelleğe al
uses: actions/cache@v4
with:
path: |
~/Library/Caches/org.swift.swiftpm
.build
key: spm-${{ hashFiles('**/Package.resolved') }}
- name: SwiftLint çalıştır
run: swiftlint --strict
- name: Testleri çalıştır
run: fastlane test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: TestFlight'a yükle
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.ASC_API_KEY }}
run: fastlane beta
deploy işi yalnızca test işi başarılı olduğunda ve dal main ise çalışır. Bu sıralama bozuk kodun yayınlanmasını imkansız kılar.
6. App Store Connect API ile Kimlik Doğrulama
Apple ID parolası ve iki faktörlü doğrulama CI için uygun değildir. Bunun yerine App Store Connect API anahtarı (ES256 JWT tabanlı) kullanılır. Bu anahtar bir kez oluşturulur ve CI’a güvenli bir secret olarak verilir:
# Fastfile içinde API anahtarını kullanma
lane :beta do
api_key = app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_CONTENT"], # .p8 içeriği
is_key_content_base64: true
)
build_app(scheme: "MyApp")
upload_to_testflight(api_key: api_key)
end
Bu yöntem, parola gerektirmediği için tamamen otomatik ve güvenlidir.
7. Sırların (Secrets) Yönetimi
CI’da hiçbir sertifika, parola veya anahtar düz metin olarak depoda bulunmamalıdır. Tüm hassas değerler CI sağlayıcısının secret deposunda tutulur ve çalışma anında ortam değişkeni olarak enjekte edilir. .p8 gibi dosyalar base64 ile kodlanıp tek bir secret olarak saklanabilir:
# Yerel makinede .p8'i base64'e çevir, çıktıyı secret olarak ekle
base64 -i AuthKey_ABC123.p8 | pbcopy
Pipeline içinde tekrar dosyaya çözmek gerekirse:
echo "$ASC_KEY_CONTENT" | base64 --decode > AuthKey.p8
Sırları log’a yazdırmaktan kaçının; çoğu CI sağlayıcısı secret değerlerini otomatik maskeler ama yine de dikkatli olmak gerekir.
8. Derleme Süresini Kısaltmak
iOS derlemeleri yavaştır ve CI dakikası maliyetlidir. Süreyi kısaltan başlıca teknikler:
- Bağımlılık önbelleği: Swift Package Manager bağımlılıklarını önbelleğe alın (yukarıdaki workflow’da gösterildi).
- Türev veri (derived data) önbelleği: Mümkünse derleme çıktılarını önbelleğe alın.
- Test bölme: UI testlerini ayrı bir işe ayırıp birim testlerinden bağımsız paralel çalıştırın.
- Sadece gerekeni çalıştır: PR’larda tüm cihaz matrisini değil, tek bir referans simülatörü hedefleyin.
# Birim ve UI testlerini paralel bölmeye örnek
strategy:
matrix:
test-plan: [UnitTests, UITests]
steps:
- run: fastlane test plan:${{ matrix.test-plan }}
9. Sürümleme ve Otomatik Yapı Numarası
Her TestFlight yüklemesi benzersiz bir yapı numarası (build number) gerektirir. Bunu elle yönetmek hataya açıktır. CI üzerinde otomatikleştirin:
lane :beta do
latest = latest_testflight_build_number(version: get_version_number)
increment_build_number(build_number: latest + 1)
build_app(scheme: "MyApp")
upload_to_testflight
end
Bu, App Store Connect’teki son yapı numarasını okur ve bir fazlasını atar, böylece çakışma asla yaşanmaz.
10. Sonuç
iOS’ta sağlam bir CI/CD pipeline’ı kurmak, üç temel zorluğu çözmekten geçer: imzalamayı match ile merkezileştirmek, kimlik doğrulamayı App Store Connect API anahtarıyla otomatikleştirmek ve derleme süresini önbellekleme ile yönetmek. Aşamaları lint, test, build ve deploy olarak sıralayıp her birini bir öncekinin başarısına bağladığınızda, bozuk kodun kullanıcıya ulaşması imkansız hale gelir. İyi kurulmuş bir pipeline, sürüm çıkarmayı stresli bir ritüelden tek bir merge işlemine indirger.
← Yazılara dön