Compare commits

..

3 Commits

Author SHA1 Message Date
eric sciple
a4c63ec012 . 2024-06-11 12:48:51 -07:00
eric sciple
d13cf5d9a8 . 2024-06-10 21:38:17 -07:00
eric sciple
d124b0104e . 2024-06-07 13:54:33 -07:00
33 changed files with 156 additions and 708 deletions

View File

@@ -25,12 +25,10 @@ jobs:
- name: Compute image version - name: Compute image version
id: image id: image
uses: actions/github-script@v6 uses: actions/github-script@v6
env:
RUNNER_VERSION: ${{ github.event.inputs.runnerVersion }}
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
const inputRunnerVersion = process.env.RUNNER_VERSION; const inputRunnerVersion = "${{ github.event.inputs.runnerVersion }}"
if (inputRunnerVersion) { if (inputRunnerVersion) {
console.log(`Using input runner version ${inputRunnerVersion}`) console.log(`Using input runner version ${inputRunnerVersion}`)
core.setOutput('version', inputRunnerVersion); core.setOutput('version', inputRunnerVersion);

View File

@@ -4,7 +4,16 @@
## Supported Distributions and Versions ## Supported Distributions and Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#linux)." x64
- Red Hat Enterprise Linux 7+
- CentOS 7+
- Oracle Linux 7+
- Fedora 29+
- Debian 9+
- Ubuntu 16.04+
- Linux Mint 18+
- openSUSE 15+
- SUSE Enterprise Linux (SLES) 12 SP2+
## Install .Net Core 3.x Linux Dependencies ## Install .Net Core 3.x Linux Dependencies

View File

@@ -4,6 +4,7 @@
## Supported Versions ## Supported Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#macos)." - macOS High Sierra (10.13) and later versions
- x64 and arm64 (Apple Silicon)
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30) ## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)

View File

@@ -2,6 +2,11 @@
## Supported Versions ## Supported Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#windows)." - Windows 7 64-bit
- Windows 8.1 64-bit
- Windows 10 64-bit
- Windows Server 2012 R2 64-bit
- Windows Server 2016 64-bit
- Windows Server 2019 64-bit
## [More .NET Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore30) ## [More .NET Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore30)

View File

@@ -4,9 +4,9 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG RUNNER_VERSION ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.1 ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.0
ARG DOCKER_VERSION=27.1.1 ARG DOCKER_VERSION=25.0.5
ARG BUILDX_VERSION=0.16.2 ARG BUILDX_VERSION=0.13.2
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y

View File

@@ -1,8 +1,12 @@
## What's Changed ## What's Changed
- .NET 8 compat test adjustments: 1) do not trim SDK, 2) support pattern to match output, 3) modify output truncation length https://github.com/actions/runner/pull/3427 - Do not give up when uploading steps metadata by @yacaovsnc in https://github.com/actions/runner/pull/3280
- Upgrade node20 to 20.13.1 by @pje in https://github.com/actions/runner/pull/3284
- Delete all the contentHash files by @pje in https://github.com/actions/runner/pull/3285
- Make it easy to install `git` on an Action Runner Image by @jww3 in https://github.com/actions/runner/pull/3273
- Install `gpg-agent` during actions/runner container image build by @jww3 in https://github.com/actions/runner/pull/3294
**Full Changelog**: https://github.com/actions/runner/compare/v2.319.0...v2.319.1 **Full Changelog**: https://github.com/actions/runner/compare/v2.316.1...v2.317.0
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet. _Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository. To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.

View File

@@ -1 +1 @@
2.319.1 <Update to ./src/runnerversion when creating release>

View File

@@ -22,6 +22,8 @@ namespace GitHub.Runner.Common
Task<TaskAgentMessage> GetRunnerMessageAsync(Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, bool disableUpdate, CancellationToken token); Task<TaskAgentMessage> GetRunnerMessageAsync(Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, bool disableUpdate, CancellationToken token);
Task DeleteRunnerMessageAsync(Guid sessionId, string jobMessageKey, CancellationToken cancellationToken);
Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials); Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials);
Task ForceRefreshConnection(VssCredentials credentials); Task ForceRefreshConnection(VssCredentials credentials);
@@ -75,6 +77,12 @@ namespace GitHub.Runner.Common
await _brokerHttpClient.DeleteSessionAsync(cancellationToken); await _brokerHttpClient.DeleteSessionAsync(cancellationToken);
} }
public async Task DeleteRunnerMessageAsync(Guid sessionId, string jobMessageKey, CancellationToken cancellationToken)
{
CheckConnection();
await _brokerHttpClient.DeleteRunnerMessageAsync(sessionId, jobMessageKey, cancellationToken);
}
public Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials) public Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials)
{ {
if (_brokerUri != serverUri || !_hasConnection) if (_brokerUri != serverUri || !_hasConnection)

View File

@@ -181,7 +181,7 @@ namespace GitHub.Runner.Common
public static readonly string DeprecatedNodeVersion = "node16"; public static readonly string DeprecatedNodeVersion = "node16";
public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/"; public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/";
public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings"; public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings";
public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions use a deprecated Node.js version and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/"; public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions uses Node.js version which is deprecated and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/";
public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings"; public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings";
} }
@@ -280,10 +280,6 @@ namespace GitHub.Runner.Common
public static readonly string PhaseDisplayName = "system.phaseDisplayName"; public static readonly string PhaseDisplayName = "system.phaseDisplayName";
public static readonly string JobRequestType = "system.jobRequestType"; public static readonly string JobRequestType = "system.jobRequestType";
public static readonly string OrchestrationId = "system.orchestrationId"; public static readonly string OrchestrationId = "system.orchestrationId";
public static readonly string TestDotNet8Compatibility = "system.testDotNet8Compatibility";
public static readonly string DotNet8CompatibilityOutputLength = "system.dotNet8CompatibilityOutputLength";
public static readonly string DotNet8CompatibilityOutputPattern = "system.dotNet8CompatibilityOutputPattern";
public static readonly string DotNet8CompatibilityWarning = "system.dotNet8CompatibilityWarning";
} }
} }

