Mert Özen Her Satırda Daha İleri
DevOps Azure DevOps Pipelines — #05

Azure Artifacts Feed'leri: NuGet ve npm Paketlerini Yayınlamak, Upstream Source'lar ve View'lar

Mert Özen 15 Kas 2026 14 dk 6 görüntülenme
Azure Artifacts Feed'leri: NuGet ve npm Paketlerini Yayınlamak, Upstream Source'lar ve View'lar

Azure Artifacts feed'i NuGet ve npm paketlerini değişmez sürümlerle saklar; upstream source ile nuget.org ve npmjs'i tek adresten sunar, view'larla da hangi sürümün kime görüneceğini belirler. Bookstore'un contract paketini NuGetAuthenticate@1 ve dotnet nuget push ile yayınlıyor, CI restore'unu feed'e bağlıyor, build kimliğinin rollerini, promotion, retention ve eski Artifacts task'larının roadmap durumunu görüyoruz.

Sürüm notu: Bu yazıdaki paketleme, sürüm script'leri ve NuGet komutları .NET 10 (SDK 10.0.404) ve npm 11 ile yerelde denenmiştir; pipeline ve template dosyaları yq 4.54 ile ayrıştırılarak denetlenmiştir. Azure Artifacts feed'leri, izinleri, upstream source'lar, view'lar ve task'ların davranışı için Microsoft Learn'deki resmî belgelerden (Kasım 2026 sürümleri) yararlanılmıştır.

Önce Tahmin Et

Hakan'ın ekibi, sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) sipariş olaylarını taşıyan Bookstore.Contracts paketinin 1.0.0 sürümünü şirketin Azure Artifacts feed'ine yayınlıyor. Bir saat sonra OrderPlaced kaydında bir alanın yanlış adlandırıldığını fark ediyorlar. Henüz kimse paketi kullanmadığı için plan basit: 1.0.0'ı feed'den silmek, düzeltilmiş kodu yine 1.0.0 olarak yayınlamak. İkinci push başarılı olur mu?

Önce yerel bir klasörü NuGet source'u olarak kullanıp aynı sürümü iki kez gönderelim:

$ dotnet nuget push pkgs/Bookstore.Contracts.1.0.0-ci.1302.nupkg --source …/localfeed
Pushing Bookstore.Contracts.1.0.0-ci.1302.nupkg to '…/localfeed'...
Your package was pushed.
$ dotnet nuget push pkgs/Bookstore.Contracts.1.0.0-ci.1302.nupkg --source …/localfeed
Pushing Bookstore.Contracts.1.0.0-ci.1302.nupkg to '…/localfeed'...
Your package was pushed.

Klasör source'u ikinci gönderimi sessizce kabul etti ve dosyanın üstüne yazdı. Azure Artifacts'te cevap farklı: ikinci push başarısız olur, silme işlemi de bunu değiştirmez. Belgelere göre feed'deki paketler değişmez: yayınlanmış bir sürümün içeriği değiştirilemiyor, numarası yeni bir paket için tekrar kullanılamıyor ve paket silinse bile sürüm numarası feed'in geçmişinde kalıcı olarak ayrılmış durumda kalıyor. Hakan'ın ekibinin yapabileceği tek şey 1.0.1 yayınlamak. Bu kural can sıkıcı görünse de iki makinenin aynı sürüm numarasıyla farklı içerik indirmesini imkânsız kılıyor; yerel klasör source'unun yaptığı tam olarak bunun tersi.

Feed: Organization ve Project Scope

Feed, Azure Artifacts'te paketlerin tutulduğu ve erişimin yönetildiği yer. Tek bir feed NuGet, npm, Maven, Python, Cargo ve Universal Packages paketlerini birlikte barındırabiliyor; Bookstore için NuGet ve npm yeterli. Feed oluştururken iki seçim yapılıyor. Scope: project-scoped bir feed yalnızca o project içinden görülüyor; organization-scoped bir feed organization'daki her project'ten görülebiliyor ve sonradan project-scoped'a çevrilemiyor. Görünürlük: feed'i kimlerin görebileceği; başka feed'lerin bu feed'i upstream olarak kullanabilmesi için görünürlüğün organization üyelerine ya da Microsoft Entra ID (Microsoft'un kimlik ve erişim yönetimi servisi) kiracısının üyelerine açık olması gerekiyor. Public project'ler kapatıldığı için public feed'ler de 2027'de erişilemez hâle gelecek.

