İçeriğe atla

2026-05-19

GitHub Actions'ta Üretim Onayı: Environment Kullan, if: Değil

Üretim deploy'ları gerçek bir onay adımı ister. Doğru yer: GitHub Environment, native koruma kuralları ve environment'a bağlı secret'lar; if: hilesi ya da üçüncü parti onay action'ı değil.

Üretim deploy’larında insan onayı gerekir, ama çoğu ekip yanlış parçayı seçer. En sık görülen üç şekil şudur: bir label’a bakan workflow if: koşulu, issues: write izniyle yorum bekleyen marketplace onay action’ı, ya da herkesin tetikleyebildiği workflow_dispatch butonu. Artifact’ı main üzerinden bir kez üretin, staging’e otomatik düşsün, aynı artifact production’a ancak bir PM ya da QA bir GitHub Environment kapısında onay verdikten sonra promote edilsin. Stage başına yeniden build yok — aynı artifact, iki environment, tek onay; insanı ve credential’ı aynı kapının arkasına koyan ve run’a bağlı audit kaydı bırakan tek şekil budur.

Environment nedir

GitHub Environment, repo üzerinde adlandırılmış bir nesnedir ve üç şey tutar: environment secret’ları, environment variable’ları ve deployment protection rule’lar. UI’da Settings → Environments altında, REST API’da /repos/{owner}/{repo}/environments adresinde yaşar. Bir job environment: ile bir environment’ı hedef aldığında, koruma kuralları geçmeden ne secret’ları çözebilir ne de adımlarını çalıştırabilir. Koruma kuralı ile secret erişimi aynı kapıdır; bu özellik soyutlamayı değerli kılan şeydir.

Üç deployment protection rule

Bunlar branch protection rule değil. Branch protection (ve yeni repository ruleset’ler) main’e merge’ü kapatır. Deployment protection rule ise Environment nesnesinde yaşar ve o environment’ı hedefleyen deploy’u kapatır; merge’ten sonra, bir job artifact’ı alıp deploy etmeye çalıştığında devreye girer. GitHub üç tane sunuyor.

Required reviewers. En fazla altı kullanıcı veya takım. Onaycıların repo’ya en azından read erişimi olmalıdır. Listedeki onaycılardan yalnızca birinin onay vermesi yeterlidir; diğerleri görmezden gelebilir. Onay formundaki yorum kutusu run başına audit izini taşır. Prevent self-review anahtarı vardır ve default’u kapalıdır; her üretim environment’ında bunu açın.

Wait timer. 1 ile 43.200 dakika (30 gün) arasında bir değer. Deploy job’ı tetiklendikten sonra bu süre boyunca Waiting durumunda bekler. Bekleme süresi faturalandırılan Actions dakikalarına dahil edilmez.

