GitHub Actions ile Semantic Versioning ve Otomatik Release Yönetimi

Yazılım geliştirirken “bu sürümü nasıl numaralandıracağız?” sorusu kulağa basit geliyor ama üretim ortamında yanlış yapıldığında ciddi kaoslar yaratıyor. Bir servisi güncelliyorsunuz, bağımlı olan başka bir servis bozuluyor ve “ama biz sadece küçük bir şey değiştirdik” diyorsunuz. İşte tam bu noktada Semantic Versioning devreye giriyor. Bu yazıda SemVer’i GitHub Actions ile nasıl otomatikleştireceğinizi, release süreçlerini nasıl yöneteceğinizi ve bunları gerçek dünya senaryolarıyla nasıl hayata geçireceğinizi ele alacağız.

Semantic Versioning Nedir ve Neden Önemlidir?

Semantic Versioning, yani SemVer, sürüm numaralarını MAJOR.MINOR.PATCH formatında ifade eden bir standarttır. Ama bu sadece bir format meselesi değil, aynı zamanda bir iletişim protokolü.

  • MAJOR: Geriye dönük uyumsuz değişiklikler yapıldığında artırılır
  • MINOR: Geriye dönük uyumlu yeni özellikler eklendiğinde artırılır
  • PATCH: Geriye dönük uyumlu hata düzeltmeleri yapıldığında artırılır

Örneğin 2.4.1 sürümünden 3.0.0‘a geçmek, tüm bağımlı sistemlerin dikkat etmesi gerektiği anlamına gelir. Ama 2.4.1‘den 2.4.2‘ye geçmek güvenle güncellenebilir demektir.

Bu kuralları elle takip etmeye çalışmak, takım büyüdükçe ve commit hızı arttıkça imkansız hale gelir. GitHub Actions ile bu süreci tamamen otomatikleştirebilirsiniz.

Conventional Commits ile Temeli Atmak

Otomatik versiyonlama için önce commit mesajlarını standartlaştırmanız gerekiyor. Conventional Commits standardı tam bu iş için var.

Temel commit türleri şunlar:

  • feat: Yeni özellik (MINOR artışını tetikler)
  • fix: Hata düzeltmesi (PATCH artışını tetikler)
  • feat! veya BREAKING CHANGE: Geriye uyumsuz değişiklik (MAJOR artışını tetikler)
  • chore: Rutin görevler, build sistemi değişiklikleri (versiyon artışı yok)
  • docs: Sadece dokümantasyon değişiklikleri (versiyon artışı yok)
  • refactor: Ne bug fix ne de feature, kod yeniden düzenleme (versiyon artışı yok)

Örnek commit mesajları:

git commit -m "feat: kullanicilara email bildirimi eklendi"
git commit -m "fix: login sayfasindaki null pointer hatasi duzeltildi"
git commit -m "feat!: authentication API tamamen yeniden yazildi"
git commit -m "fix: veritabani baglanti havuzu limiti duzeltildi

BREAKING CHANGE: connection pool konfigurasyonu degisti"

GitHub Actions ile Temel Versiyonlama Pipeline’ı

Şimdi asıl işe gelelim. Aşağıdaki workflow, her main branch’e push yapıldığında otomatik olarak versiyon hesaplayıp tag oluşturuyor.

# .github/workflows/versioning.yml
name: Semantic Versioning

on:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write

