GitHub Actions Runner

This commit is contained in:
Tingluo Huang
2019-10-10 00:52:42 -04:00
commit c8afc84840
1255 changed files with 198670 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class CounterNode : FunctionNode
{
protected override Object EvaluateCore(EvaluationContext evaluationContext)
{
int seed = 0;
var prefix = String.Empty;
if (Parameters.Count > 0)
{
prefix = Parameters[0].EvaluateString(evaluationContext);
}
if (Parameters.Count > 1)
{
seed = Convert.ToInt32(Parameters[1].EvaluateNumber(evaluationContext));
}
var context = evaluationContext.State as IPipelineContext;
return context.CounterStore?.Increment(context, prefix, seed) ?? seed;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ExpressionConstants
{
/// <summary>
/// Gets the name of the variables node.
/// </summary>
public static readonly String Variables = "variables";
/// <summary>
/// Gets the pipeline context available in pipeline expressions.
/// </summary>
public static readonly INamedValueInfo PipelineNamedValue = new NamedValueInfo<PipelineContextNode>("pipeline");
/// <summary>
/// Gets the variable context available in pipeline expressions.
/// </summary>
public static readonly INamedValueInfo VariablesNamedValue = new NamedValueInfo<VariablesContextNode>("variables");
/// <summary>
/// Gets the counter function available in pipeline expressions.
/// </summary>
public static readonly IFunctionInfo CounterFunction = new FunctionInfo<CounterNode>("counter", 0, 2);
}
}

View File

@@ -0,0 +1,32 @@
using System;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
internal static class InputValidationConstants
{
public static readonly String IsEmail = "isEmail";
public static readonly String IsInRange = "isInRange";
public static readonly String IsIPv4Address = "isIPv4Address";
public static readonly String IsSha1 = "isSha1";
public static readonly String IsUrl = "isUrl";
public static readonly String IsMatch = "isMatch";
public static readonly String Length = "length";
public static readonly IFunctionInfo[] Functions = new IFunctionInfo[]
{
new FunctionInfo<IsEmailNode>(InputValidationConstants.IsEmail, IsEmailNode.minParameters, IsEmailNode.maxParameters),
new FunctionInfo<IsInRangeNode>(InputValidationConstants.IsInRange, IsInRangeNode.minParameters, IsInRangeNode.maxParameters),
new FunctionInfo<IsIPv4AddressNode>(InputValidationConstants.IsIPv4Address, IsIPv4AddressNode.minParameters, IsIPv4AddressNode.maxParameters),
new FunctionInfo<IsMatchNode>(InputValidationConstants.IsMatch, IsMatchNode.minParameters, IsMatchNode.maxParameters),
new FunctionInfo<IsSHA1Node>(InputValidationConstants.IsSha1, IsSHA1Node.minParameters, IsSHA1Node.maxParameters),
new FunctionInfo<IsUrlNode>(InputValidationConstants.IsUrl, IsUrlNode.minParameters, IsUrlNode.maxParameters),
new FunctionInfo<LengthNode>(InputValidationConstants.Length, LengthNode.minParameters, LengthNode.maxParameters),
};
public static readonly INamedValueInfo[] NamedValues = new INamedValueInfo[]
{
new NamedValueInfo<InputValueNode>("value"),
};
}
}

View File

