mirror of
https://github.com/actions/runner.git
synced 2025-12-11 21:06:55 +00:00
Compare commits
1 Commits
v2.284.0
...
users/pje/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3ab3f746f |
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -54,4 +54,4 @@
|
|||||||
"requireExactSource": false,
|
"requireExactSource": false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
# ADR 1438: Support Conditionals In Composite Actions
|
|
||||||
|
|
||||||
**Date**: 2021-10-13
|
|
||||||
|
|
||||||
**Status**: Accepted
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
We recently shipped composite actions, which allows you to reuse individual steps inside an action.
|
|
||||||
However, one of the [most requested features](https://github.com/actions/runner/issues/834) has been a way to support the `if` keyword.
|
|
||||||
|
|
||||||
### Goals
|
|
||||||
- We want to keep consistent with current behavior
|
|
||||||
- We want to support conditionals via the `if` keyword
|
|
||||||
- Our built in functions like `success` should be implementable without calling them, for example you can do `job.status == success` rather then `success()` currently.
|
|
||||||
|
|
||||||
### How does composite currently work?
|
|
||||||
|
|
||||||
Currently, we have limited conditional support in composite actions for `pre` and `post` steps.
|
|
||||||
These are based on the `job status`, and support keywords like `always()`, `failed()`, `success()` and `cancelled()`.
|
|
||||||
However, generic or main steps do **not** support conditionals.
|
|
||||||
|
|
||||||
By default, in a regular workflow, a step runs on the `success()` condition. Which looks at the **job** **status**, sees if it is successful and runs.
|
|
||||||
|
|
||||||
By default, in a composite action, main steps run until a single step fails in that composite action, then the composite action is halted early. It does **not** care about the job status.
|
|
||||||
Pre, and post steps in composite actions use the job status to determine if they should run.
|
|
||||||
|
|
||||||
### How do we go forward?
|
|
||||||
|
|
||||||
Well, if we think about what composite actions are currently doing when invoking main steps, they are checking if the current composite action is successful.
|
|
||||||
Lets formalize that concept into a "real" idea.
|
|
||||||
|
|
||||||
- We will add an `action_status` field to the github context to mimic the [job's context status](https://docs.github.com/en/actions/learn-github-actions/contexts#job-context).
|
|
||||||
- We have an existing concept that does this `action_path` which is only set for composite actions on the github context.
|
|
||||||
- In a composite action during a main step, the `success()` function will check if `action_status == success`, rather then `job_status == success`. Failure will work the same way.
|
|
||||||
- Pre and post steps in composite actions will not change, they will continue to check the job status.
|
|
||||||
|
|
||||||
|
|
||||||
### Nested Scenario
|
|
||||||
For nested composite actions, we will follow the existing behavior, you only care about your current composite action, not any parents.
|
|
||||||
For example, lets imagine a scenario with a simple nested composite action
|
|
||||||
|
|
||||||
```
|
|
||||||
- Job
|
|
||||||
- Regular Step
|
|
||||||
- Composite Action
|
|
||||||
- runs: exit 1
|
|
||||||
- if: always()
|
|
||||||
uses: A child composite action
|
|
||||||
- if: success()
|
|
||||||
runs: echo "this should print"
|
|
||||||
- runs: echo "this should also print"
|
|
||||||
- if: success()
|
|
||||||
runs: echo "this will not print as the current composite action has failed already"
|
|
||||||
|
|
||||||
```
|
|
||||||
The child composite actions steps should run in this example, the child composite action has not yet failed, so it should run all steps until a step fails. This is consistent with how a composite action currently works in production if the main job fails but a composite action is invoked with `if:always()` or `if: failure()`
|
|
||||||
|
|
||||||
### Other options explored
|
|
||||||
We could add the `current_step_status` to the job context rather then `__status` to the steps context, however this comes with two major downsides:
|
|
||||||
- We need to support the field for every type of step, because its non trivial to remove a field from the job context once it has been added (its readonly)
|
|
||||||
- For all actions besides composite it would only every be `success`
|
|
||||||
- Its weird to have a `current_step` value on the job context
|
|
||||||
- We also explored a `__status` on the steps context.
|
|
||||||
- The `__` is required to prevent us from colliding with a step with id: status
|
|
||||||
- This felt wrong because the naming was not smooth, and did not fit into current conventions.
|
|
||||||
|
|
||||||
### Consequences
|
|
||||||
- github context has a new field for the status of the current composite action.
|
|
||||||
- We support conditional's in composite actions
|
|
||||||
- We keep the existing behavior for all users, but allow them to expand that functionality.
|
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Expose GITHUB_REF_* as environment variable (#1314)
|
- Support the `--ephemeral` flag (#660)
|
||||||
- Add arch to runner context (#1372)
|
- This optional flag will configure the runner to only take one job, and let the service un-configure the runner after that job finishes.
|
||||||
- Support Conditional Steps in Composite Actions (#1438)
|
- Expect to see more info in the Github API documentation soon. We'll link to those docs directly as they become generally available!
|
||||||
- Log current runner version in terminal (#1441)
|
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
- Makes the user keychains available to the service (#847)
|
- Fix a bug in `script/delete` wherein a repo with multiple runners would be unable to find the correct runner (#1268) (#1269)
|
||||||
- Use Actions Service health and api.github.com endpoints after connection failure on Actions Server and Hosted (#1385)
|
- Mitigate a race condition when requesting an OIDC `Id_token` (#1320)
|
||||||
- Fix an issue where nested local composite actions did not correctly register post steps (#1433)
|
- Make client retries more resilient in JobServer (#1316)
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
- Cleanup Older versions on MacOS now that we recreate node versions as needed (#1410)
|
- Increase readability of colored console output (#1295) (#1319)
|
||||||
|
- Add more network troubleshooting to the docs (#1325)
|
||||||
|
- Bump [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7 (#1256)
|
||||||
|
|
||||||
## Windows x64
|
## Windows x64
|
||||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.284.0
|
<Update to ./src/runnerversion when creating release>
|
||||||
|
|||||||
@@ -25,7 +25,5 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>ProcessType</key>
|
<key>ProcessType</key>
|
||||||
<string>Interactive</string>
|
<string>Interactive</string>
|
||||||
<key>SessionCreate</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
|
|||||||
logfile="_UPDATE_LOG_"
|
logfile="_UPDATE_LOG_"
|
||||||
restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
|
restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
|
||||||
|
|
||||||
telemetryfile="$rootfolder/_diag/.telemetry"
|
|
||||||
|
|
||||||
# log user who run the script
|
# log user who run the script
|
||||||
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
|
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
|
||||||
whoami >> "$logfile" 2>&1
|
whoami >> "$logfile" 2>&1
|
||||||
@@ -120,55 +118,40 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# fix upgrade issue with macOS when running as a service
|
# fix upgrade issue with macOS
|
||||||
attemptedtargetedfix=0
|
|
||||||
currentplatform=$(uname | awk '{print tolower($0)}')
|
currentplatform=$(uname | awk '{print tolower($0)}')
|
||||||
if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
if [[ "$currentplatform" == 'darwin' ]]; then
|
||||||
# We needed a fix for https://github.com/actions/runner/issues/743
|
# need a short-term fix for https://github.com/actions/runner/issues/743
|
||||||
# We will recreate the ./externals/node12/bin/node of the past runner version that launched the runnerlistener service
|
# we will recreate all the ./externals/node12/bin/node of the past 5 versions
|
||||||
# Otherwise mac gatekeeper kills the processes we spawn on creation as we are running a process with no backing file
|
# v2.280.3 v2.280.2 v2.280.1 v2.279.0 v2.278.0
|
||||||
|
if [[ ! -e "$rootfolder/externals.2.280.3/node12/bin/node" ]]
|
||||||
# We need the pid for the nodejs loop, get that here, its the parent of the runner C# pid
|
|
||||||
# assumption here is only one process is invoking rootfolder/runsvc.sh
|
|
||||||
procgroup=$(ps x -o pgid,command | grep "$rootfolder/runsvc.sh" | grep -v grep | awk '{print $1}')
|
|
||||||
if [[ $? -eq 0 && -n "$procgroup" ]]
|
|
||||||
then
|
then
|
||||||
# inspect the open file handles to find the node process
|
mkdir -p "$rootfolder/externals.2.280.3/node12/bin"
|
||||||
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
|
cp "$rootfolder/externals/node12/bin/node" "$rootfolder/externals.2.280.3/node12/bin/node"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep node12/bin/node | grep externals | tail -1 | cut -c2-)
|
fi
|
||||||
if [[ $? -eq 0 && -n "$path" ]]
|
|
||||||
then
|
if [[ ! -e "$rootfolder/externals.2.280.2/node12/bin/node" ]]
|
||||||
# trim the last 5 characters of the path '/node'
|
then
|
||||||
trimmedpath=$(dirname "$path")
|
mkdir -p "$rootfolder/externals.2.280.2/node12/bin"
|
||||||
if [[ $? -eq 0 && -n "$trimmedpath" ]]
|
cp "$rootfolder/externals/node12/bin/node" "$rootfolder/externals.2.280.2/node12/bin/node"
|
||||||
then
|
fi
|
||||||
attemptedtargetedfix=1
|
|
||||||
# Create the path if it does not exist
|
if [[ ! -e "$rootfolder/externals.2.280.1/node12/bin/node" ]]
|
||||||
if [[ ! -e "$path" ]]
|
then
|
||||||
then
|
mkdir -p "$rootfolder/externals.2.280.1/node12/bin"
|
||||||
date "+[%F %T-%4N] Creating fallback node at path $path" >> "$logfile" 2>&1
|
cp "$rootfolder/externals/node12/bin/node" "$rootfolder/externals.2.280.1/node12/bin/node"
|
||||||
mkdir -p "$trimmedpath"
|
fi
|
||||||
cp "$rootfolder/externals/node12/bin/node" "$path"
|
|
||||||
else
|
if [[ ! -e "$rootfolder/externals.2.279.0/node12/bin/node" ]]
|
||||||
date "+[%F %T-%4N] Path for fallback node exists, skipping creating $path" >> "$logfile" 2>&1
|
then
|
||||||
fi
|
mkdir -p "$rootfolder/externals.2.279.0/node12/bin"
|
||||||
else
|
cp "$rootfolder/externals/node12/bin/node" "$rootfolder/externals.2.279.0/node12/bin/node"
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to trim runner path. TrimmedPath: $trimmedpath, path: $path, pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
|
fi
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to trim runner path. TrimmedPath: $trimmedpath, path: $path, pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
|
|
||||||
fi
|
if [[ ! -e "$rootfolder/externals.2.278.0/node12/bin/node" ]]
|
||||||
else
|
then
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner path. Path: $path, pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
|
mkdir -p "$rootfolder/externals.2.278.0/node12/bin"
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner path. Path: $path, pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
|
cp "$rootfolder/externals/node12/bin/node" "$rootfolder/externals.2.278.0/node12/bin/node"
|
||||||
fi
|
|
||||||
else
|
|
||||||
runproc=$(ps x -o pgid,command | grep "run.sh" | grep -v grep | awk '{print $1}')
|
|
||||||
if [[ $? -eq 0 && -n "$runproc" ]]
|
|
||||||
then
|
|
||||||
date "+[%F %T-%4N] Running as ephemeral using run.sh, no need to recreate node folder" >> "$logfile" 2>&1
|
|
||||||
else
|
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner pgid. pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
|
|
||||||
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner pgid. pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -43,21 +43,6 @@ else
|
|||||||
else
|
else
|
||||||
sleep 5
|
sleep 5
|
||||||
fi
|
fi
|
||||||
elif [[ $returnCode == 4 ]]; then
|
|
||||||
if [ ! -x "$(command -v sleep)" ]; then
|
|
||||||
if [ ! -x "$(command -v ping)" ]; then
|
|
||||||
COUNT="0"
|
|
||||||
while [[ $COUNT != 5000 ]]; do
|
|
||||||
echo "SLEEP" > /dev/null
|
|
||||||
COUNT=$[$COUNT+1]
|
|
||||||
done
|
|
||||||
else
|
|
||||||
ping -c 5 127.0.0.1 > /dev/null
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
sleep 5
|
|
||||||
fi
|
|
||||||
"$DIR"/bin/Runner.Listener run $*
|
|
||||||
else
|
else
|
||||||
exit $returnCode
|
exit $returnCode
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ namespace GitHub.Runner.Common
|
|||||||
Certificates,
|
Certificates,
|
||||||
Options,
|
Options,
|
||||||
SetupInfo,
|
SetupInfo,
|
||||||
Telemetry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Constants
|
public static class Constants
|
||||||
@@ -129,7 +128,7 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string Ephemeral = "ephemeral";
|
public static readonly string Ephemeral = "ephemeral";
|
||||||
public static readonly string Help = "help";
|
public static readonly string Help = "help";
|
||||||
public static readonly string Replace = "replace";
|
public static readonly string Replace = "replace";
|
||||||
public static readonly string Once = "once"; // Keep this around since customers still relies on it
|
public static readonly string Once = "once"; // TODO: Remove in 10/2021
|
||||||
public static readonly string RunAsService = "runasservice";
|
public static readonly string RunAsService = "runasservice";
|
||||||
public static readonly string Unattended = "unattended";
|
public static readonly string Unattended = "unattended";
|
||||||
public static readonly string Version = "version";
|
public static readonly string Version = "version";
|
||||||
@@ -155,7 +154,6 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string LowDiskSpace = "LOW_DISK_SPACE";
|
public static readonly string LowDiskSpace = "LOW_DISK_SPACE";
|
||||||
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
|
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
|
||||||
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/en/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 class RunnerEvent
|
public static class RunnerEvent
|
||||||
@@ -215,7 +213,6 @@ namespace GitHub.Runner.Common
|
|||||||
// Keep alphabetical
|
// Keep alphabetical
|
||||||
//
|
//
|
||||||
public static readonly string AllowUnsupportedCommands = "ACTIONS_ALLOW_UNSECURE_COMMANDS";
|
public static readonly string AllowUnsupportedCommands = "ACTIONS_ALLOW_UNSECURE_COMMANDS";
|
||||||
public static readonly string AllowUnsupportedStopCommandTokens = "ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS";
|
|
||||||
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
||||||
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,12 +342,6 @@ namespace GitHub.Runner.Common
|
|||||||
GetDirectory(WellKnownDirectory.Root),
|
GetDirectory(WellKnownDirectory.Root),
|
||||||
".setup_info");
|
".setup_info");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WellKnownConfigFile.Telemetry:
|
|
||||||
path = Path.Combine(
|
|
||||||
GetDirectory(WellKnownDirectory.Diag),
|
|
||||||
".telemetry");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Unexpected well known config file: '{configFile}'");
|
throw new NotSupportedException($"Unexpected well known config file: '{configFile}'");
|
||||||
|
|||||||
@@ -2,11 +2,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Runner.Sdk;
|
|
||||||
using GitHub.Services.Common;
|
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
@@ -38,11 +35,7 @@ namespace GitHub.Runner.Common
|
|||||||
public async Task ConnectAsync(VssConnection jobConnection)
|
public async Task ConnectAsync(VssConnection jobConnection)
|
||||||
{
|
{
|
||||||
_connection = jobConnection;
|
_connection = jobConnection;
|
||||||
int totalAttempts = 5;
|
int attemptCount = 5;
|
||||||
int attemptCount = totalAttempts;
|
|
||||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
|
||||||
var runnerSettings = configurationStore.GetSettings();
|
|
||||||
|
|
||||||
while (!_connection.HasAuthenticated && attemptCount-- > 0)
|
while (!_connection.HasAuthenticated && attemptCount-- > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -52,71 +45,17 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
catch (Exception ex) when (attemptCount > 0)
|
catch (Exception ex) when (attemptCount > 0)
|
||||||
{
|
{
|
||||||
Trace.Info($"Catch exception during connect. {attemptCount} attempts left.");
|
Trace.Info($"Catch exception during connect. {attemptCount} attemp left.");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
|
|
||||||
if (runnerSettings.IsHostedServer)
|
|
||||||
{
|
|
||||||
await CheckNetworkEndpointsAsync(attemptCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int attempt = totalAttempts - attemptCount;
|
await Task.Delay(100);
|
||||||
TimeSpan backoff = BackoffTimerHelper.GetExponentialBackoff(attempt, TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(3.2), TimeSpan.FromMilliseconds(100));
|
|
||||||
|
|
||||||
await Task.Delay(backoff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_taskClient = _connection.GetClient<TaskHttpClient>();
|
_taskClient = _connection.GetClient<TaskHttpClient>();
|
||||||
_hasConnection = true;
|
_hasConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckNetworkEndpointsAsync(int attemptsLeft)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Trace.Info("Requesting Actions Service health endpoint status");
|
|
||||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
|
||||||
using (var actionsClient = new HttpClient(httpClientHandler))
|
|
||||||
{
|
|
||||||
var baseUri = new Uri(_connection.Uri.GetLeftPart(UriPartial.Authority));
|
|
||||||
|
|
||||||
actionsClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
|
||||||
|
|
||||||
// Call the _apis/health endpoint, and include how many attempts are left as a URL query for easy tracking
|
|
||||||
var response = await actionsClient.GetAsync(new Uri(baseUri, $"_apis/health?_internalRunnerAttemptsLeft={attemptsLeft}"));
|
|
||||||
Trace.Info($"Actions health status code: {response.StatusCode}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Log error, but continue as this call is best-effort
|
|
||||||
Trace.Info($"Actions Service health endpoint failed due to {ex.GetType().Name}");
|
|
||||||
Trace.Error(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Trace.Info("Requesting Github API endpoint status");
|
|
||||||
// This is a dotcom public API... just call it directly
|
|
||||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
|
||||||
using (var gitHubClient = new HttpClient(httpClientHandler))
|
|
||||||
{
|
|
||||||
gitHubClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
|
||||||
|
|
||||||
// Call the api.github.com endpoint, and include how many attempts are left as a URL query for easy tracking
|
|
||||||
var response = await gitHubClient.GetAsync($"https://api.github.com?_internalRunnerAttemptsLeft={attemptsLeft}");
|
|
||||||
Trace.Info($"api.github.com status code: {response.StatusCode}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Log error, but continue as this call is best-effort
|
|
||||||
Trace.Info($"Github API endpoint failed due to {ex.GetType().Name}");
|
|
||||||
Trace.Error(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckConnection()
|
private void CheckConnection()
|
||||||
{
|
{
|
||||||
if (!_hasConnection)
|
if (!_hasConnection)
|
||||||
|
|||||||
@@ -29,10 +29,8 @@ namespace GitHub.Runner.Common
|
|||||||
// Configuration
|
// Configuration
|
||||||
Task<TaskAgent> AddAgentAsync(Int32 agentPoolId, TaskAgent agent);
|
Task<TaskAgent> AddAgentAsync(Int32 agentPoolId, TaskAgent agent);
|
||||||
Task DeleteAgentAsync(int agentPoolId, int agentId);
|
Task DeleteAgentAsync(int agentPoolId, int agentId);
|
||||||
Task DeleteAgentAsync(int agentId);
|
|
||||||
Task<List<TaskAgentPool>> GetAgentPoolsAsync(string agentPoolName = null, TaskAgentPoolType poolType = TaskAgentPoolType.Automation);
|
Task<List<TaskAgentPool>> GetAgentPoolsAsync(string agentPoolName = null, TaskAgentPoolType poolType = TaskAgentPoolType.Automation);
|
||||||
Task<List<TaskAgent>> GetAgentsAsync(int agentPoolId, string agentName = null);
|
Task<List<TaskAgent>> GetAgentsAsync(int agentPoolId, string agentName = null);
|
||||||
Task<List<TaskAgent>> GetAgentsAsync(string agentName);
|
|
||||||
Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent);
|
Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent);
|
||||||
|
|
||||||
// messagequeue
|
// messagequeue
|
||||||
@@ -254,11 +252,6 @@ namespace GitHub.Runner.Common
|
|||||||
return _genericTaskAgentClient.GetAgentsAsync(agentPoolId, agentName, false);
|
return _genericTaskAgentClient.GetAgentsAsync(agentPoolId, agentName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<TaskAgent>> GetAgentsAsync(string agentName)
|
|
||||||
{
|
|
||||||
return GetAgentsAsync(0, agentName); // search in all all agentPools
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent)
|
public Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent)
|
||||||
{
|
{
|
||||||
CheckConnection(RunnerConnectionType.Generic);
|
CheckConnection(RunnerConnectionType.Generic);
|
||||||
@@ -271,11 +264,6 @@ namespace GitHub.Runner.Common
|
|||||||
return _genericTaskAgentClient.DeleteAgentAsync(agentPoolId, agentId);
|
return _genericTaskAgentClient.DeleteAgentAsync(agentPoolId, agentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DeleteAgentAsync(int agentId)
|
|
||||||
{
|
|
||||||
return DeleteAgentAsync(0, agentId); // agentPool is ignored server side
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
// MessageQueue
|
// MessageQueue
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ namespace GitHub.Runner.Listener
|
|||||||
Constants.Runner.CommandLine.Flags.Commit,
|
Constants.Runner.CommandLine.Flags.Commit,
|
||||||
Constants.Runner.CommandLine.Flags.Ephemeral,
|
Constants.Runner.CommandLine.Flags.Ephemeral,
|
||||||
Constants.Runner.CommandLine.Flags.Help,
|
Constants.Runner.CommandLine.Flags.Help,
|
||||||
Constants.Runner.CommandLine.Flags.Once,
|
|
||||||
Constants.Runner.CommandLine.Flags.Replace,
|
Constants.Runner.CommandLine.Flags.Replace,
|
||||||
Constants.Runner.CommandLine.Flags.RunAsService,
|
Constants.Runner.CommandLine.Flags.RunAsService,
|
||||||
Constants.Runner.CommandLine.Flags.Unattended,
|
Constants.Runner.CommandLine.Flags.Unattended,
|
||||||
@@ -69,7 +68,7 @@ namespace GitHub.Runner.Listener
|
|||||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||||
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
||||||
|
|
||||||
// Keep this around since customers still relies on it
|
// TODO: Remove in 10/2021
|
||||||
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
||||||
|
|
||||||
// Constructor.
|
// Constructor.
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
bool IsConfigured();
|
bool IsConfigured();
|
||||||
Task ConfigureAsync(CommandSettings command);
|
Task ConfigureAsync(CommandSettings command);
|
||||||
Task UnconfigureAsync(CommandSettings command);
|
Task UnconfigureAsync(CommandSettings command);
|
||||||
void DeleteLocalRunnerConfig();
|
|
||||||
RunnerSettings LoadSettings();
|
RunnerSettings LoadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,38 +329,6 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete .runner and .credentials files
|
|
||||||
public void DeleteLocalRunnerConfig()
|
|
||||||
{
|
|
||||||
bool isConfigured = _store.IsConfigured();
|
|
||||||
bool hasCredentials = _store.HasCredentials();
|
|
||||||
//delete credential config files
|
|
||||||
var currentAction = "Removing .credentials";
|
|
||||||
if (hasCredentials)
|
|
||||||
{
|
|
||||||
_store.DeleteCredential();
|
|
||||||
var keyManager = HostContext.GetService<IRSAKeyManager>();
|
|
||||||
keyManager.DeleteKey();
|
|
||||||
_term.WriteSuccessMessage("Removed .credentials");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete settings config file
|
|
||||||
currentAction = "Removing .runner";
|
|
||||||
if (isConfigured)
|
|
||||||
{
|
|
||||||
_store.DeleteSettings();
|
|
||||||
_term.WriteSuccessMessage("Removed .runner");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UnconfigureAsync(CommandSettings command)
|
public async Task UnconfigureAsync(CommandSettings command)
|
||||||
{
|
{
|
||||||
string currentAction = string.Empty;
|
string currentAction = string.Empty;
|
||||||
@@ -415,7 +382,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||||
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
|
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
|
||||||
|
|
||||||
var agents = await _runnerServer.GetAgentsAsync(settings.AgentName);
|
var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName);
|
||||||
Trace.Verbose("Returns {0} agents", agents.Count);
|
Trace.Verbose("Returns {0} agents", agents.Count);
|
||||||
TaskAgent agent = agents.FirstOrDefault();
|
TaskAgent agent = agents.FirstOrDefault();
|
||||||
if (agent == null)
|
if (agent == null)
|
||||||
@@ -424,7 +391,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _runnerServer.DeleteAgentAsync(settings.AgentId);
|
await _runnerServer.DeleteAgentAsync(settings.PoolId, settings.AgentId);
|
||||||
|
|
||||||
_term.WriteLine();
|
_term.WriteLine();
|
||||||
_term.WriteSuccessMessage("Runner removed successfully");
|
_term.WriteSuccessMessage("Runner removed successfully");
|
||||||
@@ -435,7 +402,31 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
|
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteLocalRunnerConfig();
|
//delete credential config files
|
||||||
|
currentAction = "Removing .credentials";
|
||||||
|
if (hasCredentials)
|
||||||
|
{
|
||||||
|
_store.DeleteCredential();
|
||||||
|
var keyManager = HostContext.GetService<IRSAKeyManager>();
|
||||||
|
keyManager.DeleteKey();
|
||||||
|
_term.WriteSuccessMessage("Removed .credentials");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete settings config file
|
||||||
|
currentAction = "Removing .runner";
|
||||||
|
if (isConfigured)
|
||||||
|
{
|
||||||
|
_store.DeleteSettings();
|
||||||
|
_term.WriteSuccessMessage("Removed .runner");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>();
|
private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>();
|
||||||
private int _poolId;
|
private int _poolId;
|
||||||
|
RunnerSettings _runnerSetting;
|
||||||
IConfigurationStore _configurationStore;
|
|
||||||
|
|
||||||
RunnerSettings _runnerSettings;
|
|
||||||
private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}";
|
private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}";
|
||||||
|
|
||||||
// this is not thread-safe
|
// this is not thread-safe
|
||||||
@@ -57,9 +54,9 @@ namespace GitHub.Runner.Listener
|
|||||||
base.Initialize(hostContext);
|
base.Initialize(hostContext);
|
||||||
|
|
||||||
// get pool id from config
|
// get pool id from config
|
||||||
_configurationStore = hostContext.GetService<IConfigurationStore>();
|
var configurationStore = hostContext.GetService<IConfigurationStore>();
|
||||||
_runnerSettings = _configurationStore.GetSettings();
|
_runnerSetting = configurationStore.GetSettings();
|
||||||
_poolId = _runnerSettings.PoolId;
|
_poolId = _runnerSetting.PoolId;
|
||||||
|
|
||||||
int channelTimeoutSeconds;
|
int channelTimeoutSeconds;
|
||||||
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds))
|
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds))
|
||||||
@@ -664,15 +661,13 @@ namespace GitHub.Runner.Listener
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
|
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
|
||||||
|
|
||||||
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
|
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
|
||||||
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompleted)
|
if (!firstJobRequestRenewed.Task.IsCompleted)
|
||||||
{
|
{
|
||||||
// fire first renew succeed event.
|
// fire first renew succeed event.
|
||||||
firstJobRequestRenewed.TrySetResult(0);
|
firstJobRequestRenewed.TrySetResult(0);
|
||||||
|
|
||||||
// Update settings if the runner name has been changed server-side
|
|
||||||
UpdateAgentNameIfNeeded(request.ReservedAgent?.Name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encounteringError > 0)
|
if (encounteringError > 0)
|
||||||
@@ -772,27 +767,6 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAgentNameIfNeeded(string agentName)
|
|
||||||
{
|
|
||||||
var isNewAgentName = !string.Equals(_runnerSettings.AgentName, agentName, StringComparison.Ordinal);
|
|
||||||
if (!isNewAgentName || string.IsNullOrEmpty(agentName))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_runnerSettings.AgentName = agentName;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_configurationStore.SaveSettings(_runnerSettings);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Error("Cannot update the settings file:");
|
|
||||||
Trace.Error(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Best effort upload any logs for this job.
|
// Best effort upload any logs for this job.
|
||||||
private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message)
|
private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,7 +95,16 @@ namespace GitHub.Runner.Listener
|
|||||||
var unknownCommandlines = command.Validate();
|
var unknownCommandlines = command.Validate();
|
||||||
if (unknownCommandlines.Count > 0)
|
if (unknownCommandlines.Count > 0)
|
||||||
{
|
{
|
||||||
terminal.WriteError($"Unrecognized command-line input arguments: '{string.Join(", ", unknownCommandlines)}'. For usage refer to: .\\config.cmd --help or ./config.sh --help");
|
string separator;
|
||||||
|
string ext;
|
||||||
|
#if OS_WINDOWS
|
||||||
|
separator = "\\";
|
||||||
|
ext = "cmd";
|
||||||
|
#else
|
||||||
|
separator = "/";
|
||||||
|
ext = "sh";
|
||||||
|
#endif
|
||||||
|
terminal.WriteError($"Unrecognized command-line input arguments: '{string.Join(", ", unknownCommandlines)}'. For usage refer to `.{separator}config.{ext} --help`");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer to the Runner class to execute the command.
|
// Defer to the Runner class to execute the command.
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ namespace GitHub.Runner.Listener
|
|||||||
var startupTypeAsString = command.GetStartupType();
|
var startupTypeAsString = command.GetStartupType();
|
||||||
if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
|
if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
|
||||||
{
|
{
|
||||||
// We need try our best to make the startup type accurate
|
// We need try our best to make the startup type accurate
|
||||||
// The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
|
// The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
|
||||||
// At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
|
// At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
|
||||||
// We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
|
// We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
|
||||||
@@ -233,14 +233,8 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info($"Set runner startup type - {startType}");
|
Trace.Info($"Set runner startup type - {startType}");
|
||||||
HostContext.StartupType = startType;
|
HostContext.StartupType = startType;
|
||||||
|
|
||||||
if (command.RunOnce)
|
|
||||||
{
|
|
||||||
_term.WriteLine("Warning: '--once' is going to be deprecated in the future, please consider using '--ephemeral' during runner registration.", ConsoleColor.Yellow);
|
|
||||||
_term.WriteLine("https://docs.github.com/en/actions/hosting-your-own-runners/autoscaling-with-self-hosted-runners#using-ephemeral-runners-for-autoscaling", ConsoleColor.Yellow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the runner interactively or as service
|
// Run the runner interactively or as service
|
||||||
return await RunAsync(settings, command.RunOnce || settings.Ephemeral);
|
return await RunAsync(settings, command.RunOnce || settings.Ephemeral); // TODO: Remove RunOnce later.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -312,15 +306,10 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
HostContext.WritePerfCounter("SessionCreated");
|
HostContext.WritePerfCounter("SessionCreated");
|
||||||
|
|
||||||
_term.WriteLine($"Current runner version: '{BuildConstants.RunnerPackage.Version}'");
|
|
||||||
_term.WriteLine($"{DateTime.UtcNow:u}: Listening for Jobs");
|
_term.WriteLine($"{DateTime.UtcNow:u}: Listening for Jobs");
|
||||||
|
|
||||||
IJobDispatcher jobDispatcher = null;
|
IJobDispatcher jobDispatcher = null;
|
||||||
CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken);
|
CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken);
|
||||||
|
|
||||||
// Should we try to cleanup ephemeral runners
|
|
||||||
bool runOnceJobCompleted = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var notification = HostContext.GetService<IJobNotification>();
|
var notification = HostContext.GetService<IJobNotification>();
|
||||||
@@ -382,7 +371,6 @@ namespace GitHub.Runner.Listener
|
|||||||
Task completeTask = await Task.WhenAny(getNextMessage, jobDispatcher.RunOnceJobCompleted.Task);
|
Task completeTask = await Task.WhenAny(getNextMessage, jobDispatcher.RunOnceJobCompleted.Task);
|
||||||
if (completeTask == jobDispatcher.RunOnceJobCompleted.Task)
|
if (completeTask == jobDispatcher.RunOnceJobCompleted.Task)
|
||||||
{
|
{
|
||||||
runOnceJobCompleted = true;
|
|
||||||
Trace.Info("Job has finished at backend, the runner will exit since it is running under onetime use mode.");
|
Trace.Info("Job has finished at backend, the runner will exit since it is running under onetime use mode.");
|
||||||
Trace.Info("Stop message queue looping.");
|
Trace.Info("Stop message queue looping.");
|
||||||
messageQueueLoopTokenSource.Cancel();
|
messageQueueLoopTokenSource.Cancel();
|
||||||
@@ -490,12 +478,6 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
messageQueueLoopTokenSource.Dispose();
|
messageQueueLoopTokenSource.Dispose();
|
||||||
|
|
||||||
if (settings.Ephemeral && runOnceJobCompleted)
|
|
||||||
{
|
|
||||||
var configManager = HostContext.GetService<IConfigurationManager>();
|
|
||||||
configManager.DeleteLocalRunnerConfig();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (TaskAgentAccessTokenExpiredException)
|
catch (TaskAgentAccessTokenExpiredException)
|
||||||
|
|||||||
@@ -75,9 +75,11 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info($"All running job has exited.");
|
Trace.Info($"All running job has exited.");
|
||||||
|
|
||||||
// We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743
|
// We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743
|
||||||
|
#if !OS_OSX
|
||||||
// delete runner backup
|
// delete runner backup
|
||||||
DeletePreviousVersionRunnerBackup(token);
|
DeletePreviousVersionRunnerBackup(token);
|
||||||
Trace.Info($"Delete old version runner backup.");
|
Trace.Info($"Delete old version runner backup.");
|
||||||
|
#endif
|
||||||
// generate update script from template
|
// generate update script from template
|
||||||
await UpdateRunnerUpdateStateAsync("Generate and execute update script.");
|
await UpdateRunnerUpdateStateAsync("Generate and execute update script.");
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
using GitHub.DistributedTask.Pipelines;
|
||||||
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -108,18 +110,11 @@ namespace GitHub.Runner.Worker
|
|||||||
// Stop command
|
// Stop command
|
||||||
if (string.Equals(actionCommand.Command, _stopCommand, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(actionCommand.Command, _stopCommand, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ValidateStopToken(context, actionCommand.Data);
|
context.Output(input);
|
||||||
|
context.Debug("Paused processing commands until '##[{actionCommand.Data}]' is received");
|
||||||
_stopToken = actionCommand.Data;
|
_stopToken = actionCommand.Data;
|
||||||
_stopProcessCommand = true;
|
_stopProcessCommand = true;
|
||||||
_registeredCommands.Add(_stopToken);
|
_registeredCommands.Add(_stopToken);
|
||||||
if (_stopToken.Length > 6)
|
|
||||||
{
|
|
||||||
HostContext.SecretMasker.AddValue(_stopToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Output(input);
|
|
||||||
context.Debug("Paused processing commands until the token you called ::stopCommands:: with is received");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Found command
|
// Found command
|
||||||
@@ -153,42 +148,7 @@ namespace GitHub.Runner.Worker
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateStopToken(IExecutionContext context, string stopToken)
|
internal static bool EnhancedAnnotationsEnabled(IExecutionContext context) {
|
||||||
{
|
|
||||||
#if OS_WINDOWS
|
|
||||||
var envContext = context.ExpressionValues["env"] as DictionaryContextData;
|
|
||||||
#else
|
|
||||||
var envContext = context.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
|
|
||||||
#endif
|
|
||||||
var allowUnsecureStopCommandTokens = false;
|
|
||||||
allowUnsecureStopCommandTokens = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedStopCommandTokens));
|
|
||||||
if (!allowUnsecureStopCommandTokens && envContext.ContainsKey(Constants.Variables.Actions.AllowUnsupportedStopCommandTokens))
|
|
||||||
{
|
|
||||||
allowUnsecureStopCommandTokens = StringUtil.ConvertToBoolean(envContext[Constants.Variables.Actions.AllowUnsupportedStopCommandTokens].ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isTokenInvalid = _registeredCommands.Contains(stopToken)
|
|
||||||
|| string.IsNullOrEmpty(stopToken)
|
|
||||||
|| string.Equals(stopToken, "pause-logging", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (isTokenInvalid)
|
|
||||||
{
|
|
||||||
var telemetry = new JobTelemetry
|
|
||||||
{
|
|
||||||
Message = $"Invoked ::stopCommand:: with token: [{stopToken}]",
|
|
||||||
Type = JobTelemetryType.ActionCommand
|
|
||||||
};
|
|
||||||
context.JobTelemetry.Add(telemetry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTokenInvalid && !allowUnsecureStopCommandTokens)
|
|
||||||
{
|
|
||||||
throw new Exception(Constants.Runner.UnsupportedStopCommandTokenDisabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool EnhancedAnnotationsEnabled(IExecutionContext context)
|
|
||||||
{
|
|
||||||
return context.Global.Variables.GetBoolean("DistributedTask.EnhancedAnnotations") ?? false;
|
return context.Global.Variables.GetBoolean("DistributedTask.EnhancedAnnotations") ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +252,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public const String Name = "name";
|
public const String Name = "name";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string[] _setEnvBlockList =
|
private string[] _setEnvBlockList =
|
||||||
{
|
{
|
||||||
"NODE_OPTIONS"
|
"NODE_OPTIONS"
|
||||||
};
|
};
|
||||||
@@ -393,7 +353,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||||
|
|
||||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||||
{
|
{
|
||||||
var allowUnsecureCommands = false;
|
var allowUnsecureCommands = false;
|
||||||
bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands);
|
bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands);
|
||||||
|
|
||||||
@@ -582,11 +542,11 @@ namespace GitHub.Runner.Worker
|
|||||||
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
|
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
|
||||||
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);
|
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);
|
||||||
|
|
||||||
if (!ActionCommandManager.EnhancedAnnotationsEnabled(context))
|
if (!ActionCommandManager.EnhancedAnnotationsEnabled(context))
|
||||||
{
|
{
|
||||||
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Issue issue = new Issue()
|
Issue issue = new Issue()
|
||||||
{
|
{
|
||||||
Category = "General",
|
Category = "General",
|
||||||
@@ -638,7 +598,7 @@ namespace GitHub.Runner.Worker
|
|||||||
context.AddIssue(issue);
|
context.AddIssue(issue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)
|
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)
|
||||||
{
|
{
|
||||||
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
|
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
|
||||||
command.Properties.TryGetValue(IssueCommandProperties.EndLine, out string endLine);
|
command.Properties.TryGetValue(IssueCommandProperties.EndLine, out string endLine);
|
||||||
@@ -667,28 +627,28 @@ namespace GitHub.Runner.Worker
|
|||||||
column = endColumn;
|
column = endColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasStartLine && hasColumn)
|
if (!hasStartLine && hasColumn)
|
||||||
{
|
{
|
||||||
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' can only be set if '{IssueCommandProperties.Line}' value is provided.");
|
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' can only be set if '{IssueCommandProperties.Line}' value is provided.");
|
||||||
command.Properties.Remove(IssueCommandProperties.Column);
|
command.Properties.Remove(IssueCommandProperties.Column);
|
||||||
command.Properties.Remove(IssueCommandProperties.EndColumn);
|
command.Properties.Remove(IssueCommandProperties.EndColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasEndLine && line != endLine && hasColumn)
|
if (hasEndLine && line != endLine && hasColumn)
|
||||||
{
|
{
|
||||||
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' cannot be set if '{IssueCommandProperties.Line}' and '{IssueCommandProperties.EndLine}' are different values.");
|
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' cannot be set if '{IssueCommandProperties.Line}' and '{IssueCommandProperties.EndLine}' are different values.");
|
||||||
command.Properties.Remove(IssueCommandProperties.Column);
|
command.Properties.Remove(IssueCommandProperties.Column);
|
||||||
command.Properties.Remove(IssueCommandProperties.EndColumn);
|
command.Properties.Remove(IssueCommandProperties.EndColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasStartLine && hasEndLine && endLineNumber < lineNumber)
|
if (hasStartLine && hasEndLine && endLineNumber < lineNumber)
|
||||||
{
|
{
|
||||||
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndLine}' cannot be less than '{IssueCommandProperties.Line}'.");
|
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndLine}' cannot be less than '{IssueCommandProperties.Line}'.");
|
||||||
command.Properties.Remove(IssueCommandProperties.Line);
|
command.Properties.Remove(IssueCommandProperties.Line);
|
||||||
command.Properties.Remove(IssueCommandProperties.EndLine);
|
command.Properties.Remove(IssueCommandProperties.EndLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasStartColumn && hasEndColumn && endColumnNumber < columnNumber)
|
if (hasStartColumn && hasEndColumn && endColumnNumber < columnNumber)
|
||||||
{
|
{
|
||||||
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndColumn}' cannot be less than '{IssueCommandProperties.Column}'.");
|
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndColumn}' cannot be less than '{IssueCommandProperties.Column}'.");
|
||||||
command.Properties.Remove(IssueCommandProperties.Column);
|
command.Properties.Remove(IssueCommandProperties.Column);
|
||||||
|
|||||||
@@ -267,19 +267,6 @@ namespace GitHub.Runner.Worker
|
|||||||
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
|
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (depth > 0)
|
|
||||||
{
|
|
||||||
// if we're in a composite action and haven't loaded the local action yet
|
|
||||||
// we assume it has a post step
|
|
||||||
if (!_cachedEmbeddedPostSteps.ContainsKey(parentStepId))
|
|
||||||
{
|
|
||||||
// If we haven't done so already, add the parent to the post steps
|
|
||||||
_cachedEmbeddedPostSteps[parentStepId] = new Stack<Pipelines.ActionStep>();
|
|
||||||
}
|
|
||||||
// Clone action so we can modify the condition without affecting the original
|
|
||||||
var clonedAction = action.Clone() as Pipelines.ActionStep;
|
|
||||||
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,12 +633,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is canceled.
|
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is canceled.
|
||||||
{
|
{
|
||||||
// UnresolvableActionDownloadInfoException is a 422 client error, don't retry
|
if (attempt < 3)
|
||||||
// Some possible cases are:
|
|
||||||
// * Repo is rate limited
|
|
||||||
// * Repo or tag doesn't exist, or isn't public
|
|
||||||
// * Policy validation failed
|
|
||||||
if (attempt < 3 && !(ex is WebApi.UnresolvableActionDownloadInfoException))
|
|
||||||
{
|
{
|
||||||
executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}");
|
executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}");
|
||||||
executionContext.Debug(ex.ToString());
|
executionContext.Debug(ex.ToString());
|
||||||
@@ -667,7 +649,6 @@ namespace GitHub.Runner.Worker
|
|||||||
// Some possible cases are:
|
// Some possible cases are:
|
||||||
// * Repo is rate limited
|
// * Repo is rate limited
|
||||||
// * Repo or tag doesn't exist, or isn't public
|
// * Repo or tag doesn't exist, or isn't public
|
||||||
// * Policy validation failed
|
|
||||||
if (ex is WebApi.UnresolvableActionDownloadInfoException)
|
if (ex is WebApi.UnresolvableActionDownloadInfoException)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
@@ -1047,6 +1028,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove once we remove the DistributedTask.EnableCompositeActions FF
|
||||||
foreach (var step in compositeAction.Steps)
|
foreach (var step in compositeAction.Steps)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && step.Reference.Type != Pipelines.ActionSourceType.Script)
|
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && step.Reference.Type != Pipelines.ActionSourceType.Script)
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace GitHub.Runner.Worker
|
|||||||
string ScopeName { get; }
|
string ScopeName { get; }
|
||||||
string SiblingScopeName { get; }
|
string SiblingScopeName { get; }
|
||||||
string ContextName { get; }
|
string ContextName { get; }
|
||||||
ActionRunStage Stage { get; }
|
|
||||||
Task ForceCompleted { get; }
|
Task ForceCompleted { get; }
|
||||||
TaskResult? Result { get; set; }
|
TaskResult? Result { get; set; }
|
||||||
TaskResult? Outcome { get; set; }
|
TaskResult? Outcome { get; set; }
|
||||||
@@ -53,7 +52,6 @@ namespace GitHub.Runner.Worker
|
|||||||
Dictionary<string, VariableValue> JobOutputs { get; }
|
Dictionary<string, VariableValue> JobOutputs { get; }
|
||||||
ActionsEnvironmentReference ActionsEnvironment { get; }
|
ActionsEnvironmentReference ActionsEnvironment { get; }
|
||||||
List<ActionsStepTelemetry> ActionsStepsTelemetry { get; }
|
List<ActionsStepTelemetry> ActionsStepsTelemetry { get; }
|
||||||
List<JobTelemetry> JobTelemetry { get; }
|
|
||||||
DictionaryContextData ExpressionValues { get; }
|
DictionaryContextData ExpressionValues { get; }
|
||||||
IList<IFunctionInfo> ExpressionFunctions { get; }
|
IList<IFunctionInfo> ExpressionFunctions { get; }
|
||||||
JobContext JobContext { get; }
|
JobContext JobContext { get; }
|
||||||
@@ -63,7 +61,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
// Only job level ExecutionContext has PostJobSteps
|
// Only job level ExecutionContext has PostJobSteps
|
||||||
Stack<IStep> PostJobSteps { get; }
|
Stack<IStep> PostJobSteps { get; }
|
||||||
Dictionary<Guid, string> EmbeddedStepsWithPostRegistered { get; }
|
HashSet<Guid> EmbeddedStepsWithPostRegistered{ get; }
|
||||||
|
|
||||||
// Keep track of embedded steps states
|
// Keep track of embedded steps states
|
||||||
Dictionary<Guid, Dictionary<string, string>> EmbeddedIntraActionState { get; }
|
Dictionary<Guid, Dictionary<string, string>> EmbeddedIntraActionState { get; }
|
||||||
@@ -77,8 +75,8 @@ namespace GitHub.Runner.Worker
|
|||||||
// Initialize
|
// Initialize
|
||||||
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
||||||
void CancelToken();
|
void CancelToken();
|
||||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null);
|
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null);
|
||||||
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);
|
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
long Write(string tag, string message);
|
long Write(string tag, string message);
|
||||||
@@ -145,7 +143,6 @@ namespace GitHub.Runner.Worker
|
|||||||
public string ScopeName { get; private set; }
|
public string ScopeName { get; private set; }
|
||||||
public string SiblingScopeName { get; private set; }
|
public string SiblingScopeName { get; private set; }
|
||||||
public string ContextName { get; private set; }
|
public string ContextName { get; private set; }
|
||||||
public ActionRunStage Stage { get; private set; }
|
|
||||||
public Task ForceCompleted => _forceCompleted.Task;
|
public Task ForceCompleted => _forceCompleted.Task;
|
||||||
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
public Dictionary<string, string> IntraActionState { get; private set; }
|
public Dictionary<string, string> IntraActionState { get; private set; }
|
||||||
@@ -153,7 +150,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
||||||
public List<ActionsStepTelemetry> ActionsStepsTelemetry { get; private set; }
|
public List<ActionsStepTelemetry> ActionsStepsTelemetry { get; private set; }
|
||||||
public List<JobTelemetry> JobTelemetry { get; private set; }
|
|
||||||
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>();
|
||||||
|
|
||||||
@@ -170,7 +166,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public HashSet<Guid> StepsWithPostRegistered { get; private set; }
|
public HashSet<Guid> StepsWithPostRegistered { get; private set; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has EmbeddedStepsWithPostRegistered
|
// Only job level ExecutionContext has EmbeddedStepsWithPostRegistered
|
||||||
public Dictionary<Guid, string> EmbeddedStepsWithPostRegistered { get; private set; }
|
public HashSet<Guid> EmbeddedStepsWithPostRegistered { get; private set; }
|
||||||
|
|
||||||
public Dictionary<Guid, Dictionary<string, string>> EmbeddedIntraActionState { get; private set; }
|
public Dictionary<Guid, Dictionary<string, string>> EmbeddedIntraActionState { get; private set; }
|
||||||
|
|
||||||
@@ -267,19 +263,12 @@ namespace GitHub.Runner.Worker
|
|||||||
string siblingScopeName = null;
|
string siblingScopeName = null;
|
||||||
if (this.IsEmbedded)
|
if (this.IsEmbedded)
|
||||||
{
|
{
|
||||||
if (step is IActionRunner actionRunner)
|
if (step is IActionRunner actionRunner && !Root.EmbeddedStepsWithPostRegistered.Add(actionRunner.Action.Id))
|
||||||
{
|
{
|
||||||
if (Root.EmbeddedStepsWithPostRegistered.ContainsKey(actionRunner.Action.Id))
|
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to child post step stack.");
|
||||||
{
|
|
||||||
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to child post step stack.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Root.EmbeddedStepsWithPostRegistered[actionRunner.Action.Id] = actionRunner.Condition;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
else if (step is IActionRunner actionRunner && !Root.StepsWithPostRegistered.Add(actionRunner.Action.Id))
|
else if (step is IActionRunner actionRunner && !Root.StepsWithPostRegistered.Add(actionRunner.Action.Id))
|
||||||
{
|
{
|
||||||
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to post step stack.");
|
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to post step stack.");
|
||||||
@@ -294,7 +283,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Root.PostJobSteps.Push(step);
|
Root.PostJobSteps.Push(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null)
|
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -303,10 +292,8 @@ namespace GitHub.Runner.Worker
|
|||||||
child.Global = Global;
|
child.Global = Global;
|
||||||
child.ScopeName = scopeName;
|
child.ScopeName = scopeName;
|
||||||
child.ContextName = contextName;
|
child.ContextName = contextName;
|
||||||
child.Stage = stage;
|
|
||||||
child.EmbeddedId = embeddedId;
|
child.EmbeddedId = embeddedId;
|
||||||
child.SiblingScopeName = siblingScopeName;
|
child.SiblingScopeName = siblingScopeName;
|
||||||
child.JobTelemetry = JobTelemetry;
|
|
||||||
if (intraActionState == null)
|
if (intraActionState == null)
|
||||||
{
|
{
|
||||||
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -354,9 +341,9 @@ namespace GitHub.Runner.Worker
|
|||||||
/// An embedded execution context shares the same record ID, record name, logger,
|
/// An embedded execution context shares the same record ID, record name, logger,
|
||||||
/// and a linked cancellation token.
|
/// and a linked cancellation token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null)
|
public IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, Dictionary<string, string> intraActionState = null, string siblingScopeName = null)
|
||||||
{
|
{
|
||||||
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token), intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName);
|
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token), intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(string currentOperation = null)
|
public void Start(string currentOperation = null)
|
||||||
@@ -553,8 +540,8 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
_record.WarningCount++;
|
_record.WarningCount++;
|
||||||
}
|
}
|
||||||
else if (issue.Type == IssueType.Notice)
|
else if (issue.Type == IssueType.Notice)
|
||||||
{
|
{
|
||||||
|
|
||||||
// tracking line number for each issue in log file
|
// tracking line number for each issue in log file
|
||||||
@@ -663,8 +650,6 @@ namespace GitHub.Runner.Worker
|
|||||||
// ActionsStepTelemetry
|
// ActionsStepTelemetry
|
||||||
ActionsStepsTelemetry = new List<ActionsStepTelemetry>();
|
ActionsStepsTelemetry = new List<ActionsStepTelemetry>();
|
||||||
|
|
||||||
JobTelemetry = new List<JobTelemetry>();
|
|
||||||
|
|
||||||
// Service container info
|
// Service container info
|
||||||
Global.ServiceContainers = new List<ContainerInfo>();
|
Global.ServiceContainers = new List<ContainerInfo>();
|
||||||
|
|
||||||
@@ -726,10 +711,10 @@ namespace GitHub.Runner.Worker
|
|||||||
StepsWithPostRegistered = new HashSet<Guid>();
|
StepsWithPostRegistered = new HashSet<Guid>();
|
||||||
|
|
||||||
// EmbeddedStepsWithPostRegistered for job ExecutionContext
|
// EmbeddedStepsWithPostRegistered for job ExecutionContext
|
||||||
EmbeddedStepsWithPostRegistered = new Dictionary<Guid, string>();
|
EmbeddedStepsWithPostRegistered = new HashSet<Guid>();
|
||||||
|
|
||||||
// EmbeddedIntraActionState for job ExecutionContext
|
// EmbeddedIntraActionState for job ExecutionContext
|
||||||
EmbeddedIntraActionState = new Dictionary<Guid, Dictionary<string, string>>();
|
EmbeddedIntraActionState = new Dictionary<Guid, Dictionary<string,string>>();
|
||||||
|
|
||||||
// Job timeline record.
|
// Job timeline record.
|
||||||
InitializeTimelineRecord(
|
InitializeTimelineRecord(
|
||||||
@@ -947,7 +932,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
var newGuid = Guid.NewGuid();
|
var newGuid = Guid.NewGuid();
|
||||||
return CreateChild(newGuid, displayName, newGuid.ToString("N"), null, null, ActionRunStage.Post, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count, siblingScopeName: siblingScopeName);
|
return CreateChild(newGuid, displayName, newGuid.ToString("N"), null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count, siblingScopeName: siblingScopeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,7 +965,7 @@ 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 InfrastructureError(this IExecutionContext context, string message)
|
public static void InfrastructureError(this IExecutionContext context, string message)
|
||||||
{
|
{
|
||||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
|
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
|
|||||||
@@ -24,19 +24,8 @@ namespace GitHub.Runner.Worker.Expressions
|
|||||||
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
||||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
||||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||||
|
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||||
// Decide based on 'action_status' for composite MAIN steps and 'job.status' for pre, post and job-level steps
|
return jobStatus == ActionResult.Failure;
|
||||||
var isCompositeMainStep = executionContext.IsEmbedded && executionContext.Stage == ActionRunStage.Main;
|
|
||||||
if (isCompositeMainStep)
|
|
||||||
{
|
|
||||||
ActionResult actionStatus = EnumUtil.TryParse<ActionResult>(executionContext.GetGitHubContext("action_status")) ?? ActionResult.Success;
|
|
||||||
return actionStatus == ActionResult.Failure;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
|
||||||
return jobStatus == ActionResult.Failure;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,19 +24,8 @@ namespace GitHub.Runner.Worker.Expressions
|
|||||||
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
||||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
||||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||||
|
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||||
// Decide based on 'action_status' for composite MAIN steps and 'job.status' for pre, post and job-level steps
|
return jobStatus == ActionResult.Success;
|
||||||
var isCompositeMainStep = executionContext.IsEmbedded && executionContext.Stage == ActionRunStage.Main;
|
|
||||||
if (isCompositeMainStep)
|
|
||||||
{
|
|
||||||
ActionResult actionStatus = EnumUtil.TryParse<ActionResult>(executionContext.GetGitHubContext("action_status")) ?? ActionResult.Success;
|
|
||||||
return actionStatus == ActionResult.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
|
||||||
return jobStatus == ActionResult.Success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,6 @@ namespace GitHub.Runner.Worker
|
|||||||
"job",
|
"job",
|
||||||
"path",
|
"path",
|
||||||
"ref",
|
"ref",
|
||||||
"ref_name",
|
|
||||||
"ref_protected",
|
|
||||||
"ref_type",
|
|
||||||
"repository",
|
"repository",
|
||||||
"repository_owner",
|
"repository_owner",
|
||||||
"retention_days",
|
"retention_days",
|
||||||
@@ -42,16 +39,9 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var data in this)
|
foreach (var data in this)
|
||||||
{
|
{
|
||||||
if (_contextEnvAllowlist.Contains(data.Key))
|
if (_contextEnvAllowlist.Contains(data.Key) && data.Value is StringContextData value)
|
||||||
{
|
{
|
||||||
if (data.Value is StringContextData value)
|
yield return new KeyValuePair<string, string>($"GITHUB_{data.Key.ToUpperInvariant()}", value);
|
||||||
{
|
|
||||||
yield return new KeyValuePair<string, string>($"GITHUB_{data.Key.ToUpperInvariant()}", value);
|
|
||||||
}
|
|
||||||
else if (data.Value is BooleanContextData booleanValue)
|
|
||||||
{
|
|
||||||
yield return new KeyValuePair<string, string>($"GITHUB_{data.Key.ToUpperInvariant()}", booleanValue.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,8 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
// Only register post steps for steps that actually ran
|
// Only register post steps for steps that actually ran
|
||||||
foreach (var step in Data.PostSteps.ToList())
|
foreach (var step in Data.PostSteps.ToList())
|
||||||
{
|
{
|
||||||
if (ExecutionContext.Root.EmbeddedStepsWithPostRegistered.ContainsKey(step.Id))
|
if (ExecutionContext.Root.EmbeddedStepsWithPostRegistered.Contains(step.Id))
|
||||||
{
|
{
|
||||||
step.Condition = ExecutionContext.Root.EmbeddedStepsWithPostRegistered[step.Id];
|
|
||||||
steps.Add(step);
|
steps.Add(step);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -125,7 +124,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
ArgUtil.NotNull(step, step.DisplayName);
|
ArgUtil.NotNull(step, step.DisplayName);
|
||||||
var stepId = $"__{Guid.NewGuid()}";
|
var stepId = $"__{Guid.NewGuid()}";
|
||||||
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepId, Guid.NewGuid(), stage);
|
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepId, Guid.NewGuid());
|
||||||
embeddedSteps.Add(step);
|
embeddedSteps.Add(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +143,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
step.Stage = stage;
|
step.Stage = stage;
|
||||||
step.Condition = stepData.Condition;
|
step.Condition = stepData.Condition;
|
||||||
ExecutionContext.Root.EmbeddedIntraActionState.TryGetValue(step.Action.Id, out var intraActionState);
|
ExecutionContext.Root.EmbeddedIntraActionState.TryGetValue(step.Action.Id, out var intraActionState);
|
||||||
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepData.ContextName, step.Action.Id, stage, intraActionState: intraActionState, siblingScopeName: siblingScopeName);
|
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepData.ContextName, step.Action.Id, intraActionState: intraActionState, siblingScopeName: siblingScopeName);
|
||||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||||
if (!String.IsNullOrEmpty(ExecutionContext.SiblingScopeName))
|
if (!String.IsNullOrEmpty(ExecutionContext.SiblingScopeName))
|
||||||
{
|
{
|
||||||
@@ -242,10 +241,6 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<FailureFunction>(PipelineTemplateConstants.Failure, 0, 0));
|
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<FailureFunction>(PipelineTemplateConstants.Failure, 0, 0));
|
||||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
|
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
|
||||||
|
|
||||||
// Set action_status to the success of the current composite action
|
|
||||||
var actionResult = ExecutionContext.Result?.ToActionResult() ?? ActionResult.Success;
|
|
||||||
step.ExecutionContext.SetGitHubContext("action_status", actionResult.ToString());
|
|
||||||
|
|
||||||
// Initialize env context
|
// Initialize env context
|
||||||
Trace.Info("Initialize Env context for embedded step");
|
Trace.Info("Initialize Env context for embedded step");
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
@@ -300,100 +295,108 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
CancellationTokenRegistration? jobCancelRegister = null;
|
CancellationTokenRegistration? jobCancelRegister = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Register job cancellation call back only if job cancellation token not been fire before each step run
|
// For main steps just run the action
|
||||||
if (!ExecutionContext.Root.CancellationToken.IsCancellationRequested)
|
if (stage == ActionRunStage.Main)
|
||||||
{
|
|
||||||
// Test the condition again. The job was canceled after the condition was originally evaluated.
|
|
||||||
jobCancelRegister = ExecutionContext.Root.CancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
// Mark job as cancelled
|
|
||||||
ExecutionContext.Root.Result = TaskResult.Canceled;
|
|
||||||
ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult();
|
|
||||||
|
|
||||||
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
|
|
||||||
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
|
|
||||||
var conditionReTestResult = false;
|
|
||||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionReTestTraceWriter);
|
|
||||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
|
||||||
conditionReTestResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Cancel the step since we get exception while re-evaluate step condition
|
|
||||||
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
|
||||||
step.ExecutionContext.Error(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conditionReTestResult)
|
|
||||||
{
|
|
||||||
// Cancel the step
|
|
||||||
Trace.Info("Cancel current running step.");
|
|
||||||
step.ExecutionContext.CancelToken();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ExecutionContext.Root.Result != TaskResult.Canceled)
|
|
||||||
{
|
|
||||||
// Mark job as cancelled
|
|
||||||
ExecutionContext.Root.Result = TaskResult.Canceled;
|
|
||||||
ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Evaluate condition
|
|
||||||
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
|
||||||
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
|
||||||
var conditionResult = false;
|
|
||||||
var conditionEvaluateError = default(Exception);
|
|
||||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
|
||||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
|
||||||
conditionResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Info("Caught exception from expression.");
|
|
||||||
Trace.Error(ex);
|
|
||||||
conditionEvaluateError = ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!conditionResult && conditionEvaluateError == null)
|
|
||||||
{
|
|
||||||
// Condition is false
|
|
||||||
Trace.Info("Skipping step due to condition evaluation.");
|
|
||||||
step.ExecutionContext.Result = TaskResult.Skipped;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (conditionEvaluateError != null)
|
|
||||||
{
|
|
||||||
// Condition error
|
|
||||||
step.ExecutionContext.Error(conditionEvaluateError);
|
|
||||||
step.ExecutionContext.Result = TaskResult.Failed;
|
|
||||||
ExecutionContext.Result = TaskResult.Failed;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await RunStepAsync(step);
|
await RunStepAsync(step);
|
||||||
}
|
}
|
||||||
|
// We need to evaluate conditions for pre/post steps
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Register job cancellation call back only if job cancellation token not been fire before each step run
|
||||||
|
if (!ExecutionContext.Root.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Test the condition again. The job was canceled after the condition was originally evaluated.
|
||||||
|
jobCancelRegister = ExecutionContext.Root.CancellationToken.Register(() =>
|
||||||
|
{
|
||||||
|
// Mark job as cancelled
|
||||||
|
ExecutionContext.Root.Result = TaskResult.Canceled;
|
||||||
|
ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult();
|
||||||
|
|
||||||
|
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
|
||||||
|
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
|
||||||
|
var conditionReTestResult = false;
|
||||||
|
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionReTestTraceWriter);
|
||||||
|
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||||
|
conditionReTestResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Cancel the step since we get exception while re-evaluate step condition
|
||||||
|
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conditionReTestResult)
|
||||||
|
{
|
||||||
|
// Cancel the step
|
||||||
|
Trace.Info("Cancel current running step.");
|
||||||
|
step.ExecutionContext.CancelToken();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ExecutionContext.Root.Result != TaskResult.Canceled)
|
||||||
|
{
|
||||||
|
// Mark job as cancelled
|
||||||
|
ExecutionContext.Root.Result = TaskResult.Canceled;
|
||||||
|
ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Evaluate condition
|
||||||
|
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
||||||
|
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
||||||
|
var conditionResult = false;
|
||||||
|
var conditionEvaluateError = default(Exception);
|
||||||
|
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
||||||
|
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||||
|
conditionResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Info("Caught exception from expression.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
conditionEvaluateError = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conditionResult && conditionEvaluateError == null)
|
||||||
|
{
|
||||||
|
// Condition is false
|
||||||
|
Trace.Info("Skipping step due to condition evaluation.");
|
||||||
|
step.ExecutionContext.Result = TaskResult.Skipped;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (conditionEvaluateError != null)
|
||||||
|
{
|
||||||
|
// Condition error
|
||||||
|
step.ExecutionContext.Error(conditionEvaluateError);
|
||||||
|
step.ExecutionContext.Result = TaskResult.Failed;
|
||||||
|
ExecutionContext.Result = TaskResult.Failed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await RunStepAsync(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -409,6 +412,12 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
Trace.Info($"Update job result with current composite step result '{step.ExecutionContext.Result}'.");
|
Trace.Info($"Update job result with current composite step result '{step.ExecutionContext.Result}'.");
|
||||||
ExecutionContext.Result = TaskResultUtil.MergeTaskResults(ExecutionContext.Result, step.ExecutionContext.Result.Value);
|
ExecutionContext.Result = TaskResultUtil.MergeTaskResults(ExecutionContext.Result, step.ExecutionContext.Result.Value);
|
||||||
|
|
||||||
|
// We should run cleanup even if one of the cleanup step fails
|
||||||
|
if (stage != ActionRunStage.Post)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNull(message, nameof(message));
|
ArgUtil.NotNull(message, nameof(message));
|
||||||
|
|
||||||
// Create a new timeline record for 'Set up job'
|
// Create a new timeline record for 'Set up job'
|
||||||
IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Set up job", $"{nameof(JobExtension)}_Init", null, null, ActionRunStage.Pre);
|
IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Set up job", $"{nameof(JobExtension)}_Init", null, null);
|
||||||
|
|
||||||
List<IStep> preJobSteps = new List<IStep>();
|
List<IStep> preJobSteps = new List<IStep>();
|
||||||
List<IStep> jobSteps = new List<IStep>();
|
List<IStep> jobSteps = new List<IStep>();
|
||||||
@@ -306,13 +306,13 @@ namespace GitHub.Runner.Worker
|
|||||||
JobExtensionRunner extensionStep = step as JobExtensionRunner;
|
JobExtensionRunner extensionStep = step as JobExtensionRunner;
|
||||||
ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
|
ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
|
||||||
Guid stepId = Guid.NewGuid();
|
Guid stepId = Guid.NewGuid();
|
||||||
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N"), ActionRunStage.Pre);
|
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N"));
|
||||||
}
|
}
|
||||||
else if (step is IActionRunner actionStep)
|
else if (step is IActionRunner actionStep)
|
||||||
{
|
{
|
||||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||||
Guid stepId = Guid.NewGuid();
|
Guid stepId = Guid.NewGuid();
|
||||||
actionStep.ExecutionContext = jobContext.CreateChild(stepId, actionStep.DisplayName, stepId.ToString("N"), null, null, ActionRunStage.Pre, intraActionStates[actionStep.Action.Id]);
|
actionStep.ExecutionContext = jobContext.CreateChild(stepId, actionStep.DisplayName, stepId.ToString("N"), null, null, intraActionStates[actionStep.Action.Id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||||
intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState);
|
intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState);
|
||||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, null, actionStep.Action.ContextName, ActionRunStage.Main, intraActionState);
|
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, null, actionStep.Action.ContextName, intraActionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +394,7 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNull(jobContext, nameof(jobContext));
|
ArgUtil.NotNull(jobContext, nameof(jobContext));
|
||||||
|
|
||||||
// create a new timeline record node for 'Finalize job'
|
// create a new timeline record node for 'Finalize job'
|
||||||
IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Complete job", $"{nameof(JobExtension)}_Final", null, null, ActionRunStage.Post);
|
IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Complete job", $"{nameof(JobExtension)}_Final", null, null);
|
||||||
using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); }))
|
using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); }))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -106,10 +105,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
jobContext.SetRunnerContext("os", VarUtil.OS);
|
jobContext.SetRunnerContext("os", VarUtil.OS);
|
||||||
jobContext.SetRunnerContext("arch", VarUtil.OSArchitecture);
|
|
||||||
|
|
||||||
var runnerSettings = HostContext.GetService<IConfigurationStore>().GetSettings();
|
|
||||||
jobContext.SetRunnerContext("name", runnerSettings.AgentName);
|
|
||||||
|
|
||||||
string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools);
|
string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools);
|
||||||
Directory.CreateDirectory(toolsDirectory);
|
Directory.CreateDirectory(toolsDirectory);
|
||||||
@@ -230,15 +225,8 @@ namespace GitHub.Runner.Worker
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load any upgrade telemetry
|
|
||||||
LoadFromTelemetryFile(jobContext.JobTelemetry);
|
|
||||||
|
|
||||||
// Make sure we don't submit secrets as telemetry
|
|
||||||
MaskTelemetrySecrets(jobContext.JobTelemetry);
|
|
||||||
|
|
||||||
Trace.Info("Raising job completed event.");
|
Trace.Info("Raising job completed event.");
|
||||||
var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, result, jobContext.JobOutputs, jobContext.ActionsEnvironment, jobContext.ActionsStepsTelemetry, jobContext.JobTelemetry);
|
var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, result, jobContext.JobOutputs, jobContext.ActionsEnvironment, jobContext.ActionsStepsTelemetry);
|
||||||
|
|
||||||
|
|
||||||
var completeJobRetryLimit = 5;
|
var completeJobRetryLimit = 5;
|
||||||
var exceptions = new List<Exception>();
|
var exceptions = new List<Exception>();
|
||||||
@@ -282,38 +270,6 @@ namespace GitHub.Runner.Worker
|
|||||||
throw new AggregateException(exceptions);
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MaskTelemetrySecrets(List<JobTelemetry> jobTelemetry)
|
|
||||||
{
|
|
||||||
foreach (var telemetryItem in jobTelemetry)
|
|
||||||
{
|
|
||||||
telemetryItem.Message = HostContext.SecretMasker.MaskSecrets(telemetryItem.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadFromTelemetryFile(List<JobTelemetry> jobTelemetry)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var telemetryFilePath = HostContext.GetConfigFile(WellKnownConfigFile.Telemetry);
|
|
||||||
if (File.Exists(telemetryFilePath))
|
|
||||||
{
|
|
||||||
var telemetryData = File.ReadAllText(telemetryFilePath, Encoding.UTF8);
|
|
||||||
var telemetry = new JobTelemetry
|
|
||||||
{
|
|
||||||
Message = $"Runner File Telemetry:\n{telemetryData}",
|
|
||||||
Type = JobTelemetryType.General
|
|
||||||
};
|
|
||||||
jobTelemetry.Add(telemetry);
|
|
||||||
IOUtil.DeleteFile(telemetryFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Trace.Error("Error when trying to load telemetry from telemetry file");
|
|
||||||
Trace.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ShutdownQueue(bool throwOnFailure)
|
private async Task ShutdownQueue(bool throwOnFailure)
|
||||||
{
|
{
|
||||||
if (_jobServerQueue != null)
|
if (_jobServerQueue != null)
|
||||||
|
|||||||
@@ -123,7 +123,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": "string-steps-context",
|
"name": "string-steps-context",
|
||||||
"id": "non-empty-string",
|
"id": "non-empty-string",
|
||||||
"if": "step-if",
|
|
||||||
"run": {
|
"run": {
|
||||||
"type": "string-steps-context",
|
"type": "string-steps-context",
|
||||||
"required": true
|
"required": true
|
||||||
@@ -142,7 +141,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": "string-steps-context",
|
"name": "string-steps-context",
|
||||||
"id": "non-empty-string",
|
"id": "non-empty-string",
|
||||||
"if": "step-if",
|
|
||||||
"uses": {
|
"uses": {
|
||||||
"type": "non-empty-string",
|
"type": "non-empty-string",
|
||||||
"required": true
|
"required": true
|
||||||
@@ -218,24 +216,6 @@
|
|||||||
"loose-value-type": "string"
|
"loose-value-type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"step-if": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"inputs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"steps",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"always(0,0)",
|
|
||||||
"failure(0,0)",
|
|
||||||
"cancelled(0,0)",
|
|
||||||
"success(0,0)",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"string": {}
|
|
||||||
},
|
|
||||||
"step-with": {
|
"step-with": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
|
|||||||
@@ -638,7 +638,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Matrix),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Matrix),
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Steps),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Steps),
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Inputs),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Job),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Job),
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
|
||||||
|
|||||||
@@ -153,19 +153,6 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
{
|
{
|
||||||
this.ActionsEnvironment = actionsEnvironment;
|
this.ActionsEnvironment = actionsEnvironment;
|
||||||
this.ActionsStepsTelemetry = actionsStepsTelemetry;
|
this.ActionsStepsTelemetry = actionsStepsTelemetry;
|
||||||
}
|
|
||||||
|
|
||||||
public JobCompletedEvent(
|
|
||||||
Int64 requestId,
|
|
||||||
Guid jobId,
|
|
||||||
TaskResult result,
|
|
||||||
Dictionary<String, VariableValue> outputs,
|
|
||||||
ActionsEnvironmentReference actionsEnvironment,
|
|
||||||
List<ActionsStepTelemetry> actionsStepsTelemetry,
|
|
||||||
List<JobTelemetry> jobTelemetry)
|
|
||||||
: this(requestId, jobId, result, outputs, actionsEnvironment, actionsStepsTelemetry)
|
|
||||||
{
|
|
||||||
this.JobTelemetry = jobTelemetry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
@@ -202,13 +189,6 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public List<JobTelemetry> JobTelemetry
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace GitHub.DistributedTask.WebApi
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Information about a job run on the runner
|
|
||||||
/// </summary>
|
|
||||||
[DataContract]
|
|
||||||
public class JobTelemetry
|
|
||||||
{
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public string Message { get; set; }
|
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public JobTelemetryType Type { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace GitHub.DistributedTask.WebApi
|
|
||||||
{
|
|
||||||
public enum JobTelemetryType
|
|
||||||
{
|
|
||||||
[EnumMember]
|
|
||||||
General = 0,
|
|
||||||
|
|
||||||
[EnumMember]
|
|
||||||
ActionCommand = 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -161,8 +161,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
"--work", _expectedWorkFolder,
|
"--work", _expectedWorkFolder,
|
||||||
"--auth", _expectedAuthType,
|
"--auth", _expectedAuthType,
|
||||||
"--token", _expectedToken,
|
"--token", _expectedToken,
|
||||||
"--labels", userLabels,
|
"--labels", userLabels
|
||||||
"--ephemeral",
|
|
||||||
});
|
});
|
||||||
trace.Info("Constructed.");
|
trace.Info("Constructed.");
|
||||||
_store.Setup(x => x.IsConfigured()).Returns(false);
|
_store.Setup(x => x.IsConfigured()).Returns(false);
|
||||||
@@ -180,7 +179,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
||||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
||||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
||||||
Assert.True(s.Ephemeral.Equals(true));
|
|
||||||
|
|
||||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
||||||
|
|||||||
@@ -264,170 +264,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Runner")]
|
|
||||||
public async void RenewJobRequestNewAgentNameUpdatesSettings()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
using (var hc = new TestHostContext(this))
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
var oldName = "OldName";
|
|
||||||
var newName = "NewName";
|
|
||||||
var oldSettings = new RunnerSettings { AgentName = oldName };
|
|
||||||
var reservedAgent = new TaskAgentReference { Name = newName };
|
|
||||||
|
|
||||||
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
|
||||||
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
|
||||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var request = new Mock<TaskAgentJobRequest>();
|
|
||||||
request.Object.ReservedAgent = reservedAgent;
|
|
||||||
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
Assert.NotNull(lockUntilProperty);
|
|
||||||
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
|
||||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
|
||||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
|
||||||
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
|
||||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
|
||||||
.Returns(() =>
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (count < 5)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
else if (count == 5 || count == 6 || count == 7)
|
|
||||||
{
|
|
||||||
throw new TimeoutException("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cancellationTokenSource.Cancel();
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var jobDispatcher = new JobDispatcher();
|
|
||||||
jobDispatcher.Initialize(hc);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.Is<RunnerSettings>(settings => settings.AgentName == newName)), Times.Once);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Runner")]
|
|
||||||
public async void RenewJobRequestSameAgentNameIgnored()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
using (var hc = new TestHostContext(this))
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
var oldName = "OldName";
|
|
||||||
var newName = "OldName";
|
|
||||||
var oldSettings = new RunnerSettings { AgentName = oldName };
|
|
||||||
var reservedAgent = new TaskAgentReference { Name = newName };
|
|
||||||
|
|
||||||
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
|
||||||
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
|
||||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var request = new Mock<TaskAgentJobRequest>();
|
|
||||||
request.Object.ReservedAgent = reservedAgent;
|
|
||||||
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
Assert.NotNull(lockUntilProperty);
|
|
||||||
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
|
||||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
|
||||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
|
||||||
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
|
||||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
|
||||||
.Returns(() =>
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (count < 5)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
else if (count == 5 || count == 6 || count == 7)
|
|
||||||
{
|
|
||||||
throw new TimeoutException("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cancellationTokenSource.Cancel();
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var jobDispatcher = new JobDispatcher();
|
|
||||||
jobDispatcher.Initialize(hc);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Runner")]
|
|
||||||
public async void RenewJobRequestNullAgentNameIgnored()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
using (var hc = new TestHostContext(this))
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
var oldName = "OldName";
|
|
||||||
var oldSettings = new RunnerSettings { AgentName = oldName };
|
|
||||||
|
|
||||||
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
|
||||||
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
|
||||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var request = new Mock<TaskAgentJobRequest>();
|
|
||||||
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
Assert.NotNull(lockUntilProperty);
|
|
||||||
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
|
||||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
|
||||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
|
||||||
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
|
||||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
|
||||||
.Returns(() =>
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (count < 5)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
else if (count == 5 || count == 6 || count == 7)
|
|
||||||
{
|
|
||||||
throw new TimeoutException("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cancellationTokenSource.Cancel();
|
|
||||||
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var jobDispatcher = new JobDispatcher();
|
|
||||||
jobDispatcher.Initialize(hc);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
[Trait("Category", "Runner")]
|
||||||
|
|||||||
@@ -149,9 +149,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
||||||
|
|
||||||
// verify that we didn't try to delete local settings file (since we're not ephemeral)
|
|
||||||
_configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Never());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,9 +312,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
||||||
|
|
||||||
// verify that we did try to delete local settings file (since we're ephemeral)
|
|
||||||
_configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Once());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
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.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
@@ -84,7 +83,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
using (TestHostContext hc = CreateTestContext())
|
using (TestHostContext hc = CreateTestContext())
|
||||||
{
|
{
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(GetExpressionValues());
|
|
||||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||||
.Returns((string tag, string line) =>
|
.Returns((string tag, string line) =>
|
||||||
{
|
{
|
||||||
@@ -107,88 +105,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("stop-commands", "1")]
|
|
||||||
[InlineData("", "1")]
|
|
||||||
[InlineData("set-env", "1")]
|
|
||||||
[InlineData("stop-commands", "true")]
|
|
||||||
[InlineData("", "true")]
|
|
||||||
[InlineData("set-env", "true")]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public void StopProcessCommand__AllowsInvalidStopTokens__IfEnvVarIsSet(string invalidToken, string allowUnsupportedStopCommandTokens)
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
|
||||||
var expressionValues = new DictionaryContextData
|
|
||||||
{
|
|
||||||
["env"] =
|
|
||||||
#if OS_WINDOWS
|
|
||||||
new DictionaryContextData{ { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) }}
|
|
||||||
#else
|
|
||||||
new CaseSensitiveDictionaryContextData{ { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) }}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(expressionValues);
|
|
||||||
_ec.Setup(x => x.JobTelemetry).Returns(new List<JobTelemetry>());
|
|
||||||
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, $"::stop-commands::{invalidToken}", null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("stop-commands")]
|
|
||||||
[InlineData("")]
|
|
||||||
[InlineData("set-env")]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public void StopProcessCommand__FailOnInvalidStopTokens(string invalidToken)
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(GetExpressionValues());
|
|
||||||
_ec.Setup(x => x.JobTelemetry).Returns(new List<JobTelemetry>());
|
|
||||||
Assert.Throws<Exception>(() => _commandManager.TryProcessCommand(_ec.Object, $"::stop-commands::{invalidToken}", null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public void StopProcessCommandAcceptsValidToken()
|
|
||||||
{
|
|
||||||
var validToken = "randomToken";
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(GetExpressionValues());
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, $"::stop-commands::{validToken}", null));
|
|
||||||
Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, $"::{validToken}::", null));
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public void StopProcessCommandMasksValidTokenForEntireRun()
|
|
||||||
{
|
|
||||||
var validToken = "randomToken";
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(GetExpressionValues());
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, $"::stop-commands::{validToken}", null));
|
|
||||||
Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
|
||||||
Assert.Equal("***", hc.SecretMasker.MaskSecrets(validToken));
|
|
||||||
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, $"::{validToken}::", null));
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
|
||||||
Assert.Equal("***", hc.SecretMasker.MaskSecrets(validToken));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Worker")]
|
[Trait("Category", "Worker")]
|
||||||
@@ -286,15 +202,15 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
var registeredCommands = new HashSet<string>(new string[1] { "warning" });
|
var registeredCommands = new HashSet<string>(new string[1]{ "warning" });
|
||||||
ActionCommand command;
|
ActionCommand command;
|
||||||
|
|
||||||
// Columns when lines are different
|
// Columns when lines are different
|
||||||
ActionCommand.TryParseV2("::warning line=1,endLine=2,col=1,endColumn=2::this is a warning", registeredCommands, out command);
|
ActionCommand.TryParseV2("::warning line=1,endLine=2,col=1,endColumn=2::this is a warning", registeredCommands, out command);
|
||||||
Assert.Equal("1", command.Properties["col"]);
|
Assert.Equal("1", command.Properties["col"]);
|
||||||
IssueCommandExtension.ValidateLinesAndColumns(command, _ec.Object);
|
IssueCommandExtension.ValidateLinesAndColumns(command, _ec.Object);
|
||||||
Assert.False(command.Properties.ContainsKey("col"));
|
Assert.False(command.Properties.ContainsKey("col"));
|
||||||
|
|
||||||
// No lines with columns
|
// No lines with columns
|
||||||
ActionCommand.TryParseV2("::warning col=1,endColumn=2::this is a warning", registeredCommands, out command);
|
ActionCommand.TryParseV2("::warning col=1,endColumn=2::this is a warning", registeredCommands, out command);
|
||||||
Assert.Equal("1", command.Properties["col"]);
|
Assert.Equal("1", command.Properties["col"]);
|
||||||
@@ -459,19 +375,5 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
return hostContext;
|
return hostContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DictionaryContextData GetExpressionValues()
|
|
||||||
{
|
|
||||||
return new DictionaryContextData
|
|
||||||
{
|
|
||||||
["env"] =
|
|
||||||
#if OS_WINDOWS
|
|
||||||
new DictionaryContextData()
|
|
||||||
#else
|
|
||||||
new CaseSensitiveDictionaryContextData()
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,32 +626,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
Teardown();
|
Teardown();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public void Load_ConditionalCompositeAction()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Setup();
|
|
||||||
|
|
||||||
var actionManifest = new ActionManifestManager();
|
|
||||||
actionManifest.Initialize(_hc);
|
|
||||||
|
|
||||||
//Act
|
|
||||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "conditional_composite_action.yml"));
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Assert.Equal("Conditional Composite", result.Name);
|
|
||||||
Assert.Equal(ActionExecutionType.Composite, result.Execution.ExecutionType);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Teardown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -193,9 +193,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Act.
|
// Act.
|
||||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||||
|
|
||||||
var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1", "action_1", null, null, 0);
|
var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1", "action_1", null, null);
|
||||||
action1.IntraActionState["state"] = "1";
|
action1.IntraActionState["state"] = "1";
|
||||||
var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_2", "action_2", null, null, 0);
|
var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_2", "action_2", null, null);
|
||||||
action2.IntraActionState["state"] = "2";
|
action2.IntraActionState["state"] = "2";
|
||||||
|
|
||||||
|
|
||||||
@@ -291,8 +291,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Act.
|
// Act.
|
||||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||||
|
|
||||||
var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, 0);
|
var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null);
|
||||||
var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_1_main", "action_1_main", null, null, 0);
|
var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_1_main", "action_1_main", null, null);
|
||||||
|
|
||||||
var actionId = Guid.NewGuid();
|
var actionId = Guid.NewGuid();
|
||||||
var postRunner1 = hc.CreateService<IActionRunner>();
|
var postRunner1 = hc.CreateService<IActionRunner>();
|
||||||
|
|||||||
@@ -105,36 +105,6 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
[InlineData(ActionResult.Failure, ActionResult.Failure, true)]
|
|
||||||
[InlineData(ActionResult.Failure, ActionResult.Success, false)]
|
|
||||||
[InlineData(ActionResult.Success, ActionResult.Failure, true)]
|
|
||||||
[InlineData(ActionResult.Success, ActionResult.Success, false)]
|
|
||||||
[InlineData(ActionResult.Success, null, false)]
|
|
||||||
public void FailureFunctionComposite(ActionResult jobStatus, ActionResult? actionStatus, bool expected)
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
|
|
||||||
var executionContext = InitializeExecutionContext(hc);
|
|
||||||
executionContext.Setup(x => x.GetGitHubContext("action_status")).Returns(actionStatus.ToString());
|
|
||||||
executionContext.Setup( x=> x.IsEmbedded).Returns(true);
|
|
||||||
executionContext.Setup( x=> x.Stage).Returns(ActionRunStage.Main);
|
|
||||||
|
|
||||||
_jobContext.Status = jobStatus;
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
bool actual = Evaluate("failure()");
|
|
||||||
|
|
||||||
// Assert.
|
|
||||||
Assert.Equal(expected, actual);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Worker")]
|
[Trait("Category", "Worker")]
|
||||||
@@ -164,43 +134,12 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
[InlineData(ActionResult.Failure, ActionResult.Failure, false)]
|
|
||||||
[InlineData(ActionResult.Failure, ActionResult.Success, true)]
|
|
||||||
[InlineData(ActionResult.Success, ActionResult.Failure, false)]
|
|
||||||
[InlineData(ActionResult.Success, ActionResult.Success, true)]
|
|
||||||
[InlineData(ActionResult.Success, null, true)]
|
|
||||||
public void SuccessFunctionComposite(ActionResult jobStatus, ActionResult? actionStatus, bool expected)
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = CreateTestContext())
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
|
|
||||||
var executionContext = InitializeExecutionContext(hc);
|
|
||||||
executionContext.Setup(x => x.GetGitHubContext("action_status")).Returns(actionStatus.ToString());
|
|
||||||
executionContext.Setup( x=> x.IsEmbedded).Returns(true);
|
|
||||||
executionContext.Setup( x=> x.Stage).Returns(ActionRunStage.Main);
|
|
||||||
|
|
||||||
_jobContext.Status = jobStatus;
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
bool actual = Evaluate("success()");
|
|
||||||
|
|
||||||
// Assert.
|
|
||||||
Assert.Equal(expected, actual);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||||
{
|
{
|
||||||
return new TestHostContext(this, testName);
|
return new TestHostContext(this, testName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mock<IExecutionContext> InitializeExecutionContext(TestHostContext hc)
|
private void InitializeExecutionContext(TestHostContext hc)
|
||||||
{
|
{
|
||||||
_jobContext = new JobContext();
|
_jobContext = new JobContext();
|
||||||
|
|
||||||
@@ -210,8 +149,6 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
|||||||
|
|
||||||
_templateContext = new TemplateContext();
|
_templateContext = new TemplateContext();
|
||||||
_templateContext.State[nameof(IExecutionContext)] = executionContext.Object;
|
_templateContext.State[nameof(IExecutionContext)] = executionContext.Object;
|
||||||
|
|
||||||
return executionContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Evaluate(string expression)
|
private bool Evaluate(string expression)
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
name: 'Conditional Composite'
|
|
||||||
description: 'Test composite run step conditionals'
|
|
||||||
inputs:
|
|
||||||
exit-code:
|
|
||||||
description: 'Action fails if set to non-zero'
|
|
||||||
default: '0'
|
|
||||||
outputs:
|
|
||||||
default:
|
|
||||||
description: "Did step run with default?"
|
|
||||||
value: ${{ steps.default-conditional.outputs.default }}
|
|
||||||
success:
|
|
||||||
description: "Did step run with success?"
|
|
||||||
value: ${{ steps.success-conditional.outputs.success }}
|
|
||||||
failure:
|
|
||||||
description: "Did step run with failure?"
|
|
||||||
value: ${{ steps.failure-conditional.outputs.failure }}
|
|
||||||
always:
|
|
||||||
description: "Did step run with always?"
|
|
||||||
value: ${{ steps.always-conditional.outputs.always }}
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- run: exit ${{ inputs.exit-code }}
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- run: echo "::set-output name=default::true"
|
|
||||||
id: default-conditional
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- run: echo "::set-output name=success::true"
|
|
||||||
id: success-conditional
|
|
||||||
shell: bash
|
|
||||||
if: success()
|
|
||||||
|
|
||||||
- run: echo "::set-output name=failure::true"
|
|
||||||
id: failure-conditional
|
|
||||||
shell: bash
|
|
||||||
if: failure()
|
|
||||||
|
|
||||||
- run: echo "::set-output name=always::true"
|
|
||||||
id: always-conditional
|
|
||||||
shell: bash
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
- run: echo "failed"
|
|
||||||
shell: bash
|
|
||||||
if: ${{ inputs.exit-code == 1 && failure() }}
|
|
||||||
@@ -1 +1 @@
|
|||||||
2.284.0
|
2.282.0
|
||||||
|
|||||||
Reference in New Issue
Block a user