Compare commits

..

3 Commits

Author SHA1 Message Date
TingluoHuang
2e44f985bd Release 2.299.1 runner. 2022-11-02 19:59:20 -04:00
Tingluo Huang
c638ccf940 Forward parameters into run() func in run.sh. (#2240) 2022-11-02 19:58:07 -04:00
Cory Miller
c0d21101a3 Release 2.299.0 runner 2022-11-02 14:56:56 -04:00
49 changed files with 488 additions and 939 deletions

View File

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

View File

@@ -660,52 +660,3 @@ jobs:
asset_path: ${{ github.workspace }}/linux-arm64-trimmedpackages.json asset_path: ${{ github.workspace }}/linux-arm64-trimmedpackages.json
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream 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

View File

@@ -22,4 +22,4 @@ Runner releases:
## Contribute ## Contribute
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. We accept contributions in the form of issues and pull requests. [Read more here](docs/contribute.md) before contributing.

View File

@@ -1,6 +1,6 @@
# Contributions # 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.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. 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.
> 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. > 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.

View File

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

View File

@@ -2,7 +2,7 @@
- Displays the error logs in dedicated sub-sections of the Initialize containers section (#2182) - Displays the error logs in dedicated sub-sections of the Initialize containers section (#2182)
- Add generateServiceConfig option for configure command (#2226) - Add generateServiceConfig option for configure command (#2226)
- Setting debug using GitHub Action variables (#2234) - 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 ## Bugs

View File

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

View File

@@ -13,7 +13,7 @@ set -e
flags_found=false flags_found=false
while getopts 's:g:n:r:u:l:df' opt; do while getopts 's:g:n:r:u:l:d' opt; do
flags_found=true flags_found=true
case $opt in case $opt in
@@ -35,9 +35,6 @@ while getopts 's:g:n:r:u:l:df' opt; do
l) l)
labels=$OPTARG labels=$OPTARG
;; ;;
f)
replace='true'
;;
d) d)
disableupdate='true' disableupdate='true'
;; ;;
@@ -56,8 +53,7 @@ Usage:
-r optional name of the runner group to add the runner to, defaults to the Default group -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 -u optional user svc will run as, defaults to current
-l optional list of labels (split by comma) applied on the runner -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 exit 0
;; ;;
esac esac
@@ -178,7 +174,7 @@ fi
echo echo
echo "Configuring ${runner_name} @ $runner_url" 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}" 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 ${replace:+--replace} --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}
#--------------------------------------- #---------------------------------------
# Configuring as a service # Configuring as a service

View File

@@ -1,4 +1,4 @@
#!/bin/bash #/bin/bash
set -e set -e
@@ -12,7 +12,7 @@ set -e
# #
# Usage: # Usage:
# export RUNNER_CFG_PAT=<yourPAT> # export RUNNER_CFG_PAT=<yourPAT>
# ./delete.sh <scope> [<name>] # ./delete.sh scope name
# #
# scope required repo (:owner/:repo) or org (:organization) # scope required repo (:owner/:repo) or org (:organization)
# name optional defaults to hostname. name to delete # name optional defaults to hostname. name to delete
@@ -26,6 +26,8 @@ set -e
runner_scope=${1} runner_scope=${1}
runner_name=${2} runner_name=${2}
echo "Deleting runner ${runner_name} @ ${runner_scope}"
function fatal() function fatal()
{ {
echo "error: $1" >&2 echo "error: $1" >&2
@@ -33,10 +35,8 @@ function fatal()
} }
if [ -z "${runner_scope}" ]; then fatal "supply scope as argument 1"; fi 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_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 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" which jq || fatal "jq required. Please install in PATH with apt-get, brew, etc"

View File

@@ -48,7 +48,7 @@ runWithManualTrap() {
trap - INT TERM trap - INT TERM
# wait for last parts to be logged # wait for last parts to be logged
wait $PID wait $PID
exit $returnCode exit 0
fi fi
done done
} }

View File

@@ -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 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 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 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. Please update the following actions to use Node.js 16: {0}. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/."; public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. 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 class RunnerEvent public static class RunnerEvent
@@ -245,7 +245,6 @@ 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. // 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 ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_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 public static class System

View File

@@ -94,13 +94,6 @@ namespace GitHub.Runner.Common
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPreAmpersandEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPreAmpersandEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPostAmpersandEscape); 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. // Create the trace manager.
if (string.IsNullOrEmpty(logFile)) if (string.IsNullOrEmpty(logFile))
{ {
@@ -120,11 +113,11 @@ namespace GitHub.Runner.Common
// this should give us _diag folder under runner root directory // 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); 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), stdoutTraceListener, this.SecretMasker); _traceManager = new TraceManager(new HostTraceListener(diagLogDirectory, hostType, logPageSize, logRetentionDays), this.SecretMasker);
} }
else else
{ {
_traceManager = new TraceManager(new HostTraceListener(logFile), stdoutTraceListener, this.SecretMasker); _traceManager = new TraceManager(new HostTraceListener(logFile), this.SecretMasker);
} }
_trace = GetTrace(nameof(HostContext)); _trace = GetTrace(nameof(HostContext));

View File

@@ -600,7 +600,8 @@ namespace GitHub.Runner.Common
{ {
foreach (var issue in record.Issues) foreach (var issue in record.Issues)
{ {
string source = issue["sourcepath"]; String source;
issue.Data.TryGetValue("sourcepath", out source);
Trace.Verbose($" Issue: c={issue.Category}, t={issue.Type}, s={source ?? string.Empty}, m={issue.Message}"); Trace.Verbose($" Issue: c={issue.Category}, t={issue.Type}, s={source ?? string.Empty}, m={issue.Message}");
} }
} }

View File

@@ -38,7 +38,7 @@ namespace GitHub.Runner.Common
Task<TaskAgentSession> CreateAgentSessionAsync(Int32 poolId, TaskAgentSession session, CancellationToken cancellationToken); Task<TaskAgentSession> CreateAgentSessionAsync(Int32 poolId, TaskAgentSession session, CancellationToken cancellationToken);
Task DeleteAgentMessageAsync(Int32 poolId, Int64 messageId, Guid sessionId, CancellationToken cancellationToken); Task DeleteAgentMessageAsync(Int32 poolId, Int64 messageId, Guid sessionId, CancellationToken cancellationToken);
Task DeleteAgentSessionAsync(Int32 poolId, Guid sessionId, CancellationToken cancellationToken); Task DeleteAgentSessionAsync(Int32 poolId, Guid sessionId, CancellationToken cancellationToken);
Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken); Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken);
// job request // job request
Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken); Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken);
@@ -272,10 +272,10 @@ namespace GitHub.Runner.Common
return _messageTaskAgentClient.DeleteAgentSessionAsync(poolId, sessionId, cancellationToken: cancellationToken); return _messageTaskAgentClient.DeleteAgentSessionAsync(poolId, sessionId, cancellationToken: cancellationToken);
} }
public Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken) public Task<TaskAgentMessage> GetAgentMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken)
{ {
CheckConnection(RunnerConnectionType.MessageQueue); CheckConnection(RunnerConnectionType.MessageQueue);
return _messageTaskAgentClient.GetMessageAsync(poolId, sessionId, lastMessageId, status, runnerVersion, cancellationToken: cancellationToken); return _messageTaskAgentClient.GetMessageAsync(poolId, sessionId, lastMessageId, status, cancellationToken: cancellationToken);
} }
//----------------------------------------------------------------- //-----------------------------------------------------------------

View File

@@ -1,90 +0,0 @@
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--;
}
}
}

View File

@@ -18,7 +18,7 @@ namespace GitHub.Runner.Common
string ReadSecret(); string ReadSecret();
void Write(string message, ConsoleColor? colorCode = null); void Write(string message, ConsoleColor? colorCode = null);
void WriteLine(); void WriteLine();
void WriteLine(string line, ConsoleColor? colorCode = null, bool skipTracing = false); void WriteLine(string line, ConsoleColor? colorCode = null);
void WriteError(Exception ex); void WriteError(Exception ex);
void WriteError(string line); void WriteError(string line);
void WriteSection(string message); void WriteSection(string message);
@@ -116,12 +116,9 @@ namespace GitHub.Runner.Common
// Do not add a format string overload. Terminal messages are user facing and therefore // 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. // should be localized. Use the Loc method in the StringUtil class.
public void WriteLine(string line, ConsoleColor? colorCode = null, bool skipTracing = false) public void WriteLine(string line, ConsoleColor? colorCode = null)
{
if (!skipTracing)
{ {
Trace.Info($"WRITE LINE: {line}"); Trace.Info($"WRITE LINE: {line}");
}
if (!Silent) if (!Silent)
{ {
if (colorCode != null) if (colorCode != null)

View File

@@ -16,23 +16,21 @@ namespace GitHub.Runner.Common
{ {
private readonly ConcurrentDictionary<string, Tracing> _sources = new(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, Tracing> _sources = new(StringComparer.OrdinalIgnoreCase);
private readonly HostTraceListener _hostTraceListener; private readonly HostTraceListener _hostTraceListener;
private readonly StdoutTraceListener _stdoutTraceListener;
private TraceSetting _traceSetting; private TraceSetting _traceSetting;
private ISecretMasker _secretMasker; private ISecretMasker _secretMasker;
public TraceManager(HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener, ISecretMasker secretMasker) public TraceManager(HostTraceListener traceListener, ISecretMasker secretMasker)
: this(traceListener, stdoutTraceListener, new TraceSetting(), secretMasker) : this(traceListener, new TraceSetting(), secretMasker)
{ {
} }
public TraceManager(HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener, TraceSetting traceSetting, ISecretMasker secretMasker) public TraceManager(HostTraceListener traceListener, TraceSetting traceSetting, ISecretMasker secretMasker)
{ {
// Validate and store params. // Validate and store params.
ArgUtil.NotNull(traceListener, nameof(traceListener)); ArgUtil.NotNull(traceListener, nameof(traceListener));
ArgUtil.NotNull(traceSetting, nameof(traceSetting)); ArgUtil.NotNull(traceSetting, nameof(traceSetting));
ArgUtil.NotNull(secretMasker, nameof(secretMasker)); ArgUtil.NotNull(secretMasker, nameof(secretMasker));
_hostTraceListener = traceListener; _hostTraceListener = traceListener;
_stdoutTraceListener = stdoutTraceListener;
_traceSetting = traceSetting; _traceSetting = traceSetting;
_secretMasker = secretMasker; _secretMasker = secretMasker;
@@ -83,7 +81,7 @@ namespace GitHub.Runner.Common
Level = sourceTraceLevel.ToSourceLevels() Level = sourceTraceLevel.ToSourceLevels()
}; };
} }
return new Tracing(name, _secretMasker, sourceSwitch, _hostTraceListener, _stdoutTraceListener); return new Tracing(name, _secretMasker, sourceSwitch, _hostTraceListener);
} }
} }
} }

