GitHub Actions'ta Debug, Hız ve Maliyet: Debug Logging, act, Cache Stratejisi ve Artifact Retention
Debug için ACTIONS_STEP_DEBUG/ACTIONS_RUNNER_DEBUG'ı tek bir re-run'da aç, döngüyü act ile kısalt, cache key'ini lock dosyasının hash'iyle kur ve restore-keys ver, artifact'ları kısa sakla. act'te üç koşuda gerçek cache miss, hit ve restore-keys çıktısı, act'in sınırlarının listesi, 10 GB kota ve 7 gün kuralı, retention, billing ekranları ve hız checklist'i.
Sürüm notu: Bu yazıdaki örnekler act 0.2.97, actionlint 1.7.12, Docker 29.2, .NET 10, actions/cache@v6 ve actions/setup-dotnet@v6 ile denenmiştir. GitHub'ın debug log arşivi, billing ekranları ve repository cache limitleri GitHub'da koşulmamıştır; değerler Kasım 2026 itibarıyla GitHub dokümantasyonu ve changelog'undan doğrulanmıştır.
Önce Tahmin Et
Bookstore'un CI (Continuous Integration) job'ında NuGet paketleri actions/cache ile cache'leniyor; key, packages.lock.json dosyalarının hash'inden üretiliyor. Bir feature branch'te bir paketi güncelliyorsun, lock dosyası değişiyor. O branch'in ilk koşusu cache'ten hiçbir şey alamaz mı, yoksa bir şeyler alır mı?
İkinci soru: upload-artifact'a retention-days: 120 yazdın, repository'nin retention ayarı ise varsayılan 90 gün. Artifact kaç gün yaşar?
Debug Logging: Log'un Göstermediğini Görmek
Bir step düştüğünde ilk bakılan yer log, ama varsayılan log action'ların içini göstermez. İki ayrı anahtar var. ACTIONS_STEP_DEBUG=true, action'ların core.debug() ile yazdığı ve normalde gizlenen ::debug:: satırlarını açar. ACTIONS_RUNNER_DEBUG=true, runner'ın kendi tanılama log'larını üretir; bunlar run'ın log arşivinde runner-diagnostic-logs klasörüne düşer. İkisi de repository'de secret veya variable olarak tanımlanabilir; ikisi birden varsa secret kazanır. Kalıcı açmak yerine tek bir run için açmak daha iyi: başarısız run'da "Re-run jobs" penceresindeki "Enable debug logging" kutusu veya gh run rerun RUN_ID --debug.
Debug açıkken runner'da RUNNER_DEBUG=1 tanımlıdır ve runner.debug context'i '1' olur. Bunu kendi teşhis step'lerini yalnızca debug koşularında çalıştırmak için kullanabilirsin:
name: api-ci-cache
on:
push:
branches: [main]
permissions:
contents: read
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v6
with:
global-json-file: global.json
- id: nuget-cache
uses: actions/cache@v6
with:
path: ${{ env.NUGET_PACKAGES }}
key: nuget-${{ runner.os }}-${{ hashFiles('api/**/packages.lock.json') }}
restore-keys: |
nuget-${{ runner.os }}-
- name: Restore
run: dotnet restore api/Bookstore.slnx --locked-mode
- name: Build and test
run: |
dotnet build api/Bookstore.slnx --no-restore -c Release
dotnet test --project api/tests/Bookstore.Api.Tests --no-build -c Release
- name: Debug details
if: runner.debug == '1'
run: |
echo "::debug::cache-hit=${{ steps.nuget-cache.outputs.cache-hit }}"
du -sh "$NUGET_PACKAGES"
act'te aynı etkiyi secret vererek alırsın. İkinci koşuyu --secret ACTIONS_STEP_DEBUG=true --secret ACTIONS_RUNNER_DEBUG=true ile yaptım:
[api-ci-cache/build-test] | Cache restored successfully
[api-ci-cache/build-test] | Cache restored from key: nuget-linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946
[api-ci-cache/build-test] ✅ Success - Main actions/cache@v6 [1.824401208s]
[api-ci-cache/build-test] ⚙ ::set-output:: cache-hit=***
[api-ci-cache/build-test] ✅ Success - Main Restore [2.071911375s]
[api-ci-cache/build-test] ✅ Success - Main Build and test [5.137234042s]
Bu çıktıda iki şey var. Birincisi, olmayan şey: act debug modunu taklit etmiyor. Hiçbir ::debug:: satırı görünmedi ve runner.debug '1' olmadığı için "Debug details" step'i hiç koşmadı. act'in kendi ayrıntılı log'u için --verbose bayrağı var, ama o act'in iç işleyişini gösterir, action'ların debug çıktısını değil. Debug logging'i gerçekten denemek GitHub'da yapılacak bir iş. İkincisi, beklenmedik bir yan etki: cache-hit=***. Secret'ın değeri true olduğu için act log'daki her true'yu maskeledi. GitHub da secret değerlerini log'da maskeler; debug anahtarını secret yerine repository variable'ı olarak tanımlamak bu tür sürprizlerden kaçınmanın daha temiz yolu.
act ile Yerelde Döngüyü Kısaltmak, ve Sınırları
Bu serideki her workflow'u önce act ile koştum; kazancı somut. "Commit at, push'la, Actions sekmesini aç, bekle" döngüsü yerine YAML (YAML Ain't Markup Language) hatası, yanlış expression, eksik output, job bağımlılığı ve shell script hatası saniyeler içinde görünüyor. act job container'ını Docker'da başlatıyor, action'ları GitHub'dan klonluyor, artifact ve cache için kendi yerel server'larını açıyor. Kullandığım komutun özü şu:
act push -W .github/workflows/api-ci-cache.yml \
-P ubuntu-latest=catthehacker/ubuntu:act-24.04 \
--container-architecture linux/arm64 \
--artifact-server-path ./artifacts \
--secret DEPLOY_TOKEN=local-dummy --var APP_URL=http://localhost:18320
Ama sınırlarını bu seride tek tek gördük ve listesi kısa değil:
- Environment protection rule'ları, approval'lar ve environment'a özel secret'lar yok; bütün secret ve variable'lar tek havuzdan geliyor (09).
- Yeni syntax'ları şeması tanımıyor:
deployment: false,concurrency.queue,$/referansları (09, 12). - OIDC (OpenID Connect) token endpoint'i yok; cloud login ve attestation koşamıyor (10, 11).
- Runner image'ı GitHub'ınki değil:
azve önceden kurulu .NET SDK (Software Development Kit) yok,ImageOSyanıltıcı (10, 14). - Yerleşik artifact server'ı upload-artifact@v7 ve download-artifact@v8'i reddediyor; yerel kopyada v4'e çevirmek gerekti (09).
- GitHub API'sine (Application Programming Interface) giden action'lar token olmadan düşüyor; metadata-action bunun örneğiydi (11).
- Bazı hataları GitHub'dan daha hoşgörülü karşılıyor:
shell:'siz composite step'i çalıştırdı (12).
Doğru zihinsel model: act workflow mantığının unit test'i, GitHub'daki koşu integration test'i. İkincisinin yerini tutmaz, ama ona giden hataların çoğunu daha erken yakalar.
Cache Stratejisi: Key, restore-keys ve Scope
Yukarıdaki workflow'u act ile üç kez koştum; cache step'inin ve restore'un çıktıları:
# 1. koşu: cache yok
| Cache not found for input keys: nuget-Linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946, nuget-Linux-
✅ Success - Main Restore [5.005426042s]
| Cache Size: ~37 MB (39273317 B)
| Cache saved with key: nuget-Linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946
# 2. koşu: aynı commit, tam eşleşme
| Cache restored from key: nuget-linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946
✅ Success - Main Restore [2.071911375s]
| Cache hit occurred on the primary key nuget-Linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946, not saving cache.
# 3. koşu: test projesine bir paket eklendi, lock dosyası değişti
| Cache restored from key: nuget-linux-b1f7a458d95b3dc4b465d4b588d09bf18e110a9fa59658004d8d3305f394d946
✅ Success - Main Restore [3.028522625s]
| Cache saved with key: nuget-Linux-19ab9ab300b96d8456426a5bebf558e1c63e82b0e2c414693623f6e5c9e0ac2a
İlk koşuda key de, nuget-Linux- prefix'i de bulunamadı; restore paketleri NuGet'ten indirdi ve job sonunda 37 MB'lık bir cache kaydedildi. İkinci koşu tam eşleşme buldu, restore 5 saniyeden 2 saniyeye indi ve aynı key tekrar kaydedilmedi. Üçüncü koşuda test projesine bir paket ekledim; lock dosyasının hash'i ve dolayısıyla key değişti. Tam eşleşme yok, ama restore-keys'teki prefix eski cache'i getirdi, restore yalnızca yeni paketi indirdi ve job sonunda yeni key ile ikinci bir entry yazıldı. Bookstore küçük bir çözüm olduğu için kazanç saniyeler mertebesinde; onlarca projeli bir çözümde veya yavaş bir paket kaynağında aynı oran dakikalara karşılık gelir. act'in cache server'ı restore mesajında key'i küçük harfle raporluyor; davranışa etkisi yok.
Key tasarımı birkaç kurala dayanıyor. Key, cache'lenen içeriği belirleyen dosyaların hash'ini içermeli: NuGet için packages.lock.json, npm için package-lock.json. Lock dosyası yoksa *.csproj'un hash'i bir yaklaşıklık ama paket sürümleri floating ise yanlış içerik dönebilir; seride lock dosyası kullanmamızın bir sebebi de bu. Key'e işletim sistemini (ve gerekiyorsa mimariyi) koy; Linux'ta oluşturulmuş bir cache Windows'ta işe yaramaz. restore-keys, tam eşleşme olmadığında prefix ile en yakın cache'i getirir; tahminin ilk sorusunun cevabı bu: lock dosyası değiştiğinde key değişir ama nuget-Linux- prefix'iyle önceki cache geri yüklenir, restore yalnızca eksik paketleri indirir.
Bir cache entry'si değişmezdir; aynı key ile ikinci kez kaydedilmez. Tam eşleşme olduğunda actions/cache post step'te kaydetmeyi atlar. Bu yüzden key'e github.run_id gibi her koşuda değişen bir değer koymak, her koşuda yeni bir entry yazmak ve kotayı hızla doldurmak demek.
Scope kuralı da önemli: bir koşu, kendi branch'inin ve default branch'in cache'lerini kullanabilir; pull request koşuları buna ek olarak base branch'inkini. Kardeş branch'ler birbirinin cache'ini göremez. Pratik sonucu: default branch'te düzenli koşan bir job cache'i "ısıtır", feature branch'ler oradan beslenir. Feature branch'te ilk kez oluşan bir cache, yalnızca o branch'in sonraki koşularına yarar.
Kapasite tarafında, repository başına varsayılan sınır 10 GB; yedi günden uzun süre erişilmeyen entry'ler silinir, sınıra ulaşıldığında en uzun süredir erişilmeyenler gider. 20 Kasım 2025'ten beri Pro, Team ve Enterprise hesaplarında bu sınır kullandıkça öde modeliyle aşılabiliyor; "cache size eviction limit" ve "cache retention limit" policy'leri repository, organization ve enterprise seviyesinde ayarlanıyor, bütçe biterse cache salt okunur hâle geliyor. 11. yazıdaki Docker layer cache'i (type=gha,mode=max) de aynı kotayı paylaşıyor; büyük image'larda NuGet cache'ini kotadan dışarı itebilir.
Artifact Retention ve Boyut
Artifact'lar varsayılan olarak 90 gün saklanıyor. Repository veya organization ayarında bu süre public repository'lerde 1–90, private repository'lerde 1–400 gün arasında seçilebiliyor; 1 Ekim 2026'dan beri aynı ayar check, workflow run ve status kayıtlarını da kapsıyor. Step seviyesindeki retention-days bu süreyi yalnızca kısaltabilir. Tahminin ikinci sorusunun cevabı: 90 gün.
- uses: actions/upload-artifact@v7
with:
name: test-results
path: TestResults
retention-days: 7
Test sonuçları, Playwright trace'leri ve geçici build çıktıları için 7 gün çoğu zaman yeter; release paketleri ise artifact'ta değil GitHub Release'te veya bir registry'de yaşamalı. upload-artifact v4'ten beri artifact'lar değişmez: aynı isimle ikinci kez yükleyemezsin, gerekirse overwrite: true ile eskisini silip yenisini yazarsın. Şubat 2026'da gelen v7, archive: false ile tek bir dosyayı zip'lemeden yükleyebiliyor. Matrix job'larında artifact adına matrix değerini eklemeyi unutma, yoksa ikinci job hata alır.
Billing ve Kullanımı Okumak
Dakika ve depolama kullanımı Billing & Licensing altındaki "Usage" ve "Metered usage" sayfalarında, ürün, repository ve makine tipine göre gruplanabiliyor; kişisel hesaplarda aynı ekran github.com/settings/billing. Organization seviyesinde Actions sekmesindeki "Actions Usage Metrics" (hangi workflow ve job kaç dakika tüketiyor) ve Mart 2025'te GA (General Availability) olan "Actions Performance Metrics" (kuyrukta bekleme, çalışma süresi, başarısızlık oranı) var. Bu ekranları okurken üç soru soruyorum: en çok dakikayı hangi workflow tüketiyor, macOS veya larger runner dakikaları nereden geliyor, ve hangi job sık sık iptal ediliyor ya da timeout'a düşüyor.
Hız Checklist'i
Bookstore'un pipeline'larında işe yarayan sıra şu:
Paket ve layer cache'i. NuGet ve npm cache'i, Docker build'lerinde type=gha cache'i. Ölçmeden ekleme, ekledikten sonra ölç: cache'in restore süresi, kazandırdığı süreden uzunsa zarar ediyorsun.
Checkout derinliği. actions/checkout varsayılan olarak yalnızca son commit'i çeker (fetch-depth: 1). fetch-depth: 0 yalnızca tag, git describe veya tam geçmiş gerektiren adımlar için; büyük bir repository'de dakikalar ekler.
Paralel job'lar ve doğru bağımlılık grafiği. 04. yazıdaki gibi lint, unit test ve web build birbirini beklemesin; yalnızca gerçekten ihtiyaç duyan job needs ile beklesin.
Path filtreleri. 07. yazıdaki monorepo düzeninde yalnızca web/ değiştiğinde .NET job'larının çalışmaması, en ucuz dakika kazancı.
Eski koşuları iptal et. concurrency ve cancel-in-progress: true, aynı PR'a (pull request) art arda push'larda eski CI koşularını öldürür. Deploy job'larında bunu kapalı tuttuğumuzu 09'da konuşmuştuk.
Timeout koy. Varsayılan job timeout'u 360 dakika; takılan bir test altı saat dakika yakar. Bookstore'un CI job'larında timeout-minutes: 15 var.
Matrix'i kontrol altında tut. Her eksen çarpan; üç işletim sistemi, üç SDK ve iki mimari 18 job demek. Tam matrix'i gece koşusuna, PR'larda tek satırlık hızlı bir kontrol.
Üretimde Dikkat
Debug logging'i kalıcı açık bırakma. Debug çıktısı action'ların iç ayrıntılarını, bazen ortam bilgilerini ve maskelenmeyen ara değerleri log'a taşır; public repository'de herkes okur.
Cache'e güvenliği emanet etme. 13. yazıdaki cache poisoning konusu burada da geçerli: release build'inde cache'ten gelen paket klasörü --locked-mode ile doğrulanmalı.
Kotayı izle. Repository'nin Actions cache sayfası entry'leri, boyutlarını ve son erişim zamanlarını listeler; hiç hit almayan büyük entry'ler kötü bir key tasarımının işaretidir.
Cache rate limit'lerini bil. Repository başına dakikada 200 upload ve 1.500 download sınırı var; çok geniş bir matrix'in her job'ı aynı anda cache yazmaya çalışırsa bazı kayıtlar düşer.
Sık Düşülen Tuzaklar
Key'e run_id veya tarih koymak. Her koşu yeni entry, sıfır hit, dolan kota.
restore-keys vermemek. Lock dosyası her değiştiğinde cache sıfırdan dolar.
Platformu key'e koymamak. Windows ve Linux job'ları aynı cache'i birbirine yükler.
retention-days ile süreyi uzatmaya çalışmak. Repository ayarından uzun olamaz.
act'te geçen bir workflow'u bitti saymak. Environment, OIDC, image ve yeni syntax davranışlarını act doğrulamaz.
Debug'ı açıp kapatmayı unutmak. ACTIONS_STEP_DEBUG bir secret olarak kalırsa her koşu ayrıntılı log üretir.
Küçük Bir Deneme
★☆☆ api-ci-cache workflow'unu act ile üç kez koştur ve her koşudaki Restore süresini not al. Sonra key'den hashFiles'ı çıkarıp sabit bir key ile tekrar dene; paket güncellendiğinde ne oluyor?
★★☆ Workflow'a bir job summary step'i ekle: cache hit durumu, restore süresi ve NUGET_PACKAGES boyutu $GITHUB_STEP_SUMMARY'ye bir tablo olarak yazılsın.
★★★ Kendi test repository'nde aynı workflow'u bir feature branch'te ve main'de koştur; repository'nin cache sayfasında hangi entry'nin hangi ref'e ait olduğuna ve feature branch'in main'in cache'ini nasıl kullandığına bak.
Serinin son yazısında ileriye değil geriye bakıyoruz: internette karşına çıkan 2019–2023 tarihli workflow'ları okumak, set-output'tan GITHUB_OUTPUT'a ve Node 12'den Node 24'e uzanan değişiklikleri tanımak ve eski bir .NET 5 workflow'unu adım adım .NET 10'a taşımak.