Skip to content
Open
107 changes: 107 additions & 0 deletions eng/pipelines/common/templates/jobs/publish-packages-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
# Approval aliases for manual validation before publishing packages
- name: approvalAliases
type: string
default: '[ADO.Net]\\SqlClient Admins'

# Where to publish the packages: 'Internal' or 'Public' feed
- name: publishDestination
type: string

# Boolean value to indicate whether to perform a dry run or actual publish
- name: dryRun
type: boolean

# Boolean value to indicate if the build is a preview release build
- name: isPreview
type: boolean

# Internal feed source URL for publishing packages to Azure DevOps Feed
- name: internalFeedSource
type: string

# Public NuGet source URL for publishing packages to public feed
- name: publicNuGetSource
type: string

# Boolean value to indicate whether to publish symbols
- name: publishSymbols
type: boolean

# Name of the folder containing the packages to be published
- name: packageFolderName
type: string

# NuGet package version to be published
- name: nugetPackageVersion
type: string

# Product name associated with the packages
- name: product
type: string

jobs:
- job: AwaitApproval
displayName: 'Await Release Approval'
pool: server
steps:
- task: ManualValidation@0
displayName: 'Manual Approval'
timeoutInMinutes: 4320 # 3 days
inputs:
notifyUsers: ${{ parameters.approvalAliases }}
instructions: |
Release Checklist:
* Destination: ${{ parameters.publishDestination }}
* Preview build: ${{ parameters.isPreview }}
* Dry run: ${{ parameters.dryRun }}
* Symbols: ${{ parameters.publishSymbols }}
* NuGet package version: ${{ parameters.nugetPackageVersion }}
* Product: ${{ parameters.product }}
Approve to continue or Reject to abort.
- job: PublishPackages
displayName: 'Publish Packages'
variables:
- targetDownloadPath: '$(Pipeline.Workspace)/release/packages'
dependsOn: AwaitApproval
condition: succeeded()
pool:
vmImage: 'ubuntu-latest'
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Signed Packages'
inputs:
buildType: current
artifactName: ${{ parameters.packageFolderName }}
targetPath: ${{ variables.targetDownloadPath }}
- script: |
echo "NuGet Package Version: ${{ parameters.nugetPackageVersion }}"
echo "Downloaded signed packages to: ${{ variables.targetDownloadPath }}"
displayName: 'Echo NuGet Package Version'
# Push to Internal Feed if publishDestination is not 'Public'
- ${{ if ne(parameters.publishDestination, 'Public') }}:
- template: ../steps/publish-internal-feed-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
internalFeedSource: ${{ parameters.internalFeedSource }}
packagesGlob: ${{ variables.targetDownloadPath }}/*.nupkg
# Push to Public NuGet Feed if publishDestination is 'Public'
- ${{ if eq(parameters.publishDestination, 'Public') }}:
- template: ../steps/publish-public-nuget-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
publicNuGetSource: ${{ parameters.publicNuGetSource }}
packagesGlob: ${{ variables.targetDownloadPath }}/*.nupkg
# Publish Symbols if publishSymbols is true and is not a dry run
- ${{ if and(parameters.publishSymbols, ne(parameters.dryRun, true)) }}:
- template: ../steps/publish-symbols-step.yml
parameters:
publishSymbols: ${{ parameters.publishSymbols }}
symbolsArtifactName: ${{ parameters.product }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.nugetPackageVersion }}_$(System.TimelineId)
product: ${{ parameters.product }}
71 changes: 71 additions & 0 deletions eng/pipelines/common/templates/stages/release-stage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
# Boolean value to indicate whether to run the release stage
- name: runRelease
type: boolean
default: false

# Where to publish the packages: 'Internal' or 'Public' feed
- name: publishDestination
type: string

# Boolean value to indicate whether to perform a dry run or actual publish
- name: dryRun
type: boolean
default: true

# Approval aliases for manual validation before publishing packages
- name: approvalAliases
type: string

# Internal feed source URL for publishing packages to Azure DevOps Feed
- name: internalFeedSource
type: string

# Public NuGet source URL for publishing packages to public feed
- name: publicNuGetSource
type: string

# Boolean value to indicate whether to publish symbols
- name: publishSymbols
type: boolean
default: false

# Boolean value to indicate if the build is a preview release build
- name: isPreview
type: boolean

# Product name associated with the packages
- name: product
type: string

# NuGet package version to be published
- name: nugetPackageVersion
type: string

# Name of the folder containing the packages to be published
- name: packageFolderName
type: string

stages:
- stage: releaseMDS
displayName: 'Release (Manual)'
condition: and(succeeded(), eq(variables['Build.Reason'],'Manual'), eq(${{ parameters.runRelease }}, true))
jobs:
- template: ../jobs/publish-packages-job.yml
parameters:
approvalAliases: ${{ parameters.approvalAliases }}
publishDestination: ${{ parameters.publishDestination }}
dryRun: ${{ parameters.dryRun }}
isPreview: ${{ parameters.isPreview }}
internalFeedSource: ${{ parameters.internalFeedSource }}
publicNuGetSource: ${{ parameters.publicNuGetSource }}
publishSymbols: ${{ parameters.publishSymbols }}
packageFolderName: ${{ parameters.packageFolderName }}
nugetPackageVersion: ${{ parameters.nugetPackageVersion }}
product: ${{ parameters.product }}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
- name: dryRun
type: boolean
default: true

- name: internalFeedSource
type: string

- name: packagesGlob
type: string

steps:
- script: |
pwsh ./tools/scripts/publishPackagesToFeed.ps1 `
-dryRun ${{ parameters.dryRun }} `
-internalFeedSource '${{ parameters.internalFeedSource }}' `
-packagesGlob '${{ parameters.packagesGlob }}'
displayName: 'Publish to Internal Feed'
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
- name: dryRun
type: boolean
default: true

- name: publicNuGetSource
type: string

- name: nugetServiceConnection
type: string
default: 'ADO Nuget Org Connection'

- name: packagesGlob
type: string

steps:
- task: NuGetToolInstaller@1
displayName: 'Install Latest Nuget'
inputs:
checkLatest: true
- script: |
echo "[DRY RUN] Listing packages targeted for push to: ${{ parameters.publicNuGetSource }}"
echo "Using glob pattern: ${{ parameters.packagesGlob }}"
# Derive directory and filename pattern from the glob for a precise find (handles nested patterns minimally)
glob='${{ parameters.packagesGlob }}'
dir="${glob%/*}"
name="${glob##*/}"
echo "Resolved directory: $dir"
echo "Filename pattern: $name"
if [ -d "$dir" ]; then
echo "Matched files:" || true
# Print all matched files to identify what would be pushed
find "$dir" -type f -name "$name" -print || true
else
echo "Directory does not exist yet: $dir"
fi
displayName: 'Dry Run - List Packages'
condition: and(succeeded(), eq(${{ parameters.dryRun }}, true))

- task: NuGetCommand@2
displayName: 'Push to Nuget.org'
condition: and(succeeded(), eq(${{ parameters.dryRun }}, false))
inputs:
command: push
packagesToPush: '${{ parameters.packagesGlob }}'
nuGetFeedType: external
publishFeedCredentials: '${{ parameters.nugetServiceConnection }}'
Loading
Loading