---
title: 'Yayın Otomasyonu'
source: 'https://academia.sh/tr/kurslar/ios-uretim/yayin-otomasyonu'
course: 'Mimari, Test ve Yayınlama'
language: tr
updated: '2026-10-21T05:03:07+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Sürüm numarası ile derleme numarasının ayrı roller taşıdığını ve yükleme betiğinin hangisini artırdığını ayırır'
    - 'Yalnız x.y.z biçimine bakan denetimin artmayan derleme numarasını göremediğini ölçer'
    - 'Numaranın mağazadaki son numaradan büyük olup olmadığına bakan denetimin kusuru ayırdığını doğrular'
    - 'Son numaranın okunduğu yerin aynı denetimi kör, gürültülü ya da ters yapabildiğini gösterir'
---

# Yayın Otomasyonu

Derleme numarasını artırmadan yükleyen bir betiğin, numaranın mağazadaki son numaradan büyük olup olmadığına bakan denetimle ayrıldığının, biçim denetiminin ise kör kaldığının ölçümü.

Bir sürüm hazır olduktan sonra elle yapılan her adım bir hata fırsatıdır: numarayı artırmak, arşivi çıkarmak, imzalamak, yüklemek. **Yayın otomasyonu** bu adımları tek bir betiğe toplar ve her seferinde aynı sırayla koşturur. Betik hatalı yazılmışsa hata da her seferinde tekrarlanır; bu yüzden betiğin kendisi de denetlenir.

Kursun ölçü adı **ayrım**dır: aynı denetimi doğru sürüme ve kusurlu sürüme uygulayınca iki yanıt farklı mı. Dört kategori vardır: doğru sürümün geçip kusurlu sürümün kaldığı **ayıran**, ikisinin de geçtiği **kör**, ikisinin de kaldığı **gürültülü** ve doğru sürümün kalıp kusurlu sürümün geçtiği **ters**. Çıktıda bunlar `ayiran`, `kor`, `gurultulu` ve `ters` olarak görünür. Bu ders çekirdek dosyayla ölçer.

