Compare commits

...

23 Commits

Author SHA1 Message Date
eric sciple
5e57957c98 Update releaseVersion to 2.318.0 2024-07-26 10:07:25 -05:00
eric sciple
12506842c0 Prepare release 2.318.0 (#3404) 2024-07-26 10:03:59 -05:00
Tingluo Huang
2190396357 Update Docker to v27.1.1 (#3401)
* Update Docker to v27.1.1

* Update Dockerfile
2024-07-26 10:36:05 -04:00
Kynan Ware
41bc0da6fe Redirect supported OS doc section to the public docs (#3396)
* redirect supported OS doc to public docs

* Anchor to appropriate OS heading

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

---------

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2024-07-22 17:07:09 -04:00
Kynan Ware
2a7f327d93 Update supported distros to match new docs (#3226)
Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2024-07-17 10:35:03 -04:00
github-actions[bot]
dbcaa7cf3d Upgrade dotnet sdk to v8.0.303 (#3388)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-15 01:06:31 -04:00
Francesco Renzi
8df87a82b0 Rephrase node20 warning (#3376) 2024-07-08 12:41:06 +01:00
Nikola Jokic
70746ff593 Bump hook version to 0.6.1 (#3350) 2024-06-26 14:56:33 +02:00
eric sciple
054fc2e046 Backoff to avoid excessive retries to Run Service in a duration (#3354) 2024-06-24 16:33:22 -05:00
eric sciple
ecb732eaf4 Receive error body from Run Service (#3342) 2024-06-19 16:38:32 +00:00
dependabot[bot]
3dab1f1fb0 Bump System.Security.Cryptography.Pkcs from 5.0.0 to 8.0.0 in /src (#3347)
Bumps [System.Security.Cryptography.Pkcs](https://github.com/dotnet/runtime) from 5.0.0 to 8.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v5.0.0...v8.0.0)

---
updated-dependencies:
- dependency-name: System.Security.Cryptography.Pkcs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-18 12:12:10 -04:00
github-actions[bot]
8f1c723ba0 Upgrade dotnet sdk to v8.0.302 (#3346)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-18 12:03:44 -04:00
Tingluo Huang
1e74a8137b Bump runner to dotnet 8 (#3345)
* Bump runner to dotnet 8

* .
2024-06-18 11:28:53 -04:00
Josh Gross
3f28dd845f Pass runner version as environment variable in workflow (#3318) 2024-06-10 18:13:17 -04:00
Tingluo Huang
edfdbb9661 Make sure we mask secrets when reporting telemetry. (#3315) 2024-06-04 09:57:15 -04:00
Hidetake Iwata
00888c10f9 Bump docker version and docker buildx version (#3277) 2024-05-31 16:22:54 +00:00
Francesco Renzi
84b1bea43e Prepare relese 2.317.0 (#3311) 2024-05-30 13:36:44 +01:00
dependabot[bot]
ce4d7be00f Bump xunit from 2.4.1 to 2.7.1 in /src (#3242)
* Bump xunit from 2.4.1 to 2.7.1 in /src

Bumps [xunit](https://github.com/xunit/xunit) from 2.4.1 to 2.7.1.
- [Commits](https://github.com/xunit/xunit/compare/2.4.1...2.7.1)

---
updated-dependencies:
- dependency-name: xunit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Appease xunit warnings after upgrading to v2.7.1

* Appease the whitespace linter

* Appease the whitespace linter

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2024-05-21 10:47:43 -04:00
John Wesley Walker III
bd7235ef62 Install gpg-agent during actions/runner container image build (#3294)
`add-apt-repository` depends on `gpg-agent`
2024-05-17 09:55:44 -04:00
John Wesley Walker III
0f15173045 Make it easy to install git on an Action Runner Image (#3273)
(We don't actually install `git`.  We simply get the prerequisites out of the way.)
2024-05-15 18:01:06 +00:00
Patrick Ellis
76dc3a28c0 Upgrade node20: 20.8.1 → 20.13.1 (#3284)
* Upgrade node20: 20.8.1 → 20.13.1

* Call out the release process for `alpine_nodejs` in a comment

* move the comment to the end of the line so it's more obvious which variable it's talking about
2024-05-14 13:50:33 -04:00
Patrick Ellis
c67e7f2813 Delete all the contentHash files (#3285)
Nothing uses them anymore after #3074.
2024-05-13 17:40:23 -04:00
Yang Cao
54052b94fb Also do not give up when uploading steps metadata (#3280) 2024-05-10 11:02:29 -04:00
61 changed files with 684 additions and 150 deletions

View File

@@ -4,7 +4,7 @@
"features": { "features": {
"ghcr.io/devcontainers/features/docker-in-docker:1": {}, "ghcr.io/devcontainers/features/docker-in-docker:1": {},
"ghcr.io/devcontainers/features/dotnet": { "ghcr.io/devcontainers/features/dotnet": {
"version": "6.0.421" "version": "8.0.303"
}, },
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "16" "version": "16"

View File

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

1
.gitignore vendored
View File

@@ -26,4 +26,5 @@ _dotnetsdk
TestResults TestResults
TestLogs TestLogs
.DS_Store .DS_Store
.mono
**/*.DotSettings.user **/*.DotSettings.user

View File

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

View File

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

View File

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

View File

@@ -4,9 +4,9 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG RUNNER_VERSION ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.0 ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.1
ARG DOCKER_VERSION=25.0.4 ARG DOCKER_VERSION=27.1.1
ARG BUILDX_VERSION=0.13.1 ARG BUILDX_VERSION=0.16.2
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y
@@ -39,12 +39,15 @@ ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1 ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
ENV ImageOS=ubuntu22 ENV ImageOS=ubuntu22
RUN apt-get update -y \ # 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
&& apt-get install -y --no-install-recommends \ RUN apt update -y \
sudo \ && apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common \
lsb-release \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Configure git-core/ppa based on guidance here: https://git-scm.com/download/linux
RUN add-apt-repository ppa:git-core/ppa \
&& apt update -y
RUN adduser --disabled-password --gecos "" --uid 1001 runner \ RUN adduser --disabled-password --gecos "" --uid 1001 runner \
&& groupadd docker --gid 123 \ && groupadd docker --gid 123 \
&& usermod -aG sudo runner \ && usermod -aG sudo runner \

View File

@@ -1,8 +1,18 @@
## What's Changed ## What's Changed
- Preserve dates when deserializing job message from Run Service by @ericsciple in https://github.com/actions/runner/pull/3269 - Update Docker to v27.1.1 by @TingluoHuang in https://github.com/actions/runner/pull/3401
- Upgrade dotnet sdk to v8.0.303 in https://github.com/actions/runner/pull/3388
- Rephrase node20 warning by @rentziass in https://github.com/actions/runner/pull/3376
- Bump hook version to 0.6.1 by @nikola-jokic in https://github.com/actions/runner/pull/3350
- Backoff to avoid excessive retries to Run Service in a duration by @ericsciple in https://github.com/actions/runner/pull/3354
- Bump System.Security.Cryptography.Pkcs from 5.0.0 to 8.0.0 in /src in https://github.com/actions/runner/pull/3347
- Upgrade dotnet sdk to v8.0.302 in https://github.com/actions/runner/pull/3346
- Bump runner to dotnet 8 by @TingluoHuang in https://github.com/actions/runner/pull/3345
- Pass runner version as environment variable in workflow by @joshmgross in https://github.com/actions/runner/pull/3318
- Make sure we mask secrets when reporting telemetry by @TingluoHuang in https://github.com/actions/runner/pull/3315
- Bump docker version and docker buildx version by @int128 in https://github.com/actions/runner/pull/3277
**Full Changelog**: https://github.com/actions/runner/compare/v2.316.0...v2.316.1 **Full Changelog**: https://github.com/actions/runner/compare/v2.317.0...v2.318.0
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet. _Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository. To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.

View File

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

View File

@@ -1 +0,0 @@
54d95a44d118dba852395991224a6b9c1abe916858c87138656f80c619e85331

View File

@@ -1 +0,0 @@
68015af17f06a824fa478e62ae7393766ce627fd5599ab916432a14656a19a52

View File

@@ -1 +0,0 @@
a2628119ca419cb54e279103ffae7986cdbd0814d57c73ff0dc74c38be08b9ae

View File

@@ -1 +0,0 @@
de71ca09ead807e1a2ce9df0a5b23eb7690cb71fff51169a77e4c3992be53dda

View File

@@ -1 +0,0 @@
d009e05e6b26d614d65be736a15d1bd151932121c16a9ff1b986deadecc982b9

View File

@@ -1 +0,0 @@
f730db39c2305800b4653795360ba9c10c68f384a46b85d808f1f9f0ed3c42e4

View File

@@ -1 +0,0 @@
a35b5722375490e9473cdcccb5e18b41eba3dbf4344fe31abc9821e21f18ea5a

View File

@@ -1 +0,0 @@
4bf3e1af0d482af1b2eaf9f08250248a8c1aea8ec20a3c5be116d58cdd930009

View File

@@ -1 +0,0 @@
ec1719a8cb4d8687328aa64f4aa7c4e3498a715d8939117874782e3e6e63a14b

View File

@@ -1 +0,0 @@
50538de29f173bb73f708c4ed2c8328a62b8795829b97b2a6cb57197e2305287

View File

@@ -1 +0,0 @@
a0a96cbb7593643b69e669bf14d7b29b7f27800b3a00bb3305aebe041456c701

View File

@@ -1 +0,0 @@
6255b22692779467047ecebd60ad46984866d75cdfe10421d593a7b51d620b09

View File

@@ -1 +0,0 @@
6ff1abd055dc35bfbf06f75c2f08908f660346f66ad1d8f81c910068e9ba029d

View File

@@ -1 +0,0 @@
433a6d748742d12abd20dc2a79b62ac3d9718ae47ef26f8e84dc8c180eea3659

View File

@@ -5,10 +5,11 @@ PRECACHE=$2
NODE_URL=https://nodejs.org/dist NODE_URL=https://nodejs.org/dist
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
NODE16_VERSION="16.20.2" NODE16_VERSION="16.20.2"
NODE20_VERSION="20.8.1" NODE20_VERSION="20.13.1"
# used only for win-arm64, remove node16 unofficial version when official version is available NODE16_UNOFFICIAL_VERSION="16.20.0" # used only for win-arm64, remove node16 unofficial version when official version is available
NODE16_UNOFFICIAL_VERSION="16.20.0"
get_abs_path() { get_abs_path() {
# exploits the fact that pwd will print abs path when no args # exploits the fact that pwd will print abs path when no args

View File

@@ -181,7 +181,7 @@ namespace GitHub.Runner.Common
public static readonly string DeprecatedNodeVersion = "node16"; public static readonly string DeprecatedNodeVersion = "node16";
public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/"; public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/";
public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings"; public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings";
public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions uses Node.js version which is deprecated and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/"; public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions use a deprecated Node.js version and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/";
public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings"; public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings";
} }

View File

@@ -613,7 +613,7 @@ namespace GitHub.Runner.Common
private void SendResultsTelemetry(Exception ex) private void SendResultsTelemetry(Exception ex)
{ {
var issue = new Issue() { Type = IssueType.Warning, Message = $"Caught exception with results. {ex.Message}" }; var issue = new Issue() { Type = IssueType.Warning, Message = $"Caught exception with results. {HostContext.SecretMasker.MaskSecrets(ex.Message)}" };
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.ResultsUploadFailure; issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.ResultsUploadFailure;
var telemetryRecord = new TimelineRecord() var telemetryRecord = new TimelineRecord()
@@ -709,7 +709,9 @@ namespace GitHub.Runner.Common
{ {
Trace.Info("Catch exception during update steps, skip update Results."); Trace.Info("Catch exception during update steps, skip update Results.");
Trace.Error(e); Trace.Error(e);
if (!_resultsServiceOnly) _resultsServiceExceptionsCount++;
// If we hit any exceptions uploading to Results, let's skip any additional uploads to Results unless Results is serving logs
if (!_resultsServiceOnly && _resultsServiceExceptionsCount > 3)
{ {
_resultsClientInitiated = false; _resultsClientInitiated = false;
SendResultsTelemetry(e); SendResultsTelemetry(e);

View File

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

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
</PropertyGroup> </PropertyGroup>
@@ -15,11 +15,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="System.Threading.Channels" Version="4.4.0" /> <PackageReference Include="System.Threading.Channels" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@@ -1,4 +1,5 @@
#if OS_WINDOWS #if OS_WINDOWS
#pragma warning disable CA1416
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@@ -84,4 +85,5 @@ namespace GitHub.Runner.Listener.Configuration
} }
} }
} }
#pragma warning restore CA1416
#endif #endif

View File

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

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<PredefinedCulturesOnly>false</PredefinedCulturesOnly> <PredefinedCulturesOnly>false</PredefinedCulturesOnly>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite> <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
@@ -18,11 +19,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.4.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@@ -32,10 +32,25 @@ namespace GitHub.Runner.Listener
private bool _inConfigStage; private bool _inConfigStage;
private ManualResetEvent _completedCommand = new(false); private ManualResetEvent _completedCommand = new(false);
// <summary>
// Helps avoid excessive calls to Run Service when encountering non-retriable errors from /acquirejob.
// Normally we rely on the HTTP clients to back off between retry attempts. However, acquiring a job
// involves calls to both Run Serivce and Broker. And Run Service and Broker communicate with each other
// in an async fashion.
//
// When Run Service encounters a non-retriable error, it sends an async message to Broker. The runner will,
// however, immediately call Broker to get the next message. If the async event from Run Service to Broker
// has not yet been processed, the next message from Broker may be the same job message.
//
// The error throttler helps us back off when encountering successive, non-retriable errors from /acquirejob.
// </summary>
private IErrorThrottler _acquireJobThrottler;
public override void Initialize(IHostContext hostContext) public override void Initialize(IHostContext hostContext)
{ {
base.Initialize(hostContext); base.Initialize(hostContext);
_term = HostContext.GetService<ITerminal>(); _term = HostContext.GetService<ITerminal>();
_acquireJobThrottler = HostContext.CreateService<IErrorThrottler>();
} }
public async Task<int> ExecuteCommand(CommandSettings command) public async Task<int> ExecuteCommand(CommandSettings command)
@@ -213,10 +228,12 @@ namespace GitHub.Runner.Listener
var configFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), config.Key); var configFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), config.Key);
var configContent = Convert.FromBase64String(config.Value); var configContent = Convert.FromBase64String(config.Value);
#if OS_WINDOWS #if OS_WINDOWS
#pragma warning disable CA1416
if (configFile == HostContext.GetConfigFile(WellKnownConfigFile.RSACredentials)) if (configFile == HostContext.GetConfigFile(WellKnownConfigFile.RSACredentials))
{ {
configContent = ProtectedData.Protect(configContent, null, DataProtectionScope.LocalMachine); configContent = ProtectedData.Protect(configContent, null, DataProtectionScope.LocalMachine);
} }
#pragma warning restore CA1416
#endif #endif
File.WriteAllBytes(configFile, configContent); File.WriteAllBytes(configFile, configContent);
File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden); File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden);
@@ -563,13 +580,16 @@ namespace GitHub.Runner.Listener
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds); await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
try try
{ {
jobRequestMessage = jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, _acquireJobThrottler.Reset();
messageQueueLoopTokenSource.Token);
} }
catch (TaskOrchestrationJobAlreadyAcquiredException) catch (Exception ex) when (
ex is TaskOrchestrationJobNotFoundException || // HTTP status 404
ex is TaskOrchestrationJobAlreadyAcquiredException || // HTTP status 409
ex is TaskOrchestrationJobUnprocessableException) // HTTP status 422
{ {
Trace.Info("Job is already acquired, skip this message."); Trace.Info($"Skipping message Job. {ex.Message}");
await _acquireJobThrottler.IncrementAndWaitAsync(messageQueueLoopTokenSource.Token);
continue; continue;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<PredefinedCulturesOnly>false</PredefinedCulturesOnly> <PredefinedCulturesOnly>false</PredefinedCulturesOnly>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite> <PublishReadyToRunComposite>true</PublishReadyToRunComposite>

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
</PropertyGroup> </PropertyGroup>
@@ -14,9 +15,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="System.Threading.Channels" Version="4.4.0" /> <PackageReference Include="System.Threading.Channels" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<PredefinedCulturesOnly>false</PredefinedCulturesOnly> <PredefinedCulturesOnly>false</PredefinedCulturesOnly>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite> <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
@@ -18,9 +19,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
<PackageReference Include="System.Threading.Channels" Version="4.4.0" /> <PackageReference Include="System.Threading.Channels" Version="8.0.0" />
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" /> <PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
</ItemGroup> </ItemGroup>

View File

@@ -34,6 +34,7 @@ namespace GitHub.Services.Common
public String PropertyName { get; set; } public String PropertyName { get; set; }
[Obsolete]
[SecurityCritical] [SecurityCritical]
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {

View File

@@ -127,6 +127,7 @@ namespace GitHub.Services.Common
EventId = (int)info.GetValue("m_eventId", typeof(int)); EventId = (int)info.GetValue("m_eventId", typeof(int));
} }
[Obsolete]
[SecurityCritical] [SecurityCritical]
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {

View File

@@ -1539,6 +1539,26 @@ namespace GitHub.DistributedTask.WebApi
} }
} }
[Serializable]
[ExceptionMapping("0.0", "3.0", "TaskOrchestrationJobUnprocessableException", "GitHub.DistributedTask.WebApi.TaskOrchestrationJobUnprocessableException, GitHub.DistributedTask.WebApi, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public sealed class TaskOrchestrationJobUnprocessableException : DistributedTaskException
{
public TaskOrchestrationJobUnprocessableException(String message)
: base(message)
{
}
public TaskOrchestrationJobUnprocessableException(String message, Exception innerException)
: base(message, innerException)
{
}
private TaskOrchestrationJobUnprocessableException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
[Serializable] [Serializable]
[ExceptionMapping("0.0", "3.0", "TaskOrchestrationPlanSecurityException", "GitHub.DistributedTask.WebApi.TaskOrchestrationPlanSecurityException, GitHub.DistributedTask.WebApi, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [ExceptionMapping("0.0", "3.0", "TaskOrchestrationPlanSecurityException", "GitHub.DistributedTask.WebApi.TaskOrchestrationPlanSecurityException, GitHub.DistributedTask.WebApi, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public sealed class TaskOrchestrationPlanSecurityException : DistributedTaskException public sealed class TaskOrchestrationPlanSecurityException : DistributedTaskException

View File

@@ -0,0 +1,17 @@
using System.Runtime.Serialization;
namespace GitHub.Actions.RunService.WebApi
{
[DataContract]
public class RunServiceError
{
[DataMember(Name = "source", EmitDefaultValue = false)]
public string Source { get; set; }
[DataMember(Name = "statusCode", EmitDefaultValue = false)]
public int Code { get; set; }
[DataMember(Name = "errorMessage", EmitDefaultValue = false)]
public string Message { get; set; }
}
}

View File

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

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<!-- <SelfContained>true</SelfContained> -->
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
@@ -14,13 +15,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" /> <PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" /> <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.2.1" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.5.1" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="Minimatch" Version="2.0.0" /> <PackageReference Include="Minimatch" Version="2.0.0" />
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" /> <PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" /> <PackageReference Include="System.Net.Http" Version="4.3.4" />

View File

@@ -85,6 +85,7 @@ namespace GitHub.Services.OAuth
set; set;
} }
[Obsolete]
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {
base.GetObjectData(info, context); base.GetObjectData(info, context);

View File

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

View File

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

View File

@@ -24,6 +24,7 @@ namespace GitHub.Services.WebApi
HttpStatusCode = (HttpStatusCode)info.GetInt32("HttpStatusCode"); HttpStatusCode = (HttpStatusCode)info.GetInt32("HttpStatusCode");
} }
[Obsolete]
[SecurityCritical] [SecurityCritical]
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {

View File

@@ -68,7 +68,7 @@ namespace GitHub.Runner.Common.Tests
trace.Info("Parsed"); trace.Info("Parsed");
trace.Info("Commands: {0}", clp.Commands.Count); trace.Info("Commands: {0}", clp.Commands.Count);
Assert.True(clp.Commands.Count == 2); Assert.Equal(2, clp.Commands.Count);
} }
} }
@@ -88,7 +88,7 @@ namespace GitHub.Runner.Common.Tests
trace.Info("Parsed"); trace.Info("Parsed");
trace.Info("Args: {0}", clp.Args.Count); trace.Info("Args: {0}", clp.Args.Count);
Assert.True(clp.Args.Count == 2); Assert.Equal(2, clp.Args.Count);
Assert.True(clp.Args.ContainsKey("arg1")); Assert.True(clp.Args.ContainsKey("arg1"));
Assert.Equal("arg1val", clp.Args["arg1"]); Assert.Equal("arg1val", clp.Args["arg1"]);
Assert.True(clp.Args.ContainsKey("arg2")); Assert.True(clp.Args.ContainsKey("arg2"));
@@ -112,7 +112,7 @@ namespace GitHub.Runner.Common.Tests
trace.Info("Parsed"); trace.Info("Parsed");
trace.Info("Args: {0}", clp.Flags.Count); trace.Info("Args: {0}", clp.Flags.Count);
Assert.True(clp.Flags.Count == 2); Assert.Equal(2, clp.Flags.Count);
Assert.Contains("flag1", clp.Flags); Assert.Contains("flag1", clp.Flags);
Assert.Contains("flag2", clp.Flags); Assert.Contains("flag2", clp.Flags);
} }

View File

@@ -24,7 +24,7 @@ namespace GitHub.Runner.Common.Tests
"osx-arm64" "osx-arm64"
}; };
Assert.True(BuildConstants.Source.CommitHash.Length == 40, $"CommitHash should be SHA-1 hash {BuildConstants.Source.CommitHash}"); Assert.Equal(40, BuildConstants.Source.CommitHash.Length);
Assert.True(validPackageNames.Contains(BuildConstants.RunnerPackage.PackageName), $"PackageName should be one of the following '{string.Join(", ", validPackageNames)}', current PackageName is '{BuildConstants.RunnerPackage.PackageName}'"); Assert.True(validPackageNames.Contains(BuildConstants.RunnerPackage.PackageName), $"PackageName should be one of the following '{string.Join(", ", validPackageNames)}', current PackageName is '{BuildConstants.RunnerPackage.PackageName}'");
} }
} }

View File

@@ -806,7 +806,7 @@ namespace GitHub.Runner.Common.Tests
"test runner" }); "test runner" });
// Assert. // Assert.
Assert.True(command.Validate().Count == 0); Assert.Equal(0, command.Validate().Count);
} }
} }
@@ -844,7 +844,7 @@ namespace GitHub.Runner.Common.Tests
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{flag}" }); var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{flag}" });
// Assert. // Assert.
Assert.True(command.Validate().Count == 0); Assert.Equal(0, command.Validate().Count);
} }
} }
@@ -874,7 +874,7 @@ namespace GitHub.Runner.Common.Tests
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{arg}", argValue }); var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{arg}", argValue });
// Assert. // Assert.
Assert.True(command.Validate().Count == 0); Assert.Equal(0, command.Validate().Count);
} }
} }

