Commit 68dc0f68 authored by L. Ellenbeck's avatar L. Ellenbeck

Updated build pipeline coscine/issues#157

parent 39cf8353
......@@ -10,6 +10,9 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Database connection for t4 class generation from database
**/*.generated.cs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
......@@ -262,5 +265,6 @@ __pycache__/
*.pyc
#cake
tools/
dist/
\ No newline at end of file
tools/*
!tools/packages.config
dist/
stages:
- build
- test
......@@ -8,30 +6,30 @@ stages:
- build-release
- semantic-release
- release
build:
before_script:
- PowerShell .\build.ps1 -Target Clean
- PowerShell .\build.ps1 -Target Restore-NuGet-Packages
stage: build
script:
- PowerShell .\build.ps1 -Target Build
- PowerShell .\build.ps1 -Target Build -Configuration Debug
variables:
GIT_STRATEGY: clone
except:
variables:
- $GITLAB_USER_ID == $GIT_BOT_USER_ID
test:
stage: test
script:
- PowerShell .\build.ps1 -Target Resharper
- PowerShell .\build.ps1 -Target Run-Unit-Tests
- PowerShell .\build.ps1 -Target LinterAndTest -Configuration Debug
variables:
GIT_STRATEGY: none
dependencies:
- build
artifacts:
reports:
junit: TestResult.xml
junit: "./Artifacts/TestResults.xml"
paths:
- "./Artifacts/*"
except:
variables:
- $GITLAB_USER_ID == $GIT_BOT_USER_ID
......@@ -39,7 +37,7 @@ test:
update-assembly-info:
stage: update-assembly-info
script:
- PowerShell .\build.ps1 -Target Update-Assembly-Info
- PowerShell .\build.ps1 -Target UpdateAssemblyInfo
variables:
GIT_STRATEGY: none
dependencies:
......@@ -51,12 +49,10 @@ update-assembly-info:
- $GITLAB_USER_ID == $GIT_BOT_USER_ID
build-release:
before_script:
- PowerShell .\build.ps1 -Target Clean
- PowerShell .\build.ps1 -Target Restore-NuGet-Packages
stage: build-release
script:
- PowerShell .\build.ps1 -Target Build-Release
- PowerShell .\build.ps1 -Target Build -Configuration Release
- PowerShell .\build.ps1 -Configuration Release -Target NugetPack
variables:
GIT_STRATEGY: none
dependencies:
......@@ -80,11 +76,11 @@ docs:
except:
variables:
- $GITLAB_USER_ID == $GIT_BOT_USER_ID
semantic-release:
stage: semantic-release
script:
- PowerShell .\build.ps1 -Target Semantic-Release
- PowerShell .\build.ps1 -Target SemanticRelease
variables:
GIT_STRATEGY: none
dependencies:
......@@ -94,15 +90,18 @@ semantic-release:
except:
variables:
- $GITLAB_USER_ID == $GIT_BOT_USER_ID
release:
before_script:
stage: release
script:
- PowerShell .\build.ps1 -Target Build-Release
- PowerShell .\build.ps1 -Target Build -Configuration Release
- PowerShell .\build.ps1 -Configuration Release -Target NugetPack
- PowerShell .\build.ps1 -Configuration Release -Target NugetPush --nugetApiKey="$NUGET_API_KEY"
variables:
GIT_STRATEGY: none
GIT_STRATEGY: clone
artifacts:
paths:
- dist
- "./Artifacts/*"
only:
- tags
......@@ -20,4 +20,4 @@
"message": "Chore: ${nextRelease.version}\n\n${nextRelease.notes}"
}]
]
}
\ No newline at end of file
}
#tool nuget:?package=NUnit.ConsoleRunner&version=3.9.0
#tool nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.3.4
#tool nuget:?package=vswhere&version=2.6.7
#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.Npx&version=1.3.0
#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.Issues&version=0.6.2
#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.Issues.InspectCode&version=0.6.1
#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.FileHelpers&version=3.1.0
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
//////////////////////////////////////////////////////////////////////
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
var nugetApiKey = Argument<string>("nugetApiKey", null);
//////////////////////////////////////////////////////////////////////
// PREPARATION
//////////////////////////////////////////////////////////////////////
// Define directories.
string projectName;
string projectPath;
DirectoryPath buildDir;
FilePath solutionFile;
var projects = GetFiles("./**/*.csproj");
var artifactsDir = Directory("./Artifacts");
string nupkgDir;
var solutionFile = GetFiles("./**/*.sln").First();
var projectName = solutionFile.GetFilenameWithoutExtension().ToString();
var assemblyInfoSubPath = "Properties/AssemblyInfo.cs";
var nugetSource = "https://api.nuget.org/v3/index.json";
// get latest MSBuild version
var vsLatest = VSWhereLatest();
var msBuildPathX64 = (vsLatest == null) ? null : vsLatest.CombineWithFilePath("./MSBuild/Current/Bin/MSBuild.exe");
// Error rules for resharper
// Example: {"InconsistentNaming", "RedundantUsingDirective"};
string [] resharperErrorRules = {};
string[] resharperErrorRules = {};
// Paths to exclude from dupFinder
List<string> dupFinderExcludePatterns = projects.Select( x => $"{x.GetDirectory().ToString()}/{assemblyInfoSubPath}").ToList();
string[] dupFinderExcludeCodeRegionsByNameSubstring = { "DupFinder Exclusion" };
Action <NpxSettings> requiredSemanticVersionPackages = settings =>
settings.AddPackage("semantic-release")
.AddPackage("@semantic-release/commit-analyzer")
.AddPackage("@semantic-release/release-notes-generator")
.AddPackage("@semantic-release/gitlab")
.AddPackage("@semantic-release/git")
.AddPackage("@semantic-release/exec")
.AddPackage("conventional-changelog-eslint");
///////////////////////////////////////////////////////////////////////////////
// SETUP / TEARDOWN
///////////////////////////////////////////////////////////////////////////////
Setup(context =>{
nupkgDir = $"{artifactsDir.ToString()}/nupkg";
Information("Running tasks...");
});
Action<NpxSettings> requiredSemanticVersionPackages = settings => settings
.AddPackage("semantic-release")
.AddPackage("@semantic-release/commit-analyzer")
.AddPackage("@semantic-release/release-notes-generator")
.AddPackage("@semantic-release/gitlab")
.AddPackage("@semantic-release/git")
.AddPackage("@semantic-release/exec")
.AddPackage("conventional-changelog-eslint");
Teardown(context =>{
Information("Finished running tasks.");
});
//////////////////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////////////////
Task("Get-Project-Name")
.Does(() =>
{
var solutions = GetFiles("./**/*.sln");
projectName = solutions.First().GetFilenameWithoutExtension().ToString();
Information("Project Name: {0}", projectName);
Task("Clean")
.Description("Cleans all build and artifacts directories")
.Does(() =>{
var settings = new DeleteDirectorySettings {
Recursive = true,
Force = true
};
solutionFile = solutions.First().ToString();
Information("Solution File: {0}", solutionFile.ToString());
var directoriesToDelete = new List<DirectoryPath>();
projectPath = Context.Environment.WorkingDirectory.ToString().ToString() + "/src";
Information("Project Directory: {0}", projectPath);
foreach(var project in projects) {
directoriesToDelete.Add(Directory($"{project.GetDirectory()}/obj"));
directoriesToDelete.Add(Directory($"{project.GetDirectory()}/bin"));
}
buildDir = Directory(projectPath + "/" + projectName + "/bin") + Directory(configuration);
Information("Build Directory: {0}", buildDir.ToString());
directoriesToDelete.Add(artifactsDir);
foreach(var dir in directoriesToDelete) {
if (DirectoryExists(dir)) {
Information($"Cleaning path {dir} ...");
DeleteDirectory(dir, settings);
}
}
});
Task("Clean")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
CleanDirectory(buildDir);
CleanDirectory("./dist");
Task("Restore")
.Does(() =>{
// Restore all NuGet packages.
Information($"Restoring {solutionFile}...");
NuGetRestore(solutionFile, new NuGetRestoreSettings {
NoCache = true
});
});
Task("DupFinder")
.Description("Find duplicates in the code")
.Does(() =>{
var settings = new DupFinderSettings() {
ShowStats = true,
ShowText = true,
OutputFile = $"{artifactsDir}/dupfinder.xml",
ExcludeCodeRegionsByNameSubstring = dupFinderExcludeCodeRegionsByNameSubstring,
ExcludePattern = dupFinderExcludePatterns.ToArray(),
ThrowExceptionOnFindingDuplicates = true
};
DupFinder(solutionFile, settings);
});
Task("InspectCode")
.Description("Inspect the code using Resharper's rule set")
.Does(() =>{
var settings = new InspectCodeSettings() {
SolutionWideAnalysis = true,
OutputFile = $"{artifactsDir}/inspectcode.xml",
ThrowExceptionOnFindingViolations = false
};
InspectCode(solutionFile, settings);
var issues = ReadIssues(
InspectCodeIssuesFromFilePath($"{artifactsDir}/inspectcode.xml"), Context.Environment.WorkingDirectory);
Information("{0} issues are found.", issues.Count());
var errorIssues = issues.Where(issue =>resharperErrorRules.Any(issue.Rule.Contains)).ToList();
if (errorIssues.Any()) {
var errorMessage = errorIssues.Aggregate(new StringBuilder(), (stringBuilder, issue) =>stringBuilder.AppendFormat("FileName: {0} Line: {1} Message: {2}{3}", issue.AffectedFileRelativePath, issue.Line, issue.Message, Environment.NewLine));
throw new CakeException($"{errorIssues.Count} errors detected: {Environment.NewLine}{errorMessage}.");
}
});
Task("Restore-NuGet-Packages")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
NuGetRestore(solutionFile);
Task("Test")
.Does(() =>{
NUnit3($"./src/**/bin/{configuration}/*.Tests.dll", new NUnit3Settings {
// generate the xml file
NoResults = false,
Results = new NUnit3Result[] {
new NUnit3Result() {
FileName = $"{artifactsDir}/TestResults.xml",
Transform = Context.Environment.WorkingDirectory + "/nunit3-junit.xslt"
}
}
});
});
Task("Resharper")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
FilePath dupLog = Context.Environment.WorkingDirectory + "/Resharper/dupfinder.xml";
FilePath inspectLog = Context.Environment.WorkingDirectory + "/Resharper/inspectcode.xml";
DupFinder(solutionFile, new DupFinderSettings() {
OutputFile = dupLog.ToString()
});
Information("DupFinder Log:{0}{1}", Environment.NewLine, FileReadText(dupLog));
InspectCode(solutionFile, new InspectCodeSettings() {
OutputFile = inspectLog.ToString()
});
var issues = ReadIssues(
InspectCodeIssuesFromFilePath(inspectLog.ToString()),
Context.Environment.WorkingDirectory);
Information("{0} issues are found.", issues.Count());
Information("InspectCode Log:{0}{1}", Environment.NewLine, FileReadText(inspectLog));
var errorIssues = issues.Where(issue => resharperErrorRules.Any(issue.Rule.Contains)).ToList();
if(errorIssues.Any())
{
var errorMessage = errorIssues.Aggregate(new StringBuilder(), (stringBuilder, issue) => stringBuilder.AppendFormat("FileName: {0} Line: {1} Message: {2}{3}", issue.AffectedFileRelativePath, issue.Line, issue.Message, Environment.NewLine));
throw new CakeException($"{errorIssues.Count} errors detected: {Environment.NewLine}{errorMessage}.");
}
Task("NugetPush")
.Does(() =>{
var nupkgs = GetFiles($"{nupkgDir}/*.nupkg");
Information("Need to push {0} packages", nupkgs.Count);
foreach(var nupkg in nupkgs) {
Information("Pushing {0}", nupkg);
NuGetPush(nupkg, new NuGetPushSettings {
Source = nugetSource,
ApiKey = nugetApiKey
});
}
});
Task("Update-Assembly-Info")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
Information("Running semantic-release in dry run mode to extract next semantic version number");
string[] semanticReleaseOutput;
Npx("semantic-release", "--dry-run", requiredSemanticVersionPackages, out semanticReleaseOutput);
Information(string.Join(Environment.NewLine, semanticReleaseOutput));
var nextSemanticVersionNumber = ExtractNextSemanticVersionNumber(semanticReleaseOutput);
if (nextSemanticVersionNumber == null) {
Warning("There are no relevant changes. AssemblyInfo won't be updated!");
} else {
Information("Next semantic version number is {0}", nextSemanticVersionNumber);
var assemblyVersion = $"{nextSemanticVersionNumber}.0";
CreateAssemblyInfo(projectPath + "/" + projectName + "/Properties/AssemblyInfo.cs", new AssemblyInfoSettings{
Product = projectName,
Title = projectName,
Company = "RWTH Aachen University IT Center",
Version = assemblyVersion,
FileVersion = assemblyVersion,
InformationalVersion = assemblyVersion,
Copyright = "RWTH Aachen University IT Center " + DateTime.Now.Year
});
}
Task("NugetPack")
.Does(() =>{
foreach(var project in projects) {
var nuspec = $"{project.GetDirectory()}/{project.GetFilenameWithoutExtension()}.nuspec";
if(!project.ToString().EndsWith(".Tests")
&& FileExists(nuspec))
{
Information("Packing {0}...", nuspec);
if(!DirectoryExists(nupkgDir)) {
CreateDirectory(nupkgDir);
}
NuGetPack(project.ToString() ,new NuGetPackSettings
{
OutputDirectory = nupkgDir,
Properties = new Dictionary<string, string> { {"Configuration", "Release"}}
});
}
}
});
Task("Build-Release")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
if(IsRunningOnWindows())
{
// Use MSBuild
MSBuild(solutionFile, settings =>
{
settings.SetConfiguration(configuration);
settings.WithProperty("DebugSymbols", "false");
settings.WithProperty("DebugType", "None");
});
}
else
{
// Use XBuild
XBuild(solutionFile, settings =>
{
settings.SetConfiguration(configuration);
settings.WithProperty("DebugSymbols", "false");
settings.WithProperty("DebugType", "None");
});
}
CopyDirectory(buildDir, "./dist");
Task("UpdateAssemblyInfo")
.Does(() =>{
Information("Running semantic-release in dry run mode to extract next semantic version number");
string[] semanticReleaseOutput;
Npx("semantic-release", "--dry-run", requiredSemanticVersionPackages, out semanticReleaseOutput);
Information(string.Join(Environment.NewLine, semanticReleaseOutput));
var nextSemanticVersionNumber = ExtractNextSemanticVersionNumber(semanticReleaseOutput);
if (nextSemanticVersionNumber == null) {
Warning("There are no relevant changes. AssemblyInfo won't be updated!");
} else {
Information("Next semantic version number is {0}", nextSemanticVersionNumber);
var assemblyVersion = $"{nextSemanticVersionNumber}.0";
foreach(var project in projects) {
CreateAssemblyInfo($"{project.GetDirectory()}/{assemblyInfoSubPath}", new AssemblyInfoSettings {
Product = project.GetFilenameWithoutExtension().ToString(),
Title = project.GetFilenameWithoutExtension().ToString(),
Company = "IT Center, RWTH Aachen University",
Version = assemblyVersion,
FileVersion = assemblyVersion,
InformationalVersion = assemblyVersion,
Copyright = $"{DateTime.Now.Year} IT Center, RWTH Aachen University",
Description = $"{project.GetFilenameWithoutExtension().ToString()} is a part of the CoScInE group."
});
}
}
});
Task("Build")
.IsDependentOn("Get-Project-Name")
.Does(() =>
{
if(IsRunningOnWindows())
{
// Use MSBuild
MSBuild(solutionFile, settings =>
{
settings.SetConfiguration(configuration);
settings.WithProperty("RunCodeAnalysis", "true");
});
}
else
{
// Use XBuild
XBuild(solutionFile, settings =>
{
settings.SetConfiguration(configuration);
settings.WithProperty("RunCodeAnalysis", "true");
});
}
});
.IsDependentOn("Clean")
.IsDependentOn("Restore")
.Does(() =>{
if (IsRunningOnWindows()) {
var frameworkSettingsWindows = new MSBuildSettings {
Configuration = configuration,
Restore = true
};
frameworkSettingsWindows.ToolPath = msBuildPathX64;
frameworkSettingsWindows.WorkingDirectory = Context.Environment.WorkingDirectory;
if (configuration.Equals("Release")) {
frameworkSettingsWindows.WithProperty("DebugSymbols", "false");
frameworkSettingsWindows.WithProperty("DebugType", "None");
}
// Use MSBuild
Information($"Building {solutionFile}");
MSBuild(solutionFile, frameworkSettingsWindows);
}
else {
var frameworkSettingsUnix = new XBuildSettings {
Configuration = configuration
};
if (configuration.Equals("Release")) {
frameworkSettingsUnix.WithProperty("DebugSymbols", "false");
frameworkSettingsUnix.WithProperty("DebugType", "None");
}
// Use XBuild
Information($"Building {solutionFile}");
XBuild(solutionFile, frameworkSettingsUnix);
}
if (configuration.Equals("Release")) {
if(!DirectoryExists(artifactsDir)) {
CreateDirectory(artifactsDir);
}
foreach(var project in projects) {
if(!project.GetDirectory().ToString().EndsWith(".Tests")
&& !FileExists($"{project.GetDirectory()}/{project.GetFilenameWithoutExtension()}.nuspec"))
{
Information($"Copying {project.GetDirectory()}/bin/{configuration}/* to {artifactsDir}");
CopyDirectory($"{project.GetDirectory()}/bin/{configuration}/", artifactsDir);
}
}
}
Task("Run-Unit-Tests")
.Does(() =>
{
NUnit3("./src/**/bin/" + configuration + "/*.Tests.dll", new NUnit3Settings {
// generate the xml file
NoResults = false,
Results = new NUnit3Result[]{new NUnit3Result(){
FileName = Context.Environment.WorkingDirectory + "/TestResult.xml",
Transform = Context.Environment.WorkingDirectory + "/nunit3-junit.xslt"
}}
});
});
Task("Semantic-Release")
.Does(() =>
{
Npx("semantic-release", requiredSemanticVersionPackages);
Task("SemanticRelease")
.Does(() =>{
Npx("semantic-release", requiredSemanticVersionPackages);
});
//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////
Task("Linter")
.Description("Run DupFinder and InspectCode")
.IsDependentOn("DupFinder")
.IsDependentOn("InspectCode");
Task("LinterAndTest")
.Description("Run Linter and Tests")
.IsDependentOn("Linter")
.IsDependentOn("Test");
Task("Default")
.IsDependentOn("Clean")
.IsDependentOn("Restore-NuGet-Packages")
.IsDependentOn("Resharper")
.IsDependentOn("Build")
.IsDependentOn("Run-Unit-Tests");
.IsDependentOn("Clean")
.IsDependentOn("Restore")
.IsDependentOn("DupFinder")
.IsDependentOn("InspectCode")
.IsDependentOn("Build")
.IsDependentOn("Test");
//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////
RunTarget(target);
///////////////////////////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////////////////////////
string ExtractNextSemanticVersionNumber(string[] semanticReleaseOutput) {
var extractRegEx = new System.Text.RegularExpressions.Regex("^.+next release version is (?<SemanticVersionNumber>.*)$");
string ExtractNextSemanticVersionNumber(string[] semanticReleaseOutput)
{
var extractRegEx = new System.Text.RegularExpressions.Regex("^.+next release version is (?<SemanticVersionNumber>.*)$");
return semanticReleaseOutput
.Select(line => extractRegEx.Match(line).Groups["SemanticVersionNumber"].Value)
.Where(line => !string.IsNullOrWhiteSpace(line))
.SingleOrDefault();
}
\ No newline at end of file
return semanticReleaseOutput.Select(line =>extractRegEx.Match(line).Groups["SemanticVersionNumber"].Value).Where(line =>!string.IsNullOrWhiteSpace(line)).SingleOrDefault();
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.28.0" />
</packages>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment