Compare commits

..

1 Commits

Author SHA1 Message Date
Francesco Renzi
14cea13ab5 Release 2.316.0 2024-04-23 16:53:39 +01:00
76 changed files with 244 additions and 1374 deletions

View File

@@ -75,7 +75,7 @@ jobs:
# Upload runner package tar.gz/zip as artifact # Upload runner package tar.gz/zip as artifact
- name: Publish Artifact - name: Publish Artifact
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: runner-package-${{ matrix.runtime }} name: runner-package-${{ matrix.runtime }}
path: | path: |

View File

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

View File

@@ -117,11 +117,12 @@ jobs:
working-directory: _package working-directory: _package
# Upload runner package tar.gz/zip as artifact. # Upload runner package tar.gz/zip as artifact.
# Since each package name is unique, so we don't need to put ${{matrix}} info into artifact name
- name: Publish Artifact - name: Publish Artifact
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: runner-packages-${{ matrix.runtime }} name: runner-packages
path: | path: |
_package _package
@@ -133,40 +134,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
# Download runner package tar.gz/zip produced by 'build' job # Download runner package tar.gz/zip produced by 'build' job
- name: Download Artifact (win-x64) - name: Download Artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v1
with: with:
name: runner-packages-win-x64 name: runner-packages
path: ./
- name: Download Artifact (win-arm64)
uses: actions/download-artifact@v4
with:
name: runner-packages-win-arm64
path: ./
- name: Download Artifact (osx-x64)
uses: actions/download-artifact@v4
with:
name: runner-packages-osx-x64
path: ./
- name: Download Artifact (osx-arm64)
uses: actions/download-artifact@v4
with:
name: runner-packages-osx-arm64
path: ./
- name: Download Artifact (linux-x64)
uses: actions/download-artifact@v4
with:
name: runner-packages-linux-x64
path: ./
- name: Download Artifact (linux-arm)
uses: actions/download-artifact@v4
with:
name: runner-packages-linux-arm
path: ./
- name: Download Artifact (linux-arm64)
uses: actions/download-artifact@v4
with:
name: runner-packages-linux-arm64
path: ./ path: ./
# Create ReleaseNote file # Create ReleaseNote file
@@ -285,54 +256,54 @@ jobs:
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
# publish-image: publish-image:
# needs: release needs: release
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# permissions: permissions:
# contents: read contents: read
# packages: write packages: write
# env: env:
# REGISTRY: ghcr.io REGISTRY: ghcr.io
# IMAGE_NAME: ${{ github.repository_owner }}/actions-runner IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
# steps: steps:
# - name: Checkout repository - name: Checkout repository
# uses: actions/checkout@v3 uses: actions/checkout@v3
# - name: Compute image version - name: Compute image version
# id: image id: image
# uses: actions/github-script@v6 uses: actions/github-script@v6
# with: with:
# script: | script: |
# const fs = require('fs'); const fs = require('fs');
# const runnerVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '') const runnerVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '')
# console.log(`Using runner version ${runnerVersion}`) console.log(`Using runner version ${runnerVersion}`)
# core.setOutput('version', runnerVersion); core.setOutput('version', runnerVersion);
# - name: Setup Docker buildx - name: Setup Docker buildx
# uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
# - name: Log into registry ${{ env.REGISTRY }} - name: Log into registry ${{ env.REGISTRY }}
# uses: docker/login-action@v2 uses: docker/login-action@v2
# with: with:
# registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
# username: ${{ github.actor }} username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
# - name: Build and push Docker image - name: Build and push Docker image
# id: build-and-push id: build-and-push
# uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
# with: with:
# context: ./images context: ./images
# platforms: | platforms: |
# linux/amd64 linux/amd64
# linux/arm64 linux/arm64
# tags: | tags: |
# ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }}
# ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
# build-args: | build-args: |
# RUNNER_VERSION=${{ steps.image.outputs.version }} RUNNER_VERSION=${{ steps.image.outputs.version }}
# push: true push: true
# labels: | labels: |
# org.opencontainers.image.source=${{github.server_url}}/${{github.repository}} org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
# org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }} org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
# org.opencontainers.image.licenses=MIT org.opencontainers.image.licenses=MIT

View File

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

View File

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

View File

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

View File

@@ -4,9 +4,9 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG RUNNER_VERSION ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.1 ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.0
ARG DOCKER_VERSION=27.1.1 ARG DOCKER_VERSION=25.0.4
ARG BUILDX_VERSION=0.16.2 ARG BUILDX_VERSION=0.13.1
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y
@@ -39,16 +39,12 @@ 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
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows RUN apt-get update -y \
RUN apt update -y \ && apt-get install -y --no-install-recommends \
&& apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common curl jq unzip \ sudo \
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 \
&& apt install -y --no-install-recommends git
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,20 +1,28 @@
## What's Changed ## What's Changed
* Load '_runnerSettings' in the early point of JobRunner.cs by @TingluoHuang in https://github.com/actions/runner/pull/3218
* Add new SessionConflict return code by @eeSquared in https://github.com/actions/runner/pull/3215
* backoff if we retried polling for more than 50 times in less than 30minutes by @aiqiaoy in https://github.com/actions/runner/pull/3232
* Update dotnet sdk to latest version @6.0.421 by @github-actions in https://github.com/actions/runner/pull/3244
* Cleanup enabled feature flags. by @TingluoHuang in https://github.com/actions/runner/pull/3246
* Relax the condition to stop uploading to Results by @yacaovsnc in https://github.com/actions/runner/pull/3230
* Cleanup enabled feature flags. by @TingluoHuang in https://github.com/actions/runner/pull/3248
* Replace invalid file name chars in diag log name by @ericsciple in https://github.com/actions/runner/pull/3249
- Backport: Expose ENV for cache service v2. https://github.com/actions/runner/pull/3548 ## New Contributors
* @eeSquared made their first contribution in https://github.com/actions/runner/pull/3215
* @aiqiaoy made their first contribution in https://github.com/actions/runner/pull/3232
**Full Changelog**: https://github.com/actions/runner/compare/v2.320.0...v2.320.1 **Full Changelog**: https://github.com/actions/runner/compare/v2.315.0...v2.316.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.
See https://docs.github.com/en/enterprise-cloud@latest/actions/hosting-your-own-runners/adding-self-hosted-runners_ See https://docs.github.com/en/enterprise-cloud@latest/actions/hosting-your-own-runners/adding-self-hosted-runners_
## Windows x64 ## Windows x64
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows. We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
The following snipped needs to be run on `powershell`: The following snipped needs to be run on `powershell`:
``` powershell
```powershell
# Create a folder under the drive root # Create a folder under the drive root
mkdir \actions-runner ; cd \actions-runner mkdir \actions-runner ; cd \actions-runner
# Download the latest runner package # Download the latest runner package
@@ -25,14 +33,12 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
``` ```
## [Pre-release] Windows arm64 ## [Pre-release] Windows arm64
**Warning:** Windows arm64 runners are currently in preview status and use [unofficial versions of nodejs](https://unofficial-builds.nodejs.org/). They are not intended for production workflows. **Warning:** Windows arm64 runners are currently in preview status and use [unofficial versions of nodejs](https://unofficial-builds.nodejs.org/). They are not intended for production workflows.
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows. We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
The following snipped needs to be run on `powershell`: The following snipped needs to be run on `powershell`:
``` powershell
```powershell
# Create a folder under the drive root # Create a folder under the drive root
mkdir \actions-runner ; cd \actions-runner mkdir \actions-runner ; cd \actions-runner
# Download the latest runner package # Download the latest runner package
@@ -44,7 +50,7 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
## OSX x64 ## OSX x64
```bash ``` bash
# Create a folder # Create a folder
mkdir actions-runner && cd actions-runner mkdir actions-runner && cd actions-runner
# Download the latest runner package # Download the latest runner package
@@ -55,7 +61,7 @@ tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
## OSX arm64 (Apple silicon) ## OSX arm64 (Apple silicon)
```bash ``` bash
# Create a folder # Create a folder
mkdir actions-runner && cd actions-runner mkdir actions-runner && cd actions-runner
# Download the latest runner package # Download the latest runner package
@@ -66,7 +72,7 @@ tar xzf ./actions-runner-osx-arm64-<RUNNER_VERSION>.tar.gz
## Linux x64 ## Linux x64
```bash ``` bash
# Create a folder # Create a folder
mkdir actions-runner && cd actions-runner mkdir actions-runner && cd actions-runner
# Download the latest runner package # Download the latest runner package
@@ -77,7 +83,7 @@ tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
## Linux arm64 ## Linux arm64
```bash ``` bash
# Create a folder # Create a folder
mkdir actions-runner && cd actions-runner mkdir actions-runner && cd actions-runner
# Download the latest runner package # Download the latest runner package
@@ -88,7 +94,7 @@ tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
## Linux arm ## Linux arm
```bash ``` bash
# Create a folder # Create a folder
mkdir actions-runner && cd actions-runner mkdir actions-runner && cd actions-runner
# Download the latest runner package # Download the latest runner package
@@ -98,7 +104,6 @@ tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
``` ```
## Using your self hosted runner ## Using your self hosted runner
For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners) For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners)
## SHA-256 Checksums ## SHA-256 Checksums