Scope, feed adresine de yansıyor. Bookstore project'indeki bookstore adlı project-scoped feed'in NuGet adresi https://pkgs.dev.azure.com/bookstore-org/Bookstore/_packaging/bookstore/nuget/v3/index.json; organization-scoped bir feed'de adreste project adı yok. npm için aynı feed'in adresi …/_packaging/bookstore/npm/registry/ ile bitiyor.

Feed İzinleri ve Pipeline'ın Kimliği

Feed'de dört rol var. Feed Reader paketleri listeleyip indirebiliyor. Feed and Upstream Reader (Collaborator) buna ek olarak upstream source'lardan paket kaydedebiliyor. Feed Publisher (Contributor) paket yayınlayabiliyor, promote edebiliyor, deprecate ve unlist yapabiliyor. Feed Owner ise feed ayarlarını, upstream source'ları ve paket silmeyi yönetiyor. Upstream'den paket kaydetmek için en az Collaborator gerekmesi önemli bir ayrıntı: Reader rolündeki biri, feed'de henüz kayıtlı olmayan bir nuget.org paketini bu feed üzerinden indiremiyor.

Pipeline'lar bir kullanıcı adına değil, bir build kimliğiyle çalışıyor. Belgelerde iki kimlik var: organization seviyesindeki Project Collection Build Service (bookstore-org) ve project seviyesindeki Bookstore Build Service (bookstore-org). Varsayılan olarak ikisine de feed'de Collaborator rolü veriliyor; yani bir pipeline feed'den restore edebiliyor ve upstream paketlerini kaydedebiliyor ama yayın yapamıyor. Paket yayınlayacak pipeline için belgeler iki kimliğe de Feed Publisher (Contributor) rolünü vermeyi öneriyor. Hangi kimliğin kullanılacağı project ayarlarındaki job authorization scope'a bağlı; bu ayar ve neden project seviyesine daraltılması gerektiği sekizinci yazının konusu.

Contract Paketini Hazırlamak

Bookstore'un sipariş olaylarını dinleyecek başka servisler (bildirimler, raporlama) API'nin iç tiplerine bağlanmamalı. Bunun için solution'a ayrı bir class library ekledik: src/Bookstore.Contracts. İçinde şimdilik tek bir olay var:

namespace Bookstore.Contracts.Ordering;

/// <summary>Müşterinin siparişi oluşturulup stok ayrıldığında yayınlanır.</summary>
/// <param name="OrderId">Ordering module'ündeki sipariş kimliği.</param>
/// <param name="CustomerEmail">Bildirimler için müşterinin e-posta adresi.</param>
/// <param name="PlacedAtUtc">Siparişin verildiği an, UTC.</param>
/// <param name="Lines">Sipariş edilen kitaplar.</param>
/// <param name="Total">Sipariş toplamı.</param>
/// <param name="Currency">ISO 4217 para birimi kodu, örneğin TRY.</param>
public sealed record OrderPlaced(
    int OrderId,
    string CustomerEmail,
    DateTime PlacedAtUtc,
    IReadOnlyList<OrderPlacedLine> Lines,
    decimal Total,
    string Currency);

/// <summary><see cref="OrderPlaced"/> içindeki tek bir kitap satırı.</summary>
/// <param name="Isbn">Kitabın ISBN-13 numarası.</param>
/// <param name="Title">Sipariş anındaki kitap adı.</param>
/// <param name="Quantity">Adet.</param>
/// <param name="UnitPrice">Sipariş anındaki birim fiyat.</param>
public sealed record OrderPlacedLine(string Isbn, string Title, int Quantity, decimal UnitPrice);

