DevOps 1: iOS CI/CD 2026 — Xcode Cloud, GitHub Actions va kalitsiz imzolash
🛠️ DevOps Engineering
DevOps 1: iOS CI/CD 2026 — Xcode Cloud, GitHub Actions va kalitsiz imzolash
Holat: 2026-yil sentabr (Xcode 26.x, Xcode 27, iOS 27). Narxlar, runner nomlari va versiyalar tez o'zgaradi — har bir raqam yonida manba bor, ishlatishdan oldin rasmiy sahifani tekshiring.
Kursdagi CI/CD — Fastlane darsi asoslarni (Fastfile, match, gym, pilot) o'rgatadi. Bu dars bir qadam chuqurroq: 2026-yildagi haqiqiy pipeline qanday quriladi.
Mundarija
- Pipeline anatomiyasi
- Xcode Cloud — chuqurroq
- GitHub Actions macOS runner'lari (2026)
- Kalitsiz code signing: App Store Connect API key
- [Build'ni yuklash:
xcodebuild -exportArchive](#upload) - fastlane 2026-yilda
- TestFlight chegaralari
- Privacy Manifest — CI'ni to'xtatadigan darvoza
- To'liq workflow: PR'da test, tag'da TestFlight
- Qaysi CI qachon?
- Xulosa
- Manbalar
1. Pipeline anatomiyasi {#pipeline}
iOS uchun CI/CD — bu bir xil bosqichlarni har safar, bir xil tartibda, odam aralashmasdan bajarish:
push / PR → lint → build → test → archive → sign → upload → TestFlight / App Store
- CI (Continuous Integration) — har bir PR'da lint + build + test. Maqsad:
maindoim yig'iladigan (buildable) holatda bo'lsin. - CD (Continuous Delivery) — tag yoki release branch'da archive + imzolash + App Store Connect'ga yuklash.
Qoida: pipeline deterministik bo'lishi kerak — bir xil commit har doim bir xil natija beradi. Buning uchun Xcode versiyasi, dependency versiyalari (Package.resolved, Gemfile.lock) va tool versiyalari qat'iy belgilab qo'yiladi.
2. Xcode Cloud — chuqurroq {#xcode-cloud}
Xcode Cloud — Apple'ning App Store Connect'ga ulangan CI/CD xizmati. Workflow to'rt qismdan iborat:
| Qism | Nima qiladi |
|---|---|
| Environment | Xcode va macOS versiyasi, environment variable'lar (secret sifatida belgilash mumkin — log'da yashiriladi) |
| Start Conditions | Qachon ishga tushadi: branch o'zgarishi, pull request, tag, jadval (schedule) yoki qo'lda |
| Actions | Build, Test, Analyze, Archive |
| Post-Actions | TestFlight'ga tarqatish, xabarnoma yuborish |
Narx (2026)
Apple Developer Program a'zoligi bilan oyiga 25 compute-soat bepul. Undan ko'pi obuna bilan:
| Oylik soat | Narx |
|---|---|
| 100 | $49.99 / oy |
| 250 | $99.99 / oy |
| 1 000 | $399.99 / oy |
| 10 000 | $3 999.99 / oy |
Ishlatilmagan soat keyingi oyga o'tmaydi. Manba: Apple — Get started with Xcode Cloud.
Custom build script'lar: ci_scripts/
Loyiha (yoki workspace) turgan papkada ci_scripts papkasini yarating. Xcode Cloud uch nomdagi script'ni o'zi taniydi:
| Fayl | Qachon ishlaydi |
|---|---|
ci_post_clone.sh | repo klonlangandan keyin, dependency'lar (SPM) yechilishidan oldin |
ci_pre_xcodebuild.sh | har bir xcodebuild chaqiruvidan oldin |
ci_post_xcodebuild.sh | xcodebuild tugagandan keyin (build yiqilsa ham) |
Script bajariladigan bo'lishi shart:
chmod +x ci_scripts/ci_post_clone.sh
Namuna — lint'ni build'dan oldin "darvoza" qilish:
#!/bin/sh
# ci_scripts/ci_post_clone.sh
# Xcode Cloud bu faylni repo klonlangandan keyin ishga tushiradi.
set -eu
# Script ci_scripts papkasidan ishga tushadi — loyiha ildiziga o'tamiz.
cd "$(dirname "$0")/.."
echo "Xcode Cloud build #${CI_BUILD_NUMBER:-local}"
# Lint xatosi bo'lsa build shu yerda to'xtaydi.
brew install swiftlint
swiftlint lint --strict
Muhim: CI_BUILD_NUMBER, CI_XCODEBUILD_ACTION, CI_BRANCH, CI_TAG kabi o'zgaruvchilarni Xcode Cloud o'zi beradi. To'liq ro'yxat: Environment variable reference. Script ichida sudo ishlatib bo'lmaydi.
Cheklovlar
- Faqat Apple platformalari (iOS, macOS, tvOS, watchOS, visionOS) — backend yoki Android uchun emas.
- O'z serveringizda (self-hosted) ishlatib bo'lmaydi, build mashinasiga SSH yo'q.
- Kuchli tomoni: signing va TestFlight'ga tarqatish App Store Connect ichida, deyarli sozlashsiz.
3. GitHub Actions macOS runner'lari (2026) {#github-actions}
| Label | Holat |
|---|---|
macos-15 | Apple Silicon, faol |
macos-26 | Apple Silicon, 2026-02-26 dan GA |
macos-15-intel, macos-26-intel | Intel varianti ham bor |
macos-13 | 2025-12-04 da butunlay o'chirilgan — eski workflow'larni ko'chiring |
Standart Apple Silicon runner: 3 core (M1), 7 GB RAM, 14 GB SSD. Manba: GitHub-hosted runners, macOS 26 GA, macOS 13 retirement.
Narx: macOS daqiqasi ~10 barobar qimmat
| Runner | Narx (1 daqiqa) |
|---|---|
| Linux 2-core | $0.006 |
| Windows 2-core | $0.010 |
| macOS | $0.062 |
Manba: About billing for GitHub Actions. Amaliy xulosa: Linux'da bajarsa bo'ladigan ishni (masalan backend testlari, markdown lint) macOS runner'ga qo'ymang.
Xcode versiyasini tanlash
Har bir runner image'da bir nechta Xcode o'rnatilgan. Qaysilari borligini image'ning README faylidan ko'ring (runner-images) va aniq versiyani tanlang:
sudo xcode-select -s /Applications/Xcode_26.4.app
xcodebuild -version
Cache
actions/cache bilan SPM paketlarini saqlash build'ni tezlashtiradi. Bitta repo uchun standart limit 10 GB, 7 kun ishlatilmagan cache o'chiriladi (cache limitlari). To'liq DerivedData odatda bir necha GB bo'lgani uchun faqat SPM paketlarini cache qilish ko'proq foyda beradi.
4. Kalitsiz code signing: App Store Connect API key {#signing}
Eski usul — CI'da Apple ID + parol + 2FA sessiyasi. Bu mo'rt: sessiya muddati tugaydi, 2FA kodi so'raladi. Zamonaviy usul — App Store Connect API key (.p8 fayl):
- App Store Connect → Users and Access → Integrations → App Store Connect API → yangi Team Key. Kalitni faqat Account Holder yoki Admin yarata oladi.
- Kalitga eng kichik yetarli rolni bering: TestFlight'ga yuklash uchun odatda App Manager yetarli.
- Uch qiymatni CI secret sifatida saqlang:
ASC_KEY_ID,ASC_ISSUER_ID,ASC_KEY_P8_BASE64(.p8faylning base64 ko'rinishi)..p8fayl faqat bir marta yuklab olinadi — uni xavfsiz joyda saqlang.
Shundan keyin xcodebuild o'zi sertifikat va profillarni yaratadi yoki yangilaydi:
xcodebuild archive \
-project MyApp.xcodeproj -scheme MyApp -configuration Release \
-archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
-allowProvisioningUpdates \
-authenticationKeyPath "$HOME/private_keys/AuthKey_${ASC_KEY_ID}.p8" \
-authenticationKeyID "$ASC_KEY_ID" \
-authenticationKeyIssuerID "$ASC_ISSUER_ID"
-allowProvisioningUpdates — xcodebuildga Apple Developer sayti bilan gaplashishga ruxsat beradi. Automatic signing target'larida profil, App ID va sertifikatni yaratadi yoki yangilaydi; manual signing'da esa yetishmayotgan profillarni yuklab oladi. Bu ma'lumot xcodebuild -help chiqishidan olingan (Xcode 26.5).
Qo'lda boshqariladigan sertifikat bo'lsa: vaqtinchalik keychain
Agar jamoa .p12 sertifikatni o'zi boshqarsa, CI'da uni vaqtinchalik keychain'ga import qilib, ish tugagach o'chiriladi:
#!/bin/bash
# CI: .p12 sertifikatni vaqtinchalik keychain'ga import qilish
set -euo pipefail
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
CERT_PATH="$RUNNER_TEMP/cert.p12"
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH"
Ish oxirida (xato bo'lsa ham) keychain'ni o'chiring: security delete-keychain "$KEYCHAIN_PATH".
5. Build'ni yuklash: xcodebuild -exportArchive {#upload}
ExportOptions.plistda destination = upload bo'lsa, xcodebuild -exportArchive archive'ni eksport qiladi va App Store Connect'ga yuklaydi. Alohida Transporter chaqirish shart emas.
<?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>method</key>
<string>app-store-connect</string>
<key>destination</key>
<string>upload</string>
<key>manageAppVersionAndBuildNumber</key>
<true/>
</dict>
</plist>
Xcode 15.3'dan boshlab method qiymatlari yangilandi. app-store → **app-store-connect**, ad-hoc → **release-testing**, development → **debugging**. Eskilari hali ishlaydi, lekin deprecated. Qo'shimcha foydali kalitlar:
manageAppVersionAndBuildNumber— standart qiymatiYES: yuklashda build raqamini Xcode o'zi boshqaradi.testFlightInternalTestingOnly—YESbo'lsa, bu build'ni external TestFlight yoki App Store'ga chiqarib bo'lmaydi. PR va dev branch build'lari uchun qulay.
Barcha kalitlarni o'zingiz ko'rishingiz mumkin: xcodebuild -help → "Available keys for -exportOptionsPlist".
altool haqida:
xcrun altool --notarize-app2023-11-01 da o'chirilgan — bu faqat macOS notarizatsiya funksiyasi, uning o'rniganotarytoolishlatiladi. App Store Connect'ga yuklash uchunxcodebuild -exportArchive(yoki Transporter) — eng sodda yo'l. Manba: Apple — notarization tool deprecation.
6. fastlane 2026-yilda {#fastlane}
- 2021-yil oxirida Google homiyligi to'xtadi. Loyiha 2023-10-27 dan Mobile Native Foundation (MNF) qaramog'ida. Hamjamiyat uni faol qo'llab-quvvatlaydi. Manba: MNF discussion #194.
- CI'da autentifikatsiya uchun
app_store_connect_api_keyaction ishlatiladi — Apple ID/2FA kerak emas:
# fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Build yig'ib TestFlight'ga yuklash"
lane :beta do
api_key = app_store_connect_api_key(
key_id: ENV.fetch("ASC_KEY_ID"),
issuer_id: ENV.fetch("ASC_ISSUER_ID"),
key_content: ENV.fetch("ASC_KEY_P8_BASE64"),
is_key_content_base64: true
)
build_app(scheme: "MyApp", export_method: "app-store")
upload_to_testflight(api_key: api_key, skip_waiting_for_build_processing: true)
end
end
- Xavfsizlik darsi (2026): rubyzip kutubxonasida path-traversal zaifligi topilgan (CVE-2026-85396, HIGH). Uning tuzatilgan versiyasi fastlane'ning dependency cheklovi bilan to'qnashadi. 2026-sentabr holatiga ko'ra muammo issue #30175da hali ochiq. Xulosa: fastlane'ni doim Bundler orqali, versiyasi
Gemfile.lockda qotirilgan holda ishga tushiring (bundle exec fastlane beta) va dependency yangilanishlarini kuzatib boring.
Qachon fastlane kerak? Screenshot generatsiyasi (snapshot), metadata yuklash (deliver) yoki ko'p lane'li murakkab jarayonlar bo'lsa — foydali. Faqat "archive + upload" kerak bo'lsa, fastlane'siz xcodebuild yetarli (4–5 bo'limlar).
7. TestFlight chegaralari {#testflight}
| Parametr | Qiymat |
|---|---|
| Internal tester'lar | 100 tagacha (App Store Connect jamoa a'zolari), har biri 30 tagacha qurilmada |
| External tester'lar | 10 000 tagacha, 100 tagacha guruhga bo'linadi |
| Beta App Review | External tester'larga versiyaning birinchi build'ini berishdan oldin talab qilinadi |
| Build muddati | Yuklangandan 90 kun keyin build ishlamay qoladi |
Manbalar: TestFlight limits 2026, TestFlight overview.
8. Privacy Manifest — CI'ni to'xtatadigan darvoza {#privacy-manifest}
2024-yil 1-maydan App Store Connect quyidagi holatlardagi yuklashni rad etadi:
- ilova yoki SDK Required Reason API ishlatib, sababini
PrivacyInfo.xcprivacyda ko'rsatmagan bo'lsa (masalanUserDefaults, fayl timestamp'lari, disk hajmi, system boot time); - Apple'ning "keng tarqalgan uchinchi tomon SDK'lari" ro'yxatidagi kutubxona o'z privacy manifest'i va imzosisiz qo'shilgan bo'lsa.
Manba: Apple — Privacy manifest requirements.
Minimal namuna — ilova UserDefaultsdan faqat o'z ma'lumotlari uchun foydalanadi (sabab kodi CA92.1):
<?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>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>
CI uchun amaliy qoida: yangi SDK qo'shadigan PR'da manifest'ni ham ko'rib chiqing. Xcode Organizer'da archive'ni o'ng tugma bilan bosib Generate Privacy Report tanlasangiz, barcha manifest'lar jamlangan hisobot chiqadi.
9. To'liq workflow: PR'da test, tag'da TestFlight {#workflow}
Fayl: .github/workflows/ios.yml. Kerakli secret'lar: ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_P8_BASE64. Loyiha automatic signingda bo'lsin va repo'da ExportOptions.plist (5-bo'lim) tursin.
name: iOS CI
on:
pull_request:
branches: [main]
push:
tags: ["v*"]
concurrency:
group: ios-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
if: github.event_name == 'pull_request'
runs-on: macos-26
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_26.4.app
- name: Cache SPM packages
uses: actions/cache@v4
with:
path: ~/Library/Developer/Xcode/DerivedData/**/SourcePackages
key: spm-${{ runner.os }}-${{ hashFiles('**/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- name: Test
run: |
set -o pipefail
xcodebuild test \
-project MyApp.xcodeproj -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
-resultBundlePath TestResults.xcresult
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults.xcresult
testflight:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: macos-26
timeout-minutes: 45
env:
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_26.4.app
- name: Write App Store Connect API key
env:
ASC_KEY_P8_BASE64: ${{ secrets.ASC_KEY_P8_BASE64 }}
run: |
mkdir -p "$HOME/private_keys"
echo -n "$ASC_KEY_P8_BASE64" | base64 --decode -o "$HOME/private_keys/AuthKey_${ASC_KEY_ID}.p8"
- name: Archive
run: |
set -o pipefail
xcodebuild archive \
-project MyApp.xcodeproj -scheme MyApp -configuration Release \
-archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
-allowProvisioningUpdates \
-authenticationKeyPath "$HOME/private_keys/AuthKey_${ASC_KEY_ID}.p8" \
-authenticationKeyID "$ASC_KEY_ID" \
-authenticationKeyIssuerID "$ASC_ISSUER_ID"
- name: Export and upload to App Store Connect
run: |
xcodebuild -exportArchive \
-archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
-exportOptionsPlist ExportOptions.plist \
-exportPath "$RUNNER_TEMP/export" \
-allowProvisioningUpdates \
-authenticationKeyPath "$HOME/private_keys/AuthKey_${ASC_KEY_ID}.p8" \
-authenticationKeyID "$ASC_KEY_ID" \
-authenticationKeyIssuerID "$ASC_ISSUER_ID"
- name: Remove API key
if: always()
run: rm -f "$HOME/private_keys/AuthKey_${ASC_KEY_ID}.p8"
Tekshiruv ro'yxati:
Xcode_26.4.appvaiPhone 17nomlari runner image'da borligini README'dan tekshiring. Kerak bo'lsa bitta qadam qo'shib,xcrun simctl list devices availablechiqishini ko'ring.concurrency— bitta branch'da yangi push kelsa, eski ish bekor qilinadi (pul tejaydi).- Tag push qilish:
git tag v1.4.0 && git push origin v1.4.0.
10. Qaysi CI qachon? {#tanlash}
| Vaziyat | Tavsiya |
|---|---|
| Kichik jamoa, faqat Apple platformalari, oyiga ≤ 25 soat build | Xcode Cloud — sozlash eng kam |
| Backend/web ham GitHub'da, bitta joyda boshqarmoqchisiz | GitHub Actions |
| Faqat mobil, tayyor "step"lar va avtomatik signing kerak | Codemagic (500 bepul M2 daqiqa/oy) yoki Bitrise (Hobby: 300 kredit/oy) — narxni rasmiy sahifada tekshiring |
| Juda ko'p build, macOS daqiqalari qimmatga tushyapti | O'z Mac mini'laringiz + virtualizatsiya (masalan, ochiq manbali Tart). Loyiha jamoasi 2026-yilda OpenAI'ga o'tgani uchun uning kelajagini kuzatib boring |
Manbalar: Codemagic pricing, Bitrise pricing, Tart.
11. Xulosa {#xulosa}
- Pipeline bosqichlari: lint → build → test → archive → sign → upload. Har birining versiyasini qotiring.
- **App Store Connect API key +
-allowProvisioningUpdates** — 2026-yilda CI'da imzolashning eng sodda yo'li. ExportOptions.plistdaapp-store-connect+destination: upload— eksport va yuklash bitta qadamda.- macOS runner daqiqasi Linux'dan ~10 barobar qimmat.
macos-13yo'q;macos-15/macos-26ishlating. - Privacy manifest'siz SDK butun release'ni to'xtatadi. Uni PR darajasida tekshiring.
Manbalar {#manbalar}
- Apple — Get started with Xcode Cloud
- Apple — Xcode Cloud environment variable reference
- GitHub — GitHub-hosted runners
- GitHub — About billing for GitHub Actions
- GitHub changelog — macOS 13 runner closing down
- GitHub changelog — macOS 26 GA
- Apple — altool notarization deprecation
- Apple — Privacy manifest requirements
- fastlane — App Store Connect API
- Mobile Native Foundation — fastlane transfer