View File

@@ -1 +1 @@
2.320.1 2.316.0

View File

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

View File

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

View File

@@ -0,0 +1 @@
a2628119ca419cb54e279103ffae7986cdbd0814d57c73ff0dc74c38be08b9ae

View File

@@ -0,0 +1 @@
de71ca09ead807e1a2ce9df0a5b23eb7690cb71fff51169a77e4c3992be53dda

View File

@@ -0,0 +1 @@
d009e05e6b26d614d65be736a15d1bd151932121c16a9ff1b986deadecc982b9

View File

@@ -0,0 +1 @@
f730db39c2305800b4653795360ba9c10c68f384a46b85d808f1f9f0ed3c42e4

View File

@@ -0,0 +1 @@
a35b5722375490e9473cdcccb5e18b41eba3dbf4344fe31abc9821e21f18ea5a

View File

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

View File

@@ -0,0 +1 @@
ec1719a8cb4d8687328aa64f4aa7c4e3498a715d8939117874782e3e6e63a14b

View File

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

View File

@@ -0,0 +1 @@
a0a96cbb7593643b69e669bf14d7b29b7f27800b3a00bb3305aebe041456c701

View File

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

View File

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

View File

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

View File

@@ -5,11 +5,10 @@ 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.13.1" NODE20_VERSION="20.8.1"
NODE16_UNOFFICIAL_VERSION="16.20.0" # used only for win-arm64, remove node16 unofficial version when official version is available # 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

@@ -20,12 +20,12 @@ namespace GitHub.Runner.Common
{ {
private bool _hasConnection; private bool _hasConnection;
private VssConnection _connection; private VssConnection _connection;
private ActionsRunServerHttpClient _actionsRunServerClient; private TaskAgentHttpClient _taskAgentClient;
public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials) public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials)
{ {
_connection = await EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100)); _connection = await EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100));
_actionsRunServerClient = _connection.GetClient<ActionsRunServerHttpClient>(); _taskAgentClient = _connection.GetClient<TaskAgentHttpClient>();
_hasConnection = true; _hasConnection = true;
} }
@@ -42,7 +42,7 @@ namespace GitHub.Runner.Common
CheckConnection(); CheckConnection();
var jobMessage = RetryRequest<AgentJobRequestMessage>(async () => var jobMessage = RetryRequest<AgentJobRequestMessage>(async () =>
{ {
return await _actionsRunServerClient.GetJobMessageAsync(id, cancellationToken); return await _taskAgentClient.GetJobMessageAsync(id, cancellationToken);
}, cancellationToken); }, cancellationToken);
return jobMessage; return jobMessage;

View File

@@ -92,7 +92,7 @@ namespace GitHub.Runner.Common
public bool ShouldRetryException(Exception ex) public bool ShouldRetryException(Exception ex)
{ {
if (ex is AccessDeniedException ade) if (ex is AccessDeniedException ade && ade.ErrorCode == 1)
{ {
return false; return false;
} }

View File

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

View File

@@ -36,7 +36,6 @@ namespace GitHub.Runner.Common
event EventHandler Unloading; event EventHandler Unloading;
void ShutdownRunner(ShutdownReason reason); void ShutdownRunner(ShutdownReason reason);
void WritePerfCounter(string counter); void WritePerfCounter(string counter);
void LoadDefaultUserAgents();
} }
public enum StartupType public enum StartupType
@@ -68,7 +67,6 @@ namespace GitHub.Runner.Common
private StartupType _startupType; private StartupType _startupType;
private string _perfFile; private string _perfFile;
private RunnerWebProxy _webProxy = new(); private RunnerWebProxy _webProxy = new();
private string _hostType = string.Empty;
public event EventHandler Unloading; public event EventHandler Unloading;
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token; public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
@@ -80,7 +78,6 @@ namespace GitHub.Runner.Common
{ {
// Validate args. // Validate args.
ArgUtil.NotNullOrEmpty(hostType, nameof(hostType)); ArgUtil.NotNullOrEmpty(hostType, nameof(hostType));
_hostType = hostType;
_loadContext = AssemblyLoadContext.GetLoadContext(typeof(HostContext).GetTypeInfo().Assembly); _loadContext = AssemblyLoadContext.GetLoadContext(typeof(HostContext).GetTypeInfo().Assembly);
_loadContext.Unloading += LoadContext_Unloading; _loadContext.Unloading += LoadContext_Unloading;
@@ -199,16 +196,6 @@ namespace GitHub.Runner.Common
} }
} }
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY")))
{
_trace.Warning($"Runner is running under insecure mode: HTTPS server certificate validation has been turned off by GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY environment variable.");
}
LoadDefaultUserAgents();
}
public void LoadDefaultUserAgents()
{
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress)) if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
{ {
_trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)"); _trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)");
@@ -218,6 +205,11 @@ namespace GitHub.Runner.Common
_userAgents.Add(new ProductInfoHeaderValue("HttpProxyConfigured", bool.TrueString)); _userAgents.Add(new ProductInfoHeaderValue("HttpProxyConfigured", bool.TrueString));
} }
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY")))
{
_trace.Warning($"Runner is running under insecure mode: HTTPS server certificate validation has been turned off by GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY environment variable.");
}
var credFile = GetConfigFile(WellKnownConfigFile.Credentials); var credFile = GetConfigFile(WellKnownConfigFile.Credentials);
if (File.Exists(credFile)) if (File.Exists(credFile))
{ {
@@ -252,11 +244,6 @@ namespace GitHub.Runner.Common
_trace.Info($"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests."); _trace.Info($"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests.");
_userAgents.Add(extraUserAgentHeader); _userAgents.Add(extraUserAgentHeader);
} }
var currentProcess = Process.GetCurrentProcess();
_userAgents.Add(new ProductInfoHeaderValue("Pid", currentProcess.Id.ToString()));
_userAgents.Add(new ProductInfoHeaderValue("CreationTime", Uri.EscapeDataString(DateTime.UtcNow.ToString("O"))));
_userAgents.Add(new ProductInfoHeaderValue($"({_hostType})"));
} }
public string GetDirectory(WellKnownDirectory directory) public string GetDirectory(WellKnownDirectory directory)