Proje dosyası paketin kimliğini ve kendi sürüm çizgisini tanımlıyor: PackageId Bookstore.Contracts, VersionPrefix 1.0.0. Contract paketi uygulamadan bağımsız sürümleniyor; API her gün yeni bir sürüm üretse de olay şeması değişmedikçe paket sürümü yerinde duruyor. Paketin hangi kurallarla sürümleneceği, changelog ve paket tasarımının kendisi Versioning ve Release Management serisinin konusu; burada yalnızca feed'e götürme ve feed'i yönetme tarafına bakıyoruz. Yerelde paketledik:

$ dotnet pack src/Bookstore.Contracts/Bookstore.Contracts.csproj --no-restore -c Release -p:Version=1.0.0-ci.1302 -o pkgs
  Bookstore.Contracts -> …/src/Bookstore.Contracts/bin/Release/net10.0/Bookstore.Contracts.dll
  The package Bookstore.Contracts.1.0.0-ci.1302 is missing a readme. Go to https://aka.ms/nuget/authoring-best-practices/readme to learn why package readmes are important.
  Successfully created package '…/pkgs/Bookstore.Contracts.1.0.0-ci.1302.nupkg'.
$ unzip -l pkgs/Bookstore.Contracts.1.0.0-ci.1302.nupkg
  Length      Date    Time    Name
---------  ---------- -----   ----
      482  11-15-2026 12:06   _rels/.rels
      516  11-15-2026 12:06   Bookstore.Contracts.nuspec
    10240  11-15-2026 09:06   lib/net10.0/Bookstore.Contracts.dll
     4140  11-15-2026 09:06   lib/net10.0/Bookstore.Contracts.xml
      521  11-15-2026 12:06   [Content_Types].xml
      702  11-15-2026 12:06   package/services/metadata/core-properties/nuget.psmdcp
---------                     -------
    16601                     6 files

Readme uyarısı bir hata değil, bir hatırlatma; paket sayfasında görünecek README de paket tasarımının parçası. Bu yorumlardan üretilen XML dosyası (Bookstore.Contracts.xml) paketin içinde olduğu için paketi kullanan geliştirici IDE'de alanların açıklamalarını görüyor.

Pipeline'dan Feed'e Push: NuGetAuthenticate@1 ve dotnet nuget push

Paket ayrı bir pipeline'da, bookstore-contracts adıyla yayınlanıyor. İkinci yazıdaki pipeline completion trigger örneğinin kaynağı da bu pipeline'dı.

parameters:
  - name: publishStable
    displayName: Publish a stable version (main only)
    type: boolean
    default: false

trigger:
  branches:
    include:
      - main
  paths:
    include:
      - src/Bookstore.Contracts

pool:
  vmImage: ubuntu-24.04

variables:
  project: src/Bookstore.Contracts/Bookstore.Contracts.csproj
  nugetConfig: build/nuget.feed.config

steps:
  - checkout: self

  - task: UseDotNet@2
    displayName: Install .NET SDK from global.json
    inputs:
      packageType: sdk
      useGlobalJson: true

  - task: NuGetAuthenticate@1
    displayName: Authenticate to Azure Artifacts

  - script: bash scripts/set-contracts-version.sh
    displayName: Decide package version
    env:
      PUBLISH_STABLE: ${{ parameters.publishStable }}

  - script: dotnet restore $(project) --configfile $(nugetConfig) --locked-mode
    displayName: Restore through the feed

  - script: dotnet pack $(project) --no-restore -c Release -p:Version=$(contractsVersion) -o $(Build.ArtifactStagingDirectory)/packages
    displayName: Pack $(contractsVersion)

  - script: >-
      dotnet nuget push "$(Build.ArtifactStagingDirectory)/packages/*.nupkg"
      --source bookstore --configfile $(nugetConfig) --api-key az --skip-duplicate
    displayName: Push to feed