View File

@@ -12,7 +12,7 @@ namespace GitHub.Runner.Common
private ISecretMasker _secretMasker; private ISecretMasker _secretMasker;
private TraceSource _traceSource; private TraceSource _traceSource;
public Tracing(string name, ISecretMasker secretMasker, SourceSwitch sourceSwitch, HostTraceListener traceListener, StdoutTraceListener stdoutTraceListener = null) public Tracing(string name, ISecretMasker secretMasker, SourceSwitch sourceSwitch, HostTraceListener traceListener)
{ {
ArgUtil.NotNull(secretMasker, nameof(secretMasker)); ArgUtil.NotNull(secretMasker, nameof(secretMasker));
_secretMasker = secretMasker; _secretMasker = secretMasker;
@@ -27,10 +27,6 @@ namespace GitHub.Runner.Common
} }
_traceSource.Listeners.Add(traceListener); _traceSource.Listeners.Add(traceListener);
if (stdoutTraceListener != null)
{
_traceSource.Listeners.Add(stdoutTraceListener);
}
} }
public void Info(string message) public void Info(string message)

View File

@@ -400,7 +400,6 @@ namespace GitHub.Runner.Listener
Task<int> workerProcessTask = null; Task<int> workerProcessTask = null;
object _outputLock = new(); object _outputLock = new();
List<string> workerOutput = new(); List<string> workerOutput = new();
bool printToStdout = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Agent.PrintLogToStdout));
using (var processChannel = HostContext.CreateService<IProcessChannel>()) using (var processChannel = HostContext.CreateService<IProcessChannel>())
using (var processInvoker = HostContext.CreateService<IProcessInvoker>()) using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{ {
@@ -421,17 +420,9 @@ namespace GitHub.Runner.Listener
if (!string.IsNullOrEmpty(stdout.Data)) if (!string.IsNullOrEmpty(stdout.Data))
{ {
lock (_outputLock) lock (_outputLock)
{
if (!stdout.Data.StartsWith("[WORKER"))
{ {
workerOutput.Add(stdout.Data); workerOutput.Add(stdout.Data);
} }
if (printToStdout)
{
term.WriteLine(stdout.Data, skipTracing: true);
}
}
} }
}; };
@@ -512,7 +503,7 @@ namespace GitHub.Runner.Listener
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"]; var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
notification.JobStarted(message.JobId, accessToken, systemConnection.Url); notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
HostContext.WritePerfCounter($"SentJobToWorker_{requestId}"); HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
try try
{ {
@@ -1014,7 +1005,7 @@ namespace GitHub.Runner.Listener
} }
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage }; var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage };
unhandledExceptionIssue[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash; unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
jobRecord.ErrorCount++; jobRecord.ErrorCount++;
jobRecord.Issues.Add(unhandledExceptionIssue); jobRecord.Issues.Add(unhandledExceptionIssue);
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None); await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);

View File

@@ -211,7 +211,6 @@ namespace GitHub.Runner.Listener
_session.SessionId, _session.SessionId,
_lastMessageId, _lastMessageId,
runnerStatus, runnerStatus,
BuildConstants.RunnerPackage.Version,
_getMessagesTokenSource.Token); _getMessagesTokenSource.Token);
// Decrypt the message body if the session is using encryption // Decrypt the message body if the session is using encryption

View File

@@ -430,22 +430,12 @@ namespace GitHub.Runner.Listener
message = await getNextMessage; //get next message message = await getNextMessage; //get next message
HostContext.WritePerfCounter($"MessageReceived_{message.MessageType}"); 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) if (autoUpdateInProgress == false)
{ {
autoUpdateInProgress = true; autoUpdateInProgress = true;
AgentRefreshMessage runnerUpdateMessage = null; var runnerUpdateMessage = JsonUtility.FromString<AgentRefreshMessage>(message.Body);
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 #if DEBUG
// Can mock the update for testing // Can mock the update for testing
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE"))) if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE")))

View File

@@ -270,9 +270,12 @@ namespace GitHub.Runner.Worker
if (string.Equals(blocked, envName, StringComparison.OrdinalIgnoreCase)) if (string.Equals(blocked, envName, StringComparison.OrdinalIgnoreCase))
{ {
// Log Telemetry and let user know they shouldn't do this // Log Telemetry and let user know they shouldn't do this
var message = $"Can't update {blocked} environment variable using ::set-env:: command."; var issue = new Issue()
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, $"{Constants.Runner.UnsupportedCommand}_{envName}"); {
var issue = context.CreateIssue(IssueType.Error, message, metadata, true); Type = IssueType.Error,
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
context.AddIssue(issue); context.AddIssue(issue);
return; return;
@@ -306,9 +309,12 @@ namespace GitHub.Runner.Worker
{ {
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false) if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
{ {
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command); var issue = new Issue()
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand); {
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true); Type = IssueType.Warning,
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue); context.AddIssue(issue);
} }
@@ -338,9 +344,12 @@ namespace GitHub.Runner.Worker
{ {
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false) if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
{ {
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command); var issue = new Issue()
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand); {
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true); Type = IssueType.Warning,
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue); context.AddIssue(issue);
} }
@@ -609,11 +618,16 @@ namespace GitHub.Runner.Worker
context.Debug("Enhanced Annotations not enabled on the server. The 'title', 'end_line', and 'end_column' fields are unsupported."); context.Debug("Enhanced Annotations not enabled on the server. The 'title', 'end_line', and 'end_column' fields are unsupported.");
} }
var issueCategory = "General"; Issue issue = new()
{
Category = "General",
Type = this.Type,
Message = command.Data
};
if (!string.IsNullOrEmpty(file)) if (!string.IsNullOrEmpty(file))
{ {
issueCategory = "Code"; issue.Category = "Code";
if (container != null) if (container != null)
{ {
@@ -644,13 +658,14 @@ namespace GitHub.Runner.Worker
} }
} }
string keyToExclude = Constants.Runner.InternalTelemetryIssueDataKey; foreach (var property in command.Properties)
var filteredDictionaryEntries = command.Properties {
.Where(kvp => !string.Equals(kvp.Key, keyToExclude, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(property.Key, Constants.Runner.InternalTelemetryIssueDataKey, StringComparison.OrdinalIgnoreCase))
.ToList(); {
issue.Data[property.Key] = property.Value;
}
}
var metadata = new IssueMetadata(issueCategory, false, null, filteredDictionaryEntries);
var issue = context.CreateIssue(this.Type, command.Data, metadata, true);
context.AddIssue(issue); context.AddIssue(issue);
} }

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; // required for OS_WINDOWS using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;

View File

@@ -5,6 +5,7 @@ using System.IO.Compression;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using System.Linq; using System.Linq;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;

View File