View File

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

View File

@@ -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. {HostContext.SecretMasker.MaskSecrets(ex.Message)}" }; var issue = new Issue() { Type = IssueType.Warning, Message = $"Caught exception with results. {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,9 +709,7 @@ 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);
_resultsServiceExceptionsCount++; if (!_resultsServiceOnly)
// 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

@@ -1,12 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using GitHub.Services.Launch.Client; using GitHub.Services.Launch.Client;
using GitHub.Services.WebApi;
namespace GitHub.Runner.Common namespace GitHub.Runner.Common
{ {
@@ -24,21 +23,8 @@ namespace GitHub.Runner.Common
public void InitializeLaunchClient(Uri uri, string token) public void InitializeLaunchClient(Uri uri, string token)
{ {
// Using default 100 timeout var httpMessageHandler = HostContext.CreateHttpClientHandler();
RawClientHttpRequestSettings settings = VssUtil.GetHttpRequestSettings(null); this._launchClient = new LaunchHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
// Create retry handler
IEnumerable<DelegatingHandler> delegatingHandlers = new List<DelegatingHandler>();
if (settings.MaxRetryRequest > 0)
{
delegatingHandlers = new DelegatingHandler[] { new VssHttpRetryMessageHandler(settings.MaxRetryRequest) };
}
// Setup RawHttpMessageHandler without credentials
var httpMessageHandler = new RawHttpMessageHandler(new NoOpCredentials(null), settings);
var pipeline = HttpClientFactory.CreatePipeline(httpMessageHandler, delegatingHandlers);
this._launchClient = new LaunchHttpClient(uri, pipeline, token, disposeHandler: true);
} }
public Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList, public Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList,

View File

@@ -62,10 +62,7 @@ namespace GitHub.Runner.Common
CheckConnection(); CheckConnection();
return RetryRequest<AgentJobRequestMessage>( return RetryRequest<AgentJobRequestMessage>(
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, cancellationToken), cancellationToken, async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, cancellationToken), cancellationToken,
shouldRetry: ex => shouldRetry: ex => ex is not TaskOrchestrationJobAlreadyAcquiredException);
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

@@ -69,8 +69,7 @@ namespace GitHub.Runner.Listener
Version = BuildConstants.RunnerPackage.Version, Version = BuildConstants.RunnerPackage.Version,
OSDescription = RuntimeInformation.OSDescription, OSDescription = RuntimeInformation.OSDescription,
}; };
var currentProcess = Process.GetCurrentProcess(); string sessionName = $"{Environment.MachineName ?? "RUNNER"}";
string sessionName = $"{Environment.MachineName ?? "RUNNER"} (PID: {currentProcess.Id})";
var taskAgentSession = new TaskAgentSession(sessionName, agent); var taskAgentSession = new TaskAgentSession(sessionName, agent);
string errorMessage = string.Empty; string errorMessage = string.Empty;

View File

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

View File

@@ -88,8 +88,7 @@ namespace GitHub.Runner.Listener
Version = BuildConstants.RunnerPackage.Version, Version = BuildConstants.RunnerPackage.Version,
OSDescription = RuntimeInformation.OSDescription, OSDescription = RuntimeInformation.OSDescription,
}; };
var currentProcess = Process.GetCurrentProcess(); string sessionName = $"{Environment.MachineName ?? "RUNNER"}";
string sessionName = $"{Environment.MachineName ?? "RUNNER"} (PID: {currentProcess.Id})";
var taskAgentSession = new TaskAgentSession(sessionName, agent); var taskAgentSession = new TaskAgentSession(sessionName, agent);
string errorMessage = string.Empty; string errorMessage = string.Empty;

View File