NuGetAuthenticate@1 kendi başına paket indirmiyor ya da yüklemiyor; NuGet'in credential provider'ını kuruyor ve pipeline kimliğinin token'ını dotnet, NuGet ve MSBuild'in bulacağı yere koyuyor. Sonraki dotnet komutları feed'e bu kimlikle bağlanıyor. --api-key az bir sır değil: Azure Artifacts gerçek kimlik doğrulamayı credential provider üzerinden yapıyor ve API key'i yalnızca boş olmaması gereken bir yer tutucu olarak istiyor. Eski örneklerde sık görülen NuGetCommand@2'nin belgesi Ubuntu 24.04 ile birlikte hosted image'larda Mono'nun gelmediğini, bu task'ın altındaki nuget.exe'nin Mono'ya ihtiyaç duyduğunu ve Ubuntu'da NuGetAuthenticate@1 ile .NET CLI'a geçilmesi gerektiğini söylüyor.

Feed adresi tek bir yerde, build/nuget.feed.config dosyasında duruyor:

<?xml version="1.0" encoding="utf-8"?>
<!-- CI, paketleri yalnızca Bookstore feed'i üzerinden restore eder ve push'lar. nuget.org build'e feed'in upstream source'u olarak ulaşır. -->
<configuration>
  <packageSources>
    <clear />
    <add key="bookstore" value="https://pkgs.dev.azure.com/bookstore-org/Bookstore/_packaging/bookstore/nuget/v3/index.json" />
  </packageSources>
</configuration>

<clear />, makinedeki başka NuGet config dosyalarından gelen source'ları devre dışı bırakıyor; belgeler upstream kullanan bir feed için config'te tek bir source olmasını öneriyor. Dosyayı repository kökündeki varsayılan nuget.config yerine build/ altına koymamızın nedeni bu serinin snapshot'ı: kökte duran bir feed config'i, feed'e erişimi olmayan bir okurun yerel restore'unu kırardı. Gerçek bir kurumda geliştiricilerin de feed'i kullanması istenir; bu durumda config kökte durur ve geliştiriciler Azure Artifacts Credential Provider ile kimlik doğrular.

Sürüm kararı küçük bir script'te. Her run benzersiz bir prerelease sürüm üretiyor; kararlı sürüm yalnızca main'den, run başlatılırken publishStable işaretlendiğinde çıkıyor. Script, parameter'ın metne çevrilmiş hâlinin büyük ya da küçük harfle gelmesini birlikte kabul ediyor:

$ BUILD_SOURCEBRANCH=refs/heads/main BUILD_BUILDID=1302 PUBLISH_STABLE=False bash scripts/set-contracts-version.sh
Contracts package version: 1.0.0-ci.1302
##vso[task.setvariable variable=contractsVersion]1.0.0-ci.1302
$ BUILD_SOURCEBRANCH=refs/heads/main BUILD_BUILDID=1302 PUBLISH_STABLE=True bash scripts/set-contracts-version.sh
Contracts package version: 1.0.0
##vso[task.setvariable variable=contractsVersion]1.0.0
$ BUILD_SOURCEBRANCH=refs/heads/feature/order-events BUILD_BUILDID=1302 PUBLISH_STABLE=True bash scripts/set-contracts-version.sh
##vso[task.logissue type=error]Stable contracts versions are published only from main (this run: refs/heads/feature/order-events).

--skip-duplicate'in neden orada olduğu da değişmezlik kuralıyla ilgili. Pipeline artifact belgesi bunu açıkça yazıyor: paket yayınlandıktan sonra başarısız olan bir job yeniden çalıştırılırsa push aynı sürümü ikinci kez göndermeye çalışıp kırılıyor. --skip-duplicate bu durumda var olan sürümü atlayıp devam ediyor. Bedeli, gerçekten farklı içerikli bir paketi aynı sürümle göndermeye çalıştığında da sessiz kalması; bunu, sürümün her run'da benzersiz olmasıyla dengeliyoruz. Yerel klasör source'unda aynı seçenek "bu push türünde desteklenmiyor" uyarısı veriyor, yani bu davranışı yerelde değil ancak gerçek feed'de görebilirsin.

Upstream Source'lar: nuget.org ve npmjs'i Feed Üzerinden Kullanmak

