Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
40 changes: 40 additions & 0 deletions eng/pipelines/common/templates/jobs/approval-job.yml
Copy link
Contributor

Choose a reason for hiding this comment

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

We seem to have two "jobs" directories, and it's not clear which jobs belong where. Just food for thought as we re-write the pipelines. It's hard to tell which files are used by which pipelines when they're all smushed into the same tree.

Copy link
Contributor

Choose a reason for hiding this comment

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

I apologize for this. I made the new folders to separate the new (and model) pipelines from the old ones. For new (and properly built) pipelines, put stuff in eng/pipelines/jobs. Old stuff stays in eng/pipelines/common/templates/jobs.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#################################################################################
# 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: approvalAliases
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be documenting all parameters. Nothing fancy, just a sentence or two about what a parameter does.

type: string
- name: publishDestination
type: string
- name: dryRun
type: boolean
- name: isPreview
type: boolean
- name: publishSymbols
type: boolean
- name: nugetPackageVersion
type: string
- name: product
type: string

jobs:
- job: AwaitApproval
displayName: 'Await Release Approval'
pool: server
steps:
- task: ManualValidation@0
displayName: 'Manual Approval'
timeoutInMinutes: 4320
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.
60 changes: 60 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,60 @@
#################################################################################
# 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: publishDestination
type: string
- name: dryRun
type: boolean
- name: internalFeedSource
type: string
- name: publicNuGetSource
type: string
- name: publishSymbols
type: boolean
- name: packageFolderName
type: string
- name: nugetPackageVersion
type: string
- name: product
type: string

jobs:
- job: PublishPackages
displayName: 'Publish Packages'
dependsOn: AwaitApproval
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the approval step just live in this file? Is approval really a general task? It seems pretty specific to publishing packages.

condition: succeeded()
pool:
vmImage: 'ubuntu-latest'
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Signed Packages'
inputs:
buildType: current
artifactName: ${{ parameters.packageFolderName }}
targetPath: $(Pipeline.Workspace)/release/packages
- script: |
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it also be helpful to list the contents of targetPath from the previous step?

echo "NuGet Package Version: ${{ parameters.nugetPackageVersion }}"
displayName: 'Echo NuGet Package Version'
- template: ../steps/list-packages-step.yml
parameters:
path: $(Pipeline.Workspace)/release/packages
- ${{ if ne(parameters.publishDestination, 'Public') }}:
- template: ../steps/publish-internal-feed-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
internalFeedSource: ${{ parameters.internalFeedSource }}
- ${{ if eq(parameters.publishDestination, 'Public') }}:
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer #{{ else }} ?

I would probably swap this to be:

${{ if eq(parameters.publishDestination, 'Public') }}:
  # Do public stuff.
${{ else }}:
  # Do non-public stuff.

- template: ../steps/publish-public-nuget-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
publicNuGetSource: ${{ parameters.publicNuGetSource }}
- ${{ if parameters.publishSymbols }}:
- template: ../steps/publish-symbols-step.yml
parameters:
dryRun: ${{ parameters.dryRun }}
publishSymbols: ${{ parameters.publishSymbols }}
symbolsArtifactName: ${{ parameters.product }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.nugetPackageVersion }}_$(System.TimelineId)
product: ${{ parameters.product }}
53 changes: 53 additions & 0 deletions eng/pipelines/common/templates/stages/release-stage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#################################################################################
# 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: runRelease
type: boolean
- name: publishDestination
type: string
- name: dryRun
type: boolean
- name: approvalAliases
type: string
- name: internalFeedSource
type: string
- name: publicNuGetSource
type: string
- name: publishSymbols
type: boolean
- name: isPreview
type: boolean
- name: product
type: string
- name: nugetPackageVersion
type: string
- 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/approval-job.yml
parameters:
approvalAliases: ${{ parameters.approvalAliases }}
publishDestination: ${{ parameters.publishDestination }}
dryRun: ${{ parameters.dryRun }}
isPreview: ${{ parameters.isPreview }}
publishSymbols: ${{ parameters.publishSymbols }}
nugetPackageVersion: ${{ parameters.nugetPackageVersion }}
product: ${{ parameters.product }}
- template: ../jobs/publish-packages-job.yml
parameters:
publishDestination: ${{ parameters.publishDestination }}
dryRun: ${{ parameters.dryRun }}
internalFeedSource: ${{ parameters.internalFeedSource }}
publicNuGetSource: ${{ parameters.publicNuGetSource }}
publishSymbols: ${{ parameters.publishSymbols }}
packageFolderName: ${{ parameters.packageFolderName }}
nugetPackageVersion: ${{ parameters.nugetPackageVersion }}
product: ${{ parameters.product }}
14 changes: 14 additions & 0 deletions eng/pipelines/common/templates/steps/list-packages-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#################################################################################
# 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: path
type: string

