Bir pull request açtığında testlerin kendiliğinden koştuğunu, main dalına birleşince sitenin otomatik güncellendiğini gördüysen, arkasında büyük ihtimalle GitHub Actions vardı. GitHub bu özelliği 2019'da kullanıma açtı; o günden beri ayrı bir CI/CD aracı kurmadan, otomasyonu doğrudan reponun içine tanımlayabilirsin. Jenkins gibi araçlarla kıyaslandığında kurulum yükü büyük ölçüde ortadan kalkıyor, çünkü çalıştırma ortamları (runner) zaten GitHub'ın sunucularında hazır duruyor.
Bu yazıda workflow dosyasının yapısını, ilk pipeline'ı nasıl kuracağını, matrix build ile birden fazla ortamda test etmeyi ve secrets yönetimini gerçek YAML örnekleriyle anlatıyorum. Örnekleri kendi reponda deneyerek takip edebilirsin.
GitHub Actions Nedir ve Nasıl Çalışır
GitHub Actions, reponun .github/workflows/ klasörüne yazılan YAML dosyalarıyla tetiklenen bir otomasyon sistemi. Push, pull request açılması, issue oluşturulması ya da zamanlanmış bir saat gibi bir olay gerçekleştiğinde GitHub bu dosyayı okuyor ve tanımlı adımları kendi sunucularında, runner denen geçici sanal makinelerde çalıştırıyor.
Runner'lar varsayılan olarak Ubuntu, Windows veya macOS imajı kullanıyor; her çalıştırmada sıfırdan kuruluyor, iş bitince siliniyor. Kendi sunucunu self-hosted runner olarak da bağlayabilirsin, GPU gerektiren bir build ya da şirket içi ağa erişim gereken bir iş için bu tercih ediliyor. Ücretsiz katmanda public repolar sınırsız dakika kullanabilir, private repolarda ise aylık bir dakika kotası var. Bu kota zamanla değişebilir; güncel rakamı GitHub'ın fiyatlandırma sayfasından kontrol etmek gerekiyor.
Sonuçta Jenkins gibi ayrı bir sunucu kurup bakımını üstlenmene gerek kalmıyor. Workflow dosyası reponun bir parçası olduğu için kodla birlikte versiyonlanıyor ve pull request içinde review edilebilir.
İş Akışı Dosyasının Anatomisi: Trigger, Job, Step
Her workflow dosyası üç katmandan kuruluyor. En üstte on: anahtarı hangi olayın workflow'u tetikleyeceğini belirliyor. Onun altında jobs: bloğu yer alıyor; her job kendi runner'ında, diğerlerinden bağımsız çalışıyor. Job'ın içinde de sırayla işleyen steps: listesi bulunuyor.

Basit bir CI workflow'u şöyle kuruluyor:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
actions/checkout@v4 adımı reponu runner'a klonluyor, actions/setup-node@v4 adımı Node.js kuruyor. Sonraki iki satır bağımlılıkları yüklüyor ve testleri çalıştırıyor. uses: hazır bir action'ı çağırırken run: doğrudan shell komutu çalıştırıyor; aynı step içinde ikisi bir arada kullanılamıyor, her step ya biri ya diğeri. Workflow sözdiziminin temellerini GitHub Actions'ın resmi öğreticisinden öğrenebilirsiniz.
Tetikleyici seçimi önemli, çünkü yanlış tetikleyici gereksiz dakika tüketiyor. En sık kullanılan dört tetikleyici şöyle:
| Tetikleyici | Ne zaman çalışır | Tipik kullanım |
|---|---|---|
| push | Belirtilen branch'e commit gidince | Her commit'te lint/test |
| pull_request | PR açılınca veya güncellenince | Merge öncesi doğrulama |
| schedule | Cron ifadesiyle belirlenen saatte | Gece nightly build, bağımlılık taraması |
| workflow_dispatch | Manuel olarak Actions sekmesinden tetiklenince | Elle tetiklenen deploy |
İlk CI/CD Boru Hattını Kurmak
CI (continuous integration) kısmı genelde test ve lint çalıştırmakla sınırlı kalıyor: kod push'landığında hatalı bir şeyi erken yakalamak amacı taşıyor. CD (continuous deployment) ise testler geçtikten sonra kodu otomatik olarak bir ortama göndermek anlamına geliyor. Bu ortam staging olabilir, production olabilir.
Test ve deploy işini ayrı job'lara bölmek mantıklı, çünkü deploy job'ı yalnızca test job'ı başarılı olduğunda çalışmalı. Bu bağımlılığı needs: anahtarıyla kuruyorsun:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to server
run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
if: koşulu deploy'un yalnızca main dalında çalışmasını sağlıyor. Bir feature branch'te testler geçse bile deploy tetiklenmiyor. Bu ayrım takım büyüdükçe daha da önem kazanıyor, çünkü herkesin branch'inde otomatik deploy denemesi hem gereksiz hem riskli.
Gerçek projelerde genelde üçüncü bir job daha eklenir: build. Docker image oluşturup registry'ye push etmek ya da statik siteyi derleyip CDN'e yüklemek gibi işler test ile deploy arasına giriyor. Job sayısı arttıkça needs: [test, build] yazarak birden fazla bağımlılık da tanımlayabilirsin.
Matris Derlemeleri ile Çoklu Ortam Testi
Bir kütüphane geliştiriyorsan tek Node sürümünde test etmek yeterli olmuyor, çünkü kullanıcıların hangi sürümü çalıştıracağını bilemezsin. Matrix strategy tam bu noktada işe yarıyor: aynı job'ı farklı parametre kombinasyonlarıyla paralel çalıştırıyor.

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
Bu tanım aynı test job'ını üç kez, her defasında farklı bir Node sürümüyle paralel çalıştırıyor. Matrise os: [ubuntu-latest, windows-latest, macos-latest] ekleyerek sürüm ve işletim sistemini çapraz kombinasyonlarla da test edebilirsin. Üç sürüm çarpı üç işletim sistemi, dokuz ayrı çalıştırma anlamına geliyor.
Dikkat edilmesi gereken nokta, matrix'in dakika tüketimini katlaması. Private repoda kota kısıtlıysa exclude: ile gereksiz kombinasyonları eleyip yalnızca gerçekten desteklediğin kombinasyonları bırakman gerekiyor.
Gizli Bilgileri ve İzinleri Güvenli Yönetmek
API anahtarını, deploy token'ını ya da veritabanı şifresini workflow dosyasına düz metin olarak yazma. Bu bilgileri repo ayarlarındaki Settings > Secrets and variables > Actions bölümünden tanımlıyorsun, workflow içinde de ${{ secrets.SECRET_ADI }} şeklinde çağırıyorsun.

