Mert Özen Her Satırda Daha İleri
DevOps Azure DevOps Pipelines — #01

Azure DevOps Pipelines Nedir? Organization, Project, YAML Pipeline ve İlk CI Çalıştırması

Mert Özen 15 Kas 2026 14 dk 4 görüntülenme
Azure DevOps Pipelines Nedir? Organization, Project, YAML Pipeline ve İlk CI Çalıştırması

Azure Pipelines, Azure DevOps'un bir project içindeki YAML dosyasını trigger'larla çalıştırıp job'ları agent'larda koşturan CI/CD servisidir; hiyerarşi pipeline, stage, job ve step'tir. Bookstore için restore, build ve SQL Server'lı integration testi yapan ilk pipeline'ı yazıyor, Azure Repos'ta pull request doğrulamasının branch policy ile yapıldığını, GitHub App bağlantısını ve GitHub Actions eşlemesini görüyoruz.

Sürüm notu: Bu yazıdaki örnekler .NET 10 (SDK 10.0.404) ile yerelde derlenip denenmiştir; pipeline dosyası yq 4.54 ile ayrıştırılarak denetlenmiştir. Azure Pipelines'ın kendi davranışını anlatırken Microsoft Learn'deki resmî belgelerden (Kasım 2026 sürümleri) yararlanılmıştır.

Önce Tahmin Et

Emre, sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) kodunu şirketin Azure DevOps organization'ındaki bir Azure Repos Git repository'sine taşıdı. GitHub Actions'tan alışkın olduğu için repository'nin köküne şu azure-pipelines.yml dosyasını ekledi:

trigger:
  branches:
    include:
      - main

pr:
  branches:
    include:
      - main

pool:
  vmImage: ubuntu-24.04

steps:
  - script: dotnet build Bookstore.slnx

Pipeline'ı bu dosyadan oluşturdu, ardından feature/cart-badge branch'inden main'e bir pull request açtı. Soru şu: pull request ekranında bir build başlar mı? Başlamazsa, pr: bloğunu ne zaman dikkate alır?

Cevap: başlamaz ve bu dosya Azure Repos'ta durduğu sürece pr: bloğu hiç dikkate alınmaz. Azure Pipelines, YAML (girintiyle yapı kuran, insanın okuyabileceği bir veri formatı) dosyasındaki pull request trigger'ını yalnızca GitHub ve Bitbucket Cloud repository'leri için kullanıyor. Azure Repos Git'te pull request doğrulaması branch policy ile yapılıyor: hedef branch'in (burada main) ayarlarına bir "Build validation" policy'si ekleyip bu pipeline'ı seçmen gerekiyor. Aynı dosya GitHub'daki bir repository'de olsaydı pull request açılır açılmaz run başlardı. Microsoft Learn'deki "Build Azure Repos Git repositories" sayfası bunu sorun giderme bölümünde açıkça yazıyor. Bu yazıda bunun gibi, GitHub Actions'tan gelen birinin ilk haftada takıldığı farkları da göreceğiz.

Azure DevOps'un Parçaları ve Pipelines'ın Yeri

Azure DevOps tek bir ürün değil, birlikte çalışan beş servisten oluşan bir platform. Azure Boards iş takibini (backlog, sprint, work item) yapar. Azure Repos Git repository'lerini ve eski TFVC'yi (Team Foundation Version Control, merkezi sürüm kontrol sistemi) barındırır. Azure Pipelines build, test ve deploy'u çalıştırır. Azure Test Plans manuel ve keşif testlerini yönetir. Azure Artifacts NuGet, npm, Maven ve Python paketleri için feed sağlar. Bu seri yalnızca Pipelines'a ve onun doğrudan kullandığı Artifacts'e odaklanıyor; Boards, Repos ve Test Plans kapsam dışı. İş takibi tarafı ilerideki Jira ve Proje Araçları serisinin konusu.