View File

@@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Security;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -180,10 +179,6 @@ namespace GitHub.Runner.Common
userAgentValues.AddRange(UserAgentUtility.GetDefaultRestUserAgent()); userAgentValues.AddRange(UserAgentUtility.GetDefaultRestUserAgent());
userAgentValues.AddRange(HostContext.UserAgents); userAgentValues.AddRange(HostContext.UserAgents);
this._websocketClient.Options.SetRequestHeader("User-Agent", string.Join(" ", userAgentValues.Select(x => x.ToString()))); this._websocketClient.Options.SetRequestHeader("User-Agent", string.Join(" ", userAgentValues.Select(x => x.ToString())));
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY")))
{
this._websocketClient.Options.RemoteCertificateValidationCallback = (_, _, _, _) => true;
}
this._websocketConnectTask = ConnectWebSocketClient(feedStreamUrl, delay); this._websocketConnectTask = ConnectWebSocketClient(feedStreamUrl, delay);
} }

View File

@@ -62,10 +62,9 @@ namespace GitHub.Runner.Common
CheckConnection(); CheckConnection();
return RetryRequest<AgentJobRequestMessage>( return RetryRequest<AgentJobRequestMessage>(
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, cancellationToken), cancellationToken, async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, cancellationToken), cancellationToken,
shouldRetry: ex => shouldRetry: ex => ex is not TaskOrchestrationJobNotFoundException &&
ex is not TaskOrchestrationJobNotFoundException && // HTTP status 404 ex is not TaskOrchestrationJobAlreadyAcquiredException &&
ex is not TaskOrchestrationJobAlreadyAcquiredException && // HTTP status 409 ex is not TaskOrchestrationJobUnprocessableException);
ex is not TaskOrchestrationJobUnprocessableException); // HTTP status 422
} }
public Task CompleteJobAsync( public Task CompleteJobAsync(

View File

@@ -314,7 +314,30 @@ namespace GitHub.Runner.Listener
public async Task DeleteMessageAsync(TaskAgentMessage message) public async Task DeleteMessageAsync(TaskAgentMessage message)
{ {
await Task.CompletedTask; Trace.Entering();
ArgUtil.NotNull(_session, nameof(_session));
if (message == null || _session.SessionId == Guid.Empty)
{
return;
}
var jobMessageKey = "";
if (MessageUtil.IsRunServiceJob(message.MessageType))
{
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
jobMessageKey = messageRef.RunnerRequestId;
}
else
{
// Broker currently doesn't support delete for other message types
return;
}
using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
await _brokerServer.DeleteRunnerMessageAsync(_session.SessionId, jobMessageKey, cs.Token);
}
} }
private bool IsGetNextMessageExceptionRetriable(Exception ex) private bool IsGetNextMessageExceptionRetriable(Exception ex)

View File

@@ -1,44 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Runner.Common;
using GitHub.Services.Common;
namespace GitHub.Runner.Listener
{
[ServiceLocator(Default = typeof(ErrorThrottler))]
public interface IErrorThrottler : IRunnerService
{
void Reset();
Task IncrementAndWaitAsync(CancellationToken token);
}
public sealed class ErrorThrottler : RunnerService, IErrorThrottler
{
internal static readonly TimeSpan MinBackoff = TimeSpan.FromSeconds(1);
internal static readonly TimeSpan MaxBackoff = TimeSpan.FromMinutes(1);
internal static readonly TimeSpan BackoffCoefficient = TimeSpan.FromSeconds(1);
private int _count = 0;
public void Reset()
{
_count = 0;
}
public async Task IncrementAndWaitAsync(CancellationToken token)
{
if (++_count <= 1)
{
return;
}
TimeSpan backoff = BackoffTimerHelper.GetExponentialBackoff(
attempt: _count - 2, // 0-based attempt
minBackoff: MinBackoff,
maxBackoff: MaxBackoff,
deltaBackoff: BackoffCoefficient);
Trace.Warning($"Back off {backoff.TotalSeconds} seconds before next attempt. Current consecutive error count: {_count}");
await HostContext.Delay(backoff, token);
}
}
}

View File

@@ -397,11 +397,19 @@ namespace GitHub.Runner.Listener
if (message != null && _session.SessionId != Guid.Empty) if (message != null && _session.SessionId != Guid.Empty)
{ {
using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30))) using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
if (MessageUtil.IsRunServiceJob(message.MessageType))
{
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
await _brokerServer.DeleteRunnerMessageAsync(_session.SessionId, messageRef.RunnerRequestId, cs.Token);
}
else
{ {
await _runnerServer.DeleteAgentMessageAsync(_settings.PoolId, message.MessageId, _session.SessionId, cs.Token); await _runnerServer.DeleteAgentMessageAsync(_settings.PoolId, message.MessageId, _session.SessionId, cs.Token);
} }
} }
} }
}
public async Task RefreshListenerTokenAsync(CancellationToken cancellationToken) public async Task RefreshListenerTokenAsync(CancellationToken cancellationToken)
{ {

View File

@@ -32,25 +32,10 @@ namespace GitHub.Runner.Listener
private bool _inConfigStage; private bool _inConfigStage;
private ManualResetEvent _completedCommand = new(false); private ManualResetEvent _completedCommand = new(false);
// <summary>
// Helps avoid excessive calls to Run Service when encountering non-retriable errors from /acquirejob.
// Normally we rely on the HTTP clients to back off between retry attempts. However, acquiring a job
// involves calls to both Run Serivce and Broker. And Run Service and Broker communicate with each other
// in an async fashion.
//
// When Run Service encounters a non-retriable error, it sends an async message to Broker. The runner will,
// however, immediately call Broker to get the next message. If the async event from Run Service to Broker
// has not yet been processed, the next message from Broker may be the same job message.
//
// The error throttler helps us back off when encountering successive, non-retriable errors from /acquirejob.
// </summary>
private IErrorThrottler _acquireJobThrottler;
public override void Initialize(IHostContext hostContext) public override void Initialize(IHostContext hostContext)
{ {
base.Initialize(hostContext); base.Initialize(hostContext);
_term = HostContext.GetService<ITerminal>(); _term = HostContext.GetService<ITerminal>();
_acquireJobThrottler = HostContext.CreateService<IErrorThrottler>();
} }
public async Task<int> ExecuteCommand(CommandSettings command) public async Task<int> ExecuteCommand(CommandSettings command)
@@ -559,6 +544,8 @@ namespace GitHub.Runner.Listener
} }
else else
{ {
skipMessageDeletion = true;
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body); var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
Pipelines.AgentJobRequestMessage jobRequestMessage = null; Pipelines.AgentJobRequestMessage jobRequestMessage = null;
@@ -579,15 +566,14 @@ namespace GitHub.Runner.Listener
try try
{ {
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token); jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
_acquireJobThrottler.Reset();
} }
catch (Exception ex) when ( catch (Exception ex) when (
ex is TaskOrchestrationJobNotFoundException || // HTTP status 404 ex is TaskOrchestrationJobNotFoundException ||
ex is TaskOrchestrationJobAlreadyAcquiredException || // HTTP status 409 ex is TaskOrchestrationJobAlreadyAcquiredException ||
ex is TaskOrchestrationJobUnprocessableException) // HTTP status 422 ex is TaskOrchestrationJobUnprocessableException)
{ {
Trace.Info($"Skipping message Job. {ex.Message}"); Trace.Error($"Skipping job: {ex.Message}");
await _acquireJobThrottler.IncrementAndWaitAsync(messageQueueLoopTokenSource.Token); skipMessageDeletion = false;
continue; continue;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -127,10 +127,6 @@ namespace GitHub.Runner.Worker
} }
} }
// Check OS warning
var osWarningChecker = HostContext.GetService<IOSWarningChecker>();
await osWarningChecker.CheckOSAsync(context);
try try
{ {
var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? ""; var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? "";

View File

@@ -1,110 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Worker
{
[ServiceLocator(Default = typeof(OSWarningChecker))]
public interface IOSWarningChecker : IRunnerService
{
Task CheckOSAsync(IExecutionContext context);
}
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
{
private static TimeSpan s_regexTimeout = TimeSpan.FromSeconds(1);
public async Task CheckOSAsync(IExecutionContext context)
{
ArgUtil.NotNull(context, nameof(context));
if (!context.Global.Variables.System_TestDotNet8Compatibility)
{
return;
}
context.Output("Testing runner upgrade compatibility");
List<string> output = new();
object outputLock = new();
try
{
using (var process = HostContext.CreateService<IProcessInvoker>())
{
process.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
{
if (!string.IsNullOrEmpty(stdout.Data))
{
lock (outputLock)
{
output.Add(stdout.Data);
Trace.Info(stdout.Data);
}
}
};
process.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
{
if (!string.IsNullOrEmpty(stderr.Data))
{
lock (outputLock)
{
output.Add(stderr.Data);
Trace.Error(stderr.Data);
}
}
};
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
int exitCode = await process.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
fileName: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "testDotNet8Compatibility", $"TestDotNet8Compatibility{IOUtil.ExeExtension}"),
arguments: string.Empty,
environment: null,
cancellationToken: cancellationTokenSource.Token);
var outputStr = string.Join("\n", output).Trim();
if (exitCode != 0 || !string.Equals(outputStr, "Hello from .NET 8!", StringComparison.Ordinal))
{
var pattern = context.Global.Variables.System_DotNet8CompatibilityOutputPattern;
if (!string.IsNullOrEmpty(pattern))
{
var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, s_regexTimeout);
if (!regex.IsMatch(outputStr))
{
return;
}
}
var warningMessage = context.Global.Variables.System_DotNet8CompatibilityWarning;
if (!string.IsNullOrEmpty(warningMessage))
{
context.Warning(warningMessage);
}
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test failed with exit code '{exitCode}' and output: {GetShortOutput(context, output)}" });
}
}
}
}
catch (Exception ex)
{
Trace.Error("An error occurred while testing .NET 8 compatibility'");
Trace.Error(ex);
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test encountered exception type '{ex.GetType().FullName}', message: '{ex.Message}', process output: '{GetShortOutput(context, output)}'" });
}
}
private static string GetShortOutput(IExecutionContext context, List<string> output)
{
var length = context.Global.Variables.System_DotNet8CompatibilityOutputLength ?? 200;
var outputStr = string.Join("\n", output).Trim();
return outputStr.Length > length ? string.Concat(outputStr.Substring(0, length), "[...]") : outputStr;
}
}
}

View File

@@ -72,16 +72,8 @@ namespace GitHub.Runner.Worker
public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug); public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug);
public string System_DotNet8CompatibilityWarning => Get(Constants.Variables.System.DotNet8CompatibilityWarning);
public string System_DotNet8CompatibilityOutputPattern => Get(Constants.Variables.System.DotNet8CompatibilityOutputPattern);
public int? System_DotNet8CompatibilityOutputLength => GetInt(Constants.Variables.System.DotNet8CompatibilityOutputLength);
public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName); public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);
public bool System_TestDotNet8Compatibility => GetBoolean(Constants.Variables.System.TestDotNet8Compatibility) ?? false;
public string Get(string name) public string Get(string name)
{ {
Variable variable; Variable variable;

View File

@@ -9,9 +9,9 @@ namespace GitHub.Actions.RunService.WebApi
public string Source { get; set; } public string Source { get; set; }
[DataMember(Name = "statusCode", EmitDefaultValue = false)] [DataMember(Name = "statusCode", EmitDefaultValue = false)]
public int Code { get; set; } public int StatusCode { get; set; }
[DataMember(Name = "errorMessage", EmitDefaultValue = false)] [DataMember(Name = "errorMessage", EmitDefaultValue = false)]
public string Message { get; set; } public string ErrorMessage { get; set; }
} }
} }

