#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 { /// /// Evaluates parts of the workflow DOM. For example, a job strategy or step inputs. /// public class WorkflowTemplateEvaluator { /// /// Creates a new instance for evaluating tokens within a workflow template. /// /// Optional trace writer for telemetry /// Optional file table from the workflow template, for better error messages /// Optional workflow features public WorkflowTemplateEvaluator( ITraceWriter trace, IList fileTable, WorkflowFeatures features) { m_trace = trace ?? new EmptyTraceWriter(); m_fileTable = fileTable; m_features = features ?? WorkflowFeatures.GetDefaults(); m_schema = WorkflowSchemaFactory.GetSchema(m_features); } /// /// Creates a new instance for evaluating tokens within a workflow template. /// /// Optional trace writer for telemetry /// Optional file table from the workflow template, for better error messages /// Optional workflow features /// Optional parent memory counter, for byte tracking across evaluation calls. public WorkflowTemplateEvaluator( ITraceWriter trace, IList 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; /// /// Gets the maximum error message length before the message will be truncated. /// public Int32 MaxErrorMessageLength { get; set; } = 500; /// /// Gets the maximum number of errors that can be recorded when parsing a workflow. /// 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 expressionFunctions, IEnumerable> 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 expressionFunctions, IEnumerable> 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"); } /// /// Evaluates a job strategy token /// /// The default job display name (any display name expression is evaluated after strategy) public Strategy EvaluateStrategy( String jobId, TemplateToken token, DictionaryExpressionData expressionData, IList 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 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 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 EvaluateWorkflowJobOutputs( MappingToken outputDefinitions, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(IDictionary); 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(StringComparer.OrdinalIgnoreCase); } public ActionsEnvironmentReference EvaluateJobEnvironment( string jobId, TemplateToken token, DictionaryExpressionData expressionData, IList 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 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 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 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 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 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 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 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 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 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 expressionFunctions, IEnumerable> 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 EvaluateStepEnvironment( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions, StringComparer keyComparer) { var result = default(Dictionary); 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(keyComparer); } public Dictionary EvaluateStepInputs( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(Dictionary); 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(StringComparer.OrdinalIgnoreCase); } public Int32 EvaluateStepTimeout( TemplateToken token, DictionaryExpressionData expressionData, IList 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 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> EvaluateJobServiceContainers( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(List>); 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 EvaluateJobDefaultsRun( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(Dictionary); 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(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 EvaluateJobOutputs( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(Dictionary); 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(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 EvaluateWorkflowJobSecrets( TemplateToken token, DictionaryExpressionData expressionData, IList expressionFunctions) { var result = default(IDictionary); 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(StringComparer.OrdinalIgnoreCase); } private TemplateContext CreateContext( DictionaryExpressionData expressionData, IList expressionFunctions, IEnumerable> 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(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(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 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, }; } }