mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
5 Commits
users/tihu
...
v2.168.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fc0686dc2 | ||
|
|
ab001a7004 | ||
|
|
178a618e01 | ||
|
|
dfaf6e06ee | ||
|
|
b0a71481f0 |
@@ -1,27 +1,24 @@
|
||||
## Features
|
||||
- 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)
|
||||
- Update Runner Register GitHub API URL to Support Org-level Runner (#339 #345 #352)
|
||||
- Preserve workflow file/line/column for better error messages (#356)
|
||||
- Switch to use token service instead of SPS for exchanging oauth token. (#325)
|
||||
- Load and print machine setup info from .setup_info (#364)
|
||||
- Expose job name as $GITHUB_JOB (#366)
|
||||
- Add support for job outputs. (#365)
|
||||
- Set CI=true when launch process in actions runner. (#374)
|
||||
- Set steps.<id>.outcome and steps.<id>.conclusion. (#372)
|
||||
- Add support for workflow/job defaults. (#369)
|
||||
- Expose GITHUB_REPOSITORY_OWNER and ${{github.repository_owner}}. (#378)
|
||||
|
||||
## Bugs
|
||||
- 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)
|
||||
- Use authenticate endpoint for testing runner connection. (#311)
|
||||
- Commands translate file path from container action (#331)
|
||||
- Change problem matchers output to debug (#363)
|
||||
- Switch hashFiles to extension function (#362)
|
||||
- Add expanded volumes strings to container mounts (#384)
|
||||
|
||||
## Misc
|
||||
- Move .sln file under ./src (#238)
|
||||
- Treat warnings as errors during compile (#249)
|
||||
- Add runner auth documentation (#357)
|
||||
|
||||
## 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
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile);
|
||||
|
||||
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||
|
||||
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||
|
||||
string EvaluateDefaultInput(IExecutionContext executionContext, string inputName, TemplateToken token, IDictionary<string, PipelineContextData> contextData);
|
||||
string EvaluateDefaultInput(IExecutionContext executionContext, string inputName, TemplateToken token);
|
||||
}
|
||||
|
||||
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, null);
|
||||
var context = CreateContext(executionContext);
|
||||
ActionDefinitionData actionDefinition = new ActionDefinitionData();
|
||||
try
|
||||
{
|
||||
@@ -133,13 +133,13 @@ namespace GitHub.Runner.Worker
|
||||
public List<string> EvaluateContainerArguments(
|
||||
IExecutionContext executionContext,
|
||||
SequenceToken token,
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues)
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
var context = CreateContext(executionContext, extraExpressionValues);
|
||||
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> contextData)
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues)
|
||||
{
|
||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
var context = CreateContext(executionContext, extraExpressionValues);
|
||||
try
|
||||
{
|
||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "container-runs-env", token, 0, null, omitHeader: true);
|
||||
@@ -216,13 +216,12 @@ namespace GitHub.Runner.Worker
|
||||
public string EvaluateDefaultInput(
|
||||
IExecutionContext executionContext,
|
||||
string inputName,
|
||||
TemplateToken token,
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
TemplateToken token)
|
||||
{
|
||||
string result = "";
|
||||
if (token != null)
|
||||
{
|
||||
var context = CreateContext(executionContext, contextData);
|
||||
var context = CreateContext(executionContext);
|
||||
try
|
||||
{
|
||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "input-default-context", token, 0, null, omitHeader: true);
|
||||
@@ -247,7 +246,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private TemplateContext CreateContext(
|
||||
IExecutionContext executionContext,
|
||||
IDictionary<string, PipelineContextData> contextData)
|
||||
IDictionary<string, PipelineContextData> extraExpressionValues = null)
|
||||
{
|
||||
var result = new TemplateContext
|
||||
{
|
||||
@@ -261,12 +260,25 @@ namespace GitHub.Runner.Worker
|
||||
TraceWriter = executionContext.ToTemplateTraceWriter(),
|
||||
};
|
||||
|
||||
if (contextData?.Count > 0)
|
||||
{
|
||||
foreach (var pair in contextData)
|
||||
// Expression values from execution context
|
||||
foreach (var pair in executionContext.ExpressionValues)
|
||||
{
|
||||
result.ExpressionValues[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
// Extra expression values
|
||||
if (extraExpressionValues?.Count > 0)
|
||||
{
|
||||
foreach (var pair in extraExpressionValues)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace GitHub.Runner.Worker
|
||||
public interface IActionRunner : IStep, IRunnerService
|
||||
{
|
||||
ActionRunStage Stage { get; set; }
|
||||
Boolean TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context);
|
||||
bool TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context);
|
||||
Pipelines.ActionStep Action { get; set; }
|
||||
}
|
||||
|
||||
@@ -142,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);
|
||||
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);
|
||||
|
||||
foreach (KeyValuePair<string, string> input in inputs)
|
||||
{
|
||||
@@ -162,13 +162,7 @@ namespace GitHub.Runner.Worker
|
||||
string key = input.Key.AssertString("action input name").Value;
|
||||
if (!inputs.ContainsKey(key))
|
||||
{
|
||||
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);
|
||||
inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,10 +287,14 @@ namespace GitHub.Runner.Worker
|
||||
return displayName;
|
||||
}
|
||||
// Try evaluating fully
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
try
|
||||
{
|
||||
didFullyEvaluate = templateEvaluator.TryEvaluateStepDisplayName(tokenToParse, contextData, out displayName);
|
||||
if (tokenToParse.CheckHasRequiredContext(contextData, context.ExpressionFunctions))
|
||||
{
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
displayName = templateEvaluator.EvaluateStepDisplayName(tokenToParse, contextData, context.ExpressionFunctions);
|
||||
didFullyEvaluate = true;
|
||||
}
|
||||
}
|
||||
catch (TemplateValidationException e)
|
||||
{
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace GitHub.Runner.Worker.Container
|
||||
foreach (var volume in container.Volumes)
|
||||
{
|
||||
UserMountVolumes[volume] = volume;
|
||||
MountVolumes.Add(new MountVolume(volume));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,13 @@ 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.
|
||||
@@ -189,6 +196,13 @@ 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}\"");
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
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.Runner.Worker.Container;
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
@@ -16,12 +17,11 @@ 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
|
||||
{
|
||||
@@ -48,12 +48,14 @@ namespace GitHub.Runner.Worker
|
||||
PlanFeatures Features { get; }
|
||||
Variables Variables { get; }
|
||||
Dictionary<string, string> IntraActionState { get; }
|
||||
IDictionary<String, IDictionary<String, String>> JobDefaults { get; }
|
||||
Dictionary<string, VariableValue> JobOutputs { get; }
|
||||
IDictionary<String, String> EnvironmentVariables { get; }
|
||||
IDictionary<String, ContextScope> Scopes { get; }
|
||||
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; }
|
||||
@@ -140,12 +142,14 @@ namespace GitHub.Runner.Worker
|
||||
public List<ServiceEndpoint> Endpoints { get; private set; }
|
||||
public Variables Variables { get; private set; }
|
||||
public Dictionary<string, string> IntraActionState { get; private set; }
|
||||
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; private set; }
|
||||
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
||||
public IDictionary<String, String> EnvironmentVariables { get; private set; }
|
||||
public IDictionary<String, ContextScope> Scopes { get; private set; }
|
||||
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; }
|
||||
@@ -270,6 +274,7 @@ namespace GitHub.Runner.Worker
|
||||
child.IntraActionState = intraActionState;
|
||||
}
|
||||
child.EnvironmentVariables = EnvironmentVariables;
|
||||
child.JobDefaults = JobDefaults;
|
||||
child.Scopes = Scopes;
|
||||
child.FileTable = FileTable;
|
||||
child.StepsContext = StepsContext;
|
||||
@@ -277,6 +282,10 @@ 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;
|
||||
@@ -565,6 +574,9 @@ namespace GitHub.Runner.Worker
|
||||
// Environment variables shared across all actions
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer);
|
||||
|
||||
// Job defaults shared across all actions
|
||||
JobDefaults = new Dictionary<string, IDictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Job Outputs
|
||||
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -587,12 +599,6 @@ 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)
|
||||
{
|
||||
@@ -909,11 +915,19 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public static PipelineTemplateEvaluator ToPipelineTemplateEvaluator(this IExecutionContext context)
|
||||
public static IEnumerable<KeyValuePair<string, object>> ToExpressionState(this IExecutionContext context)
|
||||
{
|
||||
var templateTrace = context.ToTemplateTraceWriter();
|
||||
var schema = new PipelineTemplateSchemaFactory().CreateSchema();
|
||||
return new PipelineTemplateEvaluator(templateTrace, schema, context.FileTable);
|
||||
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);
|
||||
}
|
||||
|
||||
public static ObjectTemplating.ITraceWriter ToTemplateTraceWriter(this IExecutionContext context)
|
||||
@@ -928,6 +942,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
internal TemplateTraceWriter(IExecutionContext executionContext)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
_executionContext = executionContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,162 +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
|
||||
{
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Runner.Worker/Expressions/AlwaysFunction.cs
Normal file
25
src/Runner.Worker/Expressions/AlwaysFunction.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Runner.Worker/Expressions/CancelledFunction.cs
Normal file
31
src/Runner.Worker/Expressions/CancelledFunction.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Runner.Worker/Expressions/FailureFunction.cs
Normal file
31
src/Runner.Worker/Expressions/FailureFunction.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,28 +8,9 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GitHub.Runner.Worker.Handlers
|
||||
namespace GitHub.Runner.Worker.Expressions
|
||||
{
|
||||
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
|
||||
public sealed class HashFilesFunction : Function
|
||||
{
|
||||
protected sealed override Object EvaluateCore(
|
||||
EvaluationContext context,
|
||||
@@ -82,7 +63,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
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 FunctionTrace(context.Trace));
|
||||
var p = new ProcessInvoker(new HashFilesTrace(context.Trace));
|
||||
p.ErrorDataReceived += ((_, data) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(data.Data) && data.Data.StartsWith("__OUTPUT__") && data.Data.EndsWith("__OUTPUT__"))
|
||||
@@ -122,5 +103,24 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Runner.Worker/Expressions/SuccessFunction.cs
Normal file
31
src/Runner.Worker/Expressions/SuccessFunction.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace GitHub.Runner.Worker
|
||||
"job",
|
||||
"ref",
|
||||
"repository",
|
||||
"repository_owner",
|
||||
"run_id",
|
||||
"run_number",
|
||||
"sha",
|
||||
|
||||
@@ -97,14 +97,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
evaluateContext["inputs"] = inputsContext;
|
||||
var extraExpressionValues = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
extraExpressionValues["inputs"] = inputsContext;
|
||||
|
||||
var manifestManager = HostContext.GetService<IActionManifestManager>();
|
||||
if (Data.Arguments != null)
|
||||
{
|
||||
container.ContainerEntryPointArgs = "";
|
||||
var evaluatedArgs = manifestManager.EvaluateContainerArguments(ExecutionContext, Data.Arguments, evaluateContext);
|
||||
var evaluatedArgs = manifestManager.EvaluateContainerArguments(ExecutionContext, Data.Arguments, extraExpressionValues);
|
||||
foreach (var arg in evaluatedArgs)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(arg))
|
||||
@@ -124,7 +124,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
if (Data.Environment != null)
|
||||
{
|
||||
var evaluatedEnv = manifestManager.EvaluateContainerEnvironment(ExecutionContext, Data.Environment, evaluateContext);
|
||||
var evaluatedEnv = manifestManager.EvaluateContainerEnvironment(ExecutionContext, Data.Environment, extraExpressionValues);
|
||||
foreach (var env in evaluatedEnv)
|
||||
{
|
||||
if (!this.Environment.ContainsKey(env.Key))
|
||||
|
||||
@@ -58,7 +58,16 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
string shellCommandPath = null;
|
||||
bool validateShellOnHost = !(StepHost is ContainerStepHost);
|
||||
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.PrependPath.Reverse<string>());
|
||||
Inputs.TryGetValue("shell", out var shell);
|
||||
string shell = null;
|
||||
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
runDefaults.TryGetValue("shell", out shell);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(shell))
|
||||
{
|
||||
#if OS_WINDOWS
|
||||
@@ -139,11 +148,36 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
Inputs.TryGetValue("script", out var contents);
|
||||
contents = contents ?? string.Empty;
|
||||
|
||||
Inputs.TryGetValue("workingDirectory", out var workingDirectory);
|
||||
string workingDirectory = null;
|
||||
if (!Inputs.TryGetValue("workingDirectory", out workingDirectory))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("working-directory", out workingDirectory))
|
||||
{
|
||||
ExecutionContext.Debug("Overwrite 'working-directory' base on job defaults.");
|
||||
}
|
||||
}
|
||||
}
|
||||
var workspaceDir = githubContext["workspace"] as StringContextData;
|
||||
workingDirectory = Path.Combine(workspaceDir, workingDirectory ?? string.Empty);
|
||||
|
||||
Inputs.TryGetValue("shell", out var shell);
|
||||
string shell = null;
|
||||
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("shell", out shell))
|
||||
{
|
||||
ExecutionContext.Debug("Overwrite 'shell' base on job defaults.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isContainerStepHost = StepHost is ContainerStepHost;
|
||||
|
||||
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.PrependPath.Reverse<string>());
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace GitHub.Runner.Worker
|
||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||
foreach (var token in message.EnvironmentVariables)
|
||||
{
|
||||
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
|
||||
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, jobContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var pair in environmentVariables)
|
||||
{
|
||||
context.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty;
|
||||
@@ -142,7 +142,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Evaluate the job container
|
||||
context.Debug("Evaluating job container");
|
||||
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues);
|
||||
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||
if (container != null)
|
||||
{
|
||||
jobContext.Container = new Container.ContainerInfo(HostContext, container);
|
||||
@@ -150,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);
|
||||
var serviceContainers = templateEvaluator.EvaluateJobServiceContainers(message.JobServiceContainers, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||
if (serviceContainers?.Count > 0)
|
||||
{
|
||||
foreach (var pair in serviceContainers)
|
||||
@@ -161,6 +161,26 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the job defaults
|
||||
context.Debug("Evaluating job defaults");
|
||||
foreach (var token in message.Defaults)
|
||||
{
|
||||
var defaults = token.AssertMapping("defaults");
|
||||
if (defaults.Any(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
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);
|
||||
foreach (var pair in jobDefaults)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pair.Value))
|
||||
{
|
||||
context.JobDefaults["run"][pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build up 2 lists of steps, pre-job, job
|
||||
// Download actions not already in the cache
|
||||
Trace.Info("Downloading actions");
|
||||
@@ -317,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);
|
||||
var outputs = templateEvaluator.EvaluateJobOutput(message.JobOutputs, context.ExpressionValues, context.ExpressionFunctions);
|
||||
foreach (var output in outputs)
|
||||
{
|
||||
if (string.IsNullOrEmpty(output.Value))
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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;
|
||||
@@ -10,8 +8,13 @@ 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
|
||||
{
|
||||
@@ -63,11 +66,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
var step = jobContext.JobSteps.Dequeue();
|
||||
IStep nextStep = null;
|
||||
if (jobContext.JobSteps.Count > 0)
|
||||
{
|
||||
nextStep = jobContext.JobSteps.Peek();
|
||||
}
|
||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps.Peek() : null;
|
||||
|
||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||
@@ -76,6 +75,13 @@ 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))
|
||||
{
|
||||
@@ -99,14 +105,13 @@ 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, VarUtil.EnvironmentVariableKeyComparer);
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, 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
|
||||
@@ -120,28 +125,29 @@ namespace GitHub.Runner.Worker
|
||||
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
||||
|
||||
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
|
||||
ConditionResult conditionReTestResult;
|
||||
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
|
||||
var conditionReTestResult = false;
|
||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||
{
|
||||
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
||||
conditionReTestResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
conditionReTestResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition, hostTracingOnly: true);
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionReTestTraceWriter);
|
||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||
conditionReTestResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Cancel the step since we get exception while re-evaluate step condition.
|
||||
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
||||
step.ExecutionContext.Error(ex);
|
||||
conditionReTestResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!conditionReTestResult.Value)
|
||||
if (!conditionReTestResult)
|
||||
{
|
||||
// Cancel the step.
|
||||
Trace.Info("Cancel current running step.");
|
||||
@@ -161,34 +167,35 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Evaluate condition.
|
||||
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
||||
Exception conditionEvaluateError = null;
|
||||
ConditionResult conditionResult;
|
||||
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
||||
var conditionResult = false;
|
||||
var conditionEvaluateError = default(Exception);
|
||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||
{
|
||||
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
||||
conditionResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
conditionResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition);
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||
conditionResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Info("Caught exception from expression.");
|
||||
Trace.Error(ex);
|
||||
conditionResult = false;
|
||||
conditionEvaluateError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
// no evaluate error but condition is false
|
||||
if (!conditionResult.Value && conditionEvaluateError == null)
|
||||
if (!conditionResult && conditionEvaluateError == null)
|
||||
{
|
||||
// Condition == false
|
||||
Trace.Info("Skipping step due to condition evaluation.");
|
||||
CompleteStep(step, nextStep, TaskResult.Skipped, resultCode: conditionResult.Trace);
|
||||
CompleteStep(step, nextStep, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
|
||||
}
|
||||
else if (conditionEvaluateError != null)
|
||||
{
|
||||
@@ -248,7 +255,7 @@ namespace GitHub.Runner.Worker
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
try
|
||||
{
|
||||
timeoutMinutes = templateEvaluator.EvaluateStepTimeout(step.Timeout, step.ExecutionContext.ExpressionValues);
|
||||
timeoutMinutes = templateEvaluator.EvaluateStepTimeout(step.Timeout, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -339,7 +346,7 @@ namespace GitHub.Runner.Worker
|
||||
var continueOnError = false;
|
||||
try
|
||||
{
|
||||
continueOnError = templateEvaluator.EvaluateStepContinueOnError(step.ContinueOnError, step.ExecutionContext.ExpressionValues);
|
||||
continueOnError = templateEvaluator.EvaluateStepContinueOnError(step.ContinueOnError, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -392,7 +399,7 @@ namespace GitHub.Runner.Worker
|
||||
var inputs = default(DictionaryContextData);
|
||||
try
|
||||
{
|
||||
inputs = templateEvaluator.EvaluateStepScopeInputs(scope.Inputs, executionContext.ExpressionValues);
|
||||
inputs = templateEvaluator.EvaluateStepScopeInputs(scope.Inputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -448,7 +455,7 @@ namespace GitHub.Runner.Worker
|
||||
var outputs = default(DictionaryContextData);
|
||||
try
|
||||
{
|
||||
outputs = templateEvaluator.EvaluateStepScopeOutputs(scope.Outputs, executionContext.ExpressionValues);
|
||||
outputs = templateEvaluator.EvaluateStepScopeOutputs(scope.Outputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -476,5 +483,43 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@
|
||||
"strategy",
|
||||
"matrix",
|
||||
"job",
|
||||
"runner"
|
||||
"runner",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ using GitHub.DistributedTask.Expressions2.Sdk.Functions;
|
||||
|
||||
namespace GitHub.DistributedTask.Expressions2
|
||||
{
|
||||
public static class ExpressionConstants
|
||||
internal static class ExpressionConstants
|
||||
{
|
||||
static ExpressionConstants()
|
||||
{
|
||||
@@ -16,7 +16,6 @@ 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)
|
||||
@@ -25,12 +24,6 @@ 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;
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
|
||||
@@ -22,10 +23,27 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
{
|
||||
var context = definition[i].Value.AssertSequence($"{TemplateConstants.Context}");
|
||||
definition.RemoveAt(i);
|
||||
Context = context
|
||||
.Select(x => x.AssertString($"{TemplateConstants.Context} item").Value)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
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();
|
||||
}
|
||||
else if (String.Equals(definitionKey.Value, TemplateConstants.Description, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -40,7 +58,17 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
|
||||
internal abstract DefinitionType DefinitionType { get; }
|
||||
|
||||
internal String[] Context { get; private set; } = new String[0];
|
||||
/// <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 abstract void Validate(
|
||||
TemplateSchema schema,
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Schema
|
||||
{
|
||||
var inherited = schema.GetDefinition(Inherits);
|
||||
|
||||
if (inherited.Context.Length > 0)
|
||||
if (inherited.ReaderContext.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.Context.Length > 0)
|
||||
if (nestedDefinition.ReaderContext.Length > 0)
|
||||
{
|
||||
throw new ArgumentException($"'{name}' is a one-of definition and references another definition that defines context. This is currently not supported.");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,16 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
var evaluator = new TemplateEvaluator(context, template, removeBytes);
|
||||
try
|
||||
{
|
||||
var availableContext = new HashSet<String>(context.ExpressionValues.Keys.Concat(context.ExpressionFunctions.Select(x => $"{x.Name}({x.MinParameters},{x.MaxParameters})")));
|
||||
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 definitionInfo = new DefinitionInfo(context.Schema, type, availableContext);
|
||||
result = evaluator.Evaluate(definitionInfo);
|
||||
|
||||
@@ -393,14 +402,13 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
m_allowedContext = Definition.EvaluatorContext;
|
||||
if (Definition.EvaluatorContext.Length > 0)
|
||||
{
|
||||
m_allowedContext = Definition.Context;
|
||||
Expand = m_availableContext.IsSupersetOf(m_allowedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_allowedContext = new String[0];
|
||||
Expand = false;
|
||||
}
|
||||
}
|
||||
@@ -416,9 +424,9 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
if (Definition.EvaluatorContext.Length > 0)
|
||||
{
|
||||
m_allowedContext = new HashSet<String>(parent.m_allowedContext.Concat(Definition.Context)).ToArray();
|
||||
m_allowedContext = new HashSet<String>(parent.m_allowedContext.Concat(Definition.EvaluatorContext), StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
Expand = m_availableContext.IsSupersetOf(m_allowedContext);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -49,6 +49,14 @@ 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,15 +780,8 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
// Lookup the definition
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
{
|
||||
AllowedContext = Definition.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowedContext = new String[0];
|
||||
}
|
||||
// Record allowed context
|
||||
AllowedContext = Definition.ReaderContext;
|
||||
}
|
||||
|
||||
public DefinitionInfo(
|
||||
@@ -800,10 +793,10 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
// Lookup the definition
|
||||
Definition = m_schema.GetDefinition(name);
|
||||
|
||||
// Determine whether to expand
|
||||
if (Definition.Context.Length > 0)
|
||||
// Record allowed context
|
||||
if (Definition.ReaderContext.Length > 0)
|
||||
{
|
||||
AllowedContext = new HashSet<String>(parent.AllowedContext.Concat(Definition.Context)).ToArray();
|
||||
AllowedContext = new HashSet<String>(parent.AllowedContext.Concat(Definition.ReaderContext), StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.ObjectTemplating
|
||||
@@ -41,7 +42,7 @@ namespace GitHub.DistributedTask.ObjectTemplating
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
String message = !String.IsNullOrEmpty(messagePrefix) ? $"{messagePrefix} {ex.Message}" : ex.Message;
|
||||
String message = !String.IsNullOrEmpty(messagePrefix) ? $"{messagePrefix} {ex.Message}" : ex.ToString();
|
||||
Add(new TemplateValidationError(message));
|
||||
if (ex.InnerException == null)
|
||||
{
|
||||
@@ -88,6 +89,23 @@ 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,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
using GitHub.Services.WebApi.Internal;
|
||||
@@ -35,11 +37,29 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
String[] allowedContext,
|
||||
out Exception ex)
|
||||
{
|
||||
// Create dummy allowed contexts
|
||||
INamedValueInfo[] namedValues = null;
|
||||
// Create dummy named values and functions
|
||||
var namedValues = new List<INamedValueInfo>();
|
||||
var functions = new List<IFunctionInfo>();
|
||||
if (allowedContext?.Length > 0)
|
||||
{
|
||||
namedValues = allowedContext.Select(x => new NamedValueInfo<ContextValueNode>(x)).ToArray();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse
|
||||
@@ -47,7 +67,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
ExpressionNode root = null;
|
||||
try
|
||||
{
|
||||
root = new ExpressionParser().CreateTree(expression, null, namedValues, null) as ExpressionNode;
|
||||
root = new ExpressionParser().CreateTree(expression, null, namedValues, functions) as ExpressionNode;
|
||||
|
||||
result = true;
|
||||
ex = null;
|
||||
@@ -60,5 +80,18 @@ 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,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||
|
||||
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
|
||||
{
|
||||
@@ -106,6 +109,43 @@ 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>
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
IEnumerable<JobStep> steps,
|
||||
IEnumerable<ContextScope> scopes,
|
||||
IList<String> fileTable,
|
||||
TemplateToken jobOutputs)
|
||||
TemplateToken jobOutputs,
|
||||
IList<TemplateToken> defaults)
|
||||
{
|
||||
this.MessageType = JobRequestMessageTypes.PipelineAgentJobRequest;
|
||||
this.Plan = plan;
|
||||
@@ -69,6 +70,11 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
m_environmentVariables = new List<TemplateToken>(environmentVariables);
|
||||
}
|
||||
|
||||
if (defaults?.Count > 0)
|
||||
{
|
||||
m_defaults = new List<TemplateToken>(defaults);
|
||||
}
|
||||
|
||||
this.ContextData = new Dictionary<String, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||
if (contextData?.Count > 0)
|
||||
{
|
||||
@@ -213,6 +219,21 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hierarchy of defaults to overlay, last wins.
|
||||
/// </summary>
|
||||
public IList<TemplateToken> Defaults
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_defaults == null)
|
||||
{
|
||||
m_defaults = new List<TemplateToken>();
|
||||
}
|
||||
return m_defaults;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of variables associated with the current context.
|
||||
/// </summary>
|
||||
@@ -252,6 +273,9 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table of files used when parsing the pipeline (e.g. yaml files)
|
||||
/// </summary>
|
||||
public IList<String> FileTable
|
||||
{
|
||||
get
|
||||
@@ -372,6 +396,11 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
m_environmentVariables = null;
|
||||
}
|
||||
|
||||
if (m_defaults?.Count == 0)
|
||||
{
|
||||
m_defaults = null;
|
||||
}
|
||||
|
||||
if (m_fileTable?.Count == 0)
|
||||
{
|
||||
m_fileTable = null;
|
||||
@@ -406,6 +435,9 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
[DataMember(Name = "EnvironmentVariables", EmitDefaultValue = false)]
|
||||
private List<TemplateToken> m_environmentVariables;
|
||||
|
||||
[DataMember(Name = "Defaults", EmitDefaultValue = false)]
|
||||
private List<TemplateToken> m_defaults;
|
||||
|
||||
[DataMember(Name = "FileTable", EmitDefaultValue = false)]
|
||||
private List<String> m_fileTable;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Clean = "clean";
|
||||
public const String Container = "container";
|
||||
public const String ContinueOnError = "continue-on-error";
|
||||
public const String Defaults = "defaults";
|
||||
public const String Env = "env";
|
||||
public const String Event = "event";
|
||||
public const String EventPattern = "github.event";
|
||||
@@ -23,12 +24,15 @@ 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";
|
||||
public const String Include = "include";
|
||||
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";
|
||||
@@ -58,6 +62,7 @@ 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,6 +16,20 @@ 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;
|
||||
using GitHub.DistributedTask.Expressions2.Sdk.Functions;
|
||||
using GitHub.DistributedTask.ObjectTemplating;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Schema;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
@@ -14,6 +14,9 @@ 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
|
||||
{
|
||||
@@ -50,13 +53,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public DictionaryContextData EvaluateStepScopeInputs(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(DictionaryContextData);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeInputs, token, 0, null, omitHeader: true);
|
||||
@@ -76,13 +80,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public DictionaryContextData EvaluateStepScopeOutputs(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(DictionaryContextData);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeOutputs, token, 0, null, omitHeader: true);
|
||||
@@ -102,13 +107,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Boolean EvaluateStepContinueOnError(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(Boolean?);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.BooleanStepsContext, token, 0, null, omitHeader: true);
|
||||
@@ -126,16 +132,44 @@ 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);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepEnv, token, 0, null, omitHeader: true);
|
||||
@@ -153,15 +187,44 @@ 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)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepWith, token, 0, null, omitHeader: true);
|
||||
@@ -181,13 +244,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Int32 EvaluateStepTimeout(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(Int32?);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.NumberStepsContext, token, 0, null, omitHeader: true);
|
||||
@@ -207,13 +271,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public JobContainer EvaluateJobContainer(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(JobContainer);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Container, token, 0, null, omitHeader: true);
|
||||
@@ -233,13 +298,14 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
public Dictionary<String, String> EvaluateJobOutput(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.JobOutputs, token, 0, null, omitHeader: true);
|
||||
@@ -267,15 +333,53 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result;
|
||||
}
|
||||
|
||||
public Dictionary<String, String> EvaluateJobDefaultsRun(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(Dictionary<String, String>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.JobDefaultsRun, token, 0, null, omitHeader: true);
|
||||
context.Errors.Check();
|
||||
result = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
||||
var mapping = token.AssertMapping("defaults run");
|
||||
foreach (var pair in mapping)
|
||||
{
|
||||
// Literal key
|
||||
var key = pair.Key.AssertString("defaults run key");
|
||||
|
||||
// Literal value
|
||||
var value = pair.Value.AssertString("defaults run value");
|
||||
result[key.Value] = value.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||
{
|
||||
context.Errors.Add(ex);
|
||||
}
|
||||
|
||||
context.Errors.Check();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IList<KeyValuePair<String, JobContainer>> EvaluateJobServiceContainers(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData)
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions)
|
||||
{
|
||||
var result = default(List<KeyValuePair<String, JobContainer>>);
|
||||
|
||||
if (token != null && token.Type != TokenType.Null)
|
||||
{
|
||||
var context = CreateContext(contextData);
|
||||
var context = CreateContext(contextData, expressionFunctions);
|
||||
try
|
||||
{
|
||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Services, token, 0, null, omitHeader: true);
|
||||
@@ -293,62 +397,10 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result;
|
||||
}
|
||||
|
||||
public Boolean TryEvaluateStepDisplayName(
|
||||
TemplateToken token,
|
||||
private TemplateContext CreateContext(
|
||||
DictionaryContextData contextData,
|
||||
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)
|
||||
IList<IFunctionInfo> expressionFunctions,
|
||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
|
||||
{
|
||||
var result = new TemplateContext
|
||||
{
|
||||
@@ -371,7 +423,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
}
|
||||
|
||||
// Add named context
|
||||
// Add named values
|
||||
if (contextData != null)
|
||||
{
|
||||
foreach (var pair in contextData)
|
||||
@@ -380,14 +432,46 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
}
|
||||
|
||||
// Compat for new agent against old server
|
||||
foreach (var name in s_contextNames)
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -395,9 +479,10 @@ 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_contextNames = new[]
|
||||
private readonly String[] s_expressionValueNames = new[]
|
||||
{
|
||||
PipelineTemplateConstants.GitHub,
|
||||
PipelineTemplateConstants.Needs,
|
||||
PipelineTemplateConstants.Strategy,
|
||||
PipelineTemplateConstants.Matrix,
|
||||
PipelineTemplateConstants.Needs,
|
||||
@@ -408,5 +493,13 @@ 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,14 +2,18 @@
|
||||
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 sealed class PipelineTemplateSchemaFactory
|
||||
public static class PipelineTemplateSchemaFactory
|
||||
{
|
||||
public TemplateSchema CreateSchema()
|
||||
public static TemplateSchema GetSchema()
|
||||
{
|
||||
if (s_schema == null)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var json = default(String);
|
||||
@@ -20,7 +24,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
|
||||
var objectReader = new JsonObjectReader(null, json);
|
||||
return TemplateSchema.Load(objectReader);
|
||||
}
|
||||
var schema = TemplateSchema.Load(objectReader);
|
||||
Interlocked.CompareExchange(ref s_schema, schema, null);
|
||||
}
|
||||
|
||||
return s_schema;
|
||||
}
|
||||
|
||||
private static TemplateSchema s_schema;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"properties": {
|
||||
"on": "any",
|
||||
"name": "string",
|
||||
"defaults": "workflow-defaults",
|
||||
"env": "workflow-env",
|
||||
"jobs": "jobs"
|
||||
}
|
||||
@@ -37,8 +38,8 @@
|
||||
"steps-scope-input-value": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"secrets",
|
||||
"steps",
|
||||
@@ -65,9 +66,9 @@
|
||||
"steps-scope-output-value": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
@@ -90,9 +91,9 @@
|
||||
"description": "Default input values for a steps template",
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"one-of": [
|
||||
"string",
|
||||
@@ -113,9 +114,9 @@
|
||||
"description": "Output values for a steps template",
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -125,6 +126,23 @@
|
||||
"string": {}
|
||||
},
|
||||
|
||||
"workflow-defaults": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"run": "workflow-defaults-run"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"workflow-defaults-run": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"shell": "non-empty-string",
|
||||
"working-directory": "non-empty-string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"workflow-env": {
|
||||
"context": [
|
||||
"github",
|
||||
@@ -161,6 +179,7 @@
|
||||
"services": "services",
|
||||
"env": "job-env",
|
||||
"outputs": "job-outputs",
|
||||
"defaults": "job-defaults",
|
||||
"steps": "steps"
|
||||
}
|
||||
}
|
||||
@@ -185,6 +204,25 @@
|
||||
"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",
|
||||
@@ -253,9 +291,9 @@
|
||||
"runs-on": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"one-of": [
|
||||
"non-empty-string",
|
||||
@@ -278,10 +316,10 @@
|
||||
"job-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"secrets",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"secrets"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -289,6 +327,30 @@
|
||||
}
|
||||
},
|
||||
|
||||
"job-defaults": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"run": "job-defaults-run"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"job-defaults-run": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"env"
|
||||
],
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"shell": "non-empty-string",
|
||||
"working-directory": "non-empty-string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"job-outputs": {
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -401,9 +463,9 @@
|
||||
"step-if": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
@@ -411,7 +473,8 @@
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)"
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -419,9 +482,9 @@
|
||||
"step-if-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
@@ -430,11 +493,63 @@
|
||||
"always(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)",
|
||||
"success(0,0)"
|
||||
"success(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"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": {
|
||||
@@ -458,9 +573,9 @@
|
||||
"steps-template-reference-inputs": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -476,9 +591,9 @@
|
||||
"steps-template-reference-inputs-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
@@ -495,14 +610,15 @@
|
||||
"step-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -513,15 +629,16 @@
|
||||
"step-env-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -532,14 +649,35 @@
|
||||
"step-with": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"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)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -550,9 +688,9 @@
|
||||
"container": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"one-of": [
|
||||
"string",
|
||||
@@ -575,9 +713,9 @@
|
||||
"services": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
@@ -588,9 +726,9 @@
|
||||
"services-container": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"one-of": [
|
||||
"non-empty-string",
|
||||
@@ -605,25 +743,6 @@
|
||||
}
|
||||
},
|
||||
|
||||
"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
|
||||
@@ -639,9 +758,9 @@
|
||||
"boolean-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -649,9 +768,9 @@
|
||||
"number-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -659,9 +778,9 @@
|
||||
"string-strategy-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs"
|
||||
"matrix"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -669,14 +788,15 @@
|
||||
"boolean-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -684,15 +804,16 @@
|
||||
"boolean-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"boolean": {}
|
||||
},
|
||||
@@ -700,14 +821,15 @@
|
||||
"number-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -715,15 +837,16 @@
|
||||
"number-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"number": {}
|
||||
},
|
||||
@@ -731,9 +854,9 @@
|
||||
"string-runner-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
@@ -746,14 +869,15 @@
|
||||
"string-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
@@ -761,15 +885,16 @@
|
||||
"string-steps-context-in-template": {
|
||||
"context": [
|
||||
"github",
|
||||
"needs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"needs",
|
||||
"secrets",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env"
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||
TimelineReference timeline = null;
|
||||
Guid jobId = Guid.NewGuid();
|
||||
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", 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);
|
||||
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", 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);
|
||||
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||
TimelineReference timeline = null;
|
||||
Guid jobId = Guid.NewGuid();
|
||||
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", 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);
|
||||
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", 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);
|
||||
}
|
||||
|
||||
private JobCancelMessage CreateJobCancelMessage()
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
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);
|
||||
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,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker;
|
||||
@@ -1600,6 +1602,8 @@ 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,7 +1,9 @@
|
||||
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;
|
||||
@@ -533,26 +535,26 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
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();
|
||||
_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));
|
||||
|
||||
//Act
|
||||
var result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new StringToken(null, null, null, "defaultValue"), evaluateContext);
|
||||
var result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new StringToken(null, null, null, "defaultValue"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("defaultValue", result);
|
||||
|
||||
//Act
|
||||
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"), evaluateContext);
|
||||
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("refs/heads/master", result);
|
||||
@@ -575,6 +577,8 @@ 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,4 +1,5 @@
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
@@ -322,6 +323,7 @@ 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>()));
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
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);
|
||||
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,
|
||||
@@ -101,7 +101,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
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);
|
||||
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,
|
||||
@@ -152,7 +152,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
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);
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
namespace GitHub.Runner.Common.Tests.Worker.Expressions
|
||||
{
|
||||
public sealed class ExpressionManagerL0
|
||||
public sealed class ConditionFunctionsL0
|
||||
{
|
||||
private Mock<IExecutionContext> _ec;
|
||||
private ExpressionManager _expressionManager;
|
||||
private DictionaryContextData _expressions;
|
||||
private TemplateContext _templateContext;
|
||||
private JobContext _jobContext;
|
||||
|
||||
[Fact]
|
||||
@@ -38,7 +38,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "always()").Value;
|
||||
bool actual = Evaluate("always()");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -68,7 +68,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "cancelled()").Value;
|
||||
bool actual = Evaluate("cancelled()");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -97,7 +97,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
bool actual = _expressionManager.Evaluate(_ec.Object, "failure()").Value;
|
||||
bool actual = Evaluate("failure()");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -126,37 +126,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobContext.Status = variableSet.JobStatus;
|
||||
|
||||
// Act.
|
||||
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;
|
||||
bool actual = Evaluate("success()");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(variableSet.Expected, actual);
|
||||
@@ -166,21 +136,34 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
var hc = new TestHostContext(this, testName);
|
||||
_expressionManager = new ExpressionManager();
|
||||
_expressionManager.Initialize(hc);
|
||||
return hc;
|
||||
return new TestHostContext(this, testName);
|
||||
}
|
||||
|
||||
private void InitializeExecutionContext(TestHostContext hc)
|
||||
{
|
||||
_expressions = new DictionaryContextData();
|
||||
_jobContext = new JobContext();
|
||||
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.SetupAllProperties();
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_expressions);
|
||||
_ec.Setup(x => x.JobContext).Returns(_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ 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;
|
||||
|
||||
@@ -35,7 +34,6 @@ 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>();
|
||||
@@ -100,7 +98,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
Guid jobId = Guid.NewGuid();
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null);
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null, null);
|
||||
GitHubContext github = new GitHubContext();
|
||||
github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner");
|
||||
_message.ContextData.Add("github", github);
|
||||
@@ -108,7 +106,6 @@ 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);
|
||||
|
||||
@@ -53,9 +53,6 @@ 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);
|
||||
@@ -63,7 +60,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||
TimelineReference timeline = new Timeline(Guid.NewGuid());
|
||||
Guid jobId = Guid.NewGuid();
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, 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);
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, 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);
|
||||
_message.Variables[Constants.Variables.System.Culture] = "en-US";
|
||||
_message.Resources.Endpoints.Add(new ServiceEndpoint()
|
||||
{
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using Moq;
|
||||
using System;
|
||||
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,9 +27,6 @@ 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,
|
||||
@@ -49,6 +46,7 @@ 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();
|
||||
@@ -383,16 +381,11 @@ 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, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "fromJson('not json')") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "fromJson('not json')") },
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -610,6 +603,7 @@ 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);
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
new Pipelines.ContextData.DictionaryContextData()
|
||||
},
|
||||
};
|
||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, new StringToken(null, null, null, "ubuntu"), sidecarContainers, null, variables, new List<MaskHint>(), resources, context, null, actions, null, null, null);
|
||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, new StringToken(null, null, null, "ubuntu"), sidecarContainers, null, variables, new List<MaskHint>(), resources, context, null, actions, null, null, null, null);
|
||||
return jobRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.165.2
|
||||
2.168.0
|
||||
|
||||
Reference in New Issue
Block a user