mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
45 Commits
users/jww3
...
v2.306.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da6dfe9110 | ||
|
|
7d432fb24c | ||
|
|
e8ee6f7b1b | ||
|
|
d4bbbb8419 | ||
|
|
4ffd081aea | ||
|
|
c05e6748c3 | ||
|
|
a2b7856c9c | ||
|
|
5f1c6f4708 | ||
|
|
8415f13bab | ||
|
|
471e3ae2d9 | ||
|
|
1096b975e4 | ||
|
|
282ba4cfc8 | ||
|
|
b737a5ac5c | ||
|
|
20721bc950 | ||
|
|
fde86b0666 | ||
|
|
efffbaeabc | ||
|
|
3a1376f90e | ||
|
|
50b3edff3c | ||
|
|
58f7a379a1 | ||
|
|
e13627df81 | ||
|
|
48cbee08f9 | ||
|
|
21b49c542c | ||
|
|
8db8bbe13a | ||
|
|
49b04976f4 | ||
|
|
eeb0cf6f1e | ||
|
|
f8a28c3c4e | ||
|
|
1bc14f0607 | ||
|
|
22d1938ac4 | ||
|
|
229b9b8ecc | ||
|
|
896152d78e | ||
|
|
8d74a9ead6 | ||
|
|
77b8586a03 | ||
|
|
c8c47d4f27 | ||
|
|
58f3ff55aa | ||
|
|
6353ac84d7 | ||
|
|
ad9a4a45d1 | ||
|
|
a41397ae93 | ||
|
|
c4d41e95cb | ||
|
|
af6ed41bcb | ||
|
|
e8b2380a20 | ||
|
|
38ab9dedf4 | ||
|
|
c7629700ad | ||
|
|
b9a0b5dba9 | ||
|
|
766cefe599 | ||
|
|
2ecd7d2fc6 |
@@ -1,9 +0,0 @@
|
|||||||
# https://editorconfig.org/
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8 # Set default charset to utf-8
|
|
||||||
insert_final_newline = true # ensure all files end with a single newline
|
|
||||||
trim_trailing_whitespace = true # attempt to remove trailing whitespace on save
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false # in markdown, "two trailing spaces" is unfortunately meaningful; it means `<br>`
|
|
||||||
@@ -157,7 +157,7 @@ cat (Runner/Worker)_TIMESTAMP.log # view your log file
|
|||||||
## Styling
|
## Styling
|
||||||
|
|
||||||
We use the .NET Foundation and CoreCLR style guidelines [located here](
|
We use the .NET Foundation and CoreCLR style guidelines [located here](
|
||||||
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)
|
https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md)
|
||||||
|
|
||||||
### Format C# Code
|
### Format C# Code
|
||||||
|
|
||||||
@@ -165,4 +165,4 @@ To format both staged and unstaged .cs files
|
|||||||
```
|
```
|
||||||
cd ./src
|
cd ./src
|
||||||
./dev.(cmd|sh) format
|
./dev.(cmd|sh) format
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0 as build
|
# Source: https://github.com/dotnet/dotnet-docker
|
||||||
|
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
|
||||||
|
|
||||||
ARG RUNNER_VERSION
|
ARG RUNNER_VERSION
|
||||||
ARG RUNNER_ARCH="x64"
|
ARG RUNNER_ARCH="x64"
|
||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.1
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.2
|
||||||
ARG DOCKER_VERSION=20.10.23
|
ARG DOCKER_VERSION=20.10.23
|
||||||
|
|
||||||
RUN apt update -y && apt install curl unzip -y
|
RUN apt update -y && apt install curl unzip -y
|
||||||
@@ -22,7 +23,7 @@ RUN export DOCKER_ARCH=x86_64 \
|
|||||||
&& tar zxvf docker.tgz \
|
&& tar zxvf docker.tgz \
|
||||||
&& rm -rf docker.tgz
|
&& rm -rf docker.tgz
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0
|
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
||||||
@@ -31,6 +32,7 @@ ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
|||||||
RUN apt-get update -y \
|
RUN apt-get update -y \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
sudo \
|
sudo \
|
||||||
|
lsb-release \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
## Features
|
## Features
|
||||||
- Support matrix context in output keys (#2477)
|
- Add warning to notify about forcing actions to run on node16 instead of node12 (#2678)
|
||||||
- Add update certificates to `./run.sh` if `RUNNER_UPDATE_CA_CERTS` env is set (#2471)
|
|
||||||
- Bypass all proxies for all hosts if `no_proxy='*'` is set (#2395)
|
|
||||||
- Change runner image to make user/folder align with `ubuntu-latest` hosted runner. (#2469)
|
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
- Exit on runner version deprecation error (#2299)
|
- Remove job completion from runner listener (#2659)
|
||||||
- Runner service exit after consecutive re-try exits (#2426)
|
- Fix double error reporting (#2656)
|
||||||
|
- Fix a bug with incorrect parsing of image values in a container action (#1873)
|
||||||
|
- Fix error message reported on non-local action setup (#2668)
|
||||||
|
- Extend github context with host-workspace (#2517)
|
||||||
|
- Fixed a bug where a misplaced = character could bypass heredoc-style processing (#2627)
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
- Replace deprecated command with environment file (#2429)
|
- Send environment url to Run Service (#2650)
|
||||||
- Make requests to `Run` service to renew job request (#2461)
|
- Reduce token service and unnecessary calls - send token to redirects (#2660)
|
||||||
- Add job/step log upload to Result service (#2447, #2439)
|
- Add 'http://' to http(s)_proxy if there is no protocol (#2663)
|
||||||
|
- Remove extra result step for job itself (#2620)
|
||||||
|
|
||||||
_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.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<Update to ./src/runnerversion when creating release>
|
2.306.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[*.cs]
|
[*.cs]
|
||||||
charset = utf-8
|
charset = utf-8-bom
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
csharp_new_line_before_else = true
|
csharp_new_line_before_else = true
|
||||||
|
|||||||
@@ -55,12 +55,23 @@ function acquireExternalTool() {
|
|||||||
# Download from source to the partial file.
|
# Download from source to the partial file.
|
||||||
echo "Downloading $download_source"
|
echo "Downloading $download_source"
|
||||||
mkdir -p "$(dirname "$download_target")" || checkRC 'mkdir'
|
mkdir -p "$(dirname "$download_target")" || checkRC 'mkdir'
|
||||||
|
|
||||||
|
CURL_VERSION=$(curl --version | awk 'NR==1{print $2}')
|
||||||
|
echo "Curl version: $CURL_VERSION"
|
||||||
|
|
||||||
# curl -f Fail silently (no output at all) on HTTP errors (H)
|
# curl -f Fail silently (no output at all) on HTTP errors (H)
|
||||||
# -k Allow connections to SSL sites without certs (H)
|
# -k Allow connections to SSL sites without certs (H)
|
||||||
# -S Show error. With -s, make curl show errors when they occur
|
# -S Show error. With -s, make curl show errors when they occur
|
||||||
# -L Follow redirects (H)
|
# -L Follow redirects (H)
|
||||||
# -o FILE Write to FILE instead of stdout
|
# -o FILE Write to FILE instead of stdout
|
||||||
curl -fkSL -o "$partial_target" "$download_source" 2>"${download_target}_download.log" || checkRC 'curl'
|
# --retry 3 Retries transient errors 3 times (timeouts, 5xx)
|
||||||
|
if [[ "$(printf '%s\n' "7.71.0" "$CURL_VERSION" | sort -V | head -n1)" != "7.71.0" ]]; then
|
||||||
|
# Curl version is less than or equal to 7.71.0, skipping retry-all-errors flag
|
||||||
|
curl -fkSL --retry 3 -o "$partial_target" "$download_source" 2>"${download_target}_download.log" || checkRC 'curl'
|
||||||
|
else
|
||||||
|
# Curl version is greater than 7.71.0, running curl with --retry-all-errors flag
|
||||||
|
curl -fkSL --retry 3 --retry-all-errors -o "$partial_target" "$download_source" 2>"${download_target}_download.log" || checkRC 'curl'
|
||||||
|
fi
|
||||||
|
|
||||||
# Move the partial file to the download target.
|
# Move the partial file to the download target.
|
||||||
mv "$partial_target" "$download_target" || checkRC 'mv'
|
mv "$partial_target" "$download_target" || checkRC 'mv'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
public enum ActionResult
|
public enum ActionResult
|
||||||
{
|
{
|
||||||
@@ -10,4 +10,4 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
Skipped = 3
|
Skipped = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GitHub.DistributedTask.Logging;
|
using GitHub.DistributedTask.Logging;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string GenerateServiceConfig = "generateServiceConfig";
|
public static readonly string GenerateServiceConfig = "generateServiceConfig";
|
||||||
public static readonly string Help = "help";
|
public static readonly string Help = "help";
|
||||||
public static readonly string Local = "local";
|
public static readonly string Local = "local";
|
||||||
|
public static readonly string NoDefaultLabels = "no-default-labels";
|
||||||
public static readonly string Replace = "replace";
|
public static readonly string Replace = "replace";
|
||||||
public static readonly string DisableUpdate = "disableupdate";
|
public static readonly string DisableUpdate = "disableupdate";
|
||||||
public static readonly string Once = "once"; // Keep this around since customers still relies on it
|
public static readonly string Once = "once"; // Keep this around since customers still relies on it
|
||||||
@@ -154,6 +155,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
|
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
|
||||||
public static readonly string Node12Warning = "DistributedTask.AddWarningToNode12Action";
|
public static readonly string Node12Warning = "DistributedTask.AddWarningToNode12Action";
|
||||||
|
public static readonly string LogTemplateErrorsAsDebugMessages = "DistributedTask.LogTemplateErrorsAsDebugMessages";
|
||||||
public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate";
|
public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate";
|
||||||
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
|
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
|
||||||
}
|
}
|
||||||
@@ -170,6 +172,8 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||||
public static readonly string SummaryUploadError = "$GITHUB_STEP_SUMMARY upload aborted, an error occurred when uploading the summary. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
public static readonly string SummaryUploadError = "$GITHUB_STEP_SUMMARY upload aborted, an error occurred when uploading the summary. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||||
public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: {0}. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.";
|
public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: {0}. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.";
|
||||||
|
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 class RunnerEvent
|
public static class RunnerEvent
|
||||||
@@ -261,6 +265,8 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string AccessToken = "system.accessToken";
|
public static readonly string AccessToken = "system.accessToken";
|
||||||
public static readonly string Culture = "system.culture";
|
public static readonly string Culture = "system.culture";
|
||||||
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 OrchestrationId = "system.orchestrationId";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|
||||||
@@ -24,4 +24,4 @@ namespace GitHub.Runner.Common
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -37,10 +37,10 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
ConnectMonitor(monitorSocketAddress);
|
ConnectMonitor(monitorSocketAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartMonitor(Guid jobId, string accessToken, Uri serverUri)
|
private void StartMonitor(Guid jobId, string accessToken, Uri serverUri)
|
||||||
{
|
{
|
||||||
if(String.IsNullOrEmpty(accessToken))
|
if (String.IsNullOrEmpty(accessToken))
|
||||||
{
|
{
|
||||||
Trace.Info("No access token could be retrieved to start the monitor.");
|
Trace.Info("No access token could be retrieved to start the monitor.");
|
||||||
return;
|
return;
|
||||||
@@ -82,7 +82,7 @@ namespace GitHub.Runner.Common
|
|||||||
_monitorSocket.Send(Encoding.UTF8.GetBytes(message));
|
_monitorSocket.Send(Encoding.UTF8.GetBytes(message));
|
||||||
Trace.Info("Finished EndMonitor writing to socket");
|
Trace.Info("Finished EndMonitor writing to socket");
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
await Task.Delay(TimeSpan.FromSeconds(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SocketException e)
|
catch (SocketException e)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -11,10 +11,10 @@ using System.Threading.Tasks;
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Services.OAuth;
|
||||||
|
using GitHub.Services.Results.Client;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using GitHub.Services.WebApi.Utilities.Internal;
|
using GitHub.Services.WebApi.Utilities.Internal;
|
||||||
using GitHub.Services.Results.Client;
|
|
||||||
using GitHub.Services.OAuth;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
@@ -199,13 +199,15 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
Trace.Info($"Attempting to start websocket client with delay {delay}.");
|
Trace.Info($"Attempting to start websocket client with delay {delay}.");
|
||||||
await Task.Delay(delay);
|
await Task.Delay(delay);
|
||||||
await this._websocketClient.ConnectAsync(new Uri(feedStreamUrl), default(CancellationToken));
|
using var connectTimeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
await this._websocketClient.ConnectAsync(new Uri(feedStreamUrl), connectTimeoutTokenSource.Token);
|
||||||
Trace.Info($"Successfully started websocket client.");
|
Trace.Info($"Successfully started websocket client.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Info("Exception caught during websocket client connect, fallback of HTTP would be used now instead of websocket.");
|
Trace.Info("Exception caught during websocket client connect, fallback of HTTP would be used now instead of websocket.");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
|
this._websocketClient = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +254,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
failedAttemptsToPostBatchedLinesByWebsocket++;
|
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.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.Error(ex);
|
Trace.Verbose(ex.ToString());
|
||||||
if (totalBatchedLinesAttemptedByWebsocket > _minWebsocketBatchedLinesCountToConsider)
|
if (totalBatchedLinesAttemptedByWebsocket > _minWebsocketBatchedLinesCountToConsider)
|
||||||
{
|
{
|
||||||
// let's consider failure percentage
|
// let's consider failure percentage
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -17,7 +17,7 @@ namespace GitHub.Runner.Common
|
|||||||
TaskCompletionSource<int> JobRecordUpdated { get; }
|
TaskCompletionSource<int> JobRecordUpdated { get; }
|
||||||
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
|
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
|
||||||
Task ShutdownAsync();
|
Task ShutdownAsync();
|
||||||
void Start(Pipelines.AgentJobRequestMessage jobRequest);
|
void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultServiceOnly = false);
|
||||||
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
||||||
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
|
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
|
||||||
void QueueResultsUpload(Guid timelineRecordId, string name, string path, string type, bool deleteSource, bool finalize, bool firstBlock, long totalLines);
|
void QueueResultsUpload(Guid timelineRecordId, string name, string path, string type, bool deleteSource, bool finalize, bool firstBlock, long totalLines);
|
||||||
@@ -70,6 +70,7 @@ namespace GitHub.Runner.Common
|
|||||||
private readonly TaskCompletionSource<int> _jobCompletionSource = new();
|
private readonly TaskCompletionSource<int> _jobCompletionSource = new();
|
||||||
private readonly TaskCompletionSource<int> _jobRecordUpdated = new();
|
private readonly TaskCompletionSource<int> _jobRecordUpdated = new();
|
||||||
private bool _queueInProcess = false;
|
private bool _queueInProcess = false;
|
||||||
|
private bool _resultsServiceOnly = false;
|
||||||
|
|
||||||
public TaskCompletionSource<int> JobRecordUpdated => _jobRecordUpdated;
|
public TaskCompletionSource<int> JobRecordUpdated => _jobRecordUpdated;
|
||||||
|
|
||||||
@@ -95,13 +96,17 @@ namespace GitHub.Runner.Common
|
|||||||
_resultsServer = hostContext.GetService<IResultsServer>();
|
_resultsServer = hostContext.GetService<IResultsServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Pipelines.AgentJobRequestMessage jobRequest)
|
public void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultServiceOnly = false)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
_resultsServiceOnly = resultServiceOnly;
|
||||||
|
|
||||||
var serviceEndPoint = jobRequest.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
var serviceEndPoint = jobRequest.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
_jobServer.InitializeWebsocketClient(serviceEndPoint);
|
if (!resultServiceOnly)
|
||||||
|
{
|
||||||
|
_jobServer.InitializeWebsocketClient(serviceEndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
// This code is usually wrapped by an instance of IExecutionContext which isn't available here.
|
// This code is usually wrapped by an instance of IExecutionContext which isn't available here.
|
||||||
jobRequest.Variables.TryGetValue("system.github.results_endpoint", out VariableValue resultsEndpointVariable);
|
jobRequest.Variables.TryGetValue("system.github.results_endpoint", out VariableValue resultsEndpointVariable);
|
||||||
@@ -112,8 +117,16 @@ namespace GitHub.Runner.Common
|
|||||||
!string.IsNullOrEmpty(accessToken) &&
|
!string.IsNullOrEmpty(accessToken) &&
|
||||||
!string.IsNullOrEmpty(resultsReceiverEndpoint))
|
!string.IsNullOrEmpty(resultsReceiverEndpoint))
|
||||||
{
|
{
|
||||||
|
string liveConsoleFeedUrl = null;
|
||||||
Trace.Info("Initializing results client");
|
Trace.Info("Initializing results client");
|
||||||
_resultsServer.InitializeResultsClient(new Uri(resultsReceiverEndpoint), accessToken);
|
if (resultServiceOnly
|
||||||
|
&& serviceEndPoint.Data.TryGetValue("FeedStreamUrl", out var feedStreamUrl)
|
||||||
|
&& !string.IsNullOrEmpty(feedStreamUrl))
|
||||||
|
{
|
||||||
|
liveConsoleFeedUrl = feedStreamUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resultsServer.InitializeResultsClient(new Uri(resultsReceiverEndpoint), liveConsoleFeedUrl, accessToken);
|
||||||
_resultsClientInitiated = true;
|
_resultsClientInitiated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +207,9 @@ namespace GitHub.Runner.Common
|
|||||||
Trace.Info($"Disposing job server ...");
|
Trace.Info($"Disposing job server ...");
|
||||||
await _jobServer.DisposeAsync();
|
await _jobServer.DisposeAsync();
|
||||||
|
|
||||||
|
Trace.Info($"Disposing results server ...");
|
||||||
|
await _resultsServer.DisposeAsync();
|
||||||
|
|
||||||
Trace.Info("All queue process tasks have been stopped, and all queues are drained.");
|
Trace.Info("All queue process tasks have been stopped, and all queues are drained.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +388,14 @@ namespace GitHub.Runner.Common
|
|||||||
// Give at most 60s for each request.
|
// Give at most 60s for each request.
|
||||||
using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60)))
|
using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60)))
|
||||||
{
|
{
|
||||||
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber, timeoutTokenSource.Token);
|
if (_resultsServiceOnly)
|
||||||
|
{
|
||||||
|
await _resultsServer.AppendLiveConsoleFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber, timeoutTokenSource.Token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber, timeoutTokenSource.Token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_firstConsoleOutputs)
|
if (_firstConsoleOutputs)
|
||||||
@@ -599,7 +622,7 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
foreach (var detailTimeline in update.PendingRecords.Where(r => r.Details != null))
|
foreach (var detailTimeline in update.PendingRecords.Where(r => r.Details != null))
|
||||||
{
|
{
|
||||||
if (!_allTimelines.Contains(detailTimeline.Details.Id))
|
if (!_resultsServiceOnly && !_allTimelines.Contains(detailTimeline.Details.Id))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -621,7 +644,11 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _jobServer.UpdateTimelineRecordsAsync(_scopeIdentifier, _hubName, _planId, update.TimelineId, update.PendingRecords, default(CancellationToken));
|
if (!_resultsServiceOnly)
|
||||||
|
{
|
||||||
|
await _jobServer.UpdateTimelineRecordsAsync(_scopeIdentifier, _hubName, _planId, update.TimelineId, update.PendingRecords, default(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_resultsClientInitiated)
|
if (_resultsClientInitiated)
|
||||||
@@ -729,17 +756,17 @@ namespace GitHub.Runner.Common
|
|||||||
timelineRecord.State = rec.State ?? timelineRecord.State;
|
timelineRecord.State = rec.State ?? timelineRecord.State;
|
||||||
timelineRecord.WorkerName = rec.WorkerName ?? timelineRecord.WorkerName;
|
timelineRecord.WorkerName = rec.WorkerName ?? timelineRecord.WorkerName;
|
||||||
|
|
||||||
if (rec.ErrorCount != null && rec.ErrorCount > 0)
|
if (rec.ErrorCount > 0)
|
||||||
{
|
{
|
||||||
timelineRecord.ErrorCount = rec.ErrorCount;
|
timelineRecord.ErrorCount = rec.ErrorCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rec.WarningCount != null && rec.WarningCount > 0)
|
if (rec.WarningCount > 0)
|
||||||
{
|
{
|
||||||
timelineRecord.WarningCount = rec.WarningCount;
|
timelineRecord.WarningCount = rec.WarningCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rec.NoticeCount != null && rec.NoticeCount > 0)
|
if (rec.NoticeCount > 0)
|
||||||
{
|
{
|
||||||
timelineRecord.NoticeCount = rec.NoticeCount;
|
timelineRecord.NoticeCount = rec.NoticeCount;
|
||||||
}
|
}
|
||||||
@@ -770,7 +797,7 @@ namespace GitHub.Runner.Common
|
|||||||
foreach (var record in mergedRecords)
|
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}");
|
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 && record.Issues.Count > 0)
|
if (record.Issues != null)
|
||||||
{
|
{
|
||||||
foreach (var issue in record.Issues)
|
foreach (var issue in record.Issues)
|
||||||
{
|
{
|
||||||
@@ -780,7 +807,7 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.Variables != null && record.Variables.Count > 0)
|
if (record.Variables != null)
|
||||||
{
|
{
|
||||||
foreach (var variable in record.Variables)
|
foreach (var variable in record.Variables)
|
||||||
{
|
{
|
||||||
@@ -797,27 +824,30 @@ namespace GitHub.Runner.Common
|
|||||||
bool uploadSucceed = false;
|
bool uploadSucceed = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (String.Equals(file.Type, CoreAttachmentType.Log, StringComparison.OrdinalIgnoreCase))
|
if (!_resultsServiceOnly)
|
||||||
{
|
{
|
||||||
// Create the log
|
if (String.Equals(file.Type, CoreAttachmentType.Log, StringComparison.OrdinalIgnoreCase))
|
||||||
var taskLog = await _jobServer.CreateLogAsync(_scopeIdentifier, _hubName, _planId, new TaskLog(String.Format(@"logs\{0:D}", file.TimelineRecordId)), default(CancellationToken));
|
|
||||||
|
|
||||||
// Upload the contents
|
|
||||||
using (FileStream fs = File.Open(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
|
||||||
{
|
{
|
||||||
var logUploaded = await _jobServer.AppendLogContentAsync(_scopeIdentifier, _hubName, _planId, taskLog.Id, fs, default(CancellationToken));
|
// Create the log
|
||||||
|
var taskLog = await _jobServer.CreateLogAsync(_scopeIdentifier, _hubName, _planId, new TaskLog(String.Format(@"logs\{0:D}", file.TimelineRecordId)), default(CancellationToken));
|
||||||
|
|
||||||
|
// Upload the contents
|
||||||
|
using (FileStream fs = File.Open(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
{
|
||||||
|
var logUploaded = await _jobServer.AppendLogContentAsync(_scopeIdentifier, _hubName, _planId, taskLog.Id, fs, default(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new record and only set the Log field
|
||||||
|
var attachmentUpdataRecord = new TimelineRecord() { Id = file.TimelineRecordId, Log = taskLog };
|
||||||
|
QueueTimelineRecordUpdate(file.TimelineId, attachmentUpdataRecord);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Create a new record and only set the Log field
|
|
||||||
var attachmentUpdataRecord = new TimelineRecord() { Id = file.TimelineRecordId, Log = taskLog };
|
|
||||||
QueueTimelineRecordUpdate(file.TimelineId, attachmentUpdataRecord);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create attachment
|
|
||||||
using (FileStream fs = File.Open(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
|
||||||
{
|
{
|
||||||
var result = await _jobServer.CreateAttachmentAsync(_scopeIdentifier, _hubName, _planId, file.TimelineId, file.TimelineRecordId, file.Type, file.Name, fs, default(CancellationToken));
|
// Create attachment
|
||||||
|
using (FileStream fs = File.Open(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
{
|
||||||
|
var result = await _jobServer.CreateAttachmentAsync(_scopeIdentifier, _hubName, _planId, file.TimelineId, file.TimelineRecordId, file.Type, file.Name, fs, default(CancellationToken));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
|
|||||||
42
src/Runner.Common/LaunchServer.cs
Normal file
42
src/Runner.Common/LaunchServer.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Security;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.Results.Client;
|
using GitHub.Services.Results.Client;
|
||||||
|
using GitHub.Services.WebApi.Utilities.Internal;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
[ServiceLocator(Default = typeof(ResultServer))]
|
[ServiceLocator(Default = typeof(ResultServer))]
|
||||||
public interface IResultsServer : IRunnerService
|
public interface IResultsServer : IRunnerService, IAsyncDisposable
|
||||||
{
|
{
|
||||||
void InitializeResultsClient(Uri uri, string token);
|
void InitializeResultsClient(Uri uri, string liveConsoleFeedUrl, string token);
|
||||||
|
|
||||||
|
Task<bool> AppendLiveConsoleFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, long? startLine, CancellationToken cancellationToken);
|
||||||
|
|
||||||
// logging and console
|
// logging and console
|
||||||
Task CreateResultsStepSummaryAsync(string planId, string jobId, Guid stepId, string file,
|
Task CreateResultsStepSummaryAsync(string planId, string jobId, Guid stepId, string file,
|
||||||
@@ -31,10 +40,26 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
private ResultsHttpClient _resultsClient;
|
private ResultsHttpClient _resultsClient;
|
||||||
|
|
||||||
public void InitializeResultsClient(Uri uri, string token)
|
private ClientWebSocket _websocketClient;
|
||||||
|
private DateTime? _lastConnectionFailure;
|
||||||
|
|
||||||
|
private static readonly TimeSpan MinDelayForWebsocketReconnect = TimeSpan.FromMilliseconds(100);
|
||||||
|
private static readonly TimeSpan MaxDelayForWebsocketReconnect = TimeSpan.FromMilliseconds(500);
|
||||||
|
|
||||||
|
private Task _websocketConnectTask;
|
||||||
|
private String _liveConsoleFeedUrl;
|
||||||
|
private string _token;
|
||||||
|
|
||||||
|
public void InitializeResultsClient(Uri uri, string liveConsoleFeedUrl, string token)
|
||||||
{
|
{
|
||||||
var httpMessageHandler = HostContext.CreateHttpClientHandler();
|
var httpMessageHandler = HostContext.CreateHttpClientHandler();
|
||||||
this._resultsClient = new ResultsHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
|
this._resultsClient = new ResultsHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
|
||||||
|
_token = token;
|
||||||
|
if (!string.IsNullOrEmpty(liveConsoleFeedUrl))
|
||||||
|
{
|
||||||
|
_liveConsoleFeedUrl = liveConsoleFeedUrl;
|
||||||
|
InitializeWebsocketClient(liveConsoleFeedUrl, token, TimeSpan.Zero, retryConnection: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task CreateResultsStepSummaryAsync(string planId, string jobId, Guid stepId, string file,
|
public Task CreateResultsStepSummaryAsync(string planId, string jobId, Guid stepId, string file,
|
||||||
@@ -94,5 +119,144 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
throw new InvalidOperationException("Results client is not initialized.");
|
throw new InvalidOperationException("Results client is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
CloseWebSocket(WebSocketCloseStatus.NormalClosure, CancellationToken.None);
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeWebsocketClient(string liveConsoleFeedUrl, string accessToken, TimeSpan delay, bool retryConnection = false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(accessToken))
|
||||||
|
{
|
||||||
|
Trace.Info($"No access token from server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(liveConsoleFeedUrl))
|
||||||
|
{
|
||||||
|
Trace.Info($"No live console feed url from server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace.Info($"Creating websocket client ..." + liveConsoleFeedUrl);
|
||||||
|
this._websocketClient = new ClientWebSocket();
|
||||||
|
this._websocketClient.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}");
|
||||||
|
var userAgentValues = new List<ProductInfoHeaderValue>();
|
||||||
|
userAgentValues.AddRange(UserAgentUtility.GetDefaultRestUserAgent());
|
||||||
|
userAgentValues.AddRange(HostContext.UserAgents);
|
||||||
|
this._websocketClient.Options.SetRequestHeader("User-Agent", string.Join(" ", userAgentValues.Select(x => x.ToString())));
|
||||||
|
|
||||||
|
// during initialization, retry upto 3 times to setup connection
|
||||||
|
this._websocketConnectTask = ConnectWebSocketClient(liveConsoleFeedUrl, delay, retryConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConnectWebSocketClient(string feedStreamUrl, TimeSpan delay, bool retryConnection = false)
|
||||||
|
{
|
||||||
|
bool connected = false;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trace.Info($"Attempting to start websocket client with delay {delay}.");
|
||||||
|
await Task.Delay(delay);
|
||||||
|
using var connectTimeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
await this._websocketClient.ConnectAsync(new Uri(feedStreamUrl), connectTimeoutTokenSource.Token);
|
||||||
|
Trace.Info($"Successfully started websocket client.");
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Info("Exception caught during websocket client connect, retry connection.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
retries++;
|
||||||
|
this._websocketClient = null;
|
||||||
|
_lastConnectionFailure = DateTime.Now;
|
||||||
|
}
|
||||||
|
} while (retryConnection && !connected && retries < 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AppendLiveConsoleFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, long? startLine, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_websocketConnectTask != null)
|
||||||
|
{
|
||||||
|
await _websocketConnectTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delivered = false;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
|
// "_websocketClient != null" implies either: We have a successful connection OR we have to attempt sending again and then reconnect
|
||||||
|
// ...in other words, if websocket client is null, we will skip sending to websocket
|
||||||
|
if (_websocketClient != null)
|
||||||
|
{
|
||||||
|
var linesWrapper = startLine.HasValue
|
||||||
|
? new TimelineRecordFeedLinesWrapper(stepId, lines, startLine.Value)
|
||||||
|
: new TimelineRecordFeedLinesWrapper(stepId, lines);
|
||||||
|
var jsonData = StringUtil.ConvertToJson(linesWrapper);
|
||||||
|
var jsonDataBytes = Encoding.UTF8.GetBytes(jsonData);
|
||||||
|
// break the message into chunks of 1024 bytes
|
||||||
|
for (var i = 0; i < jsonDataBytes.Length; i += 1 * 1024)
|
||||||
|
{
|
||||||
|
var lastChunk = i + (1 * 1024) >= jsonDataBytes.Length;
|
||||||
|
var chunk = new ArraySegment<byte>(jsonDataBytes, i, Math.Min(1 * 1024, jsonDataBytes.Length - i));
|
||||||
|
|
||||||
|
delivered = false;
|
||||||
|
while (!delivered && retries < 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_websocketClient != null)
|
||||||
|
{
|
||||||
|
await _websocketClient.SendAsync(chunk, WebSocketMessageType.Text, endOfMessage: lastChunk, cancellationToken);
|
||||||
|
delivered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
retries++;
|
||||||
|
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delivered)
|
||||||
|
{
|
||||||
|
// Giving up for now, so next invocation of this method won't attempt to reconnect
|
||||||
|
_websocketClient = null;
|
||||||
|
|
||||||
|
// however if 10 minutes have already passed, let's try reestablish connection again
|
||||||
|
if (_lastConnectionFailure.HasValue && DateTime.Now > _lastConnectionFailure.Value.AddMinutes(10))
|
||||||
|
{
|
||||||
|
// Some minutes passed since we retried last time, try connection again
|
||||||
|
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return delivered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseWebSocket(WebSocketCloseStatus closeStatus, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_websocketClient?.CloseOutputAsync(closeStatus, "Closing websocket", cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception websocketEx)
|
||||||
|
{
|
||||||
|
// In some cases this might be okay since the websocket might be open yet, so just close and don't trace exceptions
|
||||||
|
Trace.Info($"Failed to close websocket gracefully {websocketEx.GetType().Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -19,7 +19,15 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
||||||
|
|
||||||
Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken token);
|
Task CompleteJobAsync(
|
||||||
|
Guid planId,
|
||||||
|
Guid jobId,
|
||||||
|
TaskResult result,
|
||||||
|
Dictionary<String, VariableValue> outputs,
|
||||||
|
IList<StepResult> stepResults,
|
||||||
|
IList<Annotation> jobAnnotations,
|
||||||
|
string environmentUrl,
|
||||||
|
CancellationToken token);
|
||||||
|
|
||||||
Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken token);
|
Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken token);
|
||||||
}
|
}
|
||||||
@@ -51,34 +59,31 @@ namespace GitHub.Runner.Common
|
|||||||
public Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken cancellationToken)
|
public Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
var jobMessage = RetryRequest<AgentJobRequestMessage>(
|
return RetryRequest<AgentJobRequestMessage>(
|
||||||
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, cancellationToken), cancellationToken);
|
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, cancellationToken), cancellationToken,
|
||||||
if (jobMessage == null)
|
shouldRetry: ex => ex is not TaskOrchestrationJobAlreadyAcquiredException);
|
||||||
{
|
|
||||||
throw new TaskOrchestrationJobNotFoundException(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jobMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken cancellationToken)
|
public Task CompleteJobAsync(
|
||||||
|
Guid planId,
|
||||||
|
Guid jobId,
|
||||||
|
TaskResult result,
|
||||||
|
Dictionary<String, VariableValue> outputs,
|
||||||
|
IList<StepResult> stepResults,
|
||||||
|
IList<Annotation> jobAnnotations,
|
||||||
|
string environmentUrl,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
return RetryRequest(
|
return RetryRequest(
|
||||||
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, cancellationToken), cancellationToken);
|
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, jobAnnotations, environmentUrl, cancellationToken), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken cancellationToken)
|
public Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
var renewJobResponse = RetryRequest<RenewJobResponse>(
|
return RetryRequest<RenewJobResponse>(
|
||||||
async () => await _runServiceHttpClient.RenewJobAsync(requestUri, planId, jobId, cancellationToken), cancellationToken);
|
async () => await _runServiceHttpClient.RenewJobAsync(requestUri, planId, jobId, cancellationToken), cancellationToken);
|
||||||
if (renewJobResponse == null)
|
|
||||||
{
|
|
||||||
throw new TaskOrchestrationJobNotFoundException(jobId.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return renewJobResponse;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|||||||
@@ -80,10 +80,11 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxRetryAttemptsCount);
|
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxRetryAttemptsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
|
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
int maxRetryAttemptsCount = 5
|
int maxRetryAttemptsCount = 5,
|
||||||
|
Func<Exception, bool> shouldRetry = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var retryCount = 0;
|
var retryCount = 0;
|
||||||
@@ -96,7 +97,7 @@ namespace GitHub.Runner.Common
|
|||||||
return await func();
|
return await func();
|
||||||
}
|
}
|
||||||
// TODO: Add handling of non-retriable exceptions: https://github.com/github/actions-broker/issues/122
|
// TODO: Add handling of non-retriable exceptions: https://github.com/github/actions-broker/issues/122
|
||||||
catch (Exception ex) when (retryCount < maxRetryAttemptsCount)
|
catch (Exception ex) when (retryCount < maxRetryAttemptsCount && (shouldRetry == null || shouldRetry(ex)))
|
||||||
{
|
{
|
||||||
Trace.Error("Catch exception during request");
|
Trace.Error("Catch exception during request");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -93,4 +93,4 @@ namespace GitHub.Runner.Common
|
|||||||
IndentLevel--;
|
IndentLevel--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using GitHub.DistributedTask.Logging;
|
using GitHub.DistributedTask.Logging;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Represents absence of value.
|
// Represents absence of value.
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
public readonly struct Unit
|
public readonly struct Unit
|
||||||
{
|
{
|
||||||
public static readonly Unit Value = default;
|
public static readonly Unit Value = default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
public static class EnumUtil
|
public static class EnumUtil
|
||||||
{
|
{
|
||||||
public static T? TryParse<T>(string value) where T: struct
|
public static T? TryParse<T>(string value) where T : struct
|
||||||
{
|
{
|
||||||
T val;
|
T val;
|
||||||
if (Enum.TryParse(value ?? string.Empty, ignoreCase: true, result: out val))
|
if (Enum.TryParse(value ?? string.Empty, ignoreCase: true, result: out val))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GitHub.Runner.Common.Util
|
namespace GitHub.Runner.Common.Util
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common.Util
|
namespace GitHub.Runner.Common.Util
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -90,4 +90,4 @@ namespace GitHub.Runner.Listener.Check
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -415,4 +415,4 @@ namespace GitHub.Runner.Listener.Check
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
|
|
||||||
@@ -27,4 +27,4 @@ namespace GitHub.Runner.Listener.Check
|
|||||||
|
|
||||||
public List<string> Logs { get; set; }
|
public List<string> Logs { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -56,4 +56,4 @@ namespace GitHub.Runner.Listener.Check
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Listener.Configuration;
|
using GitHub.Runner.Listener.Configuration;
|
||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
@@ -29,8 +29,8 @@ namespace GitHub.Runner.Listener
|
|||||||
private readonly Dictionary<string, string[]> validOptions = new()
|
private readonly Dictionary<string, string[]> validOptions = new()
|
||||||
{
|
{
|
||||||
// Valid configure flags and args
|
// Valid configure flags and args
|
||||||
[Constants.Runner.CommandLine.Commands.Configure] =
|
[Constants.Runner.CommandLine.Commands.Configure] =
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
Constants.Runner.CommandLine.Flags.DisableUpdate,
|
Constants.Runner.CommandLine.Flags.DisableUpdate,
|
||||||
Constants.Runner.CommandLine.Flags.Ephemeral,
|
Constants.Runner.CommandLine.Flags.Ephemeral,
|
||||||
@@ -38,6 +38,7 @@ namespace GitHub.Runner.Listener
|
|||||||
Constants.Runner.CommandLine.Flags.Replace,
|
Constants.Runner.CommandLine.Flags.Replace,
|
||||||
Constants.Runner.CommandLine.Flags.RunAsService,
|
Constants.Runner.CommandLine.Flags.RunAsService,
|
||||||
Constants.Runner.CommandLine.Flags.Unattended,
|
Constants.Runner.CommandLine.Flags.Unattended,
|
||||||
|
Constants.Runner.CommandLine.Flags.NoDefaultLabels,
|
||||||
Constants.Runner.CommandLine.Args.Auth,
|
Constants.Runner.CommandLine.Args.Auth,
|
||||||
Constants.Runner.CommandLine.Args.Labels,
|
Constants.Runner.CommandLine.Args.Labels,
|
||||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||||
@@ -85,6 +86,7 @@ namespace GitHub.Runner.Listener
|
|||||||
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
||||||
public bool GenerateServiceConfig => TestFlag(Constants.Runner.CommandLine.Flags.GenerateServiceConfig);
|
public bool GenerateServiceConfig => TestFlag(Constants.Runner.CommandLine.Flags.GenerateServiceConfig);
|
||||||
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
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 Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
||||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||||
public bool RemoveLocalConfig => TestFlag(Constants.Runner.CommandLine.Flags.Local);
|
public bool RemoveLocalConfig => TestFlag(Constants.Runner.CommandLine.Flags.Local);
|
||||||
@@ -182,7 +184,7 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
command = Constants.Runner.CommandLine.Commands.Warmup;
|
command = Constants.Runner.CommandLine.Commands.Warmup;
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -137,7 +137,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
||||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
runnerSettings.ServerUrl = authResult.TenantUrl;
|
||||||
runnerSettings.UseV2Flow = authResult.UseV2Flow;
|
runnerSettings.UseV2Flow = authResult.UseV2Flow;
|
||||||
_term.WriteLine($"Using V2 flow: {runnerSettings.UseV2Flow}");
|
Trace.Info($"Using V2 flow: {runnerSettings.UseV2Flow}");
|
||||||
creds = authResult.ToVssCredentials();
|
creds = authResult.ToVssCredentials();
|
||||||
Trace.Info("cred retrieved via GitHub auth");
|
Trace.Info("cred retrieved via GitHub auth");
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
if (command.GetReplace())
|
if (command.GetReplace())
|
||||||
{
|
{
|
||||||
// Update existing agent with new PublicKey, agent version.
|
// Update existing agent with new PublicKey, agent version.
|
||||||
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate);
|
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate, command.NoDefaultLabels);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -293,7 +293,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a new agent.
|
// Create a new agent.
|
||||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate);
|
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral, command.DisableUpdate, command.NoDefaultLabels);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -554,7 +554,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate)
|
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate, bool noDefaultLabels)
|
||||||
{
|
{
|
||||||
ArgUtil.NotNull(agent, nameof(agent));
|
ArgUtil.NotNull(agent, nameof(agent));
|
||||||
agent.Authorization = new TaskAgentAuthorization
|
agent.Authorization = new TaskAgentAuthorization
|
||||||
@@ -571,9 +571,16 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
agent.Labels.Clear();
|
agent.Labels.Clear();
|
||||||
|
|
||||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
if (!noDefaultLabels)
|
||||||
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
|
{
|
||||||
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var userLabel in userLabels)
|
foreach (var userLabel in userLabels)
|
||||||
{
|
{
|
||||||
@@ -583,7 +590,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate)
|
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral, bool disableUpdate, bool noDefaultLabels)
|
||||||
{
|
{
|
||||||
TaskAgent agent = new(agentName)
|
TaskAgent agent = new(agentName)
|
||||||
{
|
{
|
||||||
@@ -598,9 +605,16 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
DisableUpdate = disableUpdate
|
DisableUpdate = disableUpdate
|
||||||
};
|
};
|
||||||
|
|
||||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
if (!noDefaultLabels)
|
||||||
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
|
{
|
||||||
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var userLabel in userLabels)
|
foreach (var userLabel in userLabels)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
#pragma warning disable CA1416
|
#pragma warning disable CA1416
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if OS_LINUX || OS_OSX
|
#if OS_LINUX || OS_OSX
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
@@ -68,7 +68,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
// Lets add a suffix with a random number to reduce the chance of collisions between runner names once we truncate
|
// Lets add a suffix with a random number to reduce the chance of collisions between runner names once we truncate
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
var num = random.Next(1000, 9999).ToString();
|
var num = random.Next(1000, 9999).ToString();
|
||||||
runnerNameSubstring +=$"-{num}";
|
runnerNameSubstring += $"-{num}";
|
||||||
serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgNameSubstring, runnerNameSubstring);
|
serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgNameSubstring, runnerNameSubstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,12 +76,12 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
Trace.Info($"Service name '{serviceName}' display name '{serviceDisplayName}' will be used for service configuration.");
|
Trace.Info($"Service name '{serviceName}' display name '{serviceDisplayName}' will be used for service configuration.");
|
||||||
}
|
}
|
||||||
#if (OS_LINUX || OS_OSX)
|
#if (OS_LINUX || OS_OSX)
|
||||||
const int MaxServiceNameLength = 150;
|
const int MaxServiceNameLength = 150;
|
||||||
const int MaxRepoOrgCharacters = 70;
|
const int MaxRepoOrgCharacters = 70;
|
||||||
#elif OS_WINDOWS
|
#elif OS_WINDOWS
|
||||||
const int MaxServiceNameLength = 80;
|
const int MaxServiceNameLength = 80;
|
||||||
const int MaxRepoOrgCharacters = 45;
|
const int MaxRepoOrgCharacters = 45;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if OS_LINUX
|
#if OS_LINUX
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
#pragma warning disable CA1416
|
#pragma warning disable CA1416
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Test")]
|
[assembly: InternalsVisibleTo("Test")]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -15,6 +15,7 @@ using GitHub.Runner.Sdk;
|
|||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using GitHub.Services.WebApi.Jwt;
|
using GitHub.Services.WebApi.Jwt;
|
||||||
|
using Sdk.RSWebApi.Contracts;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
namespace GitHub.Runner.Listener
|
namespace GitHub.Runner.Listener
|
||||||
@@ -372,6 +373,8 @@ namespace GitHub.Runner.Listener
|
|||||||
TaskCompletionSource<int> firstJobRequestRenewed = new();
|
TaskCompletionSource<int> firstJobRequestRenewed = new();
|
||||||
var notification = HostContext.GetService<IJobNotification>();
|
var notification = HostContext.GetService<IJobNotification>();
|
||||||
|
|
||||||
|
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
// lock renew cancellation token.
|
// lock renew cancellation token.
|
||||||
using (var lockRenewalTokenSource = new CancellationTokenSource())
|
using (var lockRenewalTokenSource = new CancellationTokenSource())
|
||||||
using (var workerProcessCancelTokenSource = new CancellationTokenSource())
|
using (var workerProcessCancelTokenSource = new CancellationTokenSource())
|
||||||
@@ -379,8 +382,6 @@ namespace GitHub.Runner.Listener
|
|||||||
long requestId = message.RequestId;
|
long requestId = message.RequestId;
|
||||||
Guid lockToken = Guid.Empty; // lockToken has never been used, keep this here of compat
|
Guid lockToken = Guid.Empty; // lockToken has never been used, keep this here of compat
|
||||||
|
|
||||||
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
// start renew job request
|
// start renew job request
|
||||||
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
|
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
|
||||||
Task renewJobRequest = RenewJobRequestAsync(message, systemConnection, _poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
Task renewJobRequest = RenewJobRequestAsync(message, systemConnection, _poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
||||||
@@ -405,7 +406,7 @@ namespace GitHub.Runner.Listener
|
|||||||
await renewJobRequest;
|
await renewJobRequest;
|
||||||
|
|
||||||
// complete job request with result Cancelled
|
// complete job request with result Cancelled
|
||||||
await CompleteJobRequestAsync(_poolId, message, lockToken, TaskResult.Canceled);
|
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,7 +545,6 @@ namespace GitHub.Runner.Listener
|
|||||||
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
||||||
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
||||||
|
|
||||||
|
|
||||||
var jobServer = await InitializeJobServerAsync(systemConnection);
|
var jobServer = await InitializeJobServerAsync(systemConnection);
|
||||||
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
|
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ namespace GitHub.Runner.Listener
|
|||||||
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
|
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
Trace.Info($"Finish job with result 'Failed' due to IOException.");
|
Trace.Info($"Finish job with result 'Failed' due to IOException.");
|
||||||
await ForceFailJob(jobServer, message);
|
await ForceFailJob(jobServer, message, detailInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +567,7 @@ namespace GitHub.Runner.Listener
|
|||||||
await renewJobRequest;
|
await renewJobRequest;
|
||||||
|
|
||||||
// complete job request
|
// complete job request
|
||||||
await CompleteJobRequestAsync(_poolId, message, lockToken, result, detailInfo);
|
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, result, detailInfo);
|
||||||
|
|
||||||
// print out unhandled exception happened in worker after we complete job request.
|
// print out unhandled exception happened in worker after we complete job request.
|
||||||
// when we run out of disk space, report back to server has higher priority.
|
// when we run out of disk space, report back to server has higher priority.
|
||||||
@@ -664,7 +664,7 @@ namespace GitHub.Runner.Listener
|
|||||||
await renewJobRequest;
|
await renewJobRequest;
|
||||||
|
|
||||||
// complete job request
|
// complete job request
|
||||||
await CompleteJobRequestAsync(_poolId, message, lockToken, resultOnAbandonOrCancel);
|
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -1065,7 +1065,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CompleteJobRequestAsync(int poolId, Pipelines.AgentJobRequestMessage message, Guid lockToken, TaskResult result, string detailInfo = null)
|
private async Task CompleteJobRequestAsync(int poolId, Pipelines.AgentJobRequestMessage message, ServiceEndpoint systemConnection, Guid lockToken, TaskResult result, string detailInfo = null)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -1077,7 +1077,7 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
if (this._isRunServiceJob)
|
if (this._isRunServiceJob)
|
||||||
{
|
{
|
||||||
Trace.Verbose($"Skip FinishAgentRequest call from Listener because MessageType is {message.MessageType}");
|
Trace.Verbose($"Skip CompleteJobRequestAsync call from Listener because it's RunService job");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1117,7 +1117,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log an error issue to job level timeline record
|
// log an error issue to job level timeline record
|
||||||
private async Task LogWorkerProcessUnhandledException(IRunnerService server, Pipelines.AgentJobRequestMessage message, string errorMessage)
|
private async Task LogWorkerProcessUnhandledException(IRunnerService server, Pipelines.AgentJobRequestMessage message, string detailInfo)
|
||||||
{
|
{
|
||||||
if (server is IJobServer jobServer)
|
if (server is IJobServer jobServer)
|
||||||
{
|
{
|
||||||
@@ -1129,34 +1129,11 @@ namespace GitHub.Runner.Listener
|
|||||||
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
|
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
|
||||||
ArgUtil.NotNull(jobRecord, nameof(jobRecord));
|
ArgUtil.NotNull(jobRecord, nameof(jobRecord));
|
||||||
|
|
||||||
try
|
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(errorMessage) &&
|
|
||||||
message.Variables.TryGetValue("DistributedTask.EnableRunnerIPCDebug", out var enableRunnerIPCDebug) &&
|
|
||||||
StringUtil.ConvertToBoolean(enableRunnerIPCDebug.Value))
|
|
||||||
{
|
|
||||||
// the trace should be best effort and not affect any job result
|
|
||||||
var match = _invalidJsonRegex.Match(errorMessage);
|
|
||||||
if (match.Success &&
|
|
||||||
match.Groups.Count == 2)
|
|
||||||
{
|
|
||||||
var jsonPosition = int.Parse(match.Groups[1].Value);
|
|
||||||
var serializedJobMessage = JsonUtility.ToString(message);
|
|
||||||
var originalJson = serializedJobMessage.Substring(jsonPosition - 10, 20);
|
|
||||||
errorMessage = $"Runner sent Json at position '{jsonPosition}': {originalJson} ({Convert.ToBase64String(Encoding.UTF8.GetBytes(originalJson))})\n{errorMessage}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Error(ex);
|
|
||||||
errorMessage = $"Fail to check json IPC error: {ex.Message}\n{errorMessage}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage };
|
|
||||||
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||||
jobRecord.ErrorCount++;
|
jobRecord.ErrorCount++;
|
||||||
jobRecord.Issues.Add(unhandledExceptionIssue);
|
jobRecord.Issues.Add(unhandledExceptionIssue);
|
||||||
|
|
||||||
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1167,13 +1144,13 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Trace.Info("Job server does not support handling unhandled exception yet, error message: {0}", errorMessage);
|
Trace.Info("Job server does not support handling unhandled exception yet, error message: {0}", detailInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// raise job completed event to fail the job.
|
// raise job completed event to fail the job.
|
||||||
private async Task ForceFailJob(IRunnerService server, Pipelines.AgentJobRequestMessage message)
|
private async Task ForceFailJob(IRunnerService server, Pipelines.AgentJobRequestMessage message, string detailInfo)
|
||||||
{
|
{
|
||||||
if (server is IJobServer jobServer)
|
if (server is IJobServer jobServer)
|
||||||
{
|
{
|
||||||
@@ -1192,7 +1169,15 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, TaskResult.Failed, outputs: null, stepResults: null, CancellationToken.None);
|
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
||||||
|
var unhandledAnnotation = unhandledExceptionIssue.ToAnnotation();
|
||||||
|
var jobAnnotations = new List<Annotation>();
|
||||||
|
if (unhandledAnnotation.HasValue)
|
||||||
|
{
|
||||||
|
jobAnnotations.Add(unhandledAnnotation.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, TaskResult.Failed, outputs: null, stepResults: null, jobAnnotations: jobAnnotations, environmentUrl: null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -549,7 +549,17 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
var runServer = HostContext.CreateService<IRunServer>();
|
var runServer = HostContext.CreateService<IRunServer>();
|
||||||
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
|
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
|
||||||
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
|
try
|
||||||
|
{
|
||||||
|
jobRequestMessage =
|
||||||
|
await runServer.GetJobMessageAsync(messageRef.RunnerRequestId,
|
||||||
|
messageQueueLoopTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (TaskOrchestrationJobAlreadyAcquiredException)
|
||||||
|
{
|
||||||
|
Trace.Info("Job is already acquired, skip this message.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobDispatcher.Run(jobRequestMessage, runOnce);
|
jobDispatcher.Run(jobRequestMessage, runOnce);
|
||||||
@@ -673,7 +683,8 @@ Config Options:
|
|||||||
--token string Registration token. Required if unattended
|
--token string Registration token. Required if unattended
|
||||||
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
|
--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)
|
--runnergroup string Name of the runner group to add this runner to (defaults to the default runner group)
|
||||||
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
|
--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}'
|
||||||
--local Removes the runner config files from your local machine. Used as an option to the remove command
|
--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})
|
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
|
||||||
--replace Replace any existing runner with the same name (default false)
|
--replace Replace any existing runner with the same name (default false)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace GitHub.Runner.Listener
|
namespace GitHub.Runner.Listener
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|||||||
@@ -682,4 +682,4 @@ namespace GitHub.Runner.Plugins.Artifact
|
|||||||
: base(message, inner)
|
: base(message, inner)
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Actions.Pipelines.WebApi;
|
using GitHub.Actions.Pipelines.WebApi;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
@@ -36,7 +36,7 @@ namespace GitHub.Runner.Plugins.Artifact
|
|||||||
|
|
||||||
return await _pipelinesHttpClient.CreateArtifactAsync(
|
return await _pipelinesHttpClient.CreateArtifactAsync(
|
||||||
parameters,
|
parameters,
|
||||||
pipelineId,
|
pipelineId,
|
||||||
runId,
|
runId,
|
||||||
cancellationToken: cancellationToken) as Pipelines.ActionsStorageArtifact;
|
cancellationToken: cancellationToken) as Pipelines.ActionsStorageArtifact;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -97,8 +97,8 @@ namespace GitHub.Runner.Plugins.Artifact
|
|||||||
size,
|
size,
|
||||||
token);
|
token);
|
||||||
|
|
||||||
context.Output($"Associated artifact {artifactName} ({artifact.ContainerId}) with run #{buildId}");
|
context.Output($"Associated artifact {artifactName} ({artifact.ContainerId}) with run #{buildId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
@@ -220,20 +220,12 @@ namespace GitHub.Runner.Sdk
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, string> _commandEscapeMappings = new(StringComparer.OrdinalIgnoreCase)
|
private Dictionary<string, string> _commandEscapeMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{
|
{ ";", "%3B" },
|
||||||
";", "%3B"
|
{ "\r", "%0D" },
|
||||||
},
|
{ "\n", "%0A" },
|
||||||
{
|
{ "]", "%5D" },
|
||||||
"\r", "%0D"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"\n", "%0A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"]", "%5D"
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GitHub.Runner.Sdk
|
namespace GitHub.Runner.Sdk
|
||||||
{
|
{
|
||||||
/***
|
/***
|
||||||
* WARNING: This file is automatically regenerated on layout so the runner can provide version/commit info (do not manually edit it).
|
* WARNING: This file is automatically regenerated on layout so the runner can provide version/commit info (do not manually edit it).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -69,6 +69,10 @@ namespace GitHub.Runner.Sdk
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(httpProxyAddress) && !Uri.TryCreate(httpProxyAddress, UriKind.Absolute, out var _))
|
||||||
|
{
|
||||||
|
httpProxyAddress = PrependHttpIfMissing(httpProxyAddress);
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(httpProxyAddress) && Uri.TryCreate(httpProxyAddress, UriKind.Absolute, out var proxyHttpUri))
|
if (!string.IsNullOrEmpty(httpProxyAddress) && Uri.TryCreate(httpProxyAddress, UriKind.Absolute, out var proxyHttpUri))
|
||||||
{
|
{
|
||||||
_httpProxyAddress = proxyHttpUri.OriginalString;
|
_httpProxyAddress = proxyHttpUri.OriginalString;
|
||||||
@@ -99,6 +103,10 @@ namespace GitHub.Runner.Sdk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(httpsProxyAddress) && !Uri.TryCreate(httpsProxyAddress, UriKind.Absolute, out var _))
|
||||||
|
{
|
||||||
|
httpsProxyAddress = PrependHttpIfMissing(httpsProxyAddress);
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(httpsProxyAddress) && Uri.TryCreate(httpsProxyAddress, UriKind.Absolute, out var proxyHttpsUri))
|
if (!string.IsNullOrEmpty(httpsProxyAddress) && Uri.TryCreate(httpsProxyAddress, UriKind.Absolute, out var proxyHttpsUri))
|
||||||
{
|
{
|
||||||
_httpsProxyAddress = proxyHttpsUri.OriginalString;
|
_httpsProxyAddress = proxyHttpsUri.OriginalString;
|
||||||
@@ -240,5 +248,20 @@ namespace GitHub.Runner.Sdk
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string PrependHttpIfMissing(string proxyAddress)
|
||||||
|
{
|
||||||
|
// much like in golang, see https://github.com/golang/net/blob/f5464ddb689c015d1abf4df78a806a54af977e6c/http/httpproxy/proxy.go#LL156C31-L156C31
|
||||||
|
if (!proxyAddress.StartsWith("http://", StringComparison.Ordinal) && !proxyAddress.StartsWith("https://", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var prependedProxyAddress = "http://" + proxyAddress;
|
||||||
|
if (Uri.TryCreate(prependedProxyAddress, UriKind.Absolute, out var _))
|
||||||
|
{
|
||||||
|
// if prepending http:// turns the proxyAddress into a valid Uri, then use that
|
||||||
|
return prependedProxyAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxyAddress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace GitHub.Runner.Sdk
|
namespace GitHub.Runner.Sdk
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace GitHub.Runner.Sdk
|
namespace GitHub.Runner.Sdk
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ namespace GitHub.Runner.Sdk
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
string.Equals(gitHubUrl.Host, "github.com", StringComparison.OrdinalIgnoreCase) ||
|
string.Equals(gitHubUrl.Host, "github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(gitHubUrl.Host, "www.github.com", StringComparison.OrdinalIgnoreCase) ||
|
string.Equals(gitHubUrl.Host, "www.github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase) ||
|
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
gitHubUrl.Host.EndsWith(".ghe.localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||||
gitHubUrl.Host.EndsWith(".ghe.com", StringComparison.OrdinalIgnoreCase);
|
gitHubUrl.Host.EndsWith(".ghe.com", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
|
|
||||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("USE_BROKER_FLOW")))
|
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("USE_BROKER_FLOW")))
|
||||||
{
|
{
|
||||||
settings.AllowAutoRedirect = true;
|
settings.AllowAutoRedirectForBroker = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove Invariant from the list of accepted languages.
|
// Remove Invariant from the list of accepted languages.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
trace?.Verbose(ex.ToString());
|
trace?.Verbose(ex.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches != null && matches.Length > 0)
|
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
|
||||||
{
|
{
|
||||||
trace?.Info($"Location: '{matches.First()}'");
|
trace?.Info($"Location: '{matches.First()}'");
|
||||||
return matches.First();
|
return matches.First();
|
||||||
@@ -86,7 +86,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
for (int i = 0; i < pathExtSegments.Length; i++)
|
for (int i = 0; i < pathExtSegments.Length; i++)
|
||||||
{
|
{
|
||||||
string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}");
|
string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}");
|
||||||
if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)))
|
if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)) && IsPathValid(fullPath, trace))
|
||||||
{
|
{
|
||||||
trace?.Info($"Location: '{fullPath}'");
|
trace?.Info($"Location: '{fullPath}'");
|
||||||
return fullPath;
|
return fullPath;
|
||||||
@@ -105,7 +105,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
trace?.Verbose(ex.ToString());
|
trace?.Verbose(ex.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches != null && matches.Length > 0)
|
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
|
||||||
{
|
{
|
||||||
trace?.Info($"Location: '{matches.First()}'");
|
trace?.Info($"Location: '{matches.First()}'");
|
||||||
return matches.First();
|
return matches.First();
|
||||||
@@ -128,5 +128,15 @@ namespace GitHub.Runner.Sdk
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks if the file is a symlink and if the symlink`s target exists.
|
||||||
|
private static bool IsPathValid(string path, ITraceWriter trace = null)
|
||||||
|
{
|
||||||
|
var fileInfo = new FileInfo(path);
|
||||||
|
var linkTargetFullPath = fileInfo.Directory?.FullName + Path.DirectorySeparatorChar + fileInfo.LinkTarget;
|
||||||
|
if (fileInfo.LinkTarget == null || File.Exists(linkTargetFullPath) || File.Exists(fileInfo.LinkTarget)) return true;
|
||||||
|
trace?.Info($"the target '{fileInfo.LinkTarget}' of the symbolic link '{path}', does not exist");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace RunnerService
|
|||||||
catch (Win32Exception ex)
|
catch (Win32Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine("[ERROR] Unable to create '{0}' event source under 'Application' event log.", RunnerService.EventSourceName);
|
Console.WriteLine("[ERROR] Unable to create '{0}' event source under 'Application' event log.", RunnerService.EventSourceName);
|
||||||
Console.WriteLine("[ERROR] {0}",ex.Message);
|
Console.WriteLine("[ERROR] {0}", ex.Message);
|
||||||
Console.WriteLine("[ERROR] Error Code: {0}", ex.ErrorCode);
|
Console.WriteLine("[ERROR] Error Code: {0}", ex.ErrorCode);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
|
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
|
||||||
};
|
};
|
||||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
|
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
|
||||||
context.AddIssue(issue);
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||||
};
|
};
|
||||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||||
context.AddIssue(issue);
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
|
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)
|
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||||
};
|
};
|
||||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||||
context.AddIssue(issue);
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
|
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
|
||||||
@@ -666,7 +666,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AddIssue(issue);
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)
|
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
@@ -11,12 +11,14 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using WebApi = GitHub.DistributedTask.WebApi;
|
using WebApi = GitHub.DistributedTask.WebApi;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||||
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
{
|
{
|
||||||
@@ -100,7 +102,19 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
|
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
|
||||||
executionContext.Output("Prepare all required actions");
|
executionContext.Output("Prepare all required actions");
|
||||||
var result = await PrepareActionsRecursiveAsync(executionContext, state, actions, depth, rootStepId);
|
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;
|
||||||
|
}
|
||||||
if (!FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
|
if (!FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
|
||||||
{
|
{
|
||||||
if (state.ImagesToPull.Count > 0)
|
if (state.ImagesToPull.Count > 0)
|
||||||
@@ -303,15 +317,28 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
|
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
|
||||||
{
|
{
|
||||||
Trace.Info("Load action that reference container from registry.");
|
if (FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
|
||||||
CachedActionContainers.TryGetValue(action.Id, out var container);
|
|
||||||
ArgUtil.NotNull(container, nameof(container));
|
|
||||||
definition.Data.Execution = new ContainerActionExecutionData()
|
|
||||||
{
|
{
|
||||||
Image = container.ContainerImage
|
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
|
||||||
|
};
|
||||||
|
|
||||||
Trace.Info($"Using action container image: {container.ContainerImage}.");
|
Trace.Info($"Using action container image: {container.ContainerImage}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository)
|
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository)
|
||||||
{
|
{
|
||||||
@@ -648,13 +675,21 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve download info
|
// Resolve download info
|
||||||
|
var launchServer = HostContext.GetService<ILaunchServer>();
|
||||||
var jobServer = HostContext.GetService<IJobServer>();
|
var jobServer = HostContext.GetService<IJobServer>();
|
||||||
var actionDownloadInfos = default(WebApi.ActionDownloadInfoCollection);
|
var actionDownloadInfos = default(WebApi.ActionDownloadInfoCollection);
|
||||||
for (var attempt = 1; attempt <= 3; attempt++)
|
for (var attempt = 1; attempt <= 3; attempt++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
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);
|
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);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is cancelled.
|
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is cancelled.
|
||||||
@@ -1007,7 +1042,7 @@ namespace GitHub.Runner.Worker
|
|||||||
if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Container)
|
if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Container)
|
||||||
{
|
{
|
||||||
var containerAction = actionDefinitionData.Execution as ContainerActionExecutionData;
|
var containerAction = actionDefinitionData.Execution as ContainerActionExecutionData;
|
||||||
if (containerAction.Image.EndsWith("Dockerfile") || containerAction.Image.EndsWith("dockerfile"))
|
if (DockerUtil.IsDockerfile(containerAction.Image))
|
||||||
{
|
{
|
||||||
var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image);
|
var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image);
|
||||||
executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'.");
|
executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'.");
|
||||||
@@ -1092,8 +1127,16 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var fullPath = IOUtil.ResolvePath(actionEntryDirectory, "."); // resolve full path without access filesystem.
|
var reference = repositoryReference.Name;
|
||||||
throw new InvalidOperationException($"Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '{fullPath}'. Did you forget to run actions/checkout before running your local action?");
|
if (!string.IsNullOrEmpty(repositoryReference.Path))
|
||||||
|
{
|
||||||
|
reference = $"{reference}/{repositoryReference.Path}";
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(repositoryReference.Ref))
|
||||||
|
{
|
||||||
|
reference = $"{reference}@{repositoryReference.Ref}";
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException($"Can't find 'action.yml', 'action.yaml' or 'Dockerfile' for action '{reference}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -448,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))
|
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(mainToken?.Value))
|
if (string.IsNullOrEmpty(mainToken?.Value))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
@@ -30,4 +30,4 @@ namespace GitHub.Runner.Worker
|
|||||||
return $"An action could not be found at the URI '{actionUri}'";
|
return $"An action could not be found at the URI '{actionUri}'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.ObjectTemplating;
|
using GitHub.DistributedTask.ObjectTemplating;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker.Container.ContainerHooks
|
|||||||
public HookContainer(ContainerInfo container)
|
public HookContainer(ContainerInfo container)
|
||||||
{
|
{
|
||||||
Image = container.ContainerImage;
|
Image = container.ContainerImage;
|
||||||
EntryPointArgs = container.ContainerEntryPointArgs?.Split(' ').Select(arg => arg.Trim()) ?? new List<string>();
|
EntryPointArgs = container.ContainerEntryPointArgs?.Split(' ').Select(arg => arg.Trim()).Where(arg => !string.IsNullOrEmpty(arg)) ?? new List<string>();
|
||||||
EntryPoint = container.ContainerEntryPoint;
|
EntryPoint = container.ContainerEntryPoint;
|
||||||
WorkingDirectory = container.ContainerWorkDirectory;
|
WorkingDirectory = container.ContainerWorkDirectory;
|
||||||
CreateOptions = container.ContainerCreateOptions;
|
CreateOptions = container.ContainerCreateOptions;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker.Container
|
namespace GitHub.Runner.Worker.Container
|
||||||
@@ -65,6 +66,16 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsDockerfile(string image)
|
||||||
|
{
|
||||||
|
if (image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var imageWithoutPath = image.Split('/').Last();
|
||||||
|
return imageWithoutPath.StartsWith("Dockerfile.", StringComparison.OrdinalIgnoreCase) || imageWithoutPath.EndsWith("Dockerfile", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public static string CreateEscapedOption(string flag, string key)
|
public static string CreateEscapedOption(string flag, string key)
|
||||||
{
|
{
|
||||||
if (String.IsNullOrEmpty(key))
|
if (String.IsNullOrEmpty(key))
|
||||||
@@ -96,7 +107,7 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks
|
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks
|
||||||
|
|
||||||
// First, find any \ followed by a " and double the number of \ + 1.
|
// First, find any \ followed by a " and double the number of \ + 1.
|
||||||
value = QuoteEscape.Replace(value, @"$1$1\" + "\"");
|
value = QuoteEscape.Replace(value, @"$1$1\" + "\"");
|
||||||
// Next, what if it ends in `\`, it would escape the end quote. So, we need to detect that at the end of the string and perform the same escape
|
// Next, what if it ends in `\`, it would escape the end quote. So, we need to detect that at the end of the string and perform the same escape
|
||||||
// Luckily, we can just use the $ character with detects the end of string in regex
|
// Luckily, we can just use the $ character with detects the end of string in regex
|
||||||
value = EndOfStringEscape.Replace(value, @"$1$1");
|
value = EndOfStringEscape.Replace(value, @"$1$1");
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
private static string DateTimeFormat = "yyyyMMdd-HHmmss";
|
private static string DateTimeFormat = "yyyyMMdd-HHmmss";
|
||||||
public void UploadDiagnosticLogs(IExecutionContext executionContext,
|
public void UploadDiagnosticLogs(IExecutionContext executionContext,
|
||||||
IExecutionContext parentContext,
|
IExecutionContext parentContext,
|
||||||
Pipelines.AgentJobRequestMessage message,
|
Pipelines.AgentJobRequestMessage message,
|
||||||
DateTime jobStartTimeUtc)
|
DateTime jobStartTimeUtc)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -18,15 +18,22 @@ using GitHub.Runner.Sdk;
|
|||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using GitHub.Runner.Worker.Handlers;
|
using GitHub.Runner.Worker.Handlers;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Sdk.RSWebApi.Contracts;
|
||||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
{
|
{
|
||||||
public class ExecutionContextType
|
public static class ExecutionContextType
|
||||||
{
|
{
|
||||||
public static string Job = "Job";
|
public const string Job = "Job";
|
||||||
public static string Task = "Task";
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ServiceLocator(Default = typeof(ExecutionContext))]
|
[ServiceLocator(Default = typeof(ExecutionContext))]
|
||||||
@@ -92,7 +99,7 @@ namespace GitHub.Runner.Worker
|
|||||||
void SetGitHubContext(string name, string value);
|
void SetGitHubContext(string name, string value);
|
||||||
void SetOutput(string name, string value, out string reference);
|
void SetOutput(string name, string value, out string reference);
|
||||||
void SetTimeout(TimeSpan? timeout);
|
void SetTimeout(TimeSpan? timeout);
|
||||||
void AddIssue(Issue issue, string message = null);
|
void AddIssue(Issue issue, ExecutionContextLogOptions logOptions);
|
||||||
void Progress(int percentage, string currentOperation = null);
|
void Progress(int percentage, string currentOperation = null);
|
||||||
void UpdateDetailTimelineRecord(TimelineRecord record);
|
void UpdateDetailTimelineRecord(TimelineRecord record);
|
||||||
|
|
||||||
@@ -118,7 +125,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||||
{
|
{
|
||||||
private const int _maxIssueCount = 10;
|
private const int _maxCountPerIssueType = 10;
|
||||||
private const int _throttlingDelayReportThreshold = 10 * 1000; // Don't report throttling with less than 10 seconds delay
|
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 _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
|
private const int _maxIssueCountInTelemetry = 3; // Only send the first 3 issues to telemetry
|
||||||
@@ -126,8 +133,10 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
private readonly TimelineRecord _record = new();
|
private readonly TimelineRecord _record = new();
|
||||||
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
||||||
|
private readonly List<Issue> _embeddedIssueCollector;
|
||||||
private readonly object _loggerLock = new();
|
private readonly object _loggerLock = new();
|
||||||
private readonly object _matchersLock = new();
|
private readonly object _matchersLock = new();
|
||||||
|
private readonly ExecutionContext _parentExecutionContext;
|
||||||
|
|
||||||
private event OnMatcherChanged _onMatcherChanged;
|
private event OnMatcherChanged _onMatcherChanged;
|
||||||
|
|
||||||
@@ -135,7 +144,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
private IPagingLogger _logger;
|
private IPagingLogger _logger;
|
||||||
private IJobServerQueue _jobServerQueue;
|
private IJobServerQueue _jobServerQueue;
|
||||||
private ExecutionContext _parentExecutionContext;
|
|
||||||
|
|
||||||
private Guid _mainTimelineId;
|
private Guid _mainTimelineId;
|
||||||
private Guid _detailTimelineId;
|
private Guid _detailTimelineId;
|
||||||
@@ -149,6 +157,29 @@ namespace GitHub.Runner.Worker
|
|||||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||||
private bool _stepTelemetryPublished = false;
|
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 Id => _record.Id;
|
||||||
public Guid EmbeddedId { get; private set; }
|
public Guid EmbeddedId { get; private set; }
|
||||||
public string ScopeName { get; private set; }
|
public string ScopeName { get; private set; }
|
||||||
@@ -161,7 +192,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
||||||
|
|
||||||
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
||||||
public ActionsStepTelemetry StepTelemetry { get; } = new ActionsStepTelemetry();
|
public ActionsStepTelemetry StepTelemetry { get; private init; }
|
||||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||||
|
|
||||||
@@ -186,7 +217,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
// An embedded execution context shares the same record ID, record name, and logger
|
// An embedded execution context shares the same record ID, record name, and logger
|
||||||
// as its enclosing execution context.
|
// as its enclosing execution context.
|
||||||
public bool IsEmbedded { get; private set; }
|
public bool IsEmbedded { get; private init; }
|
||||||
|
|
||||||
public TaskResult? Result
|
public TaskResult? Result
|
||||||
{
|
{
|
||||||
@@ -321,7 +352,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
var child = new ExecutionContext();
|
var child = new ExecutionContext(this, isEmbedded);
|
||||||
child.Initialize(HostContext);
|
child.Initialize(HostContext);
|
||||||
child.Global = Global;
|
child.Global = Global;
|
||||||
child.ScopeName = scopeName;
|
child.ScopeName = scopeName;
|
||||||
@@ -346,7 +377,6 @@ namespace GitHub.Runner.Worker
|
|||||||
child.ExpressionFunctions.Add(item);
|
child.ExpressionFunctions.Add(item);
|
||||||
}
|
}
|
||||||
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||||
child._parentExecutionContext = this;
|
|
||||||
child.EchoOnActionCommand = EchoOnActionCommand;
|
child.EchoOnActionCommand = EchoOnActionCommand;
|
||||||
|
|
||||||
if (recordOrder != null)
|
if (recordOrder != null)
|
||||||
@@ -367,11 +397,9 @@ namespace GitHub.Runner.Worker
|
|||||||
child._logger.Setup(_mainTimelineId, recordId);
|
child._logger.Setup(_mainTimelineId, recordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
child.IsEmbedded = isEmbedded;
|
|
||||||
child.StepTelemetry.StepId = recordId;
|
child.StepTelemetry.StepId = recordId;
|
||||||
child.StepTelemetry.Stage = stage.ToString();
|
child.StepTelemetry.Stage = stage.ToString();
|
||||||
child.StepTelemetry.IsEmbedded = isEmbedded;
|
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName();
|
||||||
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
|
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@@ -413,13 +441,24 @@ namespace GitHub.Runner.Worker
|
|||||||
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
|
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.CurrentOperation = currentOperation ?? _record.CurrentOperation;
|
||||||
_record.ResultCode = resultCode ?? _record.ResultCode;
|
_record.ResultCode = resultCode ?? _record.ResultCode;
|
||||||
_record.FinishTime = DateTime.UtcNow;
|
_record.FinishTime = now;
|
||||||
_record.PercentComplete = 100;
|
_record.PercentComplete = 100;
|
||||||
_record.Result = _record.Result ?? TaskResult.Succeeded;
|
_record.Result = _record.Result ?? TaskResult.Succeeded;
|
||||||
_record.State = TimelineRecordState.Completed;
|
_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);
|
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||||
|
|
||||||
// complete all detail timeline records.
|
// complete all detail timeline records.
|
||||||
@@ -427,7 +466,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var record in _detailRecords)
|
foreach (var record in _detailRecords)
|
||||||
{
|
{
|
||||||
record.Value.FinishTime = record.Value.FinishTime ?? DateTime.UtcNow;
|
record.Value.FinishTime = record.Value.FinishTime ?? now;
|
||||||
record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
|
record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
|
||||||
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
|
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
|
||||||
record.Value.State = TimelineRecordState.Completed;
|
record.Value.State = TimelineRecordState.Completed;
|
||||||
@@ -438,16 +477,32 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
PublishStepTelemetry();
|
PublishStepTelemetry();
|
||||||
|
|
||||||
var stepResult = new StepResult();
|
if (_record.RecordType == "Task")
|
||||||
stepResult.ExternalID = _record.Id;
|
{
|
||||||
stepResult.Conclusion = _record.Result ?? TaskResult.Succeeded;
|
var stepResult = new StepResult
|
||||||
stepResult.Status = _record.State;
|
{
|
||||||
stepResult.Number = _record.Order;
|
ExternalID = _record.Id,
|
||||||
stepResult.Name = _record.Name;
|
Conclusion = _record.Result ?? TaskResult.Succeeded,
|
||||||
stepResult.StartedAt = _record.StartTime;
|
Status = _record.State,
|
||||||
stepResult.CompletedAt = _record.FinishTime;
|
Number = _record.Order,
|
||||||
|
Name = _record.Name,
|
||||||
|
StartedAt = _record.StartTime,
|
||||||
|
CompletedAt = _record.FinishTime,
|
||||||
|
Annotations = new List<Annotation>()
|
||||||
|
};
|
||||||
|
|
||||||
|
_record.Issues?.ForEach(issue =>
|
||||||
|
{
|
||||||
|
var annotation = issue.ToAnnotation();
|
||||||
|
if (annotation != null)
|
||||||
|
{
|
||||||
|
stepResult.Annotations.Add(annotation.Value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Global.StepsResult.Add(stepResult);
|
||||||
|
}
|
||||||
|
|
||||||
Global.StepsResult.Add(stepResult);
|
|
||||||
|
|
||||||
if (Root != this)
|
if (Root != this)
|
||||||
{
|
{
|
||||||
@@ -559,14 +614,10 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is not thread safe, the caller need to take lock before calling issue()
|
// This is not thread safe, the caller need to take lock before calling issue()
|
||||||
public void AddIssue(Issue issue, string logMessage = null)
|
public void AddIssue(Issue issue, ExecutionContextLogOptions logOptions)
|
||||||
{
|
{
|
||||||
ArgUtil.NotNull(issue, nameof(issue));
|
ArgUtil.NotNull(issue, nameof(issue));
|
||||||
|
ArgUtil.NotNull(logOptions, nameof(logOptions));
|
||||||
if (string.IsNullOrEmpty(logMessage))
|
|
||||||
{
|
|
||||||
logMessage = issue.Message;
|
|
||||||
}
|
|
||||||
|
|
||||||
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
|
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
|
||||||
if (issue.Message.Length > _maxIssueMessageLength)
|
if (issue.Message.Length > _maxIssueMessageLength)
|
||||||
@@ -581,53 +632,64 @@ namespace GitHub.Runner.Worker
|
|||||||
issue.Data["stepNumber"] = _record.Order.ToString();
|
issue.Data["stepNumber"] = _record.Order.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (issue.Type == IssueType.Error)
|
string wellKnownTag = null;
|
||||||
|
Int32 previousCountForIssueType = 0;
|
||||||
|
Action incrementIssueTypeCount = NoOp;
|
||||||
|
switch (issue.Type)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(logMessage))
|
case IssueType.Error:
|
||||||
{
|
wellKnownTag = WellKnownTags.Error;
|
||||||
long logLineNumber = Write(WellKnownTags.Error, logMessage);
|
previousCountForIssueType = _record.ErrorCount;
|
||||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
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 (_record.ErrorCount < _maxIssueCount)
|
if (!string.IsNullOrEmpty(wellKnownTag))
|
||||||
|
{
|
||||||
|
if (!this.IsEmbedded && previousCountForIssueType < _maxCountPerIssueType)
|
||||||
{
|
{
|
||||||
|
incrementIssueTypeCount();
|
||||||
_record.Issues.Add(issue);
|
_record.Issues.Add(issue);
|
||||||
}
|
}
|
||||||
|
|
||||||
_record.ErrorCount++;
|
if (logOptions.WriteToLog)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (issue.Type == IssueType.Warning)
|
|
||||||
|
// 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)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(logMessage))
|
_embeddedIssueCollector.Add(issue);
|
||||||
{
|
|
||||||
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)
|
else
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(logMessage))
|
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||||
{
|
|
||||||
long logLineNumber = Write(WellKnownTags.Notice, logMessage);
|
|
||||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_record.NoticeCount < _maxIssueCount)
|
|
||||||
{
|
|
||||||
_record.Issues.Add(issue);
|
|
||||||
}
|
|
||||||
|
|
||||||
_record.NoticeCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateDetailTimelineRecord(TimelineRecord record)
|
public void UpdateDetailTimelineRecord(TimelineRecord record)
|
||||||
@@ -725,6 +787,9 @@ namespace GitHub.Runner.Worker
|
|||||||
// Steps results for entire job
|
// Steps results for entire job
|
||||||
Global.StepsResult = new List<StepResult>();
|
Global.StepsResult = new List<StepResult>();
|
||||||
|
|
||||||
|
// Job level annotations
|
||||||
|
Global.JobAnnotations = new List<Annotation>();
|
||||||
|
|
||||||
// Job Outputs
|
// Job Outputs
|
||||||
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
@@ -741,6 +806,9 @@ namespace GitHub.Runner.Worker
|
|||||||
// File table
|
// File table
|
||||||
Global.FileTable = new List<String>(message.FileTable ?? new string[0]);
|
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
|
// Expression values
|
||||||
if (message.ContextData?.Count > 0)
|
if (message.ContextData?.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -995,8 +1063,7 @@ namespace GitHub.Runner.Worker
|
|||||||
StepTelemetry.FinishTime = _record.FinishTime;
|
StepTelemetry.FinishTime = _record.FinishTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsEmbedded &&
|
if (!IsEmbedded)
|
||||||
_record.Issues.Count > 0)
|
|
||||||
{
|
{
|
||||||
foreach (var issue in _record.Issues)
|
foreach (var issue in _record.Issues)
|
||||||
{
|
{
|
||||||
@@ -1162,6 +1229,11 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
UpdateGlobalStepsContext();
|
UpdateGlobalStepsContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void NoOp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Error/Warning/etc methods are created as extension methods to simplify unit testing.
|
// The Error/Warning/etc methods are created as extension methods to simplify unit testing.
|
||||||
@@ -1186,19 +1258,22 @@ namespace GitHub.Runner.Worker
|
|||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
public static void Error(this IExecutionContext context, string message)
|
public static void Error(this IExecutionContext context, string message)
|
||||||
{
|
{
|
||||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message });
|
var issue = new Issue() { Type = IssueType.Error, Message = message };
|
||||||
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
public static void InfrastructureError(this IExecutionContext context, string message)
|
public static void InfrastructureError(this IExecutionContext context, string message)
|
||||||
{
|
{
|
||||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
|
var issue = new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true };
|
||||||
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
public static void Warning(this IExecutionContext context, string message)
|
public static void Warning(this IExecutionContext context, string message)
|
||||||
{
|
{
|
||||||
context.AddIssue(new Issue() { Type = IssueType.Warning, Message = message });
|
var issue = new Issue() { Type = IssueType.Warning, Message = message };
|
||||||
|
context.AddIssue(issue, ExecutionContextLogOptions.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
@@ -1284,6 +1359,12 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var key in dict.Keys.ToList())
|
foreach (var key in dict.Keys.ToList())
|
||||||
{
|
{
|
||||||
|
if (key == PipelineTemplateConstants.HostWorkspace)
|
||||||
|
{
|
||||||
|
// The HostWorkspace context var is excluded so that there is a var that always points to the host path.
|
||||||
|
// This var can be used to translate back from container paths, e.g. in HashFilesFunction, which always runs on the host machine
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (dict[key] is StringContextData)
|
if (dict[key] is StringContextData)
|
||||||
{
|
{
|
||||||
var value = dict[key].ToString();
|
var value = dict[key].ToString();
|
||||||
@@ -1330,7 +1411,15 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public void Error(string format, params Object[] args)
|
public void Error(string format, params Object[] args)
|
||||||
{
|
{
|
||||||
_executionContext.Error(string.Format(CultureInfo.CurrentCulture, format, args));
|
/* TraceWriter should be used for logging and not creating erros. */
|
||||||
|
if (logTemplateErrorsAsDebugMessages())
|
||||||
|
{
|
||||||
|
_executionContext.Debug(string.Format(CultureInfo.CurrentCulture, format, args));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_executionContext.Error(string.Format(CultureInfo.CurrentCulture, format, args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Info(string format, params Object[] args)
|
public void Info(string format, params Object[] args)
|
||||||
@@ -1343,8 +1432,18 @@ namespace GitHub.Runner.Worker
|
|||||||
// todo: switch to verbose?
|
// todo: switch to verbose?
|
||||||
_executionContext.Debug(string.Format(CultureInfo.CurrentCulture, $"{format}", args));
|
_executionContext.Debug(string.Format(CultureInfo.CurrentCulture, $"{format}", args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool logTemplateErrorsAsDebugMessages()
|
||||||
|
{
|
||||||
|
if (_executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Runner.Features.LogTemplateErrorsAsDebugMessages, out var logErrorsAsDebug))
|
||||||
|
{
|
||||||
|
return StringUtil.ConvertToBoolean(logErrorsAsDebug, defaultValue: false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class WellKnownTags
|
public static class WellKnownTags
|
||||||
{
|
{
|
||||||
public static readonly string Section = "##[section]";
|
public static readonly string Section = "##[section]";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
@@ -26,11 +26,18 @@ namespace GitHub.Runner.Worker.Expressions
|
|||||||
ArgUtil.NotNull(githubContextData, nameof(githubContextData));
|
ArgUtil.NotNull(githubContextData, nameof(githubContextData));
|
||||||
var githubContext = githubContextData as DictionaryContextData;
|
var githubContext = githubContextData as DictionaryContextData;
|
||||||
ArgUtil.NotNull(githubContext, nameof(githubContext));
|
ArgUtil.NotNull(githubContext, nameof(githubContext));
|
||||||
githubContext.TryGetValue(PipelineTemplateConstants.Workspace, out var workspace);
|
|
||||||
|
if (!githubContext.TryGetValue(PipelineTemplateConstants.HostWorkspace, out var workspace))
|
||||||
|
{
|
||||||
|
githubContext.TryGetValue(PipelineTemplateConstants.Workspace, out workspace);
|
||||||
|
}
|
||||||
|
ArgUtil.NotNull(workspace, nameof(workspace));
|
||||||
|
|
||||||
var workspaceData = workspace as StringContextData;
|
var workspaceData = workspace as StringContextData;
|
||||||
ArgUtil.NotNull(workspaceData, nameof(workspaceData));
|
ArgUtil.NotNull(workspaceData, nameof(workspaceData));
|
||||||
|
|
||||||
string githubWorkspace = workspaceData.Value;
|
string githubWorkspace = workspaceData.Value;
|
||||||
|
|
||||||
bool followSymlink = false;
|
bool followSymlink = false;
|
||||||
List<string> patterns = new();
|
List<string> patterns = new();
|
||||||
var firstParameter = true;
|
var firstParameter = true;
|
||||||
@@ -136,4 +143,4 @@ namespace GitHub.Runner.Worker.Expressions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using System;
|
using System;
|
||||||
@@ -281,7 +281,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class EnvFileKeyValuePairs: IEnumerable<KeyValuePair<string, string>>
|
public sealed class EnvFileKeyValuePairs : IEnumerable<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
private IExecutionContext _context;
|
private IExecutionContext _context;
|
||||||
private string _filePath;
|
private string _filePath;
|
||||||
@@ -322,21 +322,9 @@ namespace GitHub.Runner.Worker
|
|||||||
var equalsIndex = line.IndexOf("=", StringComparison.Ordinal);
|
var equalsIndex = line.IndexOf("=", StringComparison.Ordinal);
|
||||||
var heredocIndex = line.IndexOf("<<", StringComparison.Ordinal);
|
var heredocIndex = line.IndexOf("<<", StringComparison.Ordinal);
|
||||||
|
|
||||||
// Normal style NAME=VALUE
|
// Heredoc style NAME<<EOF (where EOF is typically randomly-generated Base64 and may include an '=' character)
|
||||||
if (equalsIndex >= 0 && (heredocIndex < 0 || equalsIndex < heredocIndex))
|
// see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
|
||||||
{
|
if (heredocIndex >= 0 && (equalsIndex < 0 || heredocIndex < equalsIndex))
|
||||||
var split = line.Split(new[] { '=' }, 2, StringSplitOptions.None);
|
|
||||||
if (string.IsNullOrEmpty(line))
|
|
||||||
{
|
|
||||||
throw new Exception($"Invalid format '{line}'. Name must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
key = split[0];
|
|
||||||
output = split[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Heredoc style NAME<<EOF
|
|
||||||
else if (heredocIndex >= 0 && (equalsIndex < 0 || heredocIndex < equalsIndex))
|
|
||||||
{
|
{
|
||||||
var split = line.Split(new[] { "<<" }, 2, StringSplitOptions.None);
|
var split = line.Split(new[] { "<<" }, 2, StringSplitOptions.None);
|
||||||
if (string.IsNullOrEmpty(split[0]) || string.IsNullOrEmpty(split[1]))
|
if (string.IsNullOrEmpty(split[0]) || string.IsNullOrEmpty(split[1]))
|
||||||
@@ -364,6 +352,18 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
output = endIndex > startIndex ? text.Substring(startIndex, endIndex - startIndex) : string.Empty;
|
output = endIndex > startIndex ? text.Substring(startIndex, endIndex - startIndex) : string.Empty;
|
||||||
}
|
}
|
||||||
|
// Normal style NAME=VALUE
|
||||||
|
else if (equalsIndex >= 0 && heredocIndex < 0)
|
||||||
|
{
|
||||||
|
var split = line.Split(new[] { '=' }, 2, StringSplitOptions.None);
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
throw new Exception($"Invalid format '{line}'. Name must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
key = split[0];
|
||||||
|
output = split[1];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception($"Invalid format '{line}'");
|
throw new Exception($"Invalid format '{line}'");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using GitHub.DistributedTask.WebApi;
|
|||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Sdk.RSWebApi.Contracts;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; set; }
|
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; set; }
|
||||||
public List<ActionsStepTelemetry> StepsTelemetry { get; set; }
|
public List<ActionsStepTelemetry> StepsTelemetry { get; set; }
|
||||||
public List<StepResult> StepsResult { get; set; }
|
public List<StepResult> StepsResult { get; set; }
|
||||||
|
public List<Annotation> JobAnnotations { get; set; }
|
||||||
public List<JobTelemetry> JobTelemetry { get; set; }
|
public List<JobTelemetry> JobTelemetry { get; set; }
|
||||||
public TaskOrchestrationPlanReference Plan { get; set; }
|
public TaskOrchestrationPlanReference Plan { get; set; }
|
||||||
public List<string> PrependPath { get; set; }
|
public List<string> PrependPath { get; set; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
// Evaluation error
|
// Evaluation error
|
||||||
Trace.Info("Caught exception from expression for embedded step.env");
|
Trace.Info("Caught exception from expression for embedded step.env");
|
||||||
step.ExecutionContext.Error(ex);
|
step.ExecutionContext.Error(ex);
|
||||||
step.ExecutionContext.Complete(TaskResult.Failed);
|
SetStepConclusion(step, TaskResult.Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register Callback
|
// Register Callback
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user