Skip to content

Commit

Permalink
👷 Implement 'Build Once' strategy in CI pipeline (#31)
Browse files Browse the repository at this point in the history
* Run CI with very branch push.
* Build only once, generating package in CI (with version `0.0.0-ci-$(Build.BuildNumber)` for now).
This requires applying a workaround for Coverlet, to able to collect code coverage when the solution is built with `ContinuousIntegrationBuild=true` (see [this](dotnet/sourcelink#572) and [this](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/DeterministicBuild.md).
* Fix custom task conditions: `succeeded()` behavior only applies by default when `condition` is not overriden.
* Add publish package guard (`Push != ''`) and options to avoid commiting new version and tag (`NoCommit`) and pushing packages to GitHub (`NoGitHubPush`) and NuGet (`NoNuGetPush`).
* Use `dotnet-version-cli` rather than custom target to update versions.
* Remove custom targets and legacy, manual-publish ones.
* Remove NuGet test project
  • Loading branch information
eduherminio committed Jun 2, 2020
1 parent 63367f0 commit 254b093
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 356 deletions.
25 changes: 0 additions & 25 deletions NugetTest/NugetTest.sln

This file was deleted.

39 changes: 0 additions & 39 deletions NugetTest/NugetTest/LocalPackageTest.cs

This file was deleted.

6 changes: 0 additions & 6 deletions NugetTest/NugetTest/NuGet.Config

This file was deleted.

26 changes: 0 additions & 26 deletions NugetTest/NugetTest/NugetTest.csproj

This file was deleted.

3 changes: 0 additions & 3 deletions NugetTest/NugetTest/Sample_file.txt

This file was deleted.

156 changes: 83 additions & 73 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
name: $(BuildID)

trigger:
branches:
include:
- master

schedules:
- cron: "1 0 * * 1"
displayName: 'Weekly build'
Expand All @@ -15,13 +10,14 @@ schedules:

variables:
SdkVersion: '3.1.x'
DotnetVersionCliVersion: '2.0.0-next.7'

stages:
- stage: CI
displayName: 'Build and run tests'
displayName: 'CI'
jobs:
- job: ci
displayName: 'Build and run tests'
displayName: 'CI'

strategy:
maxParallel: 3
Expand All @@ -36,6 +32,8 @@ stages:
vmImage: $(imageName)

variables:
DefaultPackageVersion: '0.0.0-ci-$(Build.BuildNumber)'
PackageVersion: '$(DefaultPackageVersion)'
disable.coverage.autogenerate: 'true'
EscapedComma: '%2c'
TestResults: '$(Agent.TempDirectory)'
Expand All @@ -48,6 +46,16 @@ stages:
packageType: sdk
version: '$(SdkVersion)'

- task: CmdLine@2
displayName: 'Modify FileParser version'
condition: and(succeeded(), eq(variables['imageName'], 'ubuntu-latest'))
inputs:
failOnStderr: true
script: |
dotnet new tool-manifest
dotnet tool install dotnet-version-cli --version $(DotnetVersionCliVersion)
dotnet version -f src/FileParser/FileParser.csproj --skip-vcs $(PackageVersion)
- task: SonarCloudPrepare@1
displayName: 'Prepare SonarCloud analysis'
condition: eq(variables['imageName'], 'windows-latest')
Expand All @@ -61,11 +69,18 @@ stages:
sonar.cs.opencover.reportsPaths="$(Build.SourcesDirectory)/**/coverage.opencover.xml"
sonar.cs.vstest.reportsPaths="$(TestResults)/**/*.trx"
- task: DotNetCoreCLI@2
displayName: 'Restore'
inputs:
command: restore
arguments: '--configuration Release'
projects: './src/FileParserSolution.sln'

- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: build
arguments: '--configuration Release'
arguments: '--configuration Release --no-restore /p:ContinuousIntegrationBuild=true'
projects: './src/FileParserSolution.sln'

- task: DotNetCoreCLI@2
Expand All @@ -82,7 +97,7 @@ stages:

- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
displayName: 'Generate tests report'
condition: eq(variables['imageName'], 'ubuntu-latest')
condition: and(succeeded(), eq(variables['imageName'], 'ubuntu-latest'))
inputs:
reports: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml'
targetdir: '$(CoverageResults)'
Expand All @@ -91,13 +106,31 @@ stages:

- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage'
condition: eq(variables['imageName'], 'ubuntu-latest')
condition: and(succeeded(), eq(variables['imageName'], 'ubuntu-latest'))
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(CoverageResults)/Cobertura.xml'
reportDirectory: '$(CoverageResults)'
pathToSources: '$(Build.SourcesDirectory)'

- task: DotNetCoreCLI@2
displayName: 'Generate NuGet package'
condition: and(succeeded(), eq(variables['imageName'], 'ubuntu-latest'))
inputs:
command: 'pack'
arguments: '--configuration Release'
configuration: 'Release'
packagesToPack: '**/FileParser.csproj'
nobuild: true
packDirectory: '$(Build.SourcesDirectory)/FileParser/Artifacts'

- task: PublishBuildArtifacts@1
displayName: 'Publish artifact with NuGet package and its symbols'
condition: and(succeeded(), eq(variables['imageName'], 'ubuntu-latest'))
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/FileParser/Artifacts/'
artifactName: 'FileParser'

- task: SonarCloudAnalyze@1
displayName: 'Perform SonarCloud analysis'
condition: eq(variables['imageName'], 'windows-latest')
Expand All @@ -111,107 +144,84 @@ stages:
- stage: CD
displayName: 'Generate package(s)'
dependsOn: 'CI'
condition: and(succeeded('CI'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
condition: and(succeeded('CI'), ne(variables['PackageVersion'], variables['DefaultPackageVersion']), ne(variables['Push'], ''))
jobs:
- job: cd
displayName: 'Generate package(s)'
pool:
vmImage: ubuntu-latest
steps:

steps:
- checkout: self
persistCredentials: true
clean: true

- task: UseDotNet@2
displayName: 'Use .NET Core sdk v$(SdkVersion)'
inputs:
packageType: sdk
version: '$(SdkVersion)'

- task: NuGetToolInstaller@1
displayName: 'Setup NuGet'
- task: DownloadPipelineArtifact@2
inputs:
versionSpec: '5.5.0'
checkLatest: true
artifact: FileParser
path: $(Build.SourcesDirectory)/FileParser/Artifacts/

- task: CmdLine@2
displayName: 'Setup Git'
displayName: 'Setup git profile'
inputs:
workingDirectory: $(Build.SourcesDirectory)
workingDirectory: $(Build.SourcesDirectory)/FileParser
script: |
git config --replace-all user.email "azure@devops.com"
git config --replace-all user.name "Azure DevOps"
- task: DotNetCoreCLI@2
displayName: 'Modify project version'
condition: ne(variables['PackageVersion'], '')
inputs:
command: build
arguments: '--configuration Release /t:ReplaceVersion /p:VersionTag=$(PackageVersion)'
projects: '**/FileParser.csproj'
git config --replace-all user.email "azure@devops.com"
git config --replace-all user.name "Azure DevOps"
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: build
arguments: '--configuration Release /p:ContinuousIntegrationBuild=true'
projects: './src/FileParserSolution.sln'

- task: DotNetCoreCLI@2
displayName: 'Generate NuGet package'
inputs:
command: 'pack'
arguments: '--configuration Release'
configuration: 'Release'
packagesToPack: '**/FileParser.csproj'
nobuild: true
packDirectory: '$(Build.SourcesDirectory)/Artifacts'

- task: PublishBuildArtifacts@1
displayName: 'Publish artifact with NuGet package and its symbols'
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/Artifacts/'
artifactName: 'FileParser'

- task: DotNetCoreCLI@2
displayName: 'Test Nuget package locally'
- task: NuGetToolInstaller@1
displayName: 'Setup NuGet'
inputs:
command: test
arguments: '--configuration Release --logger trx"'
nobuild: true
projects: '**/*NugetTest.csproj'
versionSpec: '5.5.0'
checkLatest: true

- task: NuGetCommand@2
displayName: 'Push NuGet package'
condition: ne(variables['PackageVersion'], '')
condition: and(succeeded(), eq(variables['NoNuGetPush'], ''))
inputs:
command: 'push'
packagesToPush: '$(Build.SourcesDirectory)/Artifacts/*.nupkg'
packagesToPush: '$(Build.SourcesDirectory)/FileParser/Artifacts/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'FileParser_NuGet'
verbosityPush: 'Detailed'

- task: NuGetCommand@2
displayName: 'Push GitHub package'
condition: ne(variables['PackageVersion'], '')
condition: and(succeeded(), eq(variables['NoGitHubPush'], ''))
inputs:
command: 'push'
packagesToPush: '$(Build.SourcesDirectory)/Artifacts/*.nupkg'
packagesToPush: '$(Build.SourcesDirectory)/FileParser/Artifacts/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'FileParser_GitHub'
verbosityPush: 'Detailed'

- task: CmdLine@2
displayName: 'Modify FileParser version'
inputs:
failOnStderr: true
script: |
dotnet new tool-manifest
dotnet tool install dotnet-version-cli --version $(DotnetVersionCliVersion)
dotnet version -f src/FileParser/FileParser.csproj --skip-vcs $(PackageVersion)
- task: CmdLine@2
displayName: 'Commit and push version increment'
condition: and(eq(variables['NoCommit'], ''), ne(variables['PackageVersion'], ''))
condition: and(succeeded(), eq(variables['NoCommit'], ''))
inputs:
workingDirectory: $(Build.SourcesDirectory)
workingDirectory: $(Build.SourcesDirectory)/FileParser
script: |
git checkout master
git switch $(Build.SourceBranchName)
git status
git add -A
git commit -m "Release v$(PackageVersion)"
git tag -a v$(PackageVersion) -m "v$(PackageVersion)"
git commit -am "Release v$(PackageVersion)"
git push
- task: CmdLine@2
displayName: 'Tag version release'
condition: and(succeeded(), eq(variables['NoCommit'], ''), eq(variables['NoTag'], ''))
inputs:
workingDirectory: $(Build.SourcesDirectory)/FileParser
script: |
git switch $(Build.SourceBranchName)
git status
git tag -a v$(PackageVersion) -m "v$(PackageVersion)"
git push --tags
21 changes: 21 additions & 0 deletions src/DeterministicBuild.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- Workaround for deterministic build and coverlet (https://github.com/dotnet/sourcelink/issues/572), see https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/DeterministicBuild.md -->
<Project>
<PropertyGroup>
<TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
</PropertyGroup>
<ItemGroup>
<EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
</ItemGroup>
<ItemGroup>
<SourceRoot Include="$(NuGetPackageRoot)" />
</ItemGroup>

<Target Name="CoverletGetPathMap"
DependsOnTargets="InitializeSourceRootMappedPaths"
Returns="@(_LocalTopLevelSourceRoot)"
Condition="'$(DeterministicSourcePaths)' == 'true'">
<ItemGroup>
<_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
</ItemGroup>
</Target>
</Project>
4 changes: 4 additions & 0 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<Project>
<!-- Workaround for deterministic build and coverlet (https://github.com/dotnet/sourcelink/issues/572), see https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/DeterministicBuild.md -->
<Import Project="$(MSBuildThisFileDirectory)DeterministicBuild.targets" />
</Project>
Loading

0 comments on commit 254b093

Please sign in to comment.