Artifact Repository ve Release Süreci: Feed'ler, Container Image Tag'leri, Hotfix Akışı ve Destek Politikası
Container registry'lerinde tag varsayılan olarak hareketli bir işaret; bu yüzden image'ı sürüm ve commit SHA'sıyla etiketle, OCI label'larıyla kaynağını işaretle ve digest ile deploy et. Bookstore API'sinde aynı tag'in iki farklı image'ı göstermesini, SDK'nın OCI label'larını ve digest çıktısını, cherry-pick'li 1.x hotfix'ini ve release branch'inde yayın workflow'unun olmaması tuzağını gösteriyor; artifact repository seçeneklerini, release cadence'ı ve destek politikası şablonunu ele alıyoruz.
Sürüm notu: Bu yazıdaki örnekler .NET 10 (SDK 10.0.402, SDK'nın container desteğiyle), Docker 29, registry:3 (yerel container registry), Git 2.54, commit-and-tag-version 13.3 ve actionlint 1.7.12 ile denenmiştir. Deploy workflow'u actionlint ile doğrulandı, image adımının komutları yerel registry'ye karşı koşuldu; GitHub'da koşulmadı.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) API'sini, .NET SDK'nın container desteğiyle doğrudan bir registry'ye gönderiyoruz. 2026.11.3 sürümü için iki tag üretildi ve registry, 2026.11.3 tag'inin bir digest'e işaret ettiğini söylüyor:
$ dotnet publish src/Bookstore.Api -c Release -t:PublishContainer \
-p:ContainerRegistry=localhost:18500 -p:ContainerRuntimeIdentifier=linux-x64 \
-p:Version=2026.11.3 '-p:ContainerImageTags="2026.11.3;2026.11.3-ea85740"'
Pushed image 'bookstore/api:2026.11.3, 2026.11.3-ea85740' to registry 'localhost:18500'.
$ curl -sI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
http://localhost:18500/v2/bookstore/api/manifests/2026.11.3 | grep -i digest
Docker-Content-Digest: sha256:5d24bbdebb03bfbfb681f8a2f7377e71f96373a4f17c03b50eb8aef01f689af4
Bir saat sonra Emre, image'a kaynak repository bilgisini eklemek için aynı sürümü aynı tag'lerle bir kez daha publish ediyor. Kod değişmedi. Registry ikinci push'u reddeder mi? Kabul ederse 2026.11.3 aynı image'ı mı gösterir? Production'daki bir node image'ı yeniden çektiğinde ne alır?
$ dotnet publish src/Bookstore.Api -c Release -t:PublishContainer -p:PublishRepositoryUrl=true \
-p:ContainerRegistry=localhost:18500 -p:ContainerRuntimeIdentifier=linux-x64 \
-p:Version=2026.11.3 '-p:ContainerImageTags="2026.11.3;2026.11.3-ea85740"'
Pushed image 'bookstore/api:2026.11.3, 2026.11.3-ea85740' to registry 'localhost:18500'.
$ curl -sI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
http://localhost:18500/v2/bookstore/api/manifests/2026.11.3 | grep -i digest
Docker-Content-Digest: sha256:2df0d157b4e04321632347dca58550aca792aa248a397076b9e0c07da392edd8
Registry itiraz etmedi ve tag artık başka bir digest'i gösteriyor. Kod aynı, ama image aynı değil: SDK image'a oluşturulma zamanını yazıyor ve bu ikinci build'de bir label daha eklendi. NuGet ve npm'in aksine çoğu container registry'sinde tag varsayılan olarak hareketli bir işaret. Serinin son yazısında artifact'ların nerede durduğuna, image'ların nasıl etiketlenip deploy edileceğine, hotfix akışına ve bütün bunları kullanıcıya bağlayan destek politikasına bakıyoruz.
Artifact Repository Seçenekleri
Build once, deploy many fikrini CI/CD Temelleri serisinin "Build Once, Deploy Many: Artifact, Versiyonlama ve Immutable Paket" yazısında anlatmıştık: bir kez üretilen paket her ortama aynen gider. Bu paketin bir adresi olmalı. Bookstore'un dört tür artifact'ı var: NuGet paketi (Contracts), npm paketi (@bookstore/ui), container image (API) ve web'in statik dosyaları. Bunları tutabilecek üç tür yer var:
| Seçenek | Desteklediği formatlar | Ne zaman uygun |
|---|---|---|
| GitHub Packages | npm, NuGet, Maven, RubyGems, container (ghcr.io) | Kod ve CI zaten GitHub'daysa; izinler repository'den geliyor |
| Azure Artifacts | NuGet, npm, Maven, Python, Cargo, Universal Packages | Azure DevOps kullanan ekipler; feed view'ları ile prerelease/release ayrımı |
| Genel amaçlı repository manager | Ürününe göre neredeyse hepsi | Birden çok CI ve kaynak sistemi, kendi altyapında barındırma ihtiyacı |
Seçimi formatlardan çok üç özellik belirliyor. Birincisi proxy/cache: iç feed'in nuget.org ya da npmjs'i upstream olarak alıp indirdiği paketleri saklaması. Böylece dış registry'de bir paket kaldırılsa ya da erişilemese bile build devam ediyor ve kullanılan her dış paketin bir kopyası kayıt altında oluyor. İkincisi retention: kaç sürümün ve ne kadar süre saklanacağı. Feature branch'lerinden üretilen pre-release paketleri ve image'ları kısa sürede silmek, release sürümlerini ise destek süresi boyunca tutmak gerekiyor. Üçüncüsü erişim kontrolü: kim okuyabilir, kim yayınlayabilir. Önceki iki yazıda yayın yetkisini kişilerden alıp workflow'lara verdik; aynı ilke iç feed'ler için de geçerli. Azure Artifacts feed yönetimini ilerideki Azure DevOps Pipelines serisinde ayrıntılı ele alacağız.
Container Image Tag'leri ve OCI Label'ları
Bookstore'da yerel bir OCI (Open Container Initiative; container image ve registry formatlarını standartlaştıran yapı) registry'si çalıştırdık:
docker run -d --name relseries-registry -p 127.0.0.1:18500:5000 registry:3
API projesi image adını ve repository bilgisini kendisi taşıyor; registry, platform ve tag'ler komut satırından geliyor:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<!-- CalVer: CI bunu -p:Version=YYYY.M.BUILD ile ezer -->
<VersionPrefix>0.0.0</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<!-- Container image'ı: dotnet publish -t:PublishContainer -->
<ContainerRepository>bookstore/api</ContainerRepository>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup>
...
Tag şeması bir önceki yazılardaki kararların devamı: CalVer (sürüm numarasının bir kısmını yayın tarihinden alan yaklaşım) sürüm (2026.11.3) insanlar için, sürüm artı kısa commit SHA'sı (2026.11.3-ea85740) kesin eşleşme için. İkinci tag'in değeri, aynı sürüm numarasıyla iki farklı build yapıldığında hangisinin hangisi olduğunu söyleyebilmesi.
Image'ın kendisi de nereden geldiğini söylemeli. OCI image spesifikasyonu bunun için önceden tanımlı label anahtarları içeriyor: org.opencontainers.image.version, revision, source, created ve diğerleri. .NET SDK bunları kendiliğinden dolduruyor; source ve revision için projede PublishRepositoryUrl açık olmalı. Tahmin bölümündeki iki push'un farkı tam olarak buydu: ilkinde bu property yoktu ve image'da commit bilgisi oluşmadı. İkinci image'ın label'ları (okunabilirlik için satırlara böldük):
$ docker image inspect localhost:18500/bookstore/api:2026.11.3-ea85740 --format '{{json .Config.Labels}}'
{
"net.dot.runtime.majorminor": "10.0",
"net.dot.sdk.version": "10.0.402",
"org.opencontainers.artifact.created": "2026-11-14T04:25:33.5559480Z",
"org.opencontainers.image.authors": "Bookstore.Api",
"org.opencontainers.image.base.digest": "sha256:05acd0ca475e2130832bab441d28a7dd31041129bad28eac7928151fd73cd2c5",
"org.opencontainers.image.base.name": "mcr.microsoft.com/dotnet/aspnet:10.0",
"org.opencontainers.image.created": "2026-11-14T04:25:33.5559480Z",
"org.opencontainers.image.revision": "ea857409ab0c3f007bb524e96b67599d6ebb5770",
"org.opencontainers.image.source": "https://github.com/example/bookstore",
"org.opencontainers.image.version": "2026.11.3"
}
Bu label'lar sayesinde bir registry'de ya da bir cluster'da gördüğün image'dan doğrudan commit'e ve repository'ye gidebiliyorsun; base image'ın adı ve digest'i de kayıtlı, bu da bir base image güvenlik açığı duyurulduğunda etkilenen image'ları bulmayı kolaylaştırıyor. Image'ı çalıştırınca, ikinci yazıdaki /version endpoint'i aynı bilgiyi çalışan uygulamadan veriyor:
$ docker run -d -p 18080:8080 localhost:18500/bookstore/api:2026.11.3-ea85740
$ curl -s http://localhost:18080/version
{"version":"2026.11.3","commit":"ea857409ab0c3f007bb524e96b67599d6ebb5770","assemblyVersion":"2026.11.3.0","fileVersion":"2026.11.3.0","runtime":".NET 10.0.15"}
Digest ile Deploy ve latest'in Sorunu
Tag hareket edebildiği için deploy edilen şeyi tag'le değil digest'le tanımlamak gerekiyor. Digest, image manifest'inin hash'i; içerik değişirse digest değişir, aynı digest her zaman aynı image'dır. Tahmin bölümündeki ilk image, tag'i kaybettikten sonra da digest'iyle registry'de duruyor:
$ docker pull localhost:18500/bookstore/api@sha256:5d24bbdebb03bfbfb681f8a2f7377e71f96373a4f17c03b50eb8aef01f689af4
$ docker image inspect localhost:18500/bookstore/api@sha256:5d24bbdebb03bfbfb681f8a2f7377e71f96373a4f17c03b50eb8aef01f689af4 \
--format '{{index .Config.Labels "org.opencontainers.image.version"}} revision={{index .Config.Labels "org.opencontainers.image.revision"}}'
2026.11.3 revision=
Tag artık başka bir image'ı gösterse de digest'le çekilen image, ilk build'in kendisi: sürüm 2026.11.3, revision label'ı boş. Production manifest'inde bookstore/api@sha256:5d24… yazılıysa, birinin aynı tag'i yeniden push'laması çalışan sistemi değiştirmiyor.
latest bu sorunun en uç hâli. CI/CD Temelleri serisinde değindiğimiz gibi latest bir sürüm değil, en son kimin push ettiğini gösteren bir işaret; production'da latest kullanan bir sistemde rollback edilecek "önceki sürümün" adı yok ve iki node aynı anda farklı kod çalıştırabiliyor. Registry'lerin çoğu tag'leri değiştirilemez yapmaya izin veriyor (ör. Amazon'un container registry servisi ECR'de (Elastic Container Registry; Amazon'un bulut üzerindeki image deposu) tag immutability, Azure Container Registry'de repository ya da image kilidi, Harbor'da immutable tag kuralları); release tag'leri için bu ayarı açmak, tahmin bölümündeki durumu imkânsız hâle getiriyor.
SDK, ürettiği image'ın digest'ini bir MSBuild property'si olarak veriyor. CI'da bu değeri yakalayıp sonraki job'lara aktarmak için:
$ dotnet publish src/Bookstore.Api -c Release -t:PublishContainer \
-p:ContainerRegistry=localhost:18500 -p:ContainerRuntimeIdentifier=linux-x64 \
-p:Version=2026.11.4 '-p:ContainerImageTags="2026.11.4;2026.11.4-5c67033"' \
-getProperty:GeneratedContainerDigest
sha256:52b191c1c5de96bf9b71313525eb1baecc097b8f732c862ad1d1d0c22939ce15
Bookstore API'sinin release workflow'u bu değeri staging ve production job'larına aynen geçiriyor:
name: deploy-api
on:
push:
tags: ['app/v*']
permissions:
contents: read
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
version: ${{ steps.version.outputs.version }}
digest: ${{ steps.publish.outputs.digest }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v6
with:
global-json-file: global.json
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Compute version
id: version
run: echo "version=${GITHUB_REF_NAME#app/v}" >> "$GITHUB_OUTPUT"
# Tag'ler: 2026.11.4 ve 2026.11.4-<kısa sha>; deploy ettiğimiz şey digest
- name: Build and push the image
id: publish
env:
VERSION: ${{ steps.version.outputs.version }}
REGISTRY: ghcr.io
REPOSITORY: example/bookstore-api
run: |
digest=$(dotnet publish src/Bookstore.Api -c Release -t:PublishContainer \
-p:ContainerRegistry="$REGISTRY" -p:ContainerRepository="$REPOSITORY" \
-p:ContainerRuntimeIdentifier=linux-x64 -p:Version="$VERSION" \
"-p:ContainerImageTags=\"$VERSION;$VERSION-${GITHUB_SHA::7}\"" \
-getProperty:GeneratedContainerDigest)
echo "digest=$digest" >> "$GITHUB_OUTPUT"
deploy-staging:
needs: image
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.bookstore.example.com
steps:
- name: Deploy by digest
env:
IMAGE: ghcr.io/example/bookstore-api@${{ needs.image.outputs.digest }}
VERSION: ${{ needs.image.outputs.version }}
run: echo "Deploying $IMAGE ($VERSION) to staging"
deploy-production:
needs: [image, deploy-staging]
runs-on: ubuntu-latest
environment:
name: production
url: https://bookstore.example.com
steps:
- name: Deploy by digest
env:
IMAGE: ghcr.io/example/bookstore-api@${{ needs.image.outputs.digest }}
VERSION: ${{ needs.image.outputs.version }}
run: echo "Deploying $IMAGE ($VERSION) to production"
İki ortam da aynı digest'i alıyor; staging'de denenen image, production'a giden image'ın kendisi. Her job bir GitHub environment'ına bağlı; GitHub her deploy'u environment'ın geçmişine yazıyor ve url alanını o deploy'un yanında gösteriyor. Production environment'ına bir onay kuralı eklemek, aynı digest'i staging'den sonra bir insan onayına bağlıyor. Deploy adımları bu yazıda yalnız ne deploy edileceğini yazıyor; gerçek deploy mekaniğini GitHub Actions serisinin deploy yazılarında anlatmıştık. Image adımının komutlarını yerel registry'ye karşı aynı değişkenlerle koşturduk; workflow actionlint ile doğrulandı, GitHub'da koşulmadı.
Release Cadence: Sürekli Deploy ve Release Train
Ne sıklıkla yayın yapılacağı da bir release politikası kararı. İki uç var. Sürekli deploy'da main'e giren her değişiklik, testleri geçtiği anda production'a gidebilir; Bookstore API ve web için hedef bu ve CalVer'in build numarası bu yüzden sıradan bir sayaç. Release train'de ise yayınlar sabit bir takvimle (ör. her iki haftada bir) çıkar; trene yetişen değişiklikler o sürüme girer, yetişmeyenler bir sonrakini bekler. Mobil uygulamalar, kurumsal müşterilere kurulan yazılımlar ve kullanıcıların güncellemeyi planlaması gereken paketler bu modele daha yakın. Deploy ile release'in ayrılması, yani kodu production'a gönderip özelliği feature flag ile sonra açmak, iki modeli birbirine yaklaştırıyor; Deployment Stratejileri serisinin "Deploy ve Release Farkı" yazısında bunu ele almıştık.
Ölçmeden hangi modelin işe yaradığını bilemezsin. CI/CD Temelleri serisinin "Pipeline'ı Ölçmek ve Hızlandırmak" yazısında anlattığımız DORA (DevOps Research and Assessment; yazılım teslimat performansını ölçen araştırma programı) metrikleri, yani deploy sıklığı, değişikliğin production'a ulaşma süresi, hatalı değişiklik oranı ve servis geri gelme süresi, tam olarak bu yazıda kurduğumuz kayıtlardan hesaplanıyor: deploy geçmişi, tag'ler ve commit'ler.
Hotfix Akışı ve Aynı Anda İki Major'ı Desteklemek
Bookstore.Contracts 2.x'te, ama bazı kurumsal istemciler hâlâ 1.x kullanıyor ve destek politikamız 1.x'e altı ay güvenlik düzeltmesi sözü veriyor. Main'de para biriminin her zaman büyük harfle yazdırılmasını sağlayan bir düzeltme yaptık ve 2.1.1 olarak yayınladık. Aynı düzeltmeyi 1.x kullanıcılarına ulaştırmak için son 1.x tag'inden bir release branch'i açıp commit'i cherry-pick ediyoruz:
$ git switch -c release/1.x contracts/v1.1.0
$ git cherry-pick -x d41a79f
Auto-merging src/Bookstore.Contracts/Money.cs
CONFLICT (content): Merge conflict in src/Bookstore.Contracts/Money.cs
error: could not apply d41a79f... fix(contracts): print currency code in upper case
Çakışma beklenmedik değil: 1.1.0 ile main arasında Money.cs'e XML dokümantasyonu eklendi ve cherry-pick edilen değişikliğin çevresindeki satırlar artık aynı değil. Release branch'leri main'den uzaklaştıkça backport'lar da zorlaşıyor; iki major'ı aynı anda desteklemenin gerçek maliyeti bu. Git & GitHub serisinin "Git cherry-pick: Tek Bir Commit'i Başka Branch'e Taşımak" yazısında çakışma çözmeyi ve -x'in commit mesajına "cherry picked from" satırını eklediğini görmüştük; bu satır, hangi düzeltmenin hangi branch'e taşındığının kaydı. Çakışmayı yalnız ToUpperInvariant değişikliğini alarak çözüp release'i çıkarıyoruz:
$ git add src/Bookstore.Contracts/Money.cs
$ git cherry-pick --continue
$ npx commit-and-tag-version
✔ outputting changes to src/Bookstore.Contracts/CHANGELOG.md
✔ committing src/Bookstore.Contracts/CHANGELOG.md
✔ tagging release contracts/v1.1.1
ℹ Run `git push --follow-tags origin release/1.x` to publish
$ dotnet pack src/Bookstore.Contracts -c Release -o artifacts
Successfully created package 'artifacts/Bookstore.Contracts.1.1.1.nupkg'.
$ git log --oneline --graph --all
* 921c9ce (HEAD -> release/1.x, tag: contracts/v1.1.1) chore(release): contracts 1.1.1
* 0a45a59 fix(contracts): print currency code in upper case
| * 5c67033 (tag: contracts/v2.1.1, main) chore(release): contracts 2.1.1
| * d41a79f fix(contracts): print currency code in upper case
| * af1af57 build(api): container image settings
| ...
| * fecbe9d (tag: contracts/v2.0.0) chore(release): contracts 2.0.0
| * 4bb4dfc feat(contracts)!: remove BookDto.Price
|/
* 1e561ba (tag: contracts/v1.1.0) chore(release): contracts 1.1.0
commit-and-tag-version release branch'inde doğru sürümü buldu: branch'ten erişilebilen son tag contracts/v1.1.0, düzeltme bir fix, sonuç 1.1.1. MinVer de paketi tag'den 1.1.1 olarak üretti. Ama bir tuzak daha var:
$ git ls-tree --name-only contracts/v1.1.1 .github/workflows/
.github/workflows/contracts-ci.yml
.github/workflows/release-app.yml
Release branch'i, yayın workflow'u eklenmeden önceki bir commit'ten açıldı. contracts/v1.1.1 tag'i push edildiğinde GitHub, tag'in gösterdiği commit'teki workflow dosyalarına bakar; orada publish-contracts.yml olmadığı için hiçbir şey yayınlanmaz ve hata da vermez. Desteklenen her release branch'i, yayın pipeline'ını da taşımalı: workflow'u branch'e backport et ya da yayın workflow'unu tag'den değil, main'deki bir workflow_dispatch'ten tetikleyip hangi tag'in yayınlanacağını parametre olarak ver. npm tarafında da bir ek adım var: eski bir major'ın yeni patch'ini latest tag'iyle yayınlarsan kullanıcıların varsayılan kurulumu 2.x'ten 1.x'e düşer; eski major'ın patch'leri v1-lts gibi ayrı bir dist-tag'le yayınlanmalı.
"Ne Zaman, Ne Yayınlandı?" Kaydı
Serinin başından beri kurduğumuz parçalar birlikte, bu sorunun iki yönden de cevaplanmasını sağlıyor:

İleri yönde: bir commit, bir tag'le sürüm kararına dönüşüyor; tag'den bir kez bir artifact üretiliyor ve digest'iyle ortamlara gidiyor; release notes bu sürümü kullanıcıya duyuruyor. Geri yönde: production'da bir hata gördüğünde telemetrideki service.version seni tag'e, environment'ın deploy geçmişi digest'e, image'ın revision label'ı commit'e götürüyor. Zincirin her halkası değişmeyen bir kimlik taşıdığı sürece "bu hata hangi değişiklikle geldi?" sorusu tahmin değil, okuma işi oluyor. Halkalardan biri hareketli olduğunda, ör. production latest tag'ini kullandığında, zincir orada kopuyor.
Destek Politikası
Bir sürüm numarası ancak arkasında yazılı bir destek politikası varsa kullanıcıya planlama imkânı veriyor; ilk yazıda .NET ve Angular'ın politikalarını okurken bunu görmüştük. Bookstore'un repository'sindeki SUPPORT.md:
# Support policy
| Component | Line | Status | What it gets | Until |
|---|---|---|---|---|
| Bookstore.Contracts | 2.x | Active | features, bug fixes, security fixes | six months after 3.0.0 ships |
| Bookstore.Contracts | 1.x | Security fixes only | security fixes | 2027-05-14 |
| @bookstore/ui | 1.x | Active | features, bug fixes, security fixes | six months after 2.0.0 ships |
| Bookstore.Api, bookstore-web | latest release | Active | everything, rolling forward | not applicable |
## Rules
- A new major starts a six-month security-only window for the previous major.
- Security fixes ship to every line that is still supported; the release notes list the affected versions.
- End of support is announced in the release notes and on the package page at least three months ahead.
- Packages target the supported .NET LTS and Angular versions; dropping a target framework or an Angular major is a major release.
Şablon kısa ama dört soruyu cevaplıyor: hangi sürüm hattı destekleniyor, o hat ne alıyor (her şey mi, yalnız güvenlik düzeltmesi mi), ne zamana kadar ve destek sonu ne zaman duyurulacak. Son madde, bağımlılıkların politikasını kendi politikana bağlıyor: .NET 10'un desteği 14 Kasım 2028'de bittiğinde Contracts'ın net10.0'ı bırakması bir major olacak ve bu da tablonun bir sonraki satırını üretecek. Politikayı paketin README'sine, nuget.org ve npmjs sayfalarına ve release notes'a bağla; kullanıcının onu aramak zorunda kalmaması gerekiyor.
Üretimde Dikkat
Release tag'lerini değiştirilemez yap. Container registry'nde release tag'leri için immutability ayarını aç; geliştirme tag'leri (branch adı, pull request numarası) hareketli kalabilir ama retention ile kısa sürede silinmeli.
Deploy manifest'lerinde digest kullan, insanlar için tag'i yanına yaz. bookstore/api:2026.11.3@sha256:… biçimi hem okunabilir hem kesin; registry tag'i değişse de digest kazanır.
Retention politikasını destek politikasıyla hizala. Desteklenen bir sürümün image'ı ya da paketi retention ile silinirse, hotfix'ten önceki durumu yeniden kurmak imkânsızlaşır. "Desteklenen her sürümün artifact'ı saklanır" kuralını retention ayarına yaz.
Release branch'lerinin listesini ve sahibini tut. Hangi branch'in hangi major'ı taşıdığı, ne zaman kapanacağı ve backport'ları kimin yaptığı SUPPORT.md'de ya da release notes'ta yazmalı; aksi hâlde güvenlik düzeltmesi main'e girip eski hattı unutulabilir.
Sık Düşülen Tuzaklar
Production'da latest ya da yalnız sürüm tag'i kullanmak. Tahmin bölümünde gördüğümüz gibi aynı tag iki farklı image'ı gösterebilir; deploy edilen şey digest olmalı.
Image'a kaynak bilgisini koymamak. revision ve source label'ları olmayan bir image, registry'de "bu ne?" sorusuna cevap vermiyor. .NET SDK'da PublishRepositoryUrl'ü aç, Dockerfile kullanıyorsan aynı label'ları build argümanlarıyla ekle.
Hotfix'i yalnız release branch'ine yapmak. Düzeltme main'e girmezse bir sonraki sürümde hata geri gelir. Önce main'e, sonra cherry-pick ile eski hatlara; bu sırayı tersine çevirmek zorunda kaldıysan, release branch'indeki düzeltmeyi main'e taşımayı aynı gün yap.
Release branch'inde yayın pipeline'ını unutmak. Eski bir commit'ten açılan branch, sonradan eklenen workflow'ları içermez; tag atılır, hiçbir şey yayınlanmaz ve kimse fark etmez.
Destek politikasını yazmamak ya da yazıp duyurmamak. Kullanıcılar 1.x'in ne zaman biteceğini bilmezse geçişi planlamaz; son gün gelen bir güvenlik açığı herkesi aynı anda acil bir major yükseltmesine zorlar.
Küçük Bir Deneme
★☆☆ Yerel registry'ye API image'ını iki kez aynı tag'le gönder ve iki digest'i karşılaştır. Kod değişmediği hâlde digest neden farklı? org.opencontainers.image.created label'ına bak.
★★☆ release/1.x branch'ine yayın workflow'unu backport et ve contracts/v1.1.1 tag'iyle tetiklendiğinde baseline'ın 1.1.0 olacağını git describe ile doğrula. Aynı tag main'deki bir workflow_dispatch'ten yayınlansaydı workflow'da neyi değiştirmen gerekirdi?
★★★ Kendi ekibin için bir SUPPORT.md yaz: sürüm hatları, her hattın aldığı düzeltmeler, bitiş tarihleri ve bağımlı olduğun runtime'ların (.NET, Angular, Node.js) destek tarihlerinin senin politikana etkisi. Ardından registry'ndeki retention ayarının bu politikayla çelişip çelişmediğini kontrol et.
Bu yazıyla seri tamamlandı. Sürüm numarasının ne anlattığından başladık; sürümü build'e ve çalışan uygulamaya taşıdık, Git'ten ürettik, CHANGELOG ve release notes'a döktük, NuGet ve npm paketlerini doğrulanmış ve anahtarsız yayınladık ve son olarak artifact'ları, hotfix akışını ve destek politikasını tek bir zincire bağladık. Bu zincirin halkalarını kendi projende tek tek kontrol etmek, "ne zaman, ne yayınlandı?" sorusuna dakikalar içinde cevap verebilmenin yolu.