Skip to content

Commit

Permalink
Add .NET 8 OS compatibility test
Browse files Browse the repository at this point in the history
  • Loading branch information
ericsciple committed Aug 7, 2024
1 parent 8731d15 commit 572cfb7
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 52 deletions.
2 changes: 2 additions & 0 deletions src/Runner.Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ public static class System
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
public static readonly string JobRequestType = "system.jobRequestType";
public static readonly string OrchestrationId = "system.orchestrationId";
public static readonly string TestDotNet8Compatibility = "system.testDotNet8Compatibility";
public static readonly string DotNet8CompatibilityWarning = "system.dotNet8CompatibilityWarning";
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Runner.Worker/JobExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task<List<IStep>> InitializeJob(IExecutionContext jobContext, Pipel

// Check OS warning
var osWarningChecker = HostContext.GetService<IOSWarningChecker>();
await osWarningChecker.CheckOSAsync(context, message.OSWarnings);
await osWarningChecker.CheckOSAsync(context);

try
{
Expand Down
104 changes: 53 additions & 51 deletions src/Runner.Worker/OSWarningChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,75 +13,77 @@ namespace GitHub.Runner.Worker
[ServiceLocator(Default = typeof(OSWarningChecker))]
public interface IOSWarningChecker : IRunnerService
{
Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings);
Task CheckOSAsync(IExecutionContext context);
}

#if OS_WINDOWS || OS_OSX
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
{
public Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings)
public async Task CheckOSAsync(IExecutionContext context)
{
ArgUtil.NotNull(context, nameof(context));
ArgUtil.NotNull(osWarnings, nameof(osWarnings));
return Task.CompletedTask;
}
}
#else
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
{
private static readonly TimeSpan s_matchTimeout = TimeSpan.FromMilliseconds(100);
private static readonly RegexOptions s_regexOptions = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;

public async Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings)
{
ArgUtil.NotNull(context, nameof(context));
ArgUtil.NotNull(osWarnings, nameof(osWarnings));
foreach (var osWarning in osWarnings)
if (!context.Global.Variables.System_TestDotNet8Compatibility)
{
if (string.IsNullOrEmpty(osWarning.FilePath))
{
Trace.Error("The file path is not specified in the OS warning check.");
continue;
}

if (string.IsNullOrEmpty(osWarning.RegularExpression))
{
Trace.Error("The regular expression is not specified in the OS warning check.");
continue;
}
return;
}

if (string.IsNullOrEmpty(osWarning.Warning))
context.Output("Testing runner upgrade compatibility");
try
{
List<string> output = new();
object outputLock = new();
using (var process = HostContext.CreateService<IProcessInvoker>())
{
Trace.Error("The warning message is not specified in the OS warning check.");
continue;
}
process.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
{
if (!string.IsNullOrEmpty(stdout.Data))
{
lock (outputLock)
{
output.Add(stdout.Data);
Trace.Info(stdout.Data);
}
}
};

try
{
if (File.Exists(osWarning.FilePath))
process.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
{
var lines = await File.ReadAllLinesAsync(osWarning.FilePath, context.CancellationToken);
var regex = new Regex(osWarning.RegularExpression, s_regexOptions, s_matchTimeout);
foreach (var line in lines)
if (!string.IsNullOrEmpty(stderr.Data))
{
if (regex.IsMatch(line))
lock (outputLock)
{
context.Warning(osWarning.Warning);
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $"OS warning: {osWarning.Warning}" });
return;
output.Add(stderr.Data);
Trace.Error(stderr.Data);
}
}
};

int exitCode = await process.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
fileName: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "testDotNet8Compatibility", $"TestDotNet8Compatibility{IOUtil.ExeExtension}"),
arguments: string.Empty,
environment: null,
cancellationToken: context.CancellationToken);

