Compare commits

..

1 Commits

Author SHA1 Message Date
Bassem Dghaidi
2977a0fa02 Upgrade to dotnet 7.0 2023-05-03 14:16:37 +00:00
52 changed files with 352 additions and 1574 deletions

View File

@@ -2,7 +2,7 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0 as build
ARG RUNNER_VERSION
ARG RUNNER_ARCH="x64"
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.2
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.1
ARG DOCKER_VERSION=20.10.23
RUN apt update -y && apt install curl unzip -y

View File

@@ -132,7 +132,6 @@ namespace GitHub.Runner.Common
public static readonly string GenerateServiceConfig = "generateServiceConfig";
public static readonly string Help = "help";
public static readonly string Local = "local";
public static readonly string NoDefaultLabels = "no-default-labels";
public static readonly string Replace = "replace";
public static readonly string DisableUpdate = "disableupdate";
public static readonly string Once = "once"; // Keep this around since customers still relies on it
@@ -155,7 +154,6 @@ namespace GitHub.Runner.Common
{
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
public static readonly string Node12Warning = "DistributedTask.AddWarningToNode12Action";
public static readonly string LogTemplateErrorsToTraceWriter = "DistributedTask.LogTemplateErrorsToTraceWriter";
public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate";
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
}
@@ -263,7 +261,6 @@ namespace GitHub.Runner.Common
public static readonly string AccessToken = "system.accessToken";
public static readonly string Culture = "system.culture";
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
public static readonly string JobRequestType = "system.jobRequestType";
public static readonly string OrchestrationId = "system.orchestrationId";
}
}

View File

@@ -11,10 +11,10 @@ using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using GitHub.Services.OAuth;
using GitHub.Services.Results.Client;
using GitHub.Services.WebApi;
using GitHub.Services.WebApi.Utilities.Internal;
using GitHub.Services.Results.Client;
using GitHub.Services.OAuth;
namespace GitHub.Runner.Common
{
@@ -254,7 +254,7 @@ namespace GitHub.Runner.Common
{
failedAttemptsToPostBatchedLinesByWebsocket++;
Trace.Info($"Caught exception during append web console line to websocket, let's fallback to sending via non-websocket call (total calls: {totalBatchedLinesAttemptedByWebsocket}, failed calls: {failedAttemptsToPostBatchedLinesByWebsocket}, websocket state: {this._websocketClient?.State}).");
Trace.Verbose(ex.ToString());
Trace.Error(ex);
if (totalBatchedLinesAttemptedByWebsocket > _minWebsocketBatchedLinesCountToConsider)
{
// let's consider failure percentage

View File

@@ -756,17 +756,17 @@ namespace GitHub.Runner.Common
timelineRecord.State = rec.State ?? timelineRecord.State;
timelineRecord.WorkerName = rec.WorkerName ?? timelineRecord.WorkerName;
if (rec.ErrorCount > 0)
if (rec.ErrorCount != null && rec.ErrorCount > 0)
{
timelineRecord.ErrorCount = rec.ErrorCount;
}
if (rec.WarningCount > 0)
if (rec.WarningCount != null && rec.WarningCount > 0)
{
timelineRecord.WarningCount = rec.WarningCount;
}
if (rec.NoticeCount > 0)
if (rec.NoticeCount != null && rec.NoticeCount > 0)
{
timelineRecord.NoticeCount = rec.NoticeCount;
}
@@ -797,7 +797,7 @@ namespace GitHub.Runner.Common
foreach (var record in mergedRecords)
{
Trace.Verbose($" Record: t={record.RecordType}, n={record.Name}, s={record.State}, st={record.StartTime}, {record.PercentComplete}%, ft={record.FinishTime}, r={record.Result}: {record.CurrentOperation}");
if (record.Issues != null)
if (record.Issues != null && record.Issues.Count > 0)
{
foreach (var issue in record.Issues)
{
@@ -807,7 +807,7 @@ namespace GitHub.Runner.Common
}
}
if (record.Variables != null)
if (record.Variables != null && record.Variables.Count > 0)
{
foreach (var variable in record.Variables)
{

View File

@@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Services.Launch.Client;
using GitHub.Services.WebApi;
namespace GitHub.Runner.Common
{
[ServiceLocator(Default = typeof(LaunchServer))]
public interface ILaunchServer : IRunnerService
{
void InitializeLaunchClient(Uri uri, string token);
Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList, CancellationToken cancellationToken);
}
public sealed class LaunchServer : RunnerService, ILaunchServer
{
private LaunchHttpClient _launchClient;
public void InitializeLaunchClient(Uri uri, string token)
{
var httpMessageHandler = HostContext.CreateHttpClientHandler();
this._launchClient = new LaunchHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
}
public Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList,
CancellationToken cancellationToken)
{
if (_launchClient != null)
{
return _launchClient.GetResolveActionsDownloadInfoAsync(planId, jobId, actionReferenceList,
cancellationToken: cancellationToken);
}
throw new InvalidOperationException("Launch client is not initialized.");
}
}
}

View File

@@ -222,7 +222,7 @@ namespace GitHub.Runner.Common
{
var delay = BackoffTimerHelper.GetRandomBackoff(MinDelayForWebsocketReconnect, MaxDelayForWebsocketReconnect);
Trace.Info($"Websocket is not open, let's attempt to connect back again with random backoff {delay} ms.");
Trace.Verbose(ex.ToString());
Trace.Error(ex);
retries++;
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, delay);
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -29,8 +29,8 @@ namespace GitHub.Runner.Listener
private readonly Dictionary<string, string[]> validOptions = new()
{
// Valid configure flags and args
[Constants.Runner.CommandLine.Commands.Configure] =
new string[]
[Constants.Runner.CommandLine.Commands.Configure] =
new string[]
{
Constants.Runner.CommandLine.Flags.DisableUpdate,
Constants.Runner.CommandLine.Flags.Ephemeral,
@@ -38,7 +38,6 @@ namespace GitHub.Runner.Listener
Constants.Runner.CommandLine.Flags.Replace,
Constants.Runner.CommandLine.Flags.RunAsService,
Constants.Runner.CommandLine.Flags.Unattended,
Constants.Runner.CommandLine.Flags.NoDefaultLabels,
Constants.Runner.CommandLine.Args.Auth,
Constants.Runner.CommandLine.Args.Labels,
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
@@ -86,7 +85,6 @@ namespace GitHub.Runner.Listener
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
public bool GenerateServiceConfig => TestFlag(Constants.Runner.CommandLine.Flags.GenerateServiceConfig);
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
public bool NoDefaultLabels => TestFlag(Constants.Runner.CommandLine.Flags.NoDefaultLabels);
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
public bool RemoveLocalConfig => TestFlag(Constants.Runner.CommandLine.Flags.Local);
@@ -184,7 +182,7 @@ namespace GitHub.Runner.Listener
{
command = Constants.Runner.CommandLine.Commands.Warmup;
}
return command;
}

View File

@@ -137,7 +137,7 @@ namespace GitHub.Runner.Listener.Configuration
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
runnerSettings.ServerUrl = authResult.TenantUrl;
runnerSettings.UseV2Flow = authResult.UseV2Flow;
Trace.Info($"Using V2 flow: {runnerSettings.UseV2Flow}");
_term.WriteLine($"Using V2 flow: {runnerSettings.UseV2Flow}");
creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth");
}
@@ -259,7 +259,7 @@ namespace GitHub.Runner.Listener.Configuration
if (command.GetReplace())
{
// Update existing agent with new PublicKey, agent version.
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate, command.NoDefaultLabels);
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate);
try
{
@@ -293,7 +293,7 @@ namespace GitHub.Runner.Listener.Configuration
else
{
// Create a new agent.
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate, command.NoDefaultLabels);
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate);
try
{
@@ -554,7 +554,7 @@ namespace GitHub.Runner.Listener.Configuration
}
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate, bool noDefaultLabels)
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate)
{
ArgUtil.NotNull(agent, nameof(agent));
agent.Authorization = new TaskAgentAuthorization
@@ -571,16 +571,9 @@ namespace GitHub.Runner.Listener.Configuration
agent.Labels.Clear();
if (!noDefaultLabels)
{
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
}
else if (userLabels.Count == 0)
{
throw new NotSupportedException("Disabling default labels via --no-default-labels without specifying --labels is not supported");
}
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
foreach (var userLabel in userLabels)
{
@@ -590,7 +583,7 @@ namespace GitHub.Runner.Listener.Configuration
return agent;
}
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate, bool noDefaultLabels)
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate)
{
TaskAgent agent = new(agentName)
{
@@ -605,16 +598,9 @@ namespace GitHub.Runner.Listener.Configuration
DisableUpdate = disableUpdate
};
if (!noDefaultLabels)
{
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
}
else if (userLabels.Count == 0)
{
throw new NotSupportedException("Disabling default labels via --no-default-labels without specifying --labels is not supported");
}
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
foreach (var userLabel in userLabels)
{

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -683,8 +683,7 @@ Config Options:
--token string Registration token. Required if unattended
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
--runnergroup string Name of the runner group to add this runner to (defaults to the default runner group)
--labels string Custom labels that will be added to the runner. This option is mandatory if --no-default-labels is used.
--no-default-labels Disables adding the default labels: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
--local Removes the runner config files from your local machine. Used as an option to the remove command
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
--replace Replace any existing runner with the same name (default false)

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker.Container;
using System;
@@ -276,7 +276,7 @@ namespace GitHub.Runner.Worker
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(issue);
return;
}
@@ -315,7 +315,7 @@ namespace GitHub.Runner.Worker
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(issue);
}
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
@@ -350,7 +350,7 @@ namespace GitHub.Runner.Worker
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(issue);
}
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
@@ -666,7 +666,7 @@ namespace GitHub.Runner.Worker
}
}
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(issue);
}
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)

View File