@@ -16,7 +16,6 @@ using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container; using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers; using GitHub.Runner.Worker.Handlers;
using GitHub.Services.Common;
using Newtonsoft.Json; using Newtonsoft.Json;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating; using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
@@ -91,8 +90,7 @@ namespace GitHub.Runner.Worker
void SetGitHubContext(string name, string value); void SetGitHubContext(string name, string value);
void SetOutput(string name, string value, out string reference); void SetOutput(string name, string value, out string reference);
void SetTimeout(TimeSpan? timeout); void SetTimeout(TimeSpan? timeout);
IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog); void AddIssue(Issue issue, string message = null);
void AddIssue(IReadOnlyIssue issue);
void Progress(int percentage, string currentOperation = null); void Progress(int percentage, string currentOperation = null);
void UpdateDetailTimelineRecord(TimelineRecord record); void UpdateDetailTimelineRecord(TimelineRecord record);
@@ -126,10 +124,8 @@ namespace GitHub.Runner.Worker
private readonly TimelineRecord _record = new(); private readonly TimelineRecord _record = new();
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new(); private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
private readonly List<IReadOnlyIssue> _embeddedIssueCollector;
private readonly object _loggerLock = new(); private readonly object _loggerLock = new();
private readonly object _matchersLock = new(); private readonly object _matchersLock = new();
private readonly ExecutionContext _parentExecutionContext;
private event OnMatcherChanged _onMatcherChanged; private event OnMatcherChanged _onMatcherChanged;
@@ -137,6 +133,7 @@ namespace GitHub.Runner.Worker
private IPagingLogger _logger; private IPagingLogger _logger;
private IJobServerQueue _jobServerQueue; private IJobServerQueue _jobServerQueue;
private ExecutionContext _parentExecutionContext;
private Guid _mainTimelineId; private Guid _mainTimelineId;
private Guid _detailTimelineId; private Guid _detailTimelineId;
@@ -150,29 +147,6 @@ namespace GitHub.Runner.Worker
private long _totalThrottlingDelayInMilliseconds = 0; private long _totalThrottlingDelayInMilliseconds = 0;
private bool _stepTelemetryPublished = false; 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 Id => _record.Id;
public Guid EmbeddedId { get; private set; } public Guid EmbeddedId { get; private set; }
public string ScopeName { get; private set; } public string ScopeName { get; private set; }
@@ -185,7 +159,7 @@ namespace GitHub.Runner.Worker
public Dictionary<string, VariableValue> JobOutputs { get; private set; } public Dictionary<string, VariableValue> JobOutputs { get; private set; }
public ActionsEnvironmentReference ActionsEnvironment { get; private set; } public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
public ActionsStepTelemetry StepTelemetry { get; private init; } public ActionsStepTelemetry StepTelemetry { get; } = new ActionsStepTelemetry();
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData(); public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>(); public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
@@ -210,7 +184,7 @@ namespace GitHub.Runner.Worker
// An embedded execution context shares the same record ID, record name, and logger // An embedded execution context shares the same record ID, record name, and logger
// as its enclosing execution context. // as its enclosing execution context.
public bool IsEmbedded { get; private init; } public bool IsEmbedded { get; private set; }
public TaskResult? Result public TaskResult? Result
{ {
@@ -345,7 +319,7 @@ namespace GitHub.Runner.Worker
{ {
Trace.Entering(); Trace.Entering();
var child = new ExecutionContext(this, isEmbedded); var child = new ExecutionContext();
child.Initialize(HostContext); child.Initialize(HostContext);
child.Global = Global; child.Global = Global;
child.ScopeName = scopeName; child.ScopeName = scopeName;
@@ -370,6 +344,7 @@ namespace GitHub.Runner.Worker
child.ExpressionFunctions.Add(item); child.ExpressionFunctions.Add(item);
} }
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource(); child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
child._parentExecutionContext = this;
child.EchoOnActionCommand = EchoOnActionCommand; child.EchoOnActionCommand = EchoOnActionCommand;
if (recordOrder != null) if (recordOrder != null)
@@ -390,9 +365,11 @@ namespace GitHub.Runner.Worker
child._logger.Setup(_mainTimelineId, recordId); child._logger.Setup(_mainTimelineId, recordId);
} }
child.IsEmbedded = isEmbedded;
child.StepTelemetry.StepId = recordId; child.StepTelemetry.StepId = recordId;
child.StepTelemetry.Stage = stage.ToString(); child.StepTelemetry.Stage = stage.ToString();
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); child.StepTelemetry.IsEmbedded = isEmbedded;
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
return child; return child;
} }
@@ -434,24 +411,13 @@ namespace GitHub.Runner.Worker
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling."); this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
} }
DateTime now = DateTime.UtcNow;
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation; _record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
_record.ResultCode = resultCode ?? _record.ResultCode; _record.ResultCode = resultCode ?? _record.ResultCode;
_record.FinishTime = now; _record.FinishTime = DateTime.UtcNow;
_record.PercentComplete = 100; _record.PercentComplete = 100;
_record.Result = _record.Result ?? TaskResult.Succeeded; _record.Result = _record.Result ?? TaskResult.Succeeded;
_record.State = TimelineRecordState.Completed; _record.State = TimelineRecordState.Completed;
// Before our main timeline's final QueueTimelineRecordUpdate,
// inject any issues collected by embedded ExecutionContexts.
if (!this.IsEmbedded)
{
foreach (var issue in _embeddedIssueCollector)
{
AddIssue(issue);
}
}
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record); _jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
// complete all detail timeline records. // complete all detail timeline records.
@@ -459,7 +425,7 @@ namespace GitHub.Runner.Worker
{ {
foreach (var record in _detailRecords) foreach (var record in _detailRecords)
{ {
record.Value.FinishTime = record.Value.FinishTime ?? now; record.Value.FinishTime = record.Value.FinishTime ?? DateTime.UtcNow;
record.Value.PercentComplete = record.Value.PercentComplete ?? 100; record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded; record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
record.Value.State = TimelineRecordState.Completed; record.Value.State = TimelineRecordState.Completed;
@@ -579,90 +545,77 @@ namespace GitHub.Runner.Worker
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record); _jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
} }
// This is not thread safe, the caller needs to take lock before calling issue() // This is not thread safe, the caller need to take lock before calling issue()
public IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog) public void AddIssue(Issue issue, string logMessage = null)
{
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)); ArgUtil.NotNull(issue, nameof(issue));
// Embedded ExecutionContexts (a.k.a. Composite actions) should never upload a timeline record to the server. if (string.IsNullOrEmpty(logMessage))
// 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)
{ {
_embeddedIssueCollector.Add(issue); logMessage = issue.Message;
} }
else
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
if (issue.Message.Length > _maxIssueMessageLength)
{ {
issue.Message = issue.Message[.._maxIssueMessageLength];
}
// 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); _jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
} }
}
public void UpdateDetailTimelineRecord(TimelineRecord record) public void UpdateDetailTimelineRecord(TimelineRecord record)
{ {
@@ -1022,7 +975,16 @@ namespace GitHub.Runner.Worker
if ((issue.Type == IssueType.Error || issue.Type == IssueType.Warning) && if ((issue.Type == IssueType.Error || issue.Type == IssueType.Warning) &&
!string.IsNullOrEmpty(issue.Message)) !string.IsNullOrEmpty(issue.Message))
{ {
string issueTelemetry = PrimitiveExtensions.TrimExcess(issue.Message, _maxIssueMessageLengthInTelemetry); string issueTelemetry;
if (issue.Message.Length > _maxIssueMessageLengthInTelemetry)
{
issueTelemetry = $"{issue.Message[.._maxIssueMessageLengthInTelemetry]}";
}
else
{
issueTelemetry = issue.Message;
}
StepTelemetry.ErrorMessages.Add(issueTelemetry); StepTelemetry.ErrorMessages.Add(issueTelemetry);
// Only send over the first 3 issues to avoid sending too much data. // Only send over the first 3 issues to avoid sending too much data.
@@ -1196,23 +1158,19 @@ namespace GitHub.Runner.Worker
// Do not add a format string overload. See comment on ExecutionContext.Write(). // Do not add a format string overload. See comment on ExecutionContext.Write().
public static void Error(this IExecutionContext context, string message) public static void Error(this IExecutionContext context, string message)
{ {
var issue = context.CreateIssue(IssueType.Error, message, null, true); context.AddIssue(new Issue() { Type = IssueType.Error, Message = message });
context.AddIssue(issue);
} }
// Do not add a format string overload. See comment on ExecutionContext.Write(). // Do not add a format string overload. See comment on ExecutionContext.Write().
public static void InfrastructureError(this IExecutionContext context, string message) public static void InfrastructureError(this IExecutionContext context, string message)
{ {
var metadata = new IssueMetadata(null, true, null, Enumerable.Empty<KeyValuePair<string, string>>()); context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
var issue = context.CreateIssue(IssueType.Error, message, metadata, true);
context.AddIssue(issue);
} }
// Do not add a format string overload. See comment on ExecutionContext.Write(). // Do not add a format string overload. See comment on ExecutionContext.Write().
public static void Warning(this IExecutionContext context, string message) public static void Warning(this IExecutionContext context, string message)
{ {
var issue = context.CreateIssue(IssueType.Warning, message, null, true); context.AddIssue(new Issue() { Type = IssueType.Warning, Message = message });
context.AddIssue(issue);
} }
// Do not add a format string overload. See comment on ExecutionContext.Write(). // Do not add a format string overload. See comment on ExecutionContext.Write().

View File

@@ -13,7 +13,6 @@ namespace GitHub.Runner.Worker
"action_repository", "action_repository",
"action", "action",
"actor", "actor",
"actor_id",
"api_url", "api_url",
"base_ref", "base_ref",
"env", "env",
@@ -28,10 +27,8 @@ namespace GitHub.Runner.Worker
"ref_protected", "ref_protected",
"ref_type", "ref_type",
"ref", "ref",
"repository",
"repository_id",
"repository_owner", "repository_owner",
"repository_owner_id", "repository",
"retention_days", "retention_days",
"run_attempt", "run_attempt",
"run_id", "run_id",
@@ -42,9 +39,7 @@ namespace GitHub.Runner.Worker
"step_summary", "step_summary",
"triggering_actor", "triggering_actor",
"workflow", "workflow",
"workflow_ref", "workspace",
"workflow_sha",
"workspace"
}; };
public IEnumerable<KeyValuePair<string, string>> GetRuntimeEnvironmentVariables() public IEnumerable<KeyValuePair<string, string>> GetRuntimeEnvironmentVariables()

View File

@@ -239,7 +239,7 @@ namespace GitHub.Runner.Worker.Handlers
// Set action_status to the success of the current composite action // Set action_status to the success of the current composite action
var actionResult = ExecutionContext.Result?.ToActionResult() ?? ActionResult.Success; var actionResult = ExecutionContext.Result?.ToActionResult() ?? ActionResult.Success;
step.ExecutionContext.SetGitHubContext("action_status", actionResult.ToString().ToLowerInvariant()); step.ExecutionContext.SetGitHubContext("action_status", actionResult.ToString());
// Initialize env context // Initialize env context
Trace.Info("Initialize Env context for embedded step"); Trace.Info("Initialize Env context for embedded step");
@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
// Evaluation error // Evaluation error
Trace.Info("Caught exception from expression for embedded step.env"); Trace.Info("Caught exception from expression for embedded step.env");
step.ExecutionContext.Error(ex); step.ExecutionContext.Error(ex);
SetStepConclusion(step, TaskResult.Failed); step.ExecutionContext.Complete(TaskResult.Failed);
} }
// Register Callback // Register Callback

