mirror of
https://github.com/actions/runner.git
synced 2025-12-16 15:06:49 +00:00
switch hashFiles to extension function (#362)
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user