dotnet-ado-publish

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dotnet-ado-publish

dotnet-ado-publish

Publishing pipelines for .NET projects in Azure DevOps: NuGet package push to Azure Artifacts and nuget.org, container image build and push to Azure Container Registry (ACR) using
Docker@2
, artifact staging with
PublishBuildArtifacts@1
and
PublishPipelineArtifact@1
, and pipeline artifacts for multi-stage release pipelines.
Version assumptions:
DotNetCoreCLI@2
for pack/push operations.
Docker@2
for container image builds.
NuGetCommand@2
for NuGet push to external feeds.
PublishPipelineArtifact@1
(preferred over
PublishBuildArtifacts@1
).
针对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
)。

Scope

适用范围

  • NuGet package push to Azure Artifacts and nuget.org
  • Container image build and push to ACR using Docker@2
  • Artifact staging with PublishPipelineArtifact@1
  • Pipeline artifacts for multi-stage release pipelines
  • 将NuGet包推送至Azure Artifacts和nuget.org
  • 使用Docker@2构建并推送容器镜像至ACR
  • 通过PublishPipelineArtifact@1进行工件暂存
  • 为多阶段发布管道提供管道工件支持

Out of scope

不适用范围

  • Container image authoring (Dockerfile, base image selection) -- see [skill:dotnet-containers]
  • Native AOT MSBuild configuration -- see [skill:dotnet-native-aot]
  • CLI release pipelines -- see [skill:dotnet-cli-release-pipeline]
  • Starter CI templates -- see [skill:dotnet-add-ci]
  • GitHub Actions publishing -- see [skill:dotnet-gha-publish]
  • ADO-unique features (environments, service connections) -- see [skill:dotnet-ado-unique]
Cross-references: [skill:dotnet-containers] for container image authoring and SDK container properties, [skill:dotnet-native-aot] for AOT publish configuration in CI, [skill:dotnet-cli-release-pipeline] for CLI-specific release automation, [skill:dotnet-add-ci] for starter publish templates.

  • 容器镜像编写(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]
  • Azure DevOps独有功能(环境、服务连接)——详见[skill:dotnet-ado-unique]
交叉参考:容器镜像编写和SDK容器属性详见[skill:dotnet-containers],CI中的AOT发布配置详见[skill:dotnet-native-aot],CLI专属发布自动化详见[skill:dotnet-cli-release-pipeline],入门发布模板详见[skill:dotnet-add-ci]。

NuGet Push to Azure Artifacts

推送NuGet包至Azure Artifacts

Push with
DotNetCoreCLI@2

使用
DotNetCoreCLI@2
推送

yaml
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'
yaml
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'

Version from Git Tag

从Git Tag获取版本号

Extract the version from the triggering Git tag using a script step.
Build.SourceBranch
is a runtime variable, so use a script to parse it rather than compile-time template expressions:
yaml
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)'

通过脚本步骤从触发的Git Tag中提取版本号。
Build.SourceBranch
是运行时变量,因此需使用脚本来解析,而非编译时模板表达式:
yaml
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 Push to nuget.org

推送NuGet包至nuget.org

Push with
NuGetCommand@2

使用
NuGetCommand@2
推送

For pushing to external NuGet feeds (nuget.org), use a service connection:
yaml
- task: NuGetCommand@2
  displayName: 'Push to nuget.org'
  inputs:
    command: 'push'
    packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
    nuGetFeedType: 'external'
    publishFeedCredentials: 'NuGetOrgServiceConnection'
The service connection stores the nuget.org API key securely. Create it in Project Settings > Service Connections > NuGet.
推送至外部NuGet源(如nuget.org)时,需使用服务连接:
yaml
- task: NuGetCommand@2
  displayName: 'Push to nuget.org'
  inputs:
    command: 'push'
    packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
    nuGetFeedType: 'external'
    publishFeedCredentials: 'NuGetOrgServiceConnection'
服务连接会安全存储nuget.org的API密钥。可在项目设置 > 服务连接 > NuGet中创建。

Conditional Push (Stable vs Pre-Release)

条件推送(正式版 vs 预发布版)

yaml
- 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'
Pre-release versions (containing
-
like
1.2.3-preview.1
) go only to Azure Artifacts; stable versions go to both feeds.
yaml
- 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;正式版本则同时推送至两个源。

Skip Duplicate Packages

跳过重复包

yaml
- 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 returns HTTP 409 for duplicate package versions. Use
continueOnError: true
for idempotent pipeline reruns, or configure the feed to allow overwriting pre-release versions in Feed Settings.

yaml
- 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
,或在源设置中配置允许覆盖预发布版本。

Container Image Build and Push to ACR

构建并推送容器镜像至ACR

Docker@2
Task

Docker@2
任务

Build and push a container image to Azure Container Registry. See [skill:dotnet-containers] for Dockerfile authoring guidance:
yaml
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
构建容器镜像并推送至Azure Container Registry。Dockerfile编写指导详见[skill:dotnet-containers]:
yaml
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

Tagging Strategy

标签策略

yaml
- 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
Use semantic version tags for release images and commit SHA tags for traceability. The
latest
tag should only be applied to stable releases.
yaml
- 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 Container Publish (Dockerfile-Free)

SDK容器发布(无需Dockerfile)

Use .NET SDK container publish for projects without a Dockerfile. See [skill:dotnet-containers] for
PublishContainer
MSBuild configuration:
yaml
- 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)
对于没有Dockerfile的项目,可使用.NET SDK容器发布功能。
PublishContainer
的MSBuild配置详见[skill:dotnet-containers]:
yaml
- 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 Container Publish

