mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
25 Commits
v2.299.1
...
users/jww3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3e42889af | ||
|
|
f3961c4895 | ||
|
|
cb89be7aac | ||
|
|
b70f97f183 | ||
|
|
14096a7ee4 | ||
|
|
4eb9adc958 | ||
|
|
efc0a92cc7 | ||
|
|
87ababb858 | ||
|
|
3902257e9d | ||
|
|
7036627d47 | ||
|
|
2eeb90a944 | ||
|
|
369a4eccad | ||
|
|
088981a372 | ||
|
|
852a80fcbd | ||
|
|
63640e91fa | ||
|
|
9122fe7e10 | ||
|
|
6b8452170a | ||
|
|
cc49e65356 | ||
|
|
1632e4a343 | ||
|
|
b465102e7f | ||
|
|
98c857b927 | ||
|
|
dda53af485 | ||
|
|
c0bc4c02f8 | ||
|
|
c6630ce285 | ||
|
|
40ed7f8a40 |
65
.github/workflows/publish-image.yml
vendored
Normal file
65
.github/workflows/publish-image.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Publish Runner Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runnerVersion:
|
||||
type: string
|
||||
description: Version of the runner being installed
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Compute image version
|
||||
id: image
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const inputRunnerVersion = "${{ github.event.inputs.runnerVersion }}"
|
||||
if (inputRunnerVersion) {
|
||||
console.log(`Using input runner version ${inputRunnerVersion}`)
|
||||
core.setOutput('version', inputRunnerVersion);
|
||||
return
|
||||
}
|
||||
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
||||
console.log(`Using runner version ${runnerVersion}`)
|
||||
core.setOutput('version', runnerVersion);
|
||||
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./images
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
build-args: |
|
||||
RUNNER_VERSION=${{ steps.image.outputs.version }}
|
||||
push: true
|
||||
labels: |
|
||||
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.licenses=MIT
|
||||
49
.github/workflows/release.yml
vendored
49
.github/workflows/release.yml
vendored
@@ -660,3 +660,52 @@ jobs:
|
||||
asset_path: ${{ github.workspace }}/linux-arm64-trimmedpackages.json
|
||||
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
publish-image:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Compute image version
|
||||
id: image
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const runnerVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '')
|
||||
console.log(`Using runner version ${runnerVersion}`)
|
||||
core.setOutput('version', runnerVersion);
|
||||
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./images
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
build-args: |
|
||||
RUNNER_VERSION=${{ steps.image.outputs.version }}
|
||||
push: true
|
||||
labels: |
|
||||
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.licenses=MIT
|
||||
|
||||
@@ -22,4 +22,4 @@ Runner releases:
|
||||
|
||||
## Contribute
|
||||
|
||||
We accept contributions in the form of issues and pull requests. [Read more here](docs/contribute.md) before contributing.
|
||||
We accept contributions in the form of issues and pull requests. The runner typically requires changes across the entire system and we aim for issues in the runner to be entirely self contained and fixable here. Therefore, we will primarily handle bug issues opened in this repo and we kindly request you to create all feature and enhancement requests on the [GitHub Feedback](https://github.com/community/community/discussions/categories/actions-and-packages) page. [Read more about our guidelines here](docs/contribute.md) before contributing.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contributions
|
||||
|
||||
We welcome contributions in the form of issues and pull requests. We view the contributions and the process as the same for github and external contributors.
|
||||
We welcome contributions in the form of issues and pull requests. We view the contributions and the process as the same for github and external contributors.Please note the runner typically requires changes across the entire system and we aim for issues in the runner to be entirely self contained and fixable here. Therefore, we will primarily handle bug issues opened in this repo and we kindly request you to create all feature and enhancement requests on the [GitHub Feedback](https://github.com/community/community/discussions/categories/actions-and-packages) page.
|
||||
|
||||
> IMPORTANT: Building your own runner is critical for the dev inner loop process when contributing changes. However, only runners built and distributed by GitHub (releases) are supported in production. Be aware that workflows and orchestrations run service side with the runner being a remote process to run steps. For that reason, the service can pull the runner forward so customizations can be lost.
|
||||
|
||||
|
||||
24
images/Dockerfile
Normal file
24
images/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0 as build
|
||||
|
||||
ARG RUNNER_VERSION
|
||||
ARG RUNNER_ARCH="x64"
|
||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.1.3
|
||||
|
||||
RUN apt update -y && apt install curl unzip -y
|
||||
|
||||
WORKDIR /actions-runner
|
||||
RUN curl -f -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz \
|
||||
&& tar xzf ./runner.tar.gz \
|
||||
&& rm runner.tar.gz
|
||||
|
||||
RUN curl -f -L -o runner-container-hooks.zip https://github.com/actions/runner-container-hooks/releases/download/v${RUNNER_CONTAINER_HOOKS_VERSION}/actions-runner-hooks-k8s-${RUNNER_CONTAINER_HOOKS_VERSION}.zip \
|
||||
&& unzip ./runner-container-hooks.zip -d ./k8s \
|
||||
&& rm runner-container-hooks.zip
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0
|
||||
|
||||
ENV RUNNER_ALLOW_RUNASROOT=1
|
||||
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
||||
|
||||
WORKDIR /actions-runner
|
||||
COPY --from=build /actions-runner .
|
||||
@@ -2,7 +2,7 @@
|
||||
- Displays the error logs in dedicated sub-sections of the Initialize containers section (#2182)
|
||||
- Add generateServiceConfig option for configure command (#2226)
|
||||
- Setting debug using GitHub Action variables (#2234)
|
||||
- run.sh installs SIGINT and SIGTERM traps to gracefully stop runner (#2233, 2240)
|
||||
- run.sh installs SIGINT and SIGTERM traps to gracefully stop runner (#2233, #2240)
|
||||
|
||||
|
||||
## Bugs
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.299.1
|
||||
<Update to ./src/runnerversion when creating release>
|
||||
|
||||
@@ -13,7 +13,7 @@ set -e
|
||||
|
||||
flags_found=false
|
||||
|
||||
while getopts 's:g:n:r:u:l:d' opt; do
|
||||
while getopts 's:g:n:r:u:l:df' opt; do
|
||||
flags_found=true
|
||||
|
||||
case $opt in
|
||||
@@ -35,6 +35,9 @@ while getopts 's:g:n:r:u:l:d' opt; do
|
||||
l)
|
||||
labels=$OPTARG
|
||||
;;
|
||||
f)
|
||||
replace='true'
|
||||
;;
|
||||
d)
|
||||
disableupdate='true'
|
||||
;;
|
||||
@@ -53,7 +56,8 @@ Usage:
|
||||
-r optional name of the runner group to add the runner to, defaults to the Default group
|
||||
-u optional user svc will run as, defaults to current
|
||||
-l optional list of labels (split by comma) applied on the runner
|
||||
-d optional allow runner to remain on the current version for one month after the release of a newer version"
|
||||
-d optional allow runner to remain on the current version for one month after the release of a newer version
|
||||
-f optional replace any existing runner with the same name"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
@@ -174,7 +178,7 @@ fi
|
||||
echo
|
||||
echo "Configuring ${runner_name} @ $runner_url"
|
||||
echo "./config.sh --unattended --url $runner_url --token *** --name $runner_name ${labels:+--labels $labels} ${runner_group:+--runnergroup \"$runner_group\"} ${disableupdate:+--disableupdate}"
|
||||
sudo -E -u ${svc_user} ./config.sh --unattended --url $runner_url --token $RUNNER_TOKEN --name $runner_name ${labels:+--labels $labels} ${runner_group:+--runnergroup "$runner_group"} ${disableupdate:+--disableupdate}
|
||||
sudo -E -u ${svc_user} ./config.sh --unattended --url $runner_url --token $RUNNER_TOKEN ${replace:+--replace} --name $runner_name ${labels:+--labels $labels} ${runner_group:+--runnergroup "$runner_group"} ${disableupdate:+--disableupdate}
|
||||
|
||||
#---------------------------------------
|
||||
# Configuring as a service
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#/bin/bash
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
@@ -12,7 +12,7 @@ set -e
|
||||
#
|
||||
# Usage:
|
||||
# export RUNNER_CFG_PAT=<yourPAT>
|
||||
# ./delete.sh scope name
|
||||
# ./delete.sh <scope> [<name>]
|
||||
#
|
||||
# scope required repo (:owner/:repo) or org (:organization)
|
||||
# name optional defaults to hostname. name to delete
|
||||
@@ -26,8 +26,6 @@ set -e
|
||||
runner_scope=${1}
|
||||
runner_name=${2}
|
||||
|
||||
echo "Deleting runner ${runner_name} @ ${runner_scope}"
|
||||
|
||||
function fatal()
|
||||
{
|
||||
echo "error: $1" >&2
|
||||
@@ -35,8 +33,10 @@ function fatal()
|
||||
}
|
||||
|
||||
if [ -z "${runner_scope}" ]; then fatal "supply scope as argument 1"; fi
|
||||
if [ -z "${runner_name}" ]; then fatal "supply name as argument 2"; fi
|
||||
if [ -z "${RUNNER_CFG_PAT}" ]; then fatal "RUNNER_CFG_PAT must be set before calling"; fi
|
||||
if [ -z "${runner_name}" ]; then runner_name=`hostname`; fi
|
||||
|
||||
echo "Deleting runner ${runner_name} @ ${runner_scope}"
|
||||
|
||||
which curl || fatal "curl required. Please install in PATH with apt-get, brew, etc"
|
||||
which jq || fatal "jq required. Please install in PATH with apt-get, brew, etc"
|
||||
|
||||
@@ -48,7 +48,7 @@ runWithManualTrap() {
|
||||
trap - INT TERM
|
||||
# wait for last parts to be logged
|
||||
wait $PID
|
||||
exit 0
|
||||
exit $returnCode
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
|
||||
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
|
||||
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||
public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/. Please update the following actions to use Node.js 16: {0}";
|
||||
public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: {0}. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.";
|
||||
}
|
||||
|
||||
public static class RunnerEvent
|
||||
@@ -245,6 +245,7 @@ namespace GitHub.Runner.Common
|
||||
// Set this env var to "node12" to downgrade the node version for internal functions (e.g hashfiles). This does NOT affect the version of node actions.
|
||||
public static readonly string ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
|
||||
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION";
|
||||
public static readonly string PrintLogToStdout = "ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT";
|
||||
}
|
||||
|
||||
public static class System
|
||||
|
||||
@@ -94,6 +94,13 @@ namespace GitHub.Runner.Common
|
||||
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPreAmpersandEscape);
|
||||
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPostAmpersandEscape);
|
||||
|
||||
// Create StdoutTraceListener if ENV is set
|
||||
StdoutTraceListener stdoutTraceListener = null;
|
||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Agent.PrintLogToStdout)))
|
||||
{
|
||||
stdoutTraceListener = new StdoutTraceListener(hostType);
|
||||
}
|
||||
|
||||
// Create the trace manager.
|
||||
if (string.IsNullOrEmpty(logFile))
|
||||
{
|
||||
@@ -113,11 +120,11 @@ namespace GitHub.Runner.Common
|
||||
|
||||
// this should give us _diag folder under runner root directory
|
||||
string diagLogDirectory = Path.Combine(new DirectoryInfo(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)).Parent.FullName, Constants.Path.DiagDirectory);
|
||||
_traceManager = new TraceManager(new HostTraceListener(diagLogDirectory, hostType, logPageSize, logRetentionDays), this.SecretMasker);
|
||||
_traceManager = new TraceManager(new HostTraceListener(diagLogDirectory, hostType, logPageSize, logRetentionDays), stdoutTraceListener, this.SecretMasker);
|
||||
}
|
||||
else
|
||||
{
|
||||
_traceManager = new TraceManager(new HostTraceListener(logFile), this.SecretMasker);
|
||||
_traceManager = new TraceManager(new HostTraceListener(logFile), stdoutTraceListener, this.SecretMasker);
|
||||
}
|
||||
|
||||
_trace = GetTrace(nameof(HostContext));
|
||||
|
||||
@@ -600,8 +600,7 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
foreach (var issue in record.Issues)
|
||||
{
|
||||
String source;
|
||||
issue.Data.TryGetValue("sourcepath", out source);
|
||||
string source = issue["sourcepath"];
|
||||
Trace.Verbose($" Issue: c={issue.Category}, t={issue.Type}, s={source ?? string.Empty}, m={issue.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace GitHub.Runner.Common
|
||||
Task<TaskAgentSession> CreateAgentSessionAsync(Int32 poolId, TaskAgentSession session, CancellationToken cancellationToken);
|
||||
Task DeleteAgentMessageAsync(Int32 poolId, Int64 messageId, Guid sessionId, CancellationToken cancellationToken);
|
||||
Task DeleteAgentSessionAsync(Int32 poolId, Guid sessionId, CancellationToken cancellationToken);
|
||||
Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken);
|
||||
Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken);
|
||||
|
||||
// job request
|
||||
Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken);
|
||||
@@ -272,10 +272,10 @@ namespace GitHub.Runner.Common
|
||||
return _messageTaskAgentClient.DeleteAgentSessionAsync(poolId, sessionId, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken)
|
||||
public Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection(RunnerConnectionType.MessageQueue);
|
||||
return _messageTaskAgentClient.GetMessageAsync(poolId, sessionId, lastMessageId, status, cancellationToken: cancellationToken);
|
||||
return _messageTaskAgentClient.GetMessageAsync(poolId, sessionId, lastMessageId, status, runnerVersion, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
90
src/Runner.Common/StdoutTraceListener.cs
Normal file
90
src/Runner.Common/StdoutTraceListener.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using GitHub.Runner.Sdk;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
public sealed class StdoutTraceListener : ConsoleTraceListener
|
||||
{
|
||||
private readonly string _hostType;
|
||||
|
||||
public StdoutTraceListener(string hostType)
|
||||
{
|
||||
this._hostType = hostType;
|
||||
}
|
||||
|
||||
// Copied and modified slightly from .Net Core source code. Modification was required to make it compile.
|
||||
// There must be some TraceFilter extension class that is missing in this source code.
|
||||
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
|
||||
{
|
||||
if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message, null, null, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WriteHeader(source, eventType, id);
|
||||
WriteLine(message);
|
||||
WriteFooter(eventCache);
|
||||
}
|
||||
|
||||
internal bool IsEnabled(TraceOptions opts)
|
||||
{
|
||||
return (opts & TraceOutputOptions) != 0;
|
||||
}
|
||||
|
||||
// Altered from the original .Net Core implementation.
|
||||
private void WriteHeader(string source, TraceEventType eventType, int id)
|
||||
{
|
||||
string type = null;
|
||||
switch (eventType)
|
||||
{
|
||||
case TraceEventType.Critical:
|
||||
type = "CRIT";
|
||||
break;
|
||||
case TraceEventType.Error:
|
||||
type = "ERR ";
|
||||
break;
|
||||
case TraceEventType.Warning:
|
||||
type = "WARN";
|
||||
break;
|
||||
case TraceEventType.Information:
|
||||
type = "INFO";
|
||||
break;
|
||||
case TraceEventType.Verbose:
|
||||
type = "VERB";
|
||||
break;
|
||||
default:
|
||||
type = eventType.ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
Write(StringUtil.Format("[{0} {1:u} {2} {3}] ", _hostType.ToUpperInvariant(), DateTime.UtcNow, type, source));
|
||||
}
|
||||
|
||||
// Copied and modified slightly from .Net Core source code to make it compile. The original code
|
||||
// accesses a private indentLevel field. In this code it has been modified to use the getter/setter.
|
||||
private void WriteFooter(TraceEventCache eventCache)
|
||||
{
|
||||
if (eventCache == null)
|
||||
return;
|
||||
|
||||
IndentLevel++;
|
||||
if (IsEnabled(TraceOptions.ProcessId))
|
||||
WriteLine("ProcessId=" + eventCache.ProcessId);
|
||||
|
||||
if (IsEnabled(TraceOptions.ThreadId))
|
||||
WriteLine("ThreadId=" + eventCache.ThreadId);
|
||||
|
||||
if (IsEnabled(TraceOptions.DateTime))
|
||||
WriteLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));
|
||||
|
||||
if (IsEnabled(TraceOptions.Timestamp))
|
||||
WriteLine("Timestamp=" + eventCache.Timestamp);
|
||||
|
||||
IndentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace GitHub.Runner.Common
|
||||
string ReadSecret();
|
||||
void Write(string message, ConsoleColor? colorCode = null);
|
||||
void WriteLine();
|
||||
void WriteLine(string line, ConsoleColor? colorCode = null);
|
||||
void WriteLine(string line, ConsoleColor? colorCode = null, bool skipTracing = false);
|
||||
void WriteError(Exception ex);
|
||||
void WriteError(string line);
|
||||
void WriteSection(string message);
|
||||
@@ -116,9 +116,12 @@ namespace GitHub.Runner.Common
|
||||
|
||||
// Do not add a format string overload. Terminal messages are user facing and therefore
|
||||
// should be localized. Use the Loc method in the StringUtil class.
|
||||
public void WriteLine(string line, ConsoleColor? colorCode = null)
|
||||
public void WriteLine(string line, ConsoleColor? colorCode = null, bool skipTracing = false)
|
||||
{
|
||||
Trace.Info($"WRITE LINE: {line}");
|
||||
if (!skipTracing)
|
||||
{
|
||||
Trace.Info($"WRITE LINE: {line}");
|
||||
}
|
||||
if (!Silent)
|
||||
{
|
||||
if (colorCode != null)
|
||||
|
||||
@@ -16,21 +16,23 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Tracing> _sources = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly HostTraceListener _hostTraceListener;
|
||||
private readonly StdoutTraceListener _stdoutTraceListener;
|
||||
private TraceSetting _traceSetting;
|
||||
private ISecretMasker _secretMasker;
|
||||
|
||||
public TraceManager(HostTraceListener traceListener, ISecretMasker secretMasker)
|
||||
: this(traceListener, new TraceSetting(), secretMasker)
|
||||
public TraceManager(HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener, ISecretMasker secretMasker)
|
||||
: this(traceListener, stdoutTraceListener, new TraceSetting(), secretMasker)
|
||||
{
|
||||
}
|
||||
|
||||
public TraceManager(HostTraceListener traceListener, TraceSetting traceSetting, ISecretMasker secretMasker)
|
||||
public TraceManager(HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener, TraceSetting traceSetting, ISecretMasker secretMasker)
|
||||
{
|
||||
// Validate and store params.
|
||||
ArgUtil.NotNull(traceListener, nameof(traceListener));
|
||||
ArgUtil.NotNull(traceSetting, nameof(traceSetting));
|
||||
ArgUtil.NotNull(secretMasker, nameof(secretMasker));
|
||||
_hostTraceListener = traceListener;
|
||||
_stdoutTraceListener = stdoutTraceListener;
|
||||
_traceSetting = traceSetting;
|
||||
_secretMasker = secretMasker;
|
||||
|
||||
@@ -81,7 +83,7 @@ namespace GitHub.Runner.Common
|
||||
Level = sourceTraceLevel.ToSourceLevels()
|
||||
};
|
||||
}
|
||||
return new Tracing(name, _secretMasker, sourceSwitch, _hostTraceListener);
|
||||
return new Tracing(name, _secretMasker, sourceSwitch, _hostTraceListener, _stdoutTraceListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace GitHub.Runner.Common
|
||||
private ISecretMasker _secretMasker;
|
||||
private TraceSource _traceSource;
|
||||
|
||||
public Tracing(string name, ISecretMasker secretMasker, SourceSwitch sourceSwitch, HostTraceListener traceListener)
|
||||
public Tracing(string name, ISecretMasker secretMasker, SourceSwitch sourceSwitch, HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener = null)
|
||||
{
|
||||
ArgUtil.NotNull(secretMasker, nameof(secretMasker));
|
||||
_secretMasker = secretMasker;
|
||||
@@ -27,6 +27,10 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
|
||||
_traceSource.Listeners.Add(traceListener);
|
||||
if (stdoutTraceListener != null)
|
||||
{
|
||||
_traceSource.Listeners.Add(stdoutTraceListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void Info(string message)
|
||||
|
||||
@@ -400,6 +400,7 @@ namespace GitHub.Runner.Listener
|
||||
Task<int> workerProcessTask = null;
|
||||
object _outputLock = new();
|
||||
List<string> workerOutput = new();
|
||||
bool printToStdout = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Agent.PrintLogToStdout));
|
||||
using (var processChannel = HostContext.CreateService<IProcessChannel>())
|
||||
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||
{
|
||||
@@ -421,7 +422,15 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
lock (_outputLock)
|
||||
{
|
||||
workerOutput.Add(stdout.Data);
|
||||
if (!stdout.Data.StartsWith("[WORKER"))
|
||||
{
|
||||
workerOutput.Add(stdout.Data);
|
||||
}
|
||||
|
||||
if (printToStdout)
|
||||
{
|
||||
term.WriteLine(stdout.Data, skipTracing: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -503,7 +512,7 @@ namespace GitHub.Runner.Listener
|
||||
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
||||
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
||||
|
||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
|
||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId}");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1005,7 +1014,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
|
||||
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage };
|
||||
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||
unhandledExceptionIssue[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||
jobRecord.ErrorCount++;
|
||||
jobRecord.Issues.Add(unhandledExceptionIssue);
|
||||
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
||||
|
||||
@@ -211,6 +211,7 @@ namespace GitHub.Runner.Listener
|
||||
_session.SessionId,
|
||||
_lastMessageId,
|
||||
runnerStatus,
|
||||
BuildConstants.RunnerPackage.Version,
|
||||
_getMessagesTokenSource.Token);
|
||||
|
||||
// Decrypt the message body if the session is using encryption
|
||||
|
||||
@@ -430,12 +430,22 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
message = await getNextMessage; //get next message
|
||||
HostContext.WritePerfCounter($"MessageReceived_{message.MessageType}");
|
||||
if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(message.MessageType, RunnerRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (autoUpdateInProgress == false)
|
||||
{
|
||||
autoUpdateInProgress = true;
|
||||
var runnerUpdateMessage = JsonUtility.FromString<AgentRefreshMessage>(message.Body);
|
||||
AgentRefreshMessage runnerUpdateMessage = null;
|
||||
if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
runnerUpdateMessage = JsonUtility.FromString<AgentRefreshMessage>(message.Body);
|
||||
}
|
||||
else
|
||||
{
|
||||
var brokerRunnerUpdateMessage = JsonUtility.FromString<RunnerRefreshMessage>(message.Body);
|
||||
runnerUpdateMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds));
|
||||
}
|
||||
#if DEBUG
|
||||
// Can mock the update for testing
|
||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE")))
|
||||
|
||||
@@ -270,12 +270,9 @@ namespace GitHub.Runner.Worker
|
||||
if (string.Equals(blocked, envName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Log Telemetry and let user know they shouldn't do this
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Error,
|
||||
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
|
||||
var message = $"Can't update {blocked} environment variable using ::set-env:: command.";
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, $"{Constants.Runner.UnsupportedCommand}_{envName}");
|
||||
var issue = context.CreateIssue(IssueType.Error, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
|
||||
return;
|
||||
@@ -309,12 +306,9 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
|
||||
{
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Warning,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command);
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
@@ -344,12 +338,9 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
|
||||
{
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Warning,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command);
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
@@ -618,16 +609,11 @@ namespace GitHub.Runner.Worker
|
||||
context.Debug("Enhanced Annotations not enabled on the server. The 'title', 'end_line', and 'end_column' fields are unsupported.");
|
||||
}
|
||||
|
||||
Issue issue = new()
|
||||
{
|
||||
Category = "General",
|
||||
Type = this.Type,
|
||||
Message = command.Data
|
||||
};
|
||||
var issueCategory = "General";
|
||||
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
issue.Category = "Code";
|
||||
issueCategory = "Code";
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
@@ -658,14 +644,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in command.Properties)
|
||||
{
|
||||
if (!string.Equals(property.Key, Constants.Runner.InternalTelemetryIssueDataKey, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
issue.Data[property.Key] = property.Value;
|
||||
}
|
||||
}
|
||||
string keyToExclude = Constants.Runner.InternalTelemetryIssueDataKey;
|
||||
var filteredDictionaryEntries = command.Properties
|
||||
.Where(kvp => !string.Equals(kvp.Key, keyToExclude, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
var metadata = new IssueMetadata(issueCategory, false, null, filteredDictionaryEntries);
|
||||
var issue = context.CreateIssue(this.Type, command.Data, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.Compression; // required for OS_WINDOWS
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.IO.Compression;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
@@ -16,6 +16,7 @@ using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Newtonsoft.Json;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
@@ -90,7 +91,8 @@ namespace GitHub.Runner.Worker
|
||||
void SetGitHubContext(string name, string value);
|
||||
void SetOutput(string name, string value, out string reference);
|
||||
void SetTimeout(TimeSpan? timeout);
|
||||
void AddIssue(Issue issue, string message = null);
|
||||
IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog);
|
||||
void AddIssue(IReadOnlyIssue issue);
|
||||
void Progress(int percentage, string currentOperation = null);
|
||||
void UpdateDetailTimelineRecord(TimelineRecord record);
|
||||
|
||||
@@ -124,8 +126,10 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private readonly TimelineRecord _record = new();
|
||||
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
||||
private readonly List<IReadOnlyIssue> _embeddedIssueCollector;
|
||||
private readonly object _loggerLock = new();
|
||||
private readonly object _matchersLock = new();
|
||||
private readonly ExecutionContext _parentExecutionContext;
|
||||
|
||||
private event OnMatcherChanged _onMatcherChanged;
|
||||
|
||||
@@ -133,7 +137,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private IPagingLogger _logger;
|
||||
private IJobServerQueue _jobServerQueue;
|
||||
private ExecutionContext _parentExecutionContext;
|
||||
|
||||
private Guid _mainTimelineId;
|
||||
private Guid _detailTimelineId;
|
||||
@@ -147,6 +150,29 @@ namespace GitHub.Runner.Worker
|
||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||
private bool _stepTelemetryPublished = false;
|
||||
|
||||
public ExecutionContext()
|
||||
: this(null, false)
|
||||
{
|
||||
}
|
||||
|
||||
private ExecutionContext(ExecutionContext parent, bool embedded)
|
||||
{
|
||||
if (embedded)
|
||||
{
|
||||
ArgUtil.NotNull(parent, nameof(parent));
|
||||
}
|
||||
|
||||
_parentExecutionContext = parent;
|
||||
this.IsEmbedded = embedded;
|
||||
this.StepTelemetry = new ActionsStepTelemetry
|
||||
{
|
||||
IsEmbedded = embedded
|
||||
};
|
||||
|
||||
//Embedded Execution Contexts pseudo-inherit their parent's embeddedIssueCollector.
|
||||
_embeddedIssueCollector = embedded ? parent._embeddedIssueCollector : new();
|
||||
}
|
||||
|
||||
public Guid Id => _record.Id;
|
||||
public Guid EmbeddedId { get; private set; }
|
||||
public string ScopeName { get; private set; }
|
||||
@@ -159,7 +185,7 @@ namespace GitHub.Runner.Worker
|
||||
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
||||
|
||||
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
||||
public ActionsStepTelemetry StepTelemetry { get; } = new ActionsStepTelemetry();
|
||||
public ActionsStepTelemetry StepTelemetry { get; private init; }
|
||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||
|
||||
@@ -184,7 +210,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// An embedded execution context shares the same record ID, record name, and logger
|
||||
// as its enclosing execution context.
|
||||
public bool IsEmbedded { get; private set; }
|
||||
public bool IsEmbedded { get; private init; }
|
||||
|
||||
public TaskResult? Result
|
||||
{
|
||||
@@ -319,7 +345,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
Trace.Entering();
|
||||
|
||||
var child = new ExecutionContext();
|
||||
var child = new ExecutionContext(this, isEmbedded);
|
||||
child.Initialize(HostContext);
|
||||
child.Global = Global;
|
||||
child.ScopeName = scopeName;
|
||||
@@ -344,7 +370,6 @@ namespace GitHub.Runner.Worker
|
||||
child.ExpressionFunctions.Add(item);
|
||||
}
|
||||
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||
child._parentExecutionContext = this;
|
||||
child.EchoOnActionCommand = EchoOnActionCommand;
|
||||
|
||||
if (recordOrder != null)
|
||||
@@ -365,11 +390,9 @@ namespace GitHub.Runner.Worker
|
||||
child._logger.Setup(_mainTimelineId, recordId);
|
||||
}
|
||||
|
||||
child.IsEmbedded = isEmbedded;
|
||||
child.StepTelemetry.StepId = recordId;
|
||||
child.StepTelemetry.Stage = stage.ToString();
|
||||
child.StepTelemetry.IsEmbedded = isEmbedded;
|
||||
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
|
||||
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName();
|
||||
|
||||
return child;
|
||||
}
|
||||
@@ -411,13 +434,24 @@ namespace GitHub.Runner.Worker
|
||||
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
|
||||
}
|
||||
|
||||
DateTime now = DateTime.UtcNow;
|
||||
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
|
||||
_record.ResultCode = resultCode ?? _record.ResultCode;
|
||||
_record.FinishTime = DateTime.UtcNow;
|
||||
_record.FinishTime = now;
|
||||
_record.PercentComplete = 100;
|
||||
_record.Result = _record.Result ?? TaskResult.Succeeded;
|
||||
_record.State = TimelineRecordState.Completed;
|
||||
|
||||
// Before our main timeline's final QueueTimelineRecordUpdate,
|
||||
// inject any issues collected by embedded ExecutionContexts.
|
||||
if (!this.IsEmbedded)
|
||||
{
|
||||
foreach (var issue in _embeddedIssueCollector)
|
||||
{
|
||||
AddIssue(issue);
|
||||
}
|
||||
}
|
||||
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
|
||||
// complete all detail timeline records.
|
||||
@@ -425,7 +459,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
foreach (var record in _detailRecords)
|
||||
{
|
||||
record.Value.FinishTime = record.Value.FinishTime ?? DateTime.UtcNow;
|
||||
record.Value.FinishTime = record.Value.FinishTime ?? now;
|
||||
record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
|
||||
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
|
||||
record.Value.State = TimelineRecordState.Completed;
|
||||
@@ -545,76 +579,89 @@ namespace GitHub.Runner.Worker
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
// This is not thread safe, the caller need to take lock before calling issue()
|
||||
public void AddIssue(Issue issue, string logMessage = null)
|
||||
// This is not thread safe, the caller needs to take lock before calling issue()
|
||||
public IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog)
|
||||
{
|
||||
string refinedMessage = PrimitiveExtensions.TrimExcess(HostContext.SecretMasker.MaskSecrets(rawMessage), _maxIssueMessageLength);
|
||||
|
||||
var result = new Issue() {
|
||||
Type = issueType,
|
||||
Message = refinedMessage,
|
||||
};
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
result.Category = metadata.Category;
|
||||
result.IsInfrastructureIssue = metadata.IsInfrastructureIssue;
|
||||
foreach (var kvp in metadata.Data)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// It's important to keep track of the step number (key:stepNumber) and the line number (key:logFileLineNumber) of every issue that gets logged.
|
||||
// Actions UI from the run summary page use both values to easily link to an exact locations in logs where annotations originate from.
|
||||
if (_record.Order != null)
|
||||
{
|
||||
result["stepNumber"] = _record.Order.ToString();
|
||||
}
|
||||
|
||||
string wellKnownTag = null;
|
||||
Int32? previousCountForIssueType = null;
|
||||
switch (issueType)
|
||||
{
|
||||
case IssueType.Error:
|
||||
wellKnownTag = WellKnownTags.Error;
|
||||
previousCountForIssueType = _record.ErrorCount++;
|
||||
break;
|
||||
case IssueType.Warning:
|
||||
wellKnownTag = WellKnownTags.Warning;
|
||||
previousCountForIssueType = _record.WarningCount++;
|
||||
break;
|
||||
case IssueType.Notice:
|
||||
wellKnownTag = WellKnownTags.Notice;
|
||||
previousCountForIssueType = _record.NoticeCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(wellKnownTag))
|
||||
{
|
||||
if (writeToLog)
|
||||
{
|
||||
//Note that ::Write() has it's own secret masking logic
|
||||
string logText = metadata?.LogMessageOverride ?? result.Message;
|
||||
if (!string.IsNullOrEmpty(logText))
|
||||
{
|
||||
long logLineNumber = Write(wellKnownTag, logText);
|
||||
result["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
}
|
||||
if (previousCountForIssueType.GetValueOrDefault(0) < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// This is not thread safe, the caller needs to take lock before calling issue()
|
||||
public void AddIssue(IReadOnlyIssue issue)
|
||||
{
|
||||
ArgUtil.NotNull(issue, nameof(issue));
|
||||
|
||||
if (string.IsNullOrEmpty(logMessage))
|
||||
// Embedded ExecutionContexts (a.k.a. Composite actions) should never upload a timeline record to the server.
|
||||
// Instead, we store processed issues on a shared (psuedo-inherited) list (belonging to the closest
|
||||
// non-embedded ancestor ExecutionContext) so that they can be processed when that ancestor completes.
|
||||
if (this.IsEmbedded)
|
||||
{
|
||||
logMessage = issue.Message;
|
||||
_embeddedIssueCollector.Add(issue);
|
||||
}
|
||||
|
||||
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
|
||||
if (issue.Message.Length > _maxIssueMessageLength)
|
||||
else
|
||||
{
|
||||
issue.Message = issue.Message[.._maxIssueMessageLength];
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
// Tracking the line number (logFileLineNumber) and step number (stepNumber) for each issue that gets created
|
||||
// Actions UI from the run summary page use both values to easily link to an exact locations in logs where annotations originate from
|
||||
if (_record.Order != null)
|
||||
{
|
||||
issue.Data["stepNumber"] = _record.Order.ToString();
|
||||
}
|
||||
|
||||
if (issue.Type == IssueType.Error)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Error, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.ErrorCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.ErrorCount++;
|
||||
}
|
||||
else if (issue.Type == IssueType.Warning)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Warning, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.WarningCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.WarningCount++;
|
||||
}
|
||||
else if (issue.Type == IssueType.Notice)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Notice, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.NoticeCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.NoticeCount++;
|
||||
}
|
||||
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
public void UpdateDetailTimelineRecord(TimelineRecord record)
|
||||
@@ -975,16 +1022,7 @@ namespace GitHub.Runner.Worker
|
||||
if ((issue.Type == IssueType.Error || issue.Type == IssueType.Warning) &&
|
||||
!string.IsNullOrEmpty(issue.Message))
|
||||
{
|
||||
string issueTelemetry;
|
||||
if (issue.Message.Length > _maxIssueMessageLengthInTelemetry)
|
||||
{
|
||||
issueTelemetry = $"{issue.Message[.._maxIssueMessageLengthInTelemetry]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
issueTelemetry = issue.Message;
|
||||
}
|
||||
|
||||
string issueTelemetry = PrimitiveExtensions.TrimExcess(issue.Message, _maxIssueMessageLengthInTelemetry);
|
||||
StepTelemetry.ErrorMessages.Add(issueTelemetry);
|
||||
|
||||
// Only send over the first 3 issues to avoid sending too much data.
|
||||
@@ -1158,19 +1196,23 @@ namespace GitHub.Runner.Worker
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void Error(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message });
|
||||
var issue = context.CreateIssue(IssueType.Error, message, null, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void InfrastructureError(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
|
||||
var metadata = new IssueMetadata(null, true, null, Enumerable.Empty<KeyValuePair<string, string>>());
|
||||
var issue = context.CreateIssue(IssueType.Error, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void Warning(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Warning, Message = message });
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, null, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace GitHub.Runner.Worker
|
||||
"action_repository",
|
||||
"action",
|
||||
"actor",
|
||||
"actor_id",
|
||||
"api_url",
|
||||
"base_ref",
|
||||
"env",
|
||||
@@ -27,8 +28,10 @@ namespace GitHub.Runner.Worker
|
||||
"ref_protected",
|
||||
"ref_type",
|
||||
"ref",
|
||||
"repository_owner",
|
||||
"repository",
|
||||
"repository_id",
|
||||
"repository_owner",
|
||||
"repository_owner_id",
|
||||
"retention_days",
|
||||
"run_attempt",
|
||||
"run_id",
|
||||
@@ -39,7 +42,9 @@ namespace GitHub.Runner.Worker
|
||||
"step_summary",
|
||||
"triggering_actor",
|
||||
"workflow",
|
||||
"workspace",
|
||||
"workflow_ref",
|
||||
"workflow_sha",
|
||||
"workspace"
|
||||
};
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> GetRuntimeEnvironmentVariables()
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
// Set action_status to the success of the current composite action
|
||||
var actionResult = ExecutionContext.Result?.ToActionResult() ?? ActionResult.Success;
|
||||
step.ExecutionContext.SetGitHubContext("action_status", actionResult.ToString());
|
||||
step.ExecutionContext.SetGitHubContext("action_status", actionResult.ToString().ToLowerInvariant());
|
||||
|
||||
// Initialize env context
|
||||
Trace.Info("Initialize Env context for embedded step");
|
||||
@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Evaluation error
|
||||
Trace.Info("Caught exception from expression for embedded step.env");
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Complete(TaskResult.Failed);
|
||||
SetStepConclusion(step, TaskResult.Failed);
|
||||
}
|
||||
|
||||
// Register Callback
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
@@ -97,7 +98,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var matchers = _matchers;
|
||||
|
||||
// Strip color codes
|
||||
var stripped = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line;
|
||||
var refinedLine = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line;
|
||||
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
@@ -107,8 +108,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Match
|
||||
try
|
||||
{
|
||||
match = matcher.Match(stripped);
|
||||
|
||||
match = matcher.Match(refinedLine);
|
||||
break;
|
||||
}
|
||||
catch (RegexMatchTimeoutException ex)
|
||||
@@ -116,7 +116,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
if (attempt < _maxAttempts)
|
||||
{
|
||||
// Debug
|
||||
_executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{stripped}'. Exception: {ex.ToString()}");
|
||||
_executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{refinedLine}'. Exception: {ex}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,12 +139,10 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
// Convert to issue
|
||||
var issue = ConvertToIssue(match);
|
||||
|
||||
if (issue != null)
|
||||
{
|
||||
// Log issue
|
||||
_executionContext.AddIssue(issue, stripped);
|
||||
|
||||
_executionContext.AddIssue(issue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +194,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
private DTWebApi.Issue ConvertToIssue(IssueMatch match)
|
||||
private DTWebApi.IReadOnlyIssue ConvertToIssue(IssueMatch match)
|
||||
{
|
||||
// Validate the message
|
||||
if (string.IsNullOrWhiteSpace(match.Message))
|
||||
@@ -225,18 +223,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
return null;
|
||||
}
|
||||
|
||||
var issue = new DTWebApi.Issue
|
||||
{
|
||||
Message = match.Message,
|
||||
Type = issueType,
|
||||
};
|
||||
var issueData = new Dictionary<string, string>();
|
||||
|
||||
// Line
|
||||
if (!string.IsNullOrEmpty(match.Line))
|
||||
{
|
||||
if (int.TryParse(match.Line, NumberStyles.None, CultureInfo.InvariantCulture, out var line))
|
||||
{
|
||||
issue.Data["line"] = line.ToString(CultureInfo.InvariantCulture);
|
||||
issueData["line"] = line.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -249,7 +243,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
if (int.TryParse(match.Column, NumberStyles.None, CultureInfo.InvariantCulture, out var column))
|
||||
{
|
||||
issue.Data["col"] = column.ToString(CultureInfo.InvariantCulture);
|
||||
issueData["col"] = column.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -260,7 +254,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Code
|
||||
if (!string.IsNullOrWhiteSpace(match.Code))
|
||||
{
|
||||
issue.Data["code"] = match.Code.Trim();
|
||||
issueData["code"] = match.Code.Trim();
|
||||
}
|
||||
|
||||
// File
|
||||
@@ -312,7 +306,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var relativePath = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar);
|
||||
|
||||
// Prefer `/` on all platforms
|
||||
issue.Data["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
issueData["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -327,9 +321,11 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex.ToString()}");
|
||||
_executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex}");
|
||||
}
|
||||
|
||||
var metadata = new IssueMetadata(null, false, match.SourceText, issueData);
|
||||
var issue = _executionContext.CreateIssue(issueType, match.Message, metadata, true);
|
||||
return issue;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,31 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public delegate void OnMatcherChanged(object sender, MatcherChangedEventArgs e);
|
||||
|
||||
|
||||
public sealed class IssueMetadata
|
||||
{
|
||||
public IssueMetadata(string key, string value)
|
||||
: this(null, false, null, new []{ KeyValuePair.Create(key, value) })
|
||||
{
|
||||
}
|
||||
|
||||
public IssueMetadata(string category, bool infrastructureIssue, string logMessageOverride, IEnumerable<KeyValuePair<string, string>> data)
|
||||
{
|
||||
this.Category = category;
|
||||
this.IsInfrastructureIssue = infrastructureIssue;
|
||||
this.LogMessageOverride = logMessageOverride;
|
||||
|
||||
// Close-over the incoming IEnumerable to force immediate evaluation.
|
||||
var empty = Enumerable.Empty<KeyValuePair<string, string>>();
|
||||
this.Data = new Dictionary<string, string>(data ?? empty, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public readonly string Category;
|
||||
public readonly bool IsInfrastructureIssue;
|
||||
public readonly string LogMessageOverride;
|
||||
public readonly IEnumerable<KeyValuePair<string, string>> Data;
|
||||
}
|
||||
|
||||
public sealed class MatcherChangedEventArgs : EventArgs
|
||||
{
|
||||
public MatcherChangedEventArgs(IssueMatcherConfig config)
|
||||
@@ -69,7 +94,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (regexMatch.Success)
|
||||
{
|
||||
return new IssueMatch(null, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
return new IssueMatch(line, null, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -110,13 +135,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
// Return
|
||||
return new IssueMatch(runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
return new IssueMatch(line, runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
}
|
||||
// Not the last pattern
|
||||
else
|
||||
{
|
||||
// Store the match
|
||||
_state[i] = new IssueMatch(runningMatch, pattern, regexMatch.Groups);
|
||||
_state[i] = new IssueMatch(line, runningMatch, pattern, regexMatch.Groups);
|
||||
}
|
||||
}
|
||||
// Not matched
|
||||
@@ -184,8 +209,9 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public sealed class IssueMatch
|
||||
{
|
||||
public IssueMatch(IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
|
||||
public IssueMatch(string sourceText, IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
|
||||
{
|
||||
SourceText = sourceText;
|
||||
File = runningMatch?.File ?? GetValue(groups, pattern.File);
|
||||
Line = runningMatch?.Line ?? GetValue(groups, pattern.Line);
|
||||
Column = runningMatch?.Column ?? GetValue(groups, pattern.Column);
|
||||
@@ -200,6 +226,8 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public string SourceText { get; }
|
||||
|
||||
public string File { get; }
|
||||
|
||||
public string Line { get; }
|
||||
|
||||
@@ -660,8 +660,9 @@ namespace GitHub.Runner.Worker
|
||||
var freeSpaceInMB = driveInfo.AvailableFreeSpace / 1024 / 1024;
|
||||
if (freeSpaceInMB < lowDiskSpaceThreshold)
|
||||
{
|
||||
var issue = new Issue() { Type = IssueType.Warning, Message = $"You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: {freeSpaceInMB} MB" };
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.LowDiskSpace;
|
||||
var message = $"You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: {freeSpaceInMB} MB";
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.LowDiskSpace);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ namespace GitHub.Runner.Worker
|
||||
default:
|
||||
throw new ArgumentException(HostContext.RunnerShutdownReason.ToString(), nameof(HostContext.RunnerShutdownReason));
|
||||
}
|
||||
jobContext.AddIssue(new Issue() { Type = IssueType.Error, Message = errorMessage });
|
||||
var issue = jobContext.CreateIssue(IssueType.Error, errorMessage, null, true);
|
||||
jobContext.AddIssue(issue);
|
||||
});
|
||||
|
||||
// Validate directory permissions.
|
||||
|
||||
@@ -27,6 +27,18 @@ namespace GitHub.Services.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimExcess(string text, int maxLength)
|
||||
{
|
||||
string result = text;
|
||||
|
||||
if (!string.IsNullOrEmpty(result) && result.Length > maxLength)
|
||||
{
|
||||
result = result.Substring(0, maxLength);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToBase64StringNoPaddingFromString(string utf8String)
|
||||
{
|
||||
return ToBase64StringNoPadding(Encoding.UTF8.GetBytes(utf8String));
|
||||
|
||||
@@ -450,6 +450,8 @@ namespace GitHub.DistributedTask.WebApi
|
||||
/// <param name="poolId"></param>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <param name="lastMessageId"></param>
|
||||
/// <param name="status"></param>
|
||||
/// <param name="runnerVersion"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
@@ -458,6 +460,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
Guid sessionId,
|
||||
long? lastMessageId = null,
|
||||
TaskAgentStatus? status = null,
|
||||
string runnerVersion = null,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -475,12 +478,16 @@ namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
queryParams.Add("status", status.Value.ToString());
|
||||
}
|
||||
if (runnerVersion != null)
|
||||
{
|
||||
queryParams.Add("runnerVersion", runnerVersion);
|
||||
}
|
||||
|
||||
return SendAsync<TaskAgentMessage>(
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.Services.Common;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
public interface IReadOnlyIssue
|
||||
{
|
||||
IssueType Type { get; }
|
||||
string Category { get; }
|
||||
string Message { get; }
|
||||
bool? IsInfrastructureIssue { get; }
|
||||
string this[string key] { get; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class Issue
|
||||
public class Issue : IReadOnlyIssue
|
||||
{
|
||||
|
||||
public Issue()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
private Issue(Issue issueToBeCloned)
|
||||
private Issue(Issue original)
|
||||
{
|
||||
this.Type = issueToBeCloned.Type;
|
||||
this.Category = issueToBeCloned.Category;
|
||||
this.Message = issueToBeCloned.Message;
|
||||
this.IsInfrastructureIssue = issueToBeCloned.IsInfrastructureIssue;
|
||||
|
||||
if (issueToBeCloned.m_data != null)
|
||||
this.EnsureInitialized();
|
||||
if (original != null)
|
||||
{
|
||||
foreach (var item in issueToBeCloned.m_data)
|
||||
{
|
||||
this.Data.Add(item);
|
||||
}
|
||||
this.Type = original.Type;
|
||||
this.Category = original.Category;
|
||||
this.Message = original.Message;
|
||||
this.IsInfrastructureIssue = original.IsInfrastructureIssue;
|
||||
m_data.AddRange(original.m_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +44,14 @@ namespace GitHub.DistributedTask.WebApi
|
||||
}
|
||||
|
||||
[DataMember(Order = 2)]
|
||||
public String Category
|
||||
public string Category
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[DataMember(Order = 3)]
|
||||
public String Message
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -56,15 +64,16 @@ namespace GitHub.DistributedTask.WebApi
|
||||
set;
|
||||
}
|
||||
|
||||
public IDictionary<String, String> Data
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_data == null)
|
||||
{
|
||||
m_data = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
return m_data;
|
||||
m_data.TryGetValue(key, out string result);
|
||||
return result;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +86,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
private void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
SerializationHelper.Copy(ref m_serializedData, ref m_data, StringComparer.OrdinalIgnoreCase, true);
|
||||
this.EnsureInitialized();
|
||||
}
|
||||
|
||||
[OnSerializing]
|
||||
@@ -91,9 +101,21 @@ namespace GitHub.DistributedTask.WebApi
|
||||
m_serializedData = null;
|
||||
}
|
||||
|
||||
[DataMember(Name = "Data", EmitDefaultValue = false, Order = 4)]
|
||||
private IDictionary<String, String> m_serializedData;
|
||||
/// <summary>
|
||||
/// DataContractSerializer bypasses all constructor logic and inline initialization!
|
||||
/// This method takes the place of a workhorse constructor for baseline initialization.
|
||||
/// The expectation is for this logic to be accessible to constructors and also to the OnDeserialized helper.
|
||||
/// </summary>
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
//Note that ?? is a short-circuiting operator.
|
||||
m_data = m_data ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[DataMember(Name = "Data", EmitDefaultValue = false, Order = 4)]
|
||||
private IDictionary<string, string> m_serializedData;
|
||||
|
||||
private IDictionary<string, string> m_data;
|
||||
|
||||
private IDictionary<String, String> m_data;
|
||||
}
|
||||
}
|
||||
|
||||
49
src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs
Normal file
49
src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public sealed class RunnerRefreshMessage
|
||||
{
|
||||
public static readonly String MessageType = "RunnerRefresh";
|
||||
|
||||
[JsonConstructor]
|
||||
internal RunnerRefreshMessage()
|
||||
{
|
||||
}
|
||||
|
||||
public RunnerRefreshMessage(
|
||||
Int32 runnerId,
|
||||
String targetVersion,
|
||||
int? timeoutInSeconds = null)
|
||||
{
|
||||
this.RunnerId = runnerId;
|
||||
this.TimeoutInSeconds = timeoutInSeconds ?? TimeSpan.FromMinutes(60).Seconds;
|
||||
this.TargetVersion = targetVersion;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public Int32 RunnerId
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public int TimeoutInSeconds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public String TargetVersion
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,69 +10,76 @@ namespace GitHub.DistributedTask.WebApi
|
||||
public sealed class TimelineRecord
|
||||
{
|
||||
public TimelineRecord()
|
||||
: this(null)
|
||||
{
|
||||
this.Attempt = 1;
|
||||
}
|
||||
|
||||
private TimelineRecord(TimelineRecord recordToBeCloned)
|
||||
{
|
||||
this.Attempt = recordToBeCloned.Attempt;
|
||||
this.ChangeId = recordToBeCloned.ChangeId;
|
||||
this.CurrentOperation = recordToBeCloned.CurrentOperation;
|
||||
this.FinishTime = recordToBeCloned.FinishTime;
|
||||
this.Id = recordToBeCloned.Id;
|
||||
this.Identifier = recordToBeCloned.Identifier;
|
||||
this.LastModified = recordToBeCloned.LastModified;
|
||||
this.Location = recordToBeCloned.Location;
|
||||
this.Name = recordToBeCloned.Name;
|
||||
this.Order = recordToBeCloned.Order;
|
||||
this.ParentId = recordToBeCloned.ParentId;
|
||||
this.PercentComplete = recordToBeCloned.PercentComplete;
|
||||
this.RecordType = recordToBeCloned.RecordType;
|
||||
this.Result = recordToBeCloned.Result;
|
||||
this.ResultCode = recordToBeCloned.ResultCode;
|
||||
this.StartTime = recordToBeCloned.StartTime;
|
||||
this.State = recordToBeCloned.State;
|
||||
this.TimelineId = recordToBeCloned.TimelineId;
|
||||
this.WorkerName = recordToBeCloned.WorkerName;
|
||||
this.RefName = recordToBeCloned.RefName;
|
||||
this.ErrorCount = recordToBeCloned.ErrorCount;
|
||||
this.WarningCount = recordToBeCloned.WarningCount;
|
||||
this.NoticeCount = recordToBeCloned.NoticeCount;
|
||||
this.AgentPlatform = recordToBeCloned.AgentPlatform;
|
||||
|
||||
if (recordToBeCloned.Log != null)
|
||||
this.EnsureInitialized();
|
||||
if (recordToBeCloned != null)
|
||||
{
|
||||
this.Log = new TaskLogReference
|
||||
this.Attempt = recordToBeCloned.Attempt;
|
||||
this.ChangeId = recordToBeCloned.ChangeId;
|
||||
this.CurrentOperation = recordToBeCloned.CurrentOperation;
|
||||
this.FinishTime = recordToBeCloned.FinishTime;
|
||||
this.Id = recordToBeCloned.Id;
|
||||
this.Identifier = recordToBeCloned.Identifier;
|
||||
this.LastModified = recordToBeCloned.LastModified;
|
||||
this.Location = recordToBeCloned.Location;
|
||||
this.Name = recordToBeCloned.Name;
|
||||
this.Order = recordToBeCloned.Order;
|
||||
this.ParentId = recordToBeCloned.ParentId;
|
||||
this.PercentComplete = recordToBeCloned.PercentComplete;
|
||||
this.RecordType = recordToBeCloned.RecordType;
|
||||
this.Result = recordToBeCloned.Result;
|
||||
this.ResultCode = recordToBeCloned.ResultCode;
|
||||
this.StartTime = recordToBeCloned.StartTime;
|
||||
this.State = recordToBeCloned.State;
|
||||
this.TimelineId = recordToBeCloned.TimelineId;
|
||||
this.WorkerName = recordToBeCloned.WorkerName;
|
||||
this.RefName = recordToBeCloned.RefName;
|
||||
this.ErrorCount = recordToBeCloned.ErrorCount;
|
||||
this.WarningCount = recordToBeCloned.WarningCount;
|
||||
this.NoticeCount = recordToBeCloned.NoticeCount;
|
||||
this.AgentPlatform = recordToBeCloned.AgentPlatform;
|
||||
|
||||
if (recordToBeCloned.Log != null)
|
||||
{
|
||||
Id = recordToBeCloned.Log.Id,
|
||||
Location = recordToBeCloned.Log.Location,
|
||||
};
|
||||
}
|
||||
this.Log = new TaskLogReference
|
||||
{
|
||||
Id = recordToBeCloned.Log.Id,
|
||||
Location = recordToBeCloned.Log.Location,
|
||||
};
|
||||
}
|
||||
|
||||
if (recordToBeCloned.Details != null)
|
||||
{
|
||||
this.Details = new TimelineReference
|
||||
if (recordToBeCloned.Details != null)
|
||||
{
|
||||
ChangeId = recordToBeCloned.Details.ChangeId,
|
||||
Id = recordToBeCloned.Details.Id,
|
||||
Location = recordToBeCloned.Details.Location,
|
||||
};
|
||||
}
|
||||
this.Details = new TimelineReference
|
||||
{
|
||||
ChangeId = recordToBeCloned.Details.ChangeId,
|
||||
Id = recordToBeCloned.Details.Id,
|
||||
Location = recordToBeCloned.Details.Location,
|
||||
};
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_issues?.Count> 0)
|
||||
{
|
||||
this.Issues.AddRange(recordToBeCloned.Issues.Select(i => i.Clone()));
|
||||
}
|
||||
if (recordToBeCloned.m_issues?.Count > 0)
|
||||
{
|
||||
m_issues.AddRange(recordToBeCloned.m_issues.Select(i => i.Clone()));
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_previousAttempts?.Count > 0)
|
||||
{
|
||||
this.PreviousAttempts.AddRange(recordToBeCloned.PreviousAttempts);
|
||||
}
|
||||
if (recordToBeCloned.m_previousAttempts?.Count > 0)
|
||||
{
|
||||
m_previousAttempts.AddRange(recordToBeCloned.m_previousAttempts);
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_variables?.Count > 0)
|
||||
{
|
||||
this.m_variables = recordToBeCloned.Variables.ToDictionary(k => k.Key, v => v.Value.Clone());
|
||||
if (recordToBeCloned.m_variables?.Count > 0)
|
||||
{
|
||||
// Don't pave over the case-insensitive Dictionary we initialized above.
|
||||
foreach (var kvp in recordToBeCloned.m_variables) {
|
||||
m_variables[kvp.Key] = kvp.Value.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,10 +241,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_issues == null)
|
||||
{
|
||||
m_issues = new List<Issue>();
|
||||
}
|
||||
return m_issues;
|
||||
}
|
||||
}
|
||||
@@ -274,22 +277,14 @@ namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_previousAttempts == null)
|
||||
{
|
||||
m_previousAttempts = new List<TimelineAttempt>();
|
||||
}
|
||||
return m_previousAttempts;
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<String, VariableValue> Variables
|
||||
public IDictionary<string, VariableValue> Variables
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_variables == null)
|
||||
{
|
||||
m_variables = new Dictionary<String, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
return m_variables;
|
||||
}
|
||||
}
|
||||
@@ -299,11 +294,32 @@ namespace GitHub.DistributedTask.WebApi
|
||||
return new TimelineRecord(this);
|
||||
}
|
||||
|
||||
[OnDeserialized]
|
||||
private void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
this.EnsureInitialized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DataContractSerializer bypasses all constructor logic and inline initialization!
|
||||
/// This method takes the place of a workhorse constructor for baseline initialization.
|
||||
/// The expectation is for this logic to be accessible to constructors and also to the OnDeserialized helper.
|
||||
/// </summary>
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
//Note that ?? is a short-circuiting operator.
|
||||
m_issues = m_issues ?? new List<Issue>();
|
||||
m_variables = m_variables ?? new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
m_previousAttempts = m_previousAttempts ?? new List<TimelineAttempt>();
|
||||
this.Attempt = Math.Max(this.Attempt, 1);
|
||||
}
|
||||
|
||||
|
||||
[DataMember(Name = "Issues", EmitDefaultValue = false, Order = 60)]
|
||||
private List<Issue> m_issues;
|
||||
|
||||
[DataMember(Name = "Variables", EmitDefaultValue = false, Order = 80)]
|
||||
private Dictionary<String, VariableValue> m_variables;
|
||||
private Dictionary<string, VariableValue> m_variables;
|
||||
|
||||
[DataMember(Name = "PreviousAttempts", EmitDefaultValue = false, Order = 120)]
|
||||
private List<TimelineAttempt> m_previousAttempts;
|
||||
|
||||
@@ -192,8 +192,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_runnerServer
|
||||
.Setup(x => x.GetAgentMessageAsync(
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()))
|
||||
.Returns(async (Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken) =>
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(async (Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken) =>
|
||||
{
|
||||
await Task.Yield();
|
||||
return messages.Dequeue();
|
||||
@@ -208,7 +208,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
//Assert
|
||||
_runnerServer
|
||||
.Verify(x => x.GetAgentMessageAsync(
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()), Times.Exactly(arMessages.Length));
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(arMessages.Length));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_runnerServer
|
||||
.Setup(x => x.GetAgentMessageAsync(
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()))
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.Throws(new TaskAgentAccessTokenExpiredException("test"));
|
||||
try
|
||||
{
|
||||
@@ -311,7 +311,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
//Assert
|
||||
_runnerServer
|
||||
.Verify(x => x.GetAgentMessageAsync(
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
_runnerServer
|
||||
.Verify(x => x.DeleteAgentSessionAsync(
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
_secretMasker = new SecretMasker();
|
||||
_secretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
|
||||
_secretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);
|
||||
_traceManager = new TraceManager(traceListener, _secretMasker);
|
||||
_traceManager = new TraceManager(traceListener, null, _secretMasker);
|
||||
_trace = GetTrace(nameof(TestHostContext));
|
||||
|
||||
// inject a terminal in silent mode so all console output
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Xunit;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
@@ -41,5 +43,26 @@ namespace GitHub.Runner.Common.Tests
|
||||
Assert.True(Directory.Exists(testDataDir));
|
||||
return testDataDir;
|
||||
}
|
||||
|
||||
public static IReadOnlyIssue CreateTestIssue(IssueType type, string message, IssueMetadata metadata, bool writeToLog)
|
||||
{
|
||||
var result = new Issue()
|
||||
{
|
||||
Type = type,
|
||||
Message = message,
|
||||
};
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
result.Category = metadata.Category;
|
||||
result.IsInfrastructureIssue = metadata.IsInfrastructureIssue;
|
||||
foreach (var kvp in metadata.Data)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_commandManager.EnablePluginInternalCommand();
|
||||
@@ -59,10 +59,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_commandManager.EnablePluginInternalCommand();
|
||||
@@ -92,10 +92,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
return 1;
|
||||
});
|
||||
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -14,6 +13,11 @@ using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
|
||||
#if OS_WINDOWS
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using Xunit;
|
||||
@@ -2147,7 +2151,9 @@ runs:
|
||||
_ec.Object.Global.FileTable = new List<String>();
|
||||
_ec.Object.Global.Plan = new TaskOrchestrationPlanReference();
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
|
||||
|
||||
_dockerManager = new Mock<IDockerCommandManager>();
|
||||
|
||||
@@ -4,10 +4,10 @@ using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@@ -715,7 +715,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
//Assert
|
||||
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
|
||||
Assert.Contains($"Fail to load {action_path}", err.Message);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<string>()), Times.Once);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'."))), Times.Once);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -860,7 +860,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
}
|
||||
|
||||
private void Teardown()
|
||||
|
||||
@@ -3,20 +3,15 @@ using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
|
||||
@@ -330,7 +325,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Assert.Equal("invalid1", finialInputs["invalid1"]);
|
||||
Assert.Equal("invalid2", finialInputs["invalid2"]);
|
||||
|
||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'")), It.IsAny<string>()), Times.Once);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'"))), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -449,7 +444,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||
_ec.Object.Global.Variables = new Variables(_hc, new Dictionary<string, VariableValue>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
|
||||
_hc.SetSingleton<IActionManager>(_actionManager.Object);
|
||||
_hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object);
|
||||
|
||||
@@ -11,6 +11,7 @@ using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.Services.Common;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
@@ -19,7 +20,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private Mock<IJobServerQueue> _jobServerQueue;
|
||||
private ExecutionContext _jobExecutionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private Variables _variables;
|
||||
private string _rootDirectory;
|
||||
private CreateStepSummaryCommand _createStepCommand;
|
||||
@@ -186,7 +187,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
|
||||
// Setup a job request
|
||||
TaskOrchestrationPlanReference plan = new();
|
||||
@@ -247,13 +248,14 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
WriteDebug = true,
|
||||
Variables = _variables,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
{
|
||||
|
||||
@@ -52,36 +52,36 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Act.
|
||||
ec.InitializeJob(jobRequest, CancellationToken.None);
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -190,9 +190,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
bigMessage += "a";
|
||||
}
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = bigMessage });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = bigMessage });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = bigMessage });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, bigMessage, null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, bigMessage, null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, bigMessage, null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -242,13 +242,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
|
||||
embeddedStep.Start();
|
||||
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Error, "error annotation that should have step and line number information", null, true));
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Warning, "warning annotation that should have step and line number information", null, true));
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Notice, "notice annotation that should have step and line number information", null, true));
|
||||
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Error).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Warning).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Notice).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Error).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Warning).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Notice).Count() == 1)), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,12 +626,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
ec.StepTelemetry.StepId = Guid.NewGuid();
|
||||
ec.StepTelemetry.Stage = "main";
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -692,9 +692,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
embeddedStep.StepTelemetry.Action = "actions/checkout";
|
||||
embeddedStep.StepTelemetry.Ref = "v2";
|
||||
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
|
||||
embeddedStep.PublishStepTelemetry();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -21,7 +22,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IActionCommandManager> _commandManager;
|
||||
private Variables _variables;
|
||||
private OnMatcherChanged _onMatcherChanged;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private List<string> _messages;
|
||||
private List<string> _commands;
|
||||
private OutputManager _outputManager;
|
||||
@@ -82,10 +83,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 4");
|
||||
Process("NOT GOOD: message 5");
|
||||
Assert.Equal(4, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 3", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 5", _issues[3].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 3", _issues[2].Message);
|
||||
Assert.Equal("message 5", _issues[3].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(1, _messages.Count);
|
||||
Assert.Equal("ERROR: message 4", _messages[0]);
|
||||
@@ -148,11 +149,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 4");
|
||||
Process("NOT GOOD: message 5");
|
||||
Assert.Equal(5, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 3", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 4", _issues[3].Item1.Message);
|
||||
Assert.Equal("message 5", _issues[4].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 3", _issues[2].Message);
|
||||
Assert.Equal("message 4", _issues[3].Message);
|
||||
Assert.Equal("message 5", _issues[4].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -188,10 +189,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("BAD: real bad");
|
||||
Process(": not working");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal("BAD", _issues[0].Item1.Data["code"]);
|
||||
Assert.Equal("not working", _issues[1].Item1.Message);
|
||||
Assert.False(_issues[1].Item1.Data.ContainsKey("code"));
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal("BAD", _issues[0]["code"]);
|
||||
Assert.Equal("not working", _issues[1].Message);
|
||||
Assert.Null(_issues[1]["code"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -238,11 +239,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("Error: real bad");
|
||||
Process("regular message 2");
|
||||
Assert.Equal(5, _issues.Count);
|
||||
Assert.Equal("it broke", _issues[0].Item1.Message);
|
||||
Assert.Equal("oh no", _issues[1].Item1.Message);
|
||||
Assert.Equal("not good", _issues[2].Item1.Message);
|
||||
Assert.Equal("it broke again", _issues[3].Item1.Message);
|
||||
Assert.Equal("real bad", _issues[4].Item1.Message);
|
||||
Assert.Equal("it broke", _issues[0].Message);
|
||||
Assert.Equal("oh no", _issues[1].Message);
|
||||
Assert.Equal("not good", _issues[2].Message);
|
||||
Assert.Equal("it broke again", _issues[3].Message);
|
||||
Assert.Equal("real bad", _issues[4].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(4, _messages.Count);
|
||||
Assert.Equal("Start: hello", _messages[0]);
|
||||
@@ -293,8 +294,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: it is broken");
|
||||
Process("NOT GOOD: that did not work");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it is broken", _issues[0].Item1.Message);
|
||||
Assert.Equal("that did not work", _issues[1].Item1.Message);
|
||||
Assert.Equal("it is broken", _issues[0].Message);
|
||||
Assert.Equal("that did not work", _issues[1].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -332,15 +333,15 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("(12,thirty-four): it is broken");
|
||||
Process("(twelve,34): not working");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal("12", _issues[0].Item1.Data["line"]);
|
||||
Assert.Equal("34", _issues[0].Item1.Data["col"]);
|
||||
Assert.Equal("it is broken", _issues[1].Item1.Message);
|
||||
Assert.Equal("12", _issues[1].Item1.Data["line"]);
|
||||
Assert.False(_issues[1].Item1.Data.ContainsKey("col"));
|
||||
Assert.Equal("not working", _issues[2].Item1.Message);
|
||||
Assert.False(_issues[2].Item1.Data.ContainsKey("line"));
|
||||
Assert.Equal("34", _issues[2].Item1.Data["col"]);
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal("12", _issues[0]["line"]);
|
||||
Assert.Equal("34", _issues[0]["col"]);
|
||||
Assert.Equal("it is broken", _issues[1].Message);
|
||||
Assert.Equal("12", _issues[1]["line"]);
|
||||
Assert.Null(_issues[1]["col"]);
|
||||
Assert.Equal("not working", _issues[2].Message);
|
||||
Assert.Null(_issues[2]["line"]);
|
||||
Assert.Equal("34", _issues[2]["col"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Count);
|
||||
Assert.Equal("##[debug]Unable to parse column number 'thirty-four'", _messages[0]);
|
||||
@@ -373,8 +374,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("this line is a command too ##[some-command]even though it contains ERROR: not working again");
|
||||
Process("##[not-command]this line is an ERROR: it is broken again");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it is broken", _issues[0].Item1.Message);
|
||||
Assert.Equal("it is broken again", _issues[1].Item1.Message);
|
||||
Assert.Equal("it is broken", _issues[0].Message);
|
||||
Assert.Equal("it is broken again", _issues[1].Message);
|
||||
Assert.Equal(2, _commands.Count);
|
||||
Assert.Equal("##[some-command]this line is a command even though it contains ERROR: not working", _commands[0]);
|
||||
Assert.Equal("this line is a command too ##[some-command]even though it contains ERROR: not working again", _commands[1]);
|
||||
@@ -404,8 +405,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
});
|
||||
Process("the error: \033[31mred, \033[1;31mbright red, \033[mreset");
|
||||
Assert.Equal(1, _issues.Count);
|
||||
Assert.Equal("red, bright red, reset", _issues[0].Item1.Message);
|
||||
Assert.Equal("the error: red, bright red, reset", _issues[0].Item2);
|
||||
Assert.Equal("red, bright red, reset", _issues[0].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -455,9 +455,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 3");
|
||||
Process("NOT GOOD: message 4");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 4", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 4", _issues[2].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(1, _messages.Count);
|
||||
Assert.Equal("ERROR: message 3", _messages[0]);
|
||||
@@ -517,8 +517,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("Matches both line 1: hello again");
|
||||
Process("oh no, another error");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it broke", _issues[0].Item1.Message);
|
||||
Assert.Equal("oh no, another error", _issues[1].Item1.Message);
|
||||
Assert.Equal("it broke", _issues[0].Message);
|
||||
Assert.Equal("oh no, another error", _issues[1].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(4, _messages.Count);
|
||||
Assert.Equal("Matches both line 1: hello", _messages[0]);
|
||||
@@ -573,14 +573,14 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process(": not working");
|
||||
Process("ERROR! uh oh");
|
||||
Assert.Equal(4, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Item1.Type);
|
||||
Assert.Equal("not great", _issues[1].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Item1.Type);
|
||||
Assert.Equal("not working", _issues[2].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Item1.Type);
|
||||
Assert.Equal("uh oh", _issues[3].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Item1.Type);
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Type);
|
||||
Assert.Equal("not great", _issues[1].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Type);
|
||||
Assert.Equal("not working", _issues[2].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Type);
|
||||
Assert.Equal("uh oh", _issues[3].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Type);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Count);
|
||||
Assert.StartsWith("##[debug]Skipped", _messages[0]);
|
||||
@@ -633,9 +633,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("jane.doe@contoso.com");
|
||||
Process("ERR: this error");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("john.doe@contoso.com", _issues[0].Item1.Message);
|
||||
Assert.Contains("Removing issue matcher 'email'", _issues[1].Item1.Message);
|
||||
Assert.Equal("this error", _issues[2].Item1.Message);
|
||||
Assert.Equal("john.doe@contoso.com", _issues[0].Message);
|
||||
Assert.Contains("Removing issue matcher 'email'", _issues[1].Message);
|
||||
Assert.Equal("this error", _issues[2].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Where(x => x.StartsWith("##[debug]Timeout processing issue matcher")).Count());
|
||||
Assert.Equal(1, _messages.Where(x => x.Equals("t@t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.c%20")).Count());
|
||||
@@ -725,32 +725,32 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(9, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.False(_issues[0].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Null(_issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1]["file"]);
|
||||
|
||||
Assert.Equal("some error 3", _issues[2].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2].Item1.Data["file"]);
|
||||
Assert.Equal("some error 3", _issues[2].Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2]["file"]);
|
||||
|
||||
Assert.Equal("some error 4", _issues[3].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository_nestedDirectory.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[3].Item1.Data["file"]);
|
||||
Assert.Equal("some error 4", _issues[3].Message);
|
||||
Assert.Equal(file_workflowRepository_nestedDirectory.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[3]["file"]);
|
||||
|
||||
Assert.Equal("some error 5", _issues[4].Item1.Message);
|
||||
Assert.False(_issues[4].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 5", _issues[4].Message);
|
||||
Assert.Null(_issues[4]["file"]);
|
||||
|
||||
Assert.Equal("some error 6", _issues[5].Item1.Message);
|
||||
Assert.False(_issues[5].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 6", _issues[5].Message);
|
||||
Assert.Null(_issues[5]["file"]);
|
||||
|
||||
Assert.Equal("some error 7", _issues[6].Item1.Message);
|
||||
Assert.False(_issues[6].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 7", _issues[6].Message);
|
||||
Assert.Null(_issues[6]["file"]);
|
||||
|
||||
Assert.Equal("some error 8", _issues[7].Item1.Message);
|
||||
Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7].Item1.Data["file"]);
|
||||
Assert.Equal("some error 8", _issues[7].Message);
|
||||
Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7]["file"]);
|
||||
|
||||
Assert.Equal("some error 9", _issues[8].Item1.Message);
|
||||
Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8].Item1.Data["file"]);
|
||||
Assert.Equal("some error 9", _issues[8].Message);
|
||||
Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8]["file"]);
|
||||
}
|
||||
|
||||
Environment.SetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE", "");
|
||||
@@ -810,11 +810,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(2, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Equal("some-file.txt", _issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal("some-file.txt", _issues[1]["file"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,11 +871,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(2, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Equal("some-file.txt", _issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal("some-file.txt", _issues[1]["file"]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -929,8 +929,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Process
|
||||
Process("some-directory/some-file.txt: some error [workflow-repo/some-project/some-project.proj]");
|
||||
Assert.Equal(1, _issues.Count);
|
||||
Assert.Equal("some error", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-project/some-directory/some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error", _issues[0].Message);
|
||||
Assert.Equal("some-project/some-directory/some-file.txt", _issues[0]["file"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -958,7 +958,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
matchers?.Validate();
|
||||
|
||||
_onMatcherChanged = null;
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_messages = new List<string>();
|
||||
_commands = new List<string>();
|
||||
|
||||
@@ -983,10 +983,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_onMatcherChanged = handler;
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.CreateIssue(It.IsAny<DTWebApi.IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
_issues.Add(issue);
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SaveStateFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private string _rootDirectory;
|
||||
private SaveStateFileCommand _saveStateFileCommand;
|
||||
private Dictionary<string, string> _intraActionState;
|
||||
@@ -390,7 +390,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_intraActionState = new Dictionary<string, string>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
@@ -413,12 +413,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -21,7 +15,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SetEnvFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private string _rootDirectory;
|
||||
private SetEnvFileCommand _setEnvFileCommand;
|
||||
private ITraceWriter _trace;
|
||||
@@ -389,7 +383,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
|
||||
@@ -411,12 +405,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -21,7 +15,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SetOutputFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private Dictionary<string, string> _outputs;
|
||||
private string _rootDirectory;
|
||||
private SetOutputFileCommand _setOutputFileCommand;
|
||||
@@ -390,7 +384,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_outputs = new Dictionary<string, string>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
@@ -413,12 +407,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
Reference in New Issue
Block a user