View File

@@ -190,11 +190,11 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
trace.Info("Configured, verifying all the parameter value"); trace.Info("Configured, verifying all the parameter value");
var s = configManager.LoadSettings(); var s = configManager.LoadSettings();
Assert.NotNull(s); Assert.NotNull(s);
Assert.True(s.ServerUrl.Equals(_expectedServerUrl)); Assert.Equal(_expectedServerUrl, s.ServerUrl);
Assert.True(s.AgentName.Equals(_expectedAgentName)); Assert.Equal(_expectedAgentName, s.AgentName);
Assert.True(s.PoolId.Equals(_secondRunnerGroupId)); Assert.Equal(_secondRunnerGroupId, s.PoolId);
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder)); Assert.Equal(_expectedWorkFolder, s.WorkFolder);
Assert.True(s.Ephemeral.Equals(true)); Assert.True(s.Ephemeral);
// validate GetAgentPoolsAsync gets called twice with automation pool type // validate GetAgentPoolsAsync gets called twice with automation pool type
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2)); _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
@@ -292,11 +292,11 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
trace.Info("Configured, verifying all the parameter value"); trace.Info("Configured, verifying all the parameter value");
var s = configManager.LoadSettings(); var s = configManager.LoadSettings();
Assert.NotNull(s); Assert.NotNull(s);
Assert.True(s.ServerUrl.Equals(_expectedServerUrl)); Assert.Equal(_expectedServerUrl, s.ServerUrl);
Assert.True(s.AgentName.Equals(_expectedAgentName)); Assert.Equal(_expectedAgentName, s.AgentName);
Assert.True(s.PoolId.Equals(_secondRunnerGroupId)); Assert.Equal(_secondRunnerGroupId, s.PoolId);
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder)); Assert.Equal(_expectedWorkFolder, s.WorkFolder);
Assert.True(s.Ephemeral.Equals(true)); Assert.True(s.Ephemeral);
// validate GetAgentPoolsAsync gets called twice with automation pool type // validate GetAgentPoolsAsync gets called twice with automation pool type
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2)); _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));