View File

@@ -86,7 +86,7 @@ namespace GitHub.Actions.RunService.WebApi
httpMethod, httpMethod,
requestUri: requestUri, requestUri: requestUri,
content: requestContent, content: requestContent,
readErrorBody: true, readErrorContent: true,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (result.IsSuccess)
@@ -94,16 +94,16 @@ namespace GitHub.Actions.RunService.WebApi
return result.Value; return result.Value;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error)) if (TryParseErrorContent(result.ErrorContent, out RunServiceError error))
{ {
switch ((HttpStatusCode)error.Code) switch ((HttpStatusCode)error.StatusCode)
{ {
case HttpStatusCode.NotFound: case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job message not found '{messageId}'. {error.Message}"); throw new TaskOrchestrationJobNotFoundException($"Job message not found '{messageId}'. {error.ErrorMessage}");
case HttpStatusCode.Conflict: case HttpStatusCode.Conflict:
throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired '{messageId}'. {error.Message}"); throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired '{messageId}'. {error.ErrorMessage}");
case HttpStatusCode.UnprocessableEntity: case HttpStatusCode.UnprocessableEntity:
throw new TaskOrchestrationJobUnprocessableException($"Unprocessable job '{messageId}'. {error.Message}"); throw new TaskOrchestrationJobUnprocessableException($"Unprocessable job '{messageId}'. {error.ErrorMessage}");
} }
} }
@@ -116,9 +116,9 @@ namespace GitHub.Actions.RunService.WebApi
throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired: {messageId}"); throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired: {messageId}");
} }
if (!string.IsNullOrEmpty(result.ErrorBody)) if (!string.IsNullOrEmpty(result.ErrorContent))
{ {
throw new Exception($"Failed to get job message: {result.Error}. {Truncate(result.ErrorBody)}"); throw new Exception($"Failed to get job message: {result.Error}. {result.ErrorContent}");
} }
else else
{ {
@@ -130,7 +130,7 @@ namespace GitHub.Actions.RunService.WebApi
Uri requestUri, Uri requestUri,
Guid planId, Guid planId,
Guid jobId, Guid jobId,
TaskResult conclusion, TaskResult result,
Dictionary<String, VariableValue> outputs, Dictionary<String, VariableValue> outputs,
IList<StepResult> stepResults, IList<StepResult> stepResults,
IList<Annotation> jobAnnotations, IList<Annotation> jobAnnotations,
@@ -142,7 +142,7 @@ namespace GitHub.Actions.RunService.WebApi
{ {
PlanID = planId, PlanID = planId,
JobID = jobId, JobID = jobId,
Conclusion = conclusion, Conclusion = result,
Outputs = outputs, Outputs = outputs,
StepResults = stepResults, StepResults = stepResults,
Annotations = jobAnnotations, Annotations = jobAnnotations,
@@ -152,39 +152,22 @@ namespace GitHub.Actions.RunService.WebApi
requestUri = new Uri(requestUri, "completejob"); requestUri = new Uri(requestUri, "completejob");
var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true)); var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
var result = await Send2Async( var response = await SendAsync(
httpMethod, httpMethod,
requestUri, requestUri,
content: requestContent, content: requestContent,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (response.IsSuccessStatusCode)
{ {
return; return;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error)) switch (response.StatusCode)
{
switch ((HttpStatusCode)error.Code)
{
case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}");
}
}
// Temporary back compat
switch (result.StatusCode)
{ {
case HttpStatusCode.NotFound: case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}"); throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
} default:
throw new Exception($"Failed to complete job: {response.ReasonPhrase}");
if (!string.IsNullOrEmpty(result.ErrorBody))
{
throw new Exception($"Failed to complete job: {result.Error}. {Truncate(result.ErrorBody)}");
}
else
{
throw new Exception($"Failed to complete job: {result.Error}");
} }
} }
@@ -208,7 +191,6 @@ namespace GitHub.Actions.RunService.WebApi
httpMethod, httpMethod,
requestUri, requestUri,
content: requestContent, content: requestContent,
readErrorBody: true,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (result.IsSuccess)
@@ -216,28 +198,11 @@ namespace GitHub.Actions.RunService.WebApi
return result.Value; return result.Value;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error))
{
switch ((HttpStatusCode)error.Code)
{
case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}");
}
}
// Temporary back compat
switch (result.StatusCode) switch (result.StatusCode)
{ {
case HttpStatusCode.NotFound: case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}"); throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
} default:
if (!string.IsNullOrEmpty(result.ErrorBody))
{
throw new Exception($"Failed to renew job: {result.Error}. {Truncate(result.ErrorBody)}");
}
else
{
throw new Exception($"Failed to renew job: {result.Error}"); throw new Exception($"Failed to renew job: {result.Error}");
} }
} }
@@ -248,13 +213,13 @@ namespace GitHub.Actions.RunService.WebApi
return JsonConvert.DeserializeObject<T>(json, s_serializerSettings); return JsonConvert.DeserializeObject<T>(json, s_serializerSettings);
} }
private static bool TryParseErrorBody(string errorBody, out RunServiceError error) private static bool TryParseErrorContent(string errorContent, out RunServiceError error)
{ {
if (!string.IsNullOrEmpty(errorBody)) if (!string.IsNullOrEmpty(errorContent))
{ {
try try
{ {
error = JsonUtility.FromString<RunServiceError>(errorBody); error = JsonUtility.FromString<RunServiceError>(errorContent);
if (error?.Source == "actions-run-service") if (error?.Source == "actions-run-service")
{ {
return true; return true;
@@ -268,15 +233,5 @@ namespace GitHub.Actions.RunService.WebApi
error = null; error = null;
return false; return false;
} }
private static string Truncate(string errorBody)
{
if (errorBody.Length > 100)
{
return errorBody.Substring(0, 100) + "[truncated]";
}
return errorBody;
}
} }
} }

View File

@@ -63,8 +63,7 @@ namespace GitHub.Actions.RunService.WebApi
string os = null, string os = null,
string architecture = null, string architecture = null,
bool? disableUpdate = null, bool? disableUpdate = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default)
)
{ {
var requestUri = new Uri(Client.BaseAddress, "message"); var requestUri = new Uri(Client.BaseAddress, "message");
@@ -123,8 +122,42 @@ namespace GitHub.Actions.RunService.WebApi
throw new Exception($"Failed to get job message: {result.Error}"); throw new Exception($"Failed to get job message: {result.Error}");
} }
public async Task<TaskAgentSession> CreateSessionAsync( public async Task DeleteRunnerMessageAsync(
Guid? sessionId,
string jobMessageKey,
CancellationToken cancellationToken = default)
{
var requestUri = new Uri(Client.BaseAddress, "message");
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
if (sessionId != null)
{
queryParams.Add("sessionId", sessionId.Value.ToString());
}
if (!string.IsNullOrEmpty(jobMessageKey))
{
queryParams.Add("jobMessageKey", jobMessageKey);
}
queryParams.Add("status", TaskAgentStatus.Online.ToString());
var result = await SendAsync<object>(
new HttpMethod("DELETE"),
requestUri: requestUri,
queryParameters: queryParams,
cancellationToken: cancellationToken);
if (result.IsSuccess)
{
return;
}
throw new Exception($"Failed to get job message: StatusCode={result.StatusCode} Error={result.Error}");
}
public async Task<TaskAgentSession> CreateSessionAsync(
TaskAgentSession session, TaskAgentSession session,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {

View File

@@ -101,55 +101,16 @@ namespace Sdk.WebApi.WebApi
} }
} }
protected async Task<RawHttpClientResult> Send2Async(
HttpMethod method,
Uri requestUri,
HttpContent content = null,
IEnumerable<KeyValuePair<String, String>> queryParameters = null,
Object userState = null,
CancellationToken cancellationToken = default(CancellationToken))
{
using (var response = await SendAsync(method, requestUri, content, queryParameters, userState, cancellationToken).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
return new RawHttpClientResult(
isSuccess: true,
error: string.Empty,
statusCode: response.StatusCode);
}
else
{
var errorBody = default(string);
try
{
errorBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
errorBody = $"Error reading HTTP response body: {ex.Message}";
}
string errorMessage = $"Error: {response.ReasonPhrase}";
return new RawHttpClientResult(
isSuccess: false,
error: errorMessage,
statusCode: response.StatusCode,
errorBody: errorBody);
}
}
}
protected Task<RawHttpClientResult<T>> SendAsync<T>( protected Task<RawHttpClientResult<T>> SendAsync<T>(
HttpMethod method, HttpMethod method,
Uri requestUri, Uri requestUri,
HttpContent content = null, HttpContent content = null,
IEnumerable<KeyValuePair<String, String>> queryParameters = null, IEnumerable<KeyValuePair<String, String>> queryParameters = null,
Boolean readErrorBody = false, Boolean readErrorContent = false,
Object userState = null, Object userState = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
return SendAsync<T>(method, null, requestUri, content, queryParameters, readErrorBody, userState, cancellationToken); return SendAsync<T>(method, null, requestUri, content, queryParameters, readErrorContent, userState, cancellationToken);
} }
protected async Task<RawHttpClientResult<T>> SendAsync<T>( protected async Task<RawHttpClientResult<T>> SendAsync<T>(
@@ -158,20 +119,20 @@ namespace Sdk.WebApi.WebApi
Uri requestUri, Uri requestUri,
HttpContent content = null, HttpContent content = null,
IEnumerable<KeyValuePair<String, String>> queryParameters = null, IEnumerable<KeyValuePair<String, String>> queryParameters = null,
Boolean readErrorBody = false, Boolean readErrorContent = false,
Object userState = null, Object userState = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
using (VssTraceActivity.GetOrCreate().EnterCorrelationScope()) using (VssTraceActivity.GetOrCreate().EnterCorrelationScope())
using (HttpRequestMessage requestMessage = CreateRequestMessage(method, additionalHeaders, requestUri, content, queryParameters)) using (HttpRequestMessage requestMessage = CreateRequestMessage(method, additionalHeaders, requestUri, content, queryParameters))
{ {
return await SendAsync<T>(requestMessage, readErrorBody, userState, cancellationToken).ConfigureAwait(false); return await SendAsync<T>(requestMessage, readErrorContent, userState, cancellationToken).ConfigureAwait(false);
} }
} }
protected async Task<RawHttpClientResult<T>> SendAsync<T>( protected async Task<RawHttpClientResult<T>> SendAsync<T>(
HttpRequestMessage message, HttpRequestMessage message,
Boolean readErrorBody = false, Boolean readErrorContent = false,
Object userState = null, Object userState = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
@@ -187,21 +148,14 @@ namespace Sdk.WebApi.WebApi
} }
else else
{ {
var errorBody = default(string); var errorContent = default(string);
if (readErrorBody) if (readErrorContent)
{ {
try errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
{
errorBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
errorBody = $"Error reading HTTP response body: {ex.Message}";
}
} }
string errorMessage = $"Error: {response.ReasonPhrase}"; string errorMessage = $"Error: {response.ReasonPhrase}";
return RawHttpClientResult<T>.Fail(errorMessage, response.StatusCode, errorBody); return RawHttpClientResult<T>.Fail(errorMessage, response.StatusCode, errorContent);
} }
} }
} }

