By nature, many developers too lazydo not like to do the same action over and over again. It's easier for us to teach a computer to do monotonous actions for us.
As soon as someone on our team makes changes to the code (read "merge a feature branch into develop"), our build server:
- Builds source code and app installer
- puts down the assembly number, each time increasing the last digit. For example, the current version of our software 3.3.0.202 - part 3.3.0 was once introduced by the developer (hello, SemVer ), and "202" is put down during the assembly process.
- In the process, it analyzes the quality of the code (using SonarQube) - and sends a report to the internal SonarQube,
- Immediately after assembly, it launches autotests (xUnit) and analyzes the test coverage (OpenCover),
Also, depending on the branch to which the changes were made, the following can be done:
- sending an assembly (along with a changelog) to one or several telegram channels (sometimes it is more convenient to take assemblies from there).
- publishing files to the software auto-update system.
Below the cut is about how we taught Gitlab CI to do most of this dreary work for us.
Table of contents
- Install and register Gitlab Runner .
- .gitlab-ci.yml .
- Developer PowerShell for VS.
- CI .
- SonarQube.
- «» xUnit + OpenCover.
- .
, , github , WPF unit-, . gitlab.com , .
Gitlab Runner
Gitlab CI - , Gitlab Runner , . .Net Framework Windows.
Gitlab Runner, :
- Git Windows git.
- Visual Studio Microsoft. Build Tools Visual Studio 2019. , Visual Studio 2019.
- C:\GitLab-Runner gitlab runner. [ Gitlab] (https://docs.gitlab.com/runner/install/windows.html) β : Β«Download the binary for x86 or amd64Β».
- cmd powershell , C:\GitLab-Runner install (Gitlab runner ).
.\gitlab-runner.exe install
- Runner-. , Runner:
- β Settings > CI/CD Runners,
- β Settings > CI/CD Runners,
- Gitlab- β , Overview > Runners.
- Runner-,
.\gitlab-runner.exe register
Runner-:
- coordinator URL β http https gitlab;
- gitlab-ci token β , ;
- gitlab-ci description β Runner-, Gitlab-;
- gitlab-ci tags β Runner-. , β gitlab-. , Runner- (, Runner-, Windows, Runner- Linux);
- enter the executor β
shell
. , ; shell windows powershell, .
.gitlab-ci.yml
Gitlab CI , .gitlab-ci.yml
, .
.gitlab-ci.yml
.NET Framework : 1, 2. :
variables: # ; , MSBUILD_CONCURRENCY: 4 # , , NUGET_PATH: 'C:\Tools\Nuget\nuget.exe' MSBUILD_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe' XUNIT_PATH: 'C:\Tools\xunit.runner.console.2.3.1\xunit.console.exe' TESTS_OUTPUT_FOLDER_PATH: '.\tests\CiCdExample.Tests\bin\Release\' # . , : build, test deploy. # . stages: - build - test # (job-) build_job: stage: build # , build # tags: windows # , Runner- only: # - branches script: # - '& "$env:NUGET_PATH" restore' - '& "$env:MSBUILD_PATH" /p:Configuration=Release /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' # ; clp:ErrorsOnly ; nr:false msbuild artifacts: # , gitlab (.. ) expire_in: 2 days # paths: # , - '$env:TESTS_OUTPUT_FOLDER_PATH' test_job: stage: test only: - branches script: - '& "$env:XUNIT_PATH" "$env:TESTS_OUTPUT_FOLDER_PATH\CiCdExample.Tests.dll"' dependencies: # , build_job - build_job
: , (, -), gitlab. ( ) Settings > CI/CD Variables. (key) SAMPLE_PARAMETER, .gitlab-ci.yml $env:SAMPLE_PARAMETER.
( Protected) / ( Masked).
Developer PowerShell for VS
, , . , : Visual Studio. , - Visual Studio 2017 BuildTools, Visual Studio Professional 2019, .
, Visual Studio 2017 Visual Studio . vswhere, Visual Studio, . Visual Studio 2019 ( 16.1 ) , «» Powershell Developer Powershell, VS.
Variables:
variables: VSWHERE_PATH: '%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe'
before_msbuild enter_vsdevshell :
.before_msbuild: &enter_vsdevshell before_script: - '$vsWherePath = [System.Environment]::ExpandEnvironmentVariables($env:VSWHERE_PATH)' - '& $vsWherePath -latest -format value -property installationPath -products Microsoft.VisualStudio.Product.BuildTools | Tee-Object -Variable visualStudioPath' - 'Join-Path "$visualStudioPath" "\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" | Import-Module' - 'Enter-VsDevShell -VsInstallPath:"$visualStudioPath" -SkipAutomaticLocation'
, Visual Studio, . :
build_job: <<: *enter_vsdevshell stage: build only: - branches script: - 'msbuild /t:restore /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' - 'msbuild /p:Configuration=Release /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' artifacts: expire_in: 2 days paths: - '$env:TESTS_OUTPUT_FOLDER_PATH'
- vswhere.exe Visual Studio. ( VSWHERE_PATH).
%programfiles%
, . .NET System.Environment.ExpandEnvironmentVariables.
: vswhere.
- vswhere Visual Studio.
,vswhere.exe
-help
, :
- -latest ( ),
- -property installationPath ( ),
- -format value ( , ),
- -products < Visual Studio, > ( Visual Studio). ,
-products Microsoft.VisualStudio.Product.Community Microsoft.VisualStudio.Product.BuildTools
Visual Studio Community BuildTools. https://aka.ms/vs/workloads.
: $visualStudioPath Visual Studio , Visual Studio ( ).
- Import-Module Microsoft.VisualStudio.DevShell.dll, Powershell Developer-. Join-Path Visual Studio.
, Microsoft.VisualStudio.DevShell.dll Visual Studio β Import-Module , .
: Powershell .
- «» Developer Powershell. , Visual Studio (
-VsInstallPath
).SkipAutomaticLocation
(< >\source\repos
).
: Developer Powershell msbuild , .
CI
t4 : <major>.<minor>.<revision>
, Gitlab CI tt-, , , . β git tag
git describe
.
git tag
(). . , . , . git rebase git commit --amend, , . git book.
git describe
, , gitbook . : . β fatal: No tags can describe '< >'
. β , , .
: , gitflow - finish hotfix finish release. gitflow, ( ).
, gitflow, feature- develop , develop:
( git : - 1.0.5, git describe
)
. gitflow ( rebase-), master feature- develop, merge- master develop .
msbuild :
build_job: <<: *enter_vsdevshell stage: build only: - branches script: - 'msbuild /t:restore /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' - '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1' - '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value' - '[string]$version = "$major.$minor.$patch.$commit"' - 'msbuild /p:Configuration=Release /p:AssemblyVersionNumber=$version /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' artifacts: expire_in: 2 days paths: - '$env:TESTS_OUTPUT_FOLDER_PATH'
:
-
<major>.<minor>.<revision>
. -
git describe --long
,<major>.<minor>.<revision>-< >-g< >
. - , β
$versionGroup
. - 4
$major
,$minor
,$patch
,$commit
, . - msbuild .
: , gitflow, () master release hofix, : . , 3.4, release- 3.5. : , master, , 3.4.
SonarQube
SonarQube β .
SonarQube Community-, . , . (develop), (, SonarQube):
- SonarQube , .
- SonarScanner for MSBuild ( sonarqube.org)[https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-msbuild/] β .NET Framework 4.6+.
- . ,
C:\Tools\SonarScanner
.
: NuGet, - . - CI/CD Gitlab :
- SONARQUBE_PROJECT_KEY β ,
- SONARQUBE_AUTH_TOKEN β .
( ). ( Masked) , .
- Variables:
variables: SONARSCANNER_MSBUILD_PATH: 'C:\Tools\SonarScanner\SonarScanner.MSBuild.exe' SONARQUBE_HOST_URL: 'url SonarQube'
- (test_job) build_job:
test_job: stage: test only: - /^develop$/ <<: *enter_vsdevshell script: - '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1' - '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value' - '[string]$version = "$major.$minor.$patch.$commit"' - '& "$env:SONARSCANNER_MSBUILD_PATH" begin /key:$env:SONARQUBE_PROJECT_KEY /d:sonar.host.url=$env:SONARQUBE_HOST_URL /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN /d:sonar.gitlab.project_id=$CI_PROJECT_PATH /d:sonar.gitlab.ref_name=develop /v:$version /d:sonar.dotnet.excludeGeneratedCode=true' - 'msbuild /t:rebuild /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' - '& "$env:SONARSCANNER_MSBUILD_PATH" end /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN' - '& "$env:XUNIT_PATH" "$env:TESTS_OUTPUT_FOLDER_PATH\CiCdExample.Tests.dll"'
develop SonarQube .
: msbuild /t:rebuild
. , . .
:
- key β SonarQube,
- v β . ,
- sonar.gitlab.project_id β ID Gitlab,
- sonar.gitlab.ref_name β , SonarQube ,
- sonar.dotnet.excludeGeneratedCode β , System.CodeDom.Compiler.GeneratedCode ( ).
«» xUnit + OpenCover
- β . , :
- ,
- xUnit,
- OpenConver,
- SonarQube.
: OpenCover ReportGenerator, SonarQube .
:
- OpenCover zip- github.
- . ,
C:\Tools\OpenCover
.
: NuGet, - . - Variables:
variables: OBJECTS_TO_TEST_REGEX: '^Rt[^\n]*\.(dll|exe)$' OPENCOVER_PATH: 'C:\Tools\opencover-4.7.922\xunit.console.exe' OPENCOVER_FILTER: '+[Rt.*]* -[*UnitTests]* -[*AssemblyInfo]*' OPENCOVER_REPORT_FILE_PATH: '.\cover.xml'
- (test_job), OpenCover:
test_job: stage: test only: - /^develop$/ <<: *enter_vsdevshell script: - '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1' - '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value' - '[string]$version = "$major.$minor.$patch.$commit"' - '& "$env:SONARSCANNER_MSBUILD_PATH" begin /key:$env:SONARQUBE_PROJECT_KEY /d:sonar.host.url=$env:SONARQUBE_HOST_URL /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN /d:sonar.gitlab.project_id=$CI_PROJECT_PATH /d:sonar.gitlab.ref_name=develop /v:$version /d:sonar.cs.opencover.reportsPaths="$env:OPENCOVER_REPORT_FILE_PATH" /d:sonar.dotnet.excludeGeneratedCode=true' - 'msbuild /t:rebuild /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' - '$dllsToRunUnitTesting = @(Get-ChildItem "$env:TESTS_OUTPUT_FOLDER_PATH" -Recurse) | Where-Object {$_.Name -match $env:OBJECTS_TO_TEST_REGEX} | ForEach-Object { """""$_""""" } | Join-String -Separator " "' - '& "$env:OPENCOVER_PATH" -register -target:"$env:XUNIT_PATH" -targetargs:"$dllsToRunUnitTesting -noshadow" -filter:"$env:OPENCOVER_FILTER" -output:"$env:OPENCOVER_REPORT_FILE_PATH" | Write-Host' - 'if ($?) {' - '[xml]$coverXml = Get-Content "$env:OPENCOVER_REPORT_FILE_PATH"' - '$sequenceCoverage = $coverXml.CoverageSession.Summary.sequenceCoverage' - '$branchCoverage = $coverXml.CoverageSession.Summary.branchCoverage' - 'Write-Host "Total Sequence Coverage <!<$sequenceCoverage>!>"' - 'Write-Host "Total Branch Coverage [![$branchCoverage]!]"' - '} else {' - 'Write-Host "One or more tests failed!"' - 'Throw' - '}' - '& "$env:SONARSCANNER_MSBUILD_PATH" end /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN'
: begin- sonar scanner- β
/d:sonar.cs.opencover.reportsPaths
.
( ) Gitlab, Settings > CI/CDTest coverage parsing
. , Gitlab- :
- ( Sequence Coverage Statement Coverage),
<!<([^>]+)>!>
, - ( Decision Coverage Branch Coverage),
\[!\[([^>]+)\]!\]
.
- ( Sequence Coverage Statement Coverage),