- name: Push Docker image
run: docker push myapp:latest
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
Secrets, log çıktısında otomatik olarak *** şeklinde maskeleniyor. Yanlışlıkla echo $REGISTRY_TOKEN yazsan bile değer ekrana basılmıyor. Fork'lardan gelen pull request'lerde secrets'a erişim varsayılan olarak kısıtlı. Bu kısıtlama, kötü niyetli birinin PR açıp secrets'ı sızdırmaya çalışmasını önlemek için konulmuş. Bu davranışı anlamadan değiştirmemek gerekiyor.
En az ayrıcalık ilkesini workflow izinlerinde de uygulaman gerekiyor. GITHUB_TOKEN her çalıştırmada otomatik oluşuyor ve varsayılan olarak geniş yetkiler taşıyabilir. permissions: bloğuyla bu yetkiyi daraltmak iyi bir alışkanlık:
permissions:
contents: read
pull-requests: write
Organizasyon seviyesinde hangi action'ların kullanılabileceğini de kısıtlayabilirsin. Marketplace'te binlerce hazır action bulunuyor ama hepsi resmi GitHub tarafından yazılmıyor. Üçüncü taraf bir action'ı prod pipeline'ına eklemeden önce kaynağını incelemek makul bir önlem. Mümkünse commit SHA'sına pin'lemek de bu önlemi güçlendiriyor.
Workflow'u Hızlandırmak: Cache ve Paralel Çalıştırma
Bağımlılık kurulumu her çalıştırmada baştan yapılırsa dakikalar boşa gidiyor. actions/cache action'ı node_modules, pip paketleri ya da Maven repository'sini önbelleğe alarak bunu engelliyor.
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
Cache anahtarı package-lock.json dosyasının hash'ine bağlanıyor. Bağımlılıklar değişmediği sürece aynı cache tekrar kullanılıyor, değiştiğinde ise otomatik olarak yeni bir cache oluşuyor. Bu sayede eski veriyle çalışma riski ortadan kalkıyor.
Başka bir hız kazanımı gereksiz tetiklemeleri önlemekten geliyor. Yalnızca docs/ klasöründeki bir dosya değiştiyse testleri yeniden çalıştırmanın bir anlamı yok:
paths:vepaths-ignore:ayarlarıyla hangi dosya değişikliklerinin workflow'u tetikleyeceğini sınırlayabilirsinneeds:kullanmadan bıraktığın bağımsız job'ları GitHub otomatik olarak paralel çalıştırırconcurrency:ayarıyla aynı branch'te üst üste gelen çalıştırmaları iptal edip yalnızca sonuncusunu bekletebilirsin
Bu üç ayarı birlikte uyguladığında, özellikle büyük monorepo'larda pipeline süresi ciddi oranda kısalabilir.
Actions'ı Kod Dışında Kullanmak
GitHub Actions yalnızca build-test-deploy için kullanılmıyor. Issue açıldığında otomatik etiket eklemek, uzun süre güncellenmeyen pull request'leri kapatmak ya da belirli aralıklarla bağımlılık güncellemelerini kontrol etmek gibi proje yönetimi işlerini de otomatikleştirebilirsin.
workflow_dispatch ile manuel tetiklenen bir workflow tanımlayabilirsin. Haftalık rapor oluşturmak ya da eski branch'leri temizlemek gibi tek seferlik işleri Actions sekmesinden tek tıkla çalıştırabilirsin. Marketplace'teki actions/stale gibi hazır action'lar bu tür rutin işleri sıfırdan yazmana gerek bırakmıyor.
GitHub'ın resmi Actions dokümantasyonunu takip etmek, yeni eklenen özellikleri ve güncel limitleri görmek için iyi bir alışkanlık. Workflow dosyasını küçük tutmak ve her job'a tek bir sorumluluk vermek, dosyayı altı ay sonra tekrar açtığında ne yaptığını hatırlamanı kolaylaştırıyor.
Henüz yorum yok.
Sohbete katıl. Yorumlar yayınlanmadan önce moderasyondan geçer.