I come from GitLab and its .gitlab-ci.yml and I am experimenting with Azure DevOps multi stage pipelines but I am quite confused about how it works and what's the best strategy even after reading several documentation articles at https://learn.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops
Please allow me to ask several related questions for the basic scenario I'm trying, which is compile, run unit tests, pack a nuget Package for the whole solution (it could contain multiple projects/nuGet packages) and publish the package to a nuGet feed (if the branch is master, a release version, otherwise a pre-release version). This is the repository I'm grabbing the code from: https://github.com/sasw-diego/sasw-test-support It would generate only a nuGet package but I've got other multiproject solutions which should generate many nuGet packages
This is my azure-pipelines.yml
so far:
trigger:
- master
- feature/*
pool:
vmImage: ubuntu-latest
variables:
NUGET_FOLDER_NAME: nupkgs
NUGET_REPOSITORY: https://whatever
PRERELEASE_SUFFIX: $(Build.BuildId)
PIPELINE_ARTIFACT_NAME: $(Build.BuildNumber)
stages:
- stage:
displayName: 'Build'
jobs:
- job: 'Build'
steps:
- task: NuGetAuthenticate@0
displayName: 'Authenticate in NuGet feed'
- script: dotnet restore --no-cache --force
displayName: 'Restore dependencies'
- script: dotnet build --configuration Release --no-restore
displayName: 'Build for Release'
- script: ls $(System.DefaultWorkingDirectory)
displayName: 'List content'
- publish: $(System.DefaultWorkingDirectory)
artifact: $(PIPELINE_ARTIFACT_NAME)
- stage:
displayName: 'Automated Tests'
condition: succeeded()
jobs:
- job:
displayName: 'Unit Tests'
steps:
- download: current
artifact: $(PIPELINE_ARTIFACT_NAME)
- script: ls -a
displayName: 'View'
- script: ls ./test
displayName: 'View test'
- script: ls ./test/Sasw.TestSupport.UnitTests
displayName: 'View folder'
- script: dotnet vstest test/*UnitTests/bin/Release/**/*UnitTests.dll
displayName: 'Run unit tests'
- stage:
displayName: 'NuGet Package'
condition: succeeded()
jobs:
- job:
displayName: 'Pack Preview Version'
condition: ne(variables['Build.SourceBranch'], 'refs/heads/master')
steps:
- script: dotnet pack *.sln --configuration Release --output $(NUGET_FOLDER_NAME)
displayName: 'Pack'
- job:
displayName: 'Pack Stable Version'
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
steps:
- script: dotnet pack *.sln --configuration Release --output $(NUGET_FOLDER_NAME) --version-suffix $(PRERELEASE_SUFFIX) --include-source --include-symbols -p:SymbolPackageFormat=snupkg
displayName: 'Pack'
Build - AutomatedTests - NuGet Package - Publish
As you can see, it's a sequential process where each stage needs something from the previous one. The automated tests needs the built code (dll), the nuGet package needs also access to the built code, the publish needs access to the nupkg generated, etc. I don't know whether it's ok to follow this strategy or it's better to have a single stage with multiple jobs, or even a single job with multiple tasks. As I said I don't fully understand the benefit of having so many concepts and how do they fit in with my needs.No test source files were specified
. I verified this happens because there is no generated folder bin
at all. It's seems strange since I am copying everything from the previous stage (and the previous stage generated bin folders with dll), but then the next stage, despite being able to download everything, it cannot find the tests.This is the log for the stage that fails: https://gist.github.com/sasw-diego/df66eccf71bbfc044a4d72be96268c9a
It'd be very helpful if somebody spots what am I missing to be able to understand this process. Any link to clarify all these concepts would be much appreciated. Ta
PS: This is a similar generic CI/CD I had in GitLab to upload 1 or many nuGets to a feed: https://gist.github.com/sasw-diego/bf46258cb1ad0aa5241e8d1866b53f48
UPDATE: Thanks for the answer. I successfully created a CI/CD yml with multi-stage pipelines that restores, builds, executes tests, runs a container (e.g: an eventStore host) to run integration tests against it, and releases the nuGet in artifacts. So mission accomplished! I've separated it into different stages and jobs to probe some points
trigger:
- master
- feature/*
pool:
vmImage: ubuntu-18.04
variables:
- group: sasw-common-variables
- name: NUGET_FOLDER_NAME
value: nupkgs
- name: PIPELINE_ARTIFACT_NAME
value: $(Build.BuildNumber)
- name: PATH_PIPELINE_ARTIFACT_NAME
value: $(Pipeline.Workspace)/$(PIPELINE_ARTIFACT_NAME)
- name: NUGET_API_KEY
value: $(nuget-api-key)
- name: NUGET_FEED
value: $(nuget-feed)
- name: PRERELEASE_SUFFIX
value: $(nuget-prerelease-suffix)
resources:
containers:
- container: eventstore
image: eventstore/eventstore:release-5.0.2
ports:
- 1113:1113
env:
EVENTSTORE_INT_TCP_PORT: 1113
EVENTSTORE_EXT_TCP_PORT: 1113
EVENTSTORE_INT_HTTP_PORT: 2113
EVENTSTORE_EXT_HTTP_PORT: 2113
EVENTSTORE_EXT_HTTP_PREFIXES: http://*:2113/
stages:
- stage:
displayName: 'Build'
jobs:
- job: 'Build'
displayName: 'Build & Create nuGet Package'
services:
eventstore: eventstore
steps:
- task: NuGetAuthenticate@0
displayName: 'Authenticate in NuGet feed'
- script: dotnet restore --no-cache --force
displayName: 'Restore dependencies'
- script: dotnet build --configuration Release --no-restore
displayName: 'Build with Release Configuration'
- script: dotnet vstest test/*UnitTests/bin/Release/**/*UnitTests.dll
displayName: 'Run unit tests'
- script: dotnet vstest test/*IntegrationTests/bin/Release/**/*IntegrationTests.dll
displayName: 'Run integration tests'
- script: dotnet pack *.sln --configuration Release --output $(NUGET_FOLDER_NAME)
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: 'Create release nuGet'
- script: dotnet pack *.sln --configuration Release --output $(NUGET_FOLDER_NAME) --version-suffix $(PRERELEASE_SUFFIX) --include-source --include-symbols -p:SymbolPackageFormat=snupkg
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: 'Create pre-release nuGet'
- publish: $(System.DefaultWorkingDirectory)/$(NUGET_FOLDER_NAME)
artifact: $(PIPELINE_ARTIFACT_NAME)
displayName: 'Publish pipeline artifact'
- stage:
displayName: 'Release'
condition: succeeded()
jobs:
- job: 'Publish'
displayName: 'Publish nuGet Package'
steps:
- download: current
artifact: $(PIPELINE_ARTIFACT_NAME)
displayName: 'Download pipeline artifact'
- script: ls $(PATH_PIPELINE_ARTIFACT_NAME)
displayName: 'Display contents of downloaded articacts path'
- task: NuGetAuthenticate@0
displayName: 'Authenticate in NuGet feed'
- task: UseDotNet@2
displayName: 'Use latest .NET Core sdk 3.x'
inputs:
packageType: sdk
version: 3.x
includePreviewVersions: true
installationPath: $(Agent.ToolsDirectory)/dotnet
- script: dotnet nuget push $(PATH_PIPELINE_ARTIFACT_NAME)/**/*.nupkg --source $(NUGET_FEED) --api-key $(NUGET_API_KEY) --skip-duplicate
displayName: 'Uploads nuGet packages'
Build - AutomatedTests - NuGet Package - Publish
are not stages, those are logical parts of the build.