@@ -11,14 +11,12 @@ using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container;
using GitHub.Services.Common;
using WebApi = GitHub.DistributedTask.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines;
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
using GitHub.DistributedTask.WebApi;
namespace GitHub.Runner.Worker
{
@@ -102,19 +100,7 @@ namespace GitHub.Runner.Worker
}
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
executionContext.Output("Prepare all required actions");
PrepareActionsState result = new PrepareActionsState();
try
{
result = await PrepareActionsRecursiveAsync(executionContext, state, actions, depth, rootStepId);
}
catch (FailedToResolveActionDownloadInfoException ex)
{
// Log the error and fail the PrepareActionsAsync Initialization.
Trace.Error($"Caught exception from PrepareActionsAsync Initialization: {ex}");
executionContext.InfrastructureError(ex.Message);
executionContext.Result = TaskResult.Failed;
throw;
}
var result = await PrepareActionsRecursiveAsync(executionContext, state, actions, depth, rootStepId);
if (!FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
{
if (state.ImagesToPull.Count > 0)
@@ -317,28 +303,15 @@ namespace GitHub.Runner.Worker
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
{
if (FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
Trace.Info("Load action that reference container from registry.");
CachedActionContainers.TryGetValue(action.Id, out var container);
ArgUtil.NotNull(container, nameof(container));
definition.Data.Execution = new ContainerActionExecutionData()
{
Trace.Info("Load action that will run container through container hooks.");
var containerAction = action.Reference as Pipelines.ContainerRegistryReference;
definition.Data.Execution = new ContainerActionExecutionData()
{
Image = containerAction.Image,
};
Trace.Info($"Using action container image: {containerAction.Image}.");
}
else
{
Trace.Info("Load action that reference container from registry.");
CachedActionContainers.TryGetValue(action.Id, out var container);
ArgUtil.NotNull(container, nameof(container));
definition.Data.Execution = new ContainerActionExecutionData()
{
Image = container.ContainerImage
};
Image = container.ContainerImage
};
Trace.Info($"Using action container image: {container.ContainerImage}.");
}
Trace.Info($"Using action container image: {container.ContainerImage}.");
}
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository)
{
@@ -675,21 +648,13 @@ namespace GitHub.Runner.Worker
}
// Resolve download info
var launchServer = HostContext.GetService<ILaunchServer>();
var jobServer = HostContext.GetService<IJobServer>();
var actionDownloadInfos = default(WebApi.ActionDownloadInfoCollection);
for (var attempt = 1; attempt <= 3; attempt++)
{
try
{
if (MessageUtil.IsRunServiceJob(executionContext.Global.Variables.Get(Constants.Variables.System.JobRequestType)))
{
actionDownloadInfos = await launchServer.ResolveActionsDownloadInfoAsync(executionContext.Global.Plan.PlanId, executionContext.Root.Id, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
}
else
{
actionDownloadInfos = await jobServer.ResolveActionDownloadInfoAsync(executionContext.Global.Plan.ScopeIdentifier, executionContext.Global.Plan.PlanType, executionContext.Global.Plan.PlanId, executionContext.Root.Id, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
}
actionDownloadInfos = await jobServer.ResolveActionDownloadInfoAsync(executionContext.Global.Plan.ScopeIdentifier, executionContext.Global.Plan.PlanType, executionContext.Global.Plan.PlanId, executionContext.Root.Id, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
break;
}
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is cancelled.

View File

@@ -52,7 +52,7 @@ namespace GitHub.Runner.Worker
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile)
{
var templateContext = CreateTemplateContext(executionContext, null, LogTemplateErrorsToTraceWriter(executionContext));
var templateContext = CreateTemplateContext(executionContext);
ActionDefinitionData actionDefinition = new();
// Clean up file name real quick
@@ -303,8 +303,7 @@ namespace GitHub.Runner.Worker
private TemplateContext CreateTemplateContext(
IExecutionContext executionContext,
IDictionary<string, PipelineContextData> extraExpressionValues = null,
bool addErrorsToTraceWriter = true)
IDictionary<string, PipelineContextData> extraExpressionValues = null)
{
var result = new TemplateContext
{
@@ -316,7 +315,6 @@ namespace GitHub.Runner.Worker
maxBytes: 10 * 1024 * 1024),
Schema = _actionManifestSchema,
TraceWriter = executionContext.ToTemplateTraceWriter(),
LogErrorsToTraceWriter = addErrorsToTraceWriter
};
// Expression values from execution context
@@ -450,7 +448,7 @@ namespace GitHub.Runner.Worker
};
}
}
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase) ||
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)||
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(mainToken?.Value))
@@ -541,13 +539,6 @@ namespace GitHub.Runner.Worker
}
}
}
private bool LogTemplateErrorsToTraceWriter(IExecutionContext executionContext)
{
if (executionContext == null || executionContext.Global == null || executionContext.Global.EnvironmentVariables == null) return true;
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Runner.Features.LogTemplateErrorsToTraceWriter, out var logErrorsAsDebug);
return StringUtil.ConvertToBoolean(logErrorsAsDebug, defaultValue: true);
}
}
}

View File

