GitLab Wiki ve Confluence ile Dokümantasyon Yönetimi

Dokümantasyon meselesini ciddiye almayan ekiplerin eninde sonunda aynı acıyı yaşadığını gördüm: altı ay önce kurduğun sistemin nasıl çalıştığını kendin unutuyorsun. Ya da daha kötüsü, yeni katılan arkadaş “bu neden böyle yapılmış?” diye soruyor ve cevabın yok. GitLab Wiki ve Confluence, bu soruna farklı açılardan yaklaşan iki güçlü araç. Hangisinin ne zaman, nasıl kullanılacağını ve ikisini nasıl bir arada yönetebileceğini anlatacağım.

GitLab Wiki: Kod ile Beraber Yaşayan Dokümantasyon

GitLab Wiki’nin en büyük avantajı, doğrudan projeyle birlikte yaşaması. Kodun yanında, aynı repository içinde (teknik olarak ayrı bir git repo olsa da aynı arayüzde) duran bir dokümantasyon. Bu yakınlık, güncelleme alışkanlığını ciddi ölçüde artırıyor.

Wiki Repository’sini Clone Etmek

GitLab Wiki aslında ayrı bir Git repository. Bu, onu yerel makinenizde düzenleyebileceğiniz, versiyon geçmişini takip edebileceğiniz ve CI/CD pipeline’larıyla entegre edebileceğiniz anlamına geliyor.

# Wiki repository'sini clone et
git clone https://gitlab.example.com/your-group/your-project.wiki.git

# Yerel düzenleme yap
cd your-project.wiki
vim deployment-guide.md

# Değişiklikleri push et
git add deployment-guide.md
git commit -m "Deployment guide güncellendi: Redis cluster kurulumu eklendi"
git push origin master

Bu yaklaşım, wiki’yi sadece web arayüzünden düzenlemek yerine favori editörünüzde yönetmenizi sağlıyor. Özellikle büyük dokümantasyon blokları yazarken bu farkı hissediyorsunuz.

Otomatik Wiki Güncellemesi: CI/CD ile Entegrasyon

Gerçek dünyada sık yaşanan bir sorun: kod değişiyor ama dokümantasyon yerinde duruyor. Bunu önlemek için pipeline içinde wiki’yi otomatik güncelleyebilirsiniz.

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy
  - docs

update_wiki:
  stage: docs
  image: alpine:latest
  before_script:
    - apk add --no-cache git
    - git config --global user.email "[email protected]"
    - git config --global user.name "CI Bot"
  script:
    - git clone https://oauth2:${WIKI_TOKEN}@gitlab.example.com/${CI_PROJECT_PATH}.wiki.git wiki_repo
    - cd wiki_repo
    - cp ../docs/deployment-guide.md deployment-guide.md
    - cp ../docs/api-reference.md api-reference.md
    - git add -A
    - git diff --staged --quiet || git commit -m "Otomatik güncelleme: ${CI_COMMIT_SHORT_SHA}"
    - git push origin master
  only:
    - main

Burada WIKI_TOKEN olarak bir project access token oluşturmanız gerekiyor. GitLab’da Settings > Access Tokens bölümünden write_repository yetkisiyle token alın ve bunu CI/CD variable olarak ekleyin.

Wiki Yapısını Organize Etmek

Büyük projelerde wiki hızla kaotik bir hal alabilir. Bir kaç kuralı baştan koymak çok şeyi kurtarıyor:

# İyi bir wiki yapısı örneği
wiki_repo/
├── home.md                    # Ana sayfa, genel bakış
├── architecture/
│   ├── overview.md
│   ├── database-design.md
│   └── network-topology.md
├── operations/
│   ├── deployment-guide.md
│   ├── backup-restore.md
│   └── monitoring-setup.md
├── development/
│   ├── getting-started.md
│   ├── coding-standards.md
│   └── testing-guide.md
└── runbooks/
    ├── incident-response.md
    └── common-issues.md

