Skip to content

Commit

Permalink
PublicApiAnalyzer refinements (#3677)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-tengler authored May 27, 2021
1 parent 16c10d4 commit e5c0595
Show file tree
Hide file tree
Showing 38 changed files with 1,659 additions and 258 deletions.
177 changes: 177 additions & 0 deletions .build/Build.PublicApiAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using System.IO;
using System.Linq;
using Colorful;
using Nuke.Common;
using Nuke.Common.IO;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.Git.GitTasks;
using static Helpers;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text;
using System;
using System.Drawing;

partial class Build : NukeBuild
{
private readonly string _shippedApiFile = "PublicAPI.Shipped.txt";
private readonly string _unshippedApiFile = "PublicAPI.Unshipped.txt";
private readonly string _removedApiPrefix = "*REMOVED*";

[Parameter] readonly string From;
[Parameter] readonly string To;
[Parameter] readonly bool Breaking;

Target CheckPublicApi => _ => _
.DependsOn(Restore)
.Executes(() =>
{
if (!InvokedTargets.Contains(Restore))
{
DotNetBuildSonarSolution(AllSolutionFile);
}

DotNetBuild(c => c
.SetProjectFile(AllSolutionFile)
.SetNoRestore(InvokedTargets.Contains(Restore))
.SetConfiguration(Configuration)
.SetProperty("RequireDocumentationOfPublicApiChanges", true));
});

Target AddUnshipped => _ => _
.DependsOn(Restore)
.Executes(() =>
{
// first we ensure that the All.sln exists.
if (!InvokedTargets.Contains(Restore))
{
DotNetBuildSonarSolution(AllSolutionFile);
}

// new we restore our local dotnet tools including dotnet-format
DotNetToolRestore(c => c.SetProcessWorkingDirectory(RootDirectory));

// last we run the actual dotnet format command.
DotNet($@"format ""{AllSolutionFile}"" --fix-analyzers warn --diagnostics RS0016", workingDirectory: RootDirectory);
});

Target DiffShippedApi => _ => _
.Executes(() =>
{
var from = string.IsNullOrEmpty(From) ? GitRepository.Branch : From;
var to = string.IsNullOrEmpty(To) ? "main" : To;

if (from == to)
{
Colorful.Console.WriteLine("Nothing to diff here.", Color.Yellow);
return;
}

AbsolutePath shippedPath = SourceDirectory / "**" / _shippedApiFile;

Git($@" --no-pager diff --minimal -U0 --word-diff ""{from}"" ""{to}"" -- ""{shippedPath}""", RootDirectory);
});

Target DisplayUnshippedApi => _ => _
.Executes(async () =>
{
var unshippedFiles = Directory.GetFiles(SourceDirectory, _unshippedApiFile, SearchOption.AllDirectories);

if (Breaking)
{
Colorful.Console.WriteLine("Unshipped breaking changes:", Color.Red);
}
else
{
Colorful.Console.WriteLine("Unshipped changes:");
}

Colorful.Console.WriteLine();

foreach (var unshippedFile in unshippedFiles)
{
IEnumerable<string> unshippedApis = await GetNonEmptyLinesAsync(unshippedFile);

if (Breaking)
{
unshippedApis = unshippedApis.Where(u => u.StartsWith(_removedApiPrefix)).ToList();
}

if (!unshippedApis.Any())
{
continue;
}

foreach (var unshippedApi in unshippedApis)
{
if (unshippedApi.StartsWith(_removedApiPrefix))
{
var value = unshippedApi[_removedApiPrefix.Length..];
Colorful.Console.WriteLine(value);
}
else
{
Colorful.Console.WriteLine(unshippedApi);
}
}
}
});

Target MarkApiShipped => _ => _
.Executes(async () =>
{
var shippedFiles = Directory.GetFiles(SourceDirectory, _shippedApiFile, SearchOption.AllDirectories);

foreach (var shippedFile in shippedFiles)
{
var projectDir = Path.GetDirectoryName(shippedFile);
var unshippedFile = Path.Join(projectDir, _unshippedApiFile);

if (!File.Exists(unshippedFile))
{
continue;
}

List<string> unshippedApis = await GetNonEmptyLinesAsync(unshippedFile);

if (!unshippedApis.Any())
{
continue;
}

List<string> shippedApis = await GetNonEmptyLinesAsync(shippedFile);

List<string> removedApis = new();

foreach (var unshippedApi in unshippedApis)
{
if (unshippedApi.StartsWith(_removedApiPrefix))
{
var value = unshippedApi[_removedApiPrefix.Length..];
removedApis.Add(value);
}
else
{
shippedApis.Add(unshippedApi);
}
}

IOrderedEnumerable<string> newShippedApis = shippedApis
.Where(s => !removedApis.Contains(s))
.Distinct()
.OrderBy(s => s);

await File.WriteAllLinesAsync(shippedFile, newShippedApis, Encoding.ASCII);
await File.WriteAllTextAsync(unshippedFile, "", Encoding.ASCII);
}
});

private static async Task<List<string>> GetNonEmptyLinesAsync(string filepath)
{
var lines = await File.ReadAllLinesAsync(filepath);

return lines.Where(c => !string.IsNullOrWhiteSpace(c)).ToList();
}
}
1 change: 0 additions & 1 deletion .build/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,5 @@ partial class Build : NukeBuild
.SetFileVersion(GitVersion.AssemblySemFileVer)
.SetInformationalVersion(GitVersion.InformationalVersion)
.SetVersion(GitVersion.SemVer));
// .SetProperty("RequireDocumentationOfPublicApiChanges", true));
});
}
2 changes: 1 addition & 1 deletion .build/Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Nuke.Common" Version="5.0.2" />
<PackageReference Include="Nuke.Common" Version="5.1.2" />
<PackageDownload Include="GitVersion.Tool" Version="[5.6.3]" />
<PackageDownload Include="NuGet.CommandLine" Version="[5.5.1]" />
<PackageDownload Include="dotnet-sonarscanner" Version="[5.0.4]" />
Expand Down
18 changes: 12 additions & 6 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@
"dotnet-sonarscanner"
]
},
"nuke.globaltool": {
"version": "0.24.11",
"commands": [
"nuke"
]
},
"boost.tool": {
"version": "0.2.2",
"commands": [
"boo"
]
},
"dotnet-format": {
"version": "5.1.225507",
"commands": [
"dotnet-format"
]
},
"nuke.globaltool": {
"version": "5.1.2",
"commands": [
"nuke"
]
}
}
}
30 changes: 30 additions & 0 deletions .devops/azure-pipelines.release-hotchocolate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ stages:
strategy:
parallel: 5
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand All @@ -32,6 +42,16 @@ stages:
displayName: "Pack"
dependsOn: []
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand All @@ -50,6 +70,16 @@ stages:
displayName: "Publish"
dependsOn: [Test, Pack]
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand Down
30 changes: 30 additions & 0 deletions .devops/azure-pipelines.sonar-hotchocolate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ stages:
strategy:
parallel: 5
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand All @@ -37,6 +47,16 @@ stages:
displayName: "ReportCoverage"
dependsOn: [Cover]
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand All @@ -55,6 +75,16 @@ stages:
displayName: "Sonar"
dependsOn: [Cover]
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand Down
10 changes: 10 additions & 0 deletions .devops/azure-pipelines.sonar-pr-hotchocolate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ stages:
displayName: "Sonar Pull-Request"
dependsOn: []
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand Down
10 changes: 10 additions & 0 deletions .devops/azure-pipelines.test-pr-hotchocolate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ stages:
strategy:
parallel: 5
steps:
- task: UseDotNet@2
displayName: "Install .NET Core 2.1"
inputs:
packageType: 'sdk'
version: '2.1.816'
- task: UseDotNet@2
displayName: "Install .NET Core 3.1"
inputs:
packageType: 'sdk'
version: '3.1.409'
- task: UseDotNet@2
displayName: "Install .NET Core"
inputs:
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/check-public-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Check Public API

on:
pull_request:
branches:
- main

jobs:
check-public-api:
name: "Check Public API"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
- run: ./build.sh CheckPublicApi
name: "Check for undocumented public API changes"
Empty file removed .nuke
Empty file.
Loading

0 comments on commit e5c0595

Please sign in to comment.