Deployment branch and tag policies. Üç mod: kısıtlama yok, sadece protected branch’ler, ya da isim deseniyle seçilen branch ve tag’ler (main, release/*, v*.*.*). Hangi ref’lerin bu environment’a deploy talebi açabileceğini filtreler; bu olmadan bir feature branch kapıda bekleyip onay isteyebilir.

Bir de kaçış kapısı var. Bir job, job-level deployment: false (Mart 2026) ayarıyla bir environment’ı hedef alabilir; kapıda kuyruklanmaz, deployment kaydı oluşmaz. Bu, plan-only iş yükleri için doğru şekildir — terraform plan, dry-run build, manifest üretici gibi environment secret’ına read erişimi isteyen ama onaya düşmemesi gereken job’lar. Kısıtlama: deployment: false, custom deployment protection rule’lar ile birlikte kullanılamaz; özel kural taşıyan bir environment’ı hedef alan her job hâlâ auto-deploy gerektirir.

Adım adım kurulum

Bir tag, iki environment, tek onay. main üzerine atılan bir tag artifact’ı bir kez üretir, registry’ye push’lar ve staging’e otomatik deploy eder. Ayrı bir workflow_dispatch workflow’u aynı image’ı production’a promote eder ve Environment kapısında PM ya da QA onayını bekler. Build stage başına tekrar etmez; production’a giden byte’lar staging’i geçen byte’larla aynıdır.

# .github/workflows/release.yml
# main'e tag push'unda: artifact bir kez build edilir, staging'e otomatik deploy edilir.
name: release
on:
  push:
    tags: ['v*.*.*']

permissions:
  contents: read
  packages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
    steps:
      - uses: actions/checkout@v6
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - uses: actions/checkout@v6
      - run: ./deploy.sh staging "${{ needs.build.outputs.image }}"
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

staging yine bir Environment’tır — staging deploy credential’ını, staging URL’sini ve dilerseniz bir tag policy’sini tutar. Required reviewer yoktur, dolayısıyla deploy job’ı build biter bitmez çalışır.

Production ayrı bir workflow’ta yaşar. Input olarak bir tag alır, asla yeniden build etmez ve registry’deki mevcut image’ı çeker. production Environment’ı required-reviewers listesini taşır; bir PM ya da QA onaylayana kadar run kapıda bekler.

# .github/workflows/promote.yml
# Mevcut bir image'i production'a manuel promote. Required reviewer kapida bekletir.
name: promote
on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Promote edilecek release tag (orn. v1.4.2)'
        required: true

concurrency:
  group: deploy-production-${{ inputs.tag }}
  cancel-in-progress: false

permissions:
  contents: read
  packages: read
  id-token: write

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://app.example.com
    steps:
      - uses: actions/checkout@v6
      - run: ./deploy.sh production "ghcr.io/${{ github.repository }}:${{ inputs.tag }}"
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

DEPLOY_TOKEN her Environment’ta ayrı bir environment secret’ı olarak yaşar — staging deploy staging token’ını, production deploy production token’ını okur; iki workflow birbirinin secret’ına dokunamaz. id-token: write izni, deploy adımının OIDC token üretip kısa ömürlü cloud credential alabilmesi içindir. production Environment’ına v*.*.* desenli bir tag policy koymak, promote workflow’unun yalnızca release tag’leriyle çalışmasını sağlar; workflow_dispatch’i bir feature branch adıyla tetikleyen biri kapıda durur. promote.yml’in başındaki concurrency bloğu da aynı tag için iki eşzamanlı promote denemesinin yarışmasını engeller; ikincisi birincisi bitene kadar kuyrukta bekler.

Onaycı tarafı şöyle işler: deploy yetkili biri gh workflow run promote.yml -f tag=v1.4.2 der ya da UI’da Run workflow der. GitHub deploy-production job’ını kuyruğa alır, Deployments görünümüne yazar ve listedeki onaycılara mail atar. Onaycı Review deployments der, production’ı işaretler, dilerse bir yorum yazar ve Approve and deploy ya da Reject der. Yorum, audit string’i olarak saklanır.

Aynı akışın API tarafı, change ticket bot’ları için faydalıdır:

# Mevcut bir tag'e karşı promote workflow'unu tetikle
gh workflow run promote.yml -f tag=v1.4.2

# Olusan run id'sini bul
gh run list --workflow=promote.yml --limit 1

# O run icin bekleyen onaylari listele
gh api /repos/OWNER/REPO/actions/runs/RUN_ID/pending_deployments

# API uzerinden onayla
gh api -X POST /repos/OWNER/REPO/actions/runs/RUN_ID/pending_deployments \
  -F environment_ids[]=PROD_ENV_ID \
  -F state=approved \
  -F comment="CHG-12345 ile onaylandı"

state değeri approved ya da rejected olur. Reddedilen run, deploy-production job’ını durdurur ve production environment’ını o run için failed olarak işaretler; image registry tag’inde olduğu gibi durur. Rollback aynı akıştır, sadece tag farklı: en son sağlam v*.*.* ile promote.yml’i tetikleyin, kapıda onaylayın, deploy edin. Artifact değişmez olduğu için ayrı bir rollback workflow’una ihtiyaç yoktur.

Onaylandi

Reddedildi

main'den tag push

Build + image push

Staging'e deploy (auto)

PM / QA dogrulamasi

Promote workflow tetiklenir

Production gate

Ayni image production'a

Deploy yok, audit yazildi

Sık atlanan ayarlar

En çok kaçırılan üç şey YAML’da değil, environment ayarlarındadır.

Kendi kendine onay default’u

Default’ta deploy’u tetikleyen kullanıcı, required-reviewers listesinde de yer alıyorsa kendi deploy’unu onaylayabilir. Görev ayrımı (separation of duties) zorunluluğu olan ekipler için bu yanlış bir default’tur. Prevent self-review anahtarı Ekim 2023’ten beri environment ayar sayfasında durur ve tek tıklamayla açılır; workflow tarafına bir etkisi yoktur. Bunu repo bazında bir karar değil, environment template’inin parçası olarak ele alın.

Onaycı takımlar mı, kişiler mi

Required-reviewers listesi kendi başına bir listedir. CODEOWNERS değildir ve CODEOWNERS kuralları buraya akmaz. Onaycı rotasyonunun environment ayarları yerine takım üyeliğinde yaşaması için kişi yerine takım ekleyin. Birey eklerseniz, birisi her takım değiştirdiğinde environment ayarlarını düzelten kişi siz olursunuz.

Asıl kapı: environment secret’ı

Environment’ın güvenlik gerekçesi insan duraklaması değil, deploy credential’ının kapı geçene kadar okunamaz olmasıdır. Repo secret’ı, repo’daki her workflow tarafından çözülebilir; PR tetiklemeli akışlar ya da label hilesiyle atlatılmış onay yolları da buna dahildir. Environment secret’ı, ancak environment: hedefinin koruma kurallarını geçtiği bir job tarafından çözülebilir. Üretim token’ınız repo secret’ındaysa kapınız YAML’da bir yorumdur, gerçek bir sınır değil.

Hangi durumlarda kullanmayın

Environment’ın doğru cevap olmadığı üç durum.

GitOps repo’ları. Rollout’u Argo CD ya da Flux yönetiyorsa, GHA workflow yalnızca bir PR açar ya da manifest push’lar. Onay kapısı, sync politikasının yaşadığı CD controller’a aittir. PR açan workflow’a Environment koymak insanı yanlış noktaya yerleştirir.

Repo’lar arası orchestration. Bir orchestrator workflow başka repo’larda child workflow’lar tetikliyorsa, kapı orchestrator’da olur. Hem orchestrator’ı hem de child’ları kapı arkasına koyarsanız tek deploy için iki onay sorulur ve onaycılar bir süre sonra okumadan onaylar.

Deploy hedefi olmayan job’lar. Housekeeping cron’ları, dependency-update bot’ları ve üretim hedefi olmayan diğer iş yükleri Environment istemez. Checklist tutturmak için eklemek, güvenlik kazancı olmadan sürtünme yaratır.

GHEC, GHES, Free planı

Plan kapsamı, ikincil yazıların çoğunun yanlış yaptığı kısımdır. Yayın zamanında GitHub Plans sayfasıyla doğrulayın; wording zaman zaman değişiyor. Güncel docs’a göre:

  • Free. Environment’lar public repo’larda koruma kuralları dahil çalışır. Private repo’larda environment nesnesi vardır ama required reviewer ve wait timer sunulmaz.
  • Pro ve Team. Environment’lar private repo’larda çalışır. Required reviewer ve wait timer ise bu planlarda yalnızca public repo’larda kullanılabilir.
  • Enterprise. Required reviewer, wait timer, branch policy ve custom deployment protection rule’ların hepsi private ve internal repo’larda çalışır.

Üretim kodunuz Pro ya da Team planındaki bir private repo’da yaşıyorsa, elde ettiğiniz tek deployment protection rule branch ve tag policy’sidir. Bu, feature branch’in üretim deploy’u talep etmesini durdurur ama reviewer kapısı değildir. İnsan onayı ve audit kaydı için Enterprise gerekir.

Custom deployment protection rule

“Şu an deploy edebilir miyiz?” sorusunun kaynağı GitHub dışında yaşıyorsa, bir GitHub App’i custom deployment protection rule olarak kaydedebilirsiniz. App deployment_protection_rule webhook’una abone olur ve REST API’ye geri dönüş yaparak onaylar ya da reddeder. ServiceNow change window’u, Datadog deploy gate, Honeycomb SLO kontrolü ya da kendi ticket sisteminizle entegrasyon noktası budur. Public-beta duyurusu Nisan 2023 tarihli; docs sayfası artık beta etiketi taşımıyor, ama compliance tasarımına yerleştirmeden önce bağlantılı dokümandaki güncel ifadeyi teyit edin.

Trade-off açıktır: dış sistem deploy’u durdurabilen bir bağımlılık olur ve GitHub App’in diğer kritik entegrasyonlar kadar ops dikkati ister. Mevcut bir change-management sistemi varsa ve gerçek kayıt kaynağı oysa kullanın. Sırf modern görünmek için kurmayın.

Kapanış

Artifact’ı main üzerinden bir kez üretin, staging’e otomatik düşürün, aynı artifact’ı required reviewer arkasında production’a promote edin. Deploy credential’larını her Environment’ın kendi secret’larına koyun, repo secret’ına değil. Prevent self-review anahtarını açın. Production environment’ına v*.*.* desenli bir tag policy koyun, yalnızca release tag’leri promote edebilsin. Sınır durumları nettir: GitOps controller’lar kendi kapılarını kurar, orchestrator’lar child’larını yönetir, housekeeping job’ları hiçbirini istemez. Geri kalan her şey default’a oturur.

Kaynaklar

İlgili yazılar

Büyük Ölçekli Mikroservis Mimarisi için Ölçeklenebilir Bir GitHub Actions Platformu Oluşturmak

Organizasyon düzeyinde paylaşımlı bir GitHub Actions platformu kurmak için pratik bir rehber: mimari kararlar, güvenlik yönetişimi, benimseme stratejisi ve bu süreçte yaptığımız en büyük 7 hata.

github-actionsci-cddevops+5
Claude'u GitHub Action ile PR Reviewer Olarak Kurmak

Anthropic'in claude-code-action'ını bir GitHub repo'suna eklemek için sıkılaştırılmış, kopyalanmaya hazır bir kurulum; üretim için güvenlik ve maliyet ayarları açıkça anlatılıyor.

claudegithub-actionscode-review+4
Git Branching Stratejileri: Farklı Takımlar ve Ürünler için Gerçek Dünya Dersleri

Takım büyüklüğü, ürün tipi ve gerçek başarısızlıklara dayanan Git branching stratejileri hakkında acımasızca dürüst bir rehber.

gitbranchingwar-stories+5
AWS Secrets Manager & Parameter Store: Güvenlik Best Practices

AWS Secrets Manager ve Systems Manager Parameter Store'u karşılaştıran kapsamlı teknik rehber - hangi servisi ne zaman kullanmalı ve gerçek dünya implementation pattern'leri.

awssecrets-managerparameter-store+8
Custom MCP Server Geliştirme: Production-Ready Kılavuz

TypeScript ile organizasyonunuzun internal sistemleri için custom Model Context Protocol serverları nasıl geliştirip, güvenli hale getirip, deploy edeceğinizi öğren. Authentication, monitoring ve Kubernetes deployment örnekleriyle.

typescriptmcpnodejs+5