GitLab Pages ile Statik Site Yayınlama Rehberi

Statik site barındırmak için bir VPS kiralamak, Nginx kurmak, SSL sertifikası ayarlamak… Bunların hepsini daha önce yaşadıysanız ne kadar vakit aldığını biliyorsunuzdur. GitLab Pages tam bu noktada devreye giriyor ve tüm bu süreci birkaç satır YAML ile hallediyor. CI/CD pipeline’ınız zaten varsa, statik sitenizi yayına almak için ekstra bir şey yapmanıza gerek kalmıyor.

Bu yazıda GitLab Pages’i sıfırdan kuracağız, özel domain bağlayacağız, farklı static site generator’larla nasıl çalıştığını göreceğiz ve production ortamında karşılaşabileceğiniz tipik sorunları ele alacağız.

GitLab Pages Nedir, Ne Değildir

GitLab Pages, GitLab üzerindeki bir repository’den statik içerik sunmanızı sağlayan bir servistir. GitLab.com üzerinde ücretsiz kullanabilirsiniz, self-hosted GitLab kurulumlarınıza da entegre edebilirsiniz.

Önemli bir nokta: GitLab Pages sadece statik içerik sunar. PHP çalıştıramazsınız, veritabanına bağlanamazsınız, sunucu taraflı herhangi bir kod koşamazsınız. Ama Hugo, Jekyll, Gatsby, MkDocs, VuePress gibi static site generator’larla ürettiğiniz çıktıları, ya da direkt HTML/CSS/JS dosyalarını sonuna kadar güzel yayımlayabilirsiniz.

Servisin temel mantığı şu:

  • CI/CD pipeline çalışır
  • Build işlemi sonucunda public/ adında bir dizin oluşturulur
  • Bu dizin pages job’ı tarafından artifact olarak işaretlenir
  • GitLab bu dizini statik web sunucusunda yayımlar

Temel Gereksinimler

Başlamadan önce nelere ihtiyacınız var:

  • GitLab hesabı: GitLab.com veya kendi sunucunuzda GitLab CE/EE kurulu olmalı
  • Repository: Public veya private, fark etmez
  • .gitlab-ci.yml dosyası: Pipeline tanımınız
  • Build çıktısı public/ dizinine gitmeli: Bu zorunlu, değiştirilemez

Self-hosted GitLab kullanıyorsanız, sistem yöneticisinin GitLab Pages özelliğini etkinleştirmiş olması gerekiyor. /etc/gitlab/gitlab.rb dosyasında pages_external_url tanımlanmış olmalı.

En Basit Örnek: Düz HTML Sitesi

Önce işin özünü görmek için en minimal örneği yapalım. Elimizde sadece statik HTML dosyaları var diyelim.

my-site/
├── .gitlab-ci.yml
├── index.html
├── about.html
└── assets/
    ├── style.css
    └── app.js

Bu yapı için .gitlab-ci.yml dosyası şöyle olur:

pages:
  stage: deploy
  script:
    - mkdir -p public
    - cp -r index.html about.html assets/ public/
  artifacts:
    paths:
      - public
  only:
    - main

Bu kadar. Gerçekten bu kadar. pages job adı özel ve zorunludur, değiştirirseniz GitLab Pages devreye girmez. artifacts.paths altında public tanımlanması da zorunludur.

Commit’i atıp push’ladıktan sonra GitLab’ın sol menüsünden Deploy > Pages kısmına gidip sitenizin URL’ini görebilirsiniz. GitLab.com kullanıyorsanız adres şu formatta olur: https://kullaniciadiniz.gitlab.io/proje-adi/

Hugo ile GitLab Pages

Hugo, Go ile yazılmış ve inanılmaz hızlı bir static site generator. Blog, dokümantasyon sitesi, şirket web sitesi için sıklıkla tercih edilen araçlardan biri. GitLab Pages ile birlikte kullanımı oldukça yaygın.

Önce local’de Hugo projesini oluşturun:

hugo new site myblog
cd myblog
git init
git remote add origin [email protected]:kullaniciadiniz/myblog.git

Bir tema ekleyin, sonra .gitlab-ci.yml dosyasını oluşturun:

image: registry.gitlab.com/pages/hugo:latest