View File

@@ -12,20 +12,20 @@ namespace Sdk.WebApi.WebApi
public string Error { get; protected set; } public string Error { get; protected set; }
/// <summary> /// <summary>
/// The HTTP response body for unsuccessful HTTP status codes, or an error message when reading the response body fails. /// The raw of the HTTP response, for unsuccessful HTTP status codes
/// </summary> /// </summary>
public string ErrorBody { get; protected set; } public string ErrorContent { get; protected set; }
public HttpStatusCode StatusCode { get; protected set; } public HttpStatusCode StatusCode { get; protected set; }
public bool IsFailure => !IsSuccess; public bool IsFailure => !IsSuccess;
public RawHttpClientResult(bool isSuccess, string error, HttpStatusCode statusCode, string errorBody = null) protected RawHttpClientResult(bool isSuccess, string error, HttpStatusCode statusCode, string errorContent = null)
{ {
IsSuccess = isSuccess; IsSuccess = isSuccess;
Error = error; Error = error;
StatusCode = statusCode; StatusCode = statusCode;
ErrorBody = errorBody; ErrorContent = errorContent;
} }
} }
@@ -33,13 +33,13 @@ namespace Sdk.WebApi.WebApi
{ {
public T Value { get; private set; } public T Value { get; private set; }
protected internal RawHttpClientResult(T value, bool isSuccess, string error, HttpStatusCode statusCode, string errorBody) protected internal RawHttpClientResult(T value, bool isSuccess, string error, HttpStatusCode statusCode, string errorContent)
: base(isSuccess, error, statusCode, errorBody) : base(isSuccess, error, statusCode, errorContent)
{ {
Value = value; Value = value;
} }
public static RawHttpClientResult<T> Fail(string message, HttpStatusCode statusCode, string errorBody) => new RawHttpClientResult<T>(default(T), false, message, statusCode, errorBody); public static RawHttpClientResult<T> Fail(string message, HttpStatusCode statusCode, string errorContent) => new RawHttpClientResult<T>(default(T), false, message, statusCode, errorContent);
public static RawHttpClientResult<T> Ok(T value) => new RawHttpClientResult<T>(value, true, string.Empty, HttpStatusCode.OK, null); public static RawHttpClientResult<T> Ok(T value) => new RawHttpClientResult<T>(value, true, string.Empty, HttpStatusCode.OK, null);
} }
} }