En üst seviyede organization var: dev.azure.com/bookstore-org gibi bir adres, kullanıcılar, faturalandırma ve parallel job kapasitesi bu seviyede tutulur. Organization'ın altında project'ler bulunur. Bu seride tek bir project kullanacağız: Bookstore. Repository'ler, pipeline'lar, environment'lar, service connection'lar ve çoğu feed bir project'in içinde yaşar. Bir pipeline tanımı her zaman bir project'e aittir; kodu ise aynı project'teki bir Azure Repos repository'sinde ya da GitHub'da durabilir.

Bir de kurulum modeli farkı var. Azure DevOps Services, Microsoft'un barındırdığı bulut sürümü; yeni özellikler önce buraya gelir ve Microsoft-hosted agent'lar yalnızca burada vardır. Azure DevOps Server ise aynı temel servisleri kendi sunucularına kuran kurumlar için on-premises sürüm: altyapıyı, yükseltmeleri ve agent'ları sen yönetirsin, pipeline'lar self-hosted agent'larda koşar. Bu seri Services'ı anlatıyor; Server'daki farklara son yazıda kısaca döneceğiz.

Hiyerarşi: Pipeline, Stage, Job ve Step

Azure Pipelines'ın bütün kavramları dört katmanlı bir yapının üstüne oturuyor. Önce şekle bak, sonra katmanları tek tek konuşalım.

01-run-anatomy-tr.png

Bir trigger (push, zamanlayıcı, başka bir pipeline'ın bitmesi ya da elle başlatma) pipeline'ın bir run'ını başlatır. Pipeline bir ya da daha fazla stage'den oluşur. Stage mantıksal bir sınırdır: "build", "staging'e deploy", "production'a deploy" gibi. Stage'ler varsayılan olarak sırayla koşar ve onay, kontrol gibi kurallar stage seviyesinde devreye girer. Her stage bir ya da daha fazla job içerir. Job, aynı agent üzerinde sırayla çalışan step'lerin oluşturduğu çalışma sınırıdır. Aynı stage'deki job'lar birbirine dependsOn ile bağlanmadıkça paralel koşabilir; ama bunun için organization'da yeterli parallel job olması gerekir. Step en küçük birimdir ve iki türlüdür: task (Microsoft'un ya da bir extension'ın paketlediği, input'ları olan hazır bir adım; UseDotNet@2 gibi) ya da script (senin yazdığın bash, PowerShell veya komut satırı kodu).

Agent, job'ı çalıştıran makinedeki yazılımdır ve aynı anda tek bir job çalıştırır. Microsoft-hosted agent'ta her job için hiç pipeline koşmamış yeni bir VM (sanal makine) verilir ve job bitince VM silinir. Bu yüzden aynı job'ın step'leri birbirinin dosyasını görür ama iki ayrı job görmez; bir job'ın ürettiğini ötekine taşımak için pipeline artifact gerekir (dördüncü yazı). Basit bir pipeline'da stages ve jobs anahtarlarını hiç yazmayabilirsin: kökte yalnızca steps varsa Azure Pipelines bunu tek stage ve tek job'lı bir pipeline olarak işler. İlk pipeline'ımız tam olarak böyle olacak.

İlk Pipeline: Bookstore İçin Restore, Build ve Test