steps:
- script: |
echo "Packages in ${{ parameters.path }}:"
find "${{ parameters.path }}" -name "*.nupkg" -maxdepth 3
displayName: 'List Packages'
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#################################################################################
# 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
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

[nitpick] Missing default value for required parameter. While dryRun parameter has a type of boolean, it lacks a default value. For consistency with other templates in this PR and to prevent errors when the parameter is not explicitly provided, consider adding default: false.

Suggested change
type: boolean
type: boolean
default: false

Copilot uses AI. Check for mistakes.
- name: internalFeedSource
type: string
- name: packagesGlob
type: string
default: '$(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg'
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

The default value for packagesGlob is hardcoded to an MDS-specific path $(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg. This should not have a default value with 'MDS' hardcoded, as this template is intended to be reusable for all product types (MDS, MSS, AKV). Consider removing the default or making it generic.

Suggested change
default: '$(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg'

Copilot uses AI. Check for mistakes.

steps:
- script: |
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we start putting these elaborate PowerShell scripts into files so we can:

  • Read them easily (syntax highlighting etc).
  • Run them (i.e. to test that they work).
  • Version them separately from the pipeline files.

set -e
SRC='${{ parameters.internalFeedSource }}'
if [ -z "$SRC" ]; then
echo "Internal feed source parameter not set."
exit 1
fi
if [ "${{ parameters.dryRun }}" = "true" ]; then
echo "[DRY RUN] Would push to $SRC"
find "${{ parameters.packagesGlob }}" -name "*.nupkg"
exit 0
fi
for f in $(find "${{ parameters.packagesGlob }}" -name "*.nupkg"); do
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

The find command is incorrectly used here. The first argument should be a directory path, not a glob pattern. This will fail when the script tries to execute. Consider using: find "$(dirname '${{ parameters.packagesGlob }}')" -type f -name "$(basename '${{ parameters.packagesGlob }}')" or a simpler approach with shell globbing like for f in ${{ parameters.packagesGlob }}; do.

Suggested change
for f in $(find "${{ parameters.packagesGlob }}" -name "*.nupkg"); do
for f in ${{ parameters.packagesGlob }}; do

Copilot uses AI. Check for mistakes.
echo "Push $f"
dotnet nuget push --source "$SRC" --api-key az "$f"
done
Comment on lines +42 to +45
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

The dry run logic only prints files but doesn't skip the actual push operation. When dryRun is true, the script should exit after listing files, or the push loop (lines 40-43) should be conditionally executed only when dryRun is false. Currently, packages will be pushed even in dry run mode.

Suggested change
for f in $(find "${{ parameters.packagesGlob }}" -name "*.nupkg"); do
echo "Push $f"
dotnet nuget push --source "$SRC" --api-key az "$f"
done
if [ "${{ parameters.dryRun }}" != "true" ]; then
for f in $(find "${{ parameters.packagesGlob }}" -name "*.nupkg"); do
echo "Push $f"
dotnet nuget push --source "$SRC" --api-key az "$f"
done
fi

Copilot uses AI. Check for mistakes.
displayName: 'Publish to Internal Feed'
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#################################################################################
# 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
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

[nitpick] Missing default value for required parameter. While dryRun parameter has a type of boolean, it lacks a default value. For consistency with other templates in this PR and to prevent errors when the parameter is not explicitly provided, consider adding default: false.

Suggested change
type: boolean
type: boolean
default: false

Copilot uses AI. Check for mistakes.
- name: publicNuGetSource
type: string
- name: nugetServiceConnection
type: string
default: 'ADO Nuget Org Connection'
- name: packagesGlob
type: string
default: '$(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg'

Comment on lines +19 to +20
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

The default value for packagesGlob is hardcoded to an MDS-specific path $(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg. This should not have a default value with 'MDS' hardcoded, as this template is intended to be reusable for all product types (MDS, MSS, AKV). Consider removing the default or making it generic.

Suggested change
default: '$(System.DefaultWorkingDirectory)/_dotnet-sqlclient-Official/drop_buildMDS_build_signed_package/*.nupkg'

Copilot uses AI. Check for mistakes.
steps:
- task: NuGetToolInstaller@1
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd be surprised if the images we use don't have NuGet installed already.

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
# Limit depth to avoid traversing unrelated trees; adjust if patterns intentionally include deeper globs
find "$dir" -maxdepth 2 -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