View File

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

View File

@@ -734,7 +734,10 @@ namespace GitHub.Runner.Common.Tests.Listener
await jobDispatcher.WaitAsync(CancellationToken.None); await jobDispatcher.WaitAsync(CancellationToken.None);
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent."); Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent."); if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted)
{
Assert.True(await jobDispatcher.RunOnceJobCompleted.Task, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
}
} }
} }

View File

@@ -23,6 +23,7 @@ namespace GitHub.Runner.Common.Tests.Listener
private Mock<ITerminal> _term; private Mock<ITerminal> _term;
private Mock<IConfigurationStore> _configStore; private Mock<IConfigurationStore> _configStore;
private Mock<ISelfUpdater> _updater; private Mock<ISelfUpdater> _updater;
private Mock<IErrorThrottler> _acquireJobThrottler;
public RunnerL0() public RunnerL0()
{ {
@@ -35,6 +36,7 @@ namespace GitHub.Runner.Common.Tests.Listener
_term = new Mock<ITerminal>(); _term = new Mock<ITerminal>();
_configStore = new Mock<IConfigurationStore>(); _configStore = new Mock<IConfigurationStore>();
_updater = new Mock<ISelfUpdater>(); _updater = new Mock<ISelfUpdater>();
_acquireJobThrottler = new Mock<IErrorThrottler>();
} }
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName) private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
@@ -67,6 +69,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -126,7 +129,7 @@ namespace GitHub.Runner.Common.Tests.Listener
//wait for the runner to run one job //wait for the runner to run one job
if (!await signalWorkerComplete.WaitAsync(2000)) if (!await signalWorkerComplete.WaitAsync(2000))
{ {
Assert.True(false, $"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked."); Assert.Fail($"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked.");
} }
else else
{ {
@@ -174,6 +177,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, args); var command = new CommandSettings(hc, args);
@@ -205,6 +209,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "run" }); var command = new CommandSettings(hc, new[] { "run" });
@@ -242,6 +247,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -305,8 +311,11 @@ namespace GitHub.Runner.Common.Tests.Listener
await Task.WhenAny(runnerTask, Task.Delay(30000)); await Task.WhenAny(runnerTask, Task.Delay(30000));
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out."); Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString()); Assert.False(runnerTask.IsFaulted, runnerTask.Exception?.ToString());
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success); if (runnerTask.IsCompleted)
{
Assert.Equal(Constants.Runner.ReturnCode.Success, await runnerTask);
}
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(), _jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
$"{nameof(_jobDispatcher.Object.Run)} was not invoked."); $"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
@@ -335,6 +344,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -406,7 +416,10 @@ namespace GitHub.Runner.Common.Tests.Listener
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out."); Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString()); Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success); if (runnerTask.IsCompleted)
{
Assert.Equal(Constants.Runner.ReturnCode.Success, await runnerTask);
}
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(), _jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
$"{nameof(_jobDispatcher.Object.Run)} was not invoked."); $"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
@@ -433,6 +446,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<ISelfUpdater>(_updater.Object); hc.SetSingleton<ISelfUpdater>(_updater.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
@@ -492,7 +506,10 @@ namespace GitHub.Runner.Common.Tests.Listener
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out."); Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString()); Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.RunOnceRunnerUpdating); if (runnerTask.IsCompleted)
{
Assert.Equal(Constants.Runner.ReturnCode.RunOnceRunnerUpdating, await runnerTask);
}
_updater.Verify(x => x.SelfUpdate(It.IsAny<AgentRefreshMessage>(), It.IsAny<IJobDispatcher>(), false, It.IsAny<CancellationToken>()), Times.Once); _updater.Verify(x => x.SelfUpdate(It.IsAny<AgentRefreshMessage>(), It.IsAny<IJobDispatcher>(), false, It.IsAny<CancellationToken>()), Times.Once);
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Never()); _jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Never());
@@ -513,6 +530,7 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IConfigurationManager>(_configurationManager.Object); hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "remove", "--local" }); var command = new CommandSettings(hc, new[] { "remove", "--local" });