Feed oluştururken "Include packages from common public sources" seçilirse nuget.org, npmjs.com ve diğer genel kaynaklar upstream source olarak ekleniyor. Bundan sonra feed, kendi paketlerini ve genel kaynaklardaki paketleri tek bir adresten sunuyor. Belgelere göre arama sırası sabit: önce feed'e doğrudan yayınlanmış paketler, sonra upstream'den daha önce kaydedilmiş paketler, en son upstream source'lar, yapılandırıldıkları sırayla. NuGet'in birden fazla source'a paralel sorgu atıp ilk geleni almasının yarattığı belirsizlik böylece ortadan kalkıyor. Collaborator ya da üstü bir kimlik upstream'den bir paket indirdiğinde, paketin bir kopyası feed'e kaydediliyor; nuget.org erişilemez olsa bile o sürüm feed'den gelmeye devam ediyor.

Bookstore'un CI pipeline'ını da feed'e bağladık. Ortak template'lerin yeni sürümünde (v1.2.0) restore step'i, bir config dosyası verildiğinde önce kimliği kuruyor:

  - ${{ if ne(parameters.nugetConfig, '') }}:
      - task: NuGetAuthenticate@1
        displayName: Authenticate to Azure Artifacts

      - script: dotnet restore ${{ parameters.solution }} --configfile ${{ parameters.nugetConfig }} --locked-mode
        displayName: Restore (locked, feed)

  - ${{ else }}:
      - script: dotnet restore ${{ parameters.solution }} --locked-mode
        displayName: Restore (locked)

Angular job'ında aynı iş .npmrc ile yapılıyor. Template, verilen dosyayı uygulama klasörüne .npmrc olarak kopyalıyor ve npmAuthenticate@0 bu dosyadaki registry'ler için kimlik bilgisini dosyanın sonuna ekliyor:

      - ${{ if ne(parameters.npmrc, '') }}:
          - script: cp ${{ parameters.npmrc }} ${{ parameters.workingDirectory }}/.npmrc
            displayName: Use feed .npmrc

          - task: npmAuthenticate@0
            displayName: Authenticate to Azure Artifacts
            inputs:
              workingFile: ${{ parameters.workingDirectory }}/.npmrc
registry=https://pkgs.dev.azure.com/bookstore-org/Bookstore/_packaging/bookstore/npm/registry/
always-auth=true

Bir ayrıntı daha var: package-lock.json'daki her paketin resolved alanı registry.npmjs.org adresini içeriyor. npm'in replace-registry-host ayarı bu adresi yapılandırılmış registry ile değiştiriyor; varsayılan değeri yerel npm'de de görülebiliyor:

$ npm config ls -l | grep -E "replace-registry-host|^registry"
registry = "https://registry.npmjs.org/"
replace-registry-host = "npmjs"

npmjs değeri, yalnızca npmjs.org adreslerinin yapılandırılmış registry ile değiştirileceği anlamına geliyor; bu sayede aynı lock dosyası hem yerelde npmjs'ten hem CI'da feed'den çalışıyor. Uygulama pipeline'ı bu iki dosyayı nugetConfig: build/nuget.feed.config ve npmrc: build/feed.npmrc parameter'larıyla veriyor ve ref'i refs/tags/v1.2.0'a yükseltiyor.

Supply chain açısından upstream source iki yönlü bir araç. Olumlu tarafı: CI'ın genel kaynaklara doğrudan değil tek bir kurumsal feed üzerinden çıkması, hangi paketin hangi sürümünün içeri girdiğini tek yerde görmeyi sağlıyor; kaydedilen kopya, paket sahibi onu genel kaynaktan kaldırsa bile feed'de kalıyor. Dikkat edilmesi gereken taraf: zararlı olduğu sonradan anlaşılan bir sürüm de aynı şekilde feed'e kaydedilmiş oluyor ve genel kaynaktan kaldırılması onu feed'den silmiyor; bunu Feed Owner'ın elle silmesi gerekiyor. Belgelerdeki bir kural da beklenmedik olabilir: bir upstream source etkinken, o upstream'de zaten var olan bir sürümü kendi feed'ine yayınlayamıyorsun. Bilinen bir pakete kendi yamanı aynı sürümle koymak isteyen ekip, belgelerdeki sırayı izlemek zorunda: upstream'i kapat, paketi yayınla, upstream'i yeniden aç. Paket kaynaklarının güvenliği ve dependency scanning DevSecOps serisinde anlatıldı.