GitLab Wiki, dosya adlarından otomatik olarak navigasyon oluşturuyor. Ama alt dizin yapısını web arayüzünde görmek için home.md içinde manuel bağlantılar kurmanız gerekiyor:

## Mimari

- [Genel Bakış](architecture/overview)
- [Veritabanı Tasarımı](architecture/database-design)
- [Ağ Topolojisi](architecture/network-topology)

## Operasyonlar

- [Deployment Rehberi](operations/deployment-guide)
- [Yedekleme ve Geri Yükleme](operations/backup-restore)

Confluence Entegrasyonu: Kurumsal Ölçekte Dokümantasyon

Confluence, özellikle birden fazla ekibin ve projenin olduğu ortamlarda, GitLab Wiki’nin yetersiz kaldığı durumlarda devreye giriyor. Jira ile native entegrasyonu, güçlü arama özellikleri ve şablon sistemi, kurumsal ortamlarda Confluence’ı tercih nedeni yapıyor.

GitLab’dan Confluence’a Otomatik İçerik Gönderme

GitLab CI/CD pipeline’ından Confluence REST API’sini kullanarak sayfa oluşturmak veya güncellemek mümkün. Bu yaklaşım, “tek kaynak, çoklu hedef” prensibine uygun çalışıyor.

#!/bin/bash
# scripts/update-confluence.sh

CONFLUENCE_URL="https://your-company.atlassian.net"
CONFLUENCE_USER="[email protected]"
CONFLUENCE_TOKEN="${CONFLUENCE_API_TOKEN}"
SPACE_KEY="DEVOPS"
PAGE_TITLE="Deployment Guide - ${CI_PROJECT_NAME}"

# Mevcut sayfayı bul
PAGE_ID=$(curl -s 
  -u "${CONFLUENCE_USER}:${CONFLUENCE_TOKEN}" 
  -X GET 
  "${CONFLUENCE_URL}/wiki/rest/api/content?title=${PAGE_TITLE}&spaceKey=${SPACE_KEY}" 
  | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['results'][0]['id'] if data['results'] else '')")

# Markdown'ı Confluence storage formatına çevir
CONTENT=$(cat docs/deployment-guide.md | python3 scripts/md-to-confluence.py)

if [ -z "$PAGE_ID" ]; then
  # Yeni sayfa oluştur
  curl -s 
    -u "${CONFLUENCE_USER}:${CONFLUENCE_TOKEN}" 
    -H "Content-Type: application/json" 
    -X POST 
    "${CONFLUENCE_URL}/wiki/rest/api/content" 
    -d "{
      "type": "page",
      "title": "${PAGE_TITLE}",
      "space": {"key": "${SPACE_KEY}"},
      "body": {
        "storage": {
          "value": "${CONTENT}",
          "representation": "storage"
        }
      }
    }"
else
  # Mevcut sayfayı güncelle
  CURRENT_VERSION=$(curl -s 
    -u "${CONFLUENCE_USER}:${CONFLUENCE_TOKEN}" 
    "${CONFLUENCE_URL}/wiki/rest/api/content/${PAGE_ID}" 
    | python3 -c "import sys,json; print(json.load(sys.stdin)['version']['number'])")

  NEXT_VERSION=$((CURRENT_VERSION + 1))

  curl -s 
    -u "${CONFLUENCE_USER}:${CONFLUENCE_TOKEN}" 
    -H "Content-Type: application/json" 
    -X PUT 
    "${CONFLUENCE_URL}/wiki/rest/api/content/${PAGE_ID}" 
    -d "{
      "version": {"number": ${NEXT_VERSION}},
      "type": "page",
      "title": "${PAGE_TITLE}",
      "body": {
        "storage": {
          "value": "${CONTENT}",
          "representation": "storage"
        }
      }
    }"
fi

Markdown’ı Confluence Formatına Dönüştürme

