mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
1 Commits
v2.169.1
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ca5ee0fbd |
@@ -1,75 +0,0 @@
|
||||
# ADR 361: Wrapper Action
|
||||
|
||||
**Date**: 2020-03-06
|
||||
|
||||
**Status**: Pending
|
||||
|
||||
## Context
|
||||
|
||||
In addition to action's regular execution, action author may wants their action has a chance to participate in:
|
||||
- Job initialize
|
||||
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the begin of the job.
|
||||
- Job cleanup
|
||||
My Action will dirty local workspace or machine environment during execution, we need to cleanup these changes at the end of the job.
|
||||
Ex: `actions/checkout@v2` will write `github.token` into local `.git/config` during execution, it has post job cleanup defined to undo the changes.
|
||||
|
||||
## Decision
|
||||
|
||||
### Add `pre` and `post` execution to action
|
||||
|
||||
Node Action Example:
|
||||
|
||||
```yaml
|
||||
name: 'My action with pre'
|
||||
description: 'My action with pre'
|
||||
runs:
|
||||
using: 'node12'
|
||||
pre: 'setup.js'
|
||||
pre-if: 'success()' // Optional
|
||||
main: 'index.js'
|
||||
post: 'cleanup.js'
|
||||
post-if: 'success()' // Optional
|
||||
```
|
||||
|
||||
Container Action Example:
|
||||
|
||||
```yaml
|
||||
name: 'My action with pre'
|
||||
description: 'My action with pre'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'mycontainer:latest'
|
||||
pre-entrypoint: 'setup.sh'
|
||||
pre-if: 'success()' // Optional
|
||||
entrypoint: 'entrypoint.sh'
|
||||
post-entrypoint: 'cleanup.sh'
|
||||
post-if: 'success()' // Optional
|
||||
```
|
||||
|
||||
Both `pre` and `post` will has default `pre-if/post-if` sets to `always()`.
|
||||
Setting `pre` to `always()` will make sure no matter what condition evaluate result the `main` gets at runtime, the `pre` has always run already.
|
||||
`pre` executes in order of how the steps are defined.
|
||||
`pre` will always be added to job steps list during job setup.
|
||||
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checkout during job initialize.
|
||||
> We can't use GitHub api to download the repository since there is a about 3 mins delay between `git push` and the new commit available to download using GitHub api.
|
||||
|
||||
`post` will be pushed into a `poststeps` stack lazily when the action's `pre` or `main` execution passed `if` condition check and about to run, you can't have an action that only contains a `post`, we will pop and run each `post` after all `pre` and `main` finished.
|
||||
> Currently `post` works for both repository action (`org/repo@v1`) and local action (`./my-action`)
|
||||
|
||||
Valid action:
|
||||
- only has `main`
|
||||
- has `pre` and `main`
|
||||
- has `main` and `post`
|
||||
- has `pre`, `main` and `post`
|
||||
|
||||
Invalid action:
|
||||
- only has `pre`
|
||||
- only has `post`
|
||||
- has `pre` and `post`
|
||||
|
||||
Potential downside of introducing `pre`:
|
||||
|
||||
- Extra magic wrt step order. Users should control the step order. Especially when we introduce templates.
|
||||
- Eliminates the possibility to lazily download the action tarball, since `pre` always run by default, we have to download the tarball to check whether action defined a `pre`
|
||||
- `pre` doesn't work with local action, we suggested customer use local action for testing their action changes, ex CI for their action, to avoid delay between `git push` and GitHub repo tarball download api.
|
||||
- Condition on the `pre` can't be controlled using dynamic step outputs. `pre` executes too early.
|
||||
@@ -1,56 +0,0 @@
|
||||
# ADR 0397: Support adding custom labels during runner config
|
||||
**Date**: 2020-03-30
|
||||
|
||||
**Status**: Approved
|
||||
|
||||
## Context
|
||||
|
||||
Since configuring self-hosted runners is commonly automated via scripts, the labels need to be able to be created during configuration. The runner currently registers the built-in labels (os, arch) during registration but does not accept labels via command line args to extend the set registered.
|
||||
|
||||
See Issue: https://github.com/actions/runner/issues/262
|
||||
|
||||
This is another version of [ADR275](https://github.com/actions/runner/pull/275)
|
||||
|
||||
## Decision
|
||||
|
||||
This ADR proposes that we add a `--labels` option to `config`, which could be used to add custom additional labels to the configured runner.
|
||||
|
||||
For example, to add a single extra label the operator could run:
|
||||
```bash
|
||||
./config.sh --labels mylabel
|
||||
```
|
||||
> Note: the current runner command line parsing and envvar override algorithm only supports a single argument (key).
|
||||
|
||||
This would add the label `mylabel` to the runner, and enable users to select the runner in their workflow using this label:
|
||||
```yaml
|
||||
runs-on: [self-hosted, mylabel]
|
||||
```
|
||||
|
||||
To add multiple labels the operator could run:
|
||||
```bash
|
||||
./config.sh --labels mylabel,anotherlabel
|
||||
```
|
||||
> Note: the current runner command line parsing and envvar override algorithm only supports a single argument (key).
|
||||
|
||||
This would add the label `mylabel` and `anotherlabel` to the runner, and enable users to select the runner in their workflow using this label:
|
||||
```yaml
|
||||
runs-on: [self-hosted, mylabel, anotherlabel]
|
||||
```
|
||||
|
||||
It would not be possible to remove labels from an existing runner using `config.sh`, instead labels would have to be removed using the GitHub UI.
|
||||
|
||||
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commans in unattended config label names. Alternatively we could choose to escape commans but it's a nice to have.
|
||||
|
||||
## Replace
|
||||
|
||||
If an existing runner exists and the option to replace is chosen (interactively of via unattend as in this scenario), then the labels will be replaced / overwritten (not merged).
|
||||
|
||||
## Overriding built-in labels
|
||||
|
||||
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org / runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
|
||||
|
||||
We will also not make other restrictions such as limiting explicitly adding os / arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future proofing / compat.
|
||||
|
||||
## Consequences
|
||||
|
||||
The ability to add custom labels to a self-hosted runner would enable most scenarios where job runner selection based on runner capabilities or characteristics are required.
|
||||
@@ -1,11 +1,27 @@
|
||||
## Features
|
||||
- Runner support for GHES Alpha (#381 #386 #390 #393 $401)
|
||||
- Allow secrets context in Container.env (#388)
|
||||
- Expose whether debug is on/off via RUNNER_DEBUG. (#253)
|
||||
- Upload log on runner when worker get killed due to cancellation timeout. (#255)
|
||||
- Update config.sh/cmd --help documentation (#282)
|
||||
- Set http_proxy and related env vars for job/service containers (#304)
|
||||
- Set both http_proxy and HTTP_PROXY env for runner/worker processes. (#298)
|
||||
|
||||
## Bugs
|
||||
- Raise warning when volume mount root. (#413)
|
||||
- Fix typo (#394)
|
||||
- Verify runner Windows service hash started successfully after configuration (#236)
|
||||
- Detect source file path in L0 without using env. (#257)
|
||||
- Handle escaped '%' in commands data section (#200)
|
||||
- Allow container to be null/empty during matrix expansion (#266)
|
||||
- Translate problem matcher file to host path (#272)
|
||||
- Change hashFiles() expression function to use @actions/glob. (#268)
|
||||
- Default post-job action's condition to always(). (#293)
|
||||
- Support action.yaml file as action's entry file (#288)
|
||||
- Trace javascript action exit code to debug instead of user logs (#290)
|
||||
- Change prompt message when removing a runner to lines up with GitHub.com UI (#303)
|
||||
- Include step.env as part of env context. (#300)
|
||||
- Update Base64 Encoders to deal with suffixes (#284)
|
||||
|
||||
## Misc
|
||||
- N/A
|
||||
- Move .sln file under ./src (#238)
|
||||
- Treat warnings as errors during compile (#249)
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.168.0
|
||||
2.164.0
|
||||
|
||||
13
src/Misc/dotnet-install.sh
vendored
13
src/Misc/dotnet-install.sh
vendored
@@ -172,7 +172,7 @@ get_current_os_name() {
|
||||
return 0
|
||||
elif [ "$uname" = "FreeBSD" ]; then
|
||||
echo "freebsd"
|
||||
return 0
|
||||
return 0
|
||||
elif [ "$uname" = "Linux" ]; then
|
||||
local linux_platform_name
|
||||
linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
|
||||
@@ -728,12 +728,11 @@ downloadcurl() {
|
||||
# Append feed_credential as late as possible before calling curl to avoid logging feed_credential
|
||||
remote_path="${remote_path}${feed_credential}"
|
||||
|
||||
local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs "
|
||||
local failed=false
|
||||
if [ -z "$out_path" ]; then
|
||||
curl $curl_options "$remote_path" || failed=true
|
||||
curl --retry 10 -sSL -f --create-dirs "$remote_path" || failed=true
|
||||
else
|
||||
curl $curl_options -o "$out_path" "$remote_path" || failed=true
|
||||
curl --retry 10 -sSL -f --create-dirs -o "$out_path" "$remote_path" || failed=true
|
||||
fi
|
||||
if [ "$failed" = true ]; then
|
||||
say_verbose "Curl download failed"
|
||||
@@ -749,12 +748,12 @@ downloadwget() {
|
||||
|
||||
# Append feed_credential as late as possible before calling wget to avoid logging feed_credential
|
||||
remote_path="${remote_path}${feed_credential}"
|
||||
local wget_options="--tries 20 --waitretry 2 --connect-timeout 15 "
|
||||
|
||||
local failed=false
|
||||
if [ -z "$out_path" ]; then
|
||||
wget -q $wget_options -O - "$remote_path" || failed=true
|
||||
wget -q --tries 10 -O - "$remote_path" || failed=true
|
||||
else
|
||||
wget $wget_options -O "$out_path" "$remote_path" || failed=true
|
||||
wget --tries 10 -O "$out_path" "$remote_path" || failed=true
|
||||
fi
|
||||
if [ "$failed" = true ]; then
|
||||
say_verbose "Wget download failed"
|
||||
|
||||
1075
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
1075
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@
|
||||
"@types/node": "^12.7.12",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"@zeit/ncc": "^0.20.5",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-github": "^2.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.6.4"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
const { spawn } = require('child_process');
|
||||
// argv[0] = node
|
||||
// argv[1] = macos-run-invoker.js
|
||||
var shell = process.argv[2];
|
||||
var args = process.argv.slice(3);
|
||||
console.log(`::debug::macos-run-invoker: ${shell}`);
|
||||
console.log(`::debug::macos-run-invoker: ${JSON.stringify(args)}`);
|
||||
var launch = spawn(shell, args, { stdio: 'inherit' });
|
||||
launch.on('exit', function (code) {
|
||||
if (code !== 0) {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
@@ -15,9 +15,6 @@ namespace GitHub.Runner.Common
|
||||
[DataContract]
|
||||
public sealed class RunnerSettings
|
||||
{
|
||||
[DataMember(Name = "IsHostedServer", EmitDefaultValue = false)]
|
||||
private bool? _isHostedServer;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public int AgentId { get; set; }
|
||||
|
||||
@@ -45,21 +42,6 @@ namespace GitHub.Runner.Common
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string MonitorSocketAddress { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool IsHostedServer
|
||||
{
|
||||
get
|
||||
{
|
||||
// Old runners do not have this property. Hosted runners likely don't have this property either.
|
||||
return _isHostedServer ?? true;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_isHostedServer = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// Computed property for convenience. Can either return:
|
||||
// 1. If runner was configured at the repo level, returns something like: "myorg/myrepo"
|
||||
@@ -87,15 +69,6 @@ namespace GitHub.Runner.Common
|
||||
return repoOrOrgName;
|
||||
}
|
||||
}
|
||||
|
||||
[OnSerializing]
|
||||
private void OnSerializing(StreamingContext context)
|
||||
{
|
||||
if (_isHostedServer.HasValue && _isHostedServer.Value)
|
||||
{
|
||||
_isHostedServer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ServiceLocator(Default = typeof(ConfigurationStore))]
|
||||
|
||||
@@ -87,7 +87,6 @@ namespace GitHub.Runner.Common
|
||||
public static class Args
|
||||
{
|
||||
public static readonly string Auth = "auth";
|
||||
public static readonly string Labels = "labels";
|
||||
public static readonly string MonitorSocketAddress = "monitorsocketaddress";
|
||||
public static readonly string Name = "name";
|
||||
public static readonly string Pool = "pool";
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace GitHub.Runner.Common
|
||||
CancellationToken RunnerShutdownToken { get; }
|
||||
ShutdownReason RunnerShutdownReason { get; }
|
||||
ISecretMasker SecretMasker { get; }
|
||||
List<ProductInfoHeaderValue> UserAgents { get; }
|
||||
ProductInfoHeaderValue UserAgent { get; }
|
||||
RunnerWebProxy WebProxy { get; }
|
||||
string GetDirectory(WellKnownDirectory directory);
|
||||
string GetConfigFile(WellKnownConfigFile configFile);
|
||||
@@ -54,7 +54,7 @@ namespace GitHub.Runner.Common
|
||||
private readonly ConcurrentDictionary<Type, object> _serviceInstances = new ConcurrentDictionary<Type, object>();
|
||||
private readonly ConcurrentDictionary<Type, Type> _serviceTypes = new ConcurrentDictionary<Type, Type>();
|
||||
private readonly ISecretMasker _secretMasker = new SecretMasker();
|
||||
private readonly List<ProductInfoHeaderValue> _userAgents = new List<ProductInfoHeaderValue>() { new ProductInfoHeaderValue($"GitHubActionsRunner-{BuildConstants.RunnerPackage.PackageName}", BuildConstants.RunnerPackage.Version) };
|
||||
private readonly ProductInfoHeaderValue _userAgent = new ProductInfoHeaderValue($"GitHubActionsRunner-{BuildConstants.RunnerPackage.PackageName}", BuildConstants.RunnerPackage.Version);
|
||||
private CancellationTokenSource _runnerShutdownTokenSource = new CancellationTokenSource();
|
||||
private object _perfLock = new object();
|
||||
private Tracing _trace;
|
||||
@@ -72,7 +72,7 @@ namespace GitHub.Runner.Common
|
||||
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
||||
public ShutdownReason RunnerShutdownReason { get; private set; }
|
||||
public ISecretMasker SecretMasker => _secretMasker;
|
||||
public List<ProductInfoHeaderValue> UserAgents => _userAgents;
|
||||
public ProductInfoHeaderValue UserAgent => _userAgent;
|
||||
public RunnerWebProxy WebProxy => _webProxy;
|
||||
public HostContext(string hostType, string logFile = null)
|
||||
{
|
||||
@@ -189,17 +189,6 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
_trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)");
|
||||
}
|
||||
|
||||
var credFile = GetConfigFile(WellKnownConfigFile.Credentials);
|
||||
if (File.Exists(credFile))
|
||||
{
|
||||
var credData = IOUtil.LoadObject<CredentialData>(credFile);
|
||||
if (credData != null &&
|
||||
credData.Data.TryGetValue("clientId", out var clientId))
|
||||
{
|
||||
_userAgents.Add(new ProductInfoHeaderValue($"RunnerId", clientId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDirectory(WellKnownDirectory directory)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace GitHub.Runner.Common
|
||||
|
||||
// job request
|
||||
Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken);
|
||||
Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, string orchestrationId, CancellationToken cancellationToken);
|
||||
Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, CancellationToken cancellationToken);
|
||||
Task<TaskAgentJobRequest> FinishAgentRequestAsync(int poolId, long requestId, Guid lockToken, DateTime finishTime, TaskResult result, CancellationToken cancellationToken);
|
||||
|
||||
// agent package
|
||||
@@ -300,10 +300,10 @@ namespace GitHub.Runner.Common
|
||||
// JobRequest
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
public Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, string orchestrationId = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
CheckConnection(RunnerConnectionType.JobRequest);
|
||||
return _requestTaskAgentClient.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId: orchestrationId, cancellationToken: cancellationToken);
|
||||
return _requestTaskAgentClient.RenewAgentRequestAsync(poolId, requestId, lockToken, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TaskAgentJobRequest> FinishAgentRequestAsync(int poolId, long requestId, Guid lockToken, DateTime finishTime, TaskResult result, CancellationToken cancellationToken = default(CancellationToken))
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace GitHub.Runner.Listener
|
||||
private readonly string[] validArgs =
|
||||
{
|
||||
Constants.Runner.CommandLine.Args.Auth,
|
||||
Constants.Runner.CommandLine.Args.Labels,
|
||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||
Constants.Runner.CommandLine.Args.Name,
|
||||
Constants.Runner.CommandLine.Args.Pool,
|
||||
@@ -250,24 +249,6 @@ namespace GitHub.Runner.Listener
|
||||
return GetArg(Constants.Runner.CommandLine.Args.StartupType);
|
||||
}
|
||||
|
||||
public ISet<string> GetLabels()
|
||||
{
|
||||
var labelSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string labels = GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Labels,
|
||||
description: $"This runner will have the following labels: 'self-hosted', '{VarUtil.OS}', '{VarUtil.OSArchitecture}' \nEnter any additional labels (ex. label-1,label-2):",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.LabelsValidator,
|
||||
isOptional: true);
|
||||
|
||||
if (!string.IsNullOrEmpty(labels))
|
||||
{
|
||||
labelSet = labels.Split(',').Where(x => !string.IsNullOrEmpty(x)).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return labelSet;
|
||||
}
|
||||
|
||||
//
|
||||
// Private helpers.
|
||||
//
|
||||
@@ -299,8 +280,7 @@ namespace GitHub.Runner.Listener
|
||||
string name,
|
||||
string description,
|
||||
string defaultValue,
|
||||
Func<string, bool> validator,
|
||||
bool isOptional = false)
|
||||
Func<string, bool> validator)
|
||||
{
|
||||
// Check for the arg in the command line parser.
|
||||
ArgUtil.NotNull(validator, nameof(validator));
|
||||
@@ -331,8 +311,7 @@ namespace GitHub.Runner.Listener
|
||||
secret: Constants.Runner.CommandLine.Args.Secrets.Any(x => string.Equals(x, name, StringComparison.OrdinalIgnoreCase)),
|
||||
defaultValue: defaultValue,
|
||||
validator: validator,
|
||||
unattended: Unattended,
|
||||
isOptional: isOptional);
|
||||
unattended: Unattended);
|
||||
}
|
||||
|
||||
private string GetEnvArg(string name)
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
|
||||
RunnerSettings runnerSettings = new RunnerSettings();
|
||||
|
||||
bool isHostedServer = false;
|
||||
// Loop getting url and creds until you can connect
|
||||
ICredentialProvider credProvider = null;
|
||||
VssCredentials creds = null;
|
||||
@@ -94,7 +95,8 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
// Get the URL
|
||||
var inputUrl = command.GetUrl();
|
||||
if (inputUrl.Contains("codedev.ms", StringComparison.OrdinalIgnoreCase))
|
||||
if (!inputUrl.Contains("github.com", StringComparison.OrdinalIgnoreCase) &&
|
||||
!inputUrl.Contains("github.localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
runnerSettings.ServerUrl = inputUrl;
|
||||
// Get the credentials
|
||||
@@ -115,7 +117,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
try
|
||||
{
|
||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||
runnerSettings.IsHostedServer = runnerSettings.GitHubUrl == null || IsHostedServer(new UriBuilder(runnerSettings.GitHubUrl));
|
||||
isHostedServer = await IsHostedServer(runnerSettings.ServerUrl, creds);
|
||||
|
||||
// Validate can connect.
|
||||
await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), creds);
|
||||
@@ -166,9 +168,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
|
||||
_term.WriteLine();
|
||||
|
||||
var userLabels = command.GetLabels();
|
||||
_term.WriteLine();
|
||||
|
||||
var agents = await _runnerServer.GetAgentsAsync(runnerSettings.PoolId, runnerSettings.AgentName);
|
||||
Trace.Verbose("Returns {0} agents", agents.Count);
|
||||
agent = agents.FirstOrDefault();
|
||||
@@ -178,7 +177,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
if (command.GetReplace())
|
||||
{
|
||||
// Update existing agent with new PublicKey, agent version.
|
||||
agent = UpdateExistingAgent(agent, publicKey, userLabels);
|
||||
agent = UpdateExistingAgent(agent, publicKey);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -200,8 +199,8 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new agent.
|
||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels);
|
||||
// Create a new agent.
|
||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -249,6 +248,14 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
UriBuilder configServerUrl = new UriBuilder(runnerSettings.ServerUrl);
|
||||
UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl);
|
||||
if (!isHostedServer && Uri.Compare(configServerUrl.Uri, oauthEndpointUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
{
|
||||
oauthEndpointUrlBuilder.Scheme = configServerUrl.Scheme;
|
||||
oauthEndpointUrlBuilder.Host = configServerUrl.Host;
|
||||
oauthEndpointUrlBuilder.Port = configServerUrl.Port;
|
||||
Trace.Info($"Set oauth endpoint url's scheme://host:port component to match runner configure url's scheme://host:port: '{oauthEndpointUrlBuilder.Uri.AbsoluteUri}'.");
|
||||
}
|
||||
|
||||
var credentialData = new CredentialData
|
||||
{
|
||||
Scheme = Constants.Configuration.OAuth,
|
||||
@@ -284,7 +291,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
// there are two exception messages server send that indicate clock skew.
|
||||
// 1. The bearer token expired on {jwt.ValidTo}. Current server time is {DateTime.UtcNow}.
|
||||
// 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}.
|
||||
// 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}.
|
||||
Trace.Error("Catch exception during test agent connection.");
|
||||
Trace.Error(ex);
|
||||
throw new Exception("The local machine's clock may be out of sync with the server time by more than five minutes. Please sync your clock with your domain or internet time and try again.");
|
||||
@@ -374,6 +381,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
|
||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||
bool isHostedServer = await IsHostedServer(settings.ServerUrl, creds);
|
||||
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
|
||||
|
||||
var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName);
|
||||
@@ -396,7 +404,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
|
||||
}
|
||||
|
||||
//delete credential config files
|
||||
//delete credential config files
|
||||
currentAction = "Removing .credentials";
|
||||
if (hasCredentials)
|
||||
{
|
||||
@@ -410,7 +418,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
||||
}
|
||||
|
||||
//delete settings config file
|
||||
//delete settings config file
|
||||
currentAction = "Removing .runner";
|
||||
if (isConfigured)
|
||||
{
|
||||
@@ -451,7 +459,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
|
||||
|
||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels)
|
||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey)
|
||||
{
|
||||
ArgUtil.NotNull(agent, nameof(agent));
|
||||
agent.Authorization = new TaskAgentAuthorization
|
||||
@@ -459,25 +467,18 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
|
||||
};
|
||||
|
||||
// update should replace the existing labels
|
||||
// update - update instead of delete so we don't lose labels etc...
|
||||
agent.Version = BuildConstants.RunnerPackage.Version;
|
||||
agent.OSDescription = RuntimeInformation.OSDescription;
|
||||
|
||||
agent.Labels.Clear();
|
||||
|
||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
||||
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
|
||||
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
|
||||
agent.Labels.Add("self-hosted");
|
||||
agent.Labels.Add(VarUtil.OS);
|
||||
agent.Labels.Add(VarUtil.OSArchitecture);
|
||||
|
||||
foreach (var userLabel in userLabels)
|
||||
{
|
||||
agent.Labels.Add(new AgentLabel(userLabel, LabelType.User));
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels)
|
||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey)
|
||||
{
|
||||
TaskAgent agent = new TaskAgent(agentName)
|
||||
{
|
||||
@@ -490,43 +491,43 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
OSDescription = RuntimeInformation.OSDescription,
|
||||
};
|
||||
|
||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
||||
agent.Labels.Add(new AgentLabel(VarUtil.OS, LabelType.System));
|
||||
agent.Labels.Add(new AgentLabel(VarUtil.OSArchitecture, LabelType.System));
|
||||
|
||||
foreach (var userLabel in userLabels)
|
||||
{
|
||||
agent.Labels.Add(new AgentLabel(userLabel, LabelType.User));
|
||||
}
|
||||
agent.Labels.Add("self-hosted");
|
||||
agent.Labels.Add(VarUtil.OS);
|
||||
agent.Labels.Add(VarUtil.OSArchitecture);
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
private bool IsHostedServer(UriBuilder gitHubUrl)
|
||||
private async Task<bool> IsHostedServer(string serverUrl, VssCredentials credentials)
|
||||
{
|
||||
return string.Equals(gitHubUrl.Host, "github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(gitHubUrl.Host, "www.github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
|
||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||
var locationServer = HostContext.GetService<ILocationServer>();
|
||||
VssConnection connection = VssUtil.CreateConnection(new Uri(serverUrl), credentials);
|
||||
await locationServer.ConnectAsync(connection);
|
||||
try
|
||||
{
|
||||
var connectionData = await locationServer.GetConnectionDataAsync();
|
||||
Trace.Info($"Server deployment type: {connectionData.DeploymentType}");
|
||||
return connectionData.DeploymentType.HasFlag(DeploymentFlags.Hosted);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Since the DeploymentType is Enum, deserialization exception means there is a new Enum member been added.
|
||||
// It's more likely to be Hosted since OnPremises is always behind and customer can update their agent if are on-prem
|
||||
Trace.Error(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
|
||||
{
|
||||
var githubApiUrl = "";
|
||||
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||
if (IsHostedServer(gitHubUrlBuilder))
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/actions/runner-registration";
|
||||
}
|
||||
else
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/actions/runner-registration";
|
||||
}
|
||||
|
||||
var githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/actions/runner-registration";
|
||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||
using (var httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("RemoteAuth", githubToken);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
|
||||
|
||||
var bodyObject = new Dictionary<string, string>()
|
||||
{
|
||||
|
||||
@@ -20,8 +20,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
bool secret,
|
||||
string defaultValue,
|
||||
Func<String, bool> validator,
|
||||
bool unattended,
|
||||
bool isOptional = false);
|
||||
bool unattended);
|
||||
}
|
||||
|
||||
public sealed class PromptManager : RunnerService, IPromptManager
|
||||
@@ -57,8 +56,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
bool secret,
|
||||
string defaultValue,
|
||||
Func<string, bool> validator,
|
||||
bool unattended,
|
||||
bool isOptional = false)
|
||||
bool unattended)
|
||||
{
|
||||
Trace.Info(nameof(ReadValue));
|
||||
ArgUtil.NotNull(validator, nameof(validator));
|
||||
@@ -72,10 +70,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
else if (isOptional)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Otherwise throw.
|
||||
throw new Exception($"Invalid configuration provided for {argName}. Terminating unattended configuration.");
|
||||
@@ -91,28 +85,18 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
_terminal.Write($"[press Enter for {defaultValue}] ");
|
||||
}
|
||||
else if (isOptional){
|
||||
_terminal.Write($"[press Enter to skip] ");
|
||||
}
|
||||
|
||||
// Read and trim the value.
|
||||
value = secret ? _terminal.ReadSecret() : _terminal.ReadLine();
|
||||
value = value?.Trim() ?? string.Empty;
|
||||
|
||||
// Return the default if not specified.
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(defaultValue))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(defaultValue))
|
||||
{
|
||||
Trace.Info($"Falling back to the default: '{defaultValue}'");
|
||||
return defaultValue;
|
||||
}
|
||||
else if (isOptional)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
Trace.Info($"Falling back to the default: '{defaultValue}'");
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
// Return the value if it is not empty and it is valid.
|
||||
// Otherwise try the loop again.
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Security.Principal;
|
||||
|
||||
@@ -47,21 +46,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
string.Equals(value, "N", StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool LabelsValidator(string labels)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(labels))
|
||||
{
|
||||
var labelSet = labels.Split(',').Where(x => !string.IsNullOrEmpty(x)).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (labelSet.Any(x => x.Length > 256))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool NonEmptyValidator(string value)
|
||||
{
|
||||
return !string.IsNullOrEmpty(value);
|
||||
|
||||
@@ -12,7 +12,6 @@ using System.Linq;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.WebApi.Jwt;
|
||||
|
||||
namespace GitHub.Runner.Listener
|
||||
{
|
||||
@@ -87,30 +86,15 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
var orchestrationId = string.Empty;
|
||||
var systemConnection = jobRequestMessage.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
if (systemConnection?.Authorization != null &&
|
||||
systemConnection.Authorization.Parameters.TryGetValue("AccessToken", out var accessToken) &&
|
||||
!string.IsNullOrEmpty(accessToken))
|
||||
{
|
||||
var jwt = JsonWebToken.Create(accessToken);
|
||||
var claims = jwt.ExtractClaims();
|
||||
orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orchid", StringComparison.OrdinalIgnoreCase))?.Value;
|
||||
if (!string.IsNullOrEmpty(orchestrationId))
|
||||
{
|
||||
Trace.Info($"Pull OrchestrationId {orchestrationId} from JWT claims");
|
||||
}
|
||||
}
|
||||
|
||||
WorkerDispatcher newDispatch = new WorkerDispatcher(jobRequestMessage.JobId, jobRequestMessage.RequestId);
|
||||
if (runOnce)
|
||||
{
|
||||
Trace.Info("Start dispatcher for one time used runner.");
|
||||
newDispatch.WorkerDispatch = RunOnceAsync(jobRequestMessage, orchestrationId, currentDispatch, newDispatch.WorkerCancellationTokenSource.Token, newDispatch.WorkerCancelTimeoutKillTokenSource.Token);
|
||||
newDispatch.WorkerDispatch = RunOnceAsync(jobRequestMessage, currentDispatch, newDispatch.WorkerCancellationTokenSource.Token, newDispatch.WorkerCancelTimeoutKillTokenSource.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
newDispatch.WorkerDispatch = RunAsync(jobRequestMessage, orchestrationId, currentDispatch, newDispatch.WorkerCancellationTokenSource.Token, newDispatch.WorkerCancelTimeoutKillTokenSource.Token);
|
||||
newDispatch.WorkerDispatch = RunAsync(jobRequestMessage, currentDispatch, newDispatch.WorkerCancellationTokenSource.Token, newDispatch.WorkerCancelTimeoutKillTokenSource.Token);
|
||||
}
|
||||
|
||||
_jobInfos.TryAdd(newDispatch.JobId, newDispatch);
|
||||
@@ -300,11 +284,11 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunOnceAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
private async Task RunOnceAsync(Pipelines.AgentJobRequestMessage message, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
||||
await RunAsync(message, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -313,7 +297,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
private async Task RunAsync(Pipelines.AgentJobRequestMessage message, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
{
|
||||
Busy = true;
|
||||
try
|
||||
@@ -344,7 +328,7 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
// start renew job request
|
||||
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
|
||||
Task renewJobRequest = RenewJobRequestAsync(_poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
||||
Task renewJobRequest = RenewJobRequestAsync(_poolId, requestId, lockToken, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
||||
|
||||
// wait till first renew succeed or job request is canceled
|
||||
// not even start worker if the first renew fail
|
||||
@@ -623,7 +607,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RenewJobRequestAsync(int poolId, long requestId, Guid lockToken, string orchestrationId, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
||||
public async Task RenewJobRequestAsync(int poolId, long requestId, Guid lockToken, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
||||
{
|
||||
var runnerServer = HostContext.GetService<IRunnerServer>();
|
||||
TaskAgentJobRequest request = null;
|
||||
@@ -636,7 +620,7 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
try
|
||||
{
|
||||
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
|
||||
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, token);
|
||||
|
||||
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
try
|
||||
{
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy);
|
||||
|
||||
_inConfigStage = true;
|
||||
_completedCommand.Reset();
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
public static class VssUtil
|
||||
{
|
||||
public static void InitializeVssClientSettings(List<ProductInfoHeaderValue> additionalUserAgents, IWebProxy proxy)
|
||||
public static void InitializeVssClientSettings(ProductInfoHeaderValue additionalUserAgent, IWebProxy proxy)
|
||||
{
|
||||
var headerValues = new List<ProductInfoHeaderValue>();
|
||||
headerValues.AddRange(additionalUserAgents);
|
||||
headerValues.Add(additionalUserAgent);
|
||||
headerValues.Add(new ProductInfoHeaderValue($"({RuntimeInformation.OSDescription.Trim()})"));
|
||||
|
||||
if (VssClientHttpRequestSettings.Default.UserAgent != null && VssClientHttpRequestSettings.Default.UserAgent.Count > 0)
|
||||
|
||||
@@ -21,24 +21,11 @@ using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplat
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
public class PrepareResult
|
||||
{
|
||||
public PrepareResult(List<JobExtensionRunner> containerSetupSteps, Dictionary<Guid, IActionRunner> preStepTracker)
|
||||
{
|
||||
this.ContainerSetupSteps = containerSetupSteps;
|
||||
this.PreStepTracker = preStepTracker;
|
||||
}
|
||||
|
||||
public List<JobExtensionRunner> ContainerSetupSteps { get; set; }
|
||||
|
||||
public Dictionary<Guid, IActionRunner> PreStepTracker { get; set; }
|
||||
}
|
||||
|
||||
[ServiceLocator(Default = typeof(ActionManager))]
|
||||
public interface IActionManager : IRunnerService
|
||||
{
|
||||
Dictionary<Guid, ContainerInfo> CachedActionContainers { get; }
|
||||
Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action);
|
||||
}
|
||||
|
||||
@@ -52,7 +39,7 @@ namespace GitHub.Runner.Worker
|
||||
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
|
||||
|
||||
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers;
|
||||
public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
public async Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ArgUtil.NotNull(steps, nameof(steps));
|
||||
@@ -62,24 +49,17 @@ namespace GitHub.Runner.Worker
|
||||
Dictionary<string, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
|
||||
Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase);
|
||||
List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>();
|
||||
Dictionary<Guid, IActionRunner> preStepTracker = new Dictionary<Guid, IActionRunner>();
|
||||
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
|
||||
|
||||
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
||||
// TODO: Depreciate the PREVIEW_ACTION_TOKEN
|
||||
// Log even if we aren't using it to ensure users know.
|
||||
if (!string.IsNullOrEmpty(executionContext.Variables.Get("PREVIEW_ACTION_TOKEN")))
|
||||
{
|
||||
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is deprecated. Please remove it from the repository's secrets");
|
||||
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is depreciated. Please remove it from the repository's secrets");
|
||||
}
|
||||
|
||||
// Clear the cache (for self-hosted runners)
|
||||
// Note, temporarily avoid this step for the on-premises product, to avoid rate limiting.
|
||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||
if (isHostedServer)
|
||||
{
|
||||
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
||||
}
|
||||
// Clear the cache (local runner)
|
||||
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
@@ -131,22 +111,6 @@ namespace GitHub.Runner.Worker
|
||||
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo;
|
||||
}
|
||||
}
|
||||
|
||||
var repoAction = action.Reference as Pipelines.RepositoryPathReference;
|
||||
if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias)
|
||||
{
|
||||
var definition = LoadAction(executionContext, action);
|
||||
if (definition.Data.Execution.HasPre)
|
||||
{
|
||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
actionRunner.Action = action;
|
||||
actionRunner.Stage = ActionRunStage.Pre;
|
||||
actionRunner.Condition = definition.Data.Execution.InitCondition;
|
||||
|
||||
Trace.Info($"Add 'pre' execution for {action.Id}");
|
||||
preStepTracker[action.Id] = actionRunner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +147,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
#endif
|
||||
|
||||
return new PrepareResult(containerSetupSteps, preStepTracker);
|
||||
return containerSetupSteps;
|
||||
}
|
||||
|
||||
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action)
|
||||
@@ -275,19 +239,14 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Action container env: {StringUtil.ConvertToJson(containerAction.Environment)}.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(containerAction.Pre))
|
||||
{
|
||||
Trace.Info($"Action container pre entrypoint: {containerAction.Pre}.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(containerAction.EntryPoint))
|
||||
{
|
||||
Trace.Info($"Action container entrypoint: {containerAction.EntryPoint}.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(containerAction.Post))
|
||||
if (!string.IsNullOrEmpty(containerAction.Cleanup))
|
||||
{
|
||||
Trace.Info($"Action container post entrypoint: {containerAction.Post}.");
|
||||
Trace.Info($"Action container cleanup entrypoint: {containerAction.Cleanup}.");
|
||||
}
|
||||
|
||||
if (CachedActionContainers.TryGetValue(action.Id, out var container))
|
||||
@@ -299,9 +258,8 @@ namespace GitHub.Runner.Worker
|
||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS)
|
||||
{
|
||||
var nodeAction = definition.Data.Execution as NodeJSActionExecutionData;
|
||||
Trace.Info($"Action pre node.js file: {nodeAction.Pre ?? "N/A"}.");
|
||||
Trace.Info($"Action node.js file: {nodeAction.Script}.");
|
||||
Trace.Info($"Action post node.js file: {nodeAction.Post ?? "N/A"}.");
|
||||
Trace.Info($"Action cleanup node.js file: {nodeAction.Cleanup ?? "N/A"}.");
|
||||
}
|
||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Plugin)
|
||||
{
|
||||
@@ -317,7 +275,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (!string.IsNullOrEmpty(plugin.PostPluginTypeName))
|
||||
{
|
||||
pluginAction.Post = plugin.PostPluginTypeName;
|
||||
pluginAction.Cleanup = plugin.PostPluginTypeName;
|
||||
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
||||
}
|
||||
}
|
||||
@@ -490,8 +448,7 @@ namespace GitHub.Runner.Worker
|
||||
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
|
||||
|
||||
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
|
||||
string watermarkFile = destDirectory + ".completed";
|
||||
if (File.Exists(watermarkFile))
|
||||
if (File.Exists(destDirectory + ".completed"))
|
||||
{
|
||||
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
|
||||
return;
|
||||
@@ -541,36 +498,27 @@ namespace GitHub.Runner.Worker
|
||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||
using (var httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||
if (isHostedServer)
|
||||
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
||||
if (string.IsNullOrEmpty(authToken))
|
||||
{
|
||||
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
||||
if (string.IsNullOrEmpty(authToken))
|
||||
{
|
||||
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
||||
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
||||
}
|
||||
// TODO: Depreciate the PREVIEW_ACTION_TOKEN
|
||||
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(authToken))
|
||||
{
|
||||
HostContext.SecretMasker.AddValue(authToken);
|
||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var accessToken = executionContext.GetGitHubContext("token");
|
||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(authToken))
|
||||
{
|
||||
HostContext.SecretMasker.AddValue(authToken);
|
||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Intentionally empty. Temporary for GHES alpha release, download from dotcom unauthenticated.
|
||||
var accessToken = executionContext.GetGitHubContext("token");
|
||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||
}
|
||||
|
||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
|
||||
using (var result = await httpClient.GetStreamAsync(archiveLink))
|
||||
{
|
||||
await result.CopyToAsync(fs, _defaultCopyBufferSize, actionDownloadCancellation.Token);
|
||||
@@ -662,7 +610,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
Trace.Verbose("Create watermark file indicate action download succeed.");
|
||||
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||
File.WriteAllText(destDirectory + ".completed", DateTime.UtcNow.ToString());
|
||||
|
||||
executionContext.Debug($"Archive '{archiveFile}' has been unzipped into '{destDirectory}'.");
|
||||
Trace.Info("Finished getting action repository.");
|
||||
@@ -824,8 +772,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Container;
|
||||
|
||||
public override bool HasPre => !string.IsNullOrEmpty(Pre);
|
||||
public override bool HasPost => !string.IsNullOrEmpty(Post);
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Image { get; set; }
|
||||
|
||||
@@ -835,66 +782,51 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public MappingToken Environment { get; set; }
|
||||
|
||||
public string Pre { get; set; }
|
||||
|
||||
public string Post { get; set; }
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
public sealed class NodeJSActionExecutionData : ActionExecutionData
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS;
|
||||
|
||||
public override bool HasPre => !string.IsNullOrEmpty(Pre);
|
||||
public override bool HasPost => !string.IsNullOrEmpty(Post);
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Script { get; set; }
|
||||
|
||||
public string Pre { get; set; }
|
||||
|
||||
public string Post { get; set; }
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PluginActionExecutionData : ActionExecutionData
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin;
|
||||
|
||||
public override bool HasPre => false;
|
||||
|
||||
public override bool HasPost => !string.IsNullOrEmpty(Post);
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Plugin { get; set; }
|
||||
|
||||
public string Post { get; set; }
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ScriptActionExecutionData : ActionExecutionData
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Script;
|
||||
public override bool HasPre => false;
|
||||
public override bool HasPost => false;
|
||||
|
||||
public override bool HasCleanup => false;
|
||||
}
|
||||
|
||||
public abstract class ActionExecutionData
|
||||
{
|
||||
private string _initCondition = $"{Constants.Expressions.Always}()";
|
||||
private string _cleanupCondition = $"{Constants.Expressions.Always}()";
|
||||
|
||||
public abstract ActionExecutionType ExecutionType { get; }
|
||||
|
||||
public abstract bool HasPre { get; }
|
||||
public abstract bool HasPost { get; }
|
||||
public abstract bool HasCleanup { get; }
|
||||
|
||||
public string CleanupCondition
|
||||
{
|
||||
get { return _cleanupCondition; }
|
||||
set { _cleanupCondition = value; }
|
||||
}
|
||||
|
||||
public string InitCondition
|
||||
{
|
||||
get { return _initCondition; }
|
||||
set { _initCondition = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ContainerSetupInfo
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile);
|
||||
|
||||
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
|
||||
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
|
||||
string EvaluateDefaultInput(IExecutionContext executionContext, string inputName, TemplateToken token);
|
||||
string EvaluateDefaultInput(IExecutionContext executionContext, string inputName, TemplateToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
}
|
||||
|
||||
public sealed class ActionManifestManager : RunnerService, IActionManifestManager
|
||||
@@ -54,7 +54,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile)
|
||||
{
|
||||
var context = CreateContext(executionContext);
|
||||
var context = CreateContext(executionContext, null);
|
||||
ActionDefinitionData actionDefinition = new ActionDefinitionData();
|
||||
try
|
||||
{
|
||||
@@ -133,13 +133,13 @@ namespace GitHub.Runner.Worker
|
||||
public List<string> EvaluateContainerArguments(
|
||||
IExecutionContext executionContext,
|
||||
SequenceToken token,
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues)
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext, extraExpressionValues);
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
try
|
||||
{
|
||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "container-runs-args", token, 0, null, omitHeader: true);
|
||||
@@ -172,13 +172,13 @@ namespace GitHub.Runner.Worker
|
||||
public Dictionary<string, string> EvaluateContainerEnvironment(
|
||||
IExecutionContext executionContext,
|
||||
MappingToken token,
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues)
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
{
|
||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext, extraExpressionValues);
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
try
|
||||
{
|
||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "container-runs-env", token, 0, null, omitHeader: true);
|
||||
@@ -216,12 +216,13 @@ namespace GitHub.Runner.Worker
|
||||
public string EvaluateDefaultInput(
|
||||
IExecutionContext executionContext,
|
||||
string inputName,
|
||||
TemplateToken token)
|
||||
TemplateToken token,
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
{
|
||||
string result = "";
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext);
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
try
|
||||
{
|
||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "input-default-context", token, 0, null, omitHeader: true);
|
||||
@@ -246,7 +247,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private TemplateContext CreateContext(
|
||||
IExecutionContext executionContext,
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues = null)
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
{
|
||||
var result = new TemplateContext
|
||||
{
|
||||
@@ -260,27 +261,14 @@ namespace GitHub.Runner.Worker
|
||||
TraceWriter = executionContext.ToTemplateTraceWriter(),
|
||||
};
|
||||
|
||||
// Expression values from execution context
|
||||
foreach (var pair in executionContext.ExpressionValues)
|
||||
if (contextData?.Count > 0)
|
||||
{
|
||||
result.ExpressionValues[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
// Extra expression values
|
||||
if (extraExpressionValues?.Count > 0)
|
||||
{
|
||||
foreach (var pair in extraExpressionValues)
|
||||
foreach (var pair in contextData)
|
||||
{
|
||||
result.ExpressionValues[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Expression functions from execution context
|
||||
foreach (var item in executionContext.ExpressionFunctions)
|
||||
{
|
||||
result.ExpressionFunctions.Add(item);
|
||||
}
|
||||
|
||||
// Add the file table
|
||||
if (_fileTable?.Count > 0)
|
||||
{
|
||||
@@ -305,9 +293,6 @@ namespace GitHub.Runner.Worker
|
||||
var envToken = default(MappingToken);
|
||||
var mainToken = default(StringToken);
|
||||
var pluginToken = default(StringToken);
|
||||
var preToken = default(StringToken);
|
||||
var preEntrypointToken = default(StringToken);
|
||||
var preIfToken = default(StringToken);
|
||||
var postToken = default(StringToken);
|
||||
var postEntrypointToken = default(StringToken);
|
||||
var postIfToken = default(StringToken);
|
||||
@@ -346,15 +331,6 @@ namespace GitHub.Runner.Worker
|
||||
case "post-if":
|
||||
postIfToken = run.Value.AssertString("post-if");
|
||||
break;
|
||||
case "pre":
|
||||
preToken = run.Value.AssertString("pre");
|
||||
break;
|
||||
case "pre-entrypoint":
|
||||
preEntrypointToken = run.Value.AssertString("pre-entrypoint");
|
||||
break;
|
||||
case "pre-if":
|
||||
preIfToken = run.Value.AssertString("pre-if");
|
||||
break;
|
||||
default:
|
||||
Trace.Info($"Ignore run property {runsKey}.");
|
||||
break;
|
||||
@@ -377,9 +353,7 @@ namespace GitHub.Runner.Worker
|
||||
Arguments = argsToken,
|
||||
EntryPoint = entrypointToken?.Value,
|
||||
Environment = envToken,
|
||||
Pre = preEntrypointToken?.Value,
|
||||
InitCondition = preIfToken?.Value ?? "always()",
|
||||
Post = postEntrypointToken?.Value,
|
||||
Cleanup = postEntrypointToken?.Value,
|
||||
CleanupCondition = postIfToken?.Value ?? "always()"
|
||||
};
|
||||
}
|
||||
@@ -388,16 +362,14 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (string.IsNullOrEmpty(mainToken?.Value))
|
||||
{
|
||||
throw new ArgumentNullException($"Entry javascript file is not provided.");
|
||||
throw new ArgumentNullException($"Entry javascript fils is not provided.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return new NodeJSActionExecutionData()
|
||||
{
|
||||
Script = mainToken.Value,
|
||||
Pre = preToken?.Value,
|
||||
InitCondition = preIfToken?.Value ?? "always()",
|
||||
Post = postToken?.Value,
|
||||
Cleanup = postToken?.Value,
|
||||
CleanupCondition = postIfToken?.Value ?? "always()"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public enum ActionRunStage
|
||||
{
|
||||
Pre,
|
||||
Main,
|
||||
Post,
|
||||
}
|
||||
@@ -27,7 +26,7 @@ namespace GitHub.Runner.Worker
|
||||
public interface IActionRunner : IStep, IRunnerService
|
||||
{
|
||||
ActionRunStage Stage { get; set; }
|
||||
bool TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context);
|
||||
Boolean TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context);
|
||||
Pipelines.ActionStep Action { get; set; }
|
||||
}
|
||||
|
||||
@@ -82,18 +81,20 @@ namespace GitHub.Runner.Worker
|
||||
ActionExecutionData handlerData = definition.Data?.Execution;
|
||||
ArgUtil.NotNull(handlerData, nameof(handlerData));
|
||||
|
||||
if (handlerData.HasPre &&
|
||||
Action.Reference is Pipelines.RepositoryPathReference repoAction &&
|
||||
string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ExecutionContext.Warning($"`pre` execution is not supported for local action from '{repoAction.Path}'");
|
||||
}
|
||||
|
||||
// The action has post cleanup defined.
|
||||
// we need to create timeline record for them and add them to the step list that StepRunner is using
|
||||
if (handlerData.HasPost && (Stage == ActionRunStage.Pre || Stage == ActionRunStage.Main))
|
||||
if (handlerData.HasCleanup && Stage == ActionRunStage.Main)
|
||||
{
|
||||
string postDisplayName = $"Post {this.DisplayName}";
|
||||
string postDisplayName = null;
|
||||
if (this.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix))
|
||||
{
|
||||
postDisplayName = $"Post {this.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
postDisplayName = $"Post {this.DisplayName}";
|
||||
}
|
||||
|
||||
var repositoryReference = Action.Reference as RepositoryPathReference;
|
||||
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
|
||||
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
|
||||
@@ -107,7 +108,7 @@ namespace GitHub.Runner.Worker
|
||||
actionRunner.Condition = handlerData.CleanupCondition;
|
||||
actionRunner.DisplayName = postDisplayName;
|
||||
|
||||
ExecutionContext.RegisterPostJobStep(actionRunner);
|
||||
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner);
|
||||
}
|
||||
|
||||
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();
|
||||
@@ -141,7 +142,7 @@ namespace GitHub.Runner.Worker
|
||||
// Load the inputs.
|
||||
ExecutionContext.Debug("Loading inputs");
|
||||
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);
|
||||
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues);
|
||||
|
||||
foreach (KeyValuePair<string, string> input in inputs)
|
||||
{
|
||||
@@ -161,7 +162,13 @@ namespace GitHub.Runner.Worker
|
||||
string key = input.Key.AssertString("action input name").Value;
|
||||
if (!inputs.ContainsKey(key))
|
||||
{
|
||||
inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value);
|
||||
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var data in ExecutionContext.ExpressionValues)
|
||||
{
|
||||
evaluateContext[data.Key] = data.Value;
|
||||
}
|
||||
|
||||
inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value, evaluateContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,14 +293,10 @@ namespace GitHub.Runner.Worker
|
||||
return displayName;
|
||||
}
|
||||
// Try evaluating fully
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
try
|
||||
{
|
||||
if (tokenToParse.CheckHasRequiredContext(contextData, context.ExpressionFunctions))
|
||||
{
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
displayName = templateEvaluator.EvaluateStepDisplayName(tokenToParse, contextData, context.ExpressionFunctions);
|
||||
didFullyEvaluate = true;
|
||||
}
|
||||
didFullyEvaluate = templateEvaluator.TryEvaluateStepDisplayName(tokenToParse, contextData, out displayName);
|
||||
}
|
||||
catch (TemplateValidationException e)
|
||||
{
|
||||
|
||||
@@ -61,7 +61,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
foreach (var volume in container.Volumes)
|
||||
{
|
||||
UserMountVolumes[volume] = volume;
|
||||
MountVolumes.Add(new MountVolume(volume));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,13 +130,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
// Watermark for GitHub Action environment
|
||||
dockerOptions.Add("-e GITHUB_ACTIONS=true");
|
||||
|
||||
// Set CI=true when no one else already set it.
|
||||
// CI=true is common set in most CI provider in GitHub
|
||||
if (!container.ContainerEnvironmentVariables.ContainsKey("CI"))
|
||||
{
|
||||
dockerOptions.Add("-e CI=true");
|
||||
}
|
||||
|
||||
foreach (var volume in container.MountVolumes)
|
||||
{
|
||||
// replace `"` with `\"` and add `"{0}"` to all path.
|
||||
@@ -196,13 +189,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
// Watermark for GitHub Action environment
|
||||
dockerOptions.Add("-e GITHUB_ACTIONS=true");
|
||||
|
||||
// Set CI=true when no one else already set it.
|
||||
// CI=true is common set in most CI provider in GitHub
|
||||
if (!container.ContainerEnvironmentVariables.ContainsKey("CI"))
|
||||
{
|
||||
dockerOptions.Add("-e CI=true");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(container.ContainerEntryPoint))
|
||||
{
|
||||
dockerOptions.Add($"--entrypoint \"{container.ContainerEntryPoint}\"");
|
||||
|
||||
@@ -47,9 +47,9 @@ namespace GitHub.Runner.Worker
|
||||
condition: $"{PipelineTemplateConstants.Always}()",
|
||||
displayName: "Stop containers",
|
||||
data: data);
|
||||
|
||||
|
||||
executionContext.Debug($"Register post job cleanup for stopping/deleting containers.");
|
||||
executionContext.RegisterPostJobStep(postJobStep);
|
||||
executionContext.RegisterPostJobStep(nameof(StopContainersAsync), postJobStep);
|
||||
|
||||
// Check whether we are inside a container.
|
||||
// Our container feature requires to map working directory from host to the container.
|
||||
@@ -180,11 +180,6 @@ namespace GitHub.Runner.Worker
|
||||
foreach (var volume in container.UserMountVolumes)
|
||||
{
|
||||
Trace.Info($"User provided volume: {volume.Value}");
|
||||
var mount = new MountVolume(volume.Value);
|
||||
if (string.Equals(mount.SourceVolumePath, "/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionContext.Warning($"Volume mount {volume.Value} is going to mount '/' into the container which may cause file ownership change in the entire file system and cause Actions Runner to lose permission to access the disk.");
|
||||
}
|
||||
}
|
||||
|
||||
// Pull down docker image with retry up to 3 times
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
@@ -17,11 +16,12 @@ using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Services.WebApi;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -55,7 +55,6 @@ namespace GitHub.Runner.Worker
|
||||
IList<String> FileTable { get; }
|
||||
StepsContext StepsContext { get; }
|
||||
DictionaryContextData ExpressionValues { get; }
|
||||
IList<IFunctionInfo> ExpressionFunctions { get; }
|
||||
List<string> PrependPath { get; }
|
||||
ContainerInfo Container { get; set; }
|
||||
List<ContainerInfo> ServiceContainers { get; }
|
||||
@@ -103,7 +102,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(IStep step);
|
||||
void RegisterPostJobStep(string refName, IStep step);
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -149,7 +148,6 @@ namespace GitHub.Runner.Worker
|
||||
public IList<String> FileTable { get; private set; }
|
||||
public StepsContext StepsContext { get; private set; }
|
||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||
public bool WriteDebug { get; private set; }
|
||||
public List<string> PrependPath { get; private set; }
|
||||
public ContainerInfo Container { get; set; }
|
||||
@@ -161,9 +159,6 @@ namespace GitHub.Runner.Worker
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
public Stack<IStep> PostJobSteps { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has StepsWithPostRegistered
|
||||
public HashSet<Guid> StepsWithPostRegistered { get; private set; }
|
||||
|
||||
public bool EchoOnActionCommand { get; set; }
|
||||
|
||||
|
||||
@@ -251,15 +246,9 @@ namespace GitHub.Runner.Worker
|
||||
});
|
||||
}
|
||||
|
||||
public void RegisterPostJobStep(IStep step)
|
||||
public void RegisterPostJobStep(string refName, IStep step)
|
||||
{
|
||||
if (step is IActionRunner actionRunner && !Root.StepsWithPostRegistered.Add(actionRunner.Action.Id))
|
||||
{
|
||||
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to post step stack.");
|
||||
return;
|
||||
}
|
||||
|
||||
step.ExecutionContext = Root.CreatePostChild(step.DisplayName, IntraActionState);
|
||||
step.ExecutionContext = Root.CreatePostChild(step.DisplayName, refName, IntraActionState);
|
||||
Root.PostJobSteps.Push(step);
|
||||
}
|
||||
|
||||
@@ -291,10 +280,6 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
child.ExpressionValues[pair.Key] = pair.Value;
|
||||
}
|
||||
foreach (var item in ExpressionFunctions)
|
||||
{
|
||||
child.ExpressionFunctions.Add(item);
|
||||
}
|
||||
child._cancellationTokenSource = new CancellationTokenSource();
|
||||
child.WriteDebug = WriteDebug;
|
||||
child._parentExecutionContext = this;
|
||||
@@ -608,6 +593,12 @@ namespace GitHub.Runner.Worker
|
||||
// File table
|
||||
FileTable = new List<String>(message.FileTable ?? new string[0]);
|
||||
|
||||
// Expression functions
|
||||
if (Variables.GetBoolean("System.HashFilesV2") == true)
|
||||
{
|
||||
ExpressionConstants.UpdateFunction<Handlers.HashFiles>("hashFiles", 1, byte.MaxValue);
|
||||
}
|
||||
|
||||
// Expression values
|
||||
if (message.ContextData?.Count > 0)
|
||||
{
|
||||
@@ -656,9 +647,6 @@ namespace GitHub.Runner.Worker
|
||||
// PostJobSteps for job ExecutionContext
|
||||
PostJobSteps = new Stack<IStep>();
|
||||
|
||||
// StepsWithPostRegistered for job ExecutionContext
|
||||
StepsWithPostRegistered = new HashSet<Guid>();
|
||||
|
||||
// Job timeline record.
|
||||
InitializeTimelineRecord(
|
||||
timelineId: message.Timeline.Id,
|
||||
@@ -859,7 +847,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
private IExecutionContext CreatePostChild(string displayName, Dictionary<string, string> intraActionState)
|
||||
private IExecutionContext CreatePostChild(string displayName, string refName, Dictionary<string, string> intraActionState)
|
||||
{
|
||||
if (!_expandedForPostJob)
|
||||
{
|
||||
@@ -868,8 +856,7 @@ namespace GitHub.Runner.Worker
|
||||
_childTimelineRecordOrder = _childTimelineRecordOrder * 2;
|
||||
}
|
||||
|
||||
var newGuid = Guid.NewGuid();
|
||||
return CreateChild(newGuid, displayName, newGuid.ToString("N"), null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
return CreateChild(Guid.NewGuid(), displayName, refName, null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -928,19 +915,11 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, object>> ToExpressionState(this IExecutionContext context)
|
||||
public static PipelineTemplateEvaluator ToPipelineTemplateEvaluator(this IExecutionContext context)
|
||||
{
|
||||
return new[] { new KeyValuePair<string, object>(nameof(IExecutionContext), context) };
|
||||
}
|
||||
|
||||
public static PipelineTemplateEvaluator ToPipelineTemplateEvaluator(this IExecutionContext context, ObjectTemplating.ITraceWriter traceWriter = null)
|
||||
{
|
||||
if (traceWriter == null)
|
||||
{
|
||||
traceWriter = context.ToTemplateTraceWriter();
|
||||
}
|
||||
var schema = PipelineTemplateSchemaFactory.GetSchema();
|
||||
return new PipelineTemplateEvaluator(traceWriter, schema, context.FileTable);
|
||||
var templateTrace = context.ToTemplateTraceWriter();
|
||||
var schema = new PipelineTemplateSchemaFactory().CreateSchema();
|
||||
return new PipelineTemplateEvaluator(templateTrace, schema, context.FileTable);
|
||||
}
|
||||
|
||||
public static ObjectTemplating.ITraceWriter ToTemplateTraceWriter(this IExecutionContext context)
|
||||
@@ -955,7 +934,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
internal TemplateTraceWriter(IExecutionContext executionContext)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
_executionContext = executionContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,28 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
public sealed class HashFilesFunction : Function
|
||||
public class FunctionTrace : ITraceWriter
|
||||
{
|
||||
private GitHub.DistributedTask.Expressions2.ITraceWriter _trace;
|
||||
|
||||
public FunctionTrace(GitHub.DistributedTask.Expressions2.ITraceWriter trace)
|
||||
{
|
||||
_trace = trace;
|
||||
}
|
||||
public void Info(string message)
|
||||
{
|
||||
_trace.Info(message);
|
||||
}
|
||||
|
||||
public void Verbose(string message)
|
||||
{
|
||||
_trace.Info(message);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HashFiles : Function
|
||||
{
|
||||
protected sealed override Object EvaluateCore(
|
||||
EvaluationContext context,
|
||||
@@ -63,7 +82,7 @@ namespace GitHub.Runner.Worker.Expressions
|
||||
string node = Path.Combine(runnerRoot, "externals", "node12", "bin", $"node{IOUtil.ExeExtension}");
|
||||
string hashFilesScript = Path.Combine(binDir, "hashFiles");
|
||||
var hashResult = string.Empty;
|
||||
var p = new ProcessInvoker(new HashFilesTrace(context.Trace));
|
||||
var p = new ProcessInvoker(new FunctionTrace(context.Trace));
|
||||
p.ErrorDataReceived += ((_, data) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(data.Data) && data.Data.StartsWith("__OUTPUT__") && data.Data.EndsWith("__OUTPUT__"))
|
||||
@@ -103,24 +122,5 @@ namespace GitHub.Runner.Worker.Expressions
|
||||
|
||||
return hashResult;
|
||||
}
|
||||
|
||||
private sealed class HashFilesTrace : ITraceWriter
|
||||
{
|
||||
private GitHub.DistributedTask.Expressions2.ITraceWriter _trace;
|
||||
|
||||
public HashFilesTrace(GitHub.DistributedTask.Expressions2.ITraceWriter trace)
|
||||
{
|
||||
_trace = trace;
|
||||
}
|
||||
public void Info(string message)
|
||||
{
|
||||
_trace.Info(message);
|
||||
}
|
||||
|
||||
public void Verbose(string message)
|
||||
{
|
||||
_trace.Info(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
src/Runner.Worker/ExpressionManager.cs
Normal file
162
src/Runner.Worker/ExpressionManager.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
[ServiceLocator(Default = typeof(ExpressionManager))]
|
||||
public interface IExpressionManager : IRunnerService
|
||||
{
|
||||
ConditionResult Evaluate(IExecutionContext context, string condition, bool hostTracingOnly = false);
|
||||
}
|
||||
|
||||
public sealed class ExpressionManager : RunnerService, IExpressionManager
|
||||
{
|
||||
public ConditionResult Evaluate(IExecutionContext executionContext, string condition, bool hostTracingOnly = false)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
|
||||
ConditionResult result = new ConditionResult();
|
||||
var expressionTrace = new TraceWriter(Trace, hostTracingOnly ? null : executionContext);
|
||||
var tree = Parse(executionContext, expressionTrace, condition);
|
||||
var expressionResult = tree.Evaluate(expressionTrace, HostContext.SecretMasker, state: executionContext, options: null);
|
||||
result.Value = expressionResult.IsTruthy;
|
||||
result.Trace = expressionTrace.Trace;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IExpressionNode Parse(IExecutionContext executionContext, TraceWriter expressionTrace, string condition)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(condition))
|
||||
{
|
||||
condition = $"{PipelineTemplateConstants.Success}()";
|
||||
}
|
||||
|
||||
var parser = new ExpressionParser();
|
||||
var namedValues = executionContext.ExpressionValues.Keys.Select(x => new NamedValueInfo<ContextValueNode>(x)).ToArray();
|
||||
var functions = new IFunctionInfo[]
|
||||
{
|
||||
new FunctionInfo<AlwaysNode>(name: Constants.Expressions.Always, minParameters: 0, maxParameters: 0),
|
||||
new FunctionInfo<CancelledNode>(name: Constants.Expressions.Cancelled, minParameters: 0, maxParameters: 0),
|
||||
new FunctionInfo<FailureNode>(name: Constants.Expressions.Failure, minParameters: 0, maxParameters: 0),
|
||||
new FunctionInfo<SuccessNode>(name: Constants.Expressions.Success, minParameters: 0, maxParameters: 0),
|
||||
};
|
||||
return parser.CreateTree(condition, expressionTrace, namedValues, functions) ?? new SuccessNode();
|
||||
}
|
||||
|
||||
private sealed class TraceWriter : DistributedTask.Expressions2.ITraceWriter
|
||||
{
|
||||
private readonly IExecutionContext _executionContext;
|
||||
private readonly Tracing _trace;
|
||||
private readonly StringBuilder _traceBuilder = new StringBuilder();
|
||||
|
||||
public string Trace => _traceBuilder.ToString();
|
||||
|
||||
public TraceWriter(Tracing trace, IExecutionContext executionContext)
|
||||
{
|
||||
ArgUtil.NotNull(trace, nameof(trace));
|
||||
_trace = trace;
|
||||
_executionContext = executionContext;
|
||||
}
|
||||
|
||||
public void Info(string message)
|
||||
{
|
||||
_trace.Info(message);
|
||||
_executionContext?.Debug(message);
|
||||
_traceBuilder.AppendLine(message);
|
||||
}
|
||||
|
||||
public void Verbose(string message)
|
||||
{
|
||||
_trace.Verbose(message);
|
||||
_executionContext?.Debug(message);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class AlwaysNode : Function
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext context, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CancelledNode : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var executionContext = evaluationContext.State as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FailureNode : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var executionContext = evaluationContext.State as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SuccessNode : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var executionContext = evaluationContext.State as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ContextValueNode : NamedValue
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var jobContext = evaluationContext.State as IExecutionContext;
|
||||
ArgUtil.NotNull(jobContext, nameof(jobContext));
|
||||
return jobContext.ExpressionValues[Name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ConditionResult
|
||||
{
|
||||
public ConditionResult(bool value = false, string trace = null)
|
||||
{
|
||||
this.Value = value;
|
||||
this.Trace = trace;
|
||||
}
|
||||
|
||||
public bool Value { get; set; }
|
||||
public string Trace { get; set; }
|
||||
|
||||
public static implicit operator ConditionResult(bool value)
|
||||
{
|
||||
return new ConditionResult(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
{
|
||||
public sealed class AlwaysFunction : Function
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext context, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
{
|
||||
public sealed class CancelledFunction : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var templateContext = evaluationContext.State as TemplateContext;
|
||||
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Cancelled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
{
|
||||
public sealed class FailureFunction : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var templateContext = evaluationContext.State as TemplateContext;
|
||||
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
{
|
||||
public sealed class SuccessFunction : Function
|
||||
{
|
||||
protected sealed override object EvaluateCore(EvaluationContext evaluationContext, out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
var templateContext = evaluationContext.State as TemplateContext;
|
||||
ArgUtil.NotNull(templateContext, nameof(templateContext));
|
||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ActionResult jobStatus = executionContext.JobContext.Status ?? ActionResult.Success;
|
||||
return jobStatus == ActionResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
"action",
|
||||
"actor",
|
||||
"api_url", // temp for GHES alpha release
|
||||
"base_ref",
|
||||
"event_name",
|
||||
"event_path",
|
||||
@@ -18,11 +17,9 @@ namespace GitHub.Runner.Worker
|
||||
"job",
|
||||
"ref",
|
||||
"repository",
|
||||
"repository_owner",
|
||||
"run_id",
|
||||
"run_number",
|
||||
"sha",
|
||||
"url", // temp for GHES alpha release
|
||||
"workflow",
|
||||
"workspace",
|
||||
};
|
||||
|
||||
@@ -82,13 +82,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint");
|
||||
}
|
||||
}
|
||||
else if (stage == ActionRunStage.Pre)
|
||||
{
|
||||
container.ContainerEntryPoint = Data.Pre;
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
container.ContainerEntryPoint = Data.Post;
|
||||
container.ContainerEntryPoint = Data.Cleanup;
|
||||
}
|
||||
|
||||
// create inputs context for template evaluation
|
||||
@@ -101,14 +97,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
var extraExpressionValues = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
extraExpressionValues["inputs"] = inputsContext;
|
||||
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
evaluateContext["inputs"] = inputsContext;
|
||||
|
||||
var manifestManager = HostContext.GetService<IActionManifestManager>();
|
||||
if (Data.Arguments != null)
|
||||
{
|
||||
container.ContainerEntryPointArgs = "";
|
||||
var evaluatedArgs = manifestManager.EvaluateContainerArguments(ExecutionContext, Data.Arguments, extraExpressionValues);
|
||||
var evaluatedArgs = manifestManager.EvaluateContainerArguments(ExecutionContext, Data.Arguments, evaluateContext);
|
||||
foreach (var arg in evaluatedArgs)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(arg))
|
||||
@@ -128,7 +124,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
if (Data.Environment != null)
|
||||
{
|
||||
var evaluatedEnv = manifestManager.EvaluateContainerEnvironment(ExecutionContext, Data.Environment, extraExpressionValues);
|
||||
var evaluatedEnv = manifestManager.EvaluateContainerEnvironment(ExecutionContext, Data.Environment, evaluateContext);
|
||||
foreach (var env in evaluatedEnv)
|
||||
{
|
||||
if (!this.Environment.ContainsKey(env.Key))
|
||||
|
||||
@@ -60,13 +60,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
target = Data.Script;
|
||||
}
|
||||
else if (stage == ActionRunStage.Pre)
|
||||
{
|
||||
target = Data.Pre;
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
target = Data.Post;
|
||||
target = Data.Cleanup;
|
||||
}
|
||||
|
||||
ArgUtil.NotNullOrEmpty(target, nameof(target));
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
plugin = Data.Post;
|
||||
plugin = Data.Cleanup;
|
||||
}
|
||||
|
||||
ArgUtil.NotNullOrEmpty(plugin, nameof(plugin));
|
||||
|
||||
@@ -259,16 +259,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
// dump out the command
|
||||
var fileName = isContainerStepHost ? shellCommand : commandPath;
|
||||
#if OS_OSX
|
||||
if (Environment.ContainsKey("DYLD_INSERT_LIBRARIES")) // We don't check `isContainerStepHost` because we don't support container on macOS
|
||||
{
|
||||
// launch `node macOSRunInvoker.js shell args` instead of `shell args` to avoid macOS SIP remove `DYLD_INSERT_LIBRARIES` when launch process
|
||||
string node12 = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node12", "bin", $"node{IOUtil.ExeExtension}");
|
||||
string macOSRunInvoker = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "macos-run-invoker.js");
|
||||
arguments = $"\"{macOSRunInvoker.Replace("\"", "\\\"")}\" \"{fileName.Replace("\"", "\\\"")}\" {arguments}";
|
||||
fileName = node12;
|
||||
}
|
||||
#endif
|
||||
ExecutionContext.Debug($"{fileName} {arguments}");
|
||||
|
||||
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
||||
|
||||
@@ -110,9 +110,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
// try to resolve path inside container if the request path is part of the mount volume
|
||||
#if OS_WINDOWS
|
||||
if (Container.MountVolumes.Exists(x => !string.IsNullOrEmpty(x.SourceVolumePath) && path.StartsWith(x.SourceVolumePath, StringComparison.OrdinalIgnoreCase)))
|
||||
if (Container.MountVolumes.Exists(x => path.StartsWith(x.SourceVolumePath, StringComparison.OrdinalIgnoreCase)))
|
||||
#else
|
||||
if (Container.MountVolumes.Exists(x => !string.IsNullOrEmpty(x.SourceVolumePath) && path.StartsWith(x.SourceVolumePath)))
|
||||
if (Container.MountVolumes.Exists(x => path.StartsWith(x.SourceVolumePath)))
|
||||
#endif
|
||||
{
|
||||
return Container.TranslateToContainerPath(path);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -128,23 +127,12 @@ namespace GitHub.Runner.Worker
|
||||
context.SetRunnerContext("workspace", Path.Combine(_workDirectory, trackingConfig.PipelineDirectory));
|
||||
context.SetGitHubContext("workspace", Path.Combine(_workDirectory, trackingConfig.WorkspaceDirectory));
|
||||
|
||||
// Temporary hack for GHES alpha
|
||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||
var runnerSettings = configurationStore.GetSettings();
|
||||
if (!runnerSettings.IsHostedServer && !string.IsNullOrEmpty(runnerSettings.GitHubUrl))
|
||||
{
|
||||
var url = new Uri(runnerSettings.GitHubUrl);
|
||||
var portInfo = url.IsDefaultPort ? string.Empty : $":{url.Port.ToString(CultureInfo.InvariantCulture)}";
|
||||
context.SetGitHubContext("url", $"{url.Scheme}://{url.Host}{portInfo}");
|
||||
context.SetGitHubContext("api_url", $"{url.Scheme}://{url.Host}{portInfo}/api/v3");
|
||||
}
|
||||
|
||||
// Evaluate the job-level environment variables
|
||||
context.Debug("Evaluating job-level environment variables");
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
foreach (var token in message.EnvironmentVariables)
|
||||
{
|
||||
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, jobContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var pair in environmentVariables)
|
||||
{
|
||||
context.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty;
|
||||
@@ -154,7 +142,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Evaluate the job container
|
||||
context.Debug("Evaluating job container");
|
||||
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues);
|
||||
if (container != null)
|
||||
{
|
||||
jobContext.Container = new Container.ContainerInfo(HostContext, container);
|
||||
@@ -162,7 +150,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Evaluate the job service containers
|
||||
context.Debug("Evaluating job service containers");
|
||||
var serviceContainers = templateEvaluator.EvaluateJobServiceContainers(message.JobServiceContainers, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||
var serviceContainers = templateEvaluator.EvaluateJobServiceContainers(message.JobServiceContainers, jobContext.ExpressionValues);
|
||||
if (serviceContainers?.Count > 0)
|
||||
{
|
||||
foreach (var pair in serviceContainers)
|
||||
@@ -182,7 +170,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
context.JobDefaults["run"] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
var defaultsRun = defaults.First(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase));
|
||||
var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||
var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues);
|
||||
foreach (var pair in jobDefaults)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pair.Value))
|
||||
@@ -197,8 +185,8 @@ namespace GitHub.Runner.Worker
|
||||
// Download actions not already in the cache
|
||||
Trace.Info("Downloading actions");
|
||||
var actionManager = HostContext.GetService<IActionManager>();
|
||||
var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
|
||||
var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareSteps);
|
||||
|
||||
// Add start-container steps, record and stop-container steps
|
||||
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
|
||||
@@ -239,23 +227,9 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
actionRunner.TryEvaluateDisplayName(contextData, context);
|
||||
jobSteps.Add(actionRunner);
|
||||
|
||||
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
|
||||
{
|
||||
Trace.Info($"Adding pre-{action.DisplayName}.");
|
||||
preStep.TryEvaluateDisplayName(contextData, context);
|
||||
preStep.DisplayName = $"Pre {preStep.DisplayName}";
|
||||
preJobSteps.Add(preStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intraActionStates = new Dictionary<Guid, Dictionary<string, string>>();
|
||||
foreach (var preStep in prepareResult.PreStepTracker)
|
||||
{
|
||||
intraActionStates[preStep.Key] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// Create execution context for pre-job steps
|
||||
foreach (var step in preJobSteps)
|
||||
{
|
||||
@@ -266,12 +240,6 @@ namespace GitHub.Runner.Worker
|
||||
Guid stepId = Guid.NewGuid();
|
||||
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N"));
|
||||
}
|
||||
else if (step is IActionRunner actionStep)
|
||||
{
|
||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||
Guid stepId = Guid.NewGuid();
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(stepId, actionStep.DisplayName, stepId.ToString("N"), null, null, intraActionStates[actionStep.Action.Id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create execution context for job steps
|
||||
@@ -280,8 +248,7 @@ namespace GitHub.Runner.Worker
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||
intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState);
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionState);
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +337,7 @@ namespace GitHub.Runner.Worker
|
||||
context.ExpressionValues["steps"] = context.StepsContext.GetScope(context.ScopeName);
|
||||
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
var outputs = templateEvaluator.EvaluateJobOutput(message.JobOutputs, context.ExpressionValues, context.ExpressionFunctions);
|
||||
var outputs = templateEvaluator.EvaluateJobOutput(message.JobOutputs, context.ExpressionValues);
|
||||
foreach (var output in outputs)
|
||||
{
|
||||
if (string.IsNullOrEmpty(output.Value))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
@@ -8,13 +10,8 @@ using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -66,7 +63,11 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
var step = jobContext.JobSteps.Dequeue();
|
||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps.Peek() : null;
|
||||
IStep nextStep = null;
|
||||
if (jobContext.JobSteps.Count > 0)
|
||||
{
|
||||
nextStep = jobContext.JobSteps.Peek();
|
||||
}
|
||||
|
||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||
@@ -75,13 +76,6 @@ namespace GitHub.Runner.Worker
|
||||
// Start
|
||||
step.ExecutionContext.Start();
|
||||
|
||||
// Expression functions
|
||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<AlwaysFunction>(PipelineTemplateConstants.Always, 0, 0));
|
||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<CancelledFunction>(PipelineTemplateConstants.Cancelled, 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<HashFilesFunction>(PipelineTemplateConstants.HashFiles, 1, byte.MaxValue));
|
||||
|
||||
// Initialize scope
|
||||
if (InitializeScope(step, scopeInputs))
|
||||
{
|
||||
@@ -105,13 +99,14 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Evaluate and merge action's env block to env context
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var env in actionEnvironment)
|
||||
{
|
||||
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
var expressionManager = HostContext.GetService<IExpressionManager>();
|
||||
try
|
||||
{
|
||||
// Register job cancellation call back only if job cancellation token not been fire before each step run
|
||||
@@ -125,29 +120,28 @@ namespace GitHub.Runner.Worker
|
||||
jobContext.JobContext.Status = jobContext.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;
|
||||
ConditionResult conditionReTestResult;
|
||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||
{
|
||||
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
||||
conditionReTestResult = false;
|
||||
}
|
||||
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());
|
||||
conditionReTestResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition, hostTracingOnly: true);
|
||||
}
|
||||
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);
|
||||
conditionReTestResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!conditionReTestResult)
|
||||
if (!conditionReTestResult.Value)
|
||||
{
|
||||
// Cancel the step.
|
||||
Trace.Info("Cancel current running step.");
|
||||
@@ -167,35 +161,34 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// 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);
|
||||
Exception conditionEvaluateError = null;
|
||||
ConditionResult conditionResult;
|
||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||
{
|
||||
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
||||
conditionResult = false;
|
||||
}
|
||||
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());
|
||||
conditionResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Info("Caught exception from expression.");
|
||||
Trace.Error(ex);
|
||||
conditionResult = false;
|
||||
conditionEvaluateError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
// no evaluate error but condition is false
|
||||
if (!conditionResult && conditionEvaluateError == null)
|
||||
if (!conditionResult.Value && conditionEvaluateError == null)
|
||||
{
|
||||
// Condition == false
|
||||
Trace.Info("Skipping step due to condition evaluation.");
|
||||
CompleteStep(step, nextStep, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
|
||||
CompleteStep(step, nextStep, TaskResult.Skipped, resultCode: conditionResult.Trace);
|
||||
}
|
||||
else if (conditionEvaluateError != null)
|
||||
{
|
||||
@@ -255,7 +248,7 @@ namespace GitHub.Runner.Worker
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
try
|
||||
{
|
||||
timeoutMinutes = templateEvaluator.EvaluateStepTimeout(step.Timeout, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions);
|
||||
timeoutMinutes = templateEvaluator.EvaluateStepTimeout(step.Timeout, step.ExecutionContext.ExpressionValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -346,7 +339,7 @@ namespace GitHub.Runner.Worker
|
||||
var continueOnError = false;
|
||||
try
|
||||
{
|
||||
continueOnError = templateEvaluator.EvaluateStepContinueOnError(step.ContinueOnError, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions);
|
||||
continueOnError = templateEvaluator.EvaluateStepContinueOnError(step.ContinueOnError, step.ExecutionContext.ExpressionValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -399,7 +392,7 @@ namespace GitHub.Runner.Worker
|
||||
var inputs = default(DictionaryContextData);
|
||||
try
|
||||
{
|
||||
inputs = templateEvaluator.EvaluateStepScopeInputs(scope.Inputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
||||
inputs = templateEvaluator.EvaluateStepScopeInputs(scope.Inputs, executionContext.ExpressionValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -455,7 +448,7 @@ namespace GitHub.Runner.Worker
|
||||
var outputs = default(DictionaryContextData);
|
||||
try
|
||||
{
|
||||
outputs = templateEvaluator.EvaluateStepScopeOutputs(scope.Outputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
||||
outputs = templateEvaluator.EvaluateStepScopeOutputs(scope.Outputs, executionContext.ExpressionValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -483,43 +476,5 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
executionContext.Complete(result, resultCode: resultCode);
|
||||
}
|
||||
|
||||
private sealed class ConditionTraceWriter : ObjectTemplating::ITraceWriter
|
||||
{
|
||||
private readonly IExecutionContext _executionContext;
|
||||
private readonly Tracing _trace;
|
||||
private readonly StringBuilder _traceBuilder = new StringBuilder();
|
||||
|
||||
public string Trace => _traceBuilder.ToString();
|
||||
|
||||
public ConditionTraceWriter(Tracing trace, IExecutionContext executionContext)
|
||||
{
|
||||
ArgUtil.NotNull(trace, nameof(trace));
|
||||
_trace = trace;
|
||||
_executionContext = executionContext;
|
||||
}
|
||||
|
||||
public void Error(string format, params Object[] args)
|
||||
{
|
||||
var message = StringUtil.Format(format, args);
|
||||
_trace.Error(message);
|
||||
_executionContext?.Debug(message);
|
||||
}
|
||||
|
||||
public void Info(string format, params Object[] args)
|
||||
{
|
||||
var message = StringUtil.Format(format, args);
|
||||
_trace.Info(message);
|
||||
_executionContext?.Debug(message);
|
||||
_traceBuilder.AppendLine(message);
|
||||
}
|
||||
|
||||
public void Verbose(string format, params Object[] args)
|
||||
{
|
||||
var message = StringUtil.Format(format, args);
|
||||
_trace.Verbose(message);
|
||||
_executionContext?.Debug(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace GitHub.Runner.Worker
|
||||
// Validate args.
|
||||
ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn));
|
||||
ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut));
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy);
|
||||
var jobRunner = HostContext.CreateService<IJobRunner>();
|
||||
|
||||
using (var channel = HostContext.CreateService<IProcessChannel>())
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
"entrypoint": "non-empty-string",
|
||||
"args": "container-runs-args",
|
||||
"env": "container-runs-env",
|
||||
"pre-entrypoint": "non-empty-string",
|
||||
"pre-if": "non-empty-string",
|
||||
"post-entrypoint": "non-empty-string",
|
||||
"post-if": "non-empty-string"
|
||||
}
|
||||
@@ -69,8 +67,6 @@
|
||||
"properties": {
|
||||
"using": "non-empty-string",
|
||||
"main": "non-empty-string",
|
||||
"pre": "non-empty-string",
|
||||
"pre-if": "non-empty-string",
|
||||
"post": "non-empty-string",
|
||||
"post-if": "non-empty-string"
|
||||
}
|
||||
@@ -95,8 +91,7 @@
|
||||
"strategy",
|
||||
"matrix",
|
||||
"job",
|
||||
"runner",
|
||||
"hashFiles(1,255)"
|
||||
"runner"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ using GitHub.DistributedTask.Expressions2.Sdk.Functions;
|
||||
|
||||
namespace GitHub.DistributedTask.Expressions2
|
||||
{
|
||||
internal static class ExpressionConstants
|
||||
public static class ExpressionConstants
|
||||
{
|
||||
static ExpressionConstants()
|
||||
{
|
||||
@@ -16,6 +16,7 @@ namespace GitHub.DistributedTask.Expressions2
|
||||
AddFunction<StartsWith>("startsWith", 2, 2);
|
||||
AddFunction<ToJson>("toJson", 1, 1);
|
||||
AddFunction<FromJson>("fromJson", 1, 1);
|
||||
AddFunction<HashFiles>("hashFiles", 1, 1);
|
||||
}
|
||||
|
||||
private static void AddFunction<T>(String name, Int32 minParameters, Int32 maxParameters)
|
||||
@@ -24,6 +25,12 @@ namespace GitHub.DistributedTask.Expressions2
|
||||
WellKnownFunctions.Add(name, new FunctionInfo<T>(name, minParameters, maxParameters));
|
||||
}
|
||||
|
||||
public static void UpdateFunction<T>(String name, Int32 minParameters, Int32 maxParameters)
|
||||
where T : Function, new()
|
||||
{
|
||||
WellKnownFunctions[name] = new FunctionInfo<T>(name, minParameters, maxParameters);
|
||||
}
|
||||
|
||||
internal static readonly String False = "false";
|
||||
internal static readonly String Infinity = "Infinity";
|
||||
internal static readonly Int32 MaxDepth = 50;
|
||||
|
||||
122
src/Sdk/DTExpressions2/Expressions2/Sdk/Functions/HashFiles.cs
Normal file
122
src/Sdk/DTExpressions2/Expressions2/Sdk/Functions/HashFiles.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Minimatch;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
namespace GitHub.DistributedTask.Expressions2.Sdk.Functions
|
||||
{
|
||||
internal sealed class HashFiles : Function
|
||||
{
|
||||
protected sealed override Object EvaluateCore(
|
||||
EvaluationContext context,
|
||||
out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
|
||||
// hashFiles() only works on the runner and only works with files under GITHUB_WORKSPACE
|
||||
// Since GITHUB_WORKSPACE is set by runner, I am using that as the fact of this code runs on server or runner.
|
||||
if (context.State is ObjectTemplating.TemplateContext templateContext &&
|
||||
templateContext.ExpressionValues.TryGetValue(PipelineTemplateConstants.GitHub, out var githubContextData) &&
|
||||
githubContextData is DictionaryContextData githubContext &&
|
||||
githubContext.TryGetValue(PipelineTemplateConstants.Workspace, out var workspace) == true &&
|
||||
workspace is StringContextData workspaceData)
|
||||
{
|
||||
string searchRoot = workspaceData.Value;
|
||||
string pattern = Parameters[0].Evaluate(context).ConvertToString();
|
||||
|
||||
// Convert slashes on Windows
|
||||
if (s_isWindows)
|
||||
{
|
||||
pattern = pattern.Replace('\\', '/');
|
||||
}
|
||||
|
||||
// Root the pattern
|
||||
if (!Path.IsPathRooted(pattern))
|
||||
{
|
||||
var patternRoot = s_isWindows ? searchRoot.Replace('\\', '/').TrimEnd('/') : searchRoot.TrimEnd('/');
|
||||
pattern = string.Concat(patternRoot, "/", pattern);
|
||||
}
|
||||
|
||||
// Get all files
|
||||
context.Trace.Info($"Search root directory: '{searchRoot}'");
|
||||
context.Trace.Info($"Search pattern: '{pattern}'");
|
||||
var files = Directory.GetFiles(searchRoot, "*", SearchOption.AllDirectories)
|
||||
.Select(x => s_isWindows ? x.Replace('\\', '/') : x)
|
||||
.OrderBy(x => x, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
if (files.Count == 0)
|
||||
{
|
||||
throw new ArgumentException($"hashFiles('{ExpressionUtility.StringEscape(pattern)}') failed. Directory '{searchRoot}' is empty");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Trace.Info($"Found {files.Count} files");
|
||||
}
|
||||
|
||||
// Match
|
||||
var matcher = new Minimatcher(pattern, s_minimatchOptions);
|
||||
files = matcher.Filter(files)
|
||||
.Select(x => s_isWindows ? x.Replace('/', '\\') : x)
|
||||
.ToList();
|
||||
if (files.Count == 0)
|
||||
{
|
||||
throw new ArgumentException($"hashFiles('{ExpressionUtility.StringEscape(pattern)}') failed. Search pattern '{pattern}' doesn't match any file under '{searchRoot}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Trace.Info($"{files.Count} matches to hash");
|
||||
}
|
||||
|
||||
// Hash each file
|
||||
List<byte> filesSha256 = new List<byte>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
context.Trace.Info($"Hash {file}");
|
||||
using (SHA256 sha256hash = SHA256.Create())
|
||||
{
|
||||
using (var fileStream = File.OpenRead(file))
|
||||
{
|
||||
filesSha256.AddRange(sha256hash.ComputeHash(fileStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hash the hashes
|
||||
using (SHA256 sha256hash = SHA256.Create())
|
||||
{
|
||||
var hashBytes = sha256hash.ComputeHash(filesSha256.ToArray());
|
||||
StringBuilder hashString = new StringBuilder();
|
||||
for (int i = 0; i < hashBytes.Length; i++)
|
||||
{
|
||||
hashString.Append(hashBytes[i].ToString("x2"));
|
||||
}
|
||||
var result = hashString.ToString();
|
||||
context.Trace.Info($"Final hash result: '{result}'");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("'hashfiles' expression function is only supported under runner context.");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly bool s_isWindows = Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX;
|
||||
|
||||
// Only support basic globbing (* ? and []) and globstar (**)
|
||||
private static readonly Options s_minimatchOptions = new Options
|
||||
{
|
||||
Dot = true,
|
||||
NoBrace = true,
|
||||
NoCase = s_isWindows,
|
||||
NoComment = true,
|
||||
NoExt = true,
|
||||
NoNegate = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content);
|
||||
@@ -109,7 +109,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
@@ -164,7 +164,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
@@ -227,7 +227,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
@@ -257,7 +257,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content);
|
||||
@@ -287,7 +287,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 2),
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
|
||||
@@ -23,27 +22,10 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
{
|
||||
var context = definition[i].Value.AssertSequence($"{TemplateConstants.Context}");
|
||||
definition.RemoveAt(i);
|
||||
var readerContext = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
|
||||
var evaluatorContext = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (TemplateToken item in context)
|
||||
{
|
||||
var itemStr = item.AssertString($"{TemplateConstants.Context} item").Value;
|
||||
readerContext.Add(itemStr);
|
||||
|
||||
// Remove min/max parameter info
|
||||
var paramIndex = itemStr.IndexOf('(');
|
||||
if (paramIndex > 0)
|
||||
{
|
||||
evaluatorContext.Add(String.Concat(itemStr.Substring(0, paramIndex + 1), ")"));
|
||||
}
|
||||
else
|
||||
{
|
||||
evaluatorContext.Add(itemStr);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderContext = readerContext.ToArray();
|
||||
EvaluatorContext = evaluatorContext.ToArray();
|
||||
Context = context
|
||||
.Select(x => x.AssertString($"{TemplateConstants.Context} item").Value)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
}
|
||||
else if (String.Equals(definitionKey.Value, TemplateConstants.Description, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -58,17 +40,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
|
||||
internal abstract DefinitionType DefinitionType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used by the template reader to determine allowed expression values and functions.
|
||||
/// Also used by the template reader to validate function min/max parameters.
|
||||
/// </summary>
|
||||
internal String[] ReaderContext { get; private set; } = new String[0];
|
||||
|
||||
/// <summary>
|
||||
/// Used by the template evaluator to determine allowed expression values and functions.
|
||||
/// The min/max parameter info is omitted.
|
||||
/// </summary>
|
||||
internal String[] EvaluatorContext { get; private set; } = new String[0];
|
||||
internal String[] Context { get; private set; } = new String[0];
|
||||
|
||||
internal abstract void Validate(
|
||||
TemplateSchema schema,
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
{
|
||||
var inherited = schema.GetDefinition(Inherits);
|
||||
|
||||
if (inherited.ReaderContext.Length > 0)
|
||||
if (inherited.Context.Length > 0)
|
||||
{
|
||||
throw new NotSupportedException($"Property '{TemplateConstants.Context}' is not supported on inhertied definitions");
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
{
|
||||
var nestedDefinition = schema.GetDefinition(nestedType);
|
||||
|
||||
if (nestedDefinition.ReaderContext.Length > 0)
|
||||
if (nestedDefinition.Context.Length > 0)
|
||||
{
|
||||
throw new ArgumentException($"'{name}' is a one-of definition and references another definition that defines context. This is currently not supported.");
|
||||
}
|
||||
|
||||
@@ -47,16 +47,7 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
var evaluator = new TemplateEvaluator(context, template, removeBytes);
|
||||
try
|
||||
{
|
||||
var availableContext = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var key in context.ExpressionValues.Keys)
|
||||
{
|
||||
availableContext.Add(key);
|
||||
}
|
||||
foreach (var function in context.ExpressionFunctions)
|
||||
{
|
||||
availableContext.Add($"{function.Name}()");
|
||||
}
|
||||
|
||||
var availableContext = new HashSet<String>(context.ExpressionValues.Keys.Concat(context.ExpressionFunctions.Select(x => $"{x.Name}({x.MinParameters},{x.MaxParameters})")));
|
||||
var definitionInfo = new DefinitionInfo(context.Schema, type, availableContext);
|
||||
result = evaluator.Evaluate(definitionInfo);
|
||||
|
||||
@@ -402,13 +393,14 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
m_allowedContext = Definition.EvaluatorContext;
|
||||
if (Definition.EvaluatorContext.Length > 0)
|
||||
if (Definition.Context.Length > 0)
|
||||
{
|
||||
m_allowedContext = Definition.Context;
|
||||
Expand = m_availableContext.IsSupersetOf(m_allowedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_allowedContext = new String[0];
|
||||
Expand = false;
|
||||
}
|
||||
}
|
||||
@@ -424,9 +416,9 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
if (Definition.EvaluatorContext.Length > 0)
|
||||
if (Definition.Context.Length > 0)
|
||||
{
|
||||
m_allowedContext = new HashSet<String>(parent.m_allowedContext.Concat(Definition.EvaluatorContext), StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
m_allowedContext = new HashSet<String>(parent.m_allowedContext.Concat(Definition.Context)).ToArray();
|
||||
Expand = m_availableContext.IsSupersetOf(m_allowedContext);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -49,14 +49,6 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
m_errors = new List<TemplateValidationError>(errors ?? Enumerable.Empty<TemplateValidationError>());
|
||||
}
|
||||
|
||||
public TemplateValidationException(
|
||||
String message,
|
||||
IEnumerable<TemplateValidationError> errors)
|
||||
: this(message)
|
||||
{
|
||||
m_errors = new List<TemplateValidationError>(errors ?? Enumerable.Empty<TemplateValidationError>());
|
||||
}
|
||||
|
||||
public TemplateValidationException(String message)
|
||||
: base(message)
|
||||
{
|
||||
|
||||
@@ -780,8 +780,15 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
// Lookup the definition
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Record allowed context
|
||||
AllowedContext = Definition.ReaderContext;
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
{
|
||||
AllowedContext = Definition.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowedContext = new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
public DefinitionInfo(
|
||||
@@ -793,10 +800,10 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
// Lookup the definition
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Record allowed context
|
||||
if (Definition.ReaderContext.Length > 0)
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
{
|
||||
AllowedContext = new HashSet<String>(parent.AllowedContext.Concat(Definition.ReaderContext), StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
AllowedContext = new HashSet<String>(parent.AllowedContext.Concat(Definition.Context)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.ObjectTemplating
|
||||
@@ -42,7 +41,7 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
String message = !String.IsNullOrEmpty(messagePrefix) ? $"{messagePrefix} {ex.Message}" : ex.ToString();
|
||||
String message = !String.IsNullOrEmpty(messagePrefix) ? $"{messagePrefix} {ex.Message}" : ex.Message;
|
||||
Add(new TemplateValidationError(message));
|
||||
if (ex.InnerException == null)
|
||||
{
|
||||
@@ -89,23 +88,6 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws <c ref="TemplateValidationException" /> if any errors.
|
||||
/// <param name="prefix">The error message prefix</param>
|
||||
/// </summary>
|
||||
public void Check(String prefix)
|
||||
{
|
||||
if (String.IsNullOrEmpty(prefix))
|
||||
{
|
||||
this.Check();
|
||||
}
|
||||
else if (m_errors.Count > 0)
|
||||
{
|
||||
var message = $"{prefix.Trim()} {String.Join(",", m_errors.Select(e => e.Message))}";
|
||||
throw new TemplateValidationException(message, m_errors);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_errors.Clear();
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.Services.WebApi.Internal;
|
||||
@@ -37,29 +35,11 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
String[] allowedContext,
|
||||
out Exception ex)
|
||||
{
|
||||
// Create dummy named values and functions
|
||||
var namedValues = new List<INamedValueInfo>();
|
||||
var functions = new List<IFunctionInfo>();
|
||||
// Create dummy allowed contexts
|
||||
INamedValueInfo[] namedValues = null;
|
||||
if (allowedContext?.Length > 0)
|
||||
{
|
||||
foreach (var contextItem in allowedContext)
|
||||
{
|
||||
var match = s_function.Match(contextItem);
|
||||
if (match.Success)
|
||||
{
|
||||
var functionName = match.Groups[1].Value;
|
||||
var minParameters = Int32.Parse(match.Groups[2].Value, NumberStyles.None, CultureInfo.InvariantCulture);
|
||||
var maxParametersRaw = match.Groups[3].Value;
|
||||
var maxParameters = String.Equals(maxParametersRaw, TemplateConstants.MaxConstant, StringComparison.Ordinal)
|
||||
? Int32.MaxValue
|
||||
: Int32.Parse(maxParametersRaw, NumberStyles.None, CultureInfo.InvariantCulture);
|
||||
functions.Add(new FunctionInfo<DummyFunction>(functionName, minParameters, maxParameters));
|
||||
}
|
||||
else
|
||||
{
|
||||
namedValues.Add(new NamedValueInfo<ContextValueNode>(contextItem));
|
||||
}
|
||||
}
|
||||
namedValues = allowedContext.Select(x => new NamedValueInfo<ContextValueNode>(x)).ToArray();
|
||||
}
|
||||
|
||||
// Parse
|
||||
@@ -67,7 +47,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
ExpressionNode root = null;
|
||||
try
|
||||
{
|
||||
root = new ExpressionParser().CreateTree(expression, null, namedValues, functions) as ExpressionNode;
|
||||
root = new ExpressionParser().CreateTree(expression, null, namedValues, null) as ExpressionNode;
|
||||
|
||||
result = true;
|
||||
ex = null;
|
||||
@@ -80,18 +60,5 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private sealed class DummyFunction : Function
|
||||
{
|
||||
protected override Object EvaluateCore(
|
||||
EvaluationContext context,
|
||||
out ResultMemory resultMemory)
|
||||
{
|
||||
resultMemory = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Regex s_function = new Regex(@"^([a-zA-Z0-9_]+)\(([0-9]+),([0-9]+|MAX)\)$", RegexOptions.Compiled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
|
||||
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
{
|
||||
@@ -109,43 +106,6 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
throw new ArgumentException($"Error while reading '{objectDescription}'. Unexpected value '{literal.ToString()}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverses the token and checks whether all required expression values
|
||||
/// and functions are provided.
|
||||
/// </summary>
|
||||
public static bool CheckHasRequiredContext(
|
||||
this TemplateToken token,
|
||||
IReadOnlyObject expressionValues,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var expressionTokens = token.Traverse()
|
||||
.OfType<BasicExpressionToken>()
|
||||
.ToArray();
|
||||
var parser = new ExpressionParser();
|
||||
foreach (var expressionToken in expressionTokens)
|
||||
{
|
||||
var tree = parser.ValidateSyntax(expressionToken.Expression, null);
|
||||
foreach (var node in tree.Traverse())
|
||||
{
|
||||
if (node is NamedValue namedValue)
|
||||
{
|
||||
if (expressionValues?.Keys.Any(x => string.Equals(x, namedValue.Name, StringComparison.OrdinalIgnoreCase)) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (node is Function function &&
|
||||
!ExpressionConstants.WellKnownFunctions.ContainsKey(function.Name) &&
|
||||
expressionFunctions?.Any(x => string.Equals(x.Name, function.Name, StringComparison.OrdinalIgnoreCase)) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all tokens (depth first)
|
||||
/// </summary>
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
{
|
||||
public const String Always = "always";
|
||||
public const String BooleanStepsContext = "boolean-steps-context";
|
||||
public const String BooleanStrategyContext = "boolean-strategy-context";
|
||||
public const String CancelTimeoutMinutes = "cancel-timeout-minutes";
|
||||
public const String Cancelled = "cancelled";
|
||||
public const String Checkout = "checkout";
|
||||
@@ -25,7 +24,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String FetchDepth = "fetch-depth";
|
||||
public const String GeneratedId = "generated-id";
|
||||
public const String GitHub = "github";
|
||||
public const String HashFiles = "hashFiles";
|
||||
public const String Id = "id";
|
||||
public const String If = "if";
|
||||
public const String Image = "image";
|
||||
@@ -33,7 +31,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Inputs = "inputs";
|
||||
public const String Job = "job";
|
||||
public const String JobDefaultsRun = "job-defaults-run";
|
||||
public const String JobIfResult = "job-if-result";
|
||||
public const String JobOutputs = "job-outputs";
|
||||
public const String Jobs = "jobs";
|
||||
public const String Labels = "labels";
|
||||
@@ -63,7 +60,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Shell = "shell";
|
||||
public const String Skipped = "skipped";
|
||||
public const String StepEnv = "step-env";
|
||||
public const String StepIfResult = "step-if-result";
|
||||
public const String Steps = "steps";
|
||||
public const String StepsScopeInputs = "steps-scope-inputs";
|
||||
public const String StepsScopeOutputs = "steps-scope-outputs";
|
||||
|
||||
@@ -16,20 +16,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
{
|
||||
internal static class PipelineTemplateConverter
|
||||
{
|
||||
internal static Boolean ConvertToIfResult(
|
||||
TemplateContext context,
|
||||
TemplateToken ifResult)
|
||||
{
|
||||
var expression = ifResult.Traverse().FirstOrDefault(x => x is ExpressionToken);
|
||||
if (expression != null)
|
||||
{
|
||||
throw new ArgumentException($"Unexpected type '{expression.GetType().Name}' encountered while reading 'if'.");
|
||||
}
|
||||
|
||||
var evaluationResult = EvaluationResult.CreateIntermediateResult(null, ifResult);
|
||||
return evaluationResult.IsTruthy;
|
||||
}
|
||||
|
||||
internal static Boolean? ConvertToStepContinueOnError(
|
||||
TemplateContext context,
|
||||
TemplateToken token,
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk.Functions;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Schema;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
@@ -14,9 +14,6 @@ using ITraceWriter = GitHub.DistributedTask.ObjectTemplating.ITraceWriter;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates parts of the workflow DOM. For example, a job strategy or step inputs.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class PipelineTemplateEvaluator
|
||||
{
|
||||
@@ -53,14 +50,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public DictionaryContextData EvaluateStepScopeInputs(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(DictionaryContextData);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeInputs, token, 0, null, omitHeader: true);
|
||||
@@ -80,14 +76,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public DictionaryContextData EvaluateStepScopeOutputs(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(DictionaryContextData);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeOutputs, token, 0, null, omitHeader: true);
|
||||
@@ -107,14 +102,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Boolean EvaluateStepContinueOnError(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(Boolean?);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.BooleanStepsContext, token, 0, null, omitHeader: true);
|
||||
@@ -132,44 +126,16 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
public String EvaluateStepDisplayName(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(String);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StringStepsContext, token, 0, null, omitHeader: true);
|
||||
context.Errors.Check();
|
||||
result = PipelineTemplateConverter.ConvertToStepDisplayName(context, token);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||
{
|
||||
context.Errors.Add(ex);
|
||||
}
|
||||
|
||||
context.Errors.Check();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Dictionary<String, String> EvaluateStepEnvironment(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions,
|
||||
StringComparer keyComparer)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepEnv, token, 0, null, omitHeader: true);
|
||||
@@ -187,44 +153,15 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result ?? new Dictionary<String, String>(keyComparer);
|
||||
}
|
||||
|
||||
public Boolean EvaluateStepIf(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions,
|
||||
IEnumerable<KeyValuePair<String, Object>> expressionState)
|
||||
{
|
||||
var result = default(Boolean?);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions, expressionState);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepIfResult, token, 0, null, omitHeader: true);
|
||||
context.Errors.Check();
|
||||
result = PipelineTemplateConverter.ConvertToIfResult(context, token);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||
{
|
||||
context.Errors.Add(ex);
|
||||
}
|
||||
|
||||
context.Errors.Check();
|
||||
}
|
||||
|
||||
return result ?? throw new InvalidOperationException("Step if cannot be null");
|
||||
}
|
||||
|
||||
public Dictionary<String, String> EvaluateStepInputs(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepWith, token, 0, null, omitHeader: true);
|
||||
@@ -244,14 +181,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Int32 EvaluateStepTimeout(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(Int32?);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.NumberStepsContext, token, 0, null, omitHeader: true);
|
||||
@@ -271,14 +207,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public JobContainer EvaluateJobContainer(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(JobContainer);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Container, token, 0, null, omitHeader: true);
|
||||
@@ -298,14 +233,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Dictionary<String, String> EvaluateJobOutput(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.JobOutputs, token, 0, null, omitHeader: true);
|
||||
@@ -335,14 +269,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Dictionary<String, String> EvaluateJobDefaultsRun(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.JobDefaultsRun, token, 0, null, omitHeader: true);
|
||||
@@ -372,14 +305,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public IList<KeyValuePair<String, JobContainer>> EvaluateJobServiceContainers(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
DictionaryContextData contextData)
|
||||
{
|
||||
var result = default(List<KeyValuePair<String, JobContainer>>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
var context = CreateContext(contextData);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Services, token, 0, null, omitHeader: true);
|
||||
@@ -397,10 +329,62 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result;
|
||||
}
|
||||
|
||||
private TemplateContext CreateContext(
|
||||
public Boolean TryEvaluateStepDisplayName(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions,
|
||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
|
||||
out String stepName)
|
||||
{
|
||||
stepName = default(String);
|
||||
var context = CreateContext(contextData);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
// We should only evaluate basic expressions if we are sure we have context on all the Named Values and functions
|
||||
// Otherwise return and use a default name
|
||||
if (token is BasicExpressionToken expressionToken)
|
||||
{
|
||||
ExpressionNode root = null;
|
||||
try
|
||||
{
|
||||
root = new ExpressionParser().ValidateSyntax(expressionToken.Expression, null) as ExpressionNode;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
context.Errors.Add(exception);
|
||||
context.Errors.Check();
|
||||
}
|
||||
foreach (var node in root.Traverse())
|
||||
{
|
||||
if (node is NamedValue namedValue && !contextData.ContainsKey(namedValue.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (node is Function function &&
|
||||
!context.ExpressionFunctions.Any(item => String.Equals(item.Name, function.Name)) &&
|
||||
!ExpressionConstants.WellKnownFunctions.ContainsKey(function.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StringStepsContext, token, 0, null, omitHeader: true);
|
||||
context.Errors.Check();
|
||||
stepName = PipelineTemplateConverter.ConvertToStepDisplayName(context, token);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||
{
|
||||
context.Errors.Add(ex);
|
||||
}
|
||||
|
||||
context.Errors.Check();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private TemplateContext CreateContext(DictionaryContextData contextData)
|
||||
{
|
||||
var result = new TemplateContext
|
||||
{
|
||||
@@ -423,7 +407,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
}
|
||||
|
||||
// Add named values
|
||||
// Add named context
|
||||
if (contextData != null)
|
||||
{
|
||||
foreach (var pair in contextData)
|
||||
@@ -432,46 +416,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
}
|
||||
|
||||
// Add functions
|
||||
var functionNames = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
|
||||
if (expressionFunctions?.Count > 0)
|
||||
{
|
||||
foreach (var function in expressionFunctions)
|
||||
{
|
||||
result.ExpressionFunctions.Add(function);
|
||||
functionNames.Add(function.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing expression values and expression functions.
|
||||
// This solves the following problems:
|
||||
// - Compat for new agent against old server (new contexts not sent down in job message)
|
||||
// - Evaluating early when all referenced contexts are available, even though all allowed
|
||||
// contexts may not yet be available. For example, evaluating step display name can often
|
||||
// be performed early.
|
||||
foreach (var name in s_expressionValueNames)
|
||||
// Compat for new agent against old server
|
||||
foreach (var name in s_contextNames)
|
||||
{
|
||||
if (!result.ExpressionValues.ContainsKey(name))
|
||||
{
|
||||
result.ExpressionValues[name] = null;
|
||||
}
|
||||
}
|
||||
foreach (var name in s_expressionFunctionNames)
|
||||
{
|
||||
if (!functionNames.Contains(name))
|
||||
{
|
||||
result.ExpressionFunctions.Add(new FunctionInfo<NoOperation>(name, 0, Int32.MaxValue));
|
||||
}
|
||||
}
|
||||
|
||||
// Add state
|
||||
if (expressionState != null)
|
||||
{
|
||||
foreach (var pair in expressionState)
|
||||
{
|
||||
result.State[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -479,10 +431,9 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
private readonly ITraceWriter m_trace;
|
||||
private readonly TemplateSchema m_schema;
|
||||
private readonly IList<String> m_fileTable;
|
||||
private readonly String[] s_expressionValueNames = new[]
|
||||
private readonly String[] s_contextNames = new[]
|
||||
{
|
||||
PipelineTemplateConstants.GitHub,
|
||||
PipelineTemplateConstants.Needs,
|
||||
PipelineTemplateConstants.Strategy,
|
||||
PipelineTemplateConstants.Matrix,
|
||||
PipelineTemplateConstants.Needs,
|
||||
@@ -493,13 +444,5 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
PipelineTemplateConstants.Runner,
|
||||
PipelineTemplateConstants.Env,
|
||||
};
|
||||
private readonly String[] s_expressionFunctionNames = new[]
|
||||
{
|
||||
PipelineTemplateConstants.Always,
|
||||
PipelineTemplateConstants.Cancelled,
|
||||
PipelineTemplateConstants.Failure,
|
||||
PipelineTemplateConstants.HashFiles,
|
||||
PipelineTemplateConstants.Success,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,25 @@
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Schema;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class PipelineTemplateSchemaFactory
|
||||
public sealed class PipelineTemplateSchemaFactory
|
||||
{
|
||||
public static TemplateSchema GetSchema()
|
||||
public TemplateSchema CreateSchema()
|
||||
{
|
||||
if (s_schema == null)
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var json = default(String);
|
||||
using (var stream = assembly.GetManifestResourceStream("GitHub.DistributedTask.Pipelines.ObjectTemplating.workflow-v1.0.json"))
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var json = default(String);
|
||||
using (var stream = assembly.GetManifestResourceStream("GitHub.DistributedTask.Pipelines.ObjectTemplating.workflow-v1.0.json"))
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
json = streamReader.ReadToEnd();
|
||||
}
|
||||
|
||||
var objectReader = new JsonObjectReader(null, json);
|
||||
var schema = TemplateSchema.Load(objectReader);
|
||||
Interlocked.CompareExchange(ref s_schema, schema, null);
|
||||
json = streamReader.ReadToEnd();
|
||||
}
|
||||
|
||||
return s_schema;
|
||||
var objectReader = new JsonObjectReader(null, json);
|
||||
return TemplateSchema.Load(objectReader);
|
||||
}
|
||||
|
||||
private static TemplateSchema s_schema;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -12,7 +12,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
/// <summary>
|
||||
/// Converts a YAML file into a TemplateToken
|
||||
/// </summary>
|
||||
internal sealed class YamlObjectReader : IObjectReader
|
||||
public sealed class YamlObjectReader : IObjectReader
|
||||
{
|
||||
internal YamlObjectReader(
|
||||
Int32? fileId,
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
"steps-scope-input-value": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"needs",
|
||||
"matrix",
|
||||
"secrets",
|
||||
"steps",
|
||||
@@ -66,9 +66,9 @@
|
||||
"steps-scope-output-value": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
@@ -91,9 +91,9 @@
|
||||
"description": "Default input values for a steps template",
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"one-of": [
|
||||
"string",
|
||||
@@ -114,9 +114,9 @@
|
||||
"description": "Output values for a steps template",
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -204,25 +204,6 @@
|
||||
"string": {}
|
||||
},
|
||||
|
||||
"job-if-result": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"always(0,0)",
|
||||
"failure(0,MAX)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,MAX)"
|
||||
],
|
||||
"one-of": [
|
||||
"null",
|
||||
"boolean",
|
||||
"number",
|
||||
"string",
|
||||
"sequence",
|
||||
"mapping"
|
||||
]
|
||||
},
|
||||
|
||||
"strategy": {
|
||||
"context": [
|
||||
"github",
|
||||
@@ -291,9 +272,9 @@
|
||||
"runs-on": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"one-of": [
|
||||
"non-empty-string",
|
||||
@@ -316,10 +297,10 @@
|
||||
"job-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"secrets",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"secrets"
|
||||
"needs"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -463,9 +444,9 @@
|
||||
"step-if": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
@@ -473,8 +454,7 @@
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
"success(0,0)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -482,9 +462,9 @@
|
||||
"step-if-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
@@ -493,63 +473,11 @@
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
"success(0,0)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
|
||||
"step-if-result": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"one-of": [
|
||||
"null",
|
||||
"boolean",
|
||||
"number",
|
||||
"string",
|
||||
"sequence",
|
||||
"mapping"
|
||||
]
|
||||
},
|
||||
|
||||
"step-if-result-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"one-of": [
|
||||
"null",
|
||||
"boolean",
|
||||
"number",
|
||||
"string",
|
||||
"sequence",
|
||||
"mapping"
|
||||
]
|
||||
},
|
||||
|
||||
"steps-template-reference": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
@@ -573,9 +501,9 @@
|
||||
"steps-template-reference-inputs": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -591,9 +519,9 @@
|
||||
"steps-template-reference-inputs-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
@@ -610,15 +538,14 @@
|
||||
"step-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -629,16 +556,15 @@
|
||||
"step-env-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -649,35 +575,14 @@
|
||||
"step-with": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"step-with-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -688,9 +593,9 @@
|
||||
"container": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"one-of": [
|
||||
"string",
|
||||
@@ -713,9 +618,9 @@
|
||||
"services": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -726,9 +631,9 @@
|
||||
"services-container": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"one-of": [
|
||||
"non-empty-string",
|
||||
@@ -739,10 +644,29 @@
|
||||
"container-env": {
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "string-runner-context"
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"step-with-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"non-empty-string": {
|
||||
"string": {
|
||||
"require-non-empty": true
|
||||
@@ -758,9 +682,9 @@
|
||||
"boolean-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -768,9 +692,9 @@
|
||||
"number-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -778,9 +702,9 @@
|
||||
"string-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix"
|
||||
"matrix",
|
||||
"needs"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -788,15 +712,14 @@
|
||||
"boolean-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -804,16 +727,15 @@
|
||||
"boolean-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -821,15 +743,14 @@
|
||||
"number-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -837,16 +758,15 @@
|
||||
"number-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -854,9 +774,9 @@
|
||||
"string-runner-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -869,15 +789,14 @@
|
||||
"string-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -885,16 +804,15 @@
|
||||
"string-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
"env"
|
||||
],
|
||||
"string": {}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public class AgentLabel
|
||||
{
|
||||
[JsonConstructor]
|
||||
public AgentLabel()
|
||||
{
|
||||
}
|
||||
|
||||
public AgentLabel(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Type = LabelType.System;
|
||||
}
|
||||
|
||||
public AgentLabel(string name, LabelType type)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
private AgentLabel(AgentLabel labelToBeCloned)
|
||||
{
|
||||
this.Id = labelToBeCloned.Id;
|
||||
this.Name = labelToBeCloned.Name;
|
||||
this.Type = labelToBeCloned.Type;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public int Id
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public LabelType Type
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public AgentLabel Clone()
|
||||
{
|
||||
return new AgentLabel(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public enum LabelType
|
||||
{
|
||||
[EnumMember]
|
||||
System = 0,
|
||||
|
||||
[EnumMember]
|
||||
User = 1
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
|
||||
if (agentToBeCloned.m_labels != null && agentToBeCloned.m_labels.Count > 0)
|
||||
{
|
||||
m_labels = new HashSet<AgentLabel>(agentToBeCloned.m_labels);
|
||||
m_labels = new HashSet<string>(agentToBeCloned.m_labels, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ namespace GitHub.DistributedTask.WebApi
|
||||
/// <summary>
|
||||
/// The labels of the runner
|
||||
/// </summary>
|
||||
public ISet<AgentLabel> Labels
|
||||
public ISet<string> Labels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_labels == null)
|
||||
{
|
||||
m_labels = new HashSet<AgentLabel>();
|
||||
m_labels = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
return m_labels;
|
||||
}
|
||||
@@ -164,6 +164,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
private PropertiesCollection m_properties;
|
||||
|
||||
[DataMember(IsRequired = false, EmitDefaultValue = false, Name = "Labels")]
|
||||
private HashSet<AgentLabel> m_labels;
|
||||
private HashSet<string> m_labels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
Int64 requestId,
|
||||
Guid lockToken,
|
||||
DateTime? expiresOn = null,
|
||||
string orchestrationId = null,
|
||||
Object userState = null,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
@@ -105,30 +104,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
LockedUntil = expiresOn,
|
||||
};
|
||||
|
||||
var additionalHeaders = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrEmpty(orchestrationId))
|
||||
{
|
||||
additionalHeaders["X-VSS-OrchestrationId"] = orchestrationId;
|
||||
}
|
||||
|
||||
HttpMethod httpMethod = new HttpMethod("PATCH");
|
||||
Guid locationId = new Guid("fc825784-c92a-4299-9221-998a02d1b54f");
|
||||
object routeValues = new { poolId = poolId, requestId = requestId };
|
||||
HttpContent content = new ObjectContent<TaskAgentJobRequest>(request, new VssJsonMediaTypeFormatter(true));
|
||||
|
||||
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
|
||||
queryParams.Add("lockToken", lockToken.ToString());
|
||||
|
||||
return SendAsync<TaskAgentJobRequest>(
|
||||
httpMethod,
|
||||
additionalHeaders,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(5.1, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content);
|
||||
return UpdateAgentRequestAsync(poolId, requestId, lockToken, request, userState, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TaskAgent> ReplaceAgentAsync(
|
||||
@@ -195,5 +171,5 @@ namespace GitHub.DistributedTask.WebApi
|
||||
}
|
||||
|
||||
private readonly ApiResourceVersion m_currentApiVersion = new ApiResourceVersion(3.0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace GitHub.Services.WebApi.Jwt
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static IEnumerable<Claim> ExtractClaims(this JsonWebToken token)
|
||||
internal static IEnumerable<Claim> ExtractClaims(this JsonWebToken token)
|
||||
{
|
||||
ArgumentUtility.CheckForNull(token, nameof(token));
|
||||
|
||||
|
||||
@@ -317,8 +317,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
Environment.MachineName, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
true, // unattended
|
||||
false)) // isOptional
|
||||
true)) // unattended
|
||||
.Returns("some runner");
|
||||
|
||||
// Act.
|
||||
@@ -345,8 +344,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
Environment.MachineName, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some runner");
|
||||
|
||||
// Act.
|
||||
@@ -373,8 +371,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
"some default auth", // defaultValue
|
||||
Validators.AuthSchemeValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some auth");
|
||||
|
||||
// Act.
|
||||
@@ -401,8 +398,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
true, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some token");
|
||||
|
||||
// Act.
|
||||
@@ -479,8 +475,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
true, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some token");
|
||||
|
||||
// Act.
|
||||
@@ -507,8 +502,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
true, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some token");
|
||||
|
||||
// Act.
|
||||
@@ -535,8 +529,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.ServerUrlValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some url");
|
||||
|
||||
// Act.
|
||||
@@ -563,8 +556,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
"some default account", // defaultValue
|
||||
Validators.NTAccountValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some windows logon account");
|
||||
|
||||
// Act.
|
||||
@@ -592,8 +584,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
true, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some windows logon password");
|
||||
|
||||
// Act.
|
||||
@@ -620,8 +611,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
"_work", // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some work");
|
||||
|
||||
// Act.
|
||||
@@ -650,8 +640,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.ServerUrlValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some url");
|
||||
|
||||
// Act.
|
||||
@@ -680,8 +669,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
false, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.ServerUrlValidator, // validator
|
||||
false, // unattended
|
||||
false)) // isOptional
|
||||
false)) // unattended
|
||||
.Returns("some url");
|
||||
|
||||
// Act.
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
|
||||
private Mock<IRSAKeyManager> _rsaKeyManager;
|
||||
private string _expectedToken = "expectedToken";
|
||||
private string _expectedServerUrl = "https://codedev.ms";
|
||||
private string _expectedServerUrl = "https://localhost";
|
||||
private string _expectedAgentName = "expectedAgentName";
|
||||
private string _expectedPoolName = "poolName";
|
||||
private string _expectedAuthType = "pat";
|
||||
@@ -145,8 +145,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
IConfigurationManager configManager = new ConfigurationManager();
|
||||
configManager.Initialize(tc);
|
||||
|
||||
var userLabels = "userlabel1,userlabel2";
|
||||
|
||||
trace.Info("Preparing command line arguments");
|
||||
var command = new CommandSettings(
|
||||
tc,
|
||||
@@ -158,8 +156,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
"--pool", _expectedPoolName,
|
||||
"--work", _expectedWorkFolder,
|
||||
"--auth", _expectedAuthType,
|
||||
"--token", _expectedToken,
|
||||
"--labels", userLabels
|
||||
"--token", _expectedToken
|
||||
});
|
||||
trace.Info("Constructed.");
|
||||
_store.Setup(x => x.IsConfigured()).Returns(false);
|
||||
@@ -181,10 +178,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
// 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));
|
||||
|
||||
var expectedLabels = new List<string>() { "self-hosted", VarUtil.OS, VarUtil.OSArchitecture};
|
||||
expectedLabels.AddRange(userLabels.Split(",").ToList());
|
||||
|
||||
_runnerServer.Verify(x => x.AddAgentAsync(It.IsAny<int>(), It.Is<TaskAgent>(a => a.Labels.Select(x => x.Name).ToHashSet().SetEquals(expectedLabels))), Times.Once);
|
||||
_runnerServer.Verify(x => x.AddAgentAsync(It.IsAny<int>(), It.Is<TaskAgent>(a => a.Labels.Contains("self-hosted") && a.Labels.Contains(VarUtil.OS) && a.Labels.Contains(VarUtil.OSArchitecture))), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
Assert.NotNull(sessionIdProperty);
|
||||
sessionIdProperty.SetValue(request, DateTime.UtcNow.AddMinutes(5));
|
||||
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(request));
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(request));
|
||||
|
||||
_runnerServer.Setup(x => x.FinishAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<DateTime>(), It.IsAny<TaskResult>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(new TaskAgentJobRequest()));
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -139,10 +139,10 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -197,11 +197,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -256,11 +256,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -315,11 +315,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||
Assert.True(cancellationTokenSource.IsCancellationRequested);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(8));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(8));
|
||||
_runnerServer.Verify(x => x.RefreshConnectionAsync(RunnerConnectionType.JobRequest, It.IsAny<TimeSpan>()), Times.Exactly(3));
|
||||
_runnerServer.Verify(x => x.SetConnectionTimeout(RunnerConnectionType.JobRequest, It.IsAny<TimeSpan>()), Times.Once);
|
||||
}
|
||||
@@ -349,7 +349,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -372,11 +372,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.False(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should failed.");
|
||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(6));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(6));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
@@ -436,11 +436,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var jobDispatcher = new JobDispatcher();
|
||||
jobDispatcher.Initialize(hc);
|
||||
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||
|
||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||
_runnerServer.Verify(x => x.RefreshConnectionAsync(RunnerConnectionType.JobRequest, It.IsAny<TimeSpan>()), Times.Exactly(3));
|
||||
_runnerServer.Verify(x => x.SetConnectionTimeout(RunnerConnectionType.JobRequest, It.IsAny<TimeSpan>()), Times.Never);
|
||||
}
|
||||
@@ -481,7 +481,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
Assert.NotNull(sessionIdProperty);
|
||||
sessionIdProperty.SetValue(request, DateTime.UtcNow.AddMinutes(5));
|
||||
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(request));
|
||||
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(request));
|
||||
|
||||
_runnerServer.Setup(x => x.FinishAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<DateTime>(), It.IsAny<TaskResult>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<TaskAgentJobRequest>(new TaskAgentJobRequest()));
|
||||
|
||||
|
||||
@@ -660,7 +660,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
_settings.AgentId))
|
||||
.Returns(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(10);
|
||||
return "https://t.server";
|
||||
});
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public List<ProductInfoHeaderValue> UserAgents => new List<ProductInfoHeaderValue>() { new ProductInfoHeaderValue("L0Test", "0.0") };
|
||||
public ProductInfoHeaderValue UserAgent => new ProductInfoHeaderValue("L0Test", "0.0");
|
||||
|
||||
public RunnerWebProxy WebProxy => new RunnerWebProxy();
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker;
|
||||
@@ -56,7 +54,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
//Assert
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
@@ -111,57 +109,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async void PrepareActions_SkipDownloadActionFromGraphWhenCached_OnPremises()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Arrange
|
||||
Setup();
|
||||
var actionId = Guid.NewGuid();
|
||||
var actions = new List<Pipelines.ActionStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = actionId,
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "actions/no-such-action",
|
||||
Ref = "master",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
||||
var actionDirectory = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "actions/no-such-action", "master");
|
||||
Directory.CreateDirectory(actionDirectory);
|
||||
var watermarkFile = $"{actionDirectory}.completed";
|
||||
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||
var actionFile = Path.Combine(actionDirectory, "action.yml");
|
||||
File.WriteAllText(actionFile, @"
|
||||
name: ""no-such-action""
|
||||
runs:
|
||||
using: node12
|
||||
main: no-such-action.js
|
||||
");
|
||||
var testFile = Path.Combine(actionDirectory, "test-file");
|
||||
File.WriteAllText(testFile, "asdf");
|
||||
|
||||
// Act
|
||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
// Assert
|
||||
Assert.True(File.Exists(testFile));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -217,7 +164,7 @@ runs:
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.True(steps.Count == 0);
|
||||
}
|
||||
@@ -256,7 +203,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
Assert.Equal(Path.Combine(actionDir, "Dockerfile"), (steps[0].Data as ContainerSetupInfo).Container.Dockerfile);
|
||||
@@ -296,7 +243,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -335,7 +282,7 @@ runs:
|
||||
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -375,7 +322,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerfileRelativePath");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -415,7 +362,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerHubImage");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image);
|
||||
@@ -454,7 +401,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionYamlFile_DockerHubImage");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal((steps[0].Data as ContainerSetupInfo).StepIds[0], actionId);
|
||||
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image);
|
||||
@@ -493,7 +440,7 @@ runs:
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithactionfileanddockerfile");
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -610,7 +557,7 @@ runs:
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
//Assert
|
||||
Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
@@ -671,7 +618,7 @@ runs:
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
// node.js based action doesn't need any extra steps to build/pull containers.
|
||||
Assert.True(steps.Count == 0);
|
||||
@@ -682,104 +629,6 @@ runs:
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async void PrepareActions_RepositoryActionWithInvalidWrapperActionfile_Node()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
var actionId = Guid.NewGuid();
|
||||
var actions = new List<Pipelines.ActionStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = actionId,
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "TingluoHuang/runner_L0",
|
||||
Ref = "RepositoryActionWithInvalidWrapperActionfile_Node",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
try
|
||||
{
|
||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
var traceFile = Path.GetTempFileName();
|
||||
File.Copy(_hc.TraceFileName, traceFile, true);
|
||||
Assert.Contains("Entry javascript file is not provided.", File.ReadAllText(traceFile));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async void PrepareActions_RepositoryActionWithWrapperActionfile_PreSteps()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
_hc.EnqueueInstance<IActionRunner>(new Mock<IActionRunner>().Object);
|
||||
_hc.EnqueueInstance<IActionRunner>(new Mock<IActionRunner>().Object);
|
||||
|
||||
var actionId1 = Guid.NewGuid();
|
||||
var actionId2 = Guid.NewGuid();
|
||||
_hc.GetTrace().Info(actionId1);
|
||||
_hc.GetTrace().Info(actionId2);
|
||||
var actions = new List<Pipelines.ActionStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action1",
|
||||
Id = actionId1,
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "TingluoHuang/runner_L0",
|
||||
Ref = "RepositoryActionWithWrapperActionfile_Node",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
},
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action2",
|
||||
Id = actionId2,
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "TingluoHuang/runner_L0",
|
||||
Ref = "RepositoryActionWithWrapperActionfile_Docker",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
var preResult = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
Assert.Equal(2, preResult.PreStepTracker.Count);
|
||||
Assert.NotNull(preResult.PreStepTracker[actionId1]);
|
||||
Assert.NotNull(preResult.PreStepTracker[actionId2]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -1524,7 +1373,7 @@ runs:
|
||||
|
||||
Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData));
|
||||
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
|
||||
Assert.Equal("cleanup.js", (definition.Data.Execution as NodeJSActionExecutionData).Post);
|
||||
Assert.Equal("cleanup.js", (definition.Data.Execution as NodeJSActionExecutionData).Cleanup);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1604,7 +1453,7 @@ runs:
|
||||
Assert.NotNull((definition.Data.Execution as ContainerActionExecutionData)); // execution.Node
|
||||
Assert.Equal("image:1234", (definition.Data.Execution as ContainerActionExecutionData).Image);
|
||||
Assert.Equal("main.sh", (definition.Data.Execution as ContainerActionExecutionData).EntryPoint);
|
||||
Assert.Equal("cleanup.sh", (definition.Data.Execution as ContainerActionExecutionData).Post);
|
||||
Assert.Equal("cleanup.sh", (definition.Data.Execution as ContainerActionExecutionData).Cleanup);
|
||||
|
||||
foreach (var arg in (definition.Data.Execution as ContainerActionExecutionData).Arguments)
|
||||
{
|
||||
@@ -1693,7 +1542,7 @@ runs:
|
||||
|
||||
Assert.NotNull((definition.Data.Execution as PluginActionExecutionData));
|
||||
Assert.Equal("plugin.class, plugin", (definition.Data.Execution as PluginActionExecutionData).Plugin);
|
||||
Assert.Equal("plugin.cleanup, plugin", (definition.Data.Execution as PluginActionExecutionData).Post);
|
||||
Assert.Equal("plugin.cleanup, plugin", (definition.Data.Execution as PluginActionExecutionData).Cleanup);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1751,8 +1600,6 @@ runs:
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -65,52 +63,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_ContainerAction_Dockerfile_Pre()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init.yml"));
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType);
|
||||
|
||||
var containerAction = result.Execution as ContainerActionExecutionData;
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("init.sh", containerAction.Pre);
|
||||
Assert.Equal("success()", containerAction.InitCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
Assert.Equal("foo", containerAction.Environment[0].Value.ToString());
|
||||
Assert.Equal("Url", containerAction.Environment[1].Key.ToString());
|
||||
Assert.Equal("bar", containerAction.Environment[1].Value.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -143,7 +95,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("cleanup.sh", containerAction.Post);
|
||||
Assert.Equal("cleanup.sh", containerAction.Cleanup);
|
||||
Assert.Equal("failure()", containerAction.CleanupCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
@@ -157,52 +109,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_ContainerAction_Dockerfile_Pre_DefaultCondition()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init_default.yml"));
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType);
|
||||
|
||||
var containerAction = result.Execution as ContainerActionExecutionData;
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("init.sh", containerAction.Pre);
|
||||
Assert.Equal("always()", containerAction.InitCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
Assert.Equal("foo", containerAction.Environment[0].Value.ToString());
|
||||
Assert.Equal("Url", containerAction.Environment[1].Key.ToString());
|
||||
Assert.Equal("bar", containerAction.Environment[1].Value.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -235,7 +141,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("cleanup.sh", containerAction.Post);
|
||||
Assert.Equal("cleanup.sh", containerAction.Cleanup);
|
||||
Assert.Equal("always()", containerAction.CleanupCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
@@ -415,94 +321,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_NodeAction_Pre()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init.yml"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
Assert.Equal(1, result.Deprecated.Count);
|
||||
|
||||
Assert.True(result.Deprecated.ContainsKey("greeting"));
|
||||
result.Deprecated.TryGetValue("greeting", out string value);
|
||||
Assert.Equal("This property has been deprecated", value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
|
||||
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("init.js", nodeAction.Pre);
|
||||
Assert.Equal("cancelled()", nodeAction.InitCondition);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_NodeAction_Init_DefaultCondition()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init_default.yml"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
Assert.Equal(1, result.Deprecated.Count);
|
||||
|
||||
Assert.True(result.Deprecated.ContainsKey("greeting"));
|
||||
result.Deprecated.TryGetValue("greeting", out string value);
|
||||
Assert.Equal("This property has been deprecated", value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
|
||||
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("init.js", nodeAction.Pre);
|
||||
Assert.Equal("always()", nodeAction.InitCondition);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -538,7 +356,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("cleanup.js", nodeAction.Post);
|
||||
Assert.Equal("cleanup.js", nodeAction.Cleanup);
|
||||
Assert.Equal("cancelled()", nodeAction.CleanupCondition);
|
||||
}
|
||||
finally
|
||||
@@ -582,7 +400,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("cleanup.js", nodeAction.Post);
|
||||
Assert.Equal("cleanup.js", nodeAction.Cleanup);
|
||||
Assert.Equal("always()", nodeAction.CleanupCondition);
|
||||
}
|
||||
finally
|
||||
@@ -715,26 +533,26 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
_ec.Object.ExpressionValues["github"] = new DictionaryContextData
|
||||
{
|
||||
{ "ref", new StringContextData("refs/heads/master") },
|
||||
};
|
||||
_ec.Object.ExpressionValues["strategy"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionValues["matrix"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionValues["steps"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionValues["job"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionValues["runner"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionValues["env"] = new DictionaryContextData();
|
||||
_ec.Object.ExpressionFunctions.Add(new FunctionInfo<HashFilesFunction>("hashFiles", 1, 255));
|
||||
var githubContext = new DictionaryContextData();
|
||||
githubContext.Add("ref", new StringContextData("refs/heads/master"));
|
||||
|
||||
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
evaluateContext["github"] = githubContext;
|
||||
evaluateContext["strategy"] = new DictionaryContextData();
|
||||
evaluateContext["matrix"] = new DictionaryContextData();
|
||||
evaluateContext["steps"] = new DictionaryContextData();
|
||||
evaluateContext["job"] = new DictionaryContextData();
|
||||
evaluateContext["runner"] = new DictionaryContextData();
|
||||
evaluateContext["env"] = new DictionaryContextData();
|
||||
|
||||
//Act
|
||||
var result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new StringToken(null, null, null, "defaultValue"));
|
||||
var result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new StringToken(null, null, null, "defaultValue"), evaluateContext);
|
||||
|
||||
//Assert
|
||||
Assert.Equal("defaultValue", result);
|
||||
|
||||
//Act
|
||||
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"));
|
||||
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"), evaluateContext);
|
||||
|
||||
//Assert
|
||||
Assert.Equal("refs/heads/master", result);
|
||||
@@ -757,8 +575,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.WriteDebug).Returns(true);
|
||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
@@ -323,7 +322,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
||||
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
|
||||
_ec.Setup(x => x.SetGitHubContext(It.IsAny<string>(), It.IsAny<string>()));
|
||||
|
||||
@@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
|
||||
var postRunner1 = hc.CreateService<IActionRunner>();
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Stage = ActionRunStage.Post;
|
||||
postRunner1.Condition = "always()";
|
||||
postRunner1.DisplayName = "post1";
|
||||
|
||||
|
||||
var postRunner2 = hc.CreateService<IActionRunner>();
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Stage = ActionRunStage.Post;
|
||||
postRunner2.Condition = "always()";
|
||||
postRunner2.DisplayName = "post2";
|
||||
|
||||
action1.RegisterPostJobStep(postRunner1);
|
||||
action2.RegisterPostJobStep(postRunner2);
|
||||
action1.RegisterPostJobStep("post1", postRunner1);
|
||||
action2.RegisterPostJobStep("post2", postRunner2);
|
||||
|
||||
Assert.NotNull(jobContext.JobSteps);
|
||||
Assert.NotNull(jobContext.PostJobSteps);
|
||||
@@ -238,91 +238,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void RegisterPostJobAction_NotRegisterPostTwice()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange: Create a job request message.
|
||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||
TimelineReference timeline = new TimelineReference();
|
||||
Guid jobId = Guid.NewGuid();
|
||||
string jobName = "some job name";
|
||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||
{
|
||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||
Id = "github",
|
||||
Version = "sha1"
|
||||
});
|
||||
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
||||
jobRequest.Variables["ACTIONS_STEP_DEBUG"] = "true";
|
||||
|
||||
// Arrange: Setup the paging logger.
|
||||
var pagingLogger1 = new Mock<IPagingLogger>();
|
||||
var pagingLogger2 = new Mock<IPagingLogger>();
|
||||
var pagingLogger3 = new Mock<IPagingLogger>();
|
||||
var pagingLogger4 = new Mock<IPagingLogger>();
|
||||
var pagingLogger5 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
|
||||
|
||||
var actionRunner1 = new ActionRunner();
|
||||
actionRunner1.Initialize(hc);
|
||||
var actionRunner2 = new ActionRunner();
|
||||
actionRunner2.Initialize(hc);
|
||||
|
||||
hc.EnqueueInstance(pagingLogger1.Object);
|
||||
hc.EnqueueInstance(pagingLogger2.Object);
|
||||
hc.EnqueueInstance(pagingLogger3.Object);
|
||||
hc.EnqueueInstance(pagingLogger4.Object);
|
||||
hc.EnqueueInstance(pagingLogger5.Object);
|
||||
hc.EnqueueInstance(actionRunner1 as IActionRunner);
|
||||
hc.EnqueueInstance(actionRunner2 as IActionRunner);
|
||||
hc.SetSingleton(jobServerQueue.Object);
|
||||
|
||||
var jobContext = new Runner.Worker.ExecutionContext();
|
||||
jobContext.Initialize(hc);
|
||||
|
||||
// Act.
|
||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||
|
||||
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);
|
||||
|
||||
var actionId = Guid.NewGuid();
|
||||
var postRunner1 = hc.CreateService<IActionRunner>();
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Id = actionId, Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Stage = ActionRunStage.Post;
|
||||
postRunner1.Condition = "always()";
|
||||
postRunner1.DisplayName = "post1";
|
||||
|
||||
|
||||
var postRunner2 = hc.CreateService<IActionRunner>();
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Id = actionId, Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Stage = ActionRunStage.Post;
|
||||
postRunner2.Condition = "always()";
|
||||
postRunner2.DisplayName = "post2";
|
||||
|
||||
action1.RegisterPostJobStep(postRunner1);
|
||||
action2.RegisterPostJobStep(postRunner2);
|
||||
|
||||
Assert.NotNull(jobContext.JobSteps);
|
||||
Assert.NotNull(jobContext.PostJobSteps);
|
||||
Assert.Equal(1, jobContext.PostJobSteps.Count);
|
||||
var post1 = jobContext.PostJobSteps.Pop();
|
||||
|
||||
Assert.Equal("post1", (post1 as IActionRunner).Action.Name);
|
||||
|
||||
Assert.Equal(ActionRunStage.Post, (post1 as IActionRunner).Stage);
|
||||
|
||||
Assert.Equal("always()", (post1 as IActionRunner).Condition);
|
||||
}
|
||||
}
|
||||
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
var hc = new TestHostContext(this, testName);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
public sealed class ConditionFunctionsL0
|
||||
public sealed class ExpressionManagerL0
|
||||
{
|
||||
private TemplateContext _templateContext;
|
||||
private Mock<IExecutionContext> _ec;
|
||||
private ExpressionManager _expressionManager;
|
||||
private DictionaryContextData _expressions;
|
||||
private JobContext _jobContext;
|
||||
|
||||
[Fact]
|
||||
@@ -38,7 +38,7 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = Evaluate("always()");
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "always()").Value;
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -68,7 +68,7 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = Evaluate("cancelled()");
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "cancelled()").Value;
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -97,7 +97,7 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = Evaluate("failure()");
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "failure()").Value;
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -126,7 +126,37 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = Evaluate("success()");
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "success()").Value;
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void ContextNamedValue()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new { Condition = "github.ref == 'refs/heads/master'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
|
||||
new { Condition = "github['ref'] == 'refs/heads/master'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
|
||||
new { Condition = "github.nosuch || '' == ''", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
|
||||
new { Condition = "github['ref'] == 'refs/heads/release'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = false },
|
||||
new { Condition = "github.ref == 'refs/heads/release'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = false },
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
InitializeExecutionContext(hc);
|
||||
_ec.Object.ExpressionValues["github"] = new GitHubContext() { { variableSet.VariableName, new StringContextData(variableSet.VariableValue) } };
|
||||
|
||||
// Act.
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, variableSet.Condition).Value;
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -136,34 +166,21 @@ namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
return new TestHostContext(this, testName);
|
||||
var hc = new TestHostContext(this, testName);
|
||||
_expressionManager = new ExpressionManager();
|
||||
_expressionManager.Initialize(hc);
|
||||
return hc;
|
||||
}
|
||||
|
||||
private void InitializeExecutionContext(TestHostContext hc)
|
||||
{
|
||||
_expressions = new DictionaryContextData();
|
||||
_jobContext = new JobContext();
|
||||
|
||||
var executionContext = new Mock<IExecutionContext>();
|
||||
executionContext.SetupAllProperties();
|
||||
executionContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
|
||||
_templateContext = new TemplateContext();
|
||||
_templateContext.State[nameof(IExecutionContext)] = executionContext.Object;
|
||||
}
|
||||
|
||||
private bool Evaluate(string expression)
|
||||
{
|
||||
var parser = new ExpressionParser();
|
||||
var functions = new IFunctionInfo[]
|
||||
{
|
||||
new FunctionInfo<AlwaysFunction>(PipelineTemplateConstants.Always, 0, 0),
|
||||
new FunctionInfo<CancelledFunction>(PipelineTemplateConstants.Cancelled, 0, 0),
|
||||
new FunctionInfo<FailureFunction>(PipelineTemplateConstants.Failure, 0, 0),
|
||||
new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0),
|
||||
};
|
||||
var tree = parser.CreateTree(expression, null, null, functions);
|
||||
var result = tree.Evaluate(null, null, _templateContext, null);
|
||||
return result.IsTruthy;
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.SetupAllProperties();
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_expressions);
|
||||
_ec.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IJobServerQueue> _jobServerQueue;
|
||||
private Mock<IConfigurationStore> _config;
|
||||
private Mock<IPagingLogger> _logger;
|
||||
private Mock<IExpressionManager> _express;
|
||||
private Mock<IContainerOperationProvider> _containerProvider;
|
||||
private Mock<IDiagnosticLogManager> _diagnosticLogManager;
|
||||
|
||||
@@ -34,6 +35,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobServerQueue = new Mock<IJobServerQueue>();
|
||||
_config = new Mock<IConfigurationStore>();
|
||||
_logger = new Mock<IPagingLogger>();
|
||||
_express = new Mock<IExpressionManager>();
|
||||
_containerProvider = new Mock<IContainerOperationProvider>();
|
||||
_diagnosticLogManager = new Mock<IDiagnosticLogManager>();
|
||||
_directoryManager = new Mock<IPipelineDirectoryManager>();
|
||||
@@ -106,6 +108,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.SetSingleton(_actionManager.Object);
|
||||
hc.SetSingleton(_config.Object);
|
||||
hc.SetSingleton(_jobServerQueue.Object);
|
||||
hc.SetSingleton(_express.Object);
|
||||
hc.SetSingleton(_containerProvider.Object);
|
||||
hc.SetSingleton(_directoryManager.Object);
|
||||
hc.SetSingleton(_diagnosticLogManager.Object);
|
||||
@@ -141,7 +144,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>()));
|
||||
|
||||
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
@@ -176,7 +179,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }, new Dictionary<Guid, IActionRunner>())));
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }));
|
||||
|
||||
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
var expressionManager = new ExpressionManager();
|
||||
expressionManager.Initialize(hc);
|
||||
hc.SetSingleton<IExpressionManager>(expressionManager);
|
||||
|
||||
_jobRunner = new JobRunner();
|
||||
_jobRunner.Initialize(hc);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
@@ -27,6 +27,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
var hc = new TestHostContext(this, testName);
|
||||
var expressionManager = new ExpressionManager();
|
||||
expressionManager.Initialize(hc);
|
||||
hc.SetSingleton<IExpressionManager>(expressionManager);
|
||||
Dictionary<string, VariableValue> variablesToCopy = new Dictionary<string, VariableValue>();
|
||||
_variables = new Variables(
|
||||
hostContext: hc,
|
||||
@@ -46,7 +49,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_contexts["runner"] = new DictionaryContextData();
|
||||
_contexts["job"] = _jobContext;
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_contexts);
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
|
||||
_stepContext = new StepsContext();
|
||||
@@ -381,11 +383,16 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var expressionManager = new Mock<IExpressionManager>();
|
||||
expressionManager.Object.Initialize(hc);
|
||||
hc.SetSingleton<IExpressionManager>(expressionManager.Object);
|
||||
expressionManager.Setup(x => x.Evaluate(It.IsAny<IExecutionContext>(), It.IsAny<string>(), It.IsAny<bool>())).Throws(new Exception());
|
||||
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "fromJson('not json')") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "fromJson('not json')") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -603,7 +610,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
stepContext.Setup(x => x.Variables).Returns(_variables);
|
||||
stepContext.Setup(x => x.EnvironmentVariables).Returns(_env);
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(_contexts);
|
||||
stepContext.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
||||
stepContext.Setup(x => x.ContextName).Returns(step.Object.Action.ContextName);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- 'bzz'
|
||||
entrypoint: 'main.sh'
|
||||
env:
|
||||
Token: foo
|
||||
Url: bar
|
||||
pre-entrypoint: 'init.sh'
|
||||
pre-if: 'success()'
|
||||
@@ -1,26 +0,0 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- 'bzz'
|
||||
entrypoint: 'main.sh'
|
||||
env:
|
||||
Token: foo
|
||||
Url: bar
|
||||
pre-entrypoint: 'init.sh'
|
||||
@@ -1,22 +0,0 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
deprecationMessage: 'This property has been deprecated'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
pre: 'init.js'
|
||||
pre-if: 'cancelled()'
|
||||
@@ -1,21 +0,0 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
deprecationMessage: 'This property has been deprecated'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
pre: 'init.js'
|
||||
@@ -1 +1 @@
|
||||
2.169.0
|
||||
2.165.2
|
||||
|
||||
Reference in New Issue
Block a user