@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker.Container.ContainerHooks
public HookContainer(ContainerInfo container)
{
Image = container.ContainerImage;
EntryPointArgs = container.ContainerEntryPointArgs?.Split(' ').Select(arg => arg.Trim()).Where(arg => !string.IsNullOrEmpty(arg)) ?? new List<string>();
EntryPointArgs = container.ContainerEntryPointArgs?.Split(' ').Select(arg => arg.Trim()) ?? new List<string>();
EntryPoint = container.ContainerEntryPoint;
WorkingDirectory = container.ContainerWorkDirectory;
CreateOptions = container.ContainerCreateOptions;

View File

@@ -21,20 +21,13 @@ using Newtonsoft.Json;
using Sdk.RSWebApi.Contracts;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
using Pipelines = GitHub.DistributedTask.Pipelines;
using constants = GitHub.Runner.Common.Constants;
namespace GitHub.Runner.Worker
{
public static class ExecutionContextType
public class ExecutionContextType
{
public const string Job = "Job";
public const string Task = "Task";
}
public record ExecutionContextLogOptions(bool WriteToLog, string LogMessageOverride)
{
public static readonly ExecutionContextLogOptions None = new(false, null);
public static readonly ExecutionContextLogOptions Default = new(true, null);
public static string Job = "Job";
public static string Task = "Task";
}
[ServiceLocator(Default = typeof(ExecutionContext))]
@@ -100,7 +93,7 @@ namespace GitHub.Runner.Worker
void SetGitHubContext(string name, string value);
void SetOutput(string name, string value, out string reference);
void SetTimeout(TimeSpan? timeout);
void AddIssue(Issue issue, ExecutionContextLogOptions logOptions);
void AddIssue(Issue issue, string message = null);
void Progress(int percentage, string currentOperation = null);
void UpdateDetailTimelineRecord(TimelineRecord record);
@@ -126,7 +119,7 @@ namespace GitHub.Runner.Worker
public sealed class ExecutionContext : RunnerService, IExecutionContext
{
private const int _maxCountPerIssueType = 10;
private const int _maxIssueCount = 10;
private const int _throttlingDelayReportThreshold = 10 * 1000; // Don't report throttling with less than 10 seconds delay
private const int _maxIssueMessageLength = 4096; // Don't send issue with huge message since we can't forward them from actions to check annotation.
private const int _maxIssueCountInTelemetry = 3; // Only send the first 3 issues to telemetry
@@ -134,10 +127,8 @@ namespace GitHub.Runner.Worker
private readonly TimelineRecord _record = new();
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
private readonly List<Issue> _embeddedIssueCollector;
private readonly object _loggerLock = new();
private readonly object _matchersLock = new();
private readonly ExecutionContext _parentExecutionContext;
private event OnMatcherChanged _onMatcherChanged;
@@ -145,6 +136,7 @@ namespace GitHub.Runner.Worker
private IPagingLogger _logger;
private IJobServerQueue _jobServerQueue;
private ExecutionContext _parentExecutionContext;
private Guid _mainTimelineId;
private Guid _detailTimelineId;
@@ -158,29 +150,6 @@ namespace GitHub.Runner.Worker
private long _totalThrottlingDelayInMilliseconds = 0;
private bool _stepTelemetryPublished = false;
public ExecutionContext()
: this(parent: null, embedded: false)
{
}
private ExecutionContext(ExecutionContext parent, bool embedded)
{
if (embedded)
{
ArgUtil.NotNull(parent, nameof(parent));
}
_parentExecutionContext = parent;
this.IsEmbedded = embedded;
this.StepTelemetry = new ActionsStepTelemetry
{
IsEmbedded = embedded
};
//Embedded Execution Contexts pseudo-inherit their parent's embeddedIssueCollector.
_embeddedIssueCollector = embedded ? parent._embeddedIssueCollector : new();
}
public Guid Id => _record.Id;
public Guid EmbeddedId { get; private set; }
public string ScopeName { get; private set; }
@@ -193,7 +162,7 @@ namespace GitHub.Runner.Worker
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
public ActionsStepTelemetry StepTelemetry { get; private init; }
public ActionsStepTelemetry StepTelemetry { get; } = new ActionsStepTelemetry();
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
@@ -218,7 +187,7 @@ namespace GitHub.Runner.Worker
// An embedded execution context shares the same record ID, record name, and logger
// as its enclosing execution context.
public bool IsEmbedded { get; private init; }
public bool IsEmbedded { get; private set; }
public TaskResult? Result
{
@@ -353,7 +322,7 @@ namespace GitHub.Runner.Worker
{
Trace.Entering();
var child = new ExecutionContext(this, isEmbedded);
var child = new ExecutionContext();
child.Initialize(HostContext);
child.Global = Global;
child.ScopeName = scopeName;
@@ -378,6 +347,7 @@ namespace GitHub.Runner.Worker
child.ExpressionFunctions.Add(item);
}
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
child._parentExecutionContext = this;
child.EchoOnActionCommand = EchoOnActionCommand;
if (recordOrder != null)
@@ -398,9 +368,11 @@ namespace GitHub.Runner.Worker
child._logger.Setup(_mainTimelineId, recordId);
}
child.IsEmbedded = isEmbedded;
child.StepTelemetry.StepId = recordId;
child.StepTelemetry.Stage = stage.ToString();
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName();
child.StepTelemetry.IsEmbedded = isEmbedded;
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
return child;
}
@@ -442,24 +414,13 @@ namespace GitHub.Runner.Worker
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
}
DateTime now = DateTime.UtcNow;
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
_record.ResultCode = resultCode ?? _record.ResultCode;
_record.FinishTime = now;
_record.FinishTime = DateTime.UtcNow;
_record.PercentComplete = 100;
_record.Result = _record.Result ?? TaskResult.Succeeded;
_record.State = TimelineRecordState.Completed;
// Before our main timeline's final QueueTimelineRecordUpdate,
// inject any issues collected by embedded ExecutionContexts.
if (!this.IsEmbedded)
{
foreach (var issue in _embeddedIssueCollector)
{
AddIssue(issue, ExecutionContextLogOptions.None);
}
}
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
// complete all detail timeline records.
@@ -467,7 +428,7 @@ namespace GitHub.Runner.Worker
{
foreach (var record in _detailRecords)
{
record.Value.FinishTime = record.Value.FinishTime ?? now;
record.Value.FinishTime = record.Value.FinishTime ?? DateTime.UtcNow;
record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
record.Value.State = TimelineRecordState.Completed;
@@ -611,10 +572,14 @@ namespace GitHub.Runner.Worker
}
// This is not thread safe, the caller need to take lock before calling issue()
public void AddIssue(Issue issue, ExecutionContextLogOptions logOptions)
public void AddIssue(Issue issue, string logMessage = null)
{
ArgUtil.NotNull(issue, nameof(issue));
ArgUtil.NotNull(logOptions, nameof(logOptions));
if (string.IsNullOrEmpty(logMessage))
{
logMessage = issue.Message;
}
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
if (issue.Message.Length > _maxIssueMessageLength)
@@ -629,64 +594,53 @@ namespace GitHub.Runner.Worker
issue.Data["stepNumber"] = _record.Order.ToString();
}
string wellKnownTag = null;
Int32 previousCountForIssueType = 0;
Action incrementIssueTypeCount = NoOp;
switch (issue.Type)
if (issue.Type == IssueType.Error)
{
case IssueType.Error:
wellKnownTag = WellKnownTags.Error;
previousCountForIssueType = _record.ErrorCount;
incrementIssueTypeCount = () => { _record.ErrorCount++; };
break;
case IssueType.Warning:
wellKnownTag = WellKnownTags.Warning;
previousCountForIssueType = _record.WarningCount;
incrementIssueTypeCount = () => { _record.WarningCount++; };
break;
case IssueType.Notice:
wellKnownTag = WellKnownTags.Notice;
previousCountForIssueType = _record.NoticeCount;
incrementIssueTypeCount = () => { _record.NoticeCount++; };
break;
}
if (!string.IsNullOrEmpty(wellKnownTag))
{
if (!this.IsEmbedded && previousCountForIssueType < _maxCountPerIssueType)
if (!string.IsNullOrEmpty(logMessage))
{
long logLineNumber = Write(WellKnownTags.Error, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
if (_record.ErrorCount < _maxIssueCount)
{
incrementIssueTypeCount();
_record.Issues.Add(issue);
}
if (logOptions.WriteToLog)
_record.ErrorCount++;
}
else if (issue.Type == IssueType.Warning)
{
if (!string.IsNullOrEmpty(logMessage))
{
string logMessage = issue.Message;
if (!string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
logMessage = logOptions.LogMessageOverride;
}
if (!string.IsNullOrEmpty(logMessage))
{
// Note that ::Write() has its own secret-masking logic.
long logLineNumber = Write(wellKnownTag, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
long logLineNumber = Write(WellKnownTags.Warning, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
if (_record.WarningCount < _maxIssueCount)
{
_record.Issues.Add(issue);
}
_record.WarningCount++;
}
else if (issue.Type == IssueType.Notice)
{
if (!string.IsNullOrEmpty(logMessage))
{
long logLineNumber = Write(WellKnownTags.Notice, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
if (_record.NoticeCount < _maxIssueCount)
{
_record.Issues.Add(issue);
}
_record.NoticeCount++;
}
// Embedded ExecutionContexts (a.k.a. Composite actions) should never upload a timeline record to the server.
// Instead, we store processed issues on a shared (psuedo-inherited) list (belonging to the closest
// non-embedded ancestor ExecutionContext) so that they can be processed when that ancestor completes.
if (this.IsEmbedded)
{
_embeddedIssueCollector.Add(issue);
}
else
{
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
}
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
}
public void UpdateDetailTimelineRecord(TimelineRecord record)
@@ -803,9 +757,6 @@ namespace GitHub.Runner.Worker
// File table
Global.FileTable = new List<String>(message.FileTable ?? new string[0]);
// What type of job request is running (i.e. Run Service vs. pipelines)
Global.Variables.Set(Constants.Variables.System.JobRequestType, message.MessageType);
// Expression values
if (message.ContextData?.Count > 0)
{
@@ -1060,7 +1011,8 @@ namespace GitHub.Runner.Worker
StepTelemetry.FinishTime = _record.FinishTime;
}
if (!IsEmbedded)
if (!IsEmbedded &&
_record.Issues.Count > 0)
{
foreach (var issue in _record.Issues)
{
@@ -1226,11 +1178,6 @@ namespace GitHub.Runner.Worker
UpdateGlobalStepsContext();
}
private static void NoOp()
{
}
}
// The Error/Warning/etc methods are created as extension methods to simplify unit testing.
@@ -1255,22 +1202,19 @@ namespace GitHub.Runner.Worker
// Do not add a format string overload. See comment on ExecutionContext.Write().
public static void Error(this IExecutionContext context, string message)
{
var issue = new Issue() { Type = IssueType.Error, Message = message };
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message });
}
// Do not add a format string overload. See comment on ExecutionContext.Write().
public static void InfrastructureError(this IExecutionContext context, string message)
{
var issue = new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true };
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
}
// Do not add a format string overload. See comment on ExecutionContext.Write().
public static void Warning(this IExecutionContext context, string message)
{
var issue = new Issue() { Type = IssueType.Warning, Message = message };
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(new Issue() { Type = IssueType.Warning, Message = message });
}
// Do not add a format string overload. See comment on ExecutionContext.Write().
@@ -1426,5 +1370,4 @@ namespace GitHub.Runner.Worker
public static readonly string Notice = "##[notice]";
public static readonly string Debug = "##[debug]";
}
}

View File

@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
// Evaluation error
Trace.Info("Caught exception from expression for embedded step.env");
step.ExecutionContext.Error(ex);
SetStepConclusion(step, TaskResult.Failed);
step.ExecutionContext.Complete(TaskResult.Failed);
}
// Register Callback

View File

@@ -143,8 +143,7 @@ namespace GitHub.Runner.Worker.Handlers
if (issue != null)
{
// Log issue
var logOptions = new ExecutionContextLogOptions(true, stripped);
_executionContext.AddIssue(issue, logOptions);
_executionContext.AddIssue(issue, stripped);
return;
}

View File

@@ -431,6 +431,14 @@ namespace GitHub.Runner.Worker
context.Result = TaskResult.Canceled;
throw;
}
catch (FailedToResolveActionDownloadInfoException ex)
{
// Log the error and fail the JobExtension Initialization.
Trace.Error($"Caught exception from JobExtenion Initialization: {ex}");
context.InfrastructureError(ex.Message);
context.Result = TaskResult.Failed;
throw;
}
catch (Exception ex)
{
// Log the error and fail the JobExtension Initialization.
@@ -675,7 +683,7 @@ namespace GitHub.Runner.Worker
{
var issue = new Issue() { Type = IssueType.Warning, Message = $"You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: {freeSpaceInMB} MB" };
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.LowDiskSpace;
context.AddIssue(issue, ExecutionContextLogOptions.Default);
context.AddIssue(issue);
return;
}

View File

@@ -58,18 +58,6 @@ namespace GitHub.Runner.Worker
await runServer.ConnectAsync(systemConnection.Url, jobServerCredential);
server = runServer;
message.Variables.TryGetValue("system.github.launch_endpoint", out VariableValue launchEndpointVariable);
var launchReceiverEndpoint = launchEndpointVariable?.Value;
if (systemConnection?.Authorization != null &&
systemConnection.Authorization.Parameters.TryGetValue("AccessToken", out var accessToken) &&
!string.IsNullOrEmpty(accessToken) &&
!string.IsNullOrEmpty(launchReceiverEndpoint))
{
Trace.Info("Initializing launch client");
var launchServer = HostContext.GetService<ILaunchServer>();
launchServer.InitializeLaunchClient(new Uri(launchReceiverEndpoint), accessToken);
}
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
_jobServerQueue.Start(message, resultServiceOnly: true);
}
@@ -120,8 +108,7 @@ namespace GitHub.Runner.Worker
default:
throw new ArgumentException(HostContext.RunnerShutdownReason.ToString(), nameof(HostContext.RunnerShutdownReason));
}
var issue = new Issue() { Type = IssueType.Error, Message = errorMessage };
jobContext.AddIssue(issue, ExecutionContextLogOptions.Default);
jobContext.AddIssue(new Issue() { Type = IssueType.Error, Message = errorMessage });
});
// Validate directory permissions.
@@ -150,11 +137,6 @@ namespace GitHub.Runner.Worker
_runnerSettings = HostContext.GetService<IConfigurationStore>().GetSettings();
jobContext.SetRunnerContext("name", _runnerSettings.AgentName);
if (jobContext.Global.Variables.TryGetValue(WellKnownDistributedTaskVariables.RunnerEnvironment, out var runnerEnvironment))
{
jobContext.SetRunnerContext("environment", runnerEnvironment);
}
string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools);
Directory.CreateDirectory(toolsDirectory);
jobContext.SetRunnerContext("tool_cache", toolsDirectory);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@@ -34,11 +34,6 @@ namespace GitHub.DistributedTask.ObjectTemplating
m_errors = value;
}
}
/// <summary>
/// Because TraceWriter has access to the ExecutionContext and is logging template errors, duplicated issues are reported.
/// By setting LogErrorsToTraceWriter = false TemplateContext will add errors only to TemplateValidationErrors
/// </summary>
internal bool LogErrorsToTraceWriter = true;
/// <summary>
/// Available functions within expression contexts
@@ -125,9 +120,15 @@ namespace GitHub.DistributedTask.ObjectTemplating
}
}
internal void Error(TemplateValidationError error)
{
Errors.Add(error);
TraceWriter.Error(error.Message);
}
internal void Error(
TemplateToken value,
Exception ex)
TemplateToken value,
Exception ex)
{
Error(value?.FileId, value?.Line, value?.Column, ex);
}
@@ -140,11 +141,7 @@ namespace GitHub.DistributedTask.ObjectTemplating
{
var prefix = GetErrorPrefix(fileId, line, column);
Errors.Add(prefix, ex);
if (LogErrorsToTraceWriter)
{
TraceWriter.Error(prefix, ex);
}
TraceWriter.Error(prefix, ex);
}
internal void Error(
@@ -161,13 +158,13 @@ namespace GitHub.DistributedTask.ObjectTemplating
String message)
{
var prefix = GetErrorPrefix(fileId, line, column);
var fullMessage = !String.IsNullOrEmpty(prefix) ? $"{prefix} {message}" : message;
Errors.Add(fullMessage);
if (LogErrorsToTraceWriter)
if (!String.IsNullOrEmpty(prefix))
{
TraceWriter.Error(fullMessage);
message = $"{prefix} {message}";
}
Errors.Add(message);
TraceWriter.Error(message);
}
internal INamedValueInfo[] GetExpressionNamedValues()