Native AOT容器发布

Publish a Native AOT binary as a container image. AOT configuration is owned by [skill:dotnet-native-aot]; this shows the CI pipeline step only:
yaml
- 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'
The
runtime-deps
base image is sufficient for AOT binaries since they include the runtime. See [skill:dotnet-native-aot] for AOT MSBuild properties and [skill:dotnet-containers] for base image selection.

将Native AOT二进制文件发布为容器镜像。AOT配置由[skill:dotnet-native-aot]负责;以下仅展示CI管道步骤:
yaml
- 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]。

Artifact Staging

工件暂存

PublishPipelineArtifact@1
(Recommended)

PublishPipelineArtifact@1
(推荐)

Pipeline artifacts are the modern replacement for build artifacts, offering faster upload/download and deduplication:
yaml
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'
管道工件是构建工件的现代替代方案,提供更快的上传/下载速度和重复数据删除功能:
yaml
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
(Legacy)

PublishBuildArtifacts@1
(旧版)

Use only when integrating with classic release pipelines that require build artifacts:
yaml
- task: PublishBuildArtifacts@1
  displayName: 'Upload build artifact (legacy)'
  inputs:
    pathToPublish: '$(Build.ArtifactStagingDirectory)/app'
    artifactName: 'app'
    publishLocation: 'Container'
仅在与需要构建工件的经典发布管道集成时使用:
yaml
- task: PublishBuildArtifacts@1
  displayName: 'Upload build artifact (legacy)'
  inputs:
    pathToPublish: '$(Build.ArtifactStagingDirectory)/app'
    artifactName: 'app'
    publishLocation: 'Container'

Downloading Artifacts in Downstream Stages

在下游阶段下载工件

yaml
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"
The
download: current
keyword downloads artifacts from the current pipeline run. Use
download: pipelineName
for artifacts from a different pipeline.

yaml
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

Pipeline Artifacts for Release Pipelines

发布管道的管道工件

Multi-Stage Release with Artifact Promotion

带有工件晋升的多阶段发布

yaml
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"
yaml
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"

Cross-Pipeline Artifact Consumption

跨管道工件消费

Consume artifacts from a different pipeline (e.g., a shared build pipeline):
yaml
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"

从其他管道(如共享构建管道)消费工件:
yaml
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"

Agent Gotchas

代理注意事项

  1. Use
    PublishPipelineArtifact@1
    over
    PublishBuildArtifacts@1
    -- pipeline artifacts are faster, support deduplication, and work with multi-stage YAML pipelines; build artifacts are legacy and required only for classic release pipelines.
  2. Azure Artifacts returns 409 for duplicate package versions -- use
    continueOnError: true
    for idempotent reruns, or handle duplicates in feed settings by allowing pre-release version overwrites.
  3. NuGetCommand@2
    with
    external
    feed type requires a service connection
    -- do not hardcode API keys in pipeline YAML; create a NuGet service connection in Project Settings that stores the key securely.
  4. SDK container publish requires Docker on the agent --
    dotnet publish
    with
    PublishProfile=DefaultContainer
    needs Docker; hosted
    ubuntu-latest
    agents include Docker, but self-hosted agents may not.
  5. AOT publish requires matching RID --
    dotnet publish -r linux-x64
    must match the agent OS; do not use
    -r win-x64
    on a Linux agent.
  6. download: current
    uses
    $(Pipeline.Workspace)
    not
    $(Build.ArtifactStagingDirectory)
    -- artifacts downloaded in deployment jobs are at
    $(Pipeline.Workspace)/artifactName
    , not the staging directory.
  7. Never hardcode registry credentials in pipeline YAML -- use Docker service connections for ACR/DockerHub authentication; service connections store credentials securely and rotate independently.
  8. Tag triggers require explicit
    tags.include
    in the trigger section
    -- tags are not included by default CI triggers; add
    tags: include: ['v*']
    to trigger on version tags.
  1. 优先使用
    PublishPipelineArtifact@1
    而非
    PublishBuildArtifacts@1
    ——管道工件速度更快,支持重复数据删除,适用于多阶段YAML管道;构建工件属于旧版功能,仅在与经典发布管道集成时才需要使用。
  2. Azure Artifacts对重复包版本返回409状态码——对于支持幂等性的重跑,可使用
    continueOnError: true
    ,或在源设置中配置允许覆盖预发布版本。
  3. 使用
    NuGetCommand@2
    推送至外部源需要服务连接
    ——请勿在管道YAML中硬编码API密钥;应在项目设置中创建NuGet服务连接,安全存储密钥。
  4. SDK容器发布需要代理上安装Docker——使用
    PublishProfile=DefaultContainer
    dotnet publish
    命令依赖Docker;托管的
    ubuntu-latest
    代理已包含Docker,但自托管代理可能未安装。
  5. AOT发布需要匹配RID——
    dotnet publish -r linux-x64
    必须与代理操作系统匹配;请勿在Linux代理上使用
    -r win-x64
  6. download: current
    使用
    $(Pipeline.Workspace)
    而非
    $(Build.ArtifactStagingDirectory)
    ——部署作业中下载的工件存储在
    $(Pipeline.Workspace)/artifactName
    ,而非暂存目录。
  7. 切勿在管道YAML中硬编码注册表凭据——应使用Docker服务连接进行ACR/DockerHub认证;服务连接会安全存储凭据并独立轮换。
  8. Tag触发需要在trigger部分显式设置
    tags.include
    ——默认CI触发器不包含Tag;需添加
    tags: include: ['v*']
    来触发版本Tag对应的管道运行。