Confluence’ın kendi storage formatı (XHTML tabanlı) ile standart Markdown arasında bir köprüye ihtiyaç var. markdown-confluence veya md2cf gibi araçlar bu dönüşümü yapıyor:

# md2cf kurulumu
pip install md2cf

# Kullanımı
md2cf 
  --host "https://your-company.atlassian.net" 
  --username "[email protected]" 
  --password "${CONFLUENCE_API_TOKEN}" 
  --space "DEVOPS" 
  --parent-title "Teknik Dokümantasyon" 
  docs/deployment-guide.md

# Birden fazla dosyayı toplu gönder
find docs/ -name "*.md" -exec md2cf 
  --host "https://your-company.atlassian.net" 
  --username "[email protected]" 
  --password "${CONFLUENCE_API_TOKEN}" 
  --space "DEVOPS" 
  --parent-title "Teknik Dokümantasyon" 
  --use-document-title 
  {} ;

İki Platformu Birlikte Yönetmek: Pratik Bir Strateji

Birçok ekip hem GitLab Wiki hem de Confluence kullanmak zorunda kalıyor. Bu durumda “neyi nerede tutacağız?” sorusu kritik. Kendi deneyimlerime dayanarak şöyle bir ayrım işe yarıyor:

GitLab Wiki için uygun içerikler:

  • Proje spesifik teknik detaylar (mimari kararlar, API dokümantasyonu)
  • Deployment ve operasyon runbook’ları
  • Geliştirici kurulum rehberleri
  • Kod ile eş zamanlı güncellenmesi gereken her şey

Confluence için uygun içerikler:

  • Şirket genelinde politikalar ve standartlar
  • Proje yönetimi ve sprint dökümanları
  • Paydaşlara yönelik üst seviye dokümantasyon
  • Jira entegrasyonu gerektiren içerikler

Çift Yönlü Senkronizasyon Kurulumu

Bazı durumlarda içeriklerin her iki platformda da güncel kalması gerekiyor. Bu için basit bir webhook tabanlı sistem kurabilirsiniz:

# sync_docs.py
import os
import requests
import json
from pathlib import Path

class DocSync:
    def __init__(self):
        self.gitlab_token = os.environ['GITLAB_TOKEN']
        self.confluence_token = os.environ['CONFLUENCE_TOKEN']
        self.confluence_user = os.environ['CONFLUENCE_USER']
        self.confluence_url = os.environ['CONFLUENCE_URL']
        self.project_id = os.environ['CI_PROJECT_ID']

    def get_wiki_pages(self):
        """GitLab Wiki sayfalarını listele"""
        headers = {'PRIVATE-TOKEN': self.gitlab_token}
        url = f"https://gitlab.example.com/api/v4/projects/{self.project_id}/wikis"
        response = requests.get(url, headers=headers)
        return response.json()

    def create_or_update_confluence_page(self, title, content, space_key, parent_id=None):
        """Confluence'da sayfa oluştur veya güncelle"""
        auth = (self.confluence_user, self.confluence_token)
        headers = {'Content-Type': 'application/json'}

        # Mevcut sayfa kontrolü
        search_url = f"{self.confluence_url}/wiki/rest/api/content"
        params = {'title': title, 'spaceKey': space_key}
        search_resp = requests.get(search_url, auth=auth, params=params)
        results = search_resp.json().get('results', [])

        payload = {
            'type': 'page',
            'title': title,
            'space': {'key': space_key},
            'body': {
                'storage': {
                    'value': content,
                    'representation': 'wiki'
                }
            }
        }

        if parent_id:
            payload['ancestors'] = [{'id': parent_id}]

        if results:
            page_id = results[0]['id']
            current_version = results[0]['version']['number']
            payload['version'] = {'number': current_version + 1}
            url = f"{self.confluence_url}/wiki/rest/api/content/{page_id}"
            response = requests.put(url, auth=auth, headers=headers, json=payload)
        else:
            url = f"{self.confluence_url}/wiki/rest/api/content"
            response = requests.post(url, auth=auth, headers=headers, json=payload)

        return response.json()