variables:
  GIT_SUBMODULE_STRATEGY: recursive

pages:
  stage: deploy
  script:
    - hugo --minify
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

pages-test:
  stage: deploy
  script:
    - hugo --minify --baseURL "/"
  artifacts:
    paths:
      - public
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH != "main"

Burada GIT_SUBMODULE_STRATEGY: recursive ayarı önemli. Hugo temaları genellikle git submodule olarak ekleniyor, bu ayar olmadan tema dosyaları pipeline içinde boş gelecektir.

pages-test job’ı ise main dışındaki branch’lerde çalışıyor ve preview build üretiyor. Production’a almadan önce görsel kontrol yapabilmek için kullanışlı.

Hugo Sürüm Yönetimi

Zaman içinde farklı projeleriniz farklı Hugo sürümleri gerektirebilir. Bunu .gitlab-ci.yml içinde sabit bir imajla çözmek yerine, extend edilebilir bir yaklaşım kullanabilirsiniz:

variables:
  HUGO_VERSION: "0.120.4"

pages:
  stage: deploy
  image: hugomods/hugo:${HUGO_VERSION}
  script:
    - hugo --minify --gc
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Jekyll ile GitLab Pages

Jekyll, GitHub Pages’in de kullandığı Ruby tabanlı static site generator. GitLab’ın kendi dökümantasyon sitesi bile yıllarca Jekyll ile çalıştı.

image: ruby:3.2

cache:
  paths:
    - vendor/

before_script:
  - gem install bundler
  - bundle install --path vendor

pages:
  stage: deploy
  script:
    - bundle exec jekyll build -d public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

test:
  stage: test
  script:
    - bundle exec jekyll build -d test
    - bundle exec htmlproofer test --disable-external
  artifacts:
    paths:
      - test
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH != "main"

Bu örnekte htmlproofer tool’unu da ekliyoruz. Broken link’leri, eksik alt etiketlerini, geçersiz iç linkleri otomatik olarak kontrol ediyor. Production’a gitmeden önce bu kontrollerin geçmesi gerekiyor.

cache kısmı da önemli. Ruby gem’lerini her pipeline’da sıfırdan indirmek yerine cache’leyerek pipeline süresini ciddi ölçüde kısaltıyoruz.

MkDocs ile Teknik Dokümantasyon Sitesi

Proje dokümantasyonu, iç wiki, API dökümantasyonu için MkDocs mükemmel bir araç. Material teması ile de profesyonel bir görünüm elde edebiliyorsunuz.

image: python:3.11-slim

cache:
  paths:
    - .cache/pip

before_script:
  - pip install mkdocs mkdocs-material mkdocs-minify-plugin

pages:
  stage: deploy
  script:
    - mkdocs build --strict --verbose
    - mv site public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

lint-docs:
  stage: test
  script:
    - pip install mkdocs mkdocs-material
    - mkdocs build --strict
  rules:
    - if: $CI_COMMIT_BRANCH != "main"

--strict flag’i burada kritik. Uyarıları hata olarak işliyor, yani broken link veya eksik referans varsa pipeline fail ediyor. Dökümantasyon kalitesini otomatik olarak zorlamış oluyorsunuz.

Özel Domain Bağlamak

GitLab’ın verdiği *.gitlab.io URL’i çoğu kişisel proje için yeterli, ama kurumsal kullanımda kendi domain’inizi bağlamak isteyeceksiniz.

GitLab.com üzerinde özel domain eklemek için:

1. GitLab’da domain ekleyin

Repository sayfasından Deploy > Pages kısmına gidin, New Domain butonuna tıklayın ve domain’inizi girin. GitLab size bir verification TXT kaydı gösterecek.

2. DNS kayıtlarını ayarlayın

Domain sağlayıcınızın panelinde şu kayıtları ekleyin:

# Apex domain için (ornegin example.com)
CNAME  @         kullaniciadiniz.gitlab.io

# Alt domain için (ornegin blog.example.com)
CNAME  blog      kullaniciadiniz.gitlab.io

# Verification icin
TXT    _gitlab-pages-verification-code.example.com    gitlab-pages-verification-code=xxxxxxxx

3. SSL sertifikasını aktifleştirin

GitLab, Let’s Encrypt entegrasyonu ile otomatik SSL sertifikası alabiliyor. Domain doğrulandıktan sonra Force HTTPS seçeneğini de aktif edin.

DNS propagation’ı bekleyip doğruladıktan sonra siteniz https://example.com adresinden yayımlanmaya başlayacak.

Erişim Kontrolü ve Private Pages

Bazı durumlarda sitenin herkese açık olmasını istemezsiniz. Staging ortamı, iç dökümantasyon, müşteriye özel preview sayfaları… GitLab Pages için erişim kontrolü tanımlayabilirsiniz.

Repository sayfasından Settings > General > Visibility, project features, permissions kısmına gidip Pages bölümünde şu seçenekler var:

  • Everyone: Herkes erişebilir
  • Everyone with access: Proje member’ları
  • Only project members: Sadece proje üyeleri

Private pages aktif olduğunda ziyaretçilerden GitLab hesabıyla giriş yapmaları isteniyor. CI/CD job’larından erişmek için ise CI_JOB_TOKEN kullanabilirsiniz.

Gerçek Dünya Senaryosu: Çoklu Ortam Yayımı

Bir şirkette çalıştığınızı ve hem staging hem production ortamı için GitLab Pages kullanmak istediğinizi düşünelim. Develop branch’i staging’e, main branch’i production’a yayımlamalı.

stages:
  - build
  - test
  - deploy

variables:
  NODE_VERSION: "20"

.build-template: &build-template
  image: node:${NODE_VERSION}-alpine
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
  before_script:
    - npm ci

build-staging:
  <<: *build-template
  stage: build
  script:
    - npm run build:staging
  artifacts:
    paths:
      - dist/
    expire_in: 1 day
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

build-production:
  <<: *build-template
  stage: build
  script:
    - npm run build:production
  artifacts:
    paths:
      - dist/
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

lighthouse-test:
  stage: test
  image: cypress/browsers:node20-chrome108
  script:
    - npm install -g @lhci/cli
    - lhci autorun
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  allow_failure: true

pages:
  stage: deploy
  script:
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  environment:
    name: production
    url: https://example.com

pages-staging:
  stage: deploy
  script:
    - mv dist public
  artifacts:
    paths:
      - public
    expire_in: 2 weeks
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"
  environment:
    name: staging
    url: https://staging.example.com

Bu yapıda environment tanımı önemli. GitLab’ın Operate > Environments sayfasında her iki ortamın durumunu, son deploy’u ve URL’ini görebiliyorsunuz.

Pipeline Optimizasyonu

Pages pipeline’ınız zamanla yavaşlayabilir. Birkaç pratik optimizasyon:

Cache Stratejisi

cache:
  key:
    files:
      - package-lock.json
    prefix: ${CI_JOB_NAME}
  paths:
    - node_modules/
  policy: pull-push

key.files ile package-lock.json değiştiğinde cache invalidate oluyor, değişmediğinde eski cache kullanılıyor. prefix ile her job’ın kendi cache’i oluyor, çakışma olmuyor.

Parallel Build

Büyük sitelerde build işlemi uzun sürebilir. Mümkünse build’i parçalara bölebilirsiniz:

build-en:
  stage: build
  script:
    - hugo --contentDir content/en --destination public/en
  artifacts:
    paths:
      - public/en/

build-tr:
  stage: build
  script:
    - hugo --contentDir content/tr --destination public/tr
  artifacts:
    paths:
      - public/tr/

pages:
  stage: deploy
  script:
    - mkdir -p public
    - cp -r public/en/* public/
    - cp -r public/tr/* public/
  artifacts:
    paths:
      - public

Sadece Değişen Dosyalarda Build Tetikleme

Her commit’te tam build yapmak istemeyebilirsiniz. Özellikle büyük bir dokümantasyon sitesinde sadece content değiştiğinde build tetiklensin isteyebilirsiniz:

pages:
  stage: deploy
  script:
    - mkdocs build
    - mv site public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      changes:
        - docs/**/*
        - mkdocs.yml

changes direktifi ile sadece belirtilen dosyalar değiştiğinde job tetikleniyor.

Sık Karşılaşılan Sorunlar

Public Dizini Oluşturulmuyor

En yaygın hata budur. Job başarılı görünür ama Pages güncellenmez. Sebebi genellikle build aracının çıktıyı farklı bir dizine yazmasıdır.

# Yanlis: Build ciktisi dist/ dizinine gidiyor
npm run build
# public/ dizini yok, Pages guncellenmez

# Dogru: dist/ icerigi public/ altina aliniyor
npm run build
mv dist public

Base URL Problemi

Statik siteniz https://kullanici.gitlab.io/proje-adi/ altında yayımlanıyor ama site kök dizinde (/) olduğunu varsayarak asset yüklüyor. Bu durumda CSS ve JS dosyaları yüklenemiyor.

Hugo için:

script:
  - hugo --baseURL "https://kullanici.gitlab.io/proje-adi/"

Ya da config.yaml içinde:

baseURL: "https://kullanici.gitlab.io/proje-adi/"

404 Sayfaları Single Page Application’larda

React, Vue gibi SPA’lar client-side routing kullanıyor. GitLab Pages doğrudan dosya sunduğu için /about adresine gidildiğinde about.html arar, bulamazsa 404 döner.

Çözüm: 404.html dosyasını index.html ile aynı yapın. GitLab Pages 404 durumunda 404.html dosyasını sunar, SPA kendi routing’ini halleder.

pages:
  script:
    - npm run build
    - cp public/index.html public/404.html
  artifacts:
    paths:
      - public

Self-Hosted GitLab’da Pages Etkin Değil

Kendi GitLab kurulumunuzda Pages çalışmıyorsa önce /etc/gitlab/gitlab.rb dosyasını kontrol edin:

sudo grep -i pages /etc/gitlab/gitlab.rb | grep -v "^#"

Gerekli minimum konfigürasyon:

pages_external_url 'http://pages.example.com'
gitlab_pages['enable'] = true

Değişiklik yaptıktan sonra:

sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart gitlab-pages

Deployment Koruması

Production ortamı için deployment’ları koruma altına almak iyi bir pratik. Settings > CI/CD > Protected Environments kısmından production environment’ı koruma altına alabilir, sadece belirli role’lerin deployment yapabilmesini sağlayabilirsiniz.

Aynı şekilde Settings > Repository > Protected Branches kısmından main branch’ini koruma altına alın. Direkt push’u kapat, sadece merge request ile değişiklik yapılabilsin.

Bu sayede sitenize giden her değişiklik code review sürecinden geçmek zorunda kalır. Hem güvenlik hem de kalite açısından kritik.

Monitoring ve Analitik

GitLab Pages kendi access log’unu kullanıcıya açmıyor. Sitenize gelen trafiği takip etmek için client-side analitik araçları kullanmanız gerekiyor.

Privacy-friendly seçenekler:

  • Plausible.io: GDPR uyumlu, cookie’siz
  • Fathom Analytics: Basit ve hızlı
  • Umami: Self-hosted, tamamen ücretsiz

Google Analytics kullanmak istiyorsanız çoğu static site generator bunu doğrudan destekliyor. Hugo için config.yaml içine Google Analytics ID’nizi eklemek yeterli.

Sonuç

GitLab Pages, birkaç satır YAML ile production-ready statik site yayımlamanızı sağlayan güçlü bir araç. Öğrenme eğrisi neredeyse yok, bakım maliyeti çok düşük, ücretsiz SSL sertifikası ile birlikte geliyor.

Pratik öneri: Eğer hala bir VPS üzerinde manuel olarak statik site yayımlıyorsanız, en kısa sürede GitLab Pages’e geçin. Tek bir .gitlab-ci.yml dosyası, sunucu bakımı, SSL yenileme, Nginx konfigürasyonu gibi onlarca saatlik işin önüne geçiyor.

Büyük ekipler için de ölçeklenebilir: protected environment’lar, deployment approval’ları ve environment-specific build konfigürasyonları ile tam anlamıyla bir CI/CD sürecine entegre edebiliyorsunuz.

Bir sonraki adım olarak GitLab’ın Review Apps özelliğine bakmanızı öneririm. Her merge request için otomatik preview ortamı oluşturuyor ve Pages ile birlikte kullanıldığında kod review sürecini görsel olarak çok daha verimli hale getiriyor.

Bir yanıt yazın

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