Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should be locked down. In what situation would we want to allow non-admins to approve?


# 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 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
# Else Push to Internal Feed
- ${{ else }}:
- template: ../steps/publish-internal-feed-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
internalFeedSource: ${{ parameters.internalFeedSource }}
packagesGlob: ${{ variables.targetDownloadPath }}/*.nupkg
# Publish Symbols if publishSymbols is true and is not a dry run
- ${{ if and(parameters.publishSymbols, eq(parameters.dryRun, false)) }}:
- 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