CI Pipeline
Giriş — Otomatik Kalite Güvencesi
Diyelim ki büyük bir yazılım ekibindesin. Her gün onlarca PR açılıyor. Bir PR'ı review ederken "lint hatası var mı?", "testler geçiyor mu?", "build bozulmuş mu?" sorularını elle kontrol etmek insanüstü bir çaba gerektirir. Birisi bir typo yapıyor, birisi unused import bırakıyor, birisi test yazmayı unutuyor.
İşte CI pipeline bu kaosun panzehiri. Her PR açıldığında, her push yapıldığında otomatik olarak kod kalitesini kontrol eden, testleri çalıştıran, build'i doğrulayan bir sistem. "İnsan hata yapar, makine yapmaz" — CI'ın felsefesi bu.
Bu derste bir CI pipeline'ının her katmanını öğreneceksin: lint ile kod stilini, test ile doğruluğu, build ile çalışabilirliği kontrol etmeyi; artifact'larla çıktıları saklamayı; status badge'lerle durumu görselleştirmeyi; branch protection ile merge'i korumalı hale getirmeyi göreceğiz.
🎬 Analoji: Yiyecek Fabrikası Kalite Kontrol Hattı
Bir çikolata fabrikasını düşün. Çikolatalar üretim bandından geçerken birden fazla kalite kontrol noktası var:
Görsel Kontrol (Lint): Çikolatanın şekli düzgün mü? Çatlak var mı? → Kod stil kurallarına uyuyor mu?
Tat Testi (Unit Test): Tadı doğru mu? Acı, tatlı, tuzlu oranları tutarlı mı? → Fonksiyonlar beklenen çıktıyı veriyor mu?
Paketleme (Build): Kutulama düzgün mü? Etiket doğru mu? → Kod derlenip paketlenebiliyor mu?
Sertifika (Status Badge): "ISO 9001" damgası vur → "CI Passing" badge'i
Bir kontrol noktasını geçemeyen ürün hattan çıkarılır (PR merge edilemez).
PR Açıldı
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 🔍 Lint │───►│ 🧪 Test │───►│ 🏗️ Build │───►│ 📦 Artifact│
│ │ │ │ │ │ │ │
│ ESLint │ │ Jest │ │ tsc/vite │ │ dist/ │
│ Prettier │ │ Coverage │ │ webpack │ │ upload │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ ❌ │ ❌ │ ❌
│ │ │
▼ ▼ ▼
Merge Merge Merge
ENGELLENDİ ENGELLENDİ ENGELLENDİKatman 1: Lint — Kod Stilini Kontrol Et
Lint Nedir?
Lint (veya linter), kodun stil kurallarına uygunluğunu kontrol eden araçtır. Hata bulmaz — kodun okunabilirliğini, tutarlılığını ve yaygın kötü pratikleri tespit eder.
Lint'in yakaladığı sorunlar:
─────────────────────────────
✗ Kullanılmayan değişkenler (unused variables)
✗ Tanımlanmamış değişkenler
✗ Tutarsız kodlama stili (tab vs space, single vs double quote)
✗ console.log kalıntıları
✗ Potansiyel hatalar (== yerine ===)
✗ Erişilemez kod (unreachable code)
✗ Boş catch bloklarıESLint Workflow Adımı
jobs:
lint:
name: 🔍 Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
# ESLint — Kod kalitesi
- name: Run ESLint
run: npx eslint . --format=compact
# --format=compact → daha okunabilir CI çıktısı
# Prettier — Kod formatlama
- name: Check formatting
run: npx prettier --check .
# --check = sadece kontrol (düzeltme yapmaz)
# --write = düzeltir (CI'da kullanma, PR'da kullan)
# TypeScript — Tip kontrolü
- name: Type check
run: npx tsc --noEmit
# --noEmit = dosya üretme, sadece tipleri kontrol etLint Sonuçlarını PR'a Yorum Olarak Ekle
lint:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run ESLint
id: eslint
run: npx eslint . --format json --output-file eslint-report.json || true
- name: Annotate ESLint results
uses: ataylorme/eslint-annotate-action@v3
with:
report-json: eslint-report.jsonSonuç: ESLint hataları PR'daki ilgili satırlarda annotation olarak görünür:
src/auth.js
────────────────────────────────────────────────
Line 12: 'password' is defined but never used. (no-unused-vars) ⚠️
Line 25: Expected '===' and instead saw '=='. (eqeqeq) ⚠️Katman 2: Test — Kodun Doğruluğunu Kontrol Et
Test Türleri
┌─────────────────────────────────────────────────────────────┐
│ TEST PİRAMİDİ │
│ │
│ /\ │
│ / \ E2E (End-to-End) Tests │
│ / \ → Yavaş, az sayıda, tam akış │
│ /──────\ │
│ / \ Integration Tests │
│ / \ → Orta hız, modüller arası │
│ /────────────\ │
│ / \ Unit Tests │
│ / \ → Hızlı, çok sayıda, tek birim│
│ /──────────────────\ │
│ │
│ CI'da çalıştırılacaklar: │
│ ✅ Unit Tests → Her PR'da (hızlı) │
│ ✅ Integration Tests → Her PR'da (orta) │
│ ⚠️ E2E Tests → Main merge veya nightly (yavaş) │
└─────────────────────────────────────────────────────────────┘Jest ile Test Çalıştırma
jobs:
test:
name: 🧪 Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
# Unit testleri çalıştır
- name: Run unit tests
run: npm test -- --coverage --ci
env:
CI: true
# --coverage → kapsam raporu üret
# --ci → CI modunda çalış (watch mode kapalı, snapshot güncellemesi yok)Test Coverage (Kapsam)
Test coverage, kodun ne kadarının testlerle kapsandığını gösterir:
# Terminal çıktısı:
$ npm test -- --coverage
----------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
----------|---------|----------|---------|---------|
All files | 85.71 | 75.00 | 90.00 | 85.71 |
auth.js | 100.00 | 100.00 | 100.00 | 100.00 |
user.js | 71.43 | 50.00 | 80.00 | 71.43 |
----------|---------|----------|---------|---------| # Coverage eşik kontrolü
- name: Check coverage threshold
run: |
npx jest --coverage --coverageReporters=json-summary
node -e "
const coverage = require('./coverage/coverage-summary.json');
const total = coverage.total;
const lines = total.lines.pct;
console.log('Line coverage: ' + lines + '%');
if (lines < 80) {
console.error('Coverage is below 80%!');
process.exit(1);
}
"Veritabanı ile Test (Service Container)
Testlerin veritabanına ihtiyaç duyuyorsa, GitHub Actions service container sağlar:
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests with database
run: npm test
env:
DATABASE_URL: postgres://test:test@localhost:5432/testdb
REDIS_URL: redis://localhost:6379Python Projelerinde Test
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
run: pytest --cov=src --cov-report=xml
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-py${{ matrix.python-version }}
path: coverage.xmlKatman 3: Build — Derlenip Paketlenebiliyor mu?
Neden Build Kontrolü?
Kod lint'ten geçti, testler başarılı — ama bu "production'a deploy edilebilir" anlamına gelmiyor. Build adımı şunları doğrular:
Build kontrolünün yakaladığı sorunlar:
─────────────────────────────────────────
✗ TypeScript derleme hataları
✗ Import edilen ama kurulmamış paketler
✗ Environment variable eksiklikleri
✗ Asset (resim, font) referans hataları
✗ Webpack/Vite yapılandırma sorunları
✗ Tree-shaking sonrası bozulan kodlarBuild Workflow Adımı
jobs:
build:
name: 🏗️ Build
runs-on: ubuntu-latest
needs: [lint, test] # Lint ve test başarılıysa çalış
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Build project
run: npm run build
env:
NODE_ENV: production
# Build çıktısını kontrol et
- name: Verify build output
run: |
ls -la dist/
if [ ! -f dist/index.html ]; then
echo "❌ index.html not found in build output!"
exit 1
fi
echo "✅ Build output verified"
# Build boyutunu göster
- name: Report build size
run: |
echo "Build size:"
du -sh dist/
echo ""
echo "File details:"
find dist -type f -name "*.js" -exec du -h {} +Artifact — Build Çıktılarını Sakla
Artifact, bir job'un ürettiği dosyaları saklama mekanizmasıdır. Build çıktısı, test raporları, coverage dosyaları — bunları artifact olarak yükleyebilirsin.
Artifact Yükleme ve İndirme
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
# Artifact yükle
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build-output # Artifact adı
path: dist/ # Yüklenecek dosya/dizin
retention-days: 7 # 7 gün sakla (varsayılan: 90)
if-no-files-found: error # Dosya yoksa hata ver
deploy:
runs-on: ubuntu-latest
needs: build
steps:
# Artifact indir (başka job'dan)
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy
run: |
ls -la dist/
# deploy komutu...Test Raporu Artifact'ı
- name: Run tests
run: npm test -- --ci --coverage
- name: Upload test results
if: always() # Testler başarısız olsa bile yükle!
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
coverage/
test-results/
junit-report.xmlGitHub UI'da Artifact'lar:
Actions → Workflow Run → Artifacts bölümü:
┌─────────────────────────────────────────────┐
│ Artifacts │
│ │
│ 📦 build-output (2.3 MB) [Download ⬇️] │
│ 📦 test-results (156 KB) [Download ⬇️] │
│ 📦 coverage-report (89 KB) [Download ⬇️] │
│ │
│ These artifacts expire in 7 days │
└─────────────────────────────────────────────┘💡 İpucu:
if: always()eklemeyi unutma! Testler başarısız olduğunda artifact yüklenmezse, hata nedenini araştırmak zorlaşır. Test raporları özellikle başarısız olduğunda en çok işine yarar.
Status Badge — Durumu Görselleştir
README.md'ye CI durumunu gösteren bir badge ekle:
Badge URL Formatı
<!-- README.md -->
# Projem

