Compare commits

...

47 Commits

Author SHA1 Message Date
Cory Miller
0be495a2f9 Detection for heredoc (#2738) 2023-08-08 11:12:42 -04:00
Ben Wells
9c8abf2a6e updating test cases and fixing normal style check 2023-07-26 15:53:57 +00:00
Ben Wells
45d079e5f3 syntax errors 2023-07-26 14:57:57 +00:00
Ben Wells
4da8c9e69a code review suggestion of checking heredocIndex < equalsIndex to short circuit the 'equals in delimiter' case 2023-07-26 14:51:57 +00:00
Ben Wells
3ee541e26a Update src/Test/L0/Worker/FileCommandTestBase.cs
Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>
2023-07-26 10:46:04 -04:00
Ben Wells
71600dec6e throw argument exception to guard against case where pos1 is greater than pos2 2023-07-26 14:44:37 +00:00
Ben Wells
62d0a70002 Update src/Test/L0/Worker/FileCommandTestBase.cs
Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>
2023-07-26 10:40:08 -04:00
Ben Wells
fc8a966a21 Update src/Runner.Worker/FileCommandManager.cs
Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>
2023-07-26 10:39:44 -04:00
Ben Wells
184098ac5d Update src/Runner.Worker/FileCommandManager.cs
Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>
2023-07-26 10:39:36 -04:00
Ben Wells
76e2904c63 Update FileCommandManager.cs 2023-07-21 16:05:30 -04:00
Ben Wells
2796fcdd87 Update FileCommandTestBase.cs to add additional heredoc edge cases 2023-07-21 15:55:35 -04:00
Ben Wells
c71eceaae3 Update heredoc detection to handle more edge cases 2023-07-21 15:49:51 -04:00
JoannaaKL
496904c0b7 Fix feature flag location (#2703) 2023-07-19 12:19:42 +02:00
Tingluo Huang
b91ad56f92 Check connectivity for endpoints requested by service. (#2691) 2023-07-17 08:36:20 -04:00
Ferenc Hammerl
f25c9dfba3 Fixes if:cancelled() composite steps not running and normal composite steps not interrupting when the job is cancelled. (#2638)
* Set composite step's action_status when job is cancelled

Also make composite step inherit timeout from parent

* Fix eof line
2023-07-10 13:32:30 +02:00
Tatyana Kostromskaya
7d432fb24c Prepare release 2.306.0 (#2682) 2023-07-07 13:41:46 +02:00
Tatyana Kostromskaya
e8ee6f7b1b Add warning to notify about forcing node16 (#2678)
* add warning

* fix tests

* aggregate to one warning

* fix linter

* fix comm
2023-07-07 11:35:54 +02:00
Cameron Booth
d4bbbb8419 Only add StepResult for Tasks, not the Job itself (#2620) 2023-06-30 04:17:46 -07:00
John Wesley Walker III
4ffd081aea Fixed a bug where a misplaced = character could bypass heredoc-style processing. (#2627)
* Fixed a bug where a misplaced `=` character could bypass heredoc-style processing.

Fixes https://github.com/github/c2c-actions/issues/6910

GitHub Docs for context:  https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings

* Consolidate near-identical FileCommand-related unit test classes. (#2672)
2023-06-29 12:52:05 +02:00
Nikola Jokic
c05e6748c3 Extend github context with host-workspace (#2517)
* hash files translation works with host context translated

* Change file encoding to utf8 for PipelineTemplateConstants.cs

* infer the feature flag on hash files based on the existence of the context

* encoded utf8 with bom

* rename host-workspace to host-work-directory and feature flag the new context var

* Added comment to explain ignore host work directory resolution

Co-authored-by: fhammerl <fhammerl@github.com>

* trigger pipeline

* Move to the github host-workspace context instead of the runner host-work-directory

* remove unused imports

---------

Co-authored-by: fhammerl <fhammerl@github.com>
2023-06-29 12:12:23 +02:00
Nikola Jokic
a2b7856c9c Fix error message reported on non-local action setup (#2668)
* Fix error message reported on non-local action setup

* Remove tostring left from string builder
2023-06-29 11:33:46 +02:00
Ferenc Hammerl
5f1c6f4708 Add 'http://' to http(s)_proxy if there is no protocol (#2663)
* Add 'http://' to http(s)_proxy if there is no protocol

Before this commit, these URIs used to be ignored (nullproxy).

The new behaviour aligns with go style http(s)_proxy

* Update src/Runner.Sdk/RunnerWebProxy.cs

Co-authored-by: Nikola Jokic <jokicnikola07@gmail.com>

* Assert that original protocol is preserved

* Use startsWith for testing protocol

* Only modify proxy if useful

---------

Co-authored-by: Nikola Jokic <jokicnikola07@gmail.com>
2023-06-20 15:53:44 +02:00
Nikola Jokic
8415f13bab removed throw and else on container action handler (#1873)
* removed throw and else on container action handler

* repaired merge resolution error
2023-06-16 21:53:52 -04:00
Yashwanth Anantharaju
471e3ae2d9 Reduce token service and unnecessary calls - send token to redirects (#2660)
* handle redirects with token

* scope to broker

* lint
2023-06-16 13:23:13 +00:00
Dylan Geraci
1096b975e4 Send environment url to Run Service (#2650)
* add EnvironmentUrl to CompleteJobRequest

* Send environment url to Run Service

* Fix whitespace

* Fix test

* Fix more whitespace

* Apply suggestions from code review

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Apply suggestion from code review

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

---------

Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2023-06-16 04:13:08 +00:00
JoannaaKL
282ba4cfc8 Just dont log these errors (#2656)
* Dont log template errors
2023-06-13 21:10:53 -04:00
Yashwanth Anantharaju
b737a5ac5c remove job completion (#2659) 2023-06-13 20:50:36 +00:00
Tingluo Huang
20721bc950 Prepare runner release 2.305.0. (#2657)
* Prepare runner release 2.305.0.

* Update releaseNote.md

Co-authored-by: John Sudol <24583161+johnsudol@users.noreply.github.com>

* Update releaseNote.md

Co-authored-by: John Sudol <24583161+johnsudol@users.noreply.github.com>

---------

Co-authored-by: John Sudol <24583161+johnsudol@users.noreply.github.com>
2023-06-13 12:57:37 -04:00
Bassem Dghaidi
fde86b0666 Switch from Debian to Ubuntu 22.04 (#2651) 2023-06-13 10:12:22 -04:00
JoannaaKL
efffbaeabc Add utf8 with bom (#2641)
* Change default file encoding
2023-06-02 21:47:59 +02:00
Nikola Jokic
3a1376f90e Fix uses: docker://image:tag steps when container hook is used (#2626)
* Fix `uses: docker://image:tag` steps when container hook is used

* Update src/Runner.Worker/ActionManager.cs

---------

Co-authored-by: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com>
2023-06-02 11:01:59 +00:00
JoannaaKL
50b3edff3c Revert "Dont log error twice in ExecutionContext on template error (#2634)"
This reverts commit 48cbee08f9.
2023-06-01 06:46:10 +00:00
Nikola Jokic
58f7a379a1 Filter out empty arguments in container hooks (#2633) 2023-05-31 16:58:48 +02:00
Luke Tomlinson
e13627df81 Move Using V2 Flow log to Trace (#2635) 2023-05-31 10:40:34 -04:00
JoannaaKL
48cbee08f9 Dont log error twice in ExecutionContext on template error (#2634) 2023-05-31 16:33:36 +02:00
Philip Harrison
21b49c542c Set runner environment in context and env (#2518)
* Set runner environment in runner context and env

Extract runner_environment from the global context and expose in the
`github.runner` context and env as `RUNNER_ENVIRONMENT`.

Signed-off-by: Philip Harrison <philip@mailharrison.com>

* encoding.

---------

Signed-off-by: Philip Harrison <philip@mailharrison.com>
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2023-05-26 13:08:41 -04:00
Bassem Dghaidi
8db8bbe13a Update container-hooks to 0.3.2 (#2618) 2023-05-23 09:35:30 -04:00
Vallie Joseph
49b04976f4 Adding Consistency to 'Failed To Resolve Action Download Info' Infrastructure Error Flagging (#2488)
* adding extra catch for download failure in composite actions

* Adding infra error

* Adding error handling centralizing

* updating try catch bubbling

* cleaning up commits

* cleaning up commits

* cleaning up commits

* updating bubbler

* cleaning up test files

* Fixing linting errors

* updating exception bubble

* reverting composite

* updating catch to not exclude other exceptions

* removing uneeded import

* Update src/Runner.Worker/ActionRunner.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Update src/Runner.Worker/ActionManager.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Update src/Runner.Worker/ActionManager.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Update src/Runner.Worker/ActionManager.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Update src/Runner.Worker/ActionManager.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* moving download out of for loop; reverting exception wrap

* Update src/Runner.Worker/ActionManager.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>

* Adding blank lines back

* Adding blank lines back

* removing uneeded catch for download fail

* adding var back for consistency

* formatting clean

---------

Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2023-05-11 14:57:24 -04:00
Gabriel
eeb0cf6f1e Add --no-default-labels config option to self-hosted runners (#2443)
* Add --no-default-labels option

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Add tests

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* .

---------

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2023-05-11 12:37:56 -04:00
John Wesley Walker III
f8a28c3c4e [1742] Ensure multiple composite annoations are correctly written. (#2311)
* [1742] Ensure multiple composite annoations are correctly written.

This implementation uses a collector pattern to allow embedded ExecutionContexts to stash Issue objects for later processing by a non-embedded ancestor ExecutionContext.

Also:
 - Provide explicit constructor implementations for ExecutionContext
 - Leverage explicit constructors to solidify immutability of several ExecutionContext class members.
 - Fixed erroneous call to ExecutionContext.Complete in CompositeActionHandler.cs
 - Use a consistent timestamp for FinishTime in ExecutionContext::Complete

* Ensure collected issues are processed only by a non-embedded ExecutionContext.
This was already implicit.  Now, just making it explicit.

* Provide a clear mechanism that allows callers to opt-in/opt-out of ExecutionContext::AddIssue's logging behavior.
* Addressed deserialization inconsistencies in TimelineRecord.cs
* Added TimelineRecord unit tests.
* Refined unit tests related to TimelineRecord::Variables case-insensitivity
* Add a unit test that verifies ExecutionContextLogOptions::LogMessageOverride has the desired effect.
* Responded to PR feedback.
* Don't allow embedded ExecutionContexts to add Issues to a TimelineRecord
2023-05-10 16:24:02 +02:00
Tingluo Huang
1bc14f0607 Trace WebSocket exception into verbose level to reduce noise in diag log. (#2591) 2023-05-08 18:54:34 -04:00
John Hernley
22d1938ac4 Resolve Actions Directly From Launch for Run Service Jobs (#2529)
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2023-05-03 16:04:21 -04:00
John Wesley Walker III
229b9b8ecc Remove Temporary Serialization Shim (#2549) 2023-05-03 15:29:19 +02:00
Yashwanth Anantharaju
896152d78e send annotations to run-service (#2574)
* send annotations to run-service

* skip message deletion

* actually don't skip deletion

* enum as numbers

* fix enum

* linting

* remove unncessary file

* feedback
2023-05-01 08:33:03 -04:00
Yang Cao
8d74a9ead6 Fix null guard bug (#2576) 2023-04-28 20:56:10 +00:00
Per Lundberg
77b8586a03 contribute.md: Fix link to style guidelines (#2560) 2023-04-27 16:19:56 -04:00
Yashwanth Anantharaju
c8c47d4f27 handle conflict errors from run service (#2570)
* handle conflict errors from run service

* nit: formatting

* fix formatting
2023-04-27 13:59:12 -04:00
280 changed files with 3199 additions and 1896 deletions

View File

@@ -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>`

View File

@@ -157,7 +157,7 @@ cat (Runner/Worker)_TIMESTAMP.log # view your log file
## Styling
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

View File

@@ -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_ARCH="x64"
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.1
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.2
ARG DOCKER_VERSION=20.10.23
RUN apt update -y && apt install curl unzip -y
@@ -22,7 +23,7 @@ RUN export DOCKER_ARCH=x86_64 \
&& tar zxvf 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 RUNNER_MANUALLY_TRAP_SIG=1
@@ -31,6 +32,7 @@ ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends \
sudo \
lsb-release \
&& rm -rf /var/lib/apt/lists/*
RUN adduser --disabled-password --gecos "" --uid 1001 runner \

View File

@@ -1,18 +1,19 @@
## Features
- Runner changes for communication with Results service (#2510, #2531, #2535, #2516)
- Add `*.ghe.localhost` domains to hosted server check (#2536)
- Add `OrchestrationId` to user-agent for better telemetry correlation. (#2568)
- Add warning to notify about forcing actions to run on node16 instead of node12 (#2678)
## Bugs
- Fix JIT configurations on Windows (#2497)
- Guard against NullReference while creating HostContext (#2343)
- Handles broken symlink in `Which` (#2150, #2196)
- Adding curl retry for external tool downloads (#2552, #2557)
- Limit the time we wait for waiting websocket to connect. (#2554)
- Remove job completion from runner listener (#2659)
- 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
- Bump container hooks version to 0.3.1 in runner image (#2496)
- Runner changes to communicate with vNext services (#2487, #2500, #2505, #2541, #2547)
- Send environment url to Run Service (#2650)
- Reduce token service and unnecessary calls - send token to redirects (#2660)
- 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.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.

View File

@@ -1,5 +1,5 @@
[*.cs]
charset = utf-8
charset = utf-8-bom
insert_final_newline = true
csharp_new_line_before_else = true

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Sdk;
using GitHub.Runner.Sdk;
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
namespace GitHub.Runner.Common
namespace GitHub.Runner.Common
{
public enum ActionResult
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using GitHub.DistributedTask.Logging;
using GitHub.Runner.Sdk;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Sdk;
using GitHub.Runner.Sdk;
using System;
using System.IO;
using System.Linq;

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace GitHub.Runner.Common
{
@@ -132,6 +132,7 @@ namespace GitHub.Runner.Common
public static readonly string GenerateServiceConfig = "generateServiceConfig";
public static readonly string Help = "help";
public static readonly string Local = "local";
public static readonly string NoDefaultLabels = "no-default-labels";
public static readonly string Replace = "replace";
public static readonly string DisableUpdate = "disableupdate";
public static readonly string Once = "once"; // Keep this around since customers still relies on it
@@ -154,6 +155,7 @@ namespace GitHub.Runner.Common
{
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
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 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 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 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
@@ -261,6 +265,7 @@ namespace GitHub.Runner.Common
public static readonly string AccessToken = "system.accessToken";
public static readonly string Culture = "system.culture";
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
public static readonly string JobRequestType = "system.jobRequestType";
public static readonly string OrchestrationId = "system.orchestrationId";
}
}

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Sdk;
using GitHub.Runner.Sdk;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Sdk;
using GitHub.Runner.Sdk;
using System;
using System.Diagnostics;
using System.Globalization;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Net.Http;
using GitHub.Runner.Sdk;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
using System;
using System;
using GitHub.DistributedTask.WebApi;
namespace GitHub.Runner.Common

View 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.");
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace GitHub.Runner.Common

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Common.Util;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
@@ -131,13 +131,13 @@ namespace GitHub.Runner.Common
private void InitializeWebsocketClient(string liveConsoleFeedUrl, string accessToken, TimeSpan delay, bool retryConnection = false)
{
if (!string.IsNullOrEmpty(accessToken))
if (string.IsNullOrEmpty(accessToken))
{
Trace.Info($"No access token from server");
return;
}
if (!string.IsNullOrEmpty(liveConsoleFeedUrl))
if (string.IsNullOrEmpty(liveConsoleFeedUrl))
{
Trace.Info($"No live console feed url from server");
return;
@@ -222,7 +222,7 @@ namespace GitHub.Runner.Common
{
var delay = BackoffTimerHelper.GetRandomBackoff(MinDelayForWebsocketReconnect, MaxDelayForWebsocketReconnect);
Trace.Info($"Websocket is not open, let's attempt to connect back again with random backoff {delay} ms.");
Trace.Error(ex);
Trace.Verbose(ex.ToString());
retries++;
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, delay);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -19,7 +19,15 @@ namespace GitHub.Runner.Common
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);
}
@@ -52,14 +60,23 @@ namespace GitHub.Runner.Common
{
CheckConnection();
return RetryRequest<AgentJobRequestMessage>(
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, cancellationToken), cancellationToken);
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, cancellationToken), cancellationToken,
shouldRetry: ex => ex is not TaskOrchestrationJobAlreadyAcquiredException);
}
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();
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)

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.WebApi;
using System;
using System.Collections.Generic;
using System.Threading;

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.WebApi;
using System;
using System.Collections.Generic;
using System.Threading;

View File

@@ -83,7 +83,8 @@ namespace GitHub.Runner.Common
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
CancellationToken cancellationToken,
int maxRetryAttemptsCount = 5
int maxRetryAttemptsCount = 5,
Func<Exception, bool> shouldRetry = null
)
{
var retryCount = 0;
@@ -96,7 +97,7 @@ namespace GitHub.Runner.Common
return await func();
}
// 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(ex);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using GitHub.DistributedTask.Logging;

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

View File

@@ -1,4 +1,4 @@
// Represents absence of value.
// Represents absence of value.
namespace GitHub.Runner.Common
{
public readonly struct Unit

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Runner.Sdk;

View File

@@ -1,4 +1,4 @@
namespace GitHub.Runner.Common.Util
namespace GitHub.Runner.Common.Util
{
using System;
using GitHub.DistributedTask.WebApi;

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace GitHub.Runner.Common.Util
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.IO;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using GitHub.Runner.Common;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Listener.Configuration;
using GitHub.Runner.Listener.Configuration;
using GitHub.Runner.Common.Util;
using System;
using System.Collections;
@@ -38,6 +38,7 @@ namespace GitHub.Runner.Listener
Constants.Runner.CommandLine.Flags.Replace,
Constants.Runner.CommandLine.Flags.RunAsService,
Constants.Runner.CommandLine.Flags.Unattended,
Constants.Runner.CommandLine.Flags.NoDefaultLabels,
Constants.Runner.CommandLine.Args.Auth,
Constants.Runner.CommandLine.Args.Labels,
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
@@ -85,6 +86,7 @@ namespace GitHub.Runner.Listener
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
public bool GenerateServiceConfig => TestFlag(Constants.Runner.CommandLine.Flags.GenerateServiceConfig);
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
public bool NoDefaultLabels => TestFlag(Constants.Runner.CommandLine.Flags.NoDefaultLabels);
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
public bool RemoveLocalConfig => TestFlag(Constants.Runner.CommandLine.Flags.Local);

View File

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

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Runner.Common;

View File

@@ -1,4 +1,4 @@
#if OS_WINDOWS
#if OS_WINDOWS
#pragma warning disable CA1416
using System;
using System.Collections;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Common;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using System;

View File

@@ -1,4 +1,4 @@
#if OS_WINDOWS
#if OS_WINDOWS
using System.IO;
using System.Security.Cryptography;
using System.Text;

View File

@@ -1,4 +1,4 @@
#if OS_LINUX || OS_OSX
#if OS_LINUX || OS_OSX
using System;
using System.IO;
using System.Security.Cryptography;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using GitHub.Runner.Common;

View File

@@ -1,4 +1,4 @@
#if OS_LINUX
#if OS_LINUX
using System;
using System.Collections.Generic;
using System.IO;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Common.Util;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using System;
using System.Linq;

View File

@@ -1,4 +1,4 @@
#if OS_WINDOWS
#if OS_WINDOWS
#pragma warning disable CA1416
using System;
using System.IO;

View File

@@ -1,3 +1,3 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Test")]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@@ -15,6 +15,7 @@ using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
using GitHub.Services.WebApi.Jwt;
using Sdk.RSWebApi.Contracts;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Listener
@@ -372,6 +373,8 @@ namespace GitHub.Runner.Listener
TaskCompletionSource<int> firstJobRequestRenewed = new();
var notification = HostContext.GetService<IJobNotification>();
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
// lock renew cancellation token.
using (var lockRenewalTokenSource = new CancellationTokenSource())
using (var workerProcessCancelTokenSource = new CancellationTokenSource())
@@ -379,8 +382,6 @@ namespace GitHub.Runner.Listener
long requestId = message.RequestId;
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
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
Task renewJobRequest = RenewJobRequestAsync(message, systemConnection, _poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
@@ -405,7 +406,7 @@ namespace GitHub.Runner.Listener
await renewJobRequest;
// complete job request with result Cancelled
await CompleteJobRequestAsync(_poolId, message, lockToken, TaskResult.Canceled);
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
return;
}
@@ -544,7 +545,6 @@ namespace GitHub.Runner.Listener
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.");
var jobServer = await InitializeJobServerAsync(systemConnection);
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
@@ -552,7 +552,7 @@ namespace GitHub.Runner.Listener
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
{
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;
// 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.
// when we run out of disk space, report back to server has higher priority.
@@ -664,7 +664,7 @@ namespace GitHub.Runner.Listener
await renewJobRequest;
// complete job request
await CompleteJobRequestAsync(_poolId, message, lockToken, resultOnAbandonOrCancel);
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
}
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();
@@ -1077,7 +1077,7 @@ namespace GitHub.Runner.Listener
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;
}
@@ -1117,7 +1117,7 @@ namespace GitHub.Runner.Listener
}
// 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)
{
@@ -1129,34 +1129,11 @@ namespace GitHub.Runner.Listener
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
ArgUtil.NotNull(jobRecord, nameof(jobRecord));
try
{
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 };
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
jobRecord.ErrorCount++;
jobRecord.Issues.Add(unhandledExceptionIssue);
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
}
catch (Exception ex)
@@ -1167,13 +1144,13 @@ namespace GitHub.Runner.Listener
}
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;
}
}
// 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)
{
@@ -1192,7 +1169,15 @@ namespace GitHub.Runner.Listener
{
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)
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

View File

@@ -1,4 +1,4 @@
using GitHub.Runner.Common;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using System;
using System.Globalization;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -549,7 +549,17 @@ namespace GitHub.Runner.Listener
{
var runServer = HostContext.CreateService<IRunServer>();
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);
@@ -673,7 +683,8 @@ Config Options:
--token string Registration token. Required if unattended
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
--runnergroup string Name of the runner group to add this runner to (defaults to the default runner group)
--labels string 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
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
--replace Replace any existing runner with the same name (default false)

View File

@@ -1,4 +1,4 @@
using System.Runtime.Serialization;
using System.Runtime.Serialization;
namespace GitHub.Runner.Listener
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.IO;
using System.Reflection;

View File

@@ -1,4 +1,4 @@
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Actions.Pipelines.WebApi;
using GitHub.Services.WebApi;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@@ -1,4 +1,4 @@
using Pipelines = GitHub.DistributedTask.Pipelines;
using Pipelines = GitHub.DistributedTask.Pipelines;
using System;
using System.Collections.Generic;
using System.Threading;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Runner.Sdk;

View File

@@ -1,4 +1,4 @@
using Pipelines = GitHub.DistributedTask.Pipelines;
using Pipelines = GitHub.DistributedTask.Pipelines;
using System;
using System.Collections.Generic;
using System.Threading;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Runner.Sdk;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
@@ -220,20 +220,12 @@ namespace GitHub.Runner.Sdk
return input;
}
private Dictionary<string, string> _commandEscapeMappings = new(StringComparer.OrdinalIgnoreCase)
private Dictionary<string, string> _commandEscapeMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{
";", "%3B"
},
{
"\r", "%0D"
},
{
"\n", "%0A"
},
{
"]", "%5D"
},
{ ";", "%3B" },
{ "\r", "%0D" },
{ "\n", "%0A" },
{ "]", "%5D" },
};
}
}

View File

@@ -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).

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
@@ -69,6 +69,10 @@ namespace GitHub.Runner.Sdk
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))
{
_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))
{
_httpsProxyAddress = proxyHttpsUri.OriginalString;
@@ -240,5 +248,20 @@ namespace GitHub.Runner.Sdk
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;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace GitHub.Runner.Sdk

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
namespace GitHub.Runner.Sdk
{

View File

@@ -1,4 +1,4 @@
using GitHub.Services.WebApi;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
using System;
using System.Globalization;

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace GitHub.Runner.Sdk
{

View File

@@ -64,7 +64,7 @@ namespace GitHub.Runner.Sdk
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("USE_BROKER_FLOW")))
{
settings.AllowAutoRedirect = true;
settings.AllowAutoRedirectForBroker = true;
}
// Remove Invariant from the list of accepted languages.

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Linq;

View File

@@ -276,7 +276,7 @@ namespace GitHub.Runner.Worker
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
context.AddIssue(issue);
context.AddIssue(issue, ExecutionContextLogOptions.Default);
return;
}
@@ -315,7 +315,7 @@ namespace GitHub.Runner.Worker
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue);
context.AddIssue(issue, ExecutionContextLogOptions.Default);
}
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
@@ -350,7 +350,7 @@ namespace GitHub.Runner.Worker
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue);
context.AddIssue(issue, ExecutionContextLogOptions.Default);
}
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)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@@ -11,12 +11,14 @@ using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container;
using GitHub.Services.Common;
using WebApi = GitHub.DistributedTask.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines;
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
using GitHub.DistributedTask.WebApi;
namespace GitHub.Runner.Worker
{
@@ -100,7 +102,19 @@ namespace GitHub.Runner.Worker
}
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
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 (state.ImagesToPull.Count > 0)
@@ -302,6 +316,18 @@ namespace GitHub.Runner.Worker
};
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
{
if (FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
{
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);
@@ -313,6 +339,7 @@ namespace GitHub.Runner.Worker
Trace.Info($"Using action container image: {container.ContainerImage}.");
}
}
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository)
{
string actionDirectory = null;
@@ -648,13 +675,21 @@ namespace GitHub.Runner.Worker
}
// Resolve download info
var launchServer = HostContext.GetService<ILaunchServer>();
var jobServer = HostContext.GetService<IJobServer>();
var actionDownloadInfos = default(WebApi.ActionDownloadInfoCollection);
for (var attempt = 1; attempt <= 3; attempt++)
{
try
{
if (MessageUtil.IsRunServiceJob(executionContext.Global.Variables.Get(Constants.Variables.System.JobRequestType)))
{
actionDownloadInfos = await launchServer.ResolveActionsDownloadInfoAsync(executionContext.Global.Plan.PlanId, executionContext.Root.Id, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
}
else
{
actionDownloadInfos = await jobServer.ResolveActionDownloadInfoAsync(executionContext.Global.Plan.ScopeIdentifier, executionContext.Global.Plan.PlanType, executionContext.Global.Plan.PlanId, executionContext.Root.Id, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
}
break;
}
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)
{
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);
executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'.");
@@ -1092,8 +1127,16 @@ namespace GitHub.Runner.Worker
}
else
{
var fullPath = IOUtil.ResolvePath(actionEntryDirectory, "."); // resolve full path without access filesystem.
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?");
var reference = repositoryReference.Name;
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}'.");
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.Serialization;
namespace GitHub.Runner.Worker

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GitHub.DistributedTask.ObjectTemplating;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Text;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;

View File

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

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

View File

@@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace GitHub.Runner.Worker.Container
@@ -65,6 +66,16 @@ namespace GitHub.Runner.Worker.Container
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)
{
if (String.IsNullOrEmpty(key))

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -18,15 +18,22 @@ using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers;
using Newtonsoft.Json;
using Sdk.RSWebApi.Contracts;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Worker
{
public class ExecutionContextType
public static class ExecutionContextType
{
public static string Job = "Job";
public static string Task = "Task";
public const string Job = "Job";
public const string Task = "Task";
}
public record ExecutionContextLogOptions(bool WriteToLog, string LogMessageOverride)
{
public static readonly ExecutionContextLogOptions None = new(false, null);
public static readonly ExecutionContextLogOptions Default = new(true, null);
}
[ServiceLocator(Default = typeof(ExecutionContext))]
@@ -71,6 +78,7 @@ namespace GitHub.Runner.Worker
List<string> StepEnvironmentOverrides { get; }
ExecutionContext Root { get; }
ExecutionContext Parent { get; }
// Initialize
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
@@ -92,7 +100,7 @@ namespace GitHub.Runner.Worker
void SetGitHubContext(string name, string value);
void SetOutput(string name, string value, out string reference);
void SetTimeout(TimeSpan? timeout);
void AddIssue(Issue issue, string message = null);
void AddIssue(Issue issue, ExecutionContextLogOptions logOptions);
void Progress(int percentage, string currentOperation = null);
void UpdateDetailTimelineRecord(TimelineRecord record);
@@ -118,7 +126,7 @@ namespace GitHub.Runner.Worker
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 _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
@@ -126,8 +134,10 @@ namespace GitHub.Runner.Worker
private readonly TimelineRecord _record = new();
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
private readonly List<Issue> _embeddedIssueCollector;
private readonly object _loggerLock = new();
private readonly object _matchersLock = new();
private readonly ExecutionContext _parentExecutionContext;
private event OnMatcherChanged _onMatcherChanged;
@@ -135,7 +145,6 @@ namespace GitHub.Runner.Worker
private IPagingLogger _logger;
private IJobServerQueue _jobServerQueue;
private ExecutionContext _parentExecutionContext;
private Guid _mainTimelineId;
private Guid _detailTimelineId;
@@ -149,6 +158,29 @@ namespace GitHub.Runner.Worker
private long _totalThrottlingDelayInMilliseconds = 0;
private bool _stepTelemetryPublished = false;
public ExecutionContext()
: this(parent: null, embedded: false)
{
}
private ExecutionContext(ExecutionContext parent, bool embedded)
{
if (embedded)
{
ArgUtil.NotNull(parent, nameof(parent));
}
_parentExecutionContext = parent;
this.IsEmbedded = embedded;
this.StepTelemetry = new ActionsStepTelemetry
{
IsEmbedded = embedded
};
//Embedded Execution Contexts pseudo-inherit their parent's embeddedIssueCollector.
_embeddedIssueCollector = embedded ? parent._embeddedIssueCollector : new();
}
public Guid Id => _record.Id;
public Guid EmbeddedId { get; private set; }
public string ScopeName { get; private set; }
@@ -161,7 +193,7 @@ namespace GitHub.Runner.Worker
public Dictionary<string, VariableValue> JobOutputs { 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 IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
@@ -186,7 +218,7 @@ namespace GitHub.Runner.Worker
// An embedded execution context shares the same record ID, record name, and logger
// as its enclosing execution context.
public bool IsEmbedded { get; private set; }
public bool IsEmbedded { get; private init; }
public TaskResult? Result
{
@@ -233,6 +265,14 @@ namespace GitHub.Runner.Worker
}
}
public ExecutionContext Parent
{
get
{
return _parentExecutionContext;
}
}
public JobContext JobContext
{
get
@@ -321,7 +361,7 @@ namespace GitHub.Runner.Worker
{
Trace.Entering();
var child = new ExecutionContext();
var child = new ExecutionContext(this, isEmbedded);
child.Initialize(HostContext);
child.Global = Global;
child.ScopeName = scopeName;
@@ -346,7 +386,6 @@ namespace GitHub.Runner.Worker
child.ExpressionFunctions.Add(item);
}
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
child._parentExecutionContext = this;
child.EchoOnActionCommand = EchoOnActionCommand;
if (recordOrder != null)
@@ -367,18 +406,16 @@ namespace GitHub.Runner.Worker
child._logger.Setup(_mainTimelineId, recordId);
}
child.IsEmbedded = isEmbedded;
child.StepTelemetry.StepId = recordId;
child.StepTelemetry.Stage = stage.ToString();
child.StepTelemetry.IsEmbedded = isEmbedded;
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName();
return child;
}
/// <summary>
/// An embedded execution context shares the same record ID, record name, logger,
/// and a linked cancellation token.
/// but NOT the cancellation token (just like workflow steps contexts - they don't share a token)
/// </summary>
public IExecutionContext CreateEmbeddedChild(
string scopeName,
@@ -388,7 +425,7 @@ namespace GitHub.Runner.Worker
Dictionary<string, string> intraActionState = null,
string siblingScopeName = null)
{
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token), intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName);
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: null, intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName);
}
public void Start(string currentOperation = null)
@@ -413,13 +450,24 @@ namespace GitHub.Runner.Worker
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
}
DateTime now = DateTime.UtcNow;
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
_record.ResultCode = resultCode ?? _record.ResultCode;
_record.FinishTime = DateTime.UtcNow;
_record.FinishTime = now;
_record.PercentComplete = 100;
_record.Result = _record.Result ?? TaskResult.Succeeded;
_record.State = TimelineRecordState.Completed;
// Before our main timeline's final QueueTimelineRecordUpdate,
// inject any issues collected by embedded ExecutionContexts.
if (!this.IsEmbedded)
{
foreach (var issue in _embeddedIssueCollector)
{
AddIssue(issue, ExecutionContextLogOptions.None);
}
}
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
// complete all detail timeline records.
@@ -427,7 +475,7 @@ namespace GitHub.Runner.Worker
{
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.Result = record.Value.Result ?? TaskResult.Succeeded;
record.Value.State = TimelineRecordState.Completed;
@@ -438,16 +486,32 @@ namespace GitHub.Runner.Worker
PublishStepTelemetry();
var stepResult = new StepResult();
stepResult.ExternalID = _record.Id;
stepResult.Conclusion = _record.Result ?? TaskResult.Succeeded;
stepResult.Status = _record.State;
stepResult.Number = _record.Order;
stepResult.Name = _record.Name;
stepResult.StartedAt = _record.StartTime;
stepResult.CompletedAt = _record.FinishTime;
if (_record.RecordType == "Task")
{
var stepResult = new StepResult
{
ExternalID = _record.Id,
Conclusion = _record.Result ?? TaskResult.Succeeded,
Status = _record.State,
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);
}
if (Root != this)
{
@@ -542,9 +606,33 @@ namespace GitHub.Runner.Worker
if (timeout != null)
{
_cancellationTokenSource.CancelAfter(timeout.Value);
m_timeoutStartedAt = DateTime.UtcNow;
m_timeout = timeout.Value;
}
}
DateTime? m_timeoutStartedAt;
TimeSpan? m_timeout;
public TimeSpan? GetRemainingTimeout()
{
if (m_timeoutStartedAt != null && m_timeout != null)
{
var elapsedSinceTimeoutSet = DateTime.UtcNow - m_timeoutStartedAt.Value;
var remainingTimeout = m_timeout.Value - elapsedSinceTimeoutSet;
if (remainingTimeout.Ticks > 0)
{
return remainingTimeout;
}
else
{
// there was a timeout and it has expired
return TimeSpan.Zero;
}
}
// no timeout was ever set
return null;
}
public void Progress(int percentage, string currentOperation = null)
{
if (percentage > 100 || percentage < 0)
@@ -559,14 +647,10 @@ namespace GitHub.Runner.Worker
}
// 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));
if (string.IsNullOrEmpty(logMessage))
{
logMessage = issue.Message;
}
ArgUtil.NotNull(logOptions, nameof(logOptions));
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
if (issue.Message.Length > _maxIssueMessageLength)
@@ -581,54 +665,65 @@ namespace GitHub.Runner.Worker
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))
{
long logLineNumber = Write(WellKnownTags.Error, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
case IssueType.Error:
wellKnownTag = WellKnownTags.Error;
previousCountForIssueType = _record.ErrorCount;
incrementIssueTypeCount = () => { _record.ErrorCount++; };
break;
case IssueType.Warning:
wellKnownTag = WellKnownTags.Warning;
previousCountForIssueType = _record.WarningCount;
incrementIssueTypeCount = () => { _record.WarningCount++; };
break;
case IssueType.Notice:
wellKnownTag = WellKnownTags.Notice;
previousCountForIssueType = _record.NoticeCount;
incrementIssueTypeCount = () => { _record.NoticeCount++; };
break;
}
if (_record.ErrorCount < _maxIssueCount)
if (!string.IsNullOrEmpty(wellKnownTag))
{
if (!this.IsEmbedded && previousCountForIssueType < _maxCountPerIssueType)
{
incrementIssueTypeCount();
_record.Issues.Add(issue);
}
_record.ErrorCount++;
}
else if (issue.Type == IssueType.Warning)
if (logOptions.WriteToLog)
{
string logMessage = issue.Message;
if (!string.IsNullOrEmpty(logOptions.LogMessageOverride))
{
logMessage = logOptions.LogMessageOverride;
}
if (!string.IsNullOrEmpty(logMessage))
{
long logLineNumber = Write(WellKnownTags.Warning, logMessage);
// Note that ::Write() has its own secret-masking logic.
long logLineNumber = Write(wellKnownTag, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
}
}
if (_record.WarningCount < _maxIssueCount)
// 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)
{
_record.Issues.Add(issue);
_embeddedIssueCollector.Add(issue);
}
_record.WarningCount++;
}
else if (issue.Type == IssueType.Notice)
else
{
if (!string.IsNullOrEmpty(logMessage))
{
long logLineNumber = Write(WellKnownTags.Notice, logMessage);
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
}
if (_record.NoticeCount < _maxIssueCount)
{
_record.Issues.Add(issue);
}
_record.NoticeCount++;
}
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
}
}
public void UpdateDetailTimelineRecord(TimelineRecord record)
{
@@ -725,6 +820,9 @@ namespace GitHub.Runner.Worker
// Steps results for entire job
Global.StepsResult = new List<StepResult>();
// Job level annotations
Global.JobAnnotations = new List<Annotation>();
// Job Outputs
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
@@ -741,6 +839,9 @@ namespace GitHub.Runner.Worker
// File table
Global.FileTable = new List<String>(message.FileTable ?? new string[0]);
// What type of job request is running (i.e. Run Service vs. pipelines)
Global.Variables.Set(Constants.Variables.System.JobRequestType, message.MessageType);
// Expression values
if (message.ContextData?.Count > 0)
{
@@ -995,8 +1096,7 @@ namespace GitHub.Runner.Worker
StepTelemetry.FinishTime = _record.FinishTime;
}
if (!IsEmbedded &&
_record.Issues.Count > 0)
if (!IsEmbedded)
{
foreach (var issue in _record.Issues)
{
@@ -1162,6 +1262,11 @@ namespace GitHub.Runner.Worker
UpdateGlobalStepsContext();
}
private static void NoOp()
{
}
}
// The Error/Warning/etc methods are created as extension methods to simplify unit testing.
@@ -1186,19 +1291,22 @@ namespace GitHub.Runner.Worker
// Do not add a format string overload. See comment on ExecutionContext.Write().
public static void Error(this IExecutionContext context, string message)
{
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().
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().
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().
@@ -1284,6 +1392,12 @@ namespace GitHub.Runner.Worker
{
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)
{
var value = dict[key].ToString();
@@ -1329,9 +1443,17 @@ namespace GitHub.Runner.Worker
}
public void Error(string format, params Object[] 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)
{
@@ -1343,7 +1465,17 @@ namespace GitHub.Runner.Worker
// todo: switch to verbose?
_executionContext.Debug(string.Format(CultureInfo.CurrentCulture, $"{format}", args));
}
private bool logTemplateErrorsAsDebugMessages()
{
if (_executionContext.Global.Variables.TryGetValue(Constants.Runner.Features.LogTemplateErrorsAsDebugMessages, out var logErrorsAsDebug))
{
return StringUtil.ConvertToBoolean(logErrorsAsDebug, defaultValue: false);
}
return false;
}
}
public static class WellKnownTags
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.DistributedTask.Pipelines.ContextData;
@@ -26,11 +26,18 @@ namespace GitHub.Runner.Worker.Expressions
ArgUtil.NotNull(githubContextData, nameof(githubContextData));
var githubContext = githubContextData as DictionaryContextData;
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;
ArgUtil.NotNull(workspaceData, nameof(workspaceData));
string githubWorkspace = workspaceData.Value;
bool followSymlink = false;
List<string> patterns = new();
var firstParameter = true;

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Common;
using System;
@@ -322,28 +322,28 @@ namespace GitHub.Runner.Worker
var equalsIndex = line.IndexOf("=", StringComparison.Ordinal);
var heredocIndex = line.IndexOf("<<", StringComparison.Ordinal);
// Normal style NAME=VALUE
if (equalsIndex >= 0 && (heredocIndex < 0 || equalsIndex < heredocIndex))
{
var split = line.Split(new[] { '=' }, 2, StringSplitOptions.None);
if (string.IsNullOrEmpty(line))
{
throw new Exception($"Invalid format '{line}'. Name must not be empty");
}
// Heredoc style NAME=<<EOF (where EOF is typically randomly-generated Base64 and may include an '=' character)
// see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
// "key=<<EOF is also considered heredoc where the key contains an =.
// Also we detect heredoc if whitespace chars exist between = and <<. (ex: "key <<EOF")
var isHeredoc = heredocIndex >= 0 &&
(
equalsIndex < 0 ||
heredocIndex < equalsIndex ||
(
heredocIndex > equalsIndex &&
OnlyContainsWhiteSpaceBetweenPositions(line, equalsIndex, heredocIndex)
)
);
key = split[0];
output = split[1];
}
// Heredoc style NAME<<EOF
else if (heredocIndex >= 0 && (equalsIndex < 0 || heredocIndex < equalsIndex))
if (isHeredoc)
{
var split = line.Split(new[] { "<<" }, 2, StringSplitOptions.None);
if (string.IsNullOrEmpty(split[0]) || string.IsNullOrEmpty(split[1]))
{
throw new Exception($"Invalid format '{line}'. Name must not be empty and delimiter must not be empty");
}
key = split[0];
key = split[0].Trim().TrimEnd('=');
var delimiter = split[1];
var startIndex = index; // Start index of the value (inclusive)
var endIndex = index; // End index of the value (exclusive)
@@ -364,6 +364,18 @@ namespace GitHub.Runner.Worker
output = endIndex > startIndex ? text.Substring(startIndex, endIndex - startIndex) : string.Empty;
}
// Normal style NAME=VALUE, can have << in the value.
else if (equalsIndex >= 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
{
throw new Exception($"Invalid format '{line}'");
@@ -383,6 +395,32 @@ namespace GitHub.Runner.Worker
return GetEnumerator();
}
// accepts a string, two indexes, and returns true if only whitespace chars exist between those two indexes (non-inclusive)
private static bool OnlyContainsWhiteSpaceBetweenPositions(string str, int pos1, int pos2)
{
// Check if the provided positions are valid
if (pos1 < 0 || pos2 < 0 || pos1 >= str.Length || pos2 >= str.Length)
{
throw new ArgumentOutOfRangeException("OnlyContainsWhiteSpaceBetweenPositions: Positions should be within the string length.");
}
// Ensure pos1 is always the smaller position
if (pos2 < pos1)
{
throw new ArgumentException("OnlyContainsWhiteSpaceBetweenPositions: pos1 must be less than or equal to pos2.");
}
for (int i = pos1 + 1; i < pos2; i++)
{
if (!char.IsWhiteSpace(str[i]))
{
return false;
}
}
return true;
}
private static string ReadLine(
string text,
ref int index)

View File

@@ -1,4 +1,4 @@
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.Pipelines.ContextData;
using System;
using System.Collections.Generic;

View File

@@ -5,6 +5,7 @@ using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Worker.Container;
using Newtonsoft.Json.Linq;
using Sdk.RSWebApi.Contracts;
namespace GitHub.Runner.Worker
{
@@ -18,6 +19,7 @@ namespace GitHub.Runner.Worker
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; set; }
public List<ActionsStepTelemetry> StepsTelemetry { get; set; }
public List<StepResult> StepsResult { get; set; }
public List<Annotation> JobAnnotations { get; set; }
public List<JobTelemetry> JobTelemetry { get; set; }
public TaskOrchestrationPlanReference Plan { get; set; }
public List<string> PrependPath { get; set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
// Evaluation error
Trace.Info("Caught exception from expression for embedded step.env");
step.ExecutionContext.Error(ex);
step.ExecutionContext.Complete(TaskResult.Failed);
SetStepConclusion(step, TaskResult.Failed);
}
// Register Callback
@@ -310,6 +310,7 @@ namespace GitHub.Runner.Worker.Handlers
// Mark job as cancelled
ExecutionContext.Root.Result = TaskResult.Canceled;
ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult();
step.ExecutionContext.SetGitHubContext("action_status", (ExecutionContext.Root.Result?.ToActionResult() ?? ActionResult.Cancelled).ToString().ToLowerInvariant());
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
@@ -420,6 +421,8 @@ namespace GitHub.Runner.Worker.Handlers
{
Trace.Info($"Starting: {step.DisplayName}");
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
// composite steps inherit the timeout from the parent, set by https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes
step.ExecutionContext.SetTimeout(step.ExecutionContext.Parent.GetRemainingTimeout());
await Common.Util.EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -56,7 +56,7 @@ namespace GitHub.Runner.Worker.Handlers
{
Data.Image = Data.Image.Substring("docker://".Length);
}
else if (Data.Image.EndsWith("Dockerfile") || Data.Image.EndsWith("dockerfile"))
else if (DockerUtil.IsDockerfile(Data.Image))
{
// ensure docker file exist
dockerFile = Path.Combine(ActionDirectory, Data.Image);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@@ -68,6 +68,28 @@ namespace GitHub.Runner.Worker.Handlers
bool isOptOut = isWorkflowOptOutSet ? StringUtil.ConvertToBoolean(workflowOptOut) : isLocalOptOut;
if (!isOptOut)
{
var repoAction = action as Pipelines.RepositoryPathReference;
if (repoAction != null)
{
var warningActions = new HashSet<string>();
if (executionContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode12DetectedAfterEndOfLifeEnvVariable, out var node16ForceWarnings))
{
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(node16ForceWarnings);
}
var repoActionFullName = "";
if (string.IsNullOrEmpty(repoAction.Name))
{
repoActionFullName = repoAction.Path; // local actions don't have a 'Name'
}
else
{
repoActionFullName = $"{repoAction.Name}/{repoAction.Path ?? string.Empty}".TrimEnd('/') + $"@{repoAction.Ref}";
}
warningActions.Add(repoActionFullName);
executionContext.Global.Variables.Set("Node16ForceActionsWarnings", StringUtil.ConvertToJson(warningActions));
}
nodeData.NodeVersion = "node16";
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

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

View File

@@ -1,4 +1,4 @@

using System;
using System.Collections.Generic;
using System.IO;

Some files were not shown because too many files have changed in this diff Show More