View File

@@ -1,7 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using GitHub.Services.Common;
namespace GitHub.DistributedTask.WebApi
{
@@ -9,78 +10,69 @@ namespace GitHub.DistributedTask.WebApi
public sealed class TimelineRecord
{
public TimelineRecord()
: this(null)
{
this.Attempt = 1;
}
private TimelineRecord(TimelineRecord recordToBeCloned)
{
this.EnsureInitialized();
this.Attempt = recordToBeCloned.Attempt;
this.ChangeId = recordToBeCloned.ChangeId;
this.CurrentOperation = recordToBeCloned.CurrentOperation;
this.FinishTime = recordToBeCloned.FinishTime;
this.Id = recordToBeCloned.Id;
this.Identifier = recordToBeCloned.Identifier;
this.LastModified = recordToBeCloned.LastModified;
this.Location = recordToBeCloned.Location;
this.Name = recordToBeCloned.Name;
this.Order = recordToBeCloned.Order;
this.ParentId = recordToBeCloned.ParentId;
this.PercentComplete = recordToBeCloned.PercentComplete;
this.RecordType = recordToBeCloned.RecordType;
this.Result = recordToBeCloned.Result;
this.ResultCode = recordToBeCloned.ResultCode;
this.StartTime = recordToBeCloned.StartTime;
this.State = recordToBeCloned.State;
this.TimelineId = recordToBeCloned.TimelineId;
this.WorkerName = recordToBeCloned.WorkerName;
this.RefName = recordToBeCloned.RefName;
this.ErrorCount = recordToBeCloned.ErrorCount;
this.WarningCount = recordToBeCloned.WarningCount;
this.NoticeCount = recordToBeCloned.NoticeCount;
this.AgentPlatform = recordToBeCloned.AgentPlatform;
if (recordToBeCloned != null)
if (recordToBeCloned.Log != null)
{
this.Attempt = recordToBeCloned.Attempt;
this.ChangeId = recordToBeCloned.ChangeId;
this.CurrentOperation = recordToBeCloned.CurrentOperation;
this.FinishTime = recordToBeCloned.FinishTime;
this.Id = recordToBeCloned.Id;
this.Identifier = recordToBeCloned.Identifier;
this.LastModified = recordToBeCloned.LastModified;
this.Location = recordToBeCloned.Location;
this.Name = recordToBeCloned.Name;
this.Order = recordToBeCloned.Order;
this.ParentId = recordToBeCloned.ParentId;
this.PercentComplete = recordToBeCloned.PercentComplete;
this.RecordType = recordToBeCloned.RecordType;
this.Result = recordToBeCloned.Result;
this.ResultCode = recordToBeCloned.ResultCode;
this.StartTime = recordToBeCloned.StartTime;
this.State = recordToBeCloned.State;
this.TimelineId = recordToBeCloned.TimelineId;
this.WorkerName = recordToBeCloned.WorkerName;
this.RefName = recordToBeCloned.RefName;
this.ErrorCount = recordToBeCloned.ErrorCount;
this.WarningCount = recordToBeCloned.WarningCount;
this.NoticeCount = recordToBeCloned.NoticeCount;
this.AgentPlatform = recordToBeCloned.AgentPlatform;
if (recordToBeCloned.Log != null)
this.Log = new TaskLogReference
{
this.Log = new TaskLogReference
{
Id = recordToBeCloned.Log.Id,
Location = recordToBeCloned.Log.Location,
};
}
Id = recordToBeCloned.Log.Id,
Location = recordToBeCloned.Log.Location,
};
}
if (recordToBeCloned.Details != null)
if (recordToBeCloned.Details != null)
{
this.Details = new TimelineReference
{
this.Details = new TimelineReference
{
ChangeId = recordToBeCloned.Details.ChangeId,
Id = recordToBeCloned.Details.Id,
Location = recordToBeCloned.Details.Location,
};
}
ChangeId = recordToBeCloned.Details.ChangeId,
Id = recordToBeCloned.Details.Id,
Location = recordToBeCloned.Details.Location,
};
}
if (recordToBeCloned.m_issues?.Count > 0)
{
this.Issues.AddRange(recordToBeCloned.Issues.Select(i => i.Clone()));
}
if (recordToBeCloned.m_issues?.Count> 0)
{
this.Issues.AddRange(recordToBeCloned.Issues.Select(i => i.Clone()));
}
if (recordToBeCloned.m_previousAttempts?.Count > 0)
{
this.m_previousAttempts.AddRange(recordToBeCloned.m_previousAttempts);
}
if (recordToBeCloned.m_previousAttempts?.Count > 0)
{
this.PreviousAttempts.AddRange(recordToBeCloned.PreviousAttempts);
}
if (recordToBeCloned.m_variables?.Count > 0)
{
// Don't pave over the case-insensitive Dictionary we initialized above.
foreach (var kvp in recordToBeCloned.m_variables)
{
m_variables[kvp.Key] = kvp.Value.Clone();
}
}
if (recordToBeCloned.m_variables?.Count > 0)
{
this.m_variables = recordToBeCloned.Variables.ToDictionary(k => k.Key, v => v.Value.Clone());
}
}
@@ -106,14 +98,14 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Name = "Type", Order = 3)]
public string RecordType
public String RecordType
{
get;
set;
}
[DataMember(Order = 4)]
public string Name
public String Name
{
get;
set;
@@ -134,7 +126,7 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Order = 7)]
public string CurrentOperation
public String CurrentOperation
{
get;
set;
@@ -162,7 +154,7 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Order = 11)]
public string ResultCode
public String ResultCode
{
get;
set;
@@ -183,7 +175,7 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Order = 14)]
public string WorkerName
public String WorkerName
{
get;
set;
@@ -197,7 +189,7 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Order = 16, EmitDefaultValue = false)]
public string RefName
public String RefName
{
get;
set;
@@ -217,46 +209,35 @@ namespace GitHub.DistributedTask.WebApi
set;
}
public Int32 ErrorCount
[DataMember(Order = 40)]
public Int32? ErrorCount
{
get
{
return m_errorCount.GetValueOrDefault(0);
}
set
{
m_errorCount = value;
}
get;
set;
}
public Int32 WarningCount
[DataMember(Order = 50)]
public Int32? WarningCount
{
get
{
return m_warningCount.GetValueOrDefault(0);
}
set
{
m_warningCount = value;
}
get;
set;
}
public Int32 NoticeCount
[DataMember(Order = 55)]
public Int32? NoticeCount
{
get
{
return m_noticeCount.GetValueOrDefault(0);
}
set
{
m_noticeCount = value;
}
get;
set;
}
public List<Issue> Issues
{
get
{
if (m_issues == null)
{
m_issues = new List<Issue>();
}
return m_issues;
}
}
@@ -276,7 +257,7 @@ namespace GitHub.DistributedTask.WebApi
}
[DataMember(Order = 131)]
public string Identifier
public String Identifier
{
get;
set;
@@ -293,14 +274,22 @@ namespace GitHub.DistributedTask.WebApi
{
get
{
if (m_previousAttempts == null)
{
m_previousAttempts = new List<TimelineAttempt>();
}
return m_previousAttempts;
}
}
public IDictionary<string, VariableValue> Variables
public IDictionary<String, VariableValue> Variables
{
get
{
if (m_variables == null)
{
m_variables = new Dictionary<String, VariableValue>(StringComparer.OrdinalIgnoreCase);
}
return m_variables;
}
}
@@ -310,53 +299,13 @@ namespace GitHub.DistributedTask.WebApi
return new TimelineRecord(this);
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
this.EnsureInitialized();
}
/// <summary>
/// DataContractSerializer bypasses all constructor logic and inline initialization!
/// This method takes the place of a workhorse constructor for baseline initialization.
/// The expectation is for this logic to be accessible to constructors and also to the OnDeserialized helper.
/// </summary>
private void EnsureInitialized()
{
// Note that ?? is a short-circuiting operator. (??= would be preferable, but it's not supported in the .NET Framework version currently used by actions/runner.)
// De-nullify the following historically-nullable ints.
// (After several weeks in production, it may be possible to eliminate these nullable backing fields.)
m_errorCount = m_errorCount ?? 0;
m_warningCount = m_warningCount ?? 0;
m_noticeCount = m_noticeCount ?? 0;
m_issues = m_issues ?? new List<Issue>();
m_previousAttempts = m_previousAttempts ?? new List<TimelineAttempt>();
this.Attempt = Math.Max(this.Attempt, 1);
// Ensure whatever content may have been deserialized for m_variables is backed by a case-insensitive Dictionary.
var empty = Enumerable.Empty<KeyValuePair<string, VariableValue>>();
m_variables = new Dictionary<string, VariableValue>(m_variables ?? empty, StringComparer.OrdinalIgnoreCase);
}
[DataMember(Name = nameof(ErrorCount), Order = 40)]
private Int32? m_errorCount;
[DataMember(Name = nameof(WarningCount), Order = 50)]
private Int32? m_warningCount;
[DataMember(Name = nameof(NoticeCount), Order = 55)]
private Int32? m_noticeCount;
[DataMember(Name = nameof(Issues), EmitDefaultValue = false, Order = 60)]
[DataMember(Name = "Issues", EmitDefaultValue = false, Order = 60)]
private List<Issue> m_issues;
[DataMember(Name = nameof(Variables), EmitDefaultValue = false, Order = 80)]
private Dictionary<string, VariableValue> m_variables;
[DataMember(Name = "Variables", EmitDefaultValue = false, Order = 80)]
private Dictionary<String, VariableValue> m_variables;
[DataMember(Name = nameof(PreviousAttempts), EmitDefaultValue = false, Order = 120)]
[DataMember(Name = "PreviousAttempts", EmitDefaultValue = false, Order = 120)]
private List<TimelineAttempt> m_previousAttempts;
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace GitHub.DistributedTask.WebApi
{
@@ -6,6 +6,5 @@ namespace GitHub.DistributedTask.WebApi
{
public static readonly String JobId = "system.jobId";
public static readonly String RunnerLowDiskspaceThreshold = "system.runner.lowdiskspacethreshold";
public static readonly String RunnerEnvironment = "system.runnerEnvironment";
}
}