jobs:
  version:
    name: Versiyonla ve Tag Olustur
    runs-on: ubuntu-latest
    outputs:
      new_version: ${{ steps.semver.outputs.new_version }}
      version_changed: ${{ steps.semver.outputs.version_changed }}

    steps:
      - name: Kodu Checkout Et
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Git Gecmisini Getir
        run: git fetch --tags origin

      - name: Son Versiyonu Bul ve Yeni Versiyonu Hesapla
        id: semver
        run: |
          # Son git tag'ini bul
          LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
          echo "Son tag: $LAST_TAG"

          # Son tagden bu yana commit mesajlarini al
          COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"%s" 2>/dev/null || git log --pretty=format:"%s")

          # Versiyon parcalarini ayir
          VERSION=${LAST_TAG#v}
          MAJOR=$(echo $VERSION | cut -d. -f1)
          MINOR=$(echo $VERSION | cut -d. -f2)
          PATCH=$(echo $VERSION | cut -d. -f3)

          # Commit mesajlarina gore versiyon artir
          BUMP="none"

          if echo "$COMMITS" | grep -qiE "BREAKING CHANGE|^feat!|^fix!|^refactor!"; then
            BUMP="major"
          elif echo "$COMMITS" | grep -qE "^feat((.+))?:"; then
            BUMP="minor"
          elif echo "$COMMITS" | grep -qE "^fix((.+))?:|^perf((.+))?:"; then
            BUMP="patch"
          fi

          echo "Versiyon artisi turu: $BUMP"

          if [ "$BUMP" = "major" ]; then
            MAJOR=$((MAJOR + 1))
            MINOR=0
            PATCH=0
          elif [ "$BUMP" = "minor" ]; then
            MINOR=$((MINOR + 1))
            PATCH=0
          elif [ "$BUMP" = "patch" ]; then
            PATCH=$((PATCH + 1))
          fi

          NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"

          if [ "$BUMP" != "none" ]; then
            echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
            echo "version_changed=true" >> $GITHUB_OUTPUT
            echo "Yeni versiyon: $NEW_VERSION"
          else
            echo "new_version=$LAST_TAG" >> $GITHUB_OUTPUT
            echo "version_changed=false" >> $GITHUB_OUTPUT
            echo "Versiyon degismedi"
          fi

      - name: Git Tag Olustur
        if: steps.semver.outputs.version_changed == 'true'
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git tag ${{ steps.semver.outputs.new_version }}
          git push origin ${{ steps.semver.outputs.new_version }}

Release Notes Otomatik Oluşturma

Versiyonu etiketlemek yeterli değil. Kullanıcıların ve diğer geliştiricilerin “bu sürümde ne var?” sorusunu yanıtlamak için otomatik changelog oluşturmak şart.

# .github/workflows/release.yml
name: Release Olustur

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

permissions:
  contents: write

jobs:
  release:
    name: GitHub Release Olustur
    runs-on: ubuntu-latest

    steps:
      - name: Kodu Checkout Et
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Changelog Olustur
        id: changelog
        run: |
          CURRENT_TAG=${GITHUB_REF#refs/tags/}
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${CURRENT_TAG}^ 2>/dev/null || echo "")

          echo "## Degisiklikler" > CHANGELOG_TEMP.md
          echo "" >> CHANGELOG_TEMP.md

          if [ -n "$PREVIOUS_TAG" ]; then
            COMMITS=$(git log ${PREVIOUS_TAG}..${CURRENT_TAG} --pretty=format:"%s|%h|%an")
          else
            COMMITS=$(git log ${CURRENT_TAG} --pretty=format:"%s|%h|%an")
          fi

          # Kategorilere gore sirala
          FEATURES=""
          FIXES=""
          BREAKING=""
          OTHERS=""

          while IFS='|' read -r subject hash author; do
            if echo "$subject" | grep -qiE "BREAKING CHANGE|^feat!|^fix!"; then
              BREAKING="${BREAKING}n- ${subject} (${hash}) - @${author}"
            elif echo "$subject" | grep -qE "^feat((.+))?:"; then
              FEATURES="${FEATURES}n- ${subject#feat*: } (${hash}) - @${author}"
            elif echo "$subject" | grep -qE "^fix((.+))?:"; then
              FIXES="${FIXES}n- ${subject#fix*: } (${hash}) - @${author}"
            else
              OTHERS="${OTHERS}n- ${subject} (${hash}) - @${author}"
            fi
          done <<< "$COMMITS"

          if [ -n "$BREAKING" ]; then
            echo "### Kritik Degisiklikler" >> CHANGELOG_TEMP.md
            echo -e "$BREAKING" >> CHANGELOG_TEMP.md
            echo "" >> CHANGELOG_TEMP.md
          fi

          if [ -n "$FEATURES" ]; then
            echo "### Yeni Ozellikler" >> CHANGELOG_TEMP.md
            echo -e "$FEATURES" >> CHANGELOG_TEMP.md
            echo "" >> CHANGELOG_TEMP.md
          fi

          if [ -n "$FIXES" ]; then
            echo "### Hata Duzeltmeleri" >> CHANGELOG_TEMP.md
            echo -e "$FIXES" >> CHANGELOG_TEMP.md
            echo "" >> CHANGELOG_TEMP.md
          fi

          if [ -n "$OTHERS" ]; then
            echo "### Diger Degisiklikler" >> CHANGELOG_TEMP.md
            echo -e "$OTHERS" >> CHANGELOG_TEMP.md
          fi

          echo "changelog_file=CHANGELOG_TEMP.md" >> $GITHUB_OUTPUT

      - name: GitHub Release Olustur
        uses: softprops/action-gh-release@v2
        with:
          body_path: ${{ steps.changelog.outputs.changelog_file }}
          draft: false
          prerelease: false
          token: ${{ secrets.GITHUB_TOKEN }}

Pre-release ve Beta Sürümleri Yönetmek

Gerçek dünya projelerinde sadece stable release yeterli değil. Feature branch’lerinden beta, alpha veya release candidate sürümleri de oluşturmanız gerekiyor.

# .github/workflows/prerelease.yml
name: Pre-release

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

permissions:
  contents: write

jobs:
  prerelease:
    name: Pre-release Olustur
    runs-on: ubuntu-latest

    steps:
      - name: Kodu Checkout Et
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Pre-release Tipini Belirle
        id: prerelease_type
        run: |
          BRANCH=${GITHUB_REF#refs/heads/}
          echo "Branch: $BRANCH"

          if echo "$BRANCH" | grep -q "^release/"; then
            PRERELEASE_TYPE="rc"
          elif [ "$BRANCH" = "develop" ]; then
            PRERELEASE_TYPE="beta"
          else
            PRERELEASE_TYPE="alpha"
          fi

          echo "prerelease_type=$PRERELEASE_TYPE" >> $GITHUB_OUTPUT

      - name: Pre-release Versiyonu Olustur
        id: version
        run: |
          # Son stable versiyonu bul
          LAST_STABLE=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | 
            grep -v -E "alpha|beta|rc" | 
            sort -V | tail -1 || echo "v0.0.0")

          PRERELEASE_TYPE=${{ steps.prerelease_type.outputs.prerelease_type }}
          BUILD_NUMBER=${{ github.run_number }}
          SHORT_SHA=$(git rev-parse --short HEAD)

          # Pre-release versiyonu: v1.2.0-beta.42+abc1234
          VERSION="${LAST_STABLE}-${PRERELEASE_TYPE}.${BUILD_NUMBER}+${SHORT_SHA}"
          echo "Pre-release versiyonu: $VERSION"
          echo "version=$VERSION" >> $GITHUB_OUTPUT

      - name: Pre-release Tag ve Release Olustur
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.version.outputs.version }}
          name: "Pre-release ${{ steps.version.outputs.version }}"
          prerelease: true
          generate_release_notes: true
          token: ${{ secrets.GITHUB_TOKEN }}

