Skip to content

Commit

Permalink
nuke target for verifying .NET SDK versions used (#3727)
Browse files Browse the repository at this point in the history
  • Loading branch information
lachmatt authored Oct 25, 2024
1 parent 9915cd5 commit 1d515d0
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 0 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/check-sdk-versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: SdkVersionCheck

on:
push:
branches: [ main ]
pull_request:
merge_group:
workflow_dispatch:

jobs:
check-sdk-versions:
runs-on: windows-latest
steps:

- name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag: v4.2.1

- name: Setup .NET 8
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # tag: v4.0.1
with:
dotnet-version: 8.0.403

- name: Run VerifySdkVersions
run: ./build.cmd VerifySdkVersions
19 changes: 19 additions & 0 deletions OpenTelemetry.AutoInstrumentation.sln
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.RabbitMq",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.Owin.IIS.NetFramework", "test\test-applications\integrations\TestApplication.Owin.IIS.NetFramework\TestApplication.Owin.IIS.NetFramework.csproj", "{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkVersionVerifier", "tools\SdkVersionVerifier\SdkVersionVerifier.csproj", "{C75FA076-D460-414B-97F7-6F8D0E85AE74}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1495,6 +1497,22 @@ Global
{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692}.Release|x64.Build.0 = Release|Any CPU
{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692}.Release|x86.ActiveCfg = Release|Any CPU
{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692}.Release|x86.Build.0 = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|ARM64.Build.0 = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|x64.ActiveCfg = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|x64.Build.0 = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|x86.ActiveCfg = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Debug|x86.Build.0 = Debug|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|Any CPU.Build.0 = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|ARM64.ActiveCfg = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|ARM64.Build.0 = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|x64.ActiveCfg = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|x64.Build.0 = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|x86.ActiveCfg = Release|Any CPU
{C75FA076-D460-414B-97F7-6F8D0E85AE74}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1583,6 +1601,7 @@ Global
{959764E7-5A0C-4511-8004-48DE6B10F499} = {3F051815-8E0D-4356-BC36-55CA642DDF18}
{91D883EC-069E-46BC-B6F7-67C94299851E} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{C75FA076-D460-414B-97F7-6F8D0E85AE74} = {00F4C92D-6652-4BD8-A334-B35D3E711BE6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
Expand Down
10 changes: 10 additions & 0 deletions build/Build.Steps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,16 @@ void RemoveFilesInNetFolderAvailableInAdditionalStore()
.DependsOn(PublishNativeProfilerLinux)
.DependsOn(PublishNativeProfilerMacOs);

Target VerifySdkVersions => _ => _
.Executes(() =>
{
var verifier = Solution.GetProjectByName(Projects.Tools.SdkVersionVerifierTool);
DotNetRun(s => s
.SetProjectFile(verifier)
.SetApplicationArguments(RootDirectory));
});

Target GenerateLibraryVersionFiles => _ => _
.After(PublishManagedProfiler)
.Executes(() =>
Expand Down
1 change: 1 addition & 0 deletions build/Projects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ public static class Tools
{
public const string LibraryVersionsGenerator = "LibraryVersionsGenerator";
public const string GacInstallTool = "GacInstallTool";
public const string SdkVersionVerifierTool = "SdkVersionVerifier";
}
}
2 changes: 2 additions & 0 deletions tools/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<Import Project="..\Directory.Packages.props" />
<ItemGroup>
<PackageVersion Include="Dockerfile" Version="1.0.0" />
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageVersion Include="Microsoft.Build" Version="17.7.2" />
<!-- System.Text.Json - Indirect vulnerable dependency https://github.com/advisories/GHSA-hh2w-p6rv-4g7w from Microsoft.Build -->
Expand All @@ -9,5 +10,6 @@
<!-- NuGet.ProjectModel - Indirect vulnerable dependency https://github.com/advisories/GHSA-447r-wph3-92pm from NuGet.ProjectModel -->
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
<PackageVersion Include="YamlDotNet" Version="16.1.3" />
</ItemGroup>
</Project>
118 changes: 118 additions & 0 deletions tools/SdkVersionVerifier/ActionWorkflowVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using YamlDotNet.RepresentationModel;

namespace SdkVersionVerifier;

internal static class ActionWorkflowVerifier
{
public static DotnetSdkVersion? GetExpectedSdkVersionFromSampleWorkflow(string root)
{
var defaultWorkflow = Path.Combine(GetWorkflowsDirectory(root), "build.yml");
return ExtractDotnetSdkVersions(File.ReadAllText(defaultWorkflow)).FirstOrDefault();
}

public static bool VerifyVersions(string root, DotnetSdkVersion expectedDotnetSdkVersion)
{
var workflowsDir = GetWorkflowsDirectory(root);
var workflows = Directory.GetFiles(workflowsDir, "*.yml");

return FileVerifier.VerifyMultiple(workflows, VerifySdkVersions, expectedDotnetSdkVersion);
}

private static string GetWorkflowsDirectory(string root)
{
return Path.Combine(root, ".github", "workflows");
}

private static bool VerifySdkVersions(string content, DotnetSdkVersion expectedDotnetSdkVersion)
{
foreach (var extractedSdkVersion in ExtractDotnetSdkVersions(content))
{
if (!VersionComparer.CompareVersions(expectedDotnetSdkVersion, extractedSdkVersion))
{
return false;
}
}

return true;
}

private static IEnumerable<DotnetSdkVersion> ExtractDotnetSdkVersions(string content)
{
var workflow = new YamlStream();
using var stringReader = new StringReader(content);
workflow.Load(stringReader);

var jobs = ExtractJobs(workflow);
foreach (var job in jobs)
{
if (!job.Children.TryGetValue(new YamlScalarNode("steps"), out var stepsNode))
{
continue;
}

foreach (var step in (YamlSequenceNode)stepsNode)
{
var jobStepNode = (YamlMappingNode)step;
if (jobStepNode.Children.TryGetValue(new YamlScalarNode("uses"), out var usesNode) && usesNode.ToString().StartsWith("actions/setup-dotnet"))
{
var withNode = (YamlMappingNode)jobStepNode.Children[new YamlScalarNode("with")];
var dotnetVersionNode = (YamlScalarNode)withNode.Children[new YamlScalarNode("dotnet-version")];

var extractedVersion = ExtractVersion(dotnetVersionNode);
if (extractedVersion is not null)
{
yield return extractedVersion;
}
}
}
}
}

private static DotnetSdkVersion? ExtractVersion(YamlScalarNode dotnetVersionNode)
{
// Extract versions from the node value e.g.:
// dotnet-version: |
// 6.0.427
// 7.0.410
// 8.0.403

string? sdk6Version = null;
string? sdk7Version = null;
string? sdk8Version = null;

foreach (var version in dotnetVersionNode.ToString().Split())
{
if (version.StartsWith('6'))
{
sdk6Version = version;
}

if (version.StartsWith('7'))
{
sdk7Version = version;
}

if (version.StartsWith('8'))
{
sdk8Version = version;
}
}

if (sdk6Version is not null || sdk7Version is not null || sdk8Version is not null)
{
return new DotnetSdkVersion(sdk6Version, sdk7Version, sdk8Version);
}

return null;
}

private static IEnumerable<YamlMappingNode> ExtractJobs(YamlStream yaml)
{
var rootNode = yaml.Documents[0].RootNode as YamlMappingNode;
var jobsNode = (YamlMappingNode)rootNode!.Children[new YamlScalarNode("jobs")];
return jobsNode.Children.Select(j => (YamlMappingNode)j.Value);
}
}
63 changes: 63 additions & 0 deletions tools/SdkVersionVerifier/DockerfileVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Text.RegularExpressions;
using dockerfile;

namespace SdkVersionVerifier;

internal static partial class DockerfileVerifier
{
public static bool VerifyVersions(string root, DotnetSdkVersion expectedDotnetSdkVersion)
{
var dockerfilesDir = Path.Combine(root, "docker");
var dockerfiles = Directory.GetFiles(dockerfilesDir, "*.dockerfile");

return FileVerifier.VerifyMultiple(dockerfiles, VerifyVersionsFromDockerfiles, expectedDotnetSdkVersion);
}

[GeneratedRegex(@"-v (\d\.\d\.\d{3})\s", RegexOptions.IgnoreCase, "en-US")]
internal static partial Regex VersionRegex();

private static bool VerifyVersionsFromDockerfiles(string content, DotnetSdkVersion expectedDotnetSdkVersion)
{
using var stringReader = new StringReader(content);
var dockerfile = Dockerfile.Parse(stringReader);

string? net6SdkVersion = null;
string? net7SdkVersion = null;
string? net8SdkVersion = null;

foreach (var instruction in dockerfile.Instructions.Where(i => i.Arguments.Contains("./dotnet-install.sh")))
{
// Extract version from line like `&& ./dotnet-install.sh -v 6.0.427 --install-dir /usr/share/dotnet --no-path \`
var result = VersionRegex().Match(instruction.Arguments);
if (!result.Success)
{
continue;
}

var extractedSdkVersion = result.Groups[1].Value;
if (extractedSdkVersion.StartsWith('6'))
{
net6SdkVersion = extractedSdkVersion;
}
else if (extractedSdkVersion.StartsWith('7'))
{
net7SdkVersion = extractedSdkVersion;
}
}

// Extract NET8 SDK version from the base image tag
// e.g. FROM mcr.microsoft.com/dotnet/sdk:8.0.403-alpine3.20
var fromInstruction = dockerfile.Instructions
.SingleOrDefault(i => i.InstructionName == "FROM" && i.Arguments.StartsWith("mcr.microsoft.com/dotnet/sdk"));

if (fromInstruction is not null)
{
net8SdkVersion = fromInstruction.Arguments.Split(':')[1].Split('-')[0];
}

return VersionComparer.CompareVersions(expectedDotnetSdkVersion, net6SdkVersion, net7SdkVersion, net8SdkVersion);
}
}
6 changes: 6 additions & 0 deletions tools/SdkVersionVerifier/DotnetSdkVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace SdkVersionVerifier;

internal record DotnetSdkVersion(string? Net6SdkVersion, string? Net7SdkVersion, string? Net8SdkVersion);
29 changes: 29 additions & 0 deletions tools/SdkVersionVerifier/FileVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace SdkVersionVerifier;

internal static class FileVerifier
{
public static bool VerifyMultiple(
IEnumerable<string> filePaths,
Func<string, DotnetSdkVersion, bool> versionPredicate,
DotnetSdkVersion dotnetSdkVersion)
{
foreach (var filePath in filePaths)
{
Console.WriteLine($"Verifying SDK versions from {filePath}");
var content = File.ReadAllText(filePath);
var versionsMatch = versionPredicate(content, dotnetSdkVersion);
if (versionsMatch)
{
continue;
}

Console.WriteLine($"Invalid SDK versions in {filePath}");
return false;
}

return true;
}
}
43 changes: 43 additions & 0 deletions tools/SdkVersionVerifier/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace SdkVersionVerifier;

internal static class Program
{
public static int Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("Invalid arguments. Single argument with repository root is required.");
return 1;
}

var directoryRoot = args[0];

// Set expected dotnet SDK versions based on sample workflow.
// This set of versions will be expected to be used consistently
// in GitHub actions workflows and dockerfiles.
var expectedVersion = ActionWorkflowVerifier.GetExpectedSdkVersionFromSampleWorkflow(directoryRoot);
if (expectedVersion is null)
{
Console.WriteLine("Unable to extract expected SDK version from sample workflow file.");
return 1;
}

Console.WriteLine($"Expected SDK versions: {expectedVersion}");
if (!ActionWorkflowVerifier.VerifyVersions(directoryRoot, expectedVersion))
{
Console.WriteLine("Invalid SDK versions in GitHub actions workflows.");
return 1;
}

if (!DockerfileVerifier.VerifyVersions(directoryRoot, expectedVersion))
{
Console.WriteLine("Invalid SDK versions in dockerfiles.");
return 1;
}

return 0;
}
}
11 changes: 11 additions & 0 deletions tools/SdkVersionVerifier/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"SdkVersionVerifier": {
"commandName": "Project",
"commandLineArgs": "$(SolutionDir)",
"environmentVariables": {
}
}
}
}
15 changes: 15 additions & 0 deletions tools/SdkVersionVerifier/SdkVersionVerifier.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dockerfile" />
<PackageReference Include="YamlDotNet" />
</ItemGroup>

</Project>
Loading

0 comments on commit 1d515d0

Please sign in to comment.