@@ -0,0 +1,15 @@
using System;
using GitHub.DistributedTask.Expressions;
using GitHub.DistributedTask.Pipelines.Validation;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
internal class InputValueNode : NamedValueNode
{
protected sealed override Object EvaluateCore(EvaluationContext evaluationContext)
{
var validationContext = evaluationContext.State as InputValidationContext;
return validationContext.Value;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsEmailNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 1;
public static Int32 maxParameters = 1;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isEmail(value: string)
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.Email);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsIPv4AddressNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 1;
public static Int32 maxParameters = 1;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isIpV4Address(value: string)
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.IPv4Address);
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsInRangeNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 3;
public static Int32 maxParameters = 3;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isInRange(value: string, min: string, max: string)
decimal value = Parameters[0].EvaluateNumber(context);
decimal min = Parameters[1].EvaluateNumber(context);
decimal max = Parameters[2].EvaluateNumber(context);
return value >= min && value <= max;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsMatchNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 2;
public static Int32 maxParameters = 3;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isMatch(value: string, regEx: string, options?: string)
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
String regEx = Parameters[1].EvaluateString(context) ?? String.Empty;
String regExOptionsString = String.Empty;
if (Parameters.Count == 3)
{
regExOptionsString = Parameters[2].EvaluateString(context) ?? String.Empty;
}
return RegexUtility.IsMatch(value, regEx, regExOptionsString);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsSHA1Node : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 1;
public static Int32 maxParameters = 1;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isSha1(value: string)
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.SHA1);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class IsUrlNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 1;
public static Int32 maxParameters = 1;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// isUrl(value: string)
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.Url);
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class LengthNode : FunctionNode
{
protected sealed override Boolean TraceFullyRealized => false;
public static Int32 minParameters = 1;
public static Int32 maxParameters = 1;
protected sealed override Object EvaluateCore(EvaluationContext context)
{
// Length(value: object)
var evaluationResult = Parameters[0].Evaluate(context);
bool kindNotSupported = false;
Int32 length = -1;
switch (evaluationResult.Kind)
{
case ValueKind.Array:
length = ((JArray)evaluationResult.Value).Count;
break;
case ValueKind.String:
length = ((String)evaluationResult.Value).Length;
break;
case ValueKind.Object:
if (evaluationResult.Value is IReadOnlyDictionary<String, Object>)
{
length = ((IReadOnlyDictionary<String, Object>)evaluationResult.Value).Count;
}
else if (evaluationResult.Value is ICollection)
{
length = ((ICollection)evaluationResult.Value).Count;
}
else
{
kindNotSupported = true;
}
break;
case ValueKind.Boolean:
case ValueKind.Null:
case ValueKind.Number:
case ValueKind.Version:
kindNotSupported = true;
break;
}
if (kindNotSupported)
{
throw new NotSupportedException(PipelineStrings.InvalidTypeForLengthFunction(evaluationResult.Kind));
}
return new Decimal(length);
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using GitHub.DistributedTask.Expressions;
using GitHub.DistributedTask.WebApi;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
internal sealed class PipelineContextNode : NamedValueNode
{
protected override Object EvaluateCore(EvaluationContext context)
{
var state = context.State as IPipelineContext;
var result = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
// startTime
if (state.Variables.TryGetValue(WellKnownDistributedTaskVariables.PipelineStartTime, out VariableValue startTimeVariable) &&
!String.IsNullOrEmpty(startTimeVariable.Value))
{
// Leverage the expression SDK to convert to datetime
var startTimeResult = EvaluationResult.CreateIntermediateResult(context, startTimeVariable.Value, out _);
if (startTimeResult.TryConvertToDateTime(context, out DateTimeOffset startTime))
{
result["startTime"] = startTime;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,153 @@
using System;
using System.Text.RegularExpressions;
using GitHub.DistributedTask.WebApi;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
public static class RegexUtility
{
/// <summary>
/// Gets default timeout for regex
/// </summary>
/// <returns></returns>
public static TimeSpan GetRegexTimeOut()
{
return s_regexTimeout;
}
/// <summary>
/// Performs regex single match with ECMAScript-complaint behavior
/// Will throw RegularExpressionFailureException if regular expression parsing error occurs or if regular expression takes more than allotted time to execute
/// Supported regex options - 'i' (ignorecase), 'm' (multiline)
/// </summary>
/// <param name="value"></param>
/// <param name="regex"></param>
/// <param name="regexOptionsString"></param>
/// <returns></returns>
public static bool IsMatch(
String value,
String regexPattern,
String regexOptionsString)
{
return IsSafeMatch(value, regexPattern, ConvertToRegexOptions(regexOptionsString));
}
/// <summary>
/// Performs regex single match with ECMAScript-complaint behavior
/// Will throw RegularExpressionFailureException if regular expression parsing error occurs or if regular expression takes more than allotted time to execute
/// If the key is not known, returns true
/// </summary>
/// <param name="value"></param>
/// <param name="wellKnownRegexKey">One of WellKnownRegularExpressionKeys</param>
/// <returns></returns>
public static bool IsMatch(
String value,
String wellKnownRegexKey)
{
Lazy<Regex> lazyRegex = WellKnownRegularExpressions.GetRegex(wellKnownRegexKey);
if (lazyRegex == null)
{
return true;
}
Regex regex = lazyRegex.Value;
return IsSafeMatch(value, x => regex.Match(value));
}
/// <summary>
/// Converts regex in string to RegExOptions, valid flags are "i", "m"
/// Throws RegularExpressionInvalidOptionsException if there are any invalid options
/// </summary>
/// <param name="regexOptions"></param>
/// <returns></returns>
public static RegexOptions ConvertToRegexOptions(String regexOptions)
{
RegexOptions result;
if (TryConvertToRegexOptions(regexOptions, out result))
{
return result;
}
throw new RegularExpressionInvalidOptionsException(PipelineStrings.InvalidRegexOptions(regexOptions, String.Join(",", WellKnownRegexOptions.All)));
}
private static bool TryConvertToRegexOptions(
String regexOptions,
out RegexOptions result)
{
// Eg: "IgnoreCase, MultiLine" or "IgnoreCase"
result = RegexOptions.ECMAScript | RegexOptions.CultureInvariant;
if (String.IsNullOrEmpty(regexOptions))
{
return false;
}
String[] regexOptionValues = regexOptions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < regexOptionValues.Length; i++)
{
String option = regexOptionValues[i];
if (String.Equals(option, WellKnownRegexOptions.IgnoreCase, StringComparison.OrdinalIgnoreCase))
{
result = result | RegexOptions.IgnoreCase;
}
else if (String.Equals(option, WellKnownRegexOptions.Multiline, StringComparison.OrdinalIgnoreCase))
{
result = result | RegexOptions.Multiline;
}
else
{
return false;
}
}
return true;
}
private static Boolean IsSafeMatch(
String value,
Func<String, Match> getSafeMatch)
{
Boolean result = true;
try
{
var match = getSafeMatch(value);
result = match.Success;
}
catch (Exception ex) when (ex is RegexMatchTimeoutException || ex is ArgumentException)
{
throw new RegularExpressionValidationFailureException(PipelineStrings.RegexFailed(value, ex.Message), ex);
}
return result;
}
private static Boolean IsSafeMatch(
String value,
String regex,
RegexOptions regexOptions)
{
return IsSafeMatch(value, x => GetSafeMatch(x, regex, regexOptions));
}
private static Match GetSafeMatch(
String value,
String regex,
RegexOptions regexOptions)
{
return Regex.Match(value, regex, regexOptions, s_regexTimeout);
}
// 2 seconds should be enough mostly, per DataAnnotations class - http://index/?query=REGEX_DEFAULT_MATCH_TIMEOUT
private static TimeSpan s_regexTimeout = TimeSpan.FromSeconds(2);
private static class WellKnownRegexOptions
{
public static String IgnoreCase = nameof(IgnoreCase);
public static String Multiline = nameof(Multiline);
public static String[] All = new String[] { IgnoreCase, Multiline };
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.Expressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class VariablesContextNode : NamedValueNode
{
protected override Object EvaluateCore(EvaluationContext context)
{
var executionContext = context.State as IPipelineContext;
return executionContext.Variables;
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Text.RegularExpressions;
namespace GitHub.DistributedTask.Pipelines.Expressions
{
public static class WellKnownRegularExpressions
{
public const String Email = nameof(Email);
public const String IPv4Address = nameof(IPv4Address);
public const String SHA1 = nameof(SHA1);
public const String Url = nameof(Url);
/// <summary>
/// Returns null if it's not a well-known type
/// </summary>
/// <param name="regexType"></param>
/// <returns></returns>
public static Lazy<Regex> GetRegex(String regexType)
{
switch (regexType)
{
case Email:
return s_validEmail;
case IPv4Address:
return s_validIPv4Address;
case SHA1:
return s_validSha1;
case Url:
return s_validUrl;
default:
return null;
}
}
// regex from http://index/?leftProject=System.ComponentModel.DataAnnotations&leftSymbol=cmnlm5e7vdio&file=DataAnnotations%5CEmailAddressAttribute.cs&rightSymbol=jfeiathypuap
private static readonly Lazy<Regex> s_validEmail = new Lazy<Regex>(() => new Regex(
@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
)
);
// simple check - {1 to 3 digits}.{1 to 3 digits}.{1 to 3 digits}.{1 to 3 digits}
private static readonly Lazy<Regex> s_validIPv4Address = new Lazy<Regex>(() => new Regex(
@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
)
);
// 40 hex characters
private static readonly Lazy<Regex> s_validSha1 = new Lazy<Regex>(() => new Regex(
@"\b[0-9a-f]{40}\b",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
)
);
// regex from http://index/?leftProject=System.ComponentModel.DataAnnotations&leftSymbol=gk29yrysvq6y&file=DataAnnotations%5CUrlAttribute.cs&line=11
private static readonly Lazy<Regex> s_validUrl = new Lazy<Regex>(() => new Regex(
@"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
)
);
}
}