mirror of
https://github.com/actions/runner.git
synced 2025-12-16 23:20:03 +00:00
987 lines
39 KiB
C#
987 lines
39 KiB
C#
#nullable disable // Consider removing in the future to minimize likelihood of NullReferenceException; refer https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using GitHub.Actions.Expressions;
|
|
using GitHub.Actions.Expressions.Data;
|
|
using GitHub.Actions.Expressions.Sdk.Functions;
|
|
using GitHub.Actions.WorkflowParser.Conversion;
|
|
using GitHub.Actions.WorkflowParser.ObjectTemplating;
|
|
using GitHub.Actions.WorkflowParser.ObjectTemplating.Schema;
|
|
using GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens;
|
|
using ITraceWriter = GitHub.Actions.WorkflowParser.ObjectTemplating.ITraceWriter;
|
|
|
|
namespace GitHub.Actions.WorkflowParser
|
|
{
|
|
/// <summary>
|
|
/// Evaluates parts of the workflow DOM. For example, a job strategy or step inputs.
|
|
/// </summary>
|
|
public class WorkflowTemplateEvaluator
|
|
{
|
|
/// <summary>
|
|
/// Creates a new instance for evaluating tokens within a workflow template.
|
|
/// </summary>
|
|
/// <param name="trace">Optional trace writer for telemetry</param>
|
|
/// <param name="fileTable">Optional file table from the workflow template, for better error messages</param>
|
|
/// <param name="features">Optional workflow features</param>
|
|
public WorkflowTemplateEvaluator(
|
|
ITraceWriter trace,
|
|
IList<String> fileTable,
|
|
WorkflowFeatures features)
|
|
{
|
|
m_trace = trace ?? new EmptyTraceWriter();
|
|
m_fileTable = fileTable;
|
|
m_features = features ?? WorkflowFeatures.GetDefaults();
|
|
m_schema = WorkflowSchemaFactory.GetSchema(m_features);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new instance for evaluating tokens within a workflow template.
|
|
/// </summary>
|
|
/// <param name="trace">Optional trace writer for telemetry</param>
|
|
/// <param name="fileTable">Optional file table from the workflow template, for better error messages</param>
|
|
/// <param name="features">Optional workflow features</param>
|
|
/// <param name="parentMemory">Optional parent memory counter, for byte tracking across evaluation calls.</param>
|
|
public WorkflowTemplateEvaluator(
|
|
ITraceWriter trace,
|
|
IList<String> fileTable,
|
|
WorkflowFeatures features,
|
|
TemplateMemory parentMemory)
|
|
{
|
|
m_trace = trace ?? new EmptyTraceWriter();
|
|
m_fileTable = fileTable;
|
|
m_features = features ?? WorkflowFeatures.GetDefaults();
|
|
m_schema = WorkflowSchemaFactory.GetSchema(m_features);
|
|
m_parentMemory = parentMemory;
|
|
}
|
|
|
|
public Int32 MaxDepth => 50;
|
|
|
|
/// <summary>
|
|
/// Gets the maximum error message length before the message will be truncated.
|
|
/// </summary>
|
|
public Int32 MaxErrorMessageLength { get; set; } = 500;
|
|
|
|
/// <summary>
|
|
/// Gets the maximum number of errors that can be recorded when parsing a workflow.
|
|
/// </summary>
|
|
public Int32 MaxErrors => 10;
|
|
|
|
public Int32 MaxEvents => 1000000; // 1 million
|
|
|
|
public Int32 MaxResultSize { get; set; } = 10 * 1024 * 1024; // 10 mb
|
|
|
|
public Boolean EvaluateStageIf(
|
|
String stageId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
IEnumerable<KeyValuePair<String, Object>> expressionState)
|
|
{
|
|
var result = default(Boolean?);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.If}' for stage '{stageId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions, expressionState);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.JobIfResult, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToIfResult(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? throw new InvalidOperationException("Stage if cannot be null");
|
|
}
|
|
|
|
public Boolean EvaluateJobIf(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
IEnumerable<KeyValuePair<String, Object>> expressionState)
|
|
{
|
|
var result = default(Boolean?);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.If}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions, expressionState);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.JobIfResult, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToIfResult(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? throw new InvalidOperationException("Job if cannot be null");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Evaluates a job strategy token
|
|
/// </summary>
|
|
/// <param name="jobName">The default job display name (any display name expression is evaluated after strategy)</param>
|
|
public Strategy EvaluateStrategy(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
String jobName)
|
|
{
|
|
var result = new Strategy();
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Strategy}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.Strategy, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToStrategy(context, token, jobName);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
if (result.Configurations.Count == 0)
|
|
{
|
|
var configuration = new StrategyConfiguration
|
|
{
|
|
Id = WorkflowConstants.DefaultJobName,
|
|
Name = new JobNameBuilder(jobName).Build(),
|
|
};
|
|
configuration.ExpressionData.Add(WorkflowTemplateConstants.Matrix, null);
|
|
configuration.ExpressionData.Add(
|
|
WorkflowTemplateConstants.Strategy,
|
|
new DictionaryExpressionData
|
|
{
|
|
{
|
|
"fail-fast",
|
|
new BooleanExpressionData(result.FailFast)
|
|
},
|
|
{
|
|
"job-index",
|
|
new NumberExpressionData(0)
|
|
},
|
|
{
|
|
"job-total",
|
|
new NumberExpressionData(1)
|
|
},
|
|
{
|
|
"max-parallel",
|
|
new NumberExpressionData(1)
|
|
}
|
|
});
|
|
result.Configurations.Add(configuration);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public String EvaluateJobName(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
String defaultName)
|
|
{
|
|
var result = default(String);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Name}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StringStrategyContext, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToJobName(context, token);
|
|
if (string.IsNullOrEmpty(result))
|
|
{
|
|
result = defaultName;
|
|
context.Memory.AddBytes(defaultName);
|
|
}
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public DictionaryExpressionData EvaluateWorkflowJobInputs(
|
|
ReusableWorkflowJob workflowJob,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var inputDefinitions = workflowJob.InputDefinitions;
|
|
var inputValues = workflowJob.InputValues;
|
|
var result = default(DictionaryExpressionData);
|
|
|
|
if (inputDefinitions != null && inputDefinitions.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
var inputDefinitionsToken = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.WorkflowCallInputs, inputDefinitions, 0, null);
|
|
context.Errors.Check();
|
|
var inputValuesToken = default(TemplateToken);
|
|
if (inputValues != null && inputValues.Type != TokenType.Null)
|
|
{
|
|
inputValuesToken = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.WorkflowJobWith, inputValues, 0, null);
|
|
context.Errors.Check();
|
|
}
|
|
result = WorkflowTemplateConverter.ConvertToWorkflowJobInputs(context, inputDefinitionsToken, inputValuesToken, workflowJob);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? new DictionaryExpressionData();
|
|
}
|
|
|
|
public IDictionary<String, String> EvaluateWorkflowJobOutputs(
|
|
MappingToken outputDefinitions,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(IDictionary<String, String>);
|
|
|
|
if (outputDefinitions != null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
var outputs = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.WorkflowCallOutputs, outputDefinitions, 0, null);
|
|
|
|
result = WorkflowTemplateConverter.ConvertToWorkflowJobOutputs(outputs);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public ActionsEnvironmentReference EvaluateJobEnvironment(
|
|
string jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(ActionsEnvironmentReference);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Environment}' for job '{jobId}'.";
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
// Set "addMissingContexts:false" because the environment contains some properties
|
|
// that are intended to be evaluated on the server, and others on the runner.
|
|
//
|
|
// For example:
|
|
// environment:
|
|
// name: ${{ this evaluates on the server }}
|
|
// url: ${{ this evaluates on the runner }}
|
|
var context = CreateContext(expressionData, expressionFunctions, addMissingContexts: false);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.JobEnvironment, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToActionEnvironmentReference(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public TemplateToken EvaluateJobEnvironmentUrl(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(TemplateToken);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StringRunnerContextNoSecrets, token, 0, null);
|
|
context.Errors.Check();
|
|
result = token.AssertString("environment.url");
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public GroupPermitSetting EvaluateConcurrency(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(GroupPermitSetting);
|
|
string type;
|
|
string errorPrefix;
|
|
if (String.IsNullOrEmpty(jobId))
|
|
{
|
|
type = WorkflowTemplateConstants.WorkflowConcurrency;
|
|
errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Concurrency}'.";
|
|
}
|
|
else
|
|
{
|
|
type = WorkflowTemplateConstants.JobConcurrency;
|
|
errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Concurrency}' for job '{jobId}'.";
|
|
}
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, type, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToConcurrency(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public RunsOn EvaluateRunsOn(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(RunsOn);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.RunsOn}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.RunsOn, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToRunsOn(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? throw new InvalidOperationException("Job target cannot be null");
|
|
}
|
|
|
|
public Snapshot EvaluateSnapshot(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Snapshot);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.Snapshot}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.Snapshot, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToSnapshot(context, token);
|
|
}
|
|
catch (Exception ex) when (ex is not TemplateValidationException)
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Int32 EvaluateJobTimeout(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Int32?);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.TimeoutMinutes}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.NumberStrategyContext, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToJobTimeout(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? WorkflowConstants.DefaultJobTimeoutInMinutes;
|
|
}
|
|
|
|
public Int32 EvaluateJobCancelTimeout(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Int32?);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.CancelTimeoutMinutes}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.NumberStrategyContext, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToJobCancelTimeout(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? WorkflowConstants.DefaultJobCancelTimeoutInMinutes;
|
|
}
|
|
|
|
public Boolean EvaluateJobContinueOnError(
|
|
String jobId,
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Boolean?);
|
|
var errorPrefix = $"Error when evaluating '{WorkflowTemplateConstants.ContinueOnError}' for job '{jobId}'.";
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.BooleanStrategyContext, token, 0, null);
|
|
context.Errors.Check(errorPrefix);
|
|
result = WorkflowTemplateConverter.ConvertToJobContinueOnError(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check(errorPrefix);
|
|
}
|
|
|
|
return result ?? false;
|
|
}
|
|
|
|
public Boolean EvaluateStepContinueOnError(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Boolean?);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.BooleanStepsContext, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToStepContinueOnError(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? false;
|
|
}
|
|
|
|
public String EvaluateStepName(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(String);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StringStepsContext, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToStepName(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Boolean EvaluateStepIf(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
IEnumerable<KeyValuePair<String, Object>> expressionState)
|
|
{
|
|
var result = default(Boolean?);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions, expressionState);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StepIfResult, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.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> EvaluateStepEnvironment(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
StringComparer keyComparer)
|
|
{
|
|
var result = default(Dictionary<String, String>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StepEnv, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToStepEnvironment(context, token, keyComparer);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? new Dictionary<String, String>(keyComparer);
|
|
}
|
|
|
|
public Dictionary<String, String> EvaluateStepInputs(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Dictionary<String, String>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.StepWith, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToStepInputs(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public Int32 EvaluateStepTimeout(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Int32?);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.NumberStepsContext, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToStepTimeout(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? 0;
|
|
}
|
|
|
|
public JobContainer EvaluateJobContainer(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(JobContainer);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.Container, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToJobContainer(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public IList<KeyValuePair<String, JobContainer>> EvaluateJobServiceContainers(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(List<KeyValuePair<String, JobContainer>>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.Services, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToJobServiceContainers(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Dictionary<String, String> EvaluateJobDefaultsRun(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Dictionary<String, String>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.JobDefaultsRun, token, 0, null);
|
|
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 Dictionary<String, String> EvaluateJobOutputs(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(Dictionary<String, String>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.JobOutputs, token, 0, null);
|
|
context.Errors.Check();
|
|
result = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
|
var mapping = token.AssertMapping("outputs");
|
|
foreach (var pair in mapping)
|
|
{
|
|
// Literal key
|
|
var key = pair.Key.AssertString("output key");
|
|
|
|
// Literal value
|
|
var value = pair.Value.AssertString("output value");
|
|
result[key.Value] = value.Value;
|
|
}
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public IDictionary<String, String> EvaluateWorkflowJobSecrets(
|
|
TemplateToken token,
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions)
|
|
{
|
|
var result = default(IDictionary<String, String>);
|
|
|
|
if (token != null && token.Type != TokenType.Null)
|
|
{
|
|
var context = CreateContext(expressionData, expressionFunctions);
|
|
try
|
|
{
|
|
token = TemplateEvaluator.Evaluate(context, WorkflowTemplateConstants.WorkflowJobSecrets, token, 0, null);
|
|
context.Errors.Check();
|
|
result = WorkflowTemplateConverter.ConvertToWorkflowJobSecrets(context, token);
|
|
}
|
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
{
|
|
context.Errors.Add(ex);
|
|
}
|
|
|
|
context.Errors.Check();
|
|
}
|
|
|
|
return result ?? new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private TemplateContext CreateContext(
|
|
DictionaryExpressionData expressionData,
|
|
IList<IFunctionInfo> expressionFunctions,
|
|
IEnumerable<KeyValuePair<String, Object>> expressionState = null,
|
|
Boolean addMissingContexts = true)
|
|
{
|
|
var result = new TemplateContext
|
|
{
|
|
CancellationToken = CancellationToken.None,
|
|
Errors = new TemplateValidationErrors(MaxErrors, MaxErrorMessageLength),
|
|
Memory = new TemplateMemory(
|
|
maxDepth: MaxDepth,
|
|
maxEvents: MaxEvents,
|
|
maxBytes: MaxResultSize,
|
|
parent: m_parentMemory),
|
|
Schema = m_schema,
|
|
StrictJsonParsing = m_features.StrictJsonParsing,
|
|
TraceWriter = m_trace,
|
|
};
|
|
result.SetFeatures(m_features);
|
|
|
|
// Add the file table
|
|
if (m_fileTable?.Count > 0)
|
|
{
|
|
foreach (var file in m_fileTable)
|
|
{
|
|
result.GetFileId(file);
|
|
}
|
|
}
|
|
|
|
// Add named values
|
|
if (expressionData != null)
|
|
{
|
|
foreach (var pair in expressionData)
|
|
{
|
|
result.ExpressionValues[pair.Key] = pair.Value;
|
|
}
|
|
}
|
|
|
|
// 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 name can often
|
|
// be performed early.
|
|
if (addMissingContexts)
|
|
{
|
|
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 vars context even if addMissingContexts is false to avoid
|
|
// JobEnvironment Evaluation errors
|
|
if(!result.ExpressionValues.ContainsKey(WorkflowTemplateConstants.Vars))
|
|
{
|
|
result.ExpressionValues[WorkflowTemplateConstants.Vars] = null;
|
|
}
|
|
|
|
// Add state
|
|
if (expressionState != null)
|
|
{
|
|
foreach (var pair in expressionState)
|
|
{
|
|
result.State[pair.Key] = pair.Value;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private readonly ITraceWriter m_trace;
|
|
private readonly TemplateSchema m_schema;
|
|
private readonly IList<String> m_fileTable;
|
|
private readonly WorkflowFeatures m_features;
|
|
private readonly TemplateMemory m_parentMemory;
|
|
private readonly String[] s_expressionValueNames = new[]
|
|
{
|
|
WorkflowTemplateConstants.GitHub,
|
|
WorkflowTemplateConstants.Needs,
|
|
WorkflowTemplateConstants.Strategy,
|
|
WorkflowTemplateConstants.Matrix,
|
|
WorkflowTemplateConstants.Secrets,
|
|
WorkflowTemplateConstants.Vars,
|
|
WorkflowTemplateConstants.Steps,
|
|
WorkflowTemplateConstants.Inputs,
|
|
WorkflowTemplateConstants.Jobs,
|
|
WorkflowTemplateConstants.Job,
|
|
WorkflowTemplateConstants.Runner,
|
|
WorkflowTemplateConstants.Env,
|
|
};
|
|
private readonly String[] s_expressionFunctionNames = new[]
|
|
{
|
|
WorkflowTemplateConstants.Always,
|
|
WorkflowTemplateConstants.Cancelled,
|
|
WorkflowTemplateConstants.Failure,
|
|
WorkflowTemplateConstants.HashFiles,
|
|
WorkflowTemplateConstants.Success,
|
|
};
|
|
}
|
|
}
|