var outputStr = string.Join("\n", output).Trim();
if (exitCode != 0 || !string.Equals(outputStr, "Hello from .NET 8!", StringComparison.Ordinal))
{
var warningMessage = context.Global.Variables.System_DotNet8CompatibilityWarning;
if (!string.IsNullOrEmpty(warningMessage))
{
context.Warning(warningMessage);
}

var shortOutput = outputStr.Length > 200 ? string.Concat(outputStr.Substring(0, 200), "[...]") : outputStr;
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test failed with exit code '{exitCode}' and output: {shortOutput}" });
}
}
catch (Exception ex)
{
Trace.Error("An error occurred while checking OS warnings for file '{0}' and regex '{1}'.", osWarning.FilePath, osWarning.RegularExpression);
Trace.Error(ex);
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $"An error occurred while checking OS warnings for file '{osWarning.FilePath}' and regex '{osWarning.RegularExpression}': {ex.Message}" });
}
}
catch (Exception ex)
{
Trace.Error("An error occurred while testing .NET 8 compatibility'");
Trace.Error(ex);
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $"An error occurred while testing .NET 8 compatibility; exception type '{ex.GetType().FullName}'; message: {ex.Message}" });
}
}
}
#endif
}

4 changes: 4 additions & 0 deletions src/Runner.Worker/Variables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ public Variables(IHostContext hostContext, IDictionary<string, VariableValue> co

public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug);

public string System_DotNet8CompatibilityWarning => Get(Constants.Variables.System.DotNet8CompatibilityWarning);

public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);

public bool System_TestDotNet8Compatibility => GetBoolean(Constants.Variables.System.TestDotNet8Compatibility) ?? false;

public string Get(string name)
{
Variable variable;
Expand Down
1 change: 1 addition & 0 deletions src/Test/L0/Worker/JobExtensionL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ private TestHostContext CreateTestContext([CallerMemberName] String testName = "
hc.SetSingleton(_diagnosticLogManager.Object);
hc.SetSingleton(_jobHookProvider.Object);
hc.SetSingleton(_snapshotOperationProvider.Object);
hc.SetSingleton(new Mock<IOSWarningChecker>().Object);
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // JobExecutionContext
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // job start hook
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // Initial Job
Expand Down
13 changes: 13 additions & 0 deletions src/TestDotNet8Compatibility/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace TestDotNet8Compatibility
{
public static class Program
{
public static int Main(string[] args)
{
Console.WriteLine("Hello from .NET 8!");
return 1;
}
}
}
19 changes: 19 additions & 0 deletions src/TestDotNet8Compatibility/TestDotNet8Compatibility.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<Version>$(Version)</Version>
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugType>portable</DebugType>
</PropertyGroup>

</Project>
22 changes: 22 additions & 0 deletions src/TestDotNet8Compatibility/dir.proj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<ItemGroup>
<ProjectFiles Include="TestDotNet8Compatibility.csproj" />
</ItemGroup>

<Target Name="Build">
<MSBuild Targets="Restore" Projects="@(ProjectFiles)" StopOnFirstFailure="true" />
<MSBuild Targets="Publish" Projects="@(ProjectFiles)" BuildInParallel="false" StopOnFirstFailure="true" Properties="Configuration=$(BUILDCONFIG);PackageRuntime=$(PackageRuntime);Version=$(RunnerVersion);RuntimeIdentifier=$(PackageRuntime);PublishDir=$(MSBuildProjectDirectory)/../../_layout/bin/testDotNet8Compatibility" />
</Target>

<Target Name="Clean">
<RemoveDir Directories="$(MSBuildProjectDirectory)/../../_layout/bin/testDotNet8Compatibility" />
<RemoveDir Directories="TestDotNet8Compatibility/bin" />
<RemoveDir Directories="TestDotNet8Compatibility/obj" />
</Target>

<Target Name="Layout" DependsOnTargets="Clean;Build">
</Target>
</Project>
5 changes: 5 additions & 0 deletions src/TestDotNet8Compatibility/global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sdk": {
"version": "8.0.303"
}
}
54 changes: 54 additions & 0 deletions src/dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="6.0.421"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
DOTNET8SDK_VERSION="8.0.303"
DOTNET8SDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNET8SDK_VERSION"
RUNNER_VERSION=$(cat runnerversion)