```swift
// Separation.swift — bir denetimin dogru surumu kusurlu surumden ayirip ayirmadigini siniflandirir
struct Contrast<Output: CustomStringConvertible> {
    let label: String
    let correct: Output
    let faulty: Output
    let passesCorrect: Bool
    let passesFaulty: Bool

    var separation: String {
        switch (passesCorrect, passesFaulty) {
        case (true, false): return "ayiran"
        case (true, true): return "kor"
        case (false, false): return "gurultulu"
        case (false, true): return "ters"
        }
    }

    func describe() -> String {
        let onCorrect = passesCorrect ? "gecti" : "kaldi"
        let onFaulty = passesFaulty ? "gecti" : "kaldi"
        print("\(label) | dogru=\(correct)/\(onCorrect) kusurlu=\(faulty)/\(onFaulty) -> \(separation)")
        return separation
    }
}

func contrast<Output>(_ label: String, correct: Output, faulty: Output, check: (Output) -> Bool) -> Contrast<Output> {
    Contrast(label: label, correct: correct, faulty: faulty, passesCorrect: check(correct), passesFaulty: check(faulty))
}

func countSeparations(_ results: [String]) {
    for name in ["ayiran", "kor", "gurultulu", "ters"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

Bu dersin kusuru küçük bir unutuştur: yükleme betiği **derleme numarasını artırmaz**. Sürüm numarası aynı kalır, ki bu normaldir, ama derleme numarası da aynı kalır. Betik çalışır, arşiv çıkar, imza geçer ve yükleme komutu koşar. Hiçbir adım kırmızı olmaz. Sorun ancak mağaza aynı numarayı ikinci kez görünce ortaya çıkar ve o noktada yükleme çoktan yapılmıştır.

## Sürüm Numarası ve Derleme Numarası

Bir uygulamanın iki numarası vardır. **Sürüm numarası** (`2.4.0` gibi) kullanıcıya görünür ve ürünün hangi hâlini taşıdığını söyler. **Derleme numarası** ise aynı sürümün art arda yapılan yüklemelerini birbirinden ayırır: sürüm `2.4.0` iken üç kez yükleme yapılmışsa üç ayrı derleme numarası vardır. Bu iki numaranın rolü farklı olduğu için hata da farklı yerde görünür. Sürüm numarasını yanlış yazan biri bunu hemen fark eder, çünkü ekranda durur. Derleme numarasını artırmayı unutan kimse hiçbir şey görmez.

Aşağıdaki betik bir yükleme adımının iskeletidir. Araç adları genel tutuldu: ilk komut numarayı artıran küçük bir yardımcı, üçüncüsü yükleyicidir. Betik ölçülmez, ders yalnız ne yaptığını gösterir.

```zsh
./bump-build-number.sh
xcodebuild archive -scheme Shop -archivePath Shop.xcarchive
./upload-to-store.sh Shop.xcarchive
```

Kusurlu betikte ilk satır yoktur. Diğer iki satır bire bir aynıdır. Betiği okuyan göz kusuru ancak kaçıran satırı arayarak bulur ve kaçıran satırı aramak için önce kaçan bir şey olduğunu bilmek gerekir. Denetimin işi tam olarak budur.

## Numarayı Kim Artırır

Derleme numarasını artırma işi üç yere konabilir ve her yerin kendi hatası vardır. Elle artırılırsa unutulur; bu, kusurlu betiğin özel bir hâlidir. Betik artırırsa betiğin her çalıştırmada artırdığından emin olmak gerekir: betik başarısız bir yüklemeden sonra yeniden koşarsa numarayı ikinci kez artırır ve numaralar atlar. Numara hattın sayacından türetilirse, her hat çalıştırması numarayı otomatik olarak yükseltir, ama bu kez numara ile sürüm geçmişi arasındaki bağ gevşer: iki yükleme aynı değişiklikten çıkmış olsa bile farklı numaralar taşır.

Hiçbir seçenek hatasız değildir ve seçim bu dersin konusu değildir. Dersin konusu, seçilen yolun **artırdığının** denetlenip denetlenmediğidir. Elle artıran ekip denetimi bir kontrol listesi gibi tutar; betik artıran ekip denetimi betiğin çıkışına koyar. İki durumda da denetim aynı soruyu sorar: yükleme numarası bir öncekinden büyük mü.

## Modelin Kurulması

Model bir yüklemeyi iki alanla tutar: sürüm numarası ve derleme numarası. `prepareUpload` betiğin yaptığı işi taklit eder: mağazadaki son numarayı alır, doğru betikte bir artırır, kusurlu betikte olduğu gibi bırakır. `hasVersionShape` sürüm numarasının `x.y.z` biçiminde, yani nokta ile ayrılmış üç rakam grubu olduğunu denetler. `Reading` bir yüklemeyle birlikte **hangi son numarayla karşılaştırıldığını** taşır; bu, dersin son bölümünde gerekecek.

```swift
// Release.swift — yukleme betiginin sürüm ve derleme numarasi modeli
struct Upload: CustomStringConvertible {
    let version: String
    let buildNumber: Int
    var description: String { "\(version)(\(buildNumber))" }
}

struct Reading: CustomStringConvertible {
    let upload: Upload
    let recorded: Int
    var description: String { "\(upload)/kayit=\(recorded)" }
}

func hasVersionShape(_ version: String) -> Bool {
    let parts = version.split(separator: ".", omittingEmptySubsequences: false)
    return parts.count == 3 && parts.allSatisfy { !$0.isEmpty && $0.allSatisfy { $0.isNumber } }
}

func prepareUpload(version: String, lastUploaded: Int, bumpsNumber: Bool) -> Upload {
    Upload(version: version, buildNumber: bumpsNumber ? lastUploaded + 1 : lastUploaded)
}

let releaseVersion = "2.4.0"
let storeLast = 12
```

Doğru betik sürüm `2.4.0` ile derleme numarası `13` üretir. Kusurlu betik aynı sürümle `12` üretir, yani mağazadaki son numarayı tekrar eder.

**DG7.** Bu ölçüm, mağazanın aynı sürüm için derleme numarasının bir öncekinden büyük olmasını beklediğini dersin varsayımı olarak alır. Mağazanın gerçekte hangi numarayı kabul ettiği ya da reddettiği ölçülmedi.

## Üç Denetim

Betiğin ürettiği yükleme üç denetimle sınanır. Birincisi sürüm numarasının `x.y.z` biçiminde olduğuna bakar: yükleme betiklerinde en sık yazılan denetimdir, çünkü yazması kolaydır. İkincisi derleme numarasının mağazadaki son numaradan büyük olduğuna bakar. Üçüncüsü derleme numarasının pozitif olduğuna bakar.

```swift
// main.swift — uc denetim, iki kategori
let withBump = prepareUpload(version: releaseVersion, lastUploaded: storeLast, bumpsNumber: true)
let withoutBump = prepareUpload(version: releaseVersion, lastUploaded: storeLast, bumpsNumber: false)

let shape = contrast("bicim-x-y-z", correct: withBump, faulty: withoutBump) { hasVersionShape($0.version) }.describe()
let greater = contrast("numara-son-numaradan-buyuk", correct: withBump, faulty: withoutBump) { $0.buildNumber > storeLast }.describe()
let positive = contrast("numara-pozitif", correct: withBump, faulty: withoutBump) { $0.buildNumber > 0 }.describe()

countSeparations([shape, greater, positive])
```

```text
bicim-x-y-z | dogru=2.4.0(13)/gecti kusurlu=2.4.0(12)/gecti -> kor
numara-son-numaradan-buyuk | dogru=2.4.0(13)/gecti kusurlu=2.4.0(12)/kaldi -> ayiran
numara-pozitif | dogru=2.4.0(13)/gecti kusurlu=2.4.0(12)/gecti -> kor
ayiran: 1
kor: 2
gurultulu: 0
ters: 0
```

Üç satır, iki kategori. `bicim-x-y-z` ve `numara-pozitif` iki yüklemede de aynı yanıtı verir: sürüm numarası iki yüklemede de `2.4.0`dır ve derleme numarası ikisinde de pozitiftir. Kusur bu iki değere dokunmaz, çünkü kusur bir **karşılaştırmadır**: numara yanlış değildir, mağazadaki son numarayla aynıdır. Yalnız `numara-son-numaradan-buyuk` bu karşılaştırmayı yapar. Doğru yüklemede `13 > 12` geçer, kusurlu yüklemede `12 > 12` kalır: **ayıran**.

## Biçim Denetimi Neden Yaygın ve Neden Kör

Biçim denetimi yaygındır, çünkü yazması kolaydır ve çok kusur yakalar: sürüm numarasında harf, eksik grup, fazla nokta. Yakaladığı kusurlar gerçektir. Ama denetimin baktığı şey **tek bir yüklemenin kendi içindeki tutarlılıktır**; yüklemeyi başka bir yüklemeyle karşılaştırmaz. Artmayan numara ise ancak bir önceki yüklemeyle birlikte okununca görünür. Tek başına bakıldığında `2.4.0(12)` geçerli bir yüklemedir, `2.4.0(13)` de öyle.

Bu, kör denetimlerin ortak özelliğidir: denetim kusurun değiştirdiği değere bakmaz. Kusur burada derleme numarasının **önceki değerle ilişkisini** değiştirir, `x.y.z` biçimi ise yalnız sürüm numarasına bakar. Biçim denetiminin yeşil olması "numaralar doğru" cümlesini taşımaz; yalnız "sürüm numarası biçimli" cümlesini taşır. Betiğin ürettiği rapora bu iki cümleyi ayırmadan "numaralar geçerli" yazmak, ilkini ikincinin yerine geçirmektir.

`numara-pozitif` denetimi daha da zayıftır. Derleme numarasının sıfırdan büyük olmasını ister; kusurlu numara da `12`dir. Zayıf bir denetim kötü bir denetim demek değildir: sıfır ya da negatif bir numara üreten başka bir kusurda bu denetim ayıran olurdu. Ayrım, denetimle kusurun çiftine aittir; kusur değişince tablo da değişir.

## Büyüklük Denetiminin Dayandığı Veri

`numara-son-numaradan-buyuk` ayırandır, ama bu ölçümde `storeLast` değeri doğrudan mağazadan gelmiş gibi alındı. Gerçek bir betikte son numaranın nereden okunduğu ayrı bir sorudur ve denetim o kaynağın doğruluğu kadar güvenilirdir. Üç yaygın kaynak vardır. Biri mağazanın kendisidir: betik yüklemeden önce son numarayı sorar. Biri yerel bir dosyadır: ekip son yüklenen numarayı bir dosyada tutar ve her yüklemede günceller. Biri de sürüm denetim geçmişidir: numara etiketlerden okunur. Her biri farklı biçimde eskir.

Yerel dosya en kolay olanıdır ve en çok sorun çıkaranıdır. Dosya, numarayı artıran adımda yazılır. Kusurlu betikte o adım yoktur; yani dosya da yazılmaz. Dosyada bir önceki yüklemeden kalma, mağazadakinden de **eski** bir numara durur. Denetim bu eski numarayla karşılaştırırsa, kusurlu yükleme eski numaradan büyük görünür ve geçer. Doğru yükleme ise dosyayı kendisi güncellemiştir; yeni numara kendi kendisine eşittir ve `>` kalır. Bu, ayıran denetimin tersine dönmesidir.

**DG8.** Bu ölçüm yerel kaydın yalnız numarayı artıran adımda güncellendiğini varsayar. Gerçek bir betiğin kaydı ne zaman ve nasıl yazdığı ölçülmedi.

## Sınırlayıcı Ölçüm

Aynı denetim, derleme numarası son numaradan büyük, üç farklı son numara kaynağıyla koşar. Birinci kaynak mağazadır: iki yüklemede de son numara `12`dir. İkinci kaynak yükleme sonrasında yazılan bir dosyadır: iki betik de yüklediği numarayı dosyaya yazar, dolayısıyla dosya her yüklemede kendi numarasını taşır. Üçüncü kaynak yalnız numarayı artıran adımda güncellenen dosyadır: doğru betik `13` yazar, kusurlu betik yazmaz ve dosya `11`de kalır.

```swift
// main.swift — ayni buyukluk denetimi, uc son numara kaynagi
let withBump = prepareUpload(version: releaseVersion, lastUploaded: storeLast, bumpsNumber: true)
let withoutBump = prepareUpload(version: releaseVersion, lastUploaded: storeLast, bumpsNumber: false)

