İçeriğe geç
Can Uğurlu
Geri dön

GitHub Actions'ta cache ile build süresi

CI süresinin çoğu değişmeyen bağımlılığı yeniden kurmakla geçiyor. Cache anahtarını lockfile’ın hash’ine bağlarsanız değişmeyen kısım tekrar kurulmuyor, build dakikalar yerine saniyelere iniyor.

pnpm cache’i ve kurulum sırası

actions/setup-nodein cache: pnpm seçeneği pnpm store’unu cache’liyor, ama bunun çalışması için pnpm’in setup-nodedan önce kurulu olması gerekiyor:

- uses: pnpm/action-setup@v4
  with:
    version: 9

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm

- run: pnpm install --frozen-lockfile

Sırayı tersine çevirirseniz setup-node pnpm komutunu bulamaz, store yolunu tespit edemez. Hata vermez, cache adımı sessizce atlanır ve her build sıfırdan kurulum yapar.

actions/cache ve doğru anahtar

pnpm dışındaki her şey için actions/cache kullanılır. Anahtarın kalbi lockfile’ın hash’i:

- uses: actions/cache@v4
  with:
    path: ~/.cache/some-tool
    key: ${{ runner.os }}-some-tool-${{ hashFiles('**/pnpm-lock.yaml') }}

Sabit bir string yazarsanız (“some-tool-cache” gibi) cache hiç güncellenmiyor, bağımlılıklar değişse bile eski cache geri yükleniyor. Anahtar lockfile değiştiğinde değişmeli, o yüzden hashFiles şart.

restore-keys ile kısmi eşleşme

Tam eşleşme yoksa restore-keys bir önceki cache’i kısmi anahtarla buluyor:

- uses: actions/cache@v4
  with:
    path: ~/.cache/some-tool
    key: ${{ runner.os }}-some-tool-${{ hashFiles('**/pnpm-lock.yaml') }}
    restore-keys: |
      ${{ runner.os }}-some-tool-

Lockfile’da tek bağımlılık değişse bile tam anahtar tutmuyor. restore-keys en son uyan cache’i indiriyor, kurulum sıfırdan değil farktan başlıyor. restore-keys altındaki her satır tam anahtarın bir öneki olmalı, rastgele bir string eşleşmiyor.

Kısmi eşleşmede paket yöneticisi eksik kalan farkı kendisi tamamlıyor. pnpm install eski store’u bulur, lockfile’daki yeni paketleri üstüne ekler. Bu, sıfırdan indirmekten çok daha hızlı, tam eşleşme kadar hızlı değil.

cache-hit ile install adımını atlayın

Tam eşleşme varsa kurulum komutunu hiç çalıştırmamak mümkün:

- id: cache
  uses: actions/cache@v4
  with:
    path: ~/.cache/some-tool
    key: ${{ runner.os }}-some-tool-${{ hashFiles('**/pnpm-lock.yaml') }}

- if: steps.cache.outputs.cache-hit != 'true'
  run: pnpm install --frozen-lockfile

cache-hit çıktısı sadece tam anahtar eşleştiğinde true dönüyor, restore-keys üzerinden gelen kısmi eşleşmede false kalıyor. pnpm’in kendi cache mantığı zaten hızlı olduğu için bu adım genelde node_modules dışı, daha pahalı kurulumlarda (Composer vendor, derlenen native bağımlılık) fark yaratıyor.

Matrix build’te birden fazla Node sürümü ya da işletim sistemi test ediyorsanız her kombinasyon kendi cache’ini almalı. runner.os anahtarda zaten var, matrix’te Node sürümü de değişiyorsa onu da anahtara ekleyin:

key: ${{ runner.os }}-node${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}

Aksi hâlde Node 20 ile kurulan cache Node 22’nin işine yaramaz, restore edilir ama sonra baştan kurulur, sadece zaman kaybedilir.

Docker katmanlarını da cache’leyin

Image build’i de katman katman cache’lenebilir. buildx ile GitHub Actions cache backend’i:

- uses: docker/setup-buildx-action@v3

- uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    cache-from: type=gha
    cache-to: type=gha,mode=max

mode=max ara katmanları da cache’e yazıyor, sadece son katmanı değil. Bunsuz COPY gibi bir adım cache’i bozduğunda öncesindeki katmanlar da tekrar kurulur. İmajın kendisi neden büyüdüğü ayrı bir konu; Docker imajının 1,2 GB’a çıkma sebebi genelde tek aşamalı Dockerfile’dır.

Değişmeyen anahtar cache değil, bayat dizindir

Anahtarı hiç değişmeyen bir şeye bağlarsanız (sabit string, dal adı, tarih yok) o artık cache değil, bayatlamış bir dizindir. Her build aynı eski bağımlılıkları geri yükler, yeni bir paket eklediğinizde bile fark etmez. Anahtar bağımlılık listesinin kendisinden türetilmeli, başka hiçbir şeyden değil.

Özet

pnpm’i setup-nodedan önce kurun. Cache anahtarını lockfile hash’inden türetin, sabit string kullanmayın. restore-keys ekleyin. Docker build’i cache-from/cache-to: type=gha ile katman katman cache’leyin.


Bu yazıyı paylaş:

Önceki Yazı
MySQL'de utf8 değil utf8mb4 kullanın
Sonraki Yazı
Lock dosyasını commit'leyin