View File

@@ -58,7 +58,7 @@ namespace GitHub.Runner.Common.Tests
trace.Error(ex); trace.Error(ex);
} }
Assert.True(false, "Fail to retrive process environment variable."); Assert.Fail("Failed to retrieve process environment variable.");
} }
finally finally
{ {

View File

@@ -65,7 +65,14 @@ namespace GitHub.Runner.Common.Tests
} }
} }
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}"); if (badCode.Count > 0)
{
Assert.Fail($"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}");
}
else
{
Assert.True(true);
}
} }
[Fact] [Fact]
@@ -112,7 +119,14 @@ namespace GitHub.Runner.Common.Tests
} }
} }
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}"); if (badCode.Count > 0)
{
Assert.Fail($"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}");
}
else
{
Assert.True(true);
}
} }
[Fact] [Fact]

View File

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

View File

@@ -460,7 +460,7 @@ runs:
//Act //Act
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
Assert.True(steps.Count == 0); Assert.Equal(0, steps.Count);
} }
finally finally
{ {
@@ -915,7 +915,7 @@ runs:
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
// node.js based action doesn't need any extra steps to build/pull containers. // node.js based action doesn't need any extra steps to build/pull containers.
Assert.True(steps.Count == 0); Assert.Equal(0, steps.Count);
} }
finally finally
{ {
@@ -1051,7 +1051,7 @@ runs:
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
// node.js based action doesn't need any extra steps to build/pull containers. // node.js based action doesn't need any extra steps to build/pull containers.
Assert.True(steps.Count == 0); Assert.Equal(0, steps.Count);
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang/runner_L0", "CompositeBasic.completed"); var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang/runner_L0", "CompositeBasic.completed");
Assert.True(File.Exists(watermarkFile)); Assert.True(File.Exists(watermarkFile));
// Comes from the composite action // Comes from the composite action
@@ -1245,7 +1245,7 @@ runs:
// Assert. // Assert.
Assert.NotNull(definition); Assert.NotNull(definition);
Assert.NotNull(definition.Data); Assert.NotNull(definition.Data);
Assert.True(definition.Data.Execution.ExecutionType == ActionExecutionType.Script); Assert.Equal(ActionExecutionType.Script, definition.Data.Execution.ExecutionType);
} }
finally finally
{ {

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<NoWarn>NU1701;NU1603;NU1603;xUnit2013;</NoWarn> <NoWarn>NU1701;NU1603;NU1603;xUnit2013;SYSLIB0050;SYSLIB0051</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -15,13 +15,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.7.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.5.8" />
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" /> <PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageReference Include="Moq" Version="4.11.0" /> <PackageReference Include="Moq" Version="4.20.70" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x" DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package" PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk" DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="6.0.421" DOTNETSDK_VERSION="8.0.303"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION" DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion) RUNNER_VERSION=$(cat runnerversion)

View File

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

View File

@@ -1 +1 @@
2.316.1 2.318.0