View File

@@ -4,7 +4,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container; using GitHub.Runner.Worker.Container;
@@ -98,7 +97,7 @@ namespace GitHub.Runner.Worker.Handlers
var matchers = _matchers; var matchers = _matchers;
// Strip color codes // Strip color codes
var refinedLine = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line; var stripped = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line;
foreach (var matcher in matchers) foreach (var matcher in matchers)
{ {
@@ -108,7 +107,8 @@ namespace GitHub.Runner.Worker.Handlers
// Match // Match
try try
{ {
match = matcher.Match(refinedLine); match = matcher.Match(stripped);
break; break;
} }
catch (RegexMatchTimeoutException ex) catch (RegexMatchTimeoutException ex)
@@ -116,7 +116,7 @@ namespace GitHub.Runner.Worker.Handlers
if (attempt < _maxAttempts) if (attempt < _maxAttempts)
{ {
// Debug // Debug
_executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{refinedLine}'. Exception: {ex}"); _executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{stripped}'. Exception: {ex.ToString()}");
} }
else else
{ {
@@ -139,10 +139,12 @@ namespace GitHub.Runner.Worker.Handlers
// Convert to issue // Convert to issue
var issue = ConvertToIssue(match); var issue = ConvertToIssue(match);
if (issue != null) if (issue != null)
{ {
// Log issue // Log issue
_executionContext.AddIssue(issue); _executionContext.AddIssue(issue, stripped);
return; return;
} }
} }
@@ -194,7 +196,7 @@ namespace GitHub.Runner.Worker.Handlers
} }
} }
private DTWebApi.IReadOnlyIssue ConvertToIssue(IssueMatch match) private DTWebApi.Issue ConvertToIssue(IssueMatch match)
{ {
// Validate the message // Validate the message
if (string.IsNullOrWhiteSpace(match.Message)) if (string.IsNullOrWhiteSpace(match.Message))
@@ -223,14 +225,18 @@ namespace GitHub.Runner.Worker.Handlers
return null; return null;
} }
var issueData = new Dictionary<string, string>(); var issue = new DTWebApi.Issue
{
Message = match.Message,
Type = issueType,
};
// Line // Line
if (!string.IsNullOrEmpty(match.Line)) if (!string.IsNullOrEmpty(match.Line))
{ {
if (int.TryParse(match.Line, NumberStyles.None, CultureInfo.InvariantCulture, out var line)) if (int.TryParse(match.Line, NumberStyles.None, CultureInfo.InvariantCulture, out var line))
{ {
issueData["line"] = line.ToString(CultureInfo.InvariantCulture); issue.Data["line"] = line.ToString(CultureInfo.InvariantCulture);
} }
else else
{ {
@@ -243,7 +249,7 @@ namespace GitHub.Runner.Worker.Handlers
{ {
if (int.TryParse(match.Column, NumberStyles.None, CultureInfo.InvariantCulture, out var column)) if (int.TryParse(match.Column, NumberStyles.None, CultureInfo.InvariantCulture, out var column))
{ {
issueData["col"] = column.ToString(CultureInfo.InvariantCulture); issue.Data["col"] = column.ToString(CultureInfo.InvariantCulture);
} }
else else
{ {
@@ -254,7 +260,7 @@ namespace GitHub.Runner.Worker.Handlers
// Code // Code
if (!string.IsNullOrWhiteSpace(match.Code)) if (!string.IsNullOrWhiteSpace(match.Code))
{ {
issueData["code"] = match.Code.Trim(); issue.Data["code"] = match.Code.Trim();
} }
// File // File
@@ -306,7 +312,7 @@ namespace GitHub.Runner.Worker.Handlers
var relativePath = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar); var relativePath = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar);
// Prefer `/` on all platforms // Prefer `/` on all platforms
issueData["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); issue.Data["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
} }
else else
{ {
@@ -321,11 +327,9 @@ namespace GitHub.Runner.Worker.Handlers
} }
catch (Exception ex) catch (Exception ex)
{ {
_executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex}"); _executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex.ToString()}");
} }
var metadata = new IssueMetadata(null, false, match.SourceText, issueData);
var issue = _executionContext.CreateIssue(issueType, match.Message, metadata, true);
return issue; return issue;
} }

View File

@@ -8,31 +8,6 @@ namespace GitHub.Runner.Worker
{ {
public delegate void OnMatcherChanged(object sender, MatcherChangedEventArgs e); 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 sealed class MatcherChangedEventArgs : EventArgs
{ {
public MatcherChangedEventArgs(IssueMatcherConfig config) public MatcherChangedEventArgs(IssueMatcherConfig config)
@@ -94,7 +69,7 @@ namespace GitHub.Runner.Worker
if (regexMatch.Success) if (regexMatch.Success)
{ {
return new IssueMatch(line, null, pattern, regexMatch.Groups, DefaultSeverity); return new IssueMatch(null, pattern, regexMatch.Groups, DefaultSeverity);
} }
return null; return null;
@@ -135,13 +110,13 @@ namespace GitHub.Runner.Worker
} }
// Return // Return
return new IssueMatch(line, runningMatch, pattern, regexMatch.Groups, DefaultSeverity); return new IssueMatch(runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
} }
// Not the last pattern // Not the last pattern
else else
{ {
// Store the match // Store the match
_state[i] = new IssueMatch(line, runningMatch, pattern, regexMatch.Groups); _state[i] = new IssueMatch(runningMatch, pattern, regexMatch.Groups);
} }
} }
// Not matched // Not matched
@@ -209,9 +184,8 @@ namespace GitHub.Runner.Worker
public sealed class IssueMatch public sealed class IssueMatch
{ {
public IssueMatch(string sourceText, IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null) public IssueMatch(IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
{ {
SourceText = sourceText;
File = runningMatch?.File ?? GetValue(groups, pattern.File); File = runningMatch?.File ?? GetValue(groups, pattern.File);
Line = runningMatch?.Line ?? GetValue(groups, pattern.Line); Line = runningMatch?.Line ?? GetValue(groups, pattern.Line);
Column = runningMatch?.Column ?? GetValue(groups, pattern.Column); Column = runningMatch?.Column ?? GetValue(groups, pattern.Column);
@@ -226,8 +200,6 @@ namespace GitHub.Runner.Worker
} }
} }
public string SourceText { get; }
public string File { get; } public string File { get; }
public string Line { get; } public string Line { get; }

View File

@@ -660,9 +660,8 @@ namespace GitHub.Runner.Worker
var freeSpaceInMB = driveInfo.AvailableFreeSpace / 1024 / 1024; var freeSpaceInMB = driveInfo.AvailableFreeSpace / 1024 / 1024;
if (freeSpaceInMB < lowDiskSpaceThreshold) if (freeSpaceInMB < lowDiskSpaceThreshold)
{ {
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 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" };
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.LowDiskSpace); issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.LowDiskSpace;
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
context.AddIssue(issue); context.AddIssue(issue);
return; return;
} }

View File

@@ -84,8 +84,7 @@ namespace GitHub.Runner.Worker
default: default:
throw new ArgumentException(HostContext.RunnerShutdownReason.ToString(), nameof(HostContext.RunnerShutdownReason)); throw new ArgumentException(HostContext.RunnerShutdownReason.ToString(), nameof(HostContext.RunnerShutdownReason));
} }
var issue = jobContext.CreateIssue(IssueType.Error, errorMessage, null, true); jobContext.AddIssue(new Issue() { Type = IssueType.Error, Message = errorMessage });
jobContext.AddIssue(issue);
}); });
// Validate directory permissions. // Validate directory permissions.

View File