@@ -32,25 +32,10 @@ namespace GitHub.Runner.Listener
private bool _inConfigStage; private bool _inConfigStage;
private ManualResetEvent _completedCommand = new(false); private ManualResetEvent _completedCommand = new(false);
// <summary>
// Helps avoid excessive calls to Run Service when encountering non-retriable errors from /acquirejob.
// Normally we rely on the HTTP clients to back off between retry attempts. However, acquiring a job
// involves calls to both Run Serivce and Broker. And Run Service and Broker communicate with each other
// in an async fashion.
//
// When Run Service encounters a non-retriable error, it sends an async message to Broker. The runner will,
// however, immediately call Broker to get the next message. If the async event from Run Service to Broker
// has not yet been processed, the next message from Broker may be the same job message.
//
// The error throttler helps us back off when encountering successive, non-retriable errors from /acquirejob.
// </summary>
private IErrorThrottler _acquireJobThrottler;
public override void Initialize(IHostContext hostContext) public override void Initialize(IHostContext hostContext)
{ {
base.Initialize(hostContext); base.Initialize(hostContext);
_term = HostContext.GetService<ITerminal>(); _term = HostContext.GetService<ITerminal>();
_acquireJobThrottler = HostContext.CreateService<IErrorThrottler>();
} }
public async Task<int> ExecuteCommand(CommandSettings command) public async Task<int> ExecuteCommand(CommandSettings command)
@@ -237,10 +222,6 @@ namespace GitHub.Runner.Listener
File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden); File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden);
Trace.Info($"Saved {configContent.Length} bytes to '{configFile}'."); Trace.Info($"Saved {configContent.Length} bytes to '{configFile}'.");
} }
// make sure we have the right user agent data added from the jitconfig
HostContext.LoadDefaultUserAgents();
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -582,16 +563,13 @@ namespace GitHub.Runner.Listener
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds); await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
try try
{ {
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token); jobRequestMessage =
_acquireJobThrottler.Reset(); await runServer.GetJobMessageAsync(messageRef.RunnerRequestId,
messageQueueLoopTokenSource.Token);
} }
catch (Exception ex) when ( catch (TaskOrchestrationJobAlreadyAcquiredException)
ex is TaskOrchestrationJobNotFoundException || // HTTP status 404
ex is TaskOrchestrationJobAlreadyAcquiredException || // HTTP status 409
ex is TaskOrchestrationJobUnprocessableException) // HTTP status 422
{ {
Trace.Info($"Skipping message Job. {ex.Message}"); Trace.Info("Job is already acquired, skip this message.");
await _acquireJobThrottler.IncrementAndWaitAsync(messageQueueLoopTokenSource.Token);
continue; continue;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -1102,7 +1102,6 @@ namespace GitHub.Runner.Worker
int timeoutSeconds = 20 * 60; int timeoutSeconds = 20 * 60;
while (retryCount < 3) while (retryCount < 3)
{ {
string requestId = string.Empty;
using (var actionDownloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds))) using (var actionDownloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
using (var actionDownloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(actionDownloadTimeout.Token, executionContext.CancellationToken)) using (var actionDownloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(actionDownloadTimeout.Token, executionContext.CancellationToken))
{ {
@@ -1118,7 +1117,7 @@ namespace GitHub.Runner.Worker
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents); httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
using (var response = await httpClient.GetAsync(downloadUrl)) using (var response = await httpClient.GetAsync(downloadUrl))
{ {
requestId = UrlUtil.GetGitHubRequestId(response.Headers); var requestId = UrlUtil.GetGitHubRequestId(response.Headers);
if (!string.IsNullOrEmpty(requestId)) if (!string.IsNullOrEmpty(requestId))
{ {
Trace.Info($"Request URL: {downloadUrl} X-GitHub-Request-Id: {requestId} Http Status: {response.StatusCode}"); Trace.Info($"Request URL: {downloadUrl} X-GitHub-Request-Id: {requestId} Http Status: {response.StatusCode}");
@@ -1156,7 +1155,7 @@ namespace GitHub.Runner.Worker
catch (OperationCanceledException ex) when (!executionContext.CancellationToken.IsCancellationRequested && retryCount >= 2) catch (OperationCanceledException ex) when (!executionContext.CancellationToken.IsCancellationRequested && retryCount >= 2)
{ {
Trace.Info($"Action download final retry timeout after {timeoutSeconds} seconds."); Trace.Info($"Action download final retry timeout after {timeoutSeconds} seconds.");
throw new TimeoutException($"Action '{downloadUrl}' download has timed out. Error: {ex.Message} {requestId}"); throw new TimeoutException($"Action '{downloadUrl}' download has timed out. Error: {ex.Message}");
} }
catch (ActionNotFoundException) catch (ActionNotFoundException)
{ {
@@ -1171,11 +1170,11 @@ namespace GitHub.Runner.Worker
if (actionDownloadTimeout.Token.IsCancellationRequested) if (actionDownloadTimeout.Token.IsCancellationRequested)
{ {
// action download didn't finish within timeout // action download didn't finish within timeout
executionContext.Warning($"Action '{downloadUrl}' didn't finish download within {timeoutSeconds} seconds. {requestId}"); executionContext.Warning($"Action '{downloadUrl}' didn't finish download within {timeoutSeconds} seconds.");
} }
else else
{ {
executionContext.Warning($"Failed to download action '{downloadUrl}'. Error: {ex.Message} {requestId}"); executionContext.Warning($"Failed to download action '{downloadUrl}'. Error: {ex.Message}");
} }
} }
} }

View File

@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker
// Initialize // Initialize
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token); void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
void CancelToken(); void CancelToken();
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, List<Issue> embeddedIssueCollector = null, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null, TimeSpan? timeout = null); IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null, TimeSpan? timeout = null);
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null); IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);
// logging // logging
@@ -135,6 +135,7 @@ namespace GitHub.Runner.Worker
private readonly TimelineRecord _record = new(); private readonly TimelineRecord _record = new();
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new(); private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
private readonly List<Issue> _embeddedIssueCollector;
private readonly object _loggerLock = new(); private readonly object _loggerLock = new();
private readonly object _matchersLock = new(); private readonly object _matchersLock = new();
private readonly ExecutionContext _parentExecutionContext; private readonly ExecutionContext _parentExecutionContext;
@@ -153,7 +154,6 @@ namespace GitHub.Runner.Worker
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private TaskCompletionSource<int> _forceCompleted = new(); private TaskCompletionSource<int> _forceCompleted = new();
private bool _throttlingReported = false; private bool _throttlingReported = false;
private List<Issue> _embeddedIssueCollector;
// only job level ExecutionContext will track throttling delay. // only job level ExecutionContext will track throttling delay.
private long _totalThrottlingDelayInMilliseconds = 0; private long _totalThrottlingDelayInMilliseconds = 0;
@@ -356,7 +356,6 @@ namespace GitHub.Runner.Worker
int? recordOrder = null, int? recordOrder = null,
IPagingLogger logger = null, IPagingLogger logger = null,
bool isEmbedded = false, bool isEmbedded = false,
List<Issue> embeddedIssueCollector = null,
CancellationTokenSource cancellationTokenSource = null, CancellationTokenSource cancellationTokenSource = null,
Guid embeddedId = default(Guid), Guid embeddedId = default(Guid),
string siblingScopeName = null, string siblingScopeName = null,
@@ -366,10 +365,6 @@ namespace GitHub.Runner.Worker
var child = new ExecutionContext(this, isEmbedded); var child = new ExecutionContext(this, isEmbedded);
child.Initialize(HostContext); child.Initialize(HostContext);
if ((Global.Variables.GetBoolean("RunService.FixEmbeddedIssues") ?? false) && embeddedIssueCollector != null)
{
child._embeddedIssueCollector = embeddedIssueCollector;
}
child.Global = Global; child.Global = Global;
child.ScopeName = scopeName; child.ScopeName = scopeName;
child.ContextName = contextName; child.ContextName = contextName;
@@ -438,7 +433,7 @@ namespace GitHub.Runner.Worker
Dictionary<string, string> intraActionState = null, Dictionary<string, string> intraActionState = null,
string siblingScopeName = null) string siblingScopeName = null)
{ {
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, embeddedIssueCollector: _embeddedIssueCollector, cancellationTokenSource: null, intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName, timeout: GetRemainingTimeout(), recordOrder: _record.Order); 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, timeout: GetRemainingTimeout(), recordOrder: _record.Order);
} }
public void Start(string currentOperation = null) public void Start(string currentOperation = null)
@@ -525,6 +520,7 @@ namespace GitHub.Runner.Worker
Global.StepsResult.Add(stepResult); Global.StepsResult.Add(stepResult);
} }
if (Root != this) if (Root != this)
{ {
// only dispose TokenSource for step level ExecutionContext // only dispose TokenSource for step level ExecutionContext
@@ -841,6 +837,7 @@ namespace GitHub.Runner.Worker
// Actions environment // Actions environment
ActionsEnvironment = message.ActionsEnvironment; ActionsEnvironment = message.ActionsEnvironment;
// Service container info // Service container info
Global.ServiceContainers = new List<ContainerInfo>(); Global.ServiceContainers = new List<ContainerInfo>();
@@ -1421,7 +1418,7 @@ namespace GitHub.Runner.Worker
{ {
if (key == PipelineTemplateConstants.HostWorkspace) if (key == PipelineTemplateConstants.HostWorkspace)
{ {
// The HostWorkspace context var is excluded so that there is a var that always points to the host path. // 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 // This var can be used to translate back from container paths, e.g. in HashFilesFunction, which always runs on the host machine
continue; continue;
} }

View File

@@ -72,11 +72,6 @@ namespace GitHub.Runner.Worker.Handlers
Environment["ACTIONS_RESULTS_URL"] = resultsUrl; Environment["ACTIONS_RESULTS_URL"] = resultsUrl;
} }
if (ExecutionContext.Global.Variables.GetBoolean("actions_uses_cache_service_v2") ?? false)
{
Environment["ACTIONS_CACHE_SERVICE_V2"] = bool.TrueString;
}
// Resolve the target script. // Resolve the target script.
string target = null; string target = null;
if (stage == ActionRunStage.Main) if (stage == ActionRunStage.Main)
@@ -98,6 +93,7 @@ namespace GitHub.Runner.Worker.Handlers
ExecutionContext.StepTelemetry.HasPreStep = Data.HasPre; ExecutionContext.StepTelemetry.HasPreStep = Data.HasPre;
ExecutionContext.StepTelemetry.HasPostStep = Data.HasPost; ExecutionContext.StepTelemetry.HasPostStep = Data.HasPost;
} }
ExecutionContext.StepTelemetry.Type = Data.NodeVersion;
ArgUtil.NotNullOrEmpty(target, nameof(target)); ArgUtil.NotNullOrEmpty(target, nameof(target));
target = Path.Combine(ActionDirectory, target); target = Path.Combine(ActionDirectory, target);
@@ -128,7 +124,6 @@ namespace GitHub.Runner.Worker.Handlers
Data.NodeVersion = "node20"; Data.NodeVersion = "node20";
} }
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion); var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
ExecutionContext.StepTelemetry.Type = nodeRuntimeVersion;
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}"); string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
// Format the arguments passed to node. // Format the arguments passed to node.

View File