View File

@@ -7,5 +7,10 @@ namespace GitHub.Actions.RunService.WebApi
{
[DataMember(Name = "jobMessageId", EmitDefaultValue = false)]
public string JobMessageId { get; set; }
// This field will be removed in an upcoming Runner release.
// It's left here temporarily to facilitate the transition to the new field name, JobMessageId.
[DataMember(Name = "streamId", EmitDefaultValue = false)]
public string StreamId { get; set; }
}
}

View File

@@ -65,6 +65,7 @@ namespace GitHub.Actions.RunService.WebApi
var payload = new AcquireJobRequest
{
JobMessageId = messageId,
StreamId = messageId,
};
requestUri = new Uri(requestUri, "acquirejob");

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<DefineConstants>TRACE</DefineConstants>
<LangVersion>8.0</LangVersion>
<LangVersion>7.3</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

View File

@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace GitHub.Services.Launch.Contracts
{
[DataContract]
public class ActionReferenceRequest
{
[DataMember(EmitDefaultValue = false, Name = "action")]
public string Action { get; set; }
[DataMember(EmitDefaultValue = false, Name = "version")]
public string Version { get; set; }
[DataMember(EmitDefaultValue = false, Name = "path")]
public string Path { get; set; }
}
[DataContract]
public class ActionReferenceRequestList
{
[DataMember(EmitDefaultValue = false, Name = "actions")]
public IList<ActionReferenceRequest> Actions { get; set; }
}
[DataContract]
public class ActionDownloadInfoResponse
{
[DataMember(EmitDefaultValue = false, Name = "authentication")]
public ActionDownloadAuthenticationResponse Authentication { get; set; }
[DataMember(EmitDefaultValue = false, Name = "name")]
public string Name { get; set; }
[DataMember(EmitDefaultValue = false, Name = "resolved_name")]
public string ResolvedName { get; set; }
[DataMember(EmitDefaultValue = false, Name = "resolved_sha")]
public string ResolvedSha { get; set; }
[DataMember(EmitDefaultValue = false, Name = "tar_url")]
public string TarUrl { get; set; }
[DataMember(EmitDefaultValue = false, Name = "version")]
public string Version { get; set; }
[DataMember(EmitDefaultValue = false, Name = "zip_url")]
public string ZipUrl { get; set; }
}
[DataContract]
public class ActionDownloadAuthenticationResponse
{
[DataMember(EmitDefaultValue = false, Name = "expires_at")]
public DateTime ExpiresAt { get; set; }
[DataMember(EmitDefaultValue = false, Name = "token")]
public string Token { get; set; }
}
[DataContract]
public class ActionDownloadInfoResponseCollection
{
/// <summary>A mapping of action specifications to their download information.</summary>
/// <remarks>The key is the full name of the action plus version, e.g. "actions/checkout@v2".</remarks>
[DataMember(EmitDefaultValue = false, Name = "actions")]
public IDictionary<string, ActionDownloadInfoResponse> Actions { get; set; }
}
}

View File

@@ -1,115 +0,0 @@
#nullable enable
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Services.Launch.Contracts;
using Sdk.WebApi.WebApi;
namespace GitHub.Services.Launch.Client
{
public class LaunchHttpClient : RawHttpClientBase
{
public LaunchHttpClient(
Uri baseUrl,
HttpMessageHandler pipeline,
string token,
bool disposeHandler)
: base(baseUrl, pipeline, disposeHandler)
{
m_token = token;
m_launchServiceUrl = baseUrl;
m_formatter = new JsonMediaTypeFormatter();
}
public async Task<ActionDownloadInfoCollection> GetResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList, CancellationToken cancellationToken)
{
var GetResolveActionsDownloadInfoURLEndpoint = new Uri(m_launchServiceUrl, $"/actions/build/{planId.ToString()}/jobs/{jobId.ToString()}/runnerresolve/actions");
return ToServerData(await GetLaunchSignedURLResponse<ActionReferenceRequestList, ActionDownloadInfoResponseCollection>(GetResolveActionsDownloadInfoURLEndpoint, ToGitHubData(actionReferenceList), cancellationToken));
}
// Resolve Actions
private async Task<T> GetLaunchSignedURLResponse<R, T>(Uri uri, R request, CancellationToken cancellationToken)
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, uri))
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", m_token);
requestMessage.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
using (HttpContent content = new ObjectContent<R>(request, m_formatter))
{
requestMessage.Content = content;
using (var response = await SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken: cancellationToken))
{
return await ReadJsonContentAsync<T>(response, cancellationToken);
}
}
}
}
private static ActionReferenceRequestList ToGitHubData(ActionReferenceList actionReferenceList)
{
return new ActionReferenceRequestList
{
Actions = actionReferenceList.Actions?.Select(ToGitHubData).ToList()
};
}
private static ActionReferenceRequest ToGitHubData(ActionReference actionReference)
{
return new ActionReferenceRequest
{
Action = actionReference.NameWithOwner,
Version = actionReference.Ref,
Path = actionReference.Path
};
}
private static ActionDownloadInfoCollection ToServerData(ActionDownloadInfoResponseCollection actionDownloadInfoResponseCollection)
{
return new ActionDownloadInfoCollection
{
Actions = actionDownloadInfoResponseCollection.Actions?.ToDictionary(kvp => kvp.Key, kvp => ToServerData(kvp.Value))
};
}
private static ActionDownloadInfo ToServerData(ActionDownloadInfoResponse actionDownloadInfoResponse)
{
return new ActionDownloadInfo
{
Authentication = ToServerData(actionDownloadInfoResponse.Authentication),
NameWithOwner = actionDownloadInfoResponse.Name,
ResolvedNameWithOwner = actionDownloadInfoResponse.ResolvedName,
ResolvedSha = actionDownloadInfoResponse.ResolvedSha,
TarballUrl = actionDownloadInfoResponse.TarUrl,
Ref = actionDownloadInfoResponse.Version,
ZipballUrl = actionDownloadInfoResponse.ZipUrl,
};
}
private static ActionDownloadAuthentication? ToServerData(ActionDownloadAuthenticationResponse? actionDownloadAuthenticationResponse)
{
if (actionDownloadAuthenticationResponse == null)
{
return null;
}
return new ActionDownloadAuthentication
{
ExpiresAt = actionDownloadAuthenticationResponse.ExpiresAt,
Token = actionDownloadAuthenticationResponse.Token
};
}
private MediaTypeFormatter m_formatter;
private Uri m_launchServiceUrl;
private string m_token;
}
}

View File