@@ -27,18 +27,6 @@ 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) public static string ToBase64StringNoPaddingFromString(string utf8String)
{ {
return ToBase64StringNoPadding(Encoding.UTF8.GetBytes(utf8String)); return ToBase64StringNoPadding(Encoding.UTF8.GetBytes(utf8String));

View File

@@ -450,8 +450,6 @@ namespace GitHub.DistributedTask.WebApi
/// <param name="poolId"></param> /// <param name="poolId"></param>
/// <param name="sessionId"></param> /// <param name="sessionId"></param>
/// <param name="lastMessageId"></param> /// <param name="lastMessageId"></param>
/// <param name="status"></param>
/// <param name="runnerVersion"></param>
/// <param name="userState"></param> /// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
@@ -460,7 +458,6 @@ namespace GitHub.DistributedTask.WebApi
Guid sessionId, Guid sessionId,
long? lastMessageId = null, long? lastMessageId = null,
TaskAgentStatus? status = null, TaskAgentStatus? status = null,
string runnerVersion = null,
object userState = null, object userState = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@@ -478,16 +475,12 @@ namespace GitHub.DistributedTask.WebApi
{ {
queryParams.Add("status", status.Value.ToString()); queryParams.Add("status", status.Value.ToString());
} }
if (runnerVersion != null)
{
queryParams.Add("runnerVersion", runnerVersion);
}
return SendAsync<TaskAgentMessage>( return SendAsync<TaskAgentMessage>(
httpMethod, httpMethod,
locationId, locationId,
routeValues: routeValues, routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1), version: new ApiResourceVersion(5.1, 1),
queryParameters: queryParams, queryParameters: queryParams,
userState: userState, userState: userState,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);

View File

@@ -1,38 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using GitHub.Services.Common;
namespace GitHub.DistributedTask.WebApi 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] [DataContract]
public class Issue : IReadOnlyIssue public class Issue
{ {
public Issue() public Issue()
: this(null)
{ {
} }
private Issue(Issue original) private Issue(Issue issueToBeCloned)
{ {
this.EnsureInitialized(); this.Type = issueToBeCloned.Type;
if (original != null) this.Category = issueToBeCloned.Category;
this.Message = issueToBeCloned.Message;
this.IsInfrastructureIssue = issueToBeCloned.IsInfrastructureIssue;
if (issueToBeCloned.m_data != null)
{ {
this.Type = original.Type; foreach (var item in issueToBeCloned.m_data)
this.Category = original.Category; {
this.Message = original.Message; this.Data.Add(item);
this.IsInfrastructureIssue = original.IsInfrastructureIssue; }
m_data.AddRange(original.m_data);
} }
} }
@@ -44,14 +36,14 @@ namespace GitHub.DistributedTask.WebApi
} }
[DataMember(Order = 2)] [DataMember(Order = 2)]
public string Category public String Category
{ {
get; get;
set; set;
} }
[DataMember(Order = 3)] [DataMember(Order = 3)]
public string Message public String Message
{ {
get; get;
set; set;
@@ -64,16 +56,15 @@ namespace GitHub.DistributedTask.WebApi
set; set;
} }
public string this[string key] public IDictionary<String, String> Data
{ {
get get
{ {
m_data.TryGetValue(key, out string result); if (m_data == null)
return result;
}
set
{ {
m_data[key] = value; m_data = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
}
return m_data;
} }
} }
@@ -86,7 +77,6 @@ namespace GitHub.DistributedTask.WebApi
private void OnDeserialized(StreamingContext context) private void OnDeserialized(StreamingContext context)
{ {
SerializationHelper.Copy(ref m_serializedData, ref m_data, StringComparer.OrdinalIgnoreCase, true); SerializationHelper.Copy(ref m_serializedData, ref m_data, StringComparer.OrdinalIgnoreCase, true);
this.EnsureInitialized();
} }
[OnSerializing] [OnSerializing]
@@ -101,21 +91,9 @@ namespace GitHub.DistributedTask.WebApi
m_serializedData = null; m_serializedData = null;
} }
/// <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)] [DataMember(Name = "Data", EmitDefaultValue = false, Order = 4)]
private IDictionary<string, string> m_serializedData; private IDictionary<String, String> m_serializedData;
private IDictionary<string, string> m_data;
private IDictionary<String, String> m_data;
} }
} }

View File

@@ -1,49 +0,0 @@
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;
}
}
}

View File

@@ -10,14 +10,11 @@ namespace GitHub.DistributedTask.WebApi
public sealed class TimelineRecord public sealed class TimelineRecord
{ {
public TimelineRecord() public TimelineRecord()
: this(null)
{ {
this.Attempt = 1;
} }
private TimelineRecord(TimelineRecord recordToBeCloned) private TimelineRecord(TimelineRecord recordToBeCloned)
{
this.EnsureInitialized();
if (recordToBeCloned != null)
{ {
this.Attempt = recordToBeCloned.Attempt; this.Attempt = recordToBeCloned.Attempt;
this.ChangeId = recordToBeCloned.ChangeId; this.ChangeId = recordToBeCloned.ChangeId;
@@ -63,23 +60,19 @@ namespace GitHub.DistributedTask.WebApi
}; };
} }
if (recordToBeCloned.m_issues?.Count > 0) if (recordToBeCloned.m_issues?.Count> 0)
{ {
m_issues.AddRange(recordToBeCloned.m_issues.Select(i => i.Clone())); this.Issues.AddRange(recordToBeCloned.Issues.Select(i => i.Clone()));
} }
if (recordToBeCloned.m_previousAttempts?.Count > 0) if (recordToBeCloned.m_previousAttempts?.Count > 0)
{ {
m_previousAttempts.AddRange(recordToBeCloned.m_previousAttempts); this.PreviousAttempts.AddRange(recordToBeCloned.PreviousAttempts);
} }
if (recordToBeCloned.m_variables?.Count > 0) if (recordToBeCloned.m_variables?.Count > 0)
{ {
// Don't pave over the case-insensitive Dictionary we initialized above. this.m_variables = recordToBeCloned.Variables.ToDictionary(k => k.Key, v => v.Value.Clone());
foreach (var kvp in recordToBeCloned.m_variables) {
m_variables[kvp.Key] = kvp.Value.Clone();
}
}
} }
} }
@@ -241,6 +234,10 @@ namespace GitHub.DistributedTask.WebApi
{ {
get get
{ {
if (m_issues == null)
{
m_issues = new List<Issue>();
}
return m_issues; return m_issues;
} }
} }
@@ -277,14 +274,22 @@ namespace GitHub.DistributedTask.WebApi
{ {
get get
{ {
if (m_previousAttempts == null)
{
m_previousAttempts = new List<TimelineAttempt>();
}
return m_previousAttempts; return m_previousAttempts;
} }
} }
public IDictionary<string, VariableValue> Variables public IDictionary<String, VariableValue> Variables
{ {
get get
{ {
if (m_variables == null)
{
m_variables = new Dictionary<String, VariableValue>(StringComparer.OrdinalIgnoreCase);
}
return m_variables; return m_variables;
} }
} }
@@ -294,32 +299,11 @@ namespace GitHub.DistributedTask.WebApi
return new TimelineRecord(this); 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)] [DataMember(Name = "Issues", EmitDefaultValue = false, Order = 60)]
private List<Issue> m_issues; private List<Issue> m_issues;
[DataMember(Name = "Variables", EmitDefaultValue = false, Order = 80)] [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)] [DataMember(Name = "PreviousAttempts", EmitDefaultValue = false, Order = 120)]
private List<TimelineAttempt> m_previousAttempts; private List<TimelineAttempt> m_previousAttempts;

View File