@@ -127,10 +127,6 @@ namespace GitHub.Runner.Worker
} }
} }
// Check OS warning
var osWarningChecker = HostContext.GetService<IOSWarningChecker>();
await osWarningChecker.CheckOSAsync(context);
try try
{ {
var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? ""; var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? "";
@@ -403,7 +399,7 @@ namespace GitHub.Runner.Worker
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>(); var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
jobContext.RegisterPostJobStep(new JobExtensionRunner( jobContext.RegisterPostJobStep(new JobExtensionRunner(
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest), runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
condition: snapshotRequest.Condition, condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Create custom image", displayName: $"Create custom image",
data: null)); data: null));
} }

View File

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

View File

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

View File

@@ -30,7 +30,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
public const String If = "if"; public const String If = "if";
public const String Image = "image"; public const String Image = "image";
public const String ImageName = "image-name"; public const String ImageName = "image-name";
public const String CustomImageVersion = "version";
public const String Include = "include"; public const String Include = "include";
public const String Inputs = "inputs"; public const String Inputs = "inputs";
public const String Job = "job"; public const String Job = "job";

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using GitHub.DistributedTask.Expressions2; using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2.Sdk; using GitHub.DistributedTask.Expressions2.Sdk;
@@ -350,10 +349,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token) internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
{ {
string imageName = null; string imageName = null;
string version = "1.*";
string versionString = string.Empty;
var condition = $"{PipelineTemplateConstants.Success}()";
if (token is StringToken snapshotStringLiteral) if (token is StringToken snapshotStringLiteral)
{ {
imageName = snapshotStringLiteral.Value; imageName = snapshotStringLiteral.Value;
@@ -364,19 +359,11 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
foreach (var snapshotPropertyPair in snapshotMapping) foreach (var snapshotPropertyPair in snapshotMapping)
{ {
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key"); var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
var propertyValue = snapshotPropertyPair.Value;
switch (propertyName.Value) switch (propertyName.Value)
{ {
case PipelineTemplateConstants.ImageName: case PipelineTemplateConstants.ImageName:
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value; imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
break; break;
case PipelineTemplateConstants.If:
condition = ConvertToIfCondition(context, propertyValue, false);
break;
case PipelineTemplateConstants.CustomImageVersion:
versionString = propertyValue.AssertString($"job {PipelineTemplateConstants.Snapshot} {PipelineTemplateConstants.CustomImageVersion}").Value;
version = IsSnapshotImageVersionValid(versionString) ? versionString : null;
break;
default: default:
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key"); propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
break; break;
@@ -389,26 +376,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
return null; return null;
} }
return new Snapshot(imageName) return new Snapshot(imageName);
{
Condition = condition,
Version = version
};
}
private static bool IsSnapshotImageVersionValid(string versionString)
{
var versionSegments = versionString.Split(".");
if (versionSegments.Length != 2 ||
!versionSegments[1].Equals("*") ||
!Int32.TryParse(versionSegments[0], NumberStyles.None, CultureInfo.InvariantCulture, result: out int parsedMajor) ||
parsedMajor < 0)
{
return false;
}
return true;
} }
private static ActionStep ConvertToStep( private static ActionStep ConvertToStep(

View File

@@ -1,27 +1,17 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
namespace GitHub.DistributedTask.Pipelines namespace GitHub.DistributedTask.Pipelines
{ {
[DataContract] [DataContract]
public class Snapshot public class Snapshot
{ {
public Snapshot(string imageName, string condition = null, string version = null) public Snapshot(string imageName)
{ {
ImageName = imageName; ImageName = imageName;
Condition = condition ?? $"{PipelineTemplateConstants.Success}()";
Version = version ?? "1.*";
} }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public String ImageName { get; set; } public String ImageName { get; set; }
[DataMember(EmitDefaultValue = false)]
public String Condition { get; set; }
[DataMember(EmitDefaultValue = false)]
public String Version { get; set; }
} }
} }

View File

@@ -169,28 +169,11 @@
"image-name": { "image-name": {
"type": "non-empty-string", "type": "non-empty-string",
"required": true "required": true
},
"if": "snapshot-if",
"version": {
"type": "non-empty-string",
"required": false
} }
} }
} }
}, },
"snapshot-if": {
"context": [
"github",
"inputs",
"vars",
"needs",
"strategy",
"matrix"
],
"string": {}
},
"runs-on": { "runs-on": {
"context": [ "context": [
"github", "github",

View File

@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Services.Common;
using GitHub.Services.Common.Diagnostics;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.DistributedTask.WebApi
{
[ResourceArea(TaskResourceIds.AreaId)]
public class ActionsRunServerHttpClient : TaskAgentHttpClient
{
private static readonly JsonSerializerSettings s_serializerSettings;
static ActionsRunServerHttpClient()
{
s_serializerSettings = new VssJsonMediaTypeFormatter().SerializerSettings;
s_serializerSettings.DateParseHandling = DateParseHandling.None;
s_serializerSettings.FloatParseHandling = FloatParseHandling.Double;
}
public ActionsRunServerHttpClient(
Uri baseUrl,
VssCredentials credentials)
: base(baseUrl, credentials)
{
}
public ActionsRunServerHttpClient(
Uri baseUrl,
VssCredentials credentials,
VssHttpRequestSettings settings)
: base(baseUrl, credentials, settings)
{
}
public ActionsRunServerHttpClient(
Uri baseUrl,
VssCredentials credentials,
params DelegatingHandler[] handlers)
: base(baseUrl, credentials, handlers)
{
}
public ActionsRunServerHttpClient(
Uri baseUrl,
VssCredentials credentials,
VssHttpRequestSettings settings,
params DelegatingHandler[] handlers)
: base(baseUrl, credentials, settings, handlers)
{
}
public ActionsRunServerHttpClient(
Uri baseUrl,
HttpMessageHandler pipeline,
Boolean disposeHandler)
: base(baseUrl, pipeline, disposeHandler)
{
}
public Task<Pipelines.AgentJobRequestMessage> GetJobMessageAsync(
string messageId,
object userState = null,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("GET");
Guid locationId = new Guid("25adab70-1379-4186-be8e-b643061ebe3a");
object routeValues = new { messageId = messageId };
return SendAsync<Pipelines.AgentJobRequestMessage>(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1),
userState: userState,
cancellationToken: cancellationToken);
}
protected override async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default(CancellationToken))
{
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
return JsonConvert.DeserializeObject<T>(json, s_serializerSettings);
}
}
}

View File

@@ -1539,26 +1539,6 @@ 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

@@ -141,6 +141,24 @@ namespace GitHub.DistributedTask.WebApi
return ReplaceAgentAsync(poolId, agent.Id, agent, userState, cancellationToken); return ReplaceAgentAsync(poolId, agent.Id, agent, userState, cancellationToken);
} }
public Task<Pipelines.AgentJobRequestMessage> GetJobMessageAsync(
string messageId,
object userState = null,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("GET");
Guid locationId = new Guid("25adab70-1379-4186-be8e-b643061ebe3a");
object routeValues = new { messageId = messageId };
return SendAsync<Pipelines.AgentJobRequestMessage>(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1),
userState: userState,
cancellationToken: cancellationToken);
}
protected Task<T> SendAsync<T>( protected Task<T> SendAsync<T>(
HttpMethod method, HttpMethod method,
Guid locationId, Guid locationId,

View File

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

View File

@@ -1,10 +0,0 @@
using System.Runtime.Serialization;
namespace GitHub.Actions.RunService.WebApi
{
[DataContract]
public class BrokerErrorKind
{
public const string RunnerVersionTooOld = "RunnerVersionTooOld";
}
}

View File

@@ -1,17 +0,0 @@
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

@@ -9,7 +9,6 @@ using GitHub.DistributedTask.WebApi;
using GitHub.Services.Common; using GitHub.Services.Common;
using GitHub.Services.OAuth; using GitHub.Services.OAuth;
using GitHub.Services.WebApi; using GitHub.Services.WebApi;
using Newtonsoft.Json;
using Sdk.RSWebApi.Contracts; using Sdk.RSWebApi.Contracts;
using Sdk.WebApi.WebApi; using Sdk.WebApi.WebApi;
@@ -17,15 +16,6 @@ namespace GitHub.Actions.RunService.WebApi
{ {
public class RunServiceHttpClient : RawHttpClientBase public class RunServiceHttpClient : RawHttpClientBase
{ {
private static readonly JsonSerializerSettings s_serializerSettings;
static RunServiceHttpClient()
{
s_serializerSettings = new VssJsonMediaTypeFormatter().SerializerSettings;
s_serializerSettings.DateParseHandling = DateParseHandling.None;
s_serializerSettings.FloatParseHandling = FloatParseHandling.Double;
}
public RunServiceHttpClient( public RunServiceHttpClient(
Uri baseUrl, Uri baseUrl,
VssOAuthCredential credentials) VssOAuthCredential credentials)
@@ -86,7 +76,6 @@ 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)
@@ -94,26 +83,14 @@ namespace GitHub.Actions.RunService.WebApi
return result.Value; return result.Value;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error)) switch (result.StatusCode)
{ {
switch ((HttpStatusCode)error.Code) case HttpStatusCode.NotFound:
{ throw new TaskOrchestrationJobNotFoundException($"Job message not found: {messageId}");
case HttpStatusCode.NotFound: case HttpStatusCode.Conflict:
throw new TaskOrchestrationJobNotFoundException($"Job message not found '{messageId}'. {error.Message}"); throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired: {messageId}");
case HttpStatusCode.Conflict: default:
throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired '{messageId}'. {error.Message}"); throw new Exception($"Failed to get job message: {result.Error}");
case HttpStatusCode.UnprocessableEntity:
throw new TaskOrchestrationJobUnprocessableException($"Unprocessable job '{messageId}'. {error.Message}");
}
}
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}");
} }
} }
@@ -121,7 +98,7 @@ namespace GitHub.Actions.RunService.WebApi
Uri requestUri, Uri requestUri,
Guid planId, Guid planId,
Guid jobId, Guid jobId,
TaskResult conclusion, TaskResult result,
Dictionary<String, VariableValue> outputs, Dictionary<String, VariableValue> outputs,
IList<StepResult> stepResults, IList<StepResult> stepResults,
IList<Annotation> jobAnnotations, IList<Annotation> jobAnnotations,
@@ -133,7 +110,7 @@ namespace GitHub.Actions.RunService.WebApi
{ {
PlanID = planId, PlanID = planId,
JobID = jobId, JobID = jobId,
Conclusion = conclusion, Conclusion = result,
Outputs = outputs, Outputs = outputs,
StepResults = stepResults, StepResults = stepResults,
Annotations = jobAnnotations, Annotations = jobAnnotations,
@@ -143,32 +120,22 @@ namespace GitHub.Actions.RunService.WebApi
requestUri = new Uri(requestUri, "completejob"); requestUri = new Uri(requestUri, "completejob");
var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true)); var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
var result = await Send2Async( var response = await SendAsync(
httpMethod, httpMethod,
requestUri, requestUri,
content: requestContent, content: requestContent,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (response.IsSuccessStatusCode)
{ {
return; return;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error)) switch (response.StatusCode)
{ {
switch ((HttpStatusCode)error.Code) case HttpStatusCode.NotFound:
{ throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
case HttpStatusCode.NotFound: default:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}"); 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}");
} }
} }
@@ -192,7 +159,6 @@ namespace GitHub.Actions.RunService.WebApi
httpMethod, httpMethod,
requestUri, requestUri,
content: requestContent, content: requestContent,
readErrorBody: true,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (result.IsSuccess)
@@ -200,60 +166,13 @@ namespace GitHub.Actions.RunService.WebApi
return result.Value; return result.Value;
} }
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error)) switch (result.StatusCode)
{ {
switch ((HttpStatusCode)error.Code) case HttpStatusCode.NotFound:
{ throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
case HttpStatusCode.NotFound: default:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}"); 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}");
}
}
protected override async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default(CancellationToken))
{
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
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

@@ -103,7 +103,6 @@ namespace GitHub.Actions.RunService.WebApi
new HttpMethod("GET"), new HttpMethod("GET"),
requestUri: requestUri, requestUri: requestUri,
queryParameters: queryParams, queryParameters: queryParams,
readErrorBody: true,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (result.IsSuccess) if (result.IsSuccess)
@@ -111,21 +110,8 @@ namespace GitHub.Actions.RunService.WebApi
return result.Value; return result.Value;
} }
if (TryParseErrorBody(result.ErrorBody, out BrokerError brokerError)) // the only time we throw a `Forbidden` exception from Listener /messages is when the runner is
{ // disable_update and is too old to poll
switch (brokerError.ErrorKind)
{
case BrokerErrorKind.RunnerVersionTooOld:
throw new AccessDeniedException(brokerError.Message)
{
ErrorCode = 1
};
default:
break;
}
}
// temporary back compat
if (result.StatusCode == HttpStatusCode.Forbidden) if (result.StatusCode == HttpStatusCode.Forbidden)
{ {
throw new AccessDeniedException($"{result.Error} Runner version v{runnerVersion} is deprecated and cannot receive messages.") throw new AccessDeniedException($"{result.Error} Runner version v{runnerVersion} is deprecated and cannot receive messages.")
@@ -134,7 +120,7 @@ namespace GitHub.Actions.RunService.WebApi
}; };
} }
throw new Exception($"Failed to get job message. Request to {requestUri} failed with status: {result.StatusCode}. Error message {result.Error}"); throw new Exception($"Failed to get job message: {result.Error}");
} }
public async Task<TaskAgentSession> CreateSessionAsync( public async Task<TaskAgentSession> CreateSessionAsync(
@@ -186,26 +172,5 @@ namespace GitHub.Actions.RunService.WebApi
throw new Exception($"Failed to delete broker session: {result.Error}"); throw new Exception($"Failed to delete broker session: {result.Error}");
} }
private static bool TryParseErrorBody(string errorBody, out BrokerError error)
{
if (!string.IsNullOrEmpty(errorBody))
{
try
{
error = JsonUtility.FromString<BrokerError>(errorBody);
if (error?.Source == "actions-broker-listener")
{
return true;
}
}
catch (Exception)
{
}
}
error = null;
return false;
}
} }
} }