@@ -1,270 +0,0 @@
#nullable enable
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Json;
using GitHub.DistributedTask.WebApi;
using Xunit;
using System.Text;
namespace GitHub.Runner.Common.Tests.DistributedTask
{
public sealed class TimelineRecordL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_Defaults()
{
var tr = new TimelineRecord();
Assert.Equal(0, tr.ErrorCount);
Assert.Equal(0, tr.WarningCount);
Assert.Equal(0, tr.NoticeCount);
Assert.Equal(1, tr.Attempt);
Assert.NotNull(tr.Issues);
Assert.NotNull(tr.PreviousAttempts);
Assert.NotNull(tr.Variables);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_Clone()
{
var original = new TimelineRecord();
original.ErrorCount = 100;
original.WarningCount = 200;
original.NoticeCount = 300;
original.Attempt = 3;
// The Variables dictionary should be a case-insensitive dictionary.
original.Variables["xxx"] = new VariableValue("first", false);
original.Variables["XXX"] = new VariableValue("second", false);
Assert.Equal(1, original.Variables.Count);
Assert.Equal("second", original.Variables.Values.First().Value);
Assert.Equal("second", original.Variables["xXx"].Value);
var clone = original.Clone();
Assert.NotSame(original, clone);
Assert.NotSame(original.Variables, clone.Variables);
Assert.Equal(100, clone.ErrorCount);
Assert.Equal(200, clone.WarningCount);
Assert.Equal(300, clone.NoticeCount);
Assert.Equal(3, clone.Attempt);
// Now, mutate the original post-clone.
original.ErrorCount++;
original.WarningCount += 10;
original.NoticeCount *= 3;
original.Attempt--;
original.Variables["a"] = new VariableValue("1", false);
// Verify that the clone was unaffected by the changes to the original.
Assert.Equal(100, clone.ErrorCount);
Assert.Equal(200, clone.WarningCount);
Assert.Equal(300, clone.NoticeCount);
Assert.Equal(3, clone.Attempt);
Assert.Equal(1, clone.Variables.Count);
Assert.Equal("second", clone.Variables.Values.First().Value);
// Verify that the clone's Variables dictionary is also case-sensitive.
clone.Variables["yyy"] = new VariableValue("third", false);
clone.Variables["YYY"] = new VariableValue("fourth", false);
Assert.Equal(2, clone.Variables.Count);
Assert.Equal("second", clone.Variables["xXx"].Value);
Assert.Equal("fourth", clone.Variables["yYy"].Value);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_NonNullCollections()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that missing JSON fields don't result in null values for collection properties.
var tr = Deserialize(jsonSamples["minimal"]);
Assert.NotNull(tr);
Assert.Equal("minimal", tr!.Name);
Assert.NotNull(tr.Issues);
Assert.NotNull(tr.PreviousAttempts);
Assert.NotNull(tr.Variables);
// Verify that explicitly-null JSON fields don't result in null values for collection properties.
// (Our deserialization logic should fix these up and instantiate an empty collection.)
tr = Deserialize(jsonSamples["explicit-null-collections"]);
Assert.NotNull(tr);
Assert.Equal("explicit-null-collections", tr!.Name);
Assert.NotNull(tr.Issues);
Assert.NotNull(tr.PreviousAttempts);
Assert.NotNull(tr.Variables);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_AttemptCannotBeLessThan1()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that 1 is the effective floor for TimelineRecord::Attempt.
var tr = Deserialize(jsonSamples["minimal"]);
Assert.NotNull(tr);
Assert.Equal("minimal", tr!.Name);
Assert.Equal(1, tr.Attempt);
tr = Deserialize(jsonSamples["invalid-attempt-value"]);
Assert.NotNull(tr);
Assert.Equal("invalid-attempt-value", tr!.Name);
Assert.Equal(1, tr.Attempt);
tr = Deserialize(jsonSamples["zero-attempt-value"]);
Assert.NotNull(tr);
Assert.Equal("zero-attempt-value", tr!.Name);
Assert.Equal(1, tr.Attempt);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_HandleLegacyNullsGracefully()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that nulls for ErrorCount, WarningCount, and NoticeCount are interpreted as 0.
var tr = Deserialize(jsonSamples["legacy-nulls"]);
Assert.NotNull(tr);
Assert.Equal("legacy-nulls", tr!.Name);
Assert.Equal(0, tr.ErrorCount);
Assert.Equal(0, tr.WarningCount);
Assert.Equal(0, tr.NoticeCount);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_HandleMissingCountsGracefully()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that nulls for ErrorCount, WarningCount, and NoticeCount are interpreted as 0.
var tr = Deserialize(jsonSamples["missing-counts"]);
Assert.NotNull(tr);
Assert.Equal("missing-counts", tr!.Name);
Assert.Equal(0, tr.ErrorCount);
Assert.Equal(0, tr.WarningCount);
Assert.Equal(0, tr.NoticeCount);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_NonZeroCounts()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that nulls for ErrorCount, WarningCount, and NoticeCount are interpreted as 0.
var tr = Deserialize(jsonSamples["non-zero-counts"]);
Assert.NotNull(tr);
Assert.Equal("non-zero-counts", tr!.Name);
Assert.Equal(10, tr.ErrorCount);
Assert.Equal(20, tr.WarningCount);
Assert.Equal(30, tr.NoticeCount);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_Deserialization_LeanTimelineRecord()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// Verify that a lean TimelineRecord can be deserialized.
var tr = Deserialize(jsonSamples["lean"]);
Assert.NotNull(tr);
Assert.Equal("lean", tr!.Name);
Assert.Equal(4, tr.Attempt);
Assert.Equal(1, tr.Issues.Count);
Assert.Equal(3, tr.Variables.Count);
Assert.Equal(3, tr.PreviousAttempts.Count);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_Deserialization_VariablesDictionaryIsCaseInsensitive()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
var tr = Deserialize(jsonSamples["lean"]);
Assert.NotNull(tr);
Assert.Equal("lean", tr!.Name);
Assert.Equal(3, tr.Variables.Count);
// Verify that the Variables Dictionary is case-insensitive.
tr.Variables["X"] = new VariableValue("overwritten", false);
Assert.Equal(3, tr.Variables.Count);
tr.Variables["new"] = new VariableValue("new.1", false);
Assert.Equal(4, tr.Variables.Count);
tr.Variables["NEW"] = new VariableValue("new.2", false);
Assert.Equal(4, tr.Variables.Count);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "DistributedTask")]
public void VerifyTimelineRecord_DeserializationEdgeCase_DuplicateVariableKeysThrowsException()
{
var jsonSamples = LoadJsonSamples(JsonSamplesFilePath);
// We could be more forgiving in this case if we discover that it's not uncommon in Production for serialized TimelineRecords to:
// 1) get incorrectly instantiated with a case-sensitive Variables dictionary (in older versions, this was possible via TimelineRecord::Clone)
// 2) end up with case variations of the same key
// 3) make another serialization/deserialization round trip.
//
// If we wanted to grant clemency to such incorrectly-serialized TimelineRecords,
// the fix to TimelineRecord::EnsureInitialized would look something like the following:
//
// var seedVariables = m_variables ?? Enumerable.Empty<KeyValuePair<string, VariableValue>>();
// m_variables = new Dictionary<string, VariableValue>(seedVariables.Count(), StringComparer.OrdinalIgnoreCase);
// foreach (var kvp in seedVariables)
// {
// m_variables[kvp.Key] = kvp.Value;
// }
Assert.Throws<ArgumentException>(() => Deserialize(jsonSamples["duplicate-variable-keys"]));
}
private static Dictionary<string, string> LoadJsonSamples(string path)
{
// Embedding independent JSON samples within YML works well because JSON generally doesn't need to be escaped or otherwise mangled.
var yamlDeserializer = new YamlDotNet.Serialization.Deserializer();
using var stream = new StreamReader(path);
return yamlDeserializer.Deserialize<Dictionary<string, string>>(stream);
}
private static TimelineRecord? Deserialize(string rawJson)
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(rawJson ?? string.Empty));
return m_jsonSerializer.ReadObject(stream) as TimelineRecord;
}
private static string JsonSamplesFilePath
{
get
{
return Path.Combine(TestUtil.GetTestDataPath(), "timelinerecord_json_samples.yml");
}
}
private static readonly DataContractJsonSerializer m_jsonSerializer = new(typeof(TimelineRecord));
}
}

View File

@@ -206,107 +206,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "ConfigurationManagement")]
public async Task ConfigureErrorDefaultLabelsDisabledWithNoCustomLabels()
{
using (TestHostContext tc = CreateTestContext())
{
Tracing trace = tc.GetTrace();
trace.Info("Creating config manager");
IConfigurationManager configManager = new ConfigurationManager();
configManager.Initialize(tc);
trace.Info("Preparing command line arguments");
var command = new CommandSettings(
tc,
new[]
{
"configure",
"--url", _expectedServerUrl,
"--name", _expectedAgentName,
"--runnergroup", _secondRunnerGroupName,
"--work", _expectedWorkFolder,
"--auth", _expectedAuthType,
"--token", _expectedToken,
"--no-default-labels",
"--ephemeral",
"--disableupdate",
"--unattended",
});
trace.Info("Constructed.");
_store.Setup(x => x.IsConfigured()).Returns(false);
_configMgrAgentSettings = null;
trace.Info("Ensuring configure fails if default labels are disabled and no custom labels are set");
var ex = await Assert.ThrowsAsync<NotSupportedException>(() => configManager.ConfigureAsync(command));
Assert.Contains("--no-default-labels without specifying --labels is not supported", ex.Message);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "ConfigurationManagement")]
public async Task ConfigureDefaultLabelsDisabledWithCustomLabels()
{
using (TestHostContext tc = CreateTestContext())
{
Tracing trace = tc.GetTrace();
trace.Info("Creating config manager");
IConfigurationManager configManager = new ConfigurationManager();
configManager.Initialize(tc);
var userLabels = "userlabel1,userlabel2";
trace.Info("Preparing command line arguments");
var command = new CommandSettings(
tc,
new[]
{
"configure",
"--url", _expectedServerUrl,
"--name", _expectedAgentName,
"--runnergroup", _secondRunnerGroupName,
"--work", _expectedWorkFolder,
"--auth", _expectedAuthType,
"--token", _expectedToken,
"--labels", userLabels,
"--no-default-labels",
"--ephemeral",
"--disableupdate",
"--unattended",
});
trace.Info("Constructed.");
_store.Setup(x => x.IsConfigured()).Returns(false);
_configMgrAgentSettings = null;
trace.Info("Ensuring all the required parameters are available in the command line parameter");
await configManager.ConfigureAsync(command);
_store.Setup(x => x.IsConfigured()).Returns(true);
trace.Info("Configured, verifying all the parameter value");
var s = configManager.LoadSettings();
Assert.NotNull(s);
Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
Assert.True(s.AgentName.Equals(_expectedAgentName));
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
Assert.True(s.Ephemeral.Equals(true));
// validate GetAgentPoolsAsync gets called twice with automation pool type
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
var expectedLabels = userLabels.Split(",").ToList();
_runnerServer.Verify(x => x.AddAgentAsync(It.IsAny<int>(), It.Is<TaskAgent>(a => a.Labels.Select(x => x.Name).ToHashSet().SetEquals(expectedLabels))), Times.Once);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "ConfigurationManagement")]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -87,7 +87,7 @@ namespace GitHub.Runner.Common.Tests
var newFiles = new List<string>();
if (Directory.Exists(layoutBin))
{
var binDirs = Directory.GetDirectories(TestUtil.GetSrcPath(), "net6.0", SearchOption.AllDirectories);
var binDirs = Directory.GetDirectories(TestUtil.GetSrcPath(), "net7.0", SearchOption.AllDirectories);
foreach (var binDir in binDirs)
{
if (binDir.Contains("Test") || binDir.Contains("obj"))

View File

@@ -1,275 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using Xunit;
namespace GitHub.DistributedTask.ObjectTemplating.Tests
{
public sealed class TemplateContextL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithTokenAndException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 1, Col: 1): Exception of type 'System.Exception' was thrown.") };
context.Error(new StringToken(1, 1, 1, "some-token"), new System.Exception());
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithNullTokenAndException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("System.Exception: Exception of type 'System.Exception' was thrown.") };
context.Error(null, new System.Exception());
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithTokenAndMessage()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 1, Col: 1): message") };
context.Error(new StringToken(1, 1, 1, "some-token"), "message");
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithNullTokenAndMessage()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("message") };
context.Error(null, "message");
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 2, Col: 3): Fatal template exception") };
List<string> expectedTracewriterErrors = new List<string> { "(Line: 2, Col: 3):" };
context.Error(1, 2, 3, new Exception("Fatal template exception"));
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithExceptionAndInnerException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 2, Col: 3): Fatal template exception"),
new TemplateValidationError("(Line: 2, Col: 3): Inner Exception") };
List<string> expectedTracewriterErrors = new List<string> { "(Line: 2, Col: 3):" };
Exception e = new Exception("Fatal template exception", new Exception("Inner Exception"));
context.Error(1, 2, 3, e);
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Equal(2, templateValidationErrors.Count);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithLineNumbersAndMessage()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 2, Col: 3): Fatal template exception") };
List<string> expectedTracewriterErrors = new List<string> { "(Line: 2, Col: 3): Fatal template exception" };
context.Error(1, 2, 3, "Fatal template exception");
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithNullLineNumbersAndMessage()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("Fatal template exception") };
List<string> expectedTracewriterErrors = new List<string> { "Fatal template exception" };
context.Error(null, null, null, "Fatal template exception");
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithLineNumbersAndException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("(Line: 2, Col: 3): Fatal template exception") };
List<string> expectedTracewriterErrors = new List<string> { "(Line: 2, Col: 3):" };
context.Error(1, 2, 3, new Exception("Fatal template exception"));
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void VerifyErrorWithNullLineNumbersAndException()
{
TemplateContext context = buildContext();
List<TemplateValidationError> expectedErrors = new List<TemplateValidationError> { new TemplateValidationError("System.Exception: Fatal template exception") };
List<string> expectedTracewriterErrors = new List<string> { "" };
context.Error(null, null, null, new Exception("Fatal template exception"));
List<TemplateValidationError> templateValidationErrors = toList(context.Errors.GetEnumerator());
ListTraceWriter listTraceWriter = (ListTraceWriter)context.TraceWriter;
List<string> tracewriterErrors = listTraceWriter.GetErrors();
Assert.Single(templateValidationErrors);
Assert.True(areEqual(expectedErrors, templateValidationErrors));
Assert.True(expectedTracewriterErrors.SequenceEqual(tracewriterErrors));
}
private TemplateContext buildContext()
{
return new TemplateContext
{
// CancellationToken = CancellationToken.None,
Errors = new TemplateValidationErrors(10, int.MaxValue), // Don't truncate error messages otherwise we might not scrub secrets correctly
Memory = new TemplateMemory(
maxDepth: 100,
maxEvents: 1000000,
maxBytes: 10 * 1024 * 1024),
Schema = null,
TraceWriter = new ListTraceWriter(),
};
}
private List<TemplateValidationError> toList(IEnumerator<TemplateValidationError> enumerator)
{
List<TemplateValidationError> result = new();
while (enumerator.MoveNext())
{
TemplateValidationError err = enumerator.Current;
result.Add(err);
}
return result;
}
private bool areEqual(List<TemplateValidationError> l1, List<TemplateValidationError> l2)
{
if (l1.Count != l2.Count) return false;
var twoLists = l1.Zip(l2, (l1Error, l2Error) => new { Elem1 = l1Error, Elem2 = l2Error });
foreach (var elem in twoLists)
{
if (elem.Elem1.Message != elem.Elem2.Message) return false;
if (elem.Elem1.Code != elem.Elem2.Code) return false;
}
return true;
}
internal sealed class ListTraceWriter : ITraceWriter
{
private List<string> errors = new();
private List<string> infoMessages = new();
private List<string> verboseMessages = new();
public void Error(string format, params object[] args)
{
errors.Add(string.Format(System.Globalization.CultureInfo.CurrentCulture, $"{format}", args));
}
public void Info(string format, params object[] args)
{
throw new NotImplementedException();
}
public void Verbose(string format, params object[] args)
{
throw new NotImplementedException();
}
public List<string> GetErrors()
{
return errors;
}
}
}
}