Bu serinin repository'si Bookstore'un referans uygulaması: kökte Bookstore.slnx, src/Bookstore.Api (ASP.NET Core 10 Web API, Entity Framework Core (EF Core; .NET'in veritabanı erişim kütüphanesi) ve SQL Server), tests/Bookstore.Api.Tests (xUnit v3 ile integration testleri) ve web/bookstore-web (Angular 22). İlk pipeline yalnızca API tarafını derleyip testleri çalıştıracak:

# Bookstore CI: main'e gelen her push'ta restore, build ve test
trigger:
  branches:
    include:
      - main

pool:
  vmImage: ubuntu-24.04

variables:
  buildConfiguration: Release

steps:
  - checkout: self

  - task: UseDotNet@2
    displayName: Install .NET SDK from global.json
    inputs:
      packageType: sdk
      useGlobalJson: true

  - script: dotnet restore Bookstore.slnx
    displayName: Restore

  - script: dotnet build Bookstore.slnx --no-restore -c $(buildConfiguration)
    displayName: Build

  - script: bash scripts/start-test-sql.sh
    displayName: Start SQL Server for integration tests

  - script: dotnet test --solution Bookstore.slnx --no-build -c $(buildConfiguration)
    displayName: Test
    env:
      ConnectionStrings__BookstoreDb: $(TestDbConnection)

trigger bloğu bu pipeline'ın yalnızca main'e gelen push'larda otomatik başlayacağını söylüyor. pool: vmImage: ubuntu-24.04 job'ın Microsoft-hosted "Azure Pipelines" pool'undaki Ubuntu 24.04 image'ında koşacağını belirtiyor. ubuntu-latest yazmak da mümkündü ve şu an aynı image'a gidiyor; ama Ubuntu 26.04 image'ı 29 Eylül 2026'da GA (genel kullanıma açık) oldu ve "latest" etiketi bir gün ona taşınacak. Image'ı adıyla yazınca bu geçişi ne zaman yapacağına sen karar veriyorsun.

variables altındaki buildConfiguration pipeline genelinde geçerli bir değişken; step'lerde $(buildConfiguration) diye okunuyor. Bu $( ) yazımı macro syntax: değer, step çalışmadan hemen önce yerine konuyor. Azure Pipelines'ta değişkene erişmenin üç yolu var ve hangisinin ne zaman işlendiği bir sonraki yazının konusu.

checkout: self pipeline dosyasının bulunduğu repository'yi agent'a çekiyor. Aslında yazmasan da her job'ın başında otomatik eklenir; açıkça yazmak dosyayı okuyan için niyeti netleştiriyor ve ileride fetchDepth, clean gibi seçenekleri koyacağın yeri gösteriyor. UseDotNet@2 task'ı useGlobalJson: true ile repository'deki global.json'u okuyup oradaki SDK'yı kuruyor:

{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  },
  "test": {
    "runner": "Microsoft.Testing.Platform"
  }
}

Task'ın belgesine göre rollForward değerini de hesaba katıyor: latestFeature burada "10.0.100 veya üstündeki en yeni 10.0 SDK'sı" demek. Böylece geliştiricinin makinesi ile pipeline aynı dosyadan besleniyor. test.runner satırı .NET 10'daki dotnet test'i Microsoft.Testing.Platform moduna alıyor; komutun --solution parametresiyle yazılması bundan.

Geri kalan step'ler yerelde de yazdığın komutlar. Pipeline'daki iki komutu aynı repository'de yerelde çalıştırınca çıktı şöyle (uzun yerel yolları … ile kısalttım):

$ dotnet restore Bookstore.slnx
  Determining projects to restore...
  Restored …/src/Bookstore.Api/Bookstore.Api.csproj (in 147 ms).
  Restored …/tests/Bookstore.Api.Tests/Bookstore.Api.Tests.csproj (in 147 ms).
$ dotnet build Bookstore.slnx --no-restore -c Release
  Bookstore.Api -> …/src/Bookstore.Api/bin/Release/net10.0/Bookstore.Api.dll
  Bookstore.Api.Tests -> …/tests/Bookstore.Api.Tests/bin/Release/net10.0/Bookstore.Api.Tests.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Restore ile build'i ayrı step'lere bölmenin asıl faydası log'da: run ekranında hangi step'in kırmızıya döndüğünü tek bakışta görürsün. --no-restore ve --no-build aynı işi iki kez yapmamak için var.

Integration Testleri İçin Geçici SQL Server

Bookstore'un testleri gerçek bir SQL Server'a bağlanan integration testleri. Test projesi connection string'i ConnectionStrings:BookstoreDb anahtarından okuyor ve sonuna _Tests ekleyip veritabanını her koşuda sıfırdan kuruyor. Connection string yoksa ne olduğunu yerelde görmek kolay:

$ dotnet test --solution Bookstore.slnx --no-build -c Release
failed Bookstore.Api.Tests.CatalogTests.Search_finds_books_by_author_name (0ms)
  Xunit.MicrosoftTestingPlatform.XunitException: Assembly fixture type 'Bookstore.Api.Tests.BookstoreApiFactory' threw in its constructor
  ---- System.InvalidOperationException : Connection string 'BookstoreDb' is missing (see README.md).
…
Test run summary: Failed!
  total: 10
  failed: 10
  succeeded: 0
  skipped: 0
  duration: 217ms
Test run completed with non-success exit code: 2 (see: https://aka.ms/testingplatform/exitcodes)

Pipeline'da da aynı şey olurdu: exit code 2, step kırmızı, run başarısız. Microsoft-hosted Ubuntu image'ında Docker kurulu geliyor; bu yüzden testlerden hemen önce SQL Server'ı bir container olarak başlatan küçük bir script ekledik:

#!/usr/bin/env bash
# Build agent'ında integration testleri için geçici bir SQL Server container'ı başlatır
# ve connection string'i sonraki step'lere secret pipeline değişkeni olarak verir.
set -euo pipefail

container_name="bookstore-test-sql"
image="mcr.microsoft.com/mssql/server:2025-latest"

# Her koşuda yeni, rastgele bir şifre; bu agent'tan dışarı çıkmaz.
MSSQL_SA_PASSWORD="Bk-$(openssl rand -hex 16)-9z"
export MSSQL_SA_PASSWORD
echo "##vso[task.setsecret]${MSSQL_SA_PASSWORD}"

# Değersiz -e NAME, değeri bu shell'den kopyalar; şifre komut satırına yazılmaz.
docker run -d --name "$container_name" \
  -e ACCEPT_EULA=Y -e MSSQL_SA_PASSWORD \
  -p 1433:1433 "$image" > /dev/null

for attempt in $(seq 1 30); do
  if SQLCMDPASSWORD="$MSSQL_SA_PASSWORD" docker exec -e SQLCMDPASSWORD "$container_name" \
      /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -Q "SELECT 1" > /dev/null 2>&1; then
    echo "SQL Server is ready (attempt $attempt)."
    connection="Server=localhost,1433;Database=BookstoreDb;User Id=sa;Password=${MSSQL_SA_PASSWORD};TrustServerCertificate=True"
    echo "##vso[task.setvariable variable=TestDbConnection;issecret=true]${connection}"
    exit 0
  fi
  sleep 2
done

echo "##vso[task.logissue type=error]SQL Server did not become ready in 60 seconds."
docker logs "$container_name" | tail -n 20
exit 1

Script'teki ##vso[...] satırları logging command: agent, step'in standart çıktısında bu biçimde bir satır görünce onu bir komut olarak işliyor. task.setsecret şifreyi log'larda maskelenecek değerler listesine ekliyor. task.setvariable ... issecret=true sonraki step'ler için TestDbConnection adında secret bir değişken tanımlıyor. Secret değişkenler step'lere environment variable olarak kendiliğinden gelmez; bu yüzden test step'inde env: ile ConnectionStrings__BookstoreDb adına açıkça bağladık. ASP.NET Core configuration'ı çift alt çizgiyi : olarak okuduğu için test projesi değeri doğru anahtarda buluyor. ACCEPT_EULA=Y ise SQL Server image'ının lisans koşullarını kabul ettiğini söyleyen zorunlu ayar; bu kararı pipeline'ı yazan ekip verir. Logging command'lar ve secret'ların ayrıntısı ikinci yazıda.

Repository'yi Bağlamak ve Pipeline'ı Oluşturmak

Dosya repository'de hazır olunca pipeline'ı web arayüzünden oluşturuyorsun: Pipelines, New pipeline, kodun bulunduğu yer (Azure Repos Git veya GitHub), repository ve ardından var olan bir YAML dosyasını seçme. Azure Pipelines dosyayı gösterir, Run dediğinde ilk run başlar. Bu adımda dosyayı Azure DevOps'un kendi YAML editöründe de açabilirsin; editördeki "Validate" seçeneği, pipeline'ı başlatmasına engel olacak syntax hatalarını kaydetmeden önce yakalıyor.

Kod GitHub'daysa bağlantı yöntemi önemli. Belgeler üç yöntem sayıyor ve önerdiği yöntem Azure Pipelines GitHub App: uygulamayı GitHub organization'ına (veya yalnızca seçtiğin repository'lere) kurduğunda build'ler ve GitHub'daki status güncellemeleri senin kişisel hesabınla değil Azure Pipelines kimliğiyle yapılır. OAuth ve PAT (Personal Access Token, kişisel erişim token'ı) yöntemleri ise senin GitHub kimliğini kullanır; ekipten ayrılan birinin token'ıyla kurulmuş bir pipeline o gün sessizce durur. GitHub App'i kurmak için GitHub organization sahibi ya da repository admin'i olmak gerekiyor.

Trigger tarafında iki varsayılanı bilmek gerekiyor. Birincisi: dosyada hiç trigger bloğu yoksa pipeline bütün branch'lere gelen push'larda çalışır. Organization veya project ayarlarındaki "Disable implied YAML CI trigger" seçeneği bu davranışı kapatıyor ama varsayılan olarak kapalı değil. Bizim dosyamızda trigger açıkça yazıldığı için yalnızca main tetikliyor. İkincisi, giriş bölümündeki konu: Azure Repos'ta pull request doğrulaması branch policy ile kuruluyor ve bu policy'nin başlattığı run, kaynak ile hedef branch'in birleştirilmiş hâli olan merge commit üzerinde koşuyor. Draft pull request'ler policy olsa bile build başlatmıyor. GitHub repository'lerinde ise YAML'daki pr: bloğu çalışıyor; fork'lardan gelen pull request'lere secret verilmemesi gibi ayrıntılar güvenlik yazısının konusu.

Run Özeti, Log'lar ve Push'tan Önce Yerel Kontrol

Run başladığında Azure Pipelines önce dosyayı işliyor: template'leri açıyor, template expression'ları hesaplıyor, stage ve job bağımlılıklarına bakıp hangi job'ın çalışacağına karar veriyor. Sonra organization'da boş bir parallel job slot'u var mı diye bakıyor; yoksa job kuyrukta bekliyor. Slot açılınca istenen image'la yeni bir VM ayağa kalkıyor, agent job'ı alıyor, gereken task'ları indiriyor, çalışma klasörünü hazırlıyor ve step'leri sırayla koşuyor. Bir step kırılınca sonraki step'ler varsayılan olarak atlanıyor.

Run özet sayfasında job'ları ve her job'ın step'lerini görürsün; bir step'e tıklayınca onun log'u açılır. Sağ üstteki menüden bütün log'ları zip olarak indirebilirsin. Hata ayıklarken iki seçenek işe yarıyor: run'ı elle başlatırken "Enable system diagnostics" kutusunu işaretlemek ya da pipeline'a değeri true olan system.debug değişkenini eklemek. İkisi de agent ve task'ların ayrıntılı log yazmasını sağlıyor.

Push'lamadan önce dosyanın en azından geçerli bir YAML olduğunu yerelde görmek iyi bir alışkanlık. Hesap gerektirmeyen en kısa yol, YAML'ı ayrıştırıp içinden bir şey sorgulamak. yq ile step adlarını listeleyelim:

$ yq '.steps[] | (.displayName // "(checkout)")' azure-pipelines.yml
(checkout)
Install .NET SDK from global.json
Restore
Build
Start SQL Server for integration tests
Test

Şimdi Restore step'inin displayName satırını iki boşluk sola kaydıralım; YAML'da en sık yapılan hata budur:

$ yq '.steps | length' azure-pipelines.yml
Error: bad file 'azure-pipelines.yml': go-yaml load error in parser (while parsing a block collection) at L14.C3-L23.C3: did not find expected '-' indicator

Bu kontrol yalnızca YAML yapısını yakalar. Task adının doğru olup olmadığını, input'ların geçerliliğini ya da template'lerin açılmış hâlini ancak Azure Pipelines'ın kendisi söyleyebilir; bunun için editördeki "Validate" ve run öncesi yapılan doğrulama var.

GitHub Actions'tan Geliyorsan: Kavram Eşleme

Sitedeki GitHub Actions serisini okuduysan kavramların çoğu tanıdık gelecek. Adlar ve birkaç varsayılan farklı:

GitHub ActionsAzure PipelinesNot
workflow (.github/workflows/*.yml)pipeline (çoğunlukla azure-pipelines.yml)Azure'da pipeline tanımı ayrıca web'de oluşturulur ve bir dosyaya bağlanır
event (on:)trigger (trigger, pr, schedules, resources)Azure Repos'ta pull request doğrulaması branch policy ile
yok (job'lar tek düzeyde)stageOnay ve kontroller stage seviyesinde
jobjobİkisinde de her job ayrı makinede
step: uses: / run:step: task: / script:Task'lar Microsoft'un veya Marketplace extension'larının paketidir
runneragent (pool içinde)Microsoft-hosted, self-hosted, Managed DevOps Pools
environmentenvironmentAzure'da onay ve kontroller resource sahibinde tanımlanır
artifactpipeline artifactAzure Artifacts feed'iyle karıştırılmamalı

Hangisini seçeceğin çoğu zaman teknik değil kurumsal bir karar. Kod GitHub'daysa ve ekip pull request akışını orada yaşıyorsa GitHub Actions daha az parça demek. Azure DevOps'un öne çıktığı durumlar şunlar: kurum zaten Azure ve Microsoft Entra ID'ye (Microsoft'un kimlik ve erişim yönetimi servisi) yatırım yapmış ve izinleri oradan yönetiyorsa, iş takibi Azure Boards'taysa ve commit'lerin work item'lara bağlanması isteniyorsa, ya da yıllardır classic pipeline'larla çalışan bir ekip bunları YAML'a taşıyorsa. İki sistem birlikte de yaşayabiliyor: kod GitHub'da, pipeline Azure DevOps'ta olabilir. Araç seçiminin genel çerçevesi Jenkins serisinin "Jenkins Nedir? Controller, Agent, Executor ve Ne Zaman GitHub Actions Yerine Jenkins" yazısında; CI/CD kavramlarının kendisi ise CI/CD Temelleri serisinin "Pipeline Anatomisi: Trigger, Stage, Job, Step ve Fail Fast" yazısında anlatıldı.

Üretimde Dikkat

Kapasiteyi baştan bil. Yeni bir organization'da Microsoft-hosted agent'lar için ücretsiz katman otomatik gelmiyor; organization'ı geçerli bir Azure aboneliğine bağlayıp faturalandırmayı kurduğunda devreye giriyor. Ücretsiz katman private project'lerde tek bir parallel job veriyor: her job en fazla 60 dakika, ayda toplam 1.800 dakika. Bu, iki job'lı bir pipeline'ın job'larının paralel değil sırayla koşacağı anlamına geliyor; aynı anda iki geliştirici push'larsa ikinci run kuyrukta bekliyor. Ücretli parallel job'larda job başına sınır 360 dakikaya çıkıyor ve aylık sınır kalkıyor. Public project oluşturma artık kapalı; mevcut public project'ler 2027'de private'a dönüşecek.

Microsoft-hosted makinenin sınırlarını da bil: Linux ve Windows için 2 çekirdekli, 7 GB bellekli bir VM ve job'ın kullanabileceği en az 10 GB boş disk. Bu değerler artırılamıyor. Image'lar düzenli olarak güncelleniyor ve eski bir image sürümüne dönmek mümkün değil; bir hafta önce yeşil olan pipeline, image'daki bir araç sürümü değiştiği için kırmızıya dönebilir. Kritik araçları (SDK, Node.js) UseDotNet@2 gibi tool installer task'larıyla kendin sabitlemenin nedeni bu. Ubuntu 22.04 image'ı kullanımdan kaldırılma takviminde (17 Nisan 2027) ve öncesinde brownout'lar (eski image'ın kısa süreliğine bilerek kapatılması) planlanıyor; elinde hâlâ ubuntu-22.04 yazan pipeline varsa şimdiden taşı.

Süreyi en baştan izle. Bu küçük pipeline'da süreyi en çok etkileyecek adımlar SDK kurulumu, NuGet restore ve SQL Server image'ının indirilmesi. Run geçmişinde her step'in süresi görünüyor; restore ve image indirme süresini cache ile kısaltmak dördüncü yazının konusu. Ekip pull request başına 10 dakikadan uzun bir doğrulama beklemeye başlarsa geri bildirimin değeri düşer.

Sık Düşülen Tuzaklar

Eski blog yazılarındaki "Releases" ekranını aramak. İnternetteki Azure DevOps anlatımlarının çoğu classic build ve classic release pipeline'larını, yani web arayüzünde sürükle bırak ile kurulan tanımları gösterir. Sprint 226'dan (2023) beri yeni organization'larda classic build ve release pipeline oluşturma varsayılan olarak kapalı geliyor; organization veya project ayarlarındaki iki seçenek bunu yönetiyor ve ikisi de kapalıyken task group ve deployment group da oluşturulamıyor. "New release pipeline" düğmesini bulamıyorsan sorun sende değil. Bu seride her şey YAML; classic tanımlardan geçiş son yazının konusu.

Pull request doğrulamasını YAML'da beklemek. Giriş bölümündeki durum: Azure Repos'ta pr: bloğu çalışmıyor, branch policy gerekiyor. Ters yönde de hata yapılıyor: GitHub repository'sine bağlı bir pipeline'da pr: bloğunu hiç yazmazsan bütün branch'lere açılan pull request'ler için run başlıyor.

Trigger yazmayı unutmak. trigger bloğu olmayan bir pipeline bütün branch'lerdeki her push'ta koşar. Tek parallel job'lı bir organization'da bu, kimsenin bakmadığı branch'lerin kuyruğu doldurması demek.

Agent'taki yolları elle yazmak. /home/vsts/work/1/s gibi bir yolu script'e gömmek bir gün image düzeni değiştiğinde kırılır. Belgeler hosted agent'taki klasör düzeninin haber verilmeden değişebileceğini söylüyor; bunun yerine $(Build.SourcesDirectory), $(Pipeline.Workspace) gibi önceden tanımlı değişkenleri kullan.

Secret değişkeni environment variable sanmak. Script'te $TESTDBCONNECTION diye okumaya çalışırsan boş gelir; secret değişkenleri env: ile açıkça bağlaman gerekiyor. Bir sonraki yazıda bunun nedenini de göreceğiz.

Küçük Bir Deneme

★☆☆ Bu yazıdaki azure-pipelines.yml'ı kendi bir .NET projene uyarla, yq ile step adlarını listele, sonra bir girintiyi bilerek bozup hata mesajını oku.

★★☆ start-test-sql.sh'teki bekleme döngüsünü inceleyip 30 deneme ve 2 saniyelik bekleme yerine toplam süreyi bir parametreyle ayarlanabilir yap. Script'in başarısız olduğu durumda pipeline log'unda hangi satırın kırmızı hata olarak görüneceğini düşün.

★★★ Pipeline'a Angular uygulaması için ikinci bir job ekle (jobs: altında iki job). Tek parallel job'lı bir organization'da iki job'ın toplam süresini, iki parallel job'lı bir organization'dakiyle karşılaştır ve farkın nereden geldiğini yaz.

Bu pipeline'da $(buildConfiguration) ve $(TestDbConnection) gibi iki değişken kullandık ve ikisini de aynı yazımla okuduk. Oysa Azure Pipelines'ta ${{ }} ve $[ ] diye iki yazım daha var ve hangisinin ne zaman işlendiğini bilmemek en sık rastlanan pipeline hatalarının kaynağı. Bir sonraki yazıda variable, parameter ve expression'lara ve trigger türlerine bakacağız.