dotnet-ado-publish について
Azure DevOps パイプラインで公開を行う .NET チーム向けのスキルです。NuGet パッケージ、コンテナーイメージ、ビルド成果物を対象に、Azure Artifacts、nuget.org、ACR、多段階リリースの設定を扱います。
主な機能
- 01Azure Artifacts への NuGet パッケージ公開
- 02nuget.org へのパッケージ公開
- 03ACR 向けイメージのビルドとプッシュ
- 04パイプライン成果物のステージングと公開
- 05ステージ間での成果物の受け渡し
活用シーン
- 01Azure DevOps から NuGet パッケージを公開
- 02コンテナーイメージを ACR にプッシュ
- 03多段階リリースでビルド成果物を受け渡し
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-ado-publishインストール後、Claude Code で/dotnet-ado-publishを入力して読み込まれたか確認します。
詳細
dotnet-ado-publish
Azure DevOps で .NET プロジェクトのパイプラインを公開します。NuGet パッケージを Azure Artifacts および nuget.org にプッシュし、Docker@2 を使用してコンテナーイメージをビルドして Azure Container Registry(ACR)にプッシュします。また、PublishBuildArtifacts@1 と PublishPipelineArtifact@1 による成果物のステージングや、複数ステージのリリースパイプライン用のパイプライン成果物にも対応します。
バージョンの前提条件: パック/プッシュ操作には DotNetCoreCLI@2 を使用します。コンテナーイメージのビルドには Docker@2 を使用します。外部フィードへの NuGet プッシュには NuGetCommand@2 を使用します。PublishPipelineArtifact@1 を使用します(PublishBuildArtifacts@1 より推奨)。
対象範囲: このスキルは、Azure DevOps からの成果物公開パイプラインを担当します。コンテナーイメージの作成(Dockerfile のベストプラクティス、SDK コンテナーのプロパティ)は [skill:dotnet-containers] が担当します。Native AOT コンパイルの構成は [skill:dotnet-native-aot] が担当します。このスキルでは CI パイプライン構成における AOT のみを扱います。CLI 固有のリリースパイプラインは [skill:dotnet-cli-release-pipeline] が担当します。スターター CI テンプレートは [skill:dotnet-add-ci] が担当します。
対象外: コンテナーイメージの作成(Dockerfile、ベースイメージの選択)については、[skill:dotnet-containers] を参照してください。Native AOT の MSBuild 構成については、[skill:dotnet-native-aot] を参照してください。CLI リリースパイプラインについては、[skill:dotnet-cli-release-pipeline] を参照してください。スターター CI テンプレートについては、[skill:dotnet-add-ci] を参照してください。GitHub Actions による公開については、[skill:dotnet-gha-publish] を参照してください。ADO 固有の機能(環境、サービス接続)については、[skill:dotnet-ado-unique] を参照してください。
相互参照:[skill:dotnet-containers] ではコンテナーイメージの作成と SDK コンテナーのプロパティ、[skill:dotnet-native-aot] では CI での AOT 公開構成、[skill:dotnet-cli-release-pipeline] では CLI 固有のリリース自動化、[skill:dotnet-add-ci] ではスターター公開テンプレートについて説明しています。
Azure Artifacts への NuGet プッシュ
DotNetCoreCLI@2 によるプッシュ
trigger:
tags:
include:
- 'v*'
stages:
- stage: Pack
jobs:
- job: PackJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DotNetCoreCLI@2
displayName: 'Pack'
inputs:
command: 'pack'
packagesToPack: 'src/**/*.csproj'
configuration: 'Release'
outputDir: '$(Build.ArtifactStagingDirectory)/nupkgs'
versioningScheme: 'byEnvVar'
versionEnvVar: 'PACKAGE_VERSION'
env:
PACKAGE_VERSION: $(Build.SourceBranchName)
- task: PublishPipelineArtifact@1
displayName: 'Upload NuGet packages'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/nupkgs'
artifactName: 'nupkgs'
- stage: PushToFeed
dependsOn: Pack
jobs:
- job: PushJob
pool:
vmImage: 'ubuntu-latest'
steps:
- download: current
artifact: nupkgs
- task: NuGetAuthenticate@1
displayName: 'Authenticate NuGet'
- task: DotNetCoreCLI@2
displayName: 'Push to Azure Artifacts'
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: 'MyProject/MyFeed'
Git タグからバージョンを取得
スクリプトステップを使用して、トリガーとなった Git タグからバージョンを抽出します。Build.SourceBranch は実行時変数なので、コンパイル時のテンプレート式ではなくスクリプトで解析してください。
steps:
- script: |
set -euo pipefail
if [[ "$(Build.SourceBranch)" == refs/tags/v* ]]; then
VERSION="${BUILD_SOURCEBRANCH#refs/tags/v}"
else
VERSION="0.0.0-ci.$(Build.BuildId)"
fi
echo "##vso[task.setvariable variable=packageVersion]$VERSION"
displayName: 'Extract version from tag'
- task: DotNetCoreCLI@2
displayName: 'Pack'
inputs:
command: 'pack'
packagesToPack: 'src/**/*.csproj'
configuration: 'Release'
outputDir: '$(Build.ArtifactStagingDirectory)/nupkgs'
arguments: '-p:Version=$(packageVersion)'
nuget.org への NuGet プッシュ
NuGetCommand@2 によるプッシュ
外部の NuGet フィード(nuget.org)にプッシュする場合は、サービス接続を使用してください。
- task: NuGetCommand@2
displayName: 'Push to nuget.org'
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'NuGetOrgServiceConnection'
サービス接続には、nuget.org の API キーが安全に保存されます。Project Settings > Service Connections > NuGet で作成してください。
条件付きプッシュ(安定版とプレリリース)
- task: NuGetCommand@2
displayName: 'Push to nuget.org (stable only)'
condition: and(succeeded(), not(contains(variables['packageVersion'], '-')))
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'NuGetOrgServiceConnection'
- task: DotNetCoreCLI@2
displayName: 'Push to Azure Artifacts (all versions)'
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: 'MyProject/MyFeed'
プレリリースバージョン(1.2.3-preview.1 のように - を含むもの)は Azure Artifacts にのみ送信され、安定版は両方のフィードに送信されます。
重複パッケージをスキップする
- task: DotNetCoreCLI@2
displayName: 'Push (skip duplicates)'
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: 'MyProject/MyFeed'
continueOnError: true # Azure Artifacts returns 409 for duplicates
Azure Artifacts は、パッケージバージョンが重複すると HTTP 409 を返します。パイプラインの再実行を冪等にするには continueOnError: true を使用するか、フィード設定でプレリリースバージョンの上書きを許可してください。
ACR へのコンテナーイメージのビルドとプッシュ
Docker@2 タスク
Azure Container Registry にコンテナーイメージをビルドしてプッシュします。Dockerfile の作成方法については、[skill:dotnet-containers] を参照してください。
stages:
- stage: BuildContainer
jobs:
- job: DockerBuild
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
displayName: 'Login to ACR'
inputs:
command: 'login'
containerRegistry: 'MyACRServiceConnection'
- task: Docker@2
displayName: 'Build and push'
inputs:
command: 'buildAndPush'
repository: 'myapp'
containerRegistry: 'MyACRServiceConnection'
dockerfile: 'src/MyApp/Dockerfile'
buildContext: '.'
tags: |
$(Build.BuildId)
latest
タグ付け戦略
- task: Docker@2
displayName: 'Build and push with semver tags'
inputs:
command: 'buildAndPush'
repository: 'myapp'
containerRegistry: 'MyACRServiceConnection'
dockerfile: 'src/MyApp/Dockerfile'
buildContext: '.'
tags: |
$(packageVersion)
$(Build.SourceVersion)
latest
リリースイメージにはセマンティックバージョンタグを使用し、追跡性を確保するにはコミット SHA タグを使用します。latest タグは安定版のリリースにのみ適用してください。
SDK コンテナーの公開(Dockerfile 不要)
Dockerfile を使用しないプロジェクトでは、.NET SDK によるコンテナー公開を使用します。PublishContainer の MSBuild 構成については、[skill:dotnet-containers] を参照してください。
- task: Docker@2
displayName: 'Login to ACR'
inputs:
command: 'login'
containerRegistry: 'MyACRServiceConnection'
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- script: |
dotnet publish src/MyApp/MyApp.csproj \
-c Release \
-p:PublishProfile=DefaultContainer \
-p:ContainerRegistry=$(ACR_LOGIN_SERVER) \
-p:ContainerRepository=myapp \
-p:ContainerImageTags='"$(packageVersion);latest"'
displayName: 'Publish container via SDK'
env:
ACR_LOGIN_SERVER: $(acrLoginServer)
Native AOT コンテナーの公開
Native AOT バイナリをコンテナーイメージとして公開します。AOT の構成は [skill:dotnet-native-aot] が担当します。ここでは CI パイプラインのステップのみを示します。
- script: |
dotnet publish src/MyApp/MyApp.csproj \
-c Release \
-r linux-x64 \
-p:PublishAot=true \
-p:PublishProfile=DefaultContainer \
-p:ContainerRegistry=$(ACR_LOGIN_SERVER) \
-p:ContainerRepository=myapp \
-p:ContainerBaseImage=mcr.microsoft.com/dotnet/runtime-deps:8.0-noble-chiseled \
-p:ContainerImageTags='"$(packageVersion)"'
displayName: 'Publish AOT container'
AOT バイナリにはランタイムが含まれるため、runtime-deps ベースイメージで十分です。AOT の MSBuild プロパティについては [skill:dotnet-native-aot] を、ベースイメージの選択については [skill:dotnet-containers] を参照してください。
成果物のステージング
PublishPipelineArtifact@1(推奨)
パイプライン成果物はビルド成果物に代わる最新の方式であり、アップロード/ダウンロードの高速化と重複排除に対応しています。
steps:
- task: DotNetCoreCLI@2
displayName: 'Publish app'
inputs:
command: 'publish'
projects: 'src/MyApp/MyApp.csproj'
arguments: '-c Release -o $(Build.ArtifactStagingDirectory)/app'
- task: PublishPipelineArtifact@1
displayName: 'Upload app artifact'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/app'
artifactName: 'app'
- task: PublishPipelineArtifact@1
displayName: 'Upload NuGet packages'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/nupkgs'
artifactName: 'nupkgs'
PublishBuildArtifacts@1(レガシー)
ビルド成果物を必要とするクラシックリリースパイプラインと統合する場合にのみ使用します。
- task: PublishBuildArtifacts@1
displayName: 'Upload build artifact (legacy)'
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)/app'
artifactName: 'app'
publishLocation: 'Container'
後続ステージでの成果物のダウンロード
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- script: dotnet publish -c Release -o $(Build.ArtifactStagingDirectory)/app
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/app'
artifactName: 'app'
- stage: Deploy
dependsOn: Build
jobs:
- deployment: DeployJob
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- script: echo "Deploying from $(Pipeline.Workspace)/app"
download: current キーワードは、現在のパイプライン実行から成果物をダウンロードします。別のパイプラインの成果物には、download: pipelineName を使用します。
リリースパイプライン向けパイプライン成果物
成果物の昇格を伴うマルチステージリリース
trigger:
tags:
include:
- 'v*'
stages:
- stage: Build
jobs:
- job: BuildAndPack
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: 'MyApp.sln'
arguments: '-c Release'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
projects: 'src/MyApp/MyApp.csproj'
arguments: '-c Release -o $(Build.ArtifactStagingDirectory)/app'
- task: DotNetCoreCLI@2
displayName: 'Pack'
inputs:
command: 'pack'
packagesToPack: 'src/MyLibrary/MyLibrary.csproj'
configuration: 'Release'
outputDir: '$(Build.ArtifactStagingDirectory)/nupkgs'
- task: PublishPipelineArtifact@1
displayName: 'Upload app'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/app'
artifactName: 'app'
- task: PublishPipelineArtifact@1
displayName: 'Upload packages'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/nupkgs'
artifactName: 'nupkgs'
- stage: DeployStaging
dependsOn: Build
jobs:
- deployment: DeployStaging
environment: 'staging'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- script: echo "Deploying to staging from $(Pipeline.Workspace)/app"
- stage: PublishPackages
dependsOn: DeployStaging
jobs:
- job: PushPackages
pool:
vmImage: 'ubuntu-latest'
steps:
- download: current
artifact: nupkgs
- task: NuGetAuthenticate@1
- task: NuGetCommand@2
displayName: 'Push to nuget.org'
inputs:
command: 'push'
packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'NuGetOrgServiceConnection'
- stage: DeployProduction
dependsOn: DeployStaging
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProduction
environment: 'production'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- script: echo "Deploying to production from $(Pipeline.Workspace)/app"
パイプライン間での成果物の利用
別のパイプライン(例:共有ビルドパイプライン)の成果物を利用します。
resources:
pipelines:
- pipeline: buildPipeline
source: 'MyApp-Build'
trigger:
branches:
include:
- main
stages:
- stage: Deploy
jobs:
- deployment: DeployFromBuild
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- download: buildPipeline
artifact: app
- script: echo "Deploying from $(Pipeline.Workspace)/buildPipeline/app"
エージェントに関する注意点
PublishBuildArtifacts@1よりPublishPipelineArtifact@1を使用する -- パイプライン成果物は高速で、重複排除に対応し、マルチステージ YAML パイプラインで動作します。ビルド成果物はレガシーであり、クラシックリリースパイプラインでのみ必要です。- Azure Artifacts はパッケージバージョンが重複すると 409 を返す -- 冪等な再実行には
continueOnError: trueを使用するか、フィードの設定でプレリリースバージョンの上書きを許可して重複を処理します。 NuGetCommand@2でフィードタイプexternalを使用するには、サービス接続が必要 -- パイプライン YAML に API キーをハードコードしないでください。Project Settings で NuGet サービス接続を作成し、キーを安全に保管します。- SDK コンテナーの発行にはエージェント上の Docker が必要 --
PublishProfile=DefaultContainerを指定したdotnet publishには Docker が必要です。ホスト型のubuntu-latestエージェントには Docker が含まれていますが、セルフホスト型エージェントには含まれていない場合があります。 - AOT 発行では一致する RID が必要 --
dotnet publish -r linux-x64はエージェントの OS と一致させる必要があります。Linux エージェントで-r win-x64を使用しないでください。 download: currentは$(Build.ArtifactStagingDirectory)ではなく$(Pipeline.Workspace)を使用する -- デプロイジョブでダウンロードされた成果物は、ステージングディレクトリではなく$(Pipeline.Workspace)/artifactNameにあります。- レジストリの認証情報をパイプライン YAML にハードコードしない -- ACR/DockerHub 認証には Docker サービス接続を使用します。サービス接続では認証情報を安全に保管し、個別にローテーションできます。
- タグトリガーにはトリガーセクションで明示的な
tags.includeが必要 -- タグは既定の CI トリガーには含まれません。バージョンタグでトリガーするには、tags: include: ['v*']を追加します。