View File

@@ -1,213 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Listener;
using GitHub.Runner.Listener.Configuration;
using GitHub.Runner.Common.Tests;
using System.Runtime.CompilerServices;
using GitHub.Services.WebApi;
using Moq;
using Xunit;
namespace GitHub.Runner.Common.Tests.Listener
{
public sealed class ErrorThrottlerL0
{
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
[InlineData(5)]
[InlineData(6)]
[InlineData(7)]
[InlineData(8)]
public async void TestIncrementAndWait(int totalAttempts)
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange
var errorThrottler = new ErrorThrottler();
errorThrottler.Initialize(hc);
var eventArgs = new List<DelayEventArgs>();
hc.Delaying += (sender, args) =>
{
eventArgs.Add(args);
};
// Act
for (int attempt = 1; attempt <= totalAttempts; attempt++)
{
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
}
// Assert
Assert.Equal(totalAttempts - 1, eventArgs.Count);
for (int i = 0; i < eventArgs.Count; i++)
{
// Expected milliseconds
int expectedMin;
int expectedMax;
switch (i)
{
case 0:
expectedMin = 1000; // Min backoff
expectedMax = 1000;
break;
case 1:
expectedMin = 1800; // Min + 0.8 * Coefficient
expectedMax = 2200; // Min + 1.2 * Coefficient
break;
case 2:
expectedMin = 3400; // Min + 0.8 * Coefficient * 3
expectedMax = 4600; // Min + 1.2 * Coefficient * 3
break;
case 3:
expectedMin = 6600; // Min + 0.8 * Coefficient * 7
expectedMax = 9400; // Min + 1.2 * Coefficient * 7
break;
case 4:
expectedMin = 13000; // Min + 0.8 * Coefficient * 15
expectedMax = 19000; // Min + 1.2 * Coefficient * 15
break;
case 5:
expectedMin = 25800; // Min + 0.8 * Coefficient * 31
expectedMax = 38200; // Min + 1.2 * Coefficient * 31
break;
case 6:
expectedMin = 51400; // Min + 0.8 * Coefficient * 63
expectedMax = 60000; // Max backoff
break;
case 7:
expectedMin = 60000;
expectedMax = 60000;
break;
default:
throw new NotSupportedException("Unexpected eventArgs count");
}
var actualMilliseconds = eventArgs[i].Delay.TotalMilliseconds;
Assert.True(expectedMin <= actualMilliseconds, $"Unexpected min delay for eventArgs[{i}]. Expected min {expectedMin}, actual {actualMilliseconds}");
Assert.True(expectedMax >= actualMilliseconds, $"Unexpected max delay for eventArgs[{i}]. Expected max {expectedMax}, actual {actualMilliseconds}");
}
}
}
[Fact]
public async void TestReset()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange
var errorThrottler = new ErrorThrottler();
errorThrottler.Initialize(hc);
var eventArgs = new List<DelayEventArgs>();
hc.Delaying += (sender, args) =>
{
eventArgs.Add(args);
};
// Act
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
errorThrottler.Reset();
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
// Assert
Assert.Equal(4, eventArgs.Count);
for (int i = 0; i < eventArgs.Count; i++)
{
// Expected milliseconds
int expectedMin;
int expectedMax;
switch (i)
{
case 0:
case 2:
expectedMin = 1000; // Min backoff
expectedMax = 1000;
break;
case 1:
case 3:
expectedMin = 1800; // Min + 0.8 * Coefficient
expectedMax = 2200; // Min + 1.2 * Coefficient
break;
default:
throw new NotSupportedException("Unexpected eventArgs count");
}
var actualMilliseconds = eventArgs[i].Delay.TotalMilliseconds;
Assert.True(expectedMin <= actualMilliseconds, $"Unexpected min delay for eventArgs[{i}]. Expected min {expectedMin}, actual {actualMilliseconds}");
Assert.True(expectedMax >= actualMilliseconds, $"Unexpected max delay for eventArgs[{i}]. Expected max {expectedMax}, actual {actualMilliseconds}");
}
}
}
[Fact]
public async void TestReceivesCancellationToken()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange
var errorThrottler = new ErrorThrottler();
errorThrottler.Initialize(hc);
var eventArgs = new List<DelayEventArgs>();
hc.Delaying += (sender, args) =>
{
eventArgs.Add(args);
};
var cancellationTokenSource1 = new CancellationTokenSource();
var cancellationTokenSource2 = new CancellationTokenSource();
var cancellationTokenSource3 = new CancellationTokenSource();
// Act
await errorThrottler.IncrementAndWaitAsync(cancellationTokenSource1.Token);
await errorThrottler.IncrementAndWaitAsync(cancellationTokenSource2.Token);
await errorThrottler.IncrementAndWaitAsync(cancellationTokenSource3.Token);
// Assert
Assert.Equal(2, eventArgs.Count);
Assert.Equal(cancellationTokenSource2.Token, eventArgs[0].Token);
Assert.Equal(cancellationTokenSource3.Token, eventArgs[1].Token);
}
}
[Fact]
public async void TestReceivesSender()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange
var errorThrottler = new ErrorThrottler();
errorThrottler.Initialize(hc);
var senders = new List<object>();
hc.Delaying += (sender, args) =>
{
senders.Add(sender);
};
// Act
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
await errorThrottler.IncrementAndWaitAsync(CancellationToken.None);
// Assert
Assert.Equal(2, senders.Count);
Assert.Equal(hc, senders[0]);
Assert.Equal(hc, senders[1]);
}
}
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{
return new TestHostContext(this, testName);
}
}
}

