This is possible via manual approval steps as mentioned by @Blue_Clouds and described in detail here;
https://samlearnsazure.blog/2020/02/05/approvals-in-environments/
In my case I am building a nuget package to two different feeds, a pre-release feed with nuget packages built in DEBUG and when approved that same package with the same version number is built in RELEASE configuration and deployed to the main release feed.
Now developers can reference a package from the pre-release feed and debug all the way into the nuget package, and release packages with optimised code can be used for building the production deployable code.
These are the high level steps.
The process involves doing the following, In Azure DevOps
- Create an environment (for me I called it NugetRelease)
- Open the environment and in the elispses ... choose "Approvals and Checks"
- Add a new approval
- Add a user or group for approval. I added the "Project Administrators" group as this will allow your admins to approve.
Now you need to connect that environment to your release via a stages deployment.
Here is the main part of the "release" section of the .yaml file
- stage: Release
jobs:
- deployment: Build_Release_Deploy
displayName: Build and Deploy Release Package
environment: NugetRelease
pool:
vmImage: 'windows-latest'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: DotNetCoreCLI@2
displayName: DotNet Restore
inputs:
command: 'restore'
projects: '**/MyProject.sln'
feedsToUse: 'select'
vstsFeed: '40a781fa-22c1-xxxx-xxxx-xxxxxxxxxxxx/9b2782f5-76e8-xxxx-xxxx-xxxxxxxxxxxx'
This is what it looks like;
Published to Pre-Release feed and awaiting approval for Release deployment
Review
Approval
Deployed
Here is a full example of a pipeline that would run to create and publish a nuget package to a pre-release feed and then a release package to a release feed on approval. In my example the file structure is as follows:
- src
- MySolution
- MySolution.sln
Azure devops build pipeline
trigger:
- main
# the build will run on a Microsoft hosted agent, using the lastest Windows VM Image
pool:
vmImage: 'windows-latest'
variables:
majorMinor: 2.0
name: $(majorMinor)$(rev:.r)
stages:
- stage: PreRelease
jobs:
- job: Build_PreRelease
displayName: Build PreRelease
steps:
- task: DotNetCoreCLI@2
displayName: DotNet Restore
inputs:
command: 'restore'
projects: '**/MySolution.sln'
feedsToUse: 'select'
vstsFeed: '40a781fa-xxxx-xxxx-xxxx-xxxxxxxxxxxx/d7b8f0fe-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
- task: SnykSecurityScan@1
inputs:
serviceConnectionEndpoint: 'Snyk Security'
testType: 'app'
targetFile: 'src/MySolution.sln'
monitorWhen: 'always'
failOnIssues: true
- task: DotNetCoreCLI@2
displayName: 'DotNet Build'
inputs:
command: 'build'
arguments: '--configuration Debug'
projects: '**/MySolution/MySolution.csproj'
- job: PackageDeploy_PreRelease
displayName: Package and Deploy PreRelease
dependsOn: Build_PreRelease
condition: succeeded()
steps:
#package
- task: DotNetCoreCLI@2
displayName: 'DotNet Pack'
inputs:
command: 'pack'
packagesToPack: '**/MySolution/MySolution.csproj'
versioningScheme: byEnvVar
versionEnvVar: BUILD_BUILDNUMBER
#push
- task: DotNetCoreCLI@2
displayName: "NuGet Push"
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: '40a781fa-xxxx-xxxx-xxxx-xxxxxxxxxxxx/d7b8f0fe-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
#publish
- task: PublishBuildArtifacts@1
displayName: "Publish Artifact"
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
TargetPath: '\\MySolution\$(Build.DefinitionName)\$(Build.BuildNumber)'
publishLocation: 'Container'
- stage: Release
jobs:
- deployment: Build_Release_Deplpy
displayName: Build and Deploy Release Package
environment: NugetRelease
pool:
vmImage: 'windows-latest'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: PowerShell@2
displayName: 'Echo Version'
inputs:
targetType: inline
script: echo $(Build.BuildNumber)
- task: DotNetCoreCLI@2
displayName: DotNet Restore
inputs:
command: 'restore'
projects: '**/MySolution.sln'
feedsToUse: 'select'
vstsFeed: '40a781fa-xxxx-xxxx-xxxx-xxxxxxxxxxxx/9b2782f5-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
- task: SnykSecurityScan@1
inputs:
serviceConnectionEndpoint: 'Snyk Security'
testType: 'app'
targetFile: 'src/MySolution.sln'
monitorWhen: 'always'
failOnIssues: true
- task: DotNetCoreCLI@2
displayName: 'DotNet Build'
inputs:
command: 'build'
arguments: '--configuration Release'
projects: '**/MySolution/MySolution.csproj'
#package
- task: DotNetCoreCLI@2
displayName: 'DotNet Pack'
inputs:
command: 'pack'
packagesToPack: '**/MySolution/MySolution.csproj'
versioningScheme: byEnvVar
versionEnvVar: BUILD_BUILDNUMBER
#push
- task: DotNetCoreCLI@2
displayName: "NuGet Push"
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: '40a781fa-xxxx-xxxx-xxxx-xxxxxxxxxxxx/9b2782f5-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
#publish
- task: PublishBuildArtifacts@1
displayName: "Publish Artifact"
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
TargetPath: '\\MySolution\$(Build.DefinitionName)\$(Build.BuildNumber)'
publishLocation: 'Container'
Hopefully this helps :-)
dependsOn
condition on the "Staging" stage. That will ensure that it does not wait or try to deploy to Production until after it has successfully deployed to Staging. This gets more valuable as you add automated testing. – Caltrop