View File

@@ -14,9 +14,11 @@ public sealed class AcquireJobRequestL0
[Trait("Category", "Common")]
public void VerifySerialization()
{
var jobMessageId = "1526919030369-33";
var request = new AcquireJobRequest
{
JobMessageId = "1526919030369-33"
JobMessageId = jobMessageId,
StreamId = jobMessageId
};
var serializer = new DataContractJsonSerializer(typeof(AcquireJobRequest));
using var stream = new MemoryStream();
@@ -25,7 +27,7 @@ public sealed class AcquireJobRequestL0
stream.Position = 0;
using var reader = new StreamReader(stream, Encoding.UTF8);
string json = reader.ReadToEnd();
string expected = DoubleQuotify(string.Format("{{'jobMessageId':'{0}'}}", request.JobMessageId));
string expected = DoubleQuotify(string.Format("{{'jobMessageId':'{0}','streamId':'{0}'}}", request.JobMessageId));
Assert.Equal(expected, json);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
@@ -32,10 +32,10 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.GetTrace().Info($"{tag} {line}");
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((Issue issue, ExecutionContextLogOptions logOptions) =>
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
hc.GetTrace().Info($"{issue.Type} {issue.Message} {logOptions.LogMessageOverride ?? string.Empty}");
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
_commandManager.EnablePluginInternalCommand();
@@ -59,10 +59,10 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.GetTrace().Info($"{tag} {line}");
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((Issue issue, ExecutionContextLogOptions logOptions) =>
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
hc.GetTrace().Info($"{issue.Type} {issue.Message} {logOptions.LogMessageOverride ?? string.Empty}");
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
_commandManager.EnablePluginInternalCommand();
@@ -92,10 +92,10 @@ namespace GitHub.Runner.Common.Tests.Worker
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((Issue issue, ExecutionContextLogOptions logOptions) =>
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
hc.GetTrace().Info($"{issue.Type} {issue.Message} {logOptions.LogMessageOverride ?? string.Empty}");
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();

View File

@@ -29,7 +29,6 @@ namespace GitHub.Runner.Common.Tests.Worker
private Mock<IDockerCommandManager> _dockerManager;
private Mock<IExecutionContext> _ec;
private Mock<IJobServer> _jobServer;
private Mock<ILaunchServer> _launchServer;
private Mock<IRunnerPluginManager> _pluginManager;
private TestHostContext _hc;
private ActionManager _actionManager;
@@ -2148,7 +2147,7 @@ runs:
_ec.Object.Global.FileTable = new List<String>();
_ec.Object.Global.Plan = new TaskOrchestrationPlanReference();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>())).Callback((Issue issue, ExecutionContextLogOptions logOptions) => { _hc.GetTrace().Info($"[{issue.Type}]{logOptions.LogMessageOverride ?? issue.Message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
_dockerManager = new Mock<IDockerCommandManager>();
@@ -2176,25 +2175,6 @@ runs:
return Task.FromResult(result);
});
_launchServer = new Mock<ILaunchServer>();
_launchServer.Setup(x => x.ResolveActionsDownloadInfoAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<ActionReferenceList>(), It.IsAny<CancellationToken>()))
.Returns((Guid planId, Guid jobId, ActionReferenceList actions, CancellationToken cancellationToken) =>
{
var result = new ActionDownloadInfoCollection { Actions = new Dictionary<string, ActionDownloadInfo>() };
foreach (var action in actions.Actions)
{
var key = $"{action.NameWithOwner}@{action.Ref}";
result.Actions[key] = new ActionDownloadInfo
{
NameWithOwner = action.NameWithOwner,
Ref = action.Ref,
TarballUrl = $"https://api.github.com/repos/{action.NameWithOwner}/tarball/{action.Ref}",
ZipballUrl = $"https://api.github.com/repos/{action.NameWithOwner}/zipball/{action.Ref}",
};
}
return Task.FromResult(result);
});
_pluginManager = new Mock<IRunnerPluginManager>();
_pluginManager.Setup(x => x.GetPluginAction(It.IsAny<string>())).Returns(new RunnerPluginActionInfo() { PluginTypeName = "plugin.class, plugin", PostPluginTypeName = "plugin.cleanup, plugin" });
@@ -2203,7 +2183,6 @@ runs:
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
_hc.SetSingleton<IJobServer>(_jobServer.Object);
_hc.SetSingleton<ILaunchServer>(_launchServer.Object);
_hc.SetSingleton<IRunnerPluginManager>(_pluginManager.Object);
_hc.SetSingleton<IActionManifestManager>(actionManifest);
_hc.SetSingleton<IHttpClientHandlerFactory>(new HttpClientHandlerFactory());

View File

@@ -670,7 +670,7 @@ namespace GitHub.Runner.Common.Tests.Worker
{
Teardown();
}
}
}
[Fact]
[Trait("Level", "L0")]
@@ -715,7 +715,7 @@ namespace GitHub.Runner.Common.Tests.Worker
//Assert
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
Assert.Contains($"Fail to load {action_path}", err.Message);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<ExecutionContextLogOptions>()), Times.Once);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<string>()), Times.Once);
}
finally
{
@@ -860,7 +860,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>())).Callback((Issue issue, ExecutionContextLogOptions logOptions) => { _hc.GetTrace().Info($"[{issue.Type}]{logOptions.LogMessageOverride ?? issue.Message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
}
private void Teardown()

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.Pipelines.ContextData;
@@ -366,7 +366,7 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("invalid1", finialInputs["invalid1"]);
Assert.Equal("invalid2", finialInputs["invalid2"]);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'")), It.IsAny<ExecutionContextLogOptions>()), Times.Once);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'")), It.IsAny<string>()), Times.Once);
}
[Fact]
@@ -485,7 +485,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
_ec.Object.Global.Variables = new Variables(_hc, new Dictionary<string, VariableValue>());
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<ExecutionContextLogOptions>())).Callback((Issue issue, ExecutionContextLogOptions logOptions) => { _hc.GetTrace().Info($"[{issue.Type}]{logOptions.LogMessageOverride ?? issue.Message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
_hc.SetSingleton<IActionManager>(_actionManager.Object);
_hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object);

View File

@@ -247,16 +247,12 @@ namespace GitHub.Runner.Common.Tests.Worker
WriteDebug = true,
Variables = _variables,
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((DTWebApi.Issue issue, ExecutionContextLogOptions logOptions) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
var resolvedMessage = issue.Message;
if (logOptions.WriteToLog && !string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
resolvedMessage = logOptions.LogMessageOverride;
}
_issues.Add(new(issue, resolvedMessage));
_trace.Info($"Issue '{issue.Type}': {resolvedMessage}");
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>
@@ -272,4 +268,4 @@ namespace GitHub.Runner.Common.Tests.Worker
return hostContext;
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -52,43 +52,42 @@ namespace GitHub.Runner.Common.Tests.Worker
// Act.
ec.InitializeJob(jobRequest, CancellationToken.None);
// Flood the ExecutionContext with errors and warnings (past its max capacity of 10).
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.Complete();
// Assert.
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.ErrorCount > 0)), Times.AtLeast(10));
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.WarningCount > 0)), Times.AtLeast(10));
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.ErrorCount == 15)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.WarningCount == 14)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Type == IssueType.Error).Count() == 10)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Type == IssueType.Warning).Count() == 10)), Times.AtLeastOnce);
}
@@ -191,9 +190,9 @@ namespace GitHub.Runner.Common.Tests.Worker
bigMessage += "a";
}
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = bigMessage }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = bigMessage }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = bigMessage }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = bigMessage });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = bigMessage });
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = bigMessage });
ec.Complete();
@@ -204,61 +203,6 @@ namespace GitHub.Runner.Common.Tests.Worker
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddIssue_OverrideLogMessage()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange: Create a job request message.
TaskOrchestrationPlanReference plan = new();
TimelineReference timeline = new();
Guid jobId = Guid.NewGuid();
string jobName = "some job name";
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);
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
// Arrange: Setup the paging logger.
var pagingLogger = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
hc.EnqueueInstance(pagingLogger.Object);
hc.SetSingleton(jobServerQueue.Object);
var ec = new Runner.Worker.ExecutionContext();
ec.Initialize(hc);
// Act.
ec.InitializeJob(jobRequest, CancellationToken.None);
var issueMessage = "Message embedded in issue.";
var overrideMessage = "Message override.";
var options = new ExecutionContextLogOptions(true, overrideMessage);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = issueMessage }, options);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = issueMessage }, options);
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = issueMessage }, options);
// Finally, add a variation that DOESN'T override the message.
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = issueMessage }, ExecutionContextLogOptions.Default);
ec.Complete();
// Assert.
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<long?>()), Times.Exactly(4));
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.Is<string>(text => text.EndsWith(overrideMessage)), It.IsAny<long?>()), Times.Exactly(3));
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.Is<string>(text => text.EndsWith(issueMessage)), It.IsAny<long?>()), Times.Exactly(1));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
@@ -298,15 +242,13 @@ namespace GitHub.Runner.Common.Tests.Worker
var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
embeddedStep.Start();
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error annotation that should have step and line number information" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning annotation that should have step and line number information" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice annotation that should have step and line number information" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error annotation that should have step and line number information" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning annotation that should have step and line number information" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice annotation that should have step and line number information" });
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()), Times.AtLeastOnce);
// Verify that Error/Warning/Notice issues added to embedded steps don't get sent up to the server.
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Error).Count() == 1)), Times.Never);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Warning).Count() == 1)), Times.Never);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Notice).Count() == 1)), Times.Never);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Error).Count() == 1)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Warning).Count() == 1)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Notice).Count() == 1)), Times.AtLeastOnce);
}
}
@@ -684,12 +626,12 @@ namespace GitHub.Runner.Common.Tests.Worker
ec.StepTelemetry.StepId = Guid.NewGuid();
ec.StepTelemetry.Stage = "main";
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" }, ExecutionContextLogOptions.Default);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
ec.Complete();
@@ -750,9 +692,9 @@ namespace GitHub.Runner.Common.Tests.Worker
embeddedStep.StepTelemetry.Action = "actions/checkout";
embeddedStep.StepTelemetry.Ref = "v2";
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
embeddedStep.PublishStepTelemetry();
@@ -814,9 +756,9 @@ namespace GitHub.Runner.Common.Tests.Worker
embeddedStep.StepTelemetry.Action = "actions/checkout";
embeddedStep.StepTelemetry.Ref = "v2";
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" }, ExecutionContextLogOptions.Default);
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
ec.Complete();
@@ -985,7 +927,7 @@ namespace GitHub.Runner.Common.Tests.Worker
inputVarsContext["VARIABLE_2"] = new StringContextData("value2");
jobRequest.ContextData["vars"] = inputVarsContext;
// Arrange: Setup the paging logger.
// Arrange: Setup the paging logger.
var pagingLogger1 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
hc.EnqueueInstance(pagingLogger1.Object);
@@ -999,7 +941,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var expected = new DictionaryContextData();
expected["VARIABLE_1"] = new StringContextData("value1");
expected["VARIABLE_2"] = new StringContextData("value1");
Assert.True(ExpressionValuesAssertEqual(expected, jobContext.ExpressionValues["vars"] as DictionaryContextData));
}
}
@@ -1030,7 +972,7 @@ namespace GitHub.Runner.Common.Tests.Worker
inputVarsContext[Constants.Variables.Actions.RunnerDebug] = new StringContextData("true");
jobRequest.ContextData["vars"] = inputVarsContext;
// Arrange: Setup the paging logger.
// Arrange: Setup the paging logger.
var pagingLogger1 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
hc.EnqueueInstance(pagingLogger1.Object);
@@ -1041,7 +983,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobContext.InitializeJob(jobRequest, CancellationToken.None);
Assert.Equal("true", jobContext.Global.Variables.Get(Constants.Variables.Actions.StepDebug));
Assert.Equal("true", jobContext.Global.Variables.Get(Constants.Variables.Actions.RunnerDebug));
}
@@ -1076,7 +1018,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobRequest.Variables[Constants.Variables.Actions.StepDebug] = "false";
jobRequest.Variables[Constants.Variables.Actions.RunnerDebug] = "false";
// Arrange: Setup the paging logger.
// Arrange: Setup the paging logger.
var pagingLogger1 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
hc.EnqueueInstance(pagingLogger1.Object);
@@ -1087,7 +1029,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobContext.InitializeJob(jobRequest, CancellationToken.None);
Assert.Equal("false", jobContext.Global.Variables.Get(Constants.Variables.Actions.StepDebug));
Assert.Equal("false", jobContext.Global.Variables.Get(Constants.Variables.Actions.RunnerDebug));
}
@@ -1119,4 +1061,4 @@ namespace GitHub.Runner.Common.Tests.Worker
return true;
}
}
}
}

