Mert Özen Her Satırda Daha İleri
DevOps GitHub Actions — #16

Eski GitHub Actions Workflow'larını Okumak: set-output'tan GITHUB_OUTPUT'a, Node 12/16/20'den Node 24'e

Mert Özen 8 Kas 2026 11 dk 4 görüntülenme
Eski GitHub Actions Workflow'larını Okumak: set-output'tan GITHUB_OUTPUT'a, Node 12/16/20'den Node 24'e

Eski bir workflow'u güncellerken runner label'ını, action major'larını, SDK kaynağını, workflow komutlarını ve cache key'ini birlikte değiştir: ubuntu-18.04, @v2 action'lar ve ::set-output 2026'da ya çalışmıyor ya uyarı veriyor. 2018–2026 zaman çizelgesi, Node 12→24 geçişleri, 2021 tarzı .NET 5 workflow'unun actionlint ve act çıktısı, .NET 10'a taşıma checklist'i ve 2026 özeti.

Sürüm notu: Bu yazıdaki örnekler act 0.2.97, actionlint 1.7.12, Docker 29.2, .NET 5.0.408 ve .NET 10 (SDK 10.0.403) ile denenmiştir. Tarihler Kasım 2026 itibarıyla GitHub changelog'u ve action'ların release notlarından doğrulanmıştır; eski workflow GitHub'da koşulmamıştır.

Önce Tahmin Et

Bir StackOverflow cevabından 2021 tarihli, .NET 5 için yazılmış bir workflow kopyalıyorsun: runs-on: ubuntu-18.04, actions/checkout@v2, actions/setup-dotnet@v1, actions/cache@v2, upload-artifact@v2 ve sürüm numarasını echo "::set-output name=value::..." ile üreten bir step. Kasım 2026'da GitHub'da push'ladığında ilk nerede kırılır?

İkinci soru: aynı dosyadaki ::set-output satırı, Ekim 2022'de deprecated ilan edildi. Bugün çalışıyor mu?

Neden Eski Workflow Okumayı Öğrenmeli?

İnternetteki GitHub Actions örneklerinin büyük kısmı 2019–2023 arasında yazıldı; blog yazıları, StackOverflow cevapları, şirket içi wiki'ler ve senin repository'ndeki ilk workflow dosyası da muhtemelen o dönemden. Actions bu sürede hızlı değişti: workflow komutları dosya tabanlı oldu, action runtime'ı dört kez Node sürümü değiştirdi, artifact ve cache action'larının eski sürümleri ile birkaç runner image'ı tamamen kapatıldı. Eski bir dosyayı okurken neyin hâlâ geçerli, neyin uyarı verdiği, neyin artık hiç çalışmadığını ayırt edebilmek, kopyala-yapıştır seviyesinden çıkmanın son adımı.

Zaman Çizelgesi: 2018'den 2026'ya