if __name__ == '__main__':
    sync = DocSync()
    pages = sync.get_wiki_pages()
    for page in pages:
        print(f"Senkronize ediliyor: {page['title']}")
        sync.create_or_update_confluence_page(
            title=page['title'],
            content=page['content'],
            space_key='DEVOPS'
        )

Versiyon Kontrolü ve Değişiklik Takibi

GitLab Wiki’nin en değerli özelliklerinden biri, tam git geçmişine sahip olması. Bu, hangi bilginin ne zaman, kim tarafından değiştirildiğini takip etmenizi sağlıyor.

# Wiki değişiklik geçmişini görüntüle
cd your-project.wiki
git log --oneline --graph docs/deployment-guide.md

# Belirli bir değişikliği karşılaştır
git diff HEAD~3 HEAD -- deployment-guide.md

# Belirli bir tarihteki versiyona dön
git show HEAD~5:deployment-guide.md > deployment-guide-old.md

# Kimin ne değiştirdiğini gör
git blame deployment-guide.md

Confluence tarafında ise sayfa geçmişi özelliği benzer işlevi görüyor ama bunu API üzerinden de sorgulayabilirsiniz:

# Confluence sayfa geçmişini API ile çek
PAGE_ID="123456789"
curl -s 
  -u "${CONFLUENCE_USER}:${CONFLUENCE_TOKEN}" 
  "https://your-company.atlassian.net/wiki/rest/api/content/${PAGE_ID}/version" 
  | python3 -m json.tool | grep -E '"number"|"when"|"by"'

Kalitenin Korunması: Dokümantasyon Linting

Dokümantasyonun teknik doğruluğu kadar formatı ve tutarlılığı da önemli. Bunun için CI pipeline’ına basit kontroller ekleyebilirsiniz:

# .gitlab-ci.yml - Dokümantasyon kalite kontrolü
lint_docs:
  stage: test
  image: node:18-alpine
  before_script:
    - npm install -g markdownlint-cli
    - npm install -g markdown-link-check
  script:
    # Markdown formatı kontrolü
    - markdownlint 'docs/**/*.md' --config .markdownlint.json
    # Bozuk linkleri kontrol et
    - find docs -name "*.md" -exec markdown-link-check {} ;
    # Kelime sayısı kontrolü (çok kısa sayfaları yakala)
    - |
      for file in docs/**/*.md; do
        word_count=$(wc -w < "$file")
        if [ "$word_count" -lt 50 ]; then
          echo "UYARI: $file çok kısa ($word_count kelime)"
        fi
      done
  artifacts:
    when: always
    paths:
      - docs/

.markdownlint.json yapılandırması:

{
  "default": true,
  "MD013": false,
  "MD033": false,
  "MD041": false,
  "MD024": {
    "siblings_only": true
  }
}

Gerçek Dünya Senaryosu: Incident Sonrası Otomatik Dokümantasyon

Bir incident yaşandı, çözüldü ve “bunu bir yere yazalım” dedik. Ama kim yazacak, nereye yazacak, format ne olacak? Bu süreci de otomatize edebilirsiniz. GitLab Issues ile bağlantılı bir runbook güncelleme akışı:

#!/bin/bash
# post-incident-doc-update.sh
# Kullanım: ./post-incident-doc-update.sh "INC-2024-001" "Redis Memory Leak" "memory_leak"

INCIDENT_ID=$1
INCIDENT_TITLE=$2
RUNBOOK_TAG=$3

WIKI_REPO="https://oauth2:${WIKI_TOKEN}@gitlab.example.com/${CI_PROJECT_PATH}.wiki.git"
RUNBOOKS_DIR="runbooks"

# Wiki'yi çek
git clone "${WIKI_REPO}" wiki_temp
cd wiki_temp