View File

@@ -984,15 +984,10 @@ namespace GitHub.Runner.Common.Tests.Worker
{
_onMatcherChanged = handler;
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((DTWebApi.Issue issue, ExecutionContextLogOptions logOptions) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
var resolvedMessage = issue.Message;
if (logOptions.WriteToLog && !string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
resolvedMessage = logOptions.LogMessageOverride;
}
_issues.Add(new(issue, resolvedMessage));
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>

View File

@@ -413,16 +413,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true,
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((DTWebApi.Issue issue, ExecutionContextLogOptions logOptions) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
var resolvedMessage = issue.Message;
if (logOptions.WriteToLog && !string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
resolvedMessage = logOptions.LogMessageOverride;
}
_issues.Add(new(issue, resolvedMessage));
_trace.Info($"Issue '{issue.Type}': {resolvedMessage}");
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>

View File

@@ -411,16 +411,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true,
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((DTWebApi.Issue issue, ExecutionContextLogOptions logOptions) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
var resolvedMessage = issue.Message;
if (logOptions.WriteToLog && !string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
resolvedMessage = logOptions.LogMessageOverride;
}
_issues.Add(new(issue, resolvedMessage));
_trace.Info($"Issue '{issue.Type}': {resolvedMessage}");
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>

View File

@@ -413,16 +413,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true,
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<ExecutionContextLogOptions>()))
.Callback((DTWebApi.Issue issue, ExecutionContextLogOptions logOptions) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
var resolvedMessage = issue.Message;
if (logOptions.WriteToLog && !string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
resolvedMessage = logOptions.LogMessageOverride;
}
_issues.Add(new(issue, resolvedMessage));
_trace.Info($"Issue '{issue.Type}': {resolvedMessage}");
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>
@@ -434,8 +430,8 @@ namespace GitHub.Runner.Common.Tests.Worker
_executionContext.Setup(x => x.SetOutput(It.IsAny<string>(), It.IsAny<string>(), out reference))
.Callback((string name, string value, out string reference) =>
{
reference = value;
_outputs[name] = value;
reference = value;
_outputs[name] = value;
});
// SetOutputFileCommand

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -337,7 +337,7 @@ namespace GitHub.Runner.Common.Tests.Worker
// Act.
await _stepsRunner.RunAsync(jobContext: _ec.Object);
// Assert.
// Assert.
Assert.Equal(2, variableSet.Step.Length);
variableSet.Step[0].Verify(x => x.RunAsync());
variableSet.Step[1].Verify(x => x.RunAsync(), variableSet.Expected ? Times.Once() : Times.Never());
@@ -590,7 +590,7 @@ namespace GitHub.Runner.Common.Tests.Worker
step.Setup(x => x.Condition).Returns(condition);
step.Setup(x => x.ContinueOnError).Returns(new BooleanToken(null, null, null, continueOnError));
step.Setup(x => x.Action)
.Returns(new GitHub.DistributedTask.Pipelines.ActionStep()
.Returns(new DistributedTask.Pipelines.ActionStep()
{
Name = name,
Id = Guid.NewGuid(),

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603;NU1603;xUnit2013;</NoWarn>

View File

@@ -1,70 +0,0 @@
minimal: |
{ "Name": "minimal" }
invalid-attempt-value: |
{
"Name": "invalid-attempt-value",
"Attempt": -99
}
zero-attempt-value: |
{
"Name": "zero-attempt-value",
"Attempt": 0
}
legacy-nulls: |
{
"Name": "legacy-nulls",
"ErrorCount": null,
"WarningCount": null,
"NoticeCount": null
}
missing-counts: |
{
"Name": "missing-counts"
}
non-zero-counts: |
{
"Name": "non-zero-counts",
"ErrorCount": 10,
"WarningCount": 20,
"NoticeCount": 30
}
explicit-null-collections: |
{
"Name": "explicit-null-collections",
"Issues": null,
"PreviousAttempts": null,
"Variables": null
}
lean: |
{
"Id": "00000000-0000-0000-0000-000000000000",
"Name": "lean",
"LastModified": "\/Date(1679073003252+0000)\/",
"Issues": [
{
"Type": 0,
"Category": null,
"Message": null,
"IsInfrastructureIssue": null
}
],
"Variables": [
{ "Key": "x", "Value": { "Value": "1" } },
{ "Key": "y", "Value": { "Value": "2" } },
{ "Key": "z", "Value": { "Value": "3" } }
],
"Attempt": 4,
"PreviousAttempts": [
{ "Attempt": 1 },
{ "Attempt": 2 },
{ "Attempt": 3 }
]
}
duplicate-variable-keys: |
{
"Name": "duplicate-variable-keys",
"Variables": [
{ "Key": "aaa", "Value": { "Value": "a.1" } },
{ "Key": "AAA", "Value": { "Value": "a.2" } }
]
}

View File

@@ -22,7 +22,7 @@ DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package"
PACKAGE_TRIMS_DIR="$SCRIPT_DIR/../_package_trims"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="6.0.405"
DOTNETSDK_VERSION="7.0.203"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion)

View File

@@ -1,5 +1,5 @@
{
"sdk": {
"version": "6.0.405"
"version": "7.0.203"
}
}