Skip to content

Commit fb6d1ad

Browse files
authored
.NET 8 OS compatibility test (#3422)
* Revert "Warn for soon to be deprecated OS versions (#3413)" This reverts commit ae04147. * Add .NET 8 OS compatibility test * feedback
1 parent 7303cb5 commit fb6d1ad

20 files changed

+203
-418
lines changed

src/Runner.Common/Constants.cs

+2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ public static class System
280280
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
281281
public static readonly string JobRequestType = "system.jobRequestType";
282282
public static readonly string OrchestrationId = "system.orchestrationId";
283+
public static readonly string TestDotNet8Compatibility = "system.testDotNet8Compatibility";
284+
public static readonly string DotNet8CompatibilityWarning = "system.dotNet8CompatibilityWarning";
283285
}
284286
}
285287

src/Runner.Worker/JobExtension.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public async Task<List<IStep>> InitializeJob(IExecutionContext jobContext, Pipel
129129

130130
// Check OS warning
131131
var osWarningChecker = HostContext.GetService<IOSWarningChecker>();
132-
await osWarningChecker.CheckOSAsync(context, message.OSWarnings);
132+
await osWarningChecker.CheckOSAsync(context);
133133

134134
try
135135
{

src/Runner.Worker/OSWarningChecker.cs

+61-52
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Threading;
45
using System.Threading.Tasks;
5-
using System.Text.RegularExpressions;
66
using GitHub.DistributedTask.WebApi;
7-
using GitHub.DistributedTask.Pipelines;
87
using GitHub.Runner.Common;
98
using GitHub.Runner.Sdk;
109

@@ -13,75 +12,85 @@ namespace GitHub.Runner.Worker
1312
[ServiceLocator(Default = typeof(OSWarningChecker))]
1413
public interface IOSWarningChecker : IRunnerService
1514
{
16-
Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings);
15+
Task CheckOSAsync(IExecutionContext context);
1716
}
1817

19-
#if OS_WINDOWS || OS_OSX
2018
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
2119
{
22-
public Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings)
20+
public async Task CheckOSAsync(IExecutionContext context)
2321
{
2422
ArgUtil.NotNull(context, nameof(context));
25-
ArgUtil.NotNull(osWarnings, nameof(osWarnings));
26-
return Task.CompletedTask;
27-
}
28-
}
29-
#else
30-
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
31-
{
32-
private static readonly TimeSpan s_matchTimeout = TimeSpan.FromMilliseconds(100);
33-
private static readonly RegexOptions s_regexOptions = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;
34-
35-
public async Task CheckOSAsync(IExecutionContext context, IList<OSWarning> osWarnings)
36-
{
37-
ArgUtil.NotNull(context, nameof(context));
38-
ArgUtil.NotNull(osWarnings, nameof(osWarnings));
39-
foreach (var osWarning in osWarnings)
23+
if (!context.Global.Variables.System_TestDotNet8Compatibility)
4024
{
41-
if (string.IsNullOrEmpty(osWarning.FilePath))
42-
{
43-
Trace.Error("The file path is not specified in the OS warning check.");
44-
continue;
45-
}
25+
return;
26+
}
4627

47-
if (string.IsNullOrEmpty(osWarning.RegularExpression))
28+
context.Output("Testing runner upgrade compatibility");
29+
List<string> output = new();
30+
object outputLock = new();
31+
try
32+
{
33+
using (var process = HostContext.CreateService<IProcessInvoker>())
4834
{
49-
Trace.Error("The regular expression is not specified in the OS warning check.");
50-
continue;
51-
}
35+
process.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
36+
{
37+
if (!string.IsNullOrEmpty(stdout.Data))
38+
{
39+
lock (outputLock)
40+
{
41+
output.Add(stdout.Data);
42+
Trace.Info(stdout.Data);
43+
}
44+
}
45+
};
5246

53-
if (string.IsNullOrEmpty(osWarning.Warning))
54-
{
55-
Trace.Error("The warning message is not specified in the OS warning check.");
56-
continue;
57-
}
47+
process.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
48+
{
49+
if (!string.IsNullOrEmpty(stderr.Data))
50+
{
51+
lock (outputLock)
52+
{
53+
output.Add(stderr.Data);
54+
Trace.Error(stderr.Data);
55+
}
56+
}
57+
};
5858

59-
try
60-
{
61-
if (File.Exists(osWarning.FilePath))
59+
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
6260
{
63-
var lines = await File.ReadAllLinesAsync(osWarning.FilePath, context.CancellationToken);
64-
var regex = new Regex(osWarning.RegularExpression, s_regexOptions, s_matchTimeout);
65-
foreach (var line in lines)
61+
int exitCode = await process.ExecuteAsync(
62+
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
63+
fileName: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "testDotNet8Compatibility", $"TestDotNet8Compatibility{IOUtil.ExeExtension}"),
64+
arguments: string.Empty,
65+
environment: null,
66+
cancellationToken: cancellationTokenSource.Token);
67+
68+
var outputStr = string.Join("\n", output).Trim();
69+
if (exitCode != 0 || !string.Equals(outputStr, "Hello from .NET 8!", StringComparison.Ordinal))
6670
{
67-
if (regex.IsMatch(line))
71+
var warningMessage = context.Global.Variables.System_DotNet8CompatibilityWarning;
72+
if (!string.IsNullOrEmpty(warningMessage))
6873
{
69-
context.Warning(osWarning.Warning);
70-
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $"OS warning: {osWarning.Warning}" });
71-
return;
74+
context.Warning(warningMessage);
7275
}
76+
77+
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test failed with exit code '{exitCode}' and output: {GetShortOutput(output)}" });
7378
}
7479
}
7580
}
76-
catch (Exception ex)
77-
{
78-
Trace.Error("An error occurred while checking OS warnings for file '{0}' and regex '{1}'.", osWarning.FilePath, osWarning.RegularExpression);
79-
Trace.Error(ex);
80-
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}" });
81-
}
81+
}
82+
catch (Exception ex)
83+
{
84+
Trace.Error("An error occurred while testing .NET 8 compatibility'");
85+
Trace.Error(ex);
86+
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test encountered exception type '{ex.GetType().FullName}', message: '{ex.Message}', process output: '{GetShortOutput(output)}'" });
8287
}
8388
}
89+
90+
private static string GetShortOutput(List<string> output)
91+
{
92+
var outputStr = string.Join("\n", output).Trim();
93+
return outputStr.Length > 200 ? string.Concat(outputStr.Substring(0, 200), "[...]") : outputStr;
94+
}
8495
}
85-
#endif
8696
}
87-