# Runbook dosyasını güncelle veya oluştur
RUNBOOK_FILE="${RUNBOOKS_DIR}/${RUNBOOK_TAG}.md"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M')

if [ -f "${RUNBOOK_FILE}" ]; then
  # Mevcut runbook'a incident referansı ekle
  cat >> "${RUNBOOK_FILE}" << EOF

## Incident Geçmişi: ${INCIDENT_ID} (${TIMESTAMP})

**Başlık:** ${INCIDENT_TITLE}
**Tarih:** ${TIMESTAMP}
**GitLab Issue:** ${CI_PROJECT_URL}/issues (${INCIDENT_ID} için arama yapın)

### Öğrenilenler

_Bu bölüm incident sonrası ekip tarafından doldurulacak_

---
EOF
else
  # Yeni runbook oluştur
  mkdir -p "${RUNBOOKS_DIR}"
  cat > "${RUNBOOK_FILE}" << EOF
# Runbook: ${INCIDENT_TITLE}

**Son Güncelleme:** ${TIMESTAMP}
**Kategori:** ${RUNBOOK_TAG}

## Belirtiler

_Doldurun_

## Olası Nedenler

_Doldurun_

## Çözüm Adımları

_Doldurun_

## Incident Geçmişi

### ${INCIDENT_ID} (${TIMESTAMP})

İlk incident kaydı oluşturuldu.
EOF
fi

git add -A
git commit -m "Incident ${INCIDENT_ID} için runbook güncellendi: ${INCIDENT_TITLE}"
git push origin master

echo "Runbook güncellendi: ${RUNBOOK_FILE}"

Ekip İçi Benimseme: Teknik Çözümden Daha Zor Kısım

Araçları kurmak kolay, ekibin dokümantasyon yazmayı alışkanlık haline getirmesi zor. Birkaç pratik yaklaşım:

Merge Request template’leri ile dokümantasyonu zorunlu kılmak:

<!-- .gitlab/merge_request_templates/default.md -->
## Değişiklik Özeti

## Test Edildi mi?
- [ ] Birim testler geçiyor
- [ ] Integration testler geçiyor

## Dokümantasyon
- [ ] İlgili wiki sayfası güncellendi
- [ ] CHANGELOG güncellendi
- [ ] API değişikliği varsa, API dokümantasyonu güncellendi

## İlgili Sayfalar
Wiki linki: 
Confluence linki (varsa): 

Definition of Done içine dokümantasyonu dahil etmek, ekibin dokümantasyonu iş olarak görmesini sağlıyor. “Kodu bitirdim” değil, “kodu bitirdim ve yazdım” bitmiş iş sayılıyor.

Sonuç

GitLab Wiki ve Confluence’ı “rakip” araçlar olarak değil, farklı ihtiyaçları karşılayan tamamlayıcı platformlar olarak konumlandırmak gerekiyor. GitLab Wiki, kod ile birlikte yaşayan, geliştiricilerin doğal akışında yer alan teknik dokümantasyon için güçlü. Confluence ise kurumsal süreçler, çok ekipli koordinasyon ve paydaş odaklı içerik için daha uygun.

Burada anlattığım otomasyon yaklaşımlarının temel amacı, dokümantasyon yazmayı kolaylaştırmak ve insan iradesine bağlı bir süreç olmaktan çıkarmak. CI/CD pipeline’ına entegre ettiğiniz, wiki güncellemelerini otomatize ettiğiniz, linting kuralları koyduğunuz zaman dokümantasyon kalitesi doğal olarak yükseliyor.

En iyi dokümantasyon sistemi, ekibin gerçekten kullandığı sistemdir. Karmaşık otomasyon kurmadan önce ekibin hangi araçlara zaten alışkın olduğunu, hangi sürtünme noktalarını yaşadığını anlayın. Sonra bu yazıdaki yapı taşlarını kendi ihtiyaçlarınıza göre adapte edin.

Bir yanıt yazın

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