Badge görünümü:
Başarılı: [CI ✅ passing] (yeşil)
Başarısız: [CI ❌ failing] (kırmızı)
Çalışıyor: [CI 🔄 running] (sarı)Badge Ekleme (GitHub UI)
1. Repository → Actions → Sol menüden workflow seç
2. Sağ üstte "..." menüsü → "Create status badge"
3. Branch seç, Markdown'ı kopyala
4. README.md'ye yapıştırBirden Fazla Badge
# Projem
[](https://github.com/user/repo/actions/workflows/ci.yml)
[](https://github.com/user/repo/actions/workflows/deploy.yml)
[](https://codecov.io/gh/user/repo)
[](https://opensource.org/licenses/MIT)Branch Protection — Merge'i Koruma Altına Al
CI pipeline'ın var ama birisi pipeline çalışmadan veya başarısız olmasına rağmen merge edebilir. Branch protection rules bunu engeller.
Branch Protection Ayarlama
GitHub → Repository → Settings → Branches
→ "Add branch protection rule"
Branch name pattern: main
☑ Require a pull request before merging
☑ Require approvals: 1 (minimum review sayısı)
☑ Dismiss stale pull request approvals when new commits are pushed
→ Yeni commit gelince eski approval'ları iptal et
☑ Require status checks to pass before merging ← EN ÖNEMLİ!
☑ Require branches to be up to date before merging
Status checks:
→ lint ✓
→ test ✓
→ build ✓
☑ Require conversation resolution before merging
→ Tüm review yorumları çözülmeli
☑ Do not allow bypassing the above settings
→ Admin bile kuralları atlayamazNasıl Çalışır?
PR açıldı → CI pipeline başladı:
┌──────┐ ┌──────┐ ┌───────┐
│ lint │────►│ test │────►│ build │
│ ✅ │ │ ✅ │ │ ✅ │
└──────┘ └──────┘ └───────┘
Branch protection kontrolleri:
✅ lint — passed
✅ test — passed
✅ build — passed
✅ 1 approval received
✅ No unresolved conversations
┌─────────────────────────┐
│ ✅ Merge pull request │ ← Aktif (tıklanabilir)
└─────────────────────────┘PR açıldı → CI pipeline başladı:
┌──────┐ ┌──────┐
│ lint │────►│ test │
│ ✅ │ │ ❌ │ ← Test başarısız!
└──────┘ └──────┘
Branch protection kontrolleri:
✅ lint — passed
❌ test — failed ← Bu yüzden merge engellenmiş
⏭️ build — skipped
✅ 1 approval received
┌─────────────────────────┐
│ 🚫 Merge pull request │ ← Devre dışı (tıklanamaz)
│ "Required checks failed"│
└─────────────────────────┘Required Status Checks Ayarlama
# ci.yml'deki job isimleri ile branch protection'daki
# status check isimleri eşleşmeli:
jobs:
lint: # ← Branch protection'da "lint" olarak görünür
name: 🔍 Lint
runs-on: ubuntu-latest
...
test: # ← Branch protection'da "test" olarak görünür
name: 🧪 Test
runs-on: ubuntu-latest
...⚠️ Dikkat: Matrix strategy kullanıyorsan, her matrix kombinasyonu ayrı bir status check olarak görünür.
test (18),test (20),test (22)gibi. Hepsini required olarak ekle veya tek bir "summary" job oluştur.
Matrix ile Çalışan Required Check Pattern
jobs:
test:
strategy:
matrix:
node-version: [18, 20, 22]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci && npm test
# Tüm test job'larını toplayan "gate" job:
test-gate:
name: ✅ All Tests Passed
runs-on: ubuntu-latest
needs: test # Tüm matrix job'ları tamamlanınca
if: always()
steps:
- name: Check test results
run: |
if [ "${{ needs.test.result }}" != "success" ]; then
echo "❌ Some tests failed!"
exit 1
fi
echo "✅ All tests passed!"
# Branch protection'da sadece "✅ All Tests Passed" check'ini ekle
# Tek bir check ile tüm matrisi kapsaTam CI Pipeline: Gerçek Dünya Örneği
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
paths-ignore: ['**.md', 'docs/**']
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
env:
NODE_VERSION: '20'
jobs:
# ═══════════════════════════════════
# Katman 1: Kod Kalitesi
# ═══════════════════════════════════
lint:
name: 🔍 Lint & Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: ESLint
run: npx eslint . --max-warnings=0
- name: Prettier
run: npx prettier --check .
- name: TypeScript
run: npx tsc --noEmit
# ═══════════════════════════════════
# Katman 2: Testler
# ═══════════════════════════════════
test:
name: 🧪 Test (Node ${{ matrix.node }})
runs-on: ubuntu-latest
needs: lint
strategy:
fail-fast: false
matrix:
node: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- name: Unit & Integration Tests
run: npm test -- --ci --coverage
- name: Upload coverage (Node 20 only)
if: matrix.node == 20 && always()
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
# ═══════════════════════════════════
# Katman 3: Build
# ═══════════════════════════════════
build:
name: 🏗️ Build
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
# ═══════════════════════════════════
# Gate: Tüm Kontroller Başarılı mı?
# ═══════════════════════════════════
ci-gate:
name: ✅ CI Passed
runs-on: ubuntu-latest
needs: [lint, test, build]
if: always()
steps:
- name: Verify all checks
run: |
echo "Lint: ${{ needs.lint.result }}"
echo "Test: ${{ needs.test.result }}"
echo "Build: ${{ needs.build.result }}"
if [ "${{ needs.lint.result }}" != "success" ] || \
[ "${{ needs.test.result }}" != "success" ] || \
[ "${{ needs.build.result }}" != "success" ]; then
echo "❌ CI failed!"
exit 1
fi
echo "✅ All CI checks passed!"Pipeline Akış Diyagramı:
┌─────────────┐
│ PR / Push │
└──────┬──────┘
│
┌──────▼──────┐
│ 🔍 Lint │
│ (ESLint, │
│ Prettier, │
│ TypeScript)│
└──┬─────┬───┘
│ │
┌────────▼─┐ ┌─▼────────┐
│ 🧪 Test │ │ 🏗️ Build │
│ Node 18 │ │ │
│ Node 20 │ │ │
│ Node 22 │ │ │
└────────┬─┘ └─┬────────┘
│ │
┌──▼─────▼───┐
│ ✅ CI Gate │
│ (required │
│ check) │
└────────────┘Yaygın Hatalar
1. needs ile Lint ve Test'i Sıralı Yapmak
# ❌ Gereksiz bağımlılık — build lint'i bekliyor
jobs:
lint:
...
test:
needs: lint # OK: test lint'e bağımlı
...
build:
needs: test # Yavaş: build da sıralı bekliyor
...
# ✅ Lint sonrası test ve build PARALEL:
jobs:
lint:
...
test:
needs: lint # Lint'e bağımlı
...
build:
needs: lint # Lint'e bağımlı (test'e değil!)
...❌ Sıralı (yavaş):
lint (2dk) → test (3dk) → build (1dk) = 6 dk
✅ Paralel (hızlı):
lint (2dk) → test (3dk) } = 5 dk
→ build (1dk) } (paralel)2. if: always() Kullanmamak
# ❌ Test başarısız olunca coverage yüklenmez
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4 # Test fail → bu step atlanır!
with:
name: coverage
path: coverage/
# ✅ always() ile her zaman yükle
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
if: always() # Test fail olsa bile yükle
with:
name: coverage
path: coverage/3. Branch Protection Status Check Uyumsuzluğu
# ❌ Workflow dosyasındaki job adını değiştirdin ama branch protection'ı güncellemedin
# Eski: lint → Yeni: code-quality
# Branch protection hâlâ "lint" arıyor → sürekli "pending" kalır
# ✅ Job adını değiştirirsen, branch protection settings'i de güncelle!CI Pipeline En İyi Pratikleri
🏆 EN İYİ PRATİKLER:
────────────────────────────
1. HER ZAMAN cache kullan
→ npm ci 2 dakikadan 15 saniyeye düşer
2. Lint'i ilk çalıştır
→ En hızlı check; hata varsa test/build'a bile girme
3. fail-fast: false kullan
→ Hangi ortamlarda sorun olduğunu tam gör
4. paths-ignore ile gereksiz çalıştırmaları önle
→ README değişikliği CI tetiklemesin
5. concurrency ile eski workflow'ları iptal et
→ Kaynak israfını önle
6. Minimum permissions tanımla
→ Güvenlik için gerekli olmayan izinleri verme
7. CI gate job oluştur
→ Branch protection'da tek check yönetmek kolay
8. Artifact'ları retention-days ile sınırla
→ 90 gün yerine 7 gün yeterli, storage tasarrufu
9. npm ci kullan (npm install değil)
→ Deterministik build, package-lock.json'a sadık
10. Test çıktılarını always() ile yükle
→ Hata analizi için en kritik veri, başarısızlıkta gerekliÖzet
CI pipeline her PR ve push'ta otomatik kalite kontrolü sağlar — lint, test ve build katmanlarıyla kodun güvenilirliğini garanti eder
Lint (ESLint, Prettier) kod stilini kontrol eder — en hızlı check olduğu için pipeline'ın ilk adımı olmalıdır
Test (Jest, pytest) kodun doğruluğunu doğrular — coverage raporları ile test kapsamını ölç, matrix strategy ile farklı ortamlarda test et
Artifact'lar build çıktılarını ve test raporlarını saklar —
if: always()ile başarısız olsa bile yükle,retention-daysile storage yönetimini kontrol etStatus badge'ler CI durumunu README'de görselleştirir — projenin sağlığını tek bakışta gösterir
Branch protection rules CI başarısız olduğunda merge'i engeller —
Require status checks to passile kalite kapısı oluştur, CI gate job ile tüm check'leri tek noktada topla
*Bir sonraki derste CI'ın devamı olan CD (Continuous Deployment) pipeline'larını öğreneceğiz: Otomatik deploy, environment'lar, approval gate ve rollback!*
AI Asistan
Sorularını yanıtlamaya hazır