Node.js Projesi icin Tam Entegrasyon

Şimdi bir adım ileri gidelim. Node.js projesi için package.json versiyonunu otomatik güncelleyen ve npm’e publish eden tam bir pipeline görelim.

# .github/workflows/npm-release.yml
name: NPM Release

on:
  push:
    branches:
      - main

permissions:
  contents: write
  packages: write

jobs:
  determine-version:
    runs-on: ubuntu-latest
    outputs:
      new_version: ${{ steps.bump.outputs.new_version }}
      should_release: ${{ steps.bump.outputs.should_release }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Versiyon Hesapla
        id: bump
        run: |
          CURRENT_VERSION=$(node -p "require('./package.json').version")
          echo "Mevcut versiyon: $CURRENT_VERSION"

          LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
          COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"%s")

          MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1)
          MINOR=$(echo $CURRENT_VERSION | cut -d. -f2)
          PATCH=$(echo $CURRENT_VERSION | cut -d. -f3)

          if echo "$COMMITS" | grep -qiE "BREAKING CHANGE|!:"; then
            MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
            echo "should_release=true" >> $GITHUB_OUTPUT
          elif echo "$COMMITS" | grep -qE "^feat"; then
            MINOR=$((MINOR + 1)); PATCH=0
            echo "should_release=true" >> $GITHUB_OUTPUT
          elif echo "$COMMITS" | grep -qE "^fix|^perf"; then
            PATCH=$((PATCH + 1))
            echo "should_release=true" >> $GITHUB_OUTPUT
          else
            echo "should_release=false" >> $GITHUB_OUTPUT
          fi

          NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
          echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
          echo "Yeni versiyon: $NEW_VERSION"

  release:
    needs: determine-version
    if: needs.determine-version.outputs.should_release == 'true'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.PAT_TOKEN }}

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - name: Bagimliliklari Yukle ve Test Et
        run: |
          npm ci
          npm test

      - name: package.json Versiyonu Guncelle
        run: |
          NEW_VERSION=${{ needs.determine-version.outputs.new_version }}
          npm version $NEW_VERSION --no-git-tag-version
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add package.json package-lock.json
          git commit -m "chore: versiyon $NEW_VERSION olarak guncellendi [skip ci]"
          git push

      - name: NPM'e Publish Et
        run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: GitHub Release Olustur
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{ needs.determine-version.outputs.new_version }}
          generate_release_notes: true
          token: ${{ secrets.GITHUB_TOKEN }}