View File

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

View File

@@ -5,27 +5,15 @@ 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;
public RawHttpClientResult(bool isSuccess, string error, HttpStatusCode statusCode, string errorBody = null) protected RawHttpClientResult(bool isSuccess, string error, HttpStatusCode statusCode)
{ {
IsSuccess = isSuccess; IsSuccess = isSuccess;
Error = error; Error = error;
StatusCode = statusCode; StatusCode = statusCode;
ErrorBody = errorBody;
} }
} }
@@ -33,13 +21,13 @@ namespace Sdk.WebApi.WebApi
{ {
public T Value { get; private set; } public T Value { get; private set; }
protected internal RawHttpClientResult(T value, bool isSuccess, string error, HttpStatusCode statusCode, string errorBody) protected internal RawHttpClientResult(T value, bool isSuccess, string error, HttpStatusCode statusCode)
: base(isSuccess, error, statusCode, errorBody) : base(isSuccess, error, statusCode)
{ {
Value = value; Value = value;
} }
public static RawHttpClientResult<T> Fail(string message, HttpStatusCode statusCode, string errorBody) => new RawHttpClientResult<T>(default(T), false, message, statusCode, errorBody); public static RawHttpClientResult<T> Fail(string message, HttpStatusCode statusCode) => new RawHttpClientResult<T>(default(T), false, message, statusCode);
public static RawHttpClientResult<T> Ok(T value) => new RawHttpClientResult<T>(value, true, string.Empty, HttpStatusCode.OK, null); public static RawHttpClientResult<T> Ok(T value) => new RawHttpClientResult<T>(value, true, string.Empty, HttpStatusCode.OK);
} }
} }

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.Equal(2, clp.Commands.Count); Assert.True(clp.Commands.Count == 2);
} }
} }
@@ -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.Equal(2, clp.Args.Count); Assert.True(clp.Args.Count == 2);
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.Equal(2, clp.Flags.Count); Assert.True(clp.Flags.Count == 2);
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.Equal(40, BuildConstants.Source.CommitHash.Length); Assert.True(BuildConstants.Source.CommitHash.Length == 40, $"CommitHash should be SHA-1 hash {BuildConstants.Source.CommitHash}");
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.Equal(0, command.Validate().Count); Assert.True(command.Validate().Count == 0);
} }
} }
@@ -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.Equal(0, command.Validate().Count); Assert.True(command.Validate().Count == 0);
} }
} }
@@ -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.Equal(0, command.Validate().Count); Assert.True(command.Validate().Count == 0);
} }
} }

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.Equal(_expectedServerUrl, s.ServerUrl); Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
Assert.Equal(_expectedAgentName, s.AgentName); Assert.True(s.AgentName.Equals(_expectedAgentName));
Assert.Equal(_secondRunnerGroupId, s.PoolId); Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
Assert.Equal(_expectedWorkFolder, s.WorkFolder); Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
Assert.True(s.Ephemeral); Assert.True(s.Ephemeral.Equals(true));
// 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.Equal(_expectedServerUrl, s.ServerUrl); Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
Assert.Equal(_expectedAgentName, s.AgentName); Assert.True(s.AgentName.Equals(_expectedAgentName));
Assert.Equal(_secondRunnerGroupId, s.PoolId); Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
Assert.Equal(_expectedWorkFolder, s.WorkFolder); Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
Assert.True(s.Ephemeral); Assert.True(s.Ephemeral.Equals(true));
// 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

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