let fromStore = contrast("son-numara-magaza",
    correct: Reading(upload: withBump, recorded: storeLast),
    faulty: Reading(upload: withoutBump, recorded: storeLast)) { $0.upload.buildNumber > $0.recorded }.describe()
let afterUpload = contrast("son-numara-yukleme-sonrasi-dosya",
    correct: Reading(upload: withBump, recorded: withBump.buildNumber),
    faulty: Reading(upload: withoutBump, recorded: withoutBump.buildNumber)) { $0.upload.buildNumber > $0.recorded }.describe()
let staleFile = contrast("son-numara-artiran-adimda-dosya",
    correct: Reading(upload: withBump, recorded: withBump.buildNumber),
    faulty: Reading(upload: withoutBump, recorded: 11)) { $0.upload.buildNumber > $0.recorded }.describe()
let shapeAgain = contrast("bicim-x-y-z", correct: withBump, faulty: withoutBump) { hasVersionShape($0.version) }.describe()

countSeparations([fromStore, afterUpload, staleFile, shapeAgain])
```

```text
son-numara-magaza | dogru=2.4.0(13)/kayit=12/gecti kusurlu=2.4.0(12)/kayit=12/kaldi -> ayiran
son-numara-yukleme-sonrasi-dosya | dogru=2.4.0(13)/kayit=13/kaldi kusurlu=2.4.0(12)/kayit=12/kaldi -> gurultulu
son-numara-artiran-adimda-dosya | dogru=2.4.0(13)/kayit=13/kaldi kusurlu=2.4.0(12)/kayit=11/gecti -> ters
bicim-x-y-z | dogru=2.4.0(13)/gecti kusurlu=2.4.0(12)/gecti -> kor
ayiran: 1
kor: 1
gurultulu: 1
ters: 1
```

Aynı karşılaştırma, `buildNumber > recorded`, üç kaynakla üç ayrı kategoriye düştü ve dördüncü satırdaki biçim denetimiyle dört kategori de göründü. İlk satırda kaynak gerçeği söyler: **ayıran**. İkinci satırda dosya yüklemeden sonra yazılmıştır; her yükleme kendi numarasına eşit olduğundan iki sürüm de kalır: **gürültülü**. Kırmızı denetim betiğin yüklemeyi bozduğu izlenimini verir, oysa bozan karşılaştırmanın kendisidir. Üçüncü satırda doğru yükleme kalır, kusurlu yükleme geçer: **ters**.

Bu üçüncü satır ekibin neden bazen güvendiği bir denetimi kapattığını açıklar. Denetim doğru yüklemede kırmızıdır, ekip bunu yanlış alarm sayar ve eşik gevşetilir: `>` yerine `>=` yazılır. Böylece kusurlu yükleme de, doğru yükleme de geçer ve denetim körleşir. Yanlış alarm veriyormuş gibi görünen bir denetimi gevşetmek yerine **kaynağını** incelemek gerekir; `>` bu derste doğru yazılmıştı ve kusur kaynaktaydı.

Bu ölçümün sınırı açık söylenmeli. Yükleme ve mağazanın reddi ölçülmedi; betik yalnızca kaynak olarak gösterildi ve model yükleme sonucunu değil, yüklemeye giden iki sayıyı karşılaştırdı. Mağazanın ikinci aynı numarayı gerçekten reddedip reddetmediği, hangi iletiyle reddettiği ve ne kadar sürede reddettiği bu dersin dışında kalır.

**DG9.** Bu ölçümde dosyanın `11` değeri, kusurlu betiğin yazmadığı bir önceki kayıttır ve mağazadaki son numaradan eskidir. Eski kaydın gerçek bir betikte ne kadar eski olabileceği ölçülmedi.

## Betik Çıktısına Ne Yazılır

Betiğin sonunda üretilen özet, ekibin yükleme hakkında okuduğu tek yerdir. Özete "sürüm 2.4.0, derleme 12, yüklendi" yazan bir betik, kusurlu yüklemeyi de doğru yükleme kadar temiz gösterir. Özetin ayıran bir bilgi taşıması için, numaranın **önceki numarayla** birlikte yazılması yeterlidir: "derleme 12, önceki 12". Bu iki sayıyı yan yana gören ekip, artmayan numarayı betik hiçbir denetim koşmasa da fark eder.

Aynı ilke başka yerlerde de geçerlidir. Bir sayının tek başına anlamı azdır; hangi sayıyla karşılaştırıldığı yazılırsa denetim okunur hâle gelir. Yüklemenin kaydı da bu yüzden yalnız son durumu değil, bir önceki durumu da taşımalıdır. Önceki durumu taşımayan bir kayıt, bu dersin son ölçümündeki gibi, eski ya da yazılmamış olduğunda sessizce yanlış karşılaştırma üretir.

Son olarak yeni bir denetim yazarken sorulacak soruyu hatırlayalım: kusurlu betiğin ürettiği yüklemeye bu denetim ne der? Bu soruyu yanıtlamak için betiği bilerek bozmak yeterlidir; ilk satırı silmek, ilk bozma denemesidir. Denetim ilk denemede kalmıyorsa kör ya da tersidir ve nedeni, dersin gösterdiği gibi, çoğunlukla denetimin baktığı veride yatar.

## Özet

- Sürüm numarası kullanıcıya görünür, derleme numarası aynı sürümün art arda yüklemelerini ayırır; artmayan derleme numarası hiçbir adımı kırmızıya çevirmez.
- Yalnız `x.y.z` biçimine ya da numaranın pozitifliğine bakan denetimler iki yüklemede de geçer: kör.
- Derleme numarasının mağazadaki son numaradan büyük olup olmadığına bakan denetim kusurlu yüklemeyi ayırır: ayıran.
- Aynı büyüklük denetimi, son numara yükleme sonrası yazılan bir dosyadan okunursa gürültülü, yalnız numarayı artıran adımda güncellenen eski bir dosyadan okunursa ters olur.
- Kırmızı bir büyüklük denetimini gevşetmeden önce son numaranın kaynağı incelenir.
- Bu dersin ayrımı: **ayıran**.

## Sonraki Adım

Numarası doğru artan bir yükleme hâlâ herkese açılmak zorunda değildir. Sıradaki derste, Beta Dağıtımı, yeni özelliğin yalnız sınırlı bir kitleye açılması ve bir bayrağın kitle dışına da açık kalması ölçülecek.
