GitHub Actions ile Environment Protection Rules Kullanımı

Bir production ortamına kod deploy etmek her zaman heyecan verici ama bir o kadar da gergin bir süreçtir. Geceleri uyku kaçıran o “acaba bir şey gitti mi?” sorusunu en aza indirmenin yolu, deployment pipeline’ınıza akıllı güvenlik katmanları eklemekten geçiyor. GitHub Actions’ın Environment Protection Rules özelliği tam olarak bu noktada devreye giriyor.

Environment Protection Rules Nedir?

GitHub Actions’ta environment kavramı, deployment hedeflerinizi temsil eder. Development, staging, production gibi ortamlar birer environment olarak tanımlanır. Protection rules ise bu ortamlara yapılacak deploymentları belirli koşullara bağlamanızı sağlar.

Temel olarak şunları yapabilirsiniz:

  • Required reviewers: Deployment başlamadan önce belirli kişilerin onay vermesini zorunlu kılmak
  • Wait timer: Deployment öncesinde bekleme süresi tanımlamak
  • Deployment branches: Sadece belirli branch’lerden deployment izni vermek
  • Custom deployment protection rules: Üçüncü parti servislerle entegrasyon

Bu özellikler sayesinde “yanlış branch’ten production’a deploy” ya da “sabah 3’te kimse fark etmeden kritik değişiklik gitmesi” gibi senaryoların önüne geçebilirsiniz.

Environment Tanımlama

Öncelikle repository ayarlarından environment oluşturmanız gerekiyor. GitHub repo sayfasında Settings > Environments yolunu izleyin. Ama her şeyi workflow dosyasından da yönetebilirsiniz.

Basit bir deployment workflow’u şöyle görünür:

name: Deploy Application

on:
  push:
    branches:
      - main

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to staging
        run: |
          echo "Staging ortamına deploy ediliyor..."
          ./scripts/deploy.sh staging

  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    needs: deploy-staging
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to production
        run: |
          echo "Production ortamına deploy ediliyor..."
          ./scripts/deploy.sh production

Burada environment: production satırı, bu job’ın production environment’ına ait olduğunu belirtir. Eğer o environment için protection rules tanımladıysanız, job otomatik olarak bu kurallara tabi olur.

Required Reviewers Kurulumu

En sık kullanılan protection rule, deployment öncesi manuel onay mekanizmasıdır. GitHub UI’dan Settings > Environments > production > Required reviewers kısmına gidin ve onay vermesi gereken kişileri veya ekipleri ekleyin.

Onay mekanizması devreye girdiğinde workflow şöyle görünür:

name: Production Deployment Pipeline

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: |
          npm install
          npm test

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build artifact
        run: |
          npm run build
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

  deploy-production:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.com
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/

      - name: Deploy
        env:
          DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}
        run: |
          echo "Onay alındı, deploy başlıyor..."
          rsync -avz dist/ user@production-server:/var/www/myapp/

Bu workflow çalıştığında, deploy-production job’ı başlamadan önce GitHub otomatik olarak onay bekleyecektir. Onaylayıcılara e-posta bildirimi gider, Actions sekmesinde “Waiting for approval” mesajı görünür.

Wait Timer ile Otomatik Bekleme

Bazen ekip içi iletişim için sadece birkaç dakika bile yeterli olabilir. Wait timer özelliği, deployment başlamadan önce belirlediğiniz dakika kadar bekler. Bu sürede bir sorun fark ederseniz workflow’u durdurabilirsiniz.

GitHub UI’dan environment ayarlarına girip Wait timer değerini dakika cinsinden girin. Örneğin 5 dakika, staging’den production’a geçiş için yeterli bir tampon süredir.

Bu süre, özellikle şu senaryolarda hayat kurtarır:

  • Staging’de testler geçti ama monitoring dashboard’unda beklenmedik bir metrik artışı gördünüz
  • Deploy pipeline’ı başlattıktan sonra bir meslektaşınız “dur, bu feature’da sorun var” dedi
  • Canary deployment yapıyorsunuz ve ilk grup için bir süre beklemek istiyorsunuz

Branch Filtreleme ile Güvenlik

Protection rules’un en kritik parçalarından biri, hangi branch’lerden deployment yapılabileceğini kısıtlamaktır. Production environment için sadece main branch’ine izin vermek temel bir güvenlik önlemidir.

GitHub UI’da Deployment branches seçeneğini Selected branches olarak ayarlayın ve main pattern’i ekleyin. Ama bunu workflow dosyasında da destekleyen bir yapı kurabilirsiniz:

name: Environment-Aware Deployment

on:
  push:
    branches:
      - main
      - 'release/**'
      - develop

jobs:
  determine-environment:
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.set-env.outputs.environment }}
    steps:
      - name: Set target environment
        id: set-env
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "environment=production" >> $GITHUB_OUTPUT
          elif [[ "${{ github.ref }}" == refs/heads/release/* ]]; then
            echo "environment=staging" >> $GITHUB_OUTPUT
          else
            echo "environment=development" >> $GITHUB_OUTPUT
          fi

  deploy:
    needs: determine-environment
    runs-on: ubuntu-latest
    environment: ${{ needs.determine-environment.outputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to ${{ needs.determine-environment.outputs.environment }}
        run: |
          TARGET_ENV="${{ needs.determine-environment.outputs.environment }}"
          echo "Deploy ediliyor: $TARGET_ENV"
          ./scripts/deploy.sh "$TARGET_ENV"

Bu yaklaşım, branch adına göre otomatik olarak doğru environment’ı seçer ve her environment’ın kendi protection rules’larına tabi olmasını sağlar.

Environment Secrets ile Güvenli Konfigürasyon

Environment protection rules ile birlikte kullanmanın en güçlü yanlarından biri, her environment için ayrı secrets tanımlayabilmektir. Production veritabanı şifresi, staging ortamında bulunmamalıdır.

name: Secure Multi-Environment Deploy

on:
  push:
    branches: [main, develop]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-west-1

      - name: Deploy to ECS
        env:
          DB_CONNECTION_STRING: ${{ secrets.DB_CONNECTION_STRING }}
          APP_SECRET_KEY: ${{ secrets.APP_SECRET_KEY }}
          REDIS_URL: ${{ secrets.REDIS_URL }}
        run: |
          echo "Container image build ediliyor..."
          docker build -t myapp:${{ github.sha }} .

          echo "ECR'a push ediliyor..."
          aws ecr get-login-password | docker login --username AWS 
            --password-stdin ${{ secrets.ECR_REGISTRY }}

          docker tag myapp:${{ github.sha }} 
            ${{ secrets.ECR_REGISTRY }}/myapp:${{ github.sha }}
          docker push ${{ secrets.ECR_REGISTRY }}/myapp:${{ github.sha }}

          echo "ECS service güncelleniyor..."
          aws ecs update-service 
            --cluster ${{ secrets.ECS_CLUSTER_NAME }} 
            --service myapp-service 
            --force-new-deployment

Burada secrets.DB_CONNECTION_STRING production environment’ında production veritabanına, staging environment’ında ise staging veritabanına işaret eder. Aynı workflow kodu, farklı ortamlarda farklı değerlerle çalışır.

Gerçek Dünya Senaryosu: E-ticaret Deployment Pipeline

Bir e-ticaret platformu yönettiğinizi düşünün. Black Friday yaklaşıyor ve deployment sürecinde sıfır hata toleransınız var. Şöyle bir pipeline kurabilirsiniz:

name: E-Commerce Production Pipeline

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      skip_staging:
        description: 'Staging adımını atla (acil hotfix)'
        required: false
        type: boolean
        default: false

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader
      - name: Run unit tests
        run: php artisan test --testsuite=Unit

  integration-tests:
    needs: unit-tests
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: testpass
          MYSQL_DATABASE: testdb
        ports:
          - 3306:3306
    steps:
      - uses: actions/checkout@v4
      - name: Run integration tests
        env:
          DB_HOST: 127.0.0.1
          DB_PASSWORD: testpass
        run: php artisan test --testsuite=Integration

  deploy-staging:
    needs: integration-tests
    if: ${{ !inputs.skip_staging }}
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.mystore.com
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to staging
        run: |
          echo "Staging deploy başlıyor: ${{ github.sha }}"
          ssh -i ${{ secrets.DEPLOY_SSH_KEY }} 
            [email protected] 
            "cd /var/www/staging && git pull && composer install 
            && php artisan migrate --force && php artisan cache:clear"

  smoke-tests:
    needs: deploy-staging
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run smoke tests against staging
        run: |
          sleep 30  # Deploy'un tamamlanması için bekle
          curl -f https://staging.mystore.com/health || exit 1
          curl -f https://staging.mystore.com/api/products | 
            python3 -c "import sys,json; d=json.load(sys.stdin); 
            sys.exit(0 if len(d) > 0 else 1)"
          echo "Smoke testler başarılı!"

  deploy-production:
    needs: [smoke-tests]
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://mystore.com
    steps:
      - uses: actions/checkout@v4
      - name: Pre-deployment notification
        run: |
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} 
            -H 'Content-type: application/json' 
            --data "{"text":"Production deploy başlıyor: ${{ github.sha }}"}"

      - name: Enable maintenance mode
        run: |
          ssh [email protected] 
            "cd /var/www/production && php artisan down --secret=bypass123"

      - name: Deploy application
        run: |
          ssh [email protected] 
            "cd /var/www/production && git pull origin main 
            && composer install --no-dev --optimize-autoloader 
            && php artisan migrate --force 
            && php artisan config:cache 
            && php artisan route:cache 
            && php artisan view:cache"

      - name: Disable maintenance mode
        run: |
          ssh [email protected] 
            "cd /var/www/production && php artisan up"

      - name: Post-deployment verification
        run: |
          sleep 15
          HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://mystore.com)
          if [ "$HTTP_STATUS" != "200" ]; then
            echo "HATA: Site yanıt vermiyor! HTTP $HTTP_STATUS"
            exit 1
          fi
          echo "Deployment başarılı! HTTP $HTTP_STATUS"

Bu pipeline’da dikkat edin: staging atlanabilir ama sadece workflow_dispatch ile manuel tetiklenirse ve skip_staging: true seçildiyse. Normal push akışında her zaman staging üzerinden geçilir.

Deployment Durumunu Takip Etmek

Protection rules ile birlikte deployment geçmişini de takip etmek istersiniz. GitHub’ın deployment API’sini kullanarak bunu otomatize edebilirsiniz:

name: Deployment with Status Tracking

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.com
    steps:
      - uses: actions/checkout@v4

      - name: Create deployment record
        id: deployment
        uses: actions/github-script@v7
        with:
          script: |
            const deployment = await github.rest.repos.createDeployment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: context.sha,
              environment: 'production',
              description: 'Automated production deployment',
              auto_merge: false,
              required_contexts: []
            });
            return deployment.data.id;

      - name: Deploy application
        id: deploy
        run: |
          echo "Deploy ediliyor..."
          ./scripts/deploy.sh
          echo "deploy_url=https://myapp.com" >> $GITHUB_OUTPUT

      - name: Update deployment status - success
        if: success()
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.repos.createDeploymentStatus({
              owner: context.repo.owner,
              repo: context.repo.repo,
              deployment_id: ${{ steps.deployment.outputs.result }},
              state: 'success',
              environment_url: 'https://myapp.com',
              description: 'Deployment başarıyla tamamlandı'
            });

      - name: Update deployment status - failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.repos.createDeploymentStatus({
              owner: context.repo.owner,
              repo: context.repo.repo,
              deployment_id: ${{ steps.deployment.outputs.result }},
              state: 'failure',
              description: 'Deployment başarısız oldu'
            });

Sık Karşılaşılan Sorunlar ve Çözümleri

Environment bulunamıyor hatası: Workflow’da belirttiğiniz environment adı GitHub’da tanımlı değilse job hata verir. Environment adlarının büyük-küçük harf duyarlı olduğunu unutmayın. Production ile production farklı şeylerdir.

Onay bildirimleri gitmiyor: Required reviewers olarak eklediğiniz kullanıcıların e-posta bildirimlerini açmış olması ve repository’e erişimlerinin bulunması gerekir.

Branch kısıtlaması çalışmıyor: GitHub’da environment’a özel deployment branch kısıtlamasını sadece public repolarda ve GitHub Pro/Team/Enterprise hesaplarında kullanabilirsiniz. Free plan private repolarda bu özellik kısıtlıdır.

Wait timer atlanıyor: Sadece organization owner veya repository admin rolündeki kullanıcılar wait timer’ı atlayabilir. Bu bir güvenlik açığı değil, kasıtlı bir tasarım kararıdır.

Concurrency ile Deployment Çakışmalarını Önlemek

Birden fazla push aynı anda deploy tetiklediğinde ne olur? Concurrency ayarı bu sorunu çözer:

name: Safe Production Deployment

on:
  push:
    branches: [main]

concurrency:
  group: production-deployment
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        run: ./scripts/deploy.sh

cancel-in-progress: false ayarı ile mevcut deployment tamamlanmadan yeni deploy başlamaz. Böylece yarım kalan bir deployment yüzünden ortamınız tutarsız duruma girmez. cancel-in-progress: true yaparsanız eski deployment iptal edilip yenisi başlar, ancak production için bu genellikle tehlikeli bir seçenektir.

Sonuç

GitHub Actions Environment Protection Rules, sıradan bir CI/CD pipeline’ını gerçek anlamda güvenli bir deployment sürecine dönüştürür. Bu özellikleri hayata geçirdikten sonra ekibinizde şunları fark edeceksiniz:

  • Production deploymentları artık belgelenmiş ve izlenebilir bir süreç haline gelir
  • “Kim deploy etti, ne zaman?” sorularının cevabı her zaman GitHub Actions geçmişinde mevcuttur
  • Yanlış branch’ten yanlış ortama deployment vakaları neredeyse sıfıra iner
  • Ekip içi deployment koordinasyonu e-posta veya Slack yerine doğrudan GitHub üzerinden yönetilir

Başlangıç için minimum şunları yapmanızı öneririm: production environment’ınıza en az bir required reviewer ekleyin, deployment branch’ini main ile kısıtlayın ve environment-specific secrets kullanmaya başlayın. Bu üç adım bile deployment güvenliğinizi önemli ölçüde artıracaktır.

Daha karmaşık senaryolar için GitHub’ın custom deployment protection rules API’sine bakabilirsiniz. Bu sayede kendi güvenlik sistemlerinizi, third-party monitoring araçlarını ve change management süreçlerinizi doğrudan deployment pipeline’ınıza entegre edebilirsiniz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir