GitHub Packages ile Özel Paket Deposu Oluşturma
Ekip büyüdükçe, iç kütüphaneler çoğaldıkça bir noktada şu soru kaçınılmaz olarak geliyor: “Bu paketi nereye koyacağız?” Herkese açık npm, PyPI ya da Maven depolarına atmak istemiyorsunuz çünkü iç kullanıma özel. Kendi Nexus ya da Artifactory kurulumu için ne zaman ne para var. İşte tam bu noktada GitHub Packages devreye giriyor ve çoğu zaman göz ardı edilen bir çözüm sunuyor.
Bu yazıda npm, Maven ve Docker image’ları için GitHub Packages üzerinde özel paket deposu kurulumunu, CI/CD entegrasyonunu ve ekip içi yetkilendirme yapısını ele alacağız. Üretimde kullandığım gerçek konfigürasyonları paylaşacağım.
GitHub Packages Nedir ve Ne Zaman Mantıklıdır?
GitHub Packages, GitHub’ın kendi içine gömülü paket barındırma servisidir. npm, Maven, Gradle, RubyGems, NuGet ve Docker/OCI image’larını destekler. Repository ile aynı namespace altında yaşar, bu da erişim kontrolünü ciddi ölçüde basitleştirir.
Ne zaman kullanmalısınız?
- Zaten GitHub kullanıyorsanız ve ek bir servis ayağa kaldırmak istemiyorsanız
- Repository bazlı paket erişim kontrolü yeterliyse
- Küçük-orta ölçekli ekipler için (büyük ölçekte fiyatlandırma sorun olabilir)
- Private repository’lerinizle sıkı entegrasyon istiyorsanız
Ne zaman alternatif düşünmelisiniz?
- Çok sayıda büyük Docker image’ı saklayacaksanız (depolama maliyeti artar)
- Çok granüler paket bazlı izin yönetimi gerekiyorsa
- GitHub dışı CI/CD sistemleri ağırlıklıysa
Şimdi işin pratik kısmına geçelim.
Personal Access Token ile Kimlik Doğrulama
GitHub Packages’a erişmek için bir PAT (Personal Access Token) oluşturmanız gerekiyor. Fine-grained token kullanabilirsiniz ama klasik token daha geniş araç desteğine sahip, tercihim o yönde.
GitHub üzerinde Settings > Developer settings > Personal access tokens > Tokens (classic) yolunu izleyin. Gerekli scope’lar:
- read:packages: Paket indirmek için
- write:packages: Paket yayımlamak için
- delete:packages: Paket silmek için (dikkatli kullanın)
- repo: Private repository paketlerine erişmek için zorunlu
Token’ı bir değişkene alalım ve test edelim:
export GH_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"
export GH_USER="kullanici-adiniz"
# Docker registry için login testi
echo $GH_TOKEN | docker login ghcr.io -u $GH_USER --password-stdin
# Login Succeeded çıktısını görmelisiniz
CI/CD ortamlarında bu token’ı asla kod içine gömmeyin. GitHub Actions kullanıyorsanız zaten GITHUB_TOKEN otomatik olarak inject ediliyor, onu kullanın.
npm Paketi Yayımlamak
Diyelim ki şirket içi bir utility kütüphaneniz var, @sirket/utils adında. Bu paketi npm yerine GitHub Packages’a göndermek istiyorsunuz.
Önce proje dizinindeki package.json dosyasını düzenleyin:
{
"name": "@sirket/utils",
"version": "1.0.0",
"description": "Sirket ici utility kutuphanesi",
"main": "index.js",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
"repository": {
"type": "git",
"url": "https://github.com/sirket/utils.git"
}
}
Dikkat edin, name alanındaki scope (@sirket) GitHub organizasyon veya kullanıcı adınızla birebir eşleşmeli. Bu eşleşme olmazsa publish işlemi hata verir.
Sonra .npmrc dosyası oluşturun, bunu proje kökünde değil, home dizininizde (~/.npmrc) tutmak daha güvenli:
# ~/.npmrc
@sirket:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=ghp_xxxxxxxxxxxxxxxxxxxx
Ya da environment variable ile dinamik hale getirin:
# Proje dizinindeki .npmrc (token'i env'den alır)
@sirket:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GH_TOKEN}
Şimdi publish edelim:
npm publish
# Çıktı:
# npm notice Publishing to https://npm.pkg.github.com
# + @sirket/[email protected]
Başka bir projede bu paketi kullanmak için o projenin .npmrc dosyasına aynı registry tanımını eklemeniz yeterli:
# Tüketen projenin .npmrc dosyası
@sirket:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GH_TOKEN}
Ardından normal npm install ile çekilebilir:
npm install @sirket/utils
Maven Paketi için Ayarlar
Java/Kotlin ekipleri için Maven deposu kurulumu biraz daha fazla konfigürasyon istiyor ama mantık aynı.
pom.xml dosyanıza distribution management ekleyin:
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/ORGANIZASYON/REPOSITORY</url>
</repository>
</distributionManagement>
Maven’ın settings.xml dosyasına (genellikle ~/.m2/settings.xml) kimlik bilgilerini ekleyin:
<settings>
<servers>
<server>
<id>github</id>
<username>GH_KULLANICI_ADI</username>
<password>GH_TOKEN_DEGERI</password>
</server>
</servers>
</settings>
Paketi yayımlamak için:
mvn deploy
Paketi tüketmek için pom.xml içine repository tanımı eklemek gerekiyor:
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/ORGANIZASYON/REPOSITORY</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Docker Image’larını GitHub Container Registry’de Saklamak
GitHub’ın Docker registry’si ghcr.io adresiyle hizmet veriyor. Eski docker.pkg.github.com adresi deprecated durumda, artık ghcr.io kullanın.
Image build ve push işlemi oldukça basit:
# Image'ı build et
docker build -t ghcr.io/ORGANIZASYON/uygulama-adi:1.0.0 .
# Registry'ye login ol
echo $GH_TOKEN | docker login ghcr.io -u $GH_USER --password-stdin
# Image'ı gönder
docker push ghcr.io/ORGANIZASYON/uygulama-adi:1.0.0
# Latest tag'ini de push et
docker tag ghcr.io/ORGANIZASYON/uygulama-adi:1.0.0 ghcr.io/ORGANIZASYON/uygulama-adi:latest
docker push ghcr.io/ORGANIZASYON/uygulama-adi:latest
Başka bir makineden bu image’ı çekmek için:
echo $GH_TOKEN | docker login ghcr.io -u $GH_USER --password-stdin
docker pull ghcr.io/ORGANIZASYON/uygulama-adi:1.0.0
Kubernetes ortamında bu image’ı kullanmak istiyorsanız bir imagePullSecret oluşturmanız gerekiyor:
kubectl create secret docker-registry ghcr-secret
--docker-server=ghcr.io
--docker-username=GH_KULLANICI_ADI
--docker-password=GH_TOKEN_DEGERI
[email protected]
-n uygulama-namespace
GitHub Actions ile Tam Otomatik Pipeline
El ile publish yapmak geliştirme ortamında kabul edilebilir ama üretimde CI/CD pipeline’ı olmadan olmaz. İşte npm paketi için tam bir GitHub Actions workflow’u:
name: Paket Yayimla
on:
release:
types: [created]
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Kodu cek
uses: actions/checkout@v4
- name: Node.js kur
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://npm.pkg.github.com'
scope: '@sirket'
- name: Bagimliliklari yukle
run: npm ci
- name: Testleri calistir
run: npm test
- name: Paketi yayimla
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Burada kritik nokta permissions bloğu. packages: write olmadan workflow paketi yayımlayamaz. GITHUB_TOKEN ise GitHub Actions tarafından otomatik sağlanıyor, ayrıca bir secret tanımlamanıza gerek yok.
Docker image için benzer bir workflow:
name: Docker Image Olustur ve Yayimla
on:
push:
branches: [main]
tags: ['v*.*.*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Kodu cek
uses: actions/checkout@v4
- name: Docker Buildx kur
uses: docker/setup-buildx-action@v3
- name: GHCR'ye login ol
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Metadata olustur
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Image olustur ve push et
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
Bu workflow main branch’e her push’ta ve v..* formatındaki tag’lerde çalışır. docker/metadata-action sayesinde tag yönetimi otomatik hale gelir.
Paket Görünürlüğünü ve Erişim Kontrolünü Yönetmek
GitHub Packages’ta paket görünürlüğü varsayılan olarak repository’nin görünürlüğünü miras alır. Private repository’den yayımlanan paket otomatik olarak private olur.
Container image’ları için görünürlüğü sonradan değiştirmek mümkün. GitHub > Packages > Paket Adı > Package settings yolunu izleyin. Burada şunları yapabilirsiniz:
- Visibility: Public / Private arasında geçiş
- Manage Actions access: Hangi repository’lerin bu pakete erişebileceğini belirleme
- Manage access: Kullanıcı ve ekip bazlı erişim tanımlama
Organizasyon bazlı fine-grained erişim için şu akışı öneriyorum:
- Paket sahipliğini bireysel kullanıcı yerine organizasyon altında tutun
- Organizasyon içinde ekipler oluşturun (backend-team, frontend-team gibi)
- Paket erişimini bu ekiplere tanımlayın, bireysel kullanıcılara değil
Bu sayede biri ayrıldığında tek tek paket erişimlerini güncellemenize gerek kalmaz.
Gerçek Dünya Senaryosu: Monorepo’da Çoklu Paket Yönetimi
Birden fazla paketi tek bir repository’den yönetiyorsanız (monorepo) işler biraz karmaşıklaşabilir. Pratik bir örnek verelim.
Diyelim ki şu yapıda bir monorepo’nuz var:
packages/
api-client/
package.json (@sirket/api-client)
ui-components/
package.json (@sirket/ui-components)
shared-utils/
package.json (@sirket/shared-utils)
Her paketin kendi package.json dosyasında publishConfig tanımlı olacak. Workflow’da ise matrix strategy kullanmak mantıklı:
name: Monorepo Paket Yayimla
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
package: [api-client, ui-components, shared-utils]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://npm.pkg.github.com'
- name: Paketi yayimla
working-directory: packages/${{ matrix.package }}
run: |
npm ci
npm test
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Bu yaklaşım her paketi paralel olarak test edip yayımlar. Bir paket başarısız olursa diğerleri etkilenmez.
Sık Karşılaşılan Hatalar ve Çözümleri
“Package not found” hatası private repo erişiminde
Private bir repository’nin paketine erişmeye çalışırken bu hatayla karşılaşıyorsanız, token’ınızda repo scope’u eksik olabilir. Ayrıca tüketen repository’nin Packages sayfasından “Manage Actions access” bölümüne gidip erişim izni verdiğinizden emin olun.
npm publish sırasında “unauthorized” hatası
Scope ile organizasyon adının birebir eşleşmesini kontrol edin. @Sirket ile @sirket farklıdır, büyük-küçük harf duyarlıdır.
Docker push’ta “denied: installation not allowed to Write organization package”
Workflow’da permissions: packages: write bloğunun eksik olduğu durumlarda bu hata gelir. Repository Settings > Actions > General > Workflow permissions bölümünden “Read and write permissions” seçeneğini de açmanız gerekebilir.
Eski package versiyonlarının birikmesi
GitHub Packages, paket versiyonlarını otomatik olarak silmez. Depolama limitine yaklaşıyorsanız eski versiyonları temizlemek için GitHub API’sini kullanabilirsiniz:
# Belirli bir paketteki eski versiyonları listele
gh api
-H "Accept: application/vnd.github+json"
/orgs/ORGANIZASYON/packages/npm/PAKET_ADI/versions
| jq '.[].id'
# Belirli bir versiyon ID'sini sil
gh api
--method DELETE
-H "Accept: application/vnd.github+json"
/orgs/ORGANIZASYON/packages/npm/PAKET_ADI/versions/VERSION_ID
Bunu otomatikleştirmek için bir cleanup workflow yazabilirsiniz, son 5 versiyonu koruyup gerisini silebilirsiniz mesela.
Dependabot ile Paket Güncellemelerini Otomatikleştirme
GitHub Packages’ta barındırdığınız paketlerin tüketici projelerinde güncel tutulması için Dependabot konfigürasyonuna registry tanımı ekleyebilirsiniz:
# .github/dependabot.yml
version: 2
registries:
github-packages:
type: npm-registry
url: https://npm.pkg.github.com
token: ${{ secrets.DEPENDABOT_TOKEN }}
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
registries:
- github-packages
Burada DEPENDABOT_TOKEN adında bir repository secret oluşturmanız gerekiyor. GITHUB_TOKEN Dependabot context’inde beklendiği gibi çalışmıyor, ayrı bir PAT almanız şart.
Sonuç
GitHub Packages, özellikle zaten GitHub ekosisteminde olan ekipler için düşük kurulum maliyetiyle ciddi bir değer sunuyor. npm, Maven ve Docker ihtiyaçlarınızı tek bir platformda karşılıyor, erişim kontrolünü repository izin sistemiyle entegre ediyor ve GitHub Actions ile neredeyse sıfır konfigürasyonla pipeline kurmanıza olanak tanıyor.
Büyük ölçekli ya da çok karmaşık erişim gereksinimleri olan ortamlar için Nexus veya Artifactory hala daha iyi alternatifler olabilir. Ama on kişilik bir ekip, birkaç iç paket ve halihazırda GitHub kullanımı varsa, GitHub Packages makul bir seçim. Ayrı bir servis kurup yönetmek yerine bu kadar zamanı başka yere harcamak daha verimli.
Deneyimlerime göre en çok değer katan kısım Docker tarafı oluyor: ghcr.io üzerinde private image barındırmak, Kubernetes secretları ile entegre etmek ve GitHub Actions’tan sıfır ek konfigürasyonla push işlemi yapmak gerçekten kullanışlı. npm ve Maven entegrasyonları ise biraz daha fazla ilk kurulum gerektiriyor ama bir kez oturtunca bakım neredeyse yok.