View File

@@ -23,7 +23,6 @@ namespace GitHub.Runner.Common.Tests.Listener
private Mock<ITerminal> _term; private Mock<ITerminal> _term;
private Mock<IConfigurationStore> _configStore; private Mock<IConfigurationStore> _configStore;
private Mock<ISelfUpdater> _updater; private Mock<ISelfUpdater> _updater;
private Mock<IErrorThrottler> _acquireJobThrottler;
public RunnerL0() public RunnerL0()
{ {
@@ -36,7 +35,6 @@ namespace GitHub.Runner.Common.Tests.Listener
_term = new Mock<ITerminal>(); _term = new Mock<ITerminal>();
_configStore = new Mock<IConfigurationStore>(); _configStore = new Mock<IConfigurationStore>();
_updater = new Mock<ISelfUpdater>(); _updater = new Mock<ISelfUpdater>();
_acquireJobThrottler = new Mock<IErrorThrottler>();
} }
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName) private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
@@ -69,7 +67,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -177,7 +174,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, args); var command = new CommandSettings(hc, args);
@@ -209,7 +205,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "run" }); var command = new CommandSettings(hc, new[] { "run" });
@@ -247,7 +242,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -344,7 +338,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -446,7 +439,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<ISelfUpdater>(_updater.Object); hc.SetSingleton<ISelfUpdater>(_updater.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
@@ -530,7 +522,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IConfigurationManager>(_configurationManager.Object); hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "remove", "--local" }); var command = new CommandSettings(hc, new[] { "remove", "--local" });

View File

@@ -30,11 +30,9 @@ namespace GitHub.Runner.Common.Tests
private string _tempDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D")); private string _tempDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
private StartupType _startupType; private StartupType _startupType;
public event EventHandler Unloading; public event EventHandler Unloading;
public event EventHandler<DelayEventArgs> Delaying;
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token; public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
public ShutdownReason RunnerShutdownReason { get; private set; } public ShutdownReason RunnerShutdownReason { get; private set; }
public ISecretMasker SecretMasker => _secretMasker; public ISecretMasker SecretMasker => _secretMasker;
public TestHostContext(object testClass, [CallerMemberName] string testName = "") public TestHostContext(object testClass, [CallerMemberName] string testName = "")
{ {
ArgUtil.NotNull(testClass, nameof(testClass)); ArgUtil.NotNull(testClass, nameof(testClass));
@@ -94,14 +92,6 @@ namespace GitHub.Runner.Common.Tests
public async Task Delay(TimeSpan delay, CancellationToken token) public async Task Delay(TimeSpan delay, CancellationToken token)
{ {
// Event callback
EventHandler<DelayEventArgs> handler = Delaying;
if (handler != null)
{
handler(this, new DelayEventArgs(delay, token));
}
// Delay zero
await Task.Delay(TimeSpan.Zero); await Task.Delay(TimeSpan.Zero);
} }
@@ -371,19 +361,4 @@ namespace GitHub.Runner.Common.Tests
} }
} }
} }
public class DelayEventArgs : EventArgs
{
public DelayEventArgs(
TimeSpan delay,
CancellationToken token)
{
Delay = delay;
Token = token;
}
public TimeSpan Delay { get; }
public CancellationToken Token { get; }
}
} }

View File

@@ -140,7 +140,6 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.SetSingleton(_diagnosticLogManager.Object); hc.SetSingleton(_diagnosticLogManager.Object);
hc.SetSingleton(_jobHookProvider.Object); hc.SetSingleton(_jobHookProvider.Object);
hc.SetSingleton(_snapshotOperationProvider.Object); hc.SetSingleton(_snapshotOperationProvider.Object);
hc.SetSingleton(new Mock<IOSWarningChecker>().Object);
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // JobExecutionContext hc.EnqueueInstance<IPagingLogger>(_logger.Object); // JobExecutionContext
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // job start hook hc.EnqueueInstance<IPagingLogger>(_logger.Object); // job start hook
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // Initial Job hc.EnqueueInstance<IPagingLogger>(_logger.Object); // Initial Job

View File

@@ -1,13 +0,0 @@
using System;
namespace TestDotNet8Compatibility
{
public static class Program
{
public static int Main(string[] args)
{
Console.WriteLine("Hello from .NET 8!");
return 0;
}
}
}

View File