View'lar ile Promotion ve Retention

Her feed üç view ile geliyor: @Local, @Prerelease ve @Release. @Local varsayılan view; feed'e yayınlanan ve upstream'den kaydedilen her şeyi içeriyor. Yayın her zaman feed'in kendisine, yani @Local'e yapılıyor; diğer view'lara doğrudan yayın yok. Bir paket sürümünü @Prerelease ya da @Release'e taşımak "promote" işlemi: Feed Publisher (Contributor) veya Feed Owner rolü gerekiyor ve web arayüzünden ya da REST (HTTP üzerinden kaynak odaklı API tarzı) API ile yapılıyor. Belgelere göre promotion geri alınamıyor; bir sürüm @Release'e girdikten sonra oradan çıkarılamıyor.

View'ların değeri tüketici tarafında ortaya çıkıyor. Bildirim servisi, Bookstore'un contract paketlerini feed'in tamamı yerine yalnızca @Release view'ından okursa her CI run'ında üretilen 1.0.0-ci.1302 gibi prerelease sürümleri hiç görmüyor. Bir view'ın NuGet adresi feed adının yanına view adı eklenerek oluşuyor: …/_packaging/bookstore@Release/nuget/v3/index.json. Bizim akışımızda contract pipeline'ı her run'da @Local'e prerelease yayınlıyor; ekip bir sürümü entegrasyon ortamında doğrulayınca @Prerelease'e, kararlı 1.0.0'ı yayınladığında da @Release'e promote ediyor. Promotion'ı altıncı yazıdaki onaylı bir deploy stage'ine bağlamak da mümkün.

Her run'da bir prerelease üretmenin bedeli, feed'in hızla büyümesi. Feed ayarlarındaki retention policy iki değer alıyor: paket başına tutulacak en fazla sürüm sayısı ve yakın zamanda indirilmiş paketlerin kaç gün korunacağı. Belgelere göre bir sürüm ancak iki koşul birlikte sağlandığında siliniyor: sürüm sayısı sınıra ulaşmış ve o sürüm belirtilen süre içinde hiç indirilmemiş. Bir view'a promote edilmiş paketler retention'ın dışında kalıyor; kalıcı tutmak istediğin sürümleri promote etmek bu yüzden aynı zamanda bir saklama kararı. Silinen paketler 30 gün geri dönüşüm kutusunda kalıyor ve bu sürede depolama kotasından düşmeye devam ediyor.

Roadmap Notu: Eski Artifacts Task'ları

Azure DevOps roadmap'inde "Deprecate old Azure Artifacts tasks in Azure Pipelines and default to new, auth-only tasks" başlıklı bir madde var. Microsoft orada NuGet, npm ve Maven için içinde komut satırı aracı taşıyan eski, büyük task'ların sorun giderme ve güncel tutma zorluklarını anlatıyor ve yalnızca kimlik doğrulayan task'ları (NuGet Authenticate, Maven Authenticate, Python Pip Authenticate, Python Twine Upload Authenticate) öneriyor; asıl işi senin yazdığın komutlar yapıyor. Maddede eski task'ların ileride deprecate edileceği yazıyor ama bir tarih verilmiyor; 15 Kasım 2026 itibarıyla roadmap'in zaman çizelgesi de bu maddeyi tarihsiz "Future" bölümünde gösteriyor. Aynı bölümde, promotion'ı pipeline'dan yapacak bir "Package promote task" maddesi de var. Npm@1 task'ının belgesi ise task'ın artık aktif geliştirilmediğini, yalnızca kritik hataların düzeltildiğini ve Azure Artifacts için npmAuthenticate@0'ın kullanılmasını söylüyor. Bu yazıdaki pipeline'lar zaten bu yaklaşımla yazıldı: task yalnızca kimlik veriyor, restore, pack ve push düz dotnet ve npm komutları.

Üretimde Dikkat

Feed'e erişim kesintisinin etkisini hesaba kat. CI bütün paketleri tek bir feed üzerinden aldığında, feed'in erişilemez olduğu bir an bütün pipeline'ları durduruyor. Kaydedilmiş paketler bu riski azaltıyor ama yeni eklenen bir bağımlılık ilk kez indirilirken upstream'e ihtiyaç duyuyor. Cache, aynı paketleri feed'e sormadan geri yüklediği için bu durumda ikinci bir koruma sağlıyor.