@@ -192,8 +192,8 @@ namespace GitHub.Runner.Common.Tests.Listener
_runnerServer _runnerServer
.Setup(x => x.GetAgentMessageAsync( .Setup(x => x.GetAgentMessageAsync(
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>())) _settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()))
.Returns(async (Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, string runnerVersion, CancellationToken cancellationToken) => .Returns(async (Int32 poolId, Guid sessionId, Int64? lastMessageId, TaskAgentStatus status, CancellationToken cancellationToken) =>
{ {
await Task.Yield(); await Task.Yield();
return messages.Dequeue(); return messages.Dequeue();
@@ -208,7 +208,7 @@ namespace GitHub.Runner.Common.Tests.Listener
//Assert //Assert
_runnerServer _runnerServer
.Verify(x => x.GetAgentMessageAsync( .Verify(x => x.GetAgentMessageAsync(
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(arMessages.Length)); _settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()), Times.Exactly(arMessages.Length));
} }
} }
@@ -293,7 +293,7 @@ namespace GitHub.Runner.Common.Tests.Listener
_runnerServer _runnerServer
.Setup(x => x.GetAgentMessageAsync( .Setup(x => x.GetAgentMessageAsync(
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>())) _settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()))
.Throws(new TaskAgentAccessTokenExpiredException("test")); .Throws(new TaskAgentAccessTokenExpiredException("test"));
try try
{ {
@@ -311,7 +311,7 @@ namespace GitHub.Runner.Common.Tests.Listener
//Assert //Assert
_runnerServer _runnerServer
.Verify(x => x.GetAgentMessageAsync( .Verify(x => x.GetAgentMessageAsync(
_settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once); _settings.PoolId, expectedSession.SessionId, It.IsAny<long?>(), TaskAgentStatus.Online, It.IsAny<CancellationToken>()), Times.Once);
_runnerServer _runnerServer
.Verify(x => x.DeleteAgentSessionAsync( .Verify(x => x.DeleteAgentSessionAsync(

View File

@@ -59,7 +59,7 @@ namespace GitHub.Runner.Common.Tests
_secretMasker = new SecretMasker(); _secretMasker = new SecretMasker();
_secretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape); _secretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
_secretMasker.AddValueEncoder(ValueEncoders.UriDataEscape); _secretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);
_traceManager = new TraceManager(traceListener, null, _secretMasker); _traceManager = new TraceManager(traceListener, _secretMasker);
_trace = GetTrace(nameof(TestHostContext)); _trace = GetTrace(nameof(TestHostContext));
// inject a terminal in silent mode so all console output // inject a terminal in silent mode so all console output

View File

@@ -2,8 +2,6 @@
using Xunit; using Xunit;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
namespace GitHub.Runner.Common.Tests namespace GitHub.Runner.Common.Tests
{ {
@@ -43,26 +41,5 @@ namespace GitHub.Runner.Common.Tests
Assert.True(Directory.Exists(testDataDir)); Assert.True(Directory.Exists(testDataDir));
return 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;
}
} }
} }

View File

@@ -32,10 +32,10 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((IReadOnlyIssue issue) => .Callback((Issue issue, string message) =>
{ {
hc.GetTrace().Info($"{issue.Type} {issue.Message}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
_commandManager.EnablePluginInternalCommand(); _commandManager.EnablePluginInternalCommand();
@@ -59,10 +59,10 @@ namespace GitHub.Runner.Common.Tests.Worker
hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((IReadOnlyIssue issue) => .Callback((Issue issue, string message) =>
{ {
hc.GetTrace().Info($"{issue.Type} {issue.Message}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
_commandManager.EnablePluginInternalCommand(); _commandManager.EnablePluginInternalCommand();
@@ -92,12 +92,10 @@ namespace GitHub.Runner.Common.Tests.Worker
return 1; return 1;
}); });
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Returns(TestUtil.CreateTestIssue); .Callback((Issue issue, string message) =>
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
.Callback((IReadOnlyIssue issue) =>
{ {
hc.GetTrace().Info($"{issue.Type} {issue.Message}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>(); _ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -13,11 +14,6 @@ using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container; using GitHub.Runner.Worker.Container;
#if OS_WINDOWS
using System.IO.Compression;
#endif
using Moq; using Moq;
using Moq.Protected; using Moq.Protected;
using Xunit; using Xunit;
@@ -2151,9 +2147,7 @@ runs:
_ec.Object.Global.FileTable = new List<String>(); _ec.Object.Global.FileTable = new List<String>();
_ec.Object.Global.Plan = new TaskOrchestrationPlanReference(); _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.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
.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")); _ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
_dockerManager = new Mock<IDockerCommandManager>(); _dockerManager = new Mock<IDockerCommandManager>();

View File

@@ -4,10 +4,10 @@ using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Expressions; using GitHub.Runner.Worker.Expressions;
using GitHub.Services.Common;
using Moq; using Moq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -715,7 +715,7 @@ namespace GitHub.Runner.Common.Tests.Worker
//Assert //Assert
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path)); var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
Assert.Contains($"Fail to load {action_path}", err.Message); Assert.Contains($"Fail to load {action_path}", err.Message);
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'."))), Times.Once); _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);
} }
finally finally
{ {
@@ -860,10 +860,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData()); _ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>()); _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.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() private void Teardown()

View File

@@ -3,15 +3,20 @@ using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines; using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.Pipelines.ContextData; using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers; using GitHub.Runner.Worker.Handlers;
using GitHub.Services.Common;
using Moq; using Moq;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Xunit; using Xunit;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
@@ -325,7 +330,7 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("invalid1", finialInputs["invalid1"]); Assert.Equal("invalid1", finialInputs["invalid1"]);
Assert.Equal("invalid2", finialInputs["invalid2"]); Assert.Equal("invalid2", finialInputs["invalid2"]);
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'"))), Times.Once); _ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'")), It.IsAny<string>()), Times.Once);
} }
[Fact] [Fact]
@@ -444,9 +449,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token); _ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
_ec.Object.Global.Variables = new Variables(_hc, new Dictionary<string, VariableValue>()); _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.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
.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<IActionManager>(_actionManager.Object);
_hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object); _hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object);

View File

@@ -11,7 +11,6 @@ using Xunit;
using DTWebApi = GitHub.DistributedTask.WebApi; using DTWebApi = GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.Services.Common;
namespace GitHub.Runner.Common.Tests.Worker namespace GitHub.Runner.Common.Tests.Worker
{ {
@@ -20,7 +19,7 @@ namespace GitHub.Runner.Common.Tests.Worker
private Mock<IExecutionContext> _executionContext; private Mock<IExecutionContext> _executionContext;
private Mock<IJobServerQueue> _jobServerQueue; private Mock<IJobServerQueue> _jobServerQueue;
private ExecutionContext _jobExecutionContext; private ExecutionContext _jobExecutionContext;
private List<DTWebApi.IReadOnlyIssue> _issues; private List<Tuple<DTWebApi.Issue, string>> _issues;
private Variables _variables; private Variables _variables;
private string _rootDirectory; private string _rootDirectory;
private CreateStepSummaryCommand _createStepCommand; private CreateStepSummaryCommand _createStepCommand;
@@ -187,7 +186,7 @@ namespace GitHub.Runner.Common.Tests.Worker
{ {
var hostContext = new TestHostContext(this, name); var hostContext = new TestHostContext(this, name);
_issues = new List<DTWebApi.IReadOnlyIssue>(); _issues = new List<Tuple<DTWebApi.Issue, string>>();
// Setup a job request // Setup a job request
TaskOrchestrationPlanReference plan = new(); TaskOrchestrationPlanReference plan = new();
@@ -248,14 +247,13 @@ namespace GitHub.Runner.Common.Tests.Worker
WriteDebug = true, WriteDebug = true,
Variables = _variables, Variables = _variables,
}); });
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>())) _executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.IReadOnlyIssue issue) => .Callback((DTWebApi.Issue issue, string logMessage) =>
{ {
_issues.Add(issue); _issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
_trace.Info($"Issue '{issue.Type}': {issue.Message}"); var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {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>())) _executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) => .Callback((string tag, string message) =>
{ {

View File

@@ -52,36 +52,36 @@ namespace GitHub.Runner.Common.Tests.Worker
// Act. // Act.
ec.InitializeJob(jobRequest, CancellationToken.None); ec.InitializeJob(jobRequest, CancellationToken.None);
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.Complete(); ec.Complete();
@@ -190,9 +190,9 @@ namespace GitHub.Runner.Common.Tests.Worker
bigMessage += "a"; bigMessage += "a";
} }
ec.AddIssue(ec.CreateIssue(IssueType.Error, bigMessage, null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = bigMessage });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, bigMessage, null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = bigMessage });
ec.AddIssue(ec.CreateIssue(IssueType.Notice, bigMessage, null, true)); ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = bigMessage });
ec.Complete(); 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); var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
embeddedStep.Start(); embeddedStep.Start();
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Error, "error annotation that should have step and line number information", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error annotation that should have step and line number information" });
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Warning, "warning annotation that should have step and line number information", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning annotation that should have step and line number information" });
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Notice, "notice annotation that should have step and line number information", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice annotation that should have step and line number information" });
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.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["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.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["stepNumber"] != null && i["logFileLineNumber"] != null && 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.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && 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.StepId = Guid.NewGuid();
ec.StepTelemetry.Stage = "main"; ec.StepTelemetry.Stage = "main";
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true)); ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
ec.Complete(); ec.Complete();
@@ -692,9 +692,9 @@ namespace GitHub.Runner.Common.Tests.Worker
embeddedStep.StepTelemetry.Action = "actions/checkout"; embeddedStep.StepTelemetry.Action = "actions/checkout";
embeddedStep.StepTelemetry.Ref = "v2"; embeddedStep.StepTelemetry.Ref = "v2";
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true)); embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
embeddedStep.PublishStepTelemetry(); embeddedStep.PublishStepTelemetry();

View File