@@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<Version>$(Version)</Version>
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugType>portable</DebugType>
</PropertyGroup>
</Project>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectFiles Include="TestDotNet8Compatibility.csproj" />
</ItemGroup>
<Target Name="Build">
<MSBuild Targets="Restore" Projects="@(ProjectFiles)" StopOnFirstFailure="true" />
<MSBuild Targets="Publish" Projects="@(ProjectFiles)" BuildInParallel="false" StopOnFirstFailure="true" Properties="Configuration=$(BUILDCONFIG);PackageRuntime=$(PackageRuntime);Version=$(RunnerVersion);RuntimeIdentifier=$(PackageRuntime);PublishDir=$(MSBuildProjectDirectory)/../../_layout/bin/testDotNet8Compatibility" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(MSBuildProjectDirectory)/../../_layout/bin/testDotNet8Compatibility" />
<RemoveDir Directories="TestDotNet8Compatibility/bin" />
<RemoveDir Directories="TestDotNet8Compatibility/obj" />
</Target>
<Target Name="Layout" DependsOnTargets="Clean;Build">
</Target>
</Project>

View File

@@ -1,5 +0,0 @@
{
"sdk": {
"version": "8.0.303"
}
}

View File

@@ -19,8 +19,6 @@ PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk" DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="6.0.421" DOTNETSDK_VERSION="6.0.421"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION" DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
DOTNET8SDK_VERSION="8.0.303"
DOTNET8SDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNET8SDK_VERSION"
RUNNER_VERSION=$(cat runnerversion) RUNNER_VERSION=$(cat runnerversion)
pushd "$SCRIPT_DIR" pushd "$SCRIPT_DIR"
@@ -127,19 +125,6 @@ function build ()
{ {
heading "Building ..." heading "Building ..."
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
# Build TestDotNet8Compatibility
heading "Building .NET 8 compatibility test"
echo "Prepend ${DOTNET8SDK_INSTALLDIR} to %PATH%" # Prepend .NET 8 SDK to PATH
PATH_BAK=$PATH
export PATH=${DOTNET8SDK_INSTALLDIR}:$PATH
pushd "$SCRIPT_DIR/TestDotNet8Compatibility" > /dev/null # Working directory
pwd
echo "Dotnet 8 SDK Version"
dotnet --version
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
popd > /dev/null # Restore working directory
export PATH=$PATH_BAK # Restore PATH
} }
function layout () function layout ()
@@ -158,18 +143,6 @@ function layout ()
heading "Setup externals folder for $RUNTIME_ID runner's layout" heading "Setup externals folder for $RUNTIME_ID runner's layout"
bash ./Misc/externals.sh $RUNTIME_ID || checkRC externals.sh bash ./Misc/externals.sh $RUNTIME_ID || checkRC externals.sh
# Build TestDotNet8Compatibility
echo "Prepend ${DOTNET8SDK_INSTALLDIR} to %PATH%" # Prepend .NET 8 SDK to PATH
PATH_BAK=$PATH
export PATH=${DOTNET8SDK_INSTALLDIR}:$PATH
pushd "$SCRIPT_DIR/TestDotNet8Compatibility" > /dev/null # Working directory
heading "Dotnet 8 SDK Version"
dotnet --version
heading "Building .NET 8 compatibility test"
dotnet msbuild -t:layout -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
popd > /dev/null # Restore working directory
export PATH=$PATH_BAK # Restore PATH
} }
function runtest () function runtest ()
@@ -226,7 +199,6 @@ function package ()
popd > /dev/null popd > /dev/null
} }
# Install .NET SDK
if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}") || (! -e "${DOTNETSDK_INSTALLDIR}/dotnet") ]]; then if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}") || (! -e "${DOTNETSDK_INSTALLDIR}/dotnet") ]]; then
# Download dotnet SDK to ../_dotnetsdk directory # Download dotnet SDK to ../_dotnetsdk directory
@@ -252,32 +224,6 @@ if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTN
echo "${DOTNETSDK_VERSION}" > "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}" echo "${DOTNETSDK_VERSION}" > "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}"
fi fi
# Install .NET 8 SDK
if [[ (! -d "${DOTNET8SDK_INSTALLDIR}") || (! -e "${DOTNET8SDK_INSTALLDIR}/.${DOTNET8SDK_VERSION}") || (! -e "${DOTNET8SDK_INSTALLDIR}/dotnet") ]]; then
# Download dotnet 8 SDK to ../_dotnetsdk directory
heading "Ensure Dotnet 8 SDK"
# _dotnetsdk
# \1.0.x
# \dotnet
# \.1.0.x
echo "Download dotnet8sdk into ${DOTNET8SDK_INSTALLDIR}"
rm -Rf "${DOTNETSDK_DIR}"
# run dotnet-install.ps1 on windows, dotnet-install.sh on linux
if [[ ("$CURRENT_PLATFORM" == "windows") ]]; then
echo "Convert ${DOTNET8SDK_INSTALLDIR} to Windows style path"
sdkinstallwindow_path=${DOTNET8SDK_INSTALLDIR:1}
sdkinstallwindow_path=${sdkinstallwindow_path:0:1}:${sdkinstallwindow_path:1}
$POWERSHELL -NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "& \"./Misc/dotnet-install.ps1\" -Version ${DOTNET8SDK_VERSION} -InstallDir \"${sdkinstallwindow_path}\" -NoPath; exit \$LastExitCode;" || checkRC dotnet-install.ps1
else
bash ./Misc/dotnet-install.sh --version ${DOTNET8SDK_VERSION} --install-dir "${DOTNET8SDK_INSTALLDIR}" --no-path || checkRC dotnet-install.sh
fi
echo "${DOTNET8SDK_VERSION}" > "${DOTNET8SDK_INSTALLDIR}/.${DOTNET8SDK_VERSION}"
fi
echo "Prepend ${DOTNETSDK_INSTALLDIR} to %PATH%" echo "Prepend ${DOTNETSDK_INSTALLDIR} to %PATH%"
export PATH=${DOTNETSDK_INSTALLDIR}:$PATH export PATH=${DOTNETSDK_INSTALLDIR}:$PATH

View File

@@ -1 +1 @@
2.319.1 2.317.0