İzinleri en dar hâliyle başlat. Yalnızca contract pipeline'ının paket yayınlaması gerekiyor; CI pipeline'ının kimliğine Contributor vermek, bir pull request'in değiştirdiği bir script'in feed'e paket yüklemesine kapı açıyor. Yayın yapan pipeline'ı ayrı tutup yalnızca main'den çalıştırmak, sekizinci yazıdaki güvenlik ayarlarıyla birlikte bu riski küçültüyor.

Depolama tüketimini izle. Belgelere göre Azure Artifacts'in ücretsiz katmanı 2 GiB depolama veriyor, aşan kısım tüketime göre faturalanıyor ve kullanım Organization settings altındaki Storage sayfasında görülüyor. Her run'da üretilen prerelease paketler, upstream'den kaydedilen kopyalar ve geri dönüşüm kutusundaki paketler bu kullanıma ekleniyor. Retention policy'yi feed'i oluşturduğun gün ayarlamak, birkaç ay sonra binlerce sürümle uğraşmaktan kolay.

Sık Düşülen Tuzaklar

Aynı sürümü ikinci kez yayınlamaya çalışmak. Giriş bölümündeki durum: silmek de işe yaramıyor, sürüm numarası kalıcı olarak ayrılmış durumda. Sürümü pipeline'da her run için benzersiz üret, kararlı sürümleri ise bilinçli bir adımla çıkar.

Upstream'den gelen paketin feed'de kalıcı olduğunu unutmak. Kaydedilen bir sürüm, genel kaynaktan kaldırılsa bile feed'den gelmeye devam ediyor. Sorunlu bir sürüm duyurusu geldiğinde yalnızca kendi bağımlılıklarını güncellemek yetmiyor; feed'deki kopyayı da silmek gerekebiliyor.

Feed adresini her yere yazmak. Adres bir pipeline'ın script'inde, bir başkasının config'inde ve bir Dockerfile'da ayrı ayrı yazılırsa, feed taşındığında ya da organization-scoped bir feed'e geçildiğinde bunların hepsini bulmak gerekiyor. Adres tek bir config dosyasında dursun, pipeline'lar ona yalnızca source adıyla (bookstore) başvursun.

Config'te birden fazla source bırakmak. <clear /> olmadan, makinedeki genel NuGet config'inden gelen nuget.org da listede kalıyor ve restore, feed'i atlayıp paketi doğrudan nuget.org'dan alabiliyor. Bu durumda upstream'in sağladığı tek kaynak ve kayıt avantajı kayboluyor.

Build kimliğine yetki vermeyi unutmak. Feed'e yayın yapan ilk run genellikle 403 ile kırılıyor, çünkü varsayılan rol Collaborator. Hata mesajındaki kimlik adına bakıp doğru kimliğe (project ya da organization build service) Contributor vermek gerekiyor.

Küçük Bir Deneme

★☆☆ Bookstore.Contracts'a bir README.md ekleyip PackageReadmeFile ile pakete koy; dotnet pack çıktısındaki uyarının kaybolduğunu gör.

★★☆ Bir tüketici projesi için iki nuget.config yaz: biri feed'in tamamına, diğeri yalnızca bookstore@Release view'ına bakan. Prerelease bir sürümün hangisinde görüneceğini açıkla.

★★★ Contract pipeline'ına bir stage daha ekle: kararlı sürüm yayınlandığında, onay bekleyip paketi REST API ile @Release'e promote etsin. Hangi kimliğin bu işlemi yapacağını ve ona hangi rolü vermen gerektiğini yaz.

Build tarafı tamamlandı: kod derleniyor, test ediliyor, paketleniyor ve paylaşılan parçalar feed'e gidiyor. Bir sonraki yazıda bu paketi ortamlara taşımaya başlayacağız: environment resource'ları, approval ve check'ler, deployment job'ları ve production'a yalnızca main'den, onayla gitmek.