pushd "$SCRIPT_DIR"
Expand Down Expand Up @@ -125,6 +127,19 @@ function build ()
{
heading "Building ..."
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build

# Build TestDotNet8Compatibility
heading "Building .NET 8 compatibility test"
echo "Prepend ${DOTNET8SDK_INSTALLDIR} to %PATH%" # Prepend .NET 8 SDK to PATH
PATH_BAK=$PATH
export PATH=${DOTNET8SDK_INSTALLDIR}:$PATH
pushd "$SCRIPT_DIR/TestDotNet8Compatibility" > /dev/null # Working directory
pwd
echo "Dotnet 8 SDK Version"
dotnet --version
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
popd > /dev/null # Restore working directory
export PATH=$PATH_BAK # Restore PATH
}

function layout ()
Expand All @@ -143,6 +158,18 @@ function layout ()

heading "Setup externals folder for $RUNTIME_ID runner's layout"
bash ./Misc/externals.sh $RUNTIME_ID || checkRC externals.sh

# Build TestDotNet8Compatibility
echo "Prepend ${DOTNET8SDK_INSTALLDIR} to %PATH%" # Prepend .NET 8 SDK to PATH
PATH_BAK=$PATH
export PATH=${DOTNET8SDK_INSTALLDIR}:$PATH
pushd "$SCRIPT_DIR/TestDotNet8Compatibility" > /dev/null # Working directory
heading "Dotnet 8 SDK Version"
dotnet --version
heading "Building .NET 8 compatibility test"
dotnet msbuild -t:layout -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
popd > /dev/null # Restore working directory
export PATH=$PATH_BAK # Restore PATH
}

function runtest ()
Expand Down Expand Up @@ -199,6 +226,7 @@ function package ()
popd > /dev/null
}

# Install .NET SDK
if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}") || (! -e "${DOTNETSDK_INSTALLDIR}/dotnet") ]]; then

# Download dotnet SDK to ../_dotnetsdk directory
Expand All @@ -224,6 +252,32 @@ if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTN
echo "${DOTNETSDK_VERSION}" > "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}"
fi

# Install .NET 8 SDK
if [[ (! -d "${DOTNET8SDK_INSTALLDIR}") || (! -e "${DOTNET8SDK_INSTALLDIR}/.${DOTNET8SDK_VERSION}") || (! -e "${DOTNET8SDK_INSTALLDIR}/dotnet") ]]; then

# Download dotnet 8 SDK to ../_dotnetsdk directory
heading "Ensure Dotnet 8 SDK"

# _dotnetsdk
# \1.0.x
# \dotnet
# \.1.0.x
echo "Download dotnet8sdk into ${DOTNET8SDK_INSTALLDIR}"
rm -Rf "${DOTNETSDK_DIR}"

# run dotnet-install.ps1 on windows, dotnet-install.sh on linux
if [[ ("$CURRENT_PLATFORM" == "windows") ]]; then
echo "Convert ${DOTNET8SDK_INSTALLDIR} to Windows style path"
sdkinstallwindow_path=${DOTNET8SDK_INSTALLDIR:1}
sdkinstallwindow_path=${sdkinstallwindow_path:0:1}:${sdkinstallwindow_path:1}
$POWERSHELL -NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "& \"./Misc/dotnet-install.ps1\" -Version ${DOTNET8SDK_VERSION} -InstallDir \"${sdkinstallwindow_path}\" -NoPath; exit \$LastExitCode;" || checkRC dotnet-install.ps1
else
bash ./Misc/dotnet-install.sh --version ${DOTNET8SDK_VERSION} --install-dir "${DOTNET8SDK_INSTALLDIR}" --no-path || checkRC dotnet-install.sh
fi

echo "${DOTNET8SDK_VERSION}" > "${DOTNET8SDK_INSTALLDIR}/.${DOTNET8SDK_VERSION}"
fi

echo "Prepend ${DOTNETSDK_INSTALLDIR} to %PATH%"
export PATH=${DOTNETSDK_INSTALLDIR}:$PATH

Expand Down

0 comments on commit 572cfb7

Please sign in to comment.