TarihDeğişiklik
Ekim 2018Actions beta olarak duyuruldu; workflow'lar HCL (HashiCorp Configuration Language) ile main.workflow dosyasında
Ağustos 2019YAML (YAML Ain't Markup Language) syntax'ı ve CI (Continuous Integration) ile CD (Continuous Delivery) desteği; 30 Eylül 2019'da HCL workflow'ları çalıştırılmaz oldu
13 Kasım 2019GA (General Availability)
16 Kasım 2020set-env ve add-path komutları güvenlik açığı nedeniyle kapatıldı
Eylül 2022 – Haziran 2023JavaScript action'ları Node 12'den Node 16'ya
11 Ekim 2022set-output ve save-state deprecated; kaldırılması Temmuz 2023'te süresiz ertelendi
Nisan 2023ubuntu-18.04 runner image'ı kaldırıldı
Eylül 2023 – 3 Haziran 2024Node 16'dan Node 20'ye
30 Ocak 2025upload/download-artifact v1–v3 kapatıldı
1 Mart 2025actions/cache v1–v2 ve eski cache servisi kapatıldı
15 Nisan 2025ubuntu-20.04 runner image'ı kaldırıldı; 30 Haziran 2025'te windows-2019
16 Haziran 2026Node 24 varsayılan oldu
23 Eylül 2026Node 20 runner'lardan kaldırıldı, geri dönüş seçeneği de kapandı

Tablodaki her satır, eski bir workflow'da somut bir satıra karşılık geliyor. Şimdi bunları tek tek okuyalım.

Workflow Komutları: ::set-output'tan Dosyalara

İlk dönemde step'ler runner'la stdout'a özel biçimli satırlar yazarak konuşuyordu: ::set-output, ::save-state, ::set-env, ::add-path. Sorun şuydu: log'a yazdırılabilen her şey bir komut olabiliyordu. Bir test'in çıktısına veya PR (pull request) başlığına gömülü bir ::set-env satırı, runner'ın environment'ını değiştirebiliyordu. Bu yüzden Kasım 2020'de set-env ve add-path kapatıldı, Ekim 2022'de set-output ve save-state deprecated oldu. Yerlerine runner'ın her step için verdiği dosyalar geldi:

# Eski (2022 öncesi)
echo "::set-output name=value::1.0.42"
echo "::set-env name=DOTNET_ROOT::/root/.dotnet"
echo "::add-path::/root/.dotnet/tools"
echo "::save-state name=started::true"

# Bugün
echo "value=1.0.42" >> "$GITHUB_OUTPUT"
echo "DOTNET_ROOT=/root/.dotnet" >> "$GITHUB_ENV"
echo "/root/.dotnet/tools" >> "$GITHUB_PATH"
echo "started=true" >> "$GITHUB_STATE"

Çok satırlı bir değer için delimiter'lı biçim kullanılır; 03. yazıda gördüğümüz gibi:

{
  echo "notes<<EOF"
  cat release-notes.md
  echo "EOF"
} >> "$GITHUB_OUTPUT"

Tahminin ikinci sorusunun cevabı: set-output Kasım 2026'da hâlâ çalışıyor. GitHub kaldırma tarihini Temmuz 2023'te süresiz erteledi ve o zamandan beri yeni bir tarih açıklamadı; komut uyarı annotation'ı üretmeye devam ediyor. "Çalışıyor" ile "kullanılmalı" arasındaki fark burada; actionlint onu açıkça işaretliyor. set-env ve add-path ise 2020'den beri kapalı; yalnızca ACTIONS_ALLOW_UNSECURE_COMMANDS ile, adının söylediği gibi güvensiz biçimde açılabiliyor.

Action Runtime'ları: Node 12'den Node 24'e

Bir JavaScript action'ı action.yml'ındaki runs.using ile hangi Node sürümünde çalışacağını söyler: node12, node16, node20, node24. Node sürümleri ömrünü doldurdukça GitHub bu alanı zorla ilerletti: Haziran 2023'te Node 12 action'ları Node 16'da, 3 Haziran 2024'te Node 16 action'ları Node 20'de çalıştırılmaya başlandı. Son geçiş 2025 Eylül'ünde duyuruldu; Node 24, 16 Haziran 2026'da varsayılan oldu ve 23 Eylül 2026'da Node 20 runner'lardan tamamen kaldırıldı. Geçiş döneminde FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true ile erken geçiş, ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true ile geçici olarak geri dönüş mümkündü; ikincisi artık yok.

Pratikte bunun anlamı: actions/checkout@v2 gibi bir satır, Node 12 için yazılmış bir kodu bugün Node 24 ile çalıştırmaya çalışır. Basit action'lar çoğu zaman çalışır, ama bunun garantisi yok ve artık hiçbiri güvenlik güncellemesi almıyor. Node 24 kullanan action'lar (checkout@v5 ve sonrası, setup-dotnet@v5 ve sonrası) self-hosted runner'da en az 2.327.1 sürümünü istiyor; Node 24 macOS 13.4 ve öncesinde çalışmıyor, 32 bit Arm işlemcilerde ise resmî olarak desteklenmiyor.

Seride kullandığımız action'ların major sürümleri bu tarihçenin izini taşıyor:

  • actions/checkout: v2 (Aralık 2019), v3 (Node 16, Mart 2022), v4 (Node 20, Ekim 2023), v5 (Node 24, Ağustos 2025), v6 (credential'lar .git/config yerine $RUNNER_TEMP altında, Kasım 2025), v7 (pull_request_target altında fork kodunu varsayılan olarak checkout etmiyor, Haziran 2026).
  • actions/setup-dotnet: v2 (Node 16, 2022), v3 (dotnet-quality input'u, Eylül 2022), v4 (Node 20, Aralık 2023), v5 (Node 24, Eylül 2025), v6 (ESM (ECMAScript Modules) yapısı, input'larda değişiklik yok, Temmuz 2026).

Eski Bir Workflow'u Okumak: 2021 Tarzı .NET 5

Tahmindeki workflow, o dönemin şablonlarına çok benzeyen haliyle:

name: .NET

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-18.04

    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - uses: actions/cache@v2
      with:
        path: ~/.nuget/packages
        key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
    - name: Restore dependencies
      run: dotnet restore api/Bookstore.slnx
    - name: Build
      run: dotnet build api/Bookstore.slnx --no-restore
    - name: Version
      id: version
      run: echo "::set-output name=value::1.0.${{ github.run_number }}"
    - name: Publish
      run: dotnet publish api/src/Bookstore.Api -c Release -o out /p:Version=${{ steps.version.outputs.value }}
    - uses: actions/upload-artifact@v2
      with:
        name: api
        path: out

actionlint'in yorumu altı satır:

.github/workflows/dotnet.yml:12:14: label "ubuntu-18.04" is unknown. available labels are ... [runner-label]
.github/workflows/dotnet.yml:15:13: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]
.github/workflows/dotnet.yml:17:13: the runner of "actions/setup-dotnet@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]
.github/workflows/dotnet.yml:20:13: the runner of "actions/cache@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]
.github/workflows/dotnet.yml:30:12: workflow command "set-output" was deprecated. use `echo "{name}={value}" >> $GITHUB_OUTPUT` instead: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions [deprecated-commands]
.github/workflows/dotnet.yml:33:13: the runner of "actions/upload-artifact@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]

Tahminin ilk sorusunun cevabı ilk satırda: GitHub'da bu workflow hiçbir step'e ulaşmaz, çünkü ubuntu-18.04 image'ı Nisan 2023'te kaldırıldı ve job'ı alacak bir runner yok. Label'ı düzeltsen bile actions/cache@v2 ve upload-artifact@v2, servisleri 2025'te kapatıldığı için düşer.

Daha ilginç olanı act'te ne olduğu, çünkü act bu emeklilikleri bilmiyor. Label'ı act image'ına eşleyip Bookstore repository'sinde koştum:

[.NET/build]   ✅  Success - Main actions/checkout@v2 [40.617166ms]
[.NET/build]   🚧  ::warning::.NET 5.0 is no longer supported and will not receive security updates in the future. Please refer to https://aka.ms/dotnet-core-support for more information about the .NET support policy.
[.NET/build]   ✅  Success - Main Setup .NET [50.567496375s]
[.NET/build]   | Cache not found for input keys: Linux-nuget-acfe4c79a7074611f247e6e9515208b813c4185ab3ea5fbb15a3171029bcce3a
[.NET/build]   ✅  Success - Main actions/cache@v2 [1.101288208s]
[.NET/build] ⭐ Run Main Restore dependencies
[.NET/build]   |         5.0.408 [/root/.dotnet/sdk]
[.NET/build]   | Could not execute because the application was not found or a compatible .NET SDK is not installed.
[.NET/build]   |   * You intended to execute a .NET SDK command:
[.NET/build]   |       A compatible installed .NET SDK for global.json version [10.0.400] from [.../global.json] was not found.
[.NET/build]   ❌  Failure - Main Restore dependencies [58.395042ms]

act'te eski action'ların hepsi çalıştı: setup-dotnet@v1 .NET 5.0.408'i kurdu (destek dışı olduğuna dair bir uyarıyla), cache@v2 act'in yerel cache server'ıyla konuştu. Kıran şey repository'nin kendisiydi: global.json SDK 10.0.400 istiyor, makinede yalnızca 5.0.408 var. Ders iki yönlü: act'te geçmek GitHub'da geçmek değil (orada cache ve artifact v2 servisleri yok), ve eski bir workflow'u yeni bir projeye kopyalarken araç zincirinin bütün halkaları birlikte güncellenmeli.

Adım Adım .NET 10'a Taşımak

Aynı işin bugünkü hâli:

name: api-ci

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build-test:
    runs-on: ubuntu-24.04
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: actions/setup-dotnet@v6
        with:
          global-json-file: global.json
      - name: Restore
        run: dotnet restore api/Bookstore.slnx --locked-mode
      - name: Build
        run: dotnet build api/Bookstore.slnx --no-restore -c Release
      - name: Test
        run: dotnet test --project api/tests/Bookstore.Api.Tests --no-build -c Release
      - name: Version
        id: version
        run: echo "value=1.0.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"
      - name: Publish
        env:
          VERSION: ${{ steps.version.outputs.value }}
        run: dotnet publish api/src/Bookstore.Api -c Release -o out -p:Version="$VERSION"
      - uses: actions/upload-artifact@v7
        with:
          name: api-publish
          path: out
          retention-days: 7

actionlint temiz geçti; act ile koşu (09'daki artifact sürümü değişikliğiyle):

[api-ci/build-test]   ✅  Success - Main actions/setup-dotnet@v6 [13.392097084s]
[api-ci/build-test]   | SDK Version: 10.0.403
[api-ci/build-test]   ✅  Success - Main Restore [5.122548333s]
[api-ci/build-test]   ✅  Success - Main Build [1.774625542s]
[api-ci/build-test]   ✅  Success - Main Test [1.088162333s]
[api-ci/build-test]   ✅  Success - Main Version [58.091334ms]
[api-ci/build-test]   ⚙  ::set-output:: value=1.0.1
[api-ci/build-test]   ✅  Success - Main Publish [1.00435875s]
[api-ci/build-test] 🏁  Job succeeded

Bir kafa karışıklığına dikkat: act, $GITHUB_OUTPUT'a yazılan değeri kendi log'unda ::set-output:: biçiminde gösteriyor. Bu act'in gösterim biçimi; workflow deprecated komutu kullanmıyor. Taşıma sırasında yaptığım değişiklikleri bir checklist'e çevirirsek:

  • Runner label'ı: emekliye ayrılmış image'dan pin'lenmiş güncel bir image'a (ubuntu-24.04), 14. yazıdaki geçiş takvimini bilerek.
  • Action sürümleri: her uses: satırı güncel major'a, production repository'sinde tam commit SHA'sı (Secure Hash Algorithm) ile (13. yazı).
  • SDK: dotnet-version: 5.0.x yerine global-json-file; SDK sürümünün tek kaynağı repository'deki global.json.
  • Restore: --locked-mode ve lock dosyası; cache key'i *.csproj yerine lock dosyasının hash'inden (15. yazı).
  • Workflow komutları: ::set-output yerine $GITHUB_OUTPUT; expression'ı run: içine gömmek yerine env: (13. yazı).
  • Varsayılanları açık yaz: permissions: contents: read, concurrency, timeout-minutes, persist-credentials: false, artifact retention-days.
  • Branch adı: master yerine repository'nin gerçek default branch'i; tetiklenmeyen bir workflow hata vermez, sessizce hiç çalışmaz.

2026 Özeti: Bu Yıl Ne Değişti?

Seri boyunca 2026'da gelen değişikliklere yeri geldikçe değindik. Bir arada görmek, bir sonraki "eski workflow" kuşağını bugünden tanımak demek:

Ocak'ta hosted runner fiyatları düştü (14). Mart'ta environment'lar için deployment: false ve cron için timezone geldi (09). Nisan'da service container'lara entrypoint ve command, OIDC (OpenID Connect) token'larına repository custom property claim'leri eklendi; aynı ay duyurulan ID'li immutable subject formatı 15 Temmuz'dan sonra oluşturulan repository'lerde varsayılan oldu (10). Mayıs'ta concurrency gruplarına queue: max geldi (09). Haziran'da actions/checkout@v7 fork kodunu ayrıcalıklı event'lerde checkout etmeyi bıraktı, güvenilmeyen event'ler salt okunur cache token'ı almaya başladı ve Node 24 varsayılan oldu (13, 15). Temmuz'da aynı repository'deki action'lar için $/ syntax'ı geldi (12). Eylül'de Ubuntu 26.04 runner'ları GA oldu ve ubuntu-latest geçişi başladı, workflow execution protection'ları GA oldu, Node 20 kaldırıldı (13, 14). Ekim'de workflow lockfile'ları önce preview, ay sonunda GA oldu ve runner'lar AWS (Amazon Web Services) üzerinde de çalışmaya başladı (13, 14). Önümüzdeki ay, 2 Aralık 2026'da, public repository'lerde pull_request_target varsayılan olarak engellenecek (13).

Ortak yön açık: daha az örtük güven. Tag yerine kilitlenmiş commit, isim yerine ID, varsayılan yazma yetkisi yerine açıkça istenen permission, her event'e açık kapı yerine policy.

Üretimde Dikkat

Workflow dosyalarını da bağımlılık gibi yönet. Dependabot'un github-actions ecosystem'i, lockfile ve actionlint üçlüsü, eski bir action'ın sessizce yıllarca kalmasını engeller.

Changelog'u takip et. Bu yazıdaki her kırılma, aylar öncesinden GitHub changelog'unda ve çoğu zaman workflow log'unda uyarı annotation'ı olarak duyuruldu. Annotation'ları okumayan ekip, kırılmayı ilk kez kırıldığı gün görür.

Brownout'ları ciddiye al. GitHub büyük kapatmalardan önce kısa süreli, planlı kesintiler yapıyor (cache servisi için Şubat 2025'te, pull_request_target için Kasım 2026'da). Brownout saatinde kırmızıya dönen bir workflow, kapanış günü kırılacak workflow'dur.

Eski workflow'u silmekten korkma. Kimsenin bakmadığı, master'a bağlı, hiç tetiklenmeyen dosyalar repository'de yalnızca kafa karışıklığı ve saldırı yüzeyi bırakır.

Sık Düşülen Tuzaklar

Eski bir cevabı sürüm numaralarını güncellemeden kopyalamak. @v2'ler, ubuntu-18.04 ve dotnet-version: 5.0.x birlikte gelir.

Uyarı veren bir komutu "çalışıyor" diye bırakmak. set-output bugün çalışıyor; kaldırma tarihi açıklandığında elinde birikmiş bir iş listesi olur.

act'te geçen bir eski workflow'u GitHub'da da geçecek sanmak. act, kapatılmış servisleri ve kaldırılmış image'ları bilmiyor.

Yalnızca major tag'i güncelleyip davranış değişikliğini okumamak. checkout v6'da credential'ın yeri, v7'de fork PR davranışı değişti; sürüm atlarken release notlarını oku.

hashFiles('**/*.csproj') ile cache key'i. Floating paket sürümlerinde aynı key farklı içeriğe karşılık gelir.

master/main uyuşmazlığı. Branch filtresi yanlış olan bir workflow hiç koşmaz ve kimse fark etmez.

Küçük Bir Deneme

★☆☆ Kendi repository'lerinden birindeki en eski workflow dosyasını actionlint'ten geçir ve bulguları bu yazıdaki zaman çizelgesindeki satırlarla eşleştir.

★★☆ Eski .NET workflow'unu, global.json'u olmayan bir .NET 5 örnek projesinde act ile çalıştır ve ardından bu yazıdaki checklist'i uygulayarak adım adım .NET 10'a taşı; her adımdan sonra act ve actionlint çıktısını karşılaştır.

★★★ Repository'ndeki bütün workflow'lar için tek bir "workflow sağlık" job'ı yaz: actionlint, zizmor ve gh actions-lock --verify çalıştırsın, sonuçları job summary'de tablo olarak versin ve haftalık schedule ile koşsun.

GitHub Actions serisi burada bitiyor. Workflow, trigger, expression ve job'larla başladık; .NET ve Angular için gerçek CI pipeline'ları kurduk; environment, OIDC, SSH (Secure Shell) ve GHCR (GitHub Container Registry) ile deploy ettik; tekrarı azalttık, tedarik zincirini kilitledik, runner'ları ve maliyeti tanıdık. Buradan devam etmek istersen, bu seride bilerek kısa tuttuğumuz konular ilerideki serilerin konusu: blue-green ve canary mantığı ilerideki Deployment Stratejileri serisinde, container'ları cluster'a taşımak Kubernetes serisinde, aynı pipeline düşüncesinin başka bir araçtaki karşılığı ise Jenkins serisinde.