@@ -9,7 +9,6 @@ using GitHub.Runner.Sdk;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container; using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers; using GitHub.Runner.Worker.Handlers;
using GitHub.Services.Common;
using Moq; using Moq;
using Xunit; using Xunit;
using DTWebApi = GitHub.DistributedTask.WebApi; using DTWebApi = GitHub.DistributedTask.WebApi;
@@ -22,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
private Mock<IActionCommandManager> _commandManager; private Mock<IActionCommandManager> _commandManager;
private Variables _variables; private Variables _variables;
private OnMatcherChanged _onMatcherChanged; private OnMatcherChanged _onMatcherChanged;
private List<DTWebApi.IReadOnlyIssue> _issues; private List<Tuple<DTWebApi.Issue, string>> _issues;
private List<string> _messages; private List<string> _messages;
private List<string> _commands; private List<string> _commands;
private OutputManager _outputManager; private OutputManager _outputManager;
@@ -83,10 +82,10 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("ERROR: message 4"); Process("ERROR: message 4");
Process("NOT GOOD: message 5"); Process("NOT GOOD: message 5");
Assert.Equal(4, _issues.Count); Assert.Equal(4, _issues.Count);
Assert.Equal("message 1", _issues[0].Message); Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Message); Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 3", _issues[2].Message); Assert.Equal("message 3", _issues[2].Item1.Message);
Assert.Equal("message 5", _issues[3].Message); Assert.Equal("message 5", _issues[3].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(1, _messages.Count); Assert.Equal(1, _messages.Count);
Assert.Equal("ERROR: message 4", _messages[0]); Assert.Equal("ERROR: message 4", _messages[0]);
@@ -149,11 +148,11 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("ERROR: message 4"); Process("ERROR: message 4");
Process("NOT GOOD: message 5"); Process("NOT GOOD: message 5");
Assert.Equal(5, _issues.Count); Assert.Equal(5, _issues.Count);
Assert.Equal("message 1", _issues[0].Message); Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Message); Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 3", _issues[2].Message); Assert.Equal("message 3", _issues[2].Item1.Message);
Assert.Equal("message 4", _issues[3].Message); Assert.Equal("message 4", _issues[3].Item1.Message);
Assert.Equal("message 5", _issues[4].Message); Assert.Equal("message 5", _issues[4].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count); Assert.Equal(0, _messages.Count);
} }
@@ -189,10 +188,10 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("BAD: real bad"); Process("BAD: real bad");
Process(": not working"); Process(": not working");
Assert.Equal(2, _issues.Count); Assert.Equal(2, _issues.Count);
Assert.Equal("real bad", _issues[0].Message); Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal("BAD", _issues[0]["code"]); Assert.Equal("BAD", _issues[0].Item1.Data["code"]);
Assert.Equal("not working", _issues[1].Message); Assert.Equal("not working", _issues[1].Item1.Message);
Assert.Null(_issues[1]["code"]); Assert.False(_issues[1].Item1.Data.ContainsKey("code"));
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count); Assert.Equal(0, _messages.Count);
} }
@@ -239,11 +238,11 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("Error: real bad"); Process("Error: real bad");
Process("regular message 2"); Process("regular message 2");
Assert.Equal(5, _issues.Count); Assert.Equal(5, _issues.Count);
Assert.Equal("it broke", _issues[0].Message); Assert.Equal("it broke", _issues[0].Item1.Message);
Assert.Equal("oh no", _issues[1].Message); Assert.Equal("oh no", _issues[1].Item1.Message);
Assert.Equal("not good", _issues[2].Message); Assert.Equal("not good", _issues[2].Item1.Message);
Assert.Equal("it broke again", _issues[3].Message); Assert.Equal("it broke again", _issues[3].Item1.Message);
Assert.Equal("real bad", _issues[4].Message); Assert.Equal("real bad", _issues[4].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(4, _messages.Count); Assert.Equal(4, _messages.Count);
Assert.Equal("Start: hello", _messages[0]); Assert.Equal("Start: hello", _messages[0]);
@@ -294,8 +293,8 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("ERROR: it is broken"); Process("ERROR: it is broken");
Process("NOT GOOD: that did not work"); Process("NOT GOOD: that did not work");
Assert.Equal(2, _issues.Count); Assert.Equal(2, _issues.Count);
Assert.Equal("it is broken", _issues[0].Message); Assert.Equal("it is broken", _issues[0].Item1.Message);
Assert.Equal("that did not work", _issues[1].Message); Assert.Equal("that did not work", _issues[1].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count); Assert.Equal(0, _messages.Count);
} }
@@ -333,15 +332,15 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("(12,thirty-four): it is broken"); Process("(12,thirty-four): it is broken");
Process("(twelve,34): not working"); Process("(twelve,34): not working");
Assert.Equal(3, _issues.Count); Assert.Equal(3, _issues.Count);
Assert.Equal("real bad", _issues[0].Message); Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal("12", _issues[0]["line"]); Assert.Equal("12", _issues[0].Item1.Data["line"]);
Assert.Equal("34", _issues[0]["col"]); Assert.Equal("34", _issues[0].Item1.Data["col"]);
Assert.Equal("it is broken", _issues[1].Message); Assert.Equal("it is broken", _issues[1].Item1.Message);
Assert.Equal("12", _issues[1]["line"]); Assert.Equal("12", _issues[1].Item1.Data["line"]);
Assert.Null(_issues[1]["col"]); Assert.False(_issues[1].Item1.Data.ContainsKey("col"));
Assert.Equal("not working", _issues[2].Message); Assert.Equal("not working", _issues[2].Item1.Message);
Assert.Null(_issues[2]["line"]); Assert.False(_issues[2].Item1.Data.ContainsKey("line"));
Assert.Equal("34", _issues[2]["col"]); Assert.Equal("34", _issues[2].Item1.Data["col"]);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Count); Assert.Equal(2, _messages.Count);
Assert.Equal("##[debug]Unable to parse column number 'thirty-four'", _messages[0]); Assert.Equal("##[debug]Unable to parse column number 'thirty-four'", _messages[0]);
@@ -374,8 +373,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("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"); Process("##[not-command]this line is an ERROR: it is broken again");
Assert.Equal(2, _issues.Count); Assert.Equal(2, _issues.Count);
Assert.Equal("it is broken", _issues[0].Message); Assert.Equal("it is broken", _issues[0].Item1.Message);
Assert.Equal("it is broken again", _issues[1].Message); Assert.Equal("it is broken again", _issues[1].Item1.Message);
Assert.Equal(2, _commands.Count); 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("##[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]); Assert.Equal("this line is a command too ##[some-command]even though it contains ERROR: not working again", _commands[1]);
@@ -405,7 +404,8 @@ namespace GitHub.Runner.Common.Tests.Worker
}); });
Process("the error: \033[31mred, \033[1;31mbright red, \033[mreset"); Process("the error: \033[31mred, \033[1;31mbright red, \033[mreset");
Assert.Equal(1, _issues.Count); Assert.Equal(1, _issues.Count);
Assert.Equal("red, bright red, reset", _issues[0].Message); Assert.Equal("red, bright red, reset", _issues[0].Item1.Message);
Assert.Equal("the error: red, bright red, reset", _issues[0].Item2);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count); Assert.Equal(0, _messages.Count);
} }
@@ -455,9 +455,9 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("ERROR: message 3"); Process("ERROR: message 3");
Process("NOT GOOD: message 4"); Process("NOT GOOD: message 4");
Assert.Equal(3, _issues.Count); Assert.Equal(3, _issues.Count);
Assert.Equal("message 1", _issues[0].Message); Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Message); Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 4", _issues[2].Message); Assert.Equal("message 4", _issues[2].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(1, _messages.Count); Assert.Equal(1, _messages.Count);
Assert.Equal("ERROR: message 3", _messages[0]); 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("Matches both line 1: hello again");
Process("oh no, another error"); Process("oh no, another error");
Assert.Equal(2, _issues.Count); Assert.Equal(2, _issues.Count);
Assert.Equal("it broke", _issues[0].Message); Assert.Equal("it broke", _issues[0].Item1.Message);
Assert.Equal("oh no, another error", _issues[1].Message); Assert.Equal("oh no, another error", _issues[1].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(4, _messages.Count); Assert.Equal(4, _messages.Count);
Assert.Equal("Matches both line 1: hello", _messages[0]); Assert.Equal("Matches both line 1: hello", _messages[0]);
@@ -573,14 +573,14 @@ namespace GitHub.Runner.Common.Tests.Worker
Process(": not working"); Process(": not working");
Process("ERROR! uh oh"); Process("ERROR! uh oh");
Assert.Equal(4, _issues.Count); Assert.Equal(4, _issues.Count);
Assert.Equal("real bad", _issues[0].Message); Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Type); Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Item1.Type);
Assert.Equal("not great", _issues[1].Message); Assert.Equal("not great", _issues[1].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Type); Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Item1.Type);
Assert.Equal("not working", _issues[2].Message); Assert.Equal("not working", _issues[2].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Type); Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Item1.Type);
Assert.Equal("uh oh", _issues[3].Message); Assert.Equal("uh oh", _issues[3].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Type); Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Item1.Type);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Count); Assert.Equal(2, _messages.Count);
Assert.StartsWith("##[debug]Skipped", _messages[0]); Assert.StartsWith("##[debug]Skipped", _messages[0]);
@@ -633,9 +633,9 @@ namespace GitHub.Runner.Common.Tests.Worker
Process("jane.doe@contoso.com"); Process("jane.doe@contoso.com");
Process("ERR: this error"); Process("ERR: this error");
Assert.Equal(3, _issues.Count); Assert.Equal(3, _issues.Count);
Assert.Equal("john.doe@contoso.com", _issues[0].Message); Assert.Equal("john.doe@contoso.com", _issues[0].Item1.Message);
Assert.Contains("Removing issue matcher 'email'", _issues[1].Message); Assert.Contains("Removing issue matcher 'email'", _issues[1].Item1.Message);
Assert.Equal("this error", _issues[2].Message); Assert.Equal("this error", _issues[2].Item1.Message);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Where(x => x.StartsWith("##[debug]Timeout processing issue matcher")).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()); 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(9, _issues.Count);
Assert.Equal("some error 1", _issues[0].Message); Assert.Equal("some error 1", _issues[0].Item1.Message);
Assert.Null(_issues[0]["file"]); Assert.False(_issues[0].Item1.Data.ContainsKey("file"));
Assert.Equal("some error 2", _issues[1].Message); Assert.Equal("some error 2", _issues[1].Item1.Message);
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1]["file"]); Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1].Item1.Data["file"]);
Assert.Equal("some error 3", _issues[2].Message); Assert.Equal("some error 3", _issues[2].Item1.Message);
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2]["file"]); Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2].Item1.Data["file"]);
Assert.Equal("some error 4", _issues[3].Message); 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]["file"]); Assert.Equal(file_workflowRepository_nestedDirectory.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[3].Item1.Data["file"]);
Assert.Equal("some error 5", _issues[4].Message); Assert.Equal("some error 5", _issues[4].Item1.Message);
Assert.Null(_issues[4]["file"]); Assert.False(_issues[4].Item1.Data.ContainsKey("file"));
Assert.Equal("some error 6", _issues[5].Message); Assert.Equal("some error 6", _issues[5].Item1.Message);
Assert.Null(_issues[5]["file"]); Assert.False(_issues[5].Item1.Data.ContainsKey("file"));
Assert.Equal("some error 7", _issues[6].Message); Assert.Equal("some error 7", _issues[6].Item1.Message);
Assert.Null(_issues[6]["file"]); Assert.False(_issues[6].Item1.Data.ContainsKey("file"));
Assert.Equal("some error 8", _issues[7].Message); Assert.Equal("some error 8", _issues[7].Item1.Message);
Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7]["file"]); Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7].Item1.Data["file"]);
Assert.Equal("some error 9", _issues[8].Message); Assert.Equal("some error 9", _issues[8].Item1.Message);
Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8]["file"]); Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8].Item1.Data["file"]);
} }
Environment.SetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE", ""); 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(2, _issues.Count);
Assert.Equal("some error 1", _issues[0].Message); Assert.Equal("some error 1", _issues[0].Item1.Message);
Assert.Equal("some-file.txt", _issues[0]["file"]); Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
Assert.Equal("some error 2", _issues[1].Message); Assert.Equal("some error 2", _issues[1].Item1.Message);
Assert.Equal("some-file.txt", _issues[1]["file"]); Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
} }
} }
@@ -871,11 +871,11 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal(2, _issues.Count); Assert.Equal(2, _issues.Count);
Assert.Equal("some error 1", _issues[0].Message); Assert.Equal("some error 1", _issues[0].Item1.Message);
Assert.Equal("some-file.txt", _issues[0]["file"]); Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
Assert.Equal("some error 2", _issues[1].Message); Assert.Equal("some error 2", _issues[1].Item1.Message);
Assert.Equal("some-file.txt", _issues[1]["file"]); Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
} }
} }
#endif #endif
@@ -929,8 +929,8 @@ namespace GitHub.Runner.Common.Tests.Worker
// Process // Process
Process("some-directory/some-file.txt: some error [workflow-repo/some-project/some-project.proj]"); Process("some-directory/some-file.txt: some error [workflow-repo/some-project/some-project.proj]");
Assert.Equal(1, _issues.Count); Assert.Equal(1, _issues.Count);
Assert.Equal("some error", _issues[0].Message); Assert.Equal("some error", _issues[0].Item1.Message);
Assert.Equal("some-project/some-directory/some-file.txt", _issues[0]["file"]); Assert.Equal("some-project/some-directory/some-file.txt", _issues[0].Item1.Data["file"]);
Assert.Equal(0, _commands.Count); Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count); Assert.Equal(0, _messages.Count);
} }
@@ -958,7 +958,7 @@ namespace GitHub.Runner.Common.Tests.Worker
matchers?.Validate(); matchers?.Validate();
_onMatcherChanged = null; _onMatcherChanged = null;
_issues = new List<DTWebApi.IReadOnlyIssue>(); _issues = new List<Tuple<DTWebApi.Issue, string>>();
_messages = new List<string>(); _messages = new List<string>();
_commands = new List<string>(); _commands = new List<string>();
@@ -983,12 +983,10 @@ namespace GitHub.Runner.Common.Tests.Worker
{ {
_onMatcherChanged = handler; _onMatcherChanged = handler;
}); });
_executionContext.Setup(x => x.CreateIssue(It.IsAny<DTWebApi.IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>())) _executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Returns(TestUtil.CreateTestIssue); .Callback((DTWebApi.Issue issue, string logMessage) =>
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
.Callback((DTWebApi.IReadOnlyIssue issue) =>
{ {
_issues.Add(issue); _issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
}); });
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) => .Callback((string tag, string message) =>

View File

@@ -21,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
public sealed class SaveStateFileCommandL0 public sealed class SaveStateFileCommandL0
{ {
private Mock<IExecutionContext> _executionContext; private Mock<IExecutionContext> _executionContext;
private List<DTWebApi.IReadOnlyIssue> _issues; private List<Tuple<DTWebApi.Issue, string>> _issues;
private string _rootDirectory; private string _rootDirectory;
private SaveStateFileCommand _saveStateFileCommand; private SaveStateFileCommand _saveStateFileCommand;
private Dictionary<string, string> _intraActionState; private Dictionary<string, string> _intraActionState;
@@ -390,7 +390,7 @@ namespace GitHub.Runner.Common.Tests.Worker
private TestHostContext Setup([CallerMemberName] string name = "") private TestHostContext Setup([CallerMemberName] string name = "")
{ {
_issues = new List<DTWebApi.IReadOnlyIssue>(); _issues = new List<Tuple<DTWebApi.Issue, string>>();
_intraActionState = new Dictionary<string, string>(); _intraActionState = new Dictionary<string, string>();
var hostContext = new TestHostContext(this, name); var hostContext = new TestHostContext(this, name);
@@ -413,11 +413,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer), EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true, WriteDebug = true,
}); });
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>())) _executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.IReadOnlyIssue issue) => .Callback((DTWebApi.Issue issue, string logMessage) =>
{ {
_issues.Add(issue); _issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
_trace.Info($"Issue '{issue.Type}': {issue.Message}"); var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
}); });
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) => .Callback((string tag, string message) =>

View File

@@ -1,11 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers;
using Moq; using Moq;
using Xunit; using Xunit;
using DTWebApi = GitHub.DistributedTask.WebApi; using DTWebApi = GitHub.DistributedTask.WebApi;
@@ -15,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
public sealed class SetEnvFileCommandL0 public sealed class SetEnvFileCommandL0
{ {
private Mock<IExecutionContext> _executionContext; private Mock<IExecutionContext> _executionContext;
private List<DTWebApi.IReadOnlyIssue> _issues; private List<Tuple<DTWebApi.Issue, string>> _issues;
private string _rootDirectory; private string _rootDirectory;
private SetEnvFileCommand _setEnvFileCommand; private SetEnvFileCommand _setEnvFileCommand;
private ITraceWriter _trace; private ITraceWriter _trace;
@@ -383,7 +389,7 @@ namespace GitHub.Runner.Common.Tests.Worker
private TestHostContext Setup([CallerMemberName] string name = "") private TestHostContext Setup([CallerMemberName] string name = "")
{ {
_issues = new List<DTWebApi.IReadOnlyIssue>(); _issues = new List<Tuple<DTWebApi.Issue, string>>();
var hostContext = new TestHostContext(this, name); var hostContext = new TestHostContext(this, name);
@@ -405,11 +411,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer), EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true, WriteDebug = true,
}); });
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>())) _executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.IReadOnlyIssue issue) => .Callback((DTWebApi.Issue issue, string logMessage) =>
{ {
_issues.Add(issue); _issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
_trace.Info($"Issue '{issue.Type}': {issue.Message}"); var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
}); });
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) => .Callback((string tag, string message) =>

View File

@@ -1,11 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers;
using Moq; using Moq;
using Xunit; using Xunit;
using DTWebApi = GitHub.DistributedTask.WebApi; using DTWebApi = GitHub.DistributedTask.WebApi;
@@ -15,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
public sealed class SetOutputFileCommandL0 public sealed class SetOutputFileCommandL0
{ {
private Mock<IExecutionContext> _executionContext; private Mock<IExecutionContext> _executionContext;
private List<DTWebApi.IReadOnlyIssue> _issues; private List<Tuple<DTWebApi.Issue, string>> _issues;
private Dictionary<string, string> _outputs; private Dictionary<string, string> _outputs;
private string _rootDirectory; private string _rootDirectory;
private SetOutputFileCommand _setOutputFileCommand; private SetOutputFileCommand _setOutputFileCommand;
@@ -384,7 +390,7 @@ namespace GitHub.Runner.Common.Tests.Worker
private TestHostContext Setup([CallerMemberName] string name = "") private TestHostContext Setup([CallerMemberName] string name = "")
{ {
_issues = new List<DTWebApi.IReadOnlyIssue>(); _issues = new List<Tuple<DTWebApi.Issue, string>>();
_outputs = new Dictionary<string, string>(); _outputs = new Dictionary<string, string>();
var hostContext = new TestHostContext(this, name); var hostContext = new TestHostContext(this, name);
@@ -407,11 +413,12 @@ namespace GitHub.Runner.Common.Tests.Worker
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer), EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
WriteDebug = true, WriteDebug = true,
}); });
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>())) _executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.IReadOnlyIssue issue) => .Callback((DTWebApi.Issue issue, string logMessage) =>
{ {
_issues.Add(issue); _issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
_trace.Info($"Issue '{issue.Type}': {issue.Message}"); var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
_trace.Info($"Issue '{issue.Type}': {message}");
}); });
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) => .Callback((string tag, string message) =>