src/Runner.Worker/Variables.cs

+4
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ public Variables(IHostContext hostContext, IDictionary<string, VariableValue> co
7272

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

75+
public string System_DotNet8CompatibilityWarning => Get(Constants.Variables.System.DotNet8CompatibilityWarning);
76+
7577
public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);
7678

79+
public bool System_TestDotNet8Compatibility => GetBoolean(Constants.Variables.System.TestDotNet8Compatibility) ?? false;
80+
7781
public string Get(string name)
7882
{
7983
Variable variable;

src/Sdk/DTPipelines/Pipelines/AgentJobRequestMessage.cs

-26
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public AgentJobRequestMessage(
4444
IList<TemplateToken> defaults,
4545
ActionsEnvironmentReference actionsEnvironment,
4646
TemplateToken snapshot,
47-
IList<OSWarning> osWarnings,
4847
String messageType = JobRequestMessageTypes.PipelineAgentJobRequest)
4948
{
5049
this.MessageType = messageType;
@@ -74,11 +73,6 @@ public AgentJobRequestMessage(
7473
m_defaults = new List<TemplateToken>(defaults);
7574
}
7675

77-
if (osWarnings?.Count > 0)
78-
{
79-
m_osWarnings = new List<OSWarning>(osWarnings);
80-
}
81-
8276
this.ContextData = new Dictionary<String, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
8377
if (contextData?.Count > 0)
8478
{
@@ -294,18 +288,6 @@ public IList<String> FileTable
294288
}
295289
}
296290

297-
public IList<OSWarning> OSWarnings
298-
{
299-
get
300-
{
301-
if (m_osWarnings == null)
302-
{
303-
m_osWarnings = new List<OSWarning>();
304-
}
305-
return m_osWarnings;
306-
}
307-
}
308-
309291
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
310292
public void SetJobSidecarContainers(IDictionary<String, String> value)
311293
{
@@ -443,11 +425,6 @@ private void OnSerializing(StreamingContext context)
443425
{
444426
JobContainer = new StringToken(null, null, null, m_jobContainerResourceAlias);
445427
}
446-
447-
if (m_osWarnings?.Count == 0)
448-
{
449-
m_osWarnings = null;
450-
}
451428
}
452429

453430
[DataMember(Name = "EnvironmentVariables", EmitDefaultValue = false)]
@@ -472,9 +449,6 @@ private void OnSerializing(StreamingContext context)
472449
[DataMember(Name = "JobSidecarContainers", EmitDefaultValue = false)]
473450
private IDictionary<String, String> m_jobSidecarContainers;
474451

475-
[DataMember(Name = "OSWarnings", EmitDefaultValue = false)]
476-
private List<OSWarning> m_osWarnings;
477-
478452
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
479453
[IgnoreDataMember]
480454
private string m_jobContainerResourceAlias;

src/Sdk/DTPipelines/Pipelines/OSWarning.cs

-44
This file was deleted.

src/Test/L0/Listener/JobDispatcherL0.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private Pipelines.AgentJobRequestMessage CreateJobRequestMessage()
4141
TaskOrchestrationPlanReference plan = new();
4242
TimelineReference timeline = null;
4343
Guid jobId = Guid.NewGuid();
44-
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null, null);
44+
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
4545
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
4646
return result;
4747
}
@@ -810,7 +810,6 @@ private static AgentJobRequestMessage GetAgentJobRequestMessage()
810810
null,
811811
new List<TemplateToken>(),
812812
new ActionsEnvironmentReference("env"),
813-
null,
814813
null
815814
);
816815
return message;

src/Test/L0/Listener/RunnerL0.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
4444
TaskOrchestrationPlanReference plan = new();
4545
TimelineReference timeline = null;
4646
Guid jobId = Guid.NewGuid();
47-
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null, null);
47+
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
4848
}
4949

5050
private JobCancelMessage CreateJobCancelMessage()

src/Test/L0/Worker/ActionCommandManagerL0.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void EchoProcessCommandDebugOn()
232232
TimelineReference timeline = new();
233233
Guid jobId = Guid.NewGuid();
234234
string jobName = "some job name";
235-
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null, null);
235+
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
236236
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
237237
{
238238
Alias = Pipelines.PipelineConstants.SelfAlias,

src/Test/L0/Worker/CreateStepSummaryCommandL0.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private TestHostContext Setup([CallerMemberName] string name = "")
193193
TimelineReference timeline = new();
194194
Guid jobId = Guid.NewGuid();
195195
string jobName = "Summary Job";
196-
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null, null);
196+
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
197197
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
198198
{
199199
Alias = Pipelines.PipelineConstants.SelfAlias,

0 commit comments

Comments
 (0)