View File

@@ -734,10 +734,7 @@ 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.");
if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted) Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
{
Assert.True(await jobDispatcher.RunOnceJobCompleted.Task, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
}
} }
} }

View File

@@ -23,7 +23,6 @@ namespace GitHub.Runner.Common.Tests.Listener
private Mock<ITerminal> _term; private Mock<ITerminal> _term;
private Mock<IConfigurationStore> _configStore; private Mock<IConfigurationStore> _configStore;
private Mock<ISelfUpdater> _updater; private Mock<ISelfUpdater> _updater;
private Mock<IErrorThrottler> _acquireJobThrottler;
public RunnerL0() public RunnerL0()
{ {
@@ -36,7 +35,6 @@ namespace GitHub.Runner.Common.Tests.Listener
_term = new Mock<ITerminal>(); _term = new Mock<ITerminal>();
_configStore = new Mock<IConfigurationStore>(); _configStore = new Mock<IConfigurationStore>();
_updater = new Mock<ISelfUpdater>(); _updater = new Mock<ISelfUpdater>();
_acquireJobThrottler = new Mock<IErrorThrottler>();
} }
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName) private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
@@ -69,7 +67,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -129,7 +126,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.Fail($"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked."); Assert.True(false, $"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked.");
} }
else else
{ {
@@ -177,7 +174,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, args); var command = new CommandSettings(hc, args);
@@ -209,7 +205,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IMessageListener>(_messageListener.Object); hc.SetSingleton<IMessageListener>(_messageListener.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "run" }); var command = new CommandSettings(hc, new[] { "run" });
@@ -247,7 +242,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -311,11 +305,8 @@ 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.False(runnerTask.IsFaulted, runnerTask.Exception?.ToString()); Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
if (runnerTask.IsCompleted) Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success);
{
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.");
@@ -344,7 +335,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
{ {
@@ -416,10 +406,7 @@ 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());
if (runnerTask.IsCompleted) Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success);
{
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.");
@@ -446,7 +433,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IRunnerServer>(_runnerServer.Object); hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<ISelfUpdater>(_updater.Object); hc.SetSingleton<ISelfUpdater>(_updater.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
runner.Initialize(hc); runner.Initialize(hc);
var settings = new RunnerSettings var settings = new RunnerSettings
@@ -506,10 +492,7 @@ 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());
if (runnerTask.IsCompleted) Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.RunOnceRunnerUpdating);
{
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());
@@ -530,7 +513,6 @@ namespace GitHub.Runner.Common.Tests.Listener
hc.SetSingleton<IConfigurationManager>(_configurationManager.Object); hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
hc.SetSingleton<IConfigurationStore>(_configStore.Object); hc.SetSingleton<IConfigurationStore>(_configStore.Object);
hc.SetSingleton<IPromptManager>(_promptManager.Object); hc.SetSingleton<IPromptManager>(_promptManager.Object);
hc.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
var command = new CommandSettings(hc, new[] { "remove", "--local" }); var command = new CommandSettings(hc, new[] { "remove", "--local" });

View File

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

View File

@@ -65,14 +65,7 @@ namespace GitHub.Runner.Common.Tests
} }
} }
if (badCode.Count > 0) 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)}");
{
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]
@@ -119,14 +112,7 @@ namespace GitHub.Runner.Common.Tests
} }
} }
if (badCode.Count > 0) 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)}");
{
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,11 +30,9 @@ namespace GitHub.Runner.Common.Tests
private string _tempDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D")); private string _tempDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
private StartupType _startupType; private StartupType _startupType;
public event EventHandler Unloading; public event EventHandler Unloading;
public event EventHandler<DelayEventArgs> Delaying;
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token; public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
public ShutdownReason RunnerShutdownReason { get; private set; } public ShutdownReason RunnerShutdownReason { get; private set; }
public ISecretMasker SecretMasker => _secretMasker; public ISecretMasker SecretMasker => _secretMasker;
public TestHostContext(object testClass, [CallerMemberName] string testName = "") public TestHostContext(object testClass, [CallerMemberName] string testName = "")
{ {
ArgUtil.NotNull(testClass, nameof(testClass)); ArgUtil.NotNull(testClass, nameof(testClass));
@@ -94,14 +92,6 @@ namespace GitHub.Runner.Common.Tests
public async Task Delay(TimeSpan delay, CancellationToken token) public async Task Delay(TimeSpan delay, CancellationToken token)
{ {
// Event callback
EventHandler<DelayEventArgs> handler = Delaying;
if (handler != null)
{
handler(this, new DelayEventArgs(delay, token));
}
// Delay zero
await Task.Delay(TimeSpan.Zero); await Task.Delay(TimeSpan.Zero);
} }
@@ -370,25 +360,5 @@ namespace GitHub.Runner.Common.Tests
Unloading(this, null); Unloading(this, null);
} }
} }
public void LoadDefaultUserAgents()
{
return;
}
}
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.Equal(0, steps.Count); Assert.True(steps.Count == 0);
} }
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.Equal(0, steps.Count); Assert.True(steps.Count == 0);
} }
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.Equal(0, steps.Count); Assert.True(steps.Count == 0);
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.Equal(ActionExecutionType.Script, definition.Data.Execution.ExecutionType); Assert.True(definition.Data.Execution.ExecutionType == ActionExecutionType.Script);
} }
finally finally
{ {

View File

@@ -773,82 +773,6 @@ namespace GitHub.Runner.Common.Tests.Worker
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void PublishStepResult_EmbeddedStep() public void PublishStepResult_EmbeddedStep()
{
using (TestHostContext hc = CreateTestContext())
{
// Job request
TaskOrchestrationPlanReference plan = new();
TimelineReference timeline = new();
Guid jobId = Guid.NewGuid();
string jobName = "some job name";
var variables = new Dictionary<string, VariableValue>()
{
["RunService.FixEmbeddedIssues"] = new VariableValue("true"),
};
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, variables, new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
// Mocks
var pagingLogger = new Mock<IPagingLogger>();
var pagingLogger2 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
hc.EnqueueInstance(pagingLogger.Object);
hc.EnqueueInstance(pagingLogger2.Object);
hc.SetSingleton(jobServerQueue.Object);
// Job context
var jobContext = new Runner.Worker.ExecutionContext();
jobContext.Initialize(hc);
jobContext.InitializeJob(jobRequest, CancellationToken.None);
jobContext.Start();
// Step 1 context
var step1 = jobContext.CreateChild(Guid.NewGuid(), "my_step", "my_step", null, null, ActionRunStage.Main);
step1.Start();
// Embedded step 1a context
var embeddedStep1a = step1.CreateEmbeddedChild(null, null, Guid.NewGuid(), ActionRunStage.Main);
embeddedStep1a.Start();
embeddedStep1a.StepTelemetry.Type = "node16";
embeddedStep1a.StepTelemetry.Action = "actions/checkout";
embeddedStep1a.StepTelemetry.Ref = "v2";
embeddedStep1a.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" }, ExecutionContextLogOptions.Default);
embeddedStep1a.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" }, ExecutionContextLogOptions.Default);
embeddedStep1a.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" }, ExecutionContextLogOptions.Default);
embeddedStep1a.Complete();
// Embedded step 1b context
var embeddedStep1b = step1.CreateEmbeddedChild(null, null, Guid.NewGuid(), ActionRunStage.Main);
embeddedStep1b.Start();
embeddedStep1b.StepTelemetry.Type = "node16";
embeddedStep1b.StepTelemetry.Action = "actions/checkout";
embeddedStep1b.StepTelemetry.Ref = "v2";
embeddedStep1b.AddIssue(new Issue() { Type = IssueType.Error, Message = "error 2" }, ExecutionContextLogOptions.Default);
embeddedStep1b.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning 2" }, ExecutionContextLogOptions.Default);
embeddedStep1b.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice 2" }, ExecutionContextLogOptions.Default);
embeddedStep1b.Complete();
step1.Complete();
// Assert
Assert.Equal(3, jobContext.Global.StepsResult.Count);
Assert.Equal(0, jobContext.Global.StepsResult[0].Annotations.Count);
Assert.Equal(0, jobContext.Global.StepsResult[1].Annotations.Count);
Assert.Equal(6, jobContext.Global.StepsResult[2].Annotations.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void PublishStepResult_EmbeddedStep_Legacy()
{ {
using (TestHostContext hc = CreateTestContext()) using (TestHostContext hc = CreateTestContext())
{ {
@@ -883,7 +807,7 @@ namespace GitHub.Runner.Common.Tests.Worker
ec.InitializeJob(jobRequest, CancellationToken.None); ec.InitializeJob(jobRequest, CancellationToken.None);
ec.Start(); ec.Start();
var embeddedStep = ec.CreateEmbeddedChild(null, null, Guid.NewGuid(), ActionRunStage.Main); var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
embeddedStep.Start(); embeddedStep.Start();
embeddedStep.StepTelemetry.Type = "node16"; embeddedStep.StepTelemetry.Type = "node16";

View File

@@ -140,7 +140,6 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.SetSingleton(_diagnosticLogManager.Object); hc.SetSingleton(_diagnosticLogManager.Object);
hc.SetSingleton(_jobHookProvider.Object); hc.SetSingleton(_jobHookProvider.Object);
hc.SetSingleton(_snapshotOperationProvider.Object); hc.SetSingleton(_snapshotOperationProvider.Object);
hc.SetSingleton(new Mock<IOSWarningChecker>().Object);
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // JobExecutionContext hc.EnqueueInstance<IPagingLogger>(_logger.Object); // JobExecutionContext
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // job start hook hc.EnqueueInstance<IPagingLogger>(_logger.Object); // job start hook
hc.EnqueueInstance<IPagingLogger>(_logger.Object); // Initial Job hc.EnqueueInstance<IPagingLogger>(_logger.Object); // Initial Job
@@ -506,27 +505,7 @@ namespace GitHub.Runner.Common.Tests.Worker
return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot); return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot);
} }
[Fact] private async Task EnsureSnapshotPostJobStepForToken(TemplateToken snapshotToken, Pipelines.Snapshot expectedSnapshot)
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public Task EnsureSnapshotPostJobStepForMappingToken_WithIf_Is_False()
{
var snapshot = new Pipelines.Snapshot("TestImageNameFromMappingToken", condition: $"{PipelineTemplateConstants.Success}() && 1==0", version: "2.*");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
var condition = new StringToken(null, null, null, snapshot.Condition);
var version = new StringToken(null, null, null, snapshot.Version);
var mappingToken = new MappingToken(null, null, null)
{
{ new StringToken(null,null,null, PipelineTemplateConstants.ImageName), imageNameValueStringToken },
{ new StringToken(null,null,null, PipelineTemplateConstants.If), condition },
{ new StringToken(null,null,null, PipelineTemplateConstants.CustomImageVersion), version }
};
return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot, skipSnapshotStep: true);
}
private async Task EnsureSnapshotPostJobStepForToken(TemplateToken snapshotToken, Pipelines.Snapshot expectedSnapshot, bool skipSnapshotStep = false)
{ {
using (TestHostContext hc = CreateTestContext()) using (TestHostContext hc = CreateTestContext())
{ {
@@ -544,28 +523,14 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal(1, postJobSteps.Count); Assert.Equal(1, postJobSteps.Count);
var snapshotStep = postJobSteps.First(); var snapshotStep = postJobSteps.First();
_jobEc.JobSteps.Enqueue(snapshotStep);
var _stepsRunner = new StepsRunner();
_stepsRunner.Initialize(hc);
await _stepsRunner.RunAsync(_jobEc);
Assert.Equal("Create custom image", snapshotStep.DisplayName); Assert.Equal("Create custom image", snapshotStep.DisplayName);
Assert.Equal(expectedSnapshot.Condition ?? $"{PipelineTemplateConstants.Success}()", snapshotStep.Condition); Assert.Equal($"{PipelineTemplateConstants.Success}()", snapshotStep.Condition);
// Run the mock snapshot step, so we can verify it was executed with the expected snapshot object. // Run the mock snapshot step, so we can verify it was executed with the expected snapshot object.
// await snapshotStep.RunAsync(); await snapshotStep.RunAsync();
if (skipSnapshotStep)
{ Assert.NotNull(_requestedSnapshot);
Assert.Null(_requestedSnapshot); Assert.Equal(expectedSnapshot.ImageName, _requestedSnapshot.ImageName);
}
else
{
Assert.NotNull(_requestedSnapshot);
Assert.Equal(expectedSnapshot.ImageName, _requestedSnapshot.ImageName);
Assert.Equal(expectedSnapshot.Condition ?? $"{PipelineTemplateConstants.Success}()", _requestedSnapshot.Condition);
Assert.Equal(expectedSnapshot.Version ?? "1.*", _requestedSnapshot.Version);
}
} }
} }
} }

View File

@@ -16,7 +16,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.7.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<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.4.0" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +1 @@
2.320.1 2.316.0