Docker Image Versiyonlama

Mikro servis mimarilerinde Docker image versiyonlaması özellikle kritik. Şu senaryoyu düşünün: 5 farklı servisiniz var ve hepsinin hangi versiyonunun production’da çalıştığını bilmeniz gerekiyor.

# .github/workflows/docker-release.yml
name: Docker Image Release

on:
  push:
    tags:
      - 'v*.*.*'

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  docker-build-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Docker Buildx Kur
        uses: docker/setup-buildx-action@v3

      - name: GitHub Container Registry'e Giris Yap
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Metadata Hazirla
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha,prefix=sha-,format=short
            type=raw,value=latest,enable={{is_default_branch}}
          labels: |
            org.opencontainers.image.title=Uygulama Adi
            org.opencontainers.image.description=Servis aciklamasi
            org.opencontainers.image.vendor=Sirket Adi

      - name: Docker Image Build ve Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            VERSION=${{ github.ref_name }}
            BUILD_DATE=${{ github.event.head_commit.timestamp }}
            GIT_COMMIT=${{ github.sha }}

Bu pipeline ile bir image push ettiğinizde otomatik olarak şu etiketler oluşuyor:

  • ghcr.io/org/repo:2.4.1 (tam versiyon)
  • ghcr.io/org/repo:2.4 (major.minor)
  • ghcr.io/org/repo:2 (sadece major)
  • ghcr.io/org/repo:latest
  • ghcr.io/org/repo:sha-abc1234

Hotfix Senaryosu

Gece yarısı production’da kritik bir bug çıktı. Develop branch’inde bir sürü yarım kalmış feature var ve bunları release edemezsiniz. İşte bu senaryoda hotfix branch’i kullanıyorsunuz.

# Hotfix branch'i olustur
git checkout -b hotfix/kritik-guvenlik-acigi v2.3.0

# Hatay duzelt
git commit -m "fix: SQL injection acigi kapatildi

Bu acik kullanici giris formunda bulunuyordu ve yetkisiz
erisime izin veriyordu."

# Main'e merge et
git checkout main
git merge --no-ff hotfix/kritik-guvenlik-acigi -m "fix: hotfix/kritik-guvenlik-acigi branch'i merge edildi"

# Develop'a da merge et
git checkout develop
git merge --no-ff hotfix/kritik-guvenlik-acigi

# Hotfix branch'ini temizle
git branch -d hotfix/kritik-guvenlik-acigi

Main’e merge edildiğinde versiyonlama pipeline’ı otomatik olarak v2.3.0‘dan v2.3.1‘e geçecek çünkü commit fix: ile başlıyor.

Branch Koruma Kuralları ve Versiyon Tutarlılığı

Versiyonlama sisteminin sağlıklı çalışması için branch koruma kurallarını da ayarlamanız gerekiyor. Bunu kod olarak da yönetebilirsiniz.

# .github/workflows/pr-validation.yml
name: PR Dogrulama

on:
  pull_request:
    branches:
      - main
      - develop

jobs:
  validate-commits:
    runs-on: ubuntu-latest
    name: Commit Mesajlarini Dogrula

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Conventional Commits Kontrol Et
        run: |
          VALID_PATTERN="^(feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)((.+))?(!)?: .+"

          # PR'daki tum commitleri kontrol et
          COMMITS=$(git log origin/${{ github.base_ref }}..HEAD --pretty=format:"%s")

          INVALID=false
          while IFS= read -r commit; do
            if ! echo "$commit" | grep -qE "$VALID_PATTERN"; then
              # Merge commitlerini atla
              if ! echo "$commit" | grep -qE "^Merge "; then
                echo "Gecersiz commit mesaji: '$commit'"
                echo "Beklenen format: feat: yeni ozellik eklendi"
                INVALID=true
              fi
            fi
          done <<< "$COMMITS"

          if [ "$INVALID" = "true" ]; then
            echo ""
            echo "HATA: Bazi commit mesajlari Conventional Commits standardina uymuyor."
            echo "Daha fazla bilgi icin: https://www.conventionalcommits.org"
            exit 1
          fi

          echo "Tum commit mesajlari gecerli."

      - name: Versiyon Etkisini Hesapla ve Yorum Birak
        uses: actions/github-script@v7
        with:
          script: |
            const { execSync } = require('child_process');
            const commits = execSync(
              'git log origin/${{ github.base_ref }}..HEAD --pretty=format:"%s"'
            ).toString().split('n').filter(Boolean);

            let impact = 'none';
            let emoji = '📝';

            for (const commit of commits) {
              if (/BREAKING CHANGE|^feat!|^fix!/i.test(commit)) {
                impact = 'MAJOR'; emoji = '🚨'; break;
              } else if (/^feat/.test(commit) && impact !== 'MAJOR') {
                impact = 'MINOR'; emoji = '✨';
              } else if (/^fix|^perf/.test(commit) && impact === 'none') {
                impact = 'PATCH'; emoji = '🐛';
              }
            }

            const messages = {
              'none': 'Bu PR versiyon degisikligine yol acmayacak.',
              'PATCH': 'Bu PR bir PATCH versiyonu artisina neden olacak (ornek: v1.2.3 -> v1.2.4)',
              'MINOR': 'Bu PR bir MINOR versiyonu artisina neden olacak (ornek: v1.2.3 -> v1.3.0)',
              'MAJOR': 'Bu PR bir MAJOR versiyonu artisina neden olacak (ornek: v1.2.3 -> v2.0.0)'
            };

            await github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## ${emoji} Versiyon Etkisinn${messages[impact]}nn**Etki seviyesi:** `${impact}``
            });

Gerçek Dünya Tavsiyeleri

Teoriden pratiğe geçerken karşılaşacağınız bazı durumlar ve çözümleri:

Eski projelerde versiyonlamaya başlamak: v0.0.0 tag’ini elle oluşturun ve pipeline’ı devreye alın. Geçmiş commit’lerinizi yeniden yazmaya çalışmayın, orası derin bir kuyudur.

Monorepo’larda versiyonlama: Her paketin kendi workflow’u olsun ve path filter kullanın. on: push: paths: ['packages/api/**'] gibi konfigürasyonlar ile sadece ilgili servis versiyonlanır.

[skip ci] kullanımı: Versiyon bump commit’lerinin tekrar pipeline tetiklemesini önlemek için bu tag’i kullanın. Aksi halde sonsuz döngüye girersiniz.

Secrets yönetimi: GITHUB_TOKEN çoğu işlem için yeterli, ama git push işlemleri için genellikle PAT_TOKEN (Personal Access Token) oluşturmanız gerekiyor. Repository Settings’ten Actions > General > Workflow permissions bölümünü kontrol edin.

Release branch’leri: Büyük projelerde release/1.5.x gibi branch’ler açıp sadece patch release’ler için buraya commit atabilirsiniz. Bu sayede 1.5.x serisini desteklemeye devam ederken 1.6.x geliştirme yapılır.

Sonuç

Semantic Versioning ile GitHub Actions kombinasyonu, başlangıçta kurulum gerektiriyor ama bir kez çalışır hale geldiğinde inanılmaz bir zaman kazancı sağlıyor. Artık “hangi versiyonu deploy ettik?”, “bu değişiklik breaking mi?” veya “release notes nerede?” gibi sorularla uğraşmıyorsunuz.

Burada anlatılan yaklaşımın özü şu: commit mesajları birer kontrat. Her geliştirici feat: yazarken “bu bir özellik, MINOR artışı yapılacak” diyor. fix!: yazarken “dikkat, eski API değişiyor” diyor. Bu disiplini takıma oturtmak zaman alıyor, ama PR validation job’u ile otomasyonu hızlı yerleşiyor.

Küçük bir projede başlayın. Önce basit versiyonlama ve release oluşturma adımlarını çalıştırın. Sonra Docker image versiyonlamayı, pre-release’leri ve changelog otomasyonunu ekleyin. Her adımda pipeline’ı test edin ve takımınızın alışkanlıklarına göre şekillendirin. Mükemmel pipeline ilk seferde çıkmaz, iterasyonla olgunlaşır.

Bir yanıt yazın

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