* Context refactoring

* Clean up

* Fix linting
This commit is contained in:
Bernie White 2024-11-20 17:11:35 +10:00 коммит произвёл GitHub
Родитель 1b77ed73bf
Коммит 697c89fd6f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 134 добавлений и 86 удалений

Просмотреть файл

@ -614,49 +614,50 @@
},
"NuGet.Common": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "6ouw0UC3TGaFHNJyoEK2/Q5jSryRHzcbKGv9C0+t/TPnTP8PoLqnyFxO1dwmQUmJkWuKAUo6Vu0kIXHY/8LyKQ==",
"resolved": "6.12.1",
"contentHash": "nk8nTdhQl4x2VaAQUvefI7DDYAuBDlE+OZZRffm50Qx5dUAEq8wkc5JIqrN2lTEohObHPI/SXyG2UFdMQkrdyg==",
"dependencies": {
"NuGet.Frameworks": "6.11.1"
"NuGet.Frameworks": "6.12.1"
}
},
"NuGet.Configuration": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "vbEe2acKrI2QmNx9U94ewhgUt6cLpwBeQfMtrie6NMz+GkJcX/4qIkKsX3SeBO4gFgCf8eeTyURl5hxPtiUctw==",
"resolved": "6.12.1",
"contentHash": "IRwlY1379ZgJ0oEJvjD+lDuOhJ5S1fsU5n/bEC5/i0+N9bo2WIMDAdaQ/qIdyK/gMJ/YWS+++GSX6rN7luqEvg==",
"dependencies": {
"NuGet.Common": "6.11.1",
"NuGet.Common": "6.12.1",
"System.Security.Cryptography.ProtectedData": "4.4.0"
}
},
"NuGet.Frameworks": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg=="
"resolved": "6.12.1",
"contentHash": "kPaRD5RJC0ByUg+yGX6bDz5XHMI7OYmQwP8kbtef+vZ+csj/VDb5Bwas4ChxwhoAbI8lEvwP5/3aViQPpgNBow=="
},
"NuGet.Packaging": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "wiofIUUr7khwuaGXiOibMb7+dEkF97EVsAmzlaNc188HV9ujjqweQMuVCoAK2/MqXdhnrKjvicUfKo9CPsNnfg==",
"resolved": "6.12.1",
"contentHash": "6s5NO3VNX6fIx6GwuWZtIsal9W1xkelYd3Vg2KUAg1zGqnKC3wB5IZlombvVGVGcwyl/A+iDvpUwSvgeDoB3wA==",
"dependencies": {
"Newtonsoft.Json": "13.0.3",
"NuGet.Configuration": "6.11.1",
"NuGet.Versioning": "6.11.1",
"NuGet.Configuration": "6.12.1",
"NuGet.Versioning": "6.12.1",
"System.Formats.Asn1": "8.0.1",
"System.Security.Cryptography.Pkcs": "6.0.4"
}
},
"NuGet.Protocol": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "WkYlSuNHNt/j1tbHp/xjvwk2EsIdSM3raEjk3EfIFd62ER1+x4eC8/J1VKqnve6cTupF4LsuwD3Z4YCumnfCXw==",
"resolved": "6.12.1",
"contentHash": "VBN7OtG/Y9Rnj1WT3G8X88ZHu5Pq+yzca5Z6OI/FWXcENVAQkUl0ml6Cv8ghOqYyiuvnObGDV9oWLD/bIuVtDw==",
"dependencies": {
"NuGet.Packaging": "6.11.1"
"NuGet.Packaging": "6.12.1"
}
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "6.11.1",
"contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg=="
"resolved": "6.12.1",
"contentHash": "fJ6rFYANDnohFsdpaY79FvrJxI6murmoOxXz6nZlf819F48+IBKMnAIg3oIBRtZq5y498ObMtKnro5IitvizUg=="
},
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
"type": "Transitive",
@ -1646,7 +1647,7 @@
"dependencies": {
"Microsoft.PSRule.SDK": "[0.0.1, )",
"Microsoft.PowerShell.SDK": "[7.4.6, )",
"NuGet.Protocol": "[6.11.1, )",
"NuGet.Protocol": "[6.12.1, )",
"System.CommandLine": "[2.0.0-beta4.22272.1, )"
}
},

Просмотреть файл

@ -70,7 +70,7 @@ internal sealed class ExportConventionCommand : LanguageBlock
var context = RunspaceContext.CurrentThread;
if (context == null) return;
var source = context!.Source!.File;
var source = context.Source;
var errorPreference = GetErrorActionPreference();
var commentMetadata = GetCommentMetadata(source, MyInvocation.ScriptLineNumber, MyInvocation.OffsetInLine);
var metadata = new ResourceMetadata

Просмотреть файл

@ -108,7 +108,7 @@ internal sealed class NewRuleDefinitionCommand : LanguageBlock
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordSourceScope, LanguageKeywords.Rule));
var context = RunspaceContext.CurrentThread;
var source = context.Source.File;
var source = context.Source;
var errorPreference = GetErrorActionPreference();
var metadata = GetCommentMetadata(source, MyInvocation.ScriptLineNumber, MyInvocation.OffsetInLine);
var level = ResourceHelper.GetLevel(Level);

Просмотреть файл

@ -353,7 +353,7 @@ internal sealed class OrderedPropertiesContractResolver : DefaultContractResolve
/// </summary>
internal sealed class ResourceObjectJsonConverter : JsonConverter
{
private const string FIELD_APIVERSION = "apiVersion";
private const string FIELD_API_VERSION = "apiVersion";
private const string FIELD_KIND = "kind";
private const string FIELD_METADATA = "metadata";
private const string FIELD_SPEC = "spec";
@ -363,10 +363,12 @@ internal sealed class ResourceObjectJsonConverter : JsonConverter
public override bool CanWrite => false;
private readonly IResourceDiscoveryContext _Context;
private readonly SpecFactory _Factory;
public ResourceObjectJsonConverter()
public ResourceObjectJsonConverter(IResourceDiscoveryContext context)
{
_Context = context;
_Factory = new SpecFactory();
}
@ -388,7 +390,7 @@ internal sealed class ResourceObjectJsonConverter : JsonConverter
private IResource MapResource(JsonReader reader, JsonSerializer serializer)
{
reader.GetSourceExtent(RunspaceContext.CurrentThread.Source.File, out var extent);
reader.GetSourceExtent(_Context.Source, out var extent);
reader.SkipComments(out _);
if (reader.TokenType != JsonToken.StartObject || !reader.Read())
throw new PipelineSerializationException(PSRuleResources.ReadJsonFailed);
@ -471,7 +473,7 @@ internal sealed class ResourceObjectJsonConverter : JsonConverter
private static bool TryApiVersion(JsonReader reader, string propertyName, out string apiVersion)
{
apiVersion = null;
if (propertyName == FIELD_APIVERSION)
if (propertyName == FIELD_API_VERSION)
{
apiVersion = reader.ReadAsString();
return true;
@ -523,7 +525,7 @@ internal sealed class ResourceObjectJsonConverter : JsonConverter
reader.SkipComments(out _);
var deserializedSpec = serializer.Deserialize(reader, objectType: descriptor.SpecType);
spec = descriptor.CreateInstance(
source: RunspaceContext.CurrentThread.Source.File,
source: _Context.Source,
metadata: metadata,
comment: comment,
extent: extent,
@ -665,11 +667,13 @@ internal sealed class LanguageExpressionJsonConverter : JsonConverter
{
private const string OPERATOR_IF = "if";
private readonly IResourceDiscoveryContext _Context;
private readonly LanguageExpressionFactory _Factory;
private readonly FunctionBuilder _FunctionBuilder;
public LanguageExpressionJsonConverter()
public LanguageExpressionJsonConverter(IResourceDiscoveryContext context)
{
_Context = context;
_Factory = new LanguageExpressionFactory();
_FunctionBuilder = new FunctionBuilder();
}
@ -946,7 +950,7 @@ internal sealed class LanguageExpressionJsonConverter : JsonConverter
if (_Factory.TryDescriptor(type, out var descriptor))
{
expression = (T)descriptor.CreateInstance(
source: RunspaceContext.CurrentThread.Source.File,
source: _Context.Source,
properties: properties
);
return expression != null;

Просмотреть файл

@ -12,7 +12,6 @@ using PSRule.Definitions.Expressions;
using PSRule.Host;
using PSRule.Pipeline;
using PSRule.Pipeline.Emitters;
using PSRule.Runtime;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
@ -479,16 +478,18 @@ internal sealed class FieldYamlTypeInspector : ReflectionTypeInspector
/// </summary>
internal sealed class ResourceNodeDeserializer : INodeDeserializer
{
private const string FIELD_APIVERSION = "apiVersion";
private const string FIELD_API_VERSION = "apiVersion";
private const string FIELD_KIND = "kind";
private const string FIELD_METADATA = "metadata";
private const string FIELD_SPEC = "spec";
private readonly IResourceDiscoveryContext _Context;
private readonly INodeDeserializer _Next;
private readonly SpecFactory _Factory;
public ResourceNodeDeserializer(INodeDeserializer next)
public ResourceNodeDeserializer(IResourceDiscoveryContext context, INodeDeserializer next)
{
_Context = context;
_Next = next;
_Factory = new SpecFactory();
}
@ -497,7 +498,7 @@ internal sealed class ResourceNodeDeserializer : INodeDeserializer
{
if (typeof(ResourceObject).IsAssignableFrom(expectedType))
{
var comment = reader.Current == null || RunspaceContext.CurrentThread == null ? null : HostHelper.GetCommentMeta(RunspaceContext.CurrentThread.Source?.File, (int)reader.Current.Start.Line - 2, (int)reader.Current.Start.Column);
var comment = reader.Current == null ? null : GetCommentMetadata(reader.Current.Start.Line - 2, reader.Current.Start.Column);
var resource = MapResource(reader, nestedObjectDeserializer, comment, rootDeserializer);
value = new ResourceObject(resource);
return true;
@ -516,7 +517,7 @@ internal sealed class ResourceNodeDeserializer : INodeDeserializer
ResourceMetadata? metadata = null;
if (reader.TryConsume<MappingStart>(out var mappingStart) && mappingStart != null)
{
var extent = new SourceExtent(RunspaceContext.CurrentThread!.Source!.File, (int?)mappingStart.Start.Line, (int?)mappingStart.Start.Column);
var extent = GetSourceExtent(mappingStart.Start.Line, mappingStart.Start.Column);
while (reader.TryConsume<Scalar>(out var scalar) && scalar != null)
{
// Read apiVersion
@ -553,7 +554,7 @@ internal sealed class ResourceNodeDeserializer : INodeDeserializer
private static bool TryApiVersion(IParser reader, Scalar scalar, out string? apiVersion)
{
apiVersion = null;
if (scalar.Value == FIELD_APIVERSION)
if (scalar.Value == FIELD_API_VERSION)
{
apiVersion = reader.Consume<Scalar>().Value;
return true;
@ -572,6 +573,16 @@ internal sealed class ResourceNodeDeserializer : INodeDeserializer
return false;
}
private CommentMetadata? GetCommentMetadata(long line, long column)
{
return _Context == null || _Context.Source == null ? null : HostHelper.GetCommentMeta(_Context.Source, (int)line, (int)column);
}
private SourceExtent GetSourceExtent(long? line, long? column)
{
return new SourceExtent(_Context.Source, (int?)line, (int?)column);
}
private bool TryMetadata(IParser reader, Scalar scalar, Func<IParser, Type, object?> nestedObjectDeserializer, out ResourceMetadata? metadata, ObjectDeserializer rootDeserializer)
{
metadata = null;
@ -617,12 +628,14 @@ internal sealed class LanguageExpressionDeserializer : INodeDeserializer
{
private const string OPERATOR_IF = "if";
private readonly IResourceDiscoveryContext _Context;
private readonly INodeDeserializer _Next;
private readonly LanguageExpressionFactory _Factory;
private readonly FunctionBuilder _FunctionBuilder;
public LanguageExpressionDeserializer(INodeDeserializer next)
public LanguageExpressionDeserializer(IResourceDiscoveryContext context, INodeDeserializer next)
{
_Context = context;
_Next = next;
_Factory = new LanguageExpressionFactory();
_FunctionBuilder = new FunctionBuilder();
@ -874,7 +887,7 @@ internal sealed class LanguageExpressionDeserializer : INodeDeserializer
expression = null;
if (_Factory.TryDescriptor(type, out var descriptor))
{
expression = (T)descriptor.CreateInstance(RunspaceContext.CurrentThread!.Source!.File, properties);
expression = (T)descriptor.CreateInstance(_Context.Source, properties);
return expression != null;
}
return false;

Просмотреть файл

@ -18,6 +18,11 @@ internal interface IResourceDiscoveryContext
/// </summary>
IPipelineWriter Writer { get; }
/// <summary>
/// The current source file.
/// </summary>
ISourceFile? Source { get; }
/// <summary>
/// Enter a language scope.
/// </summary>

Просмотреть файл

@ -16,7 +16,7 @@ internal sealed class ResourceBuilder
private readonly List<ILanguageBlock> _Output;
private readonly IDeserializer _Deserializer;
internal ResourceBuilder()
internal ResourceBuilder(IResourceDiscoveryContext context)
{
_Output = new List<ILanguageBlock>();
_Deserializer = new DeserializerBuilder()
@ -29,7 +29,7 @@ internal sealed class ResourceBuilder
.WithTypeConverter(new StringArrayMapConverter())
.WithTypeConverter(new StringArrayConverter())
.WithNodeDeserializer(
inner => new ResourceNodeDeserializer(new LanguageExpressionDeserializer(inner)),
inner => new ResourceNodeDeserializer(context, new LanguageExpressionDeserializer(context, inner)),
s => s.InsteadOf<ObjectNodeDeserializer>())
.Build();
}

Просмотреть файл

@ -1,11 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics;
namespace PSRule.Definitions;
/// <summary>
/// A resource object.
/// </summary>
[DebuggerDisplay("{Block.Kind} - {Block.Id.Value}")]
public sealed class ResourceObject
{
internal ResourceObject(IResource block)

Просмотреть файл

@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics;
using PSRule.Definitions.Expressions;
using PSRule.Resources;
using PSRule.Runtime;
namespace PSRule.Definitions.SuppressionGroups;
[DebuggerDisplay("{Id.Value}")]
internal sealed class SuppressionGroupVisitor
{
private readonly LanguageExpressionOuterFn _Fn;
@ -20,6 +22,7 @@ internal sealed class SuppressionGroupVisitor
Source = source;
InstanceId = Guid.NewGuid();
Rule = spec.Rule;
Info = info;
_Info = new SuppressionInfo(id, info);
_Fn = new LanguageExpressionBuilder()
.WithRule(Rule)
@ -69,6 +72,8 @@ internal sealed class SuppressionGroupVisitor
public ResourceId Id { get; }
public IResourceHelpInfo Info { get; }
public ISourceFile Source { get; }
public Guid InstanceId { get; }

Просмотреть файл

@ -4,6 +4,7 @@
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Text;
using Newtonsoft.Json;
using PSRule.Annotations;
using PSRule.Converters.Yaml;
@ -104,11 +105,10 @@ internal static class HostHelper
/// </summary>
internal static CommentMetadata GetCommentMeta(ISourceFile file, int lineNumber, int offset)
{
var context = RunspaceContext.CurrentThread;
if (lineNumber < 0 || RunspaceContext.CurrentThread.IsScope(RunspaceScope.None) || context.Source.SourceContentCache == null)
if (lineNumber < 0 || !file.Exists())
return new CommentMetadata();
var lines = context.Source.SourceContentCache;
var lines = File.ReadAllLines(file.Path, Encoding.UTF8); ;
var i = lineNumber;
var comments = new List<string>();
@ -263,7 +263,7 @@ internal static class HostHelper
.WithTypeConverter(new PSObjectYamlTypeConverter())
.WithNodeTypeResolver(new PSOptionYamlTypeResolver())
.WithNodeDeserializer(
inner => new ResourceNodeDeserializer(new LanguageExpressionDeserializer(inner)),
inner => new ResourceNodeDeserializer(context, new LanguageExpressionDeserializer(context, inner)),
s => s.InsteadOf<ObjectNodeDeserializer>())
.Build();
@ -292,7 +292,9 @@ internal static class HostHelper
continue;
if (item.Visit(visitor))
{
result.Add(item.Block);
}
}
}
finally
@ -321,10 +323,10 @@ internal static class HostHelper
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
};
deserializer.Converters.Add(new ResourceObjectJsonConverter());
deserializer.Converters.Add(new ResourceObjectJsonConverter(context));
deserializer.Converters.Add(new FieldMapJsonConverter());
deserializer.Converters.Add(new StringArrayJsonConverter());
deserializer.Converters.Add(new LanguageExpressionJsonConverter());
deserializer.Converters.Add(new LanguageExpressionJsonConverter(context));
try
{
@ -351,9 +353,11 @@ internal static class HostHelper
reader.SkipComments(out _);
while (reader.TokenType != JsonToken.EndArray)
{
var value = deserializer.Deserialize<ResourceObject>(reader);
if (value?.Block != null && value.Visit(visitor))
result.Add(value.Block);
var item = deserializer.Deserialize<ResourceObject>(reader);
if (item?.Block != null && item.Visit(visitor))
{
result.Add(item.Block);
}
// Consume all end objects at the end of each resource
while (reader.TryConsume(JsonToken.EndObject)) { }
@ -624,7 +628,7 @@ internal static class HostHelper
}
}
return results.Values.ToArray();
return [.. results.Values];
}
private static Baseline[] ToBaselineV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
@ -695,7 +699,7 @@ internal static class HostHelper
if (!results.ContainsKey(block.Name))
results[block.Name] = block;
}
return results.Values.ToArray();
return [.. results.Values];
}
/// <summary>
@ -858,7 +862,7 @@ internal static class HostHelper
? new RuleHelpInfo(
name: name,
displayName: defaultDisplayName ?? name,
moduleName: context.Source.File.Module,
moduleName: context.Source.Module,
synopsis: InfoString.Create(defaultSynopsis),
description: defaultDescription,
recommendation: defaultRecommendation
@ -866,7 +870,7 @@ internal static class HostHelper
: new RuleHelpInfo(
name: name,
displayName: document.Name ?? defaultDisplayName ?? name,
moduleName: context.Source.File.Module,
moduleName: context.Source.Module,
synopsis: document.Synopsis ?? new InfoString(defaultSynopsis),
description: document.Description ?? defaultDescription,
recommendation: document.Recommendation ?? defaultRecommendation ?? document.Synopsis ?? InfoString.Create(defaultSynopsis)
@ -895,7 +899,7 @@ internal static class HostHelper
{
path = null;
culture = null;
if (string.IsNullOrEmpty(context.Source.File.HelpPath))
if (string.IsNullOrEmpty(context.Source.HelpPath))
return false;
var helpFileName = string.Concat(name, Markdown_Extension);

Просмотреть файл

@ -2,10 +2,13 @@
// Licensed under the MIT License.
using PSRule.Definitions;
using PSRule.Resources;
using PSRule.Runtime;
namespace PSRule.Pipeline;
#nullable enable
/// <summary>
/// Define a context used for early stage resource discovery.
/// </summary>
@ -13,14 +16,19 @@ internal sealed class ResourceCacheDiscoveryContext(IPipelineWriter writer) : IR
{
public IPipelineWriter Writer { get; } = writer;
public ISourceFile? Source { get; private set; }
public void EnterLanguageScope(ISourceFile file)
{
if (!file.Exists())
throw new FileNotFoundException(PSRuleResources.ScriptNotFound, file.Path);
Source = file;
}
public void ExitLanguageScope(ISourceFile file)
{
Source = null;
}
public void PopScope(RunspaceScope scope)
@ -33,3 +41,5 @@ internal sealed class ResourceCacheDiscoveryContext(IPipelineWriter writer) : IR
}
}
#nullable restore

Просмотреть файл

@ -11,7 +11,6 @@ using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Rules;
using PSRule.Runtime.Binding;
using static PSRule.Pipeline.PipelineContext;
namespace PSRule.Runtime;
@ -24,7 +23,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
{
private const string SOURCE_OUTCOME_FAIL = "Rule.Outcome.Fail";
private const string SOURCE_OUTCOME_PASS = "Rule.Outcome.Pass";
private const string ERRORID_INVALIDRULERESULT = "PSRule.Runtime.InvalidRuleResult";
private const string ERROR_ID_INVALID_RULE_RESULT = "PSRule.Runtime.InvalidRuleResult";
private const string WARN_KEY_SEPARATOR = "_";
[ThreadStatic]
@ -99,7 +98,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
internal TargetObject? TargetObject { get; private set; }
internal SourceScope? Source { get; private set; }
public ISourceFile? Source { get; private set; }
internal ILanguageScope? LanguageScope
{
@ -247,7 +246,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
PSRuleResources.InvalidRuleResult,
RuleBlock?.Id
)),
errorId: ERRORID_INVALIDRULERESULT,
errorId: ERROR_ID_INVALID_RULE_RESULT,
errorCategory: ErrorCategory.InvalidResult,
targetObject: null
));
@ -528,7 +527,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
if (TargetObject != null && LanguageScope != null)
Binding = LanguageScope.Bind(TargetObject);
Source = new SourceScope(file);
Source = file;
}
public void ExitLanguageScope(ISourceFile file)
@ -562,7 +561,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
public bool TrySelector(string name)
{
return TrySelector(ResourceHelper.GetRuleId(Source?.File?.Module, name, ResourceIdKind.Unknown));
return TrySelector(ResourceHelper.GetRuleId(Source?.Module, name, ResourceIdKind.Unknown));
}
public bool TrySelector(ResourceId id)
@ -659,7 +658,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
if (LanguageScope == null) throw new Exception("Can not call out of scope.");
ResourceHelper.ParseIdString(LanguageScope.Name, id, out var scopeName, out var name);
return !Pipeline.LanguageScope.TryScope(scopeName, out var scope) || string.IsNullOrEmpty(name) ? null : scope.GetService(name!);
return !Pipeline.LanguageScope.TryScope(scopeName, out var scope) || string.IsNullOrEmpty(name) ? null : scope.GetService(name);
}
private void RunConventionInitialize()
@ -782,7 +781,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
public string? GetLocalizedPath(string file, out string? culture)
{
culture = null;
if (string.IsNullOrEmpty(Source?.File.HelpPath))
if (string.IsNullOrEmpty(Source?.HelpPath))
return null;
var cultures = LanguageScope?.Culture;
@ -795,7 +794,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
for (var i = 0; cultures != null && i < cultures.Length; i++)
{
var path = Path.Combine(Source?.File.HelpPath, cultures[i], file);
var path = Path.Combine(Source?.HelpPath, cultures[i], file);
if (File.Exists(path))
{
culture = cultures[i];

Просмотреть файл

@ -16,6 +16,8 @@ using YamlDotNet.Serialization;
namespace PSRule;
#nullable enable
public sealed class BaselineTests : ContextBaseTests
{
private const string BaselineYamlFileName = "Baseline.Rule.yaml";
@ -161,14 +163,14 @@ public sealed class BaselineTests : ContextBaseTests
var actual = (JArray)JsonConvert.DeserializeObject<dynamic>(json);
// TestBaseline1
Assert.Equal("github.com/microsoft/PSRule/v1", actual[0]["apiVersion"]);
Assert.Equal("Baseline", actual[0]["kind"]);
Assert.Equal("TestBaseline1", actual[0]["metadata"]["name"]);
Assert.NotNull(actual[0]["spec"]);
Assert.Equal("WithBaseline", actual[0]["spec"]["rule"]["include"][0]);
Assert.Equal("value1", actual[0]["spec"]["configuration"]["key1"]);
Assert.Equal("abc", actual[0]["spec"]["configuration"]["key2"][0]["value1"]);
Assert.Equal("def", actual[0]["spec"]["configuration"]["key2"][1]["value2"]);
Assert.Equal("github.com/microsoft/PSRule/v1", actual?[0]["apiVersion"]);
Assert.Equal("Baseline", actual?[0]["kind"]);
Assert.Equal("TestBaseline1", actual?[0]["metadata"]?["name"]);
Assert.NotNull(actual?[0]["spec"]);
Assert.Equal("WithBaseline", actual?[0]["spec"]?["rule"]?["include"]?[0]);
Assert.Equal("value1", actual?[0]["spec"]?["configuration"]?["key1"]);
Assert.Equal("abc", actual?[0]["spec"]?["configuration"]?["key2"]?[0]?["value1"]);
Assert.Equal("def", actual?[0]["spec"]?["configuration"]?["key2"]?[1]?["value2"]);
}
#region Helper methods
@ -195,3 +197,5 @@ public sealed class BaselineTests : ContextBaseTests
#endregion Helper methods
}
#nullable restore

Просмотреть файл

@ -11,13 +11,13 @@ metadata:
name: SuppressWithTargetName
spec:
rule:
- 'FromFile1'
- 'FromFile2'
- 'FromFile1'
- 'FromFile2'
if:
name: '.'
in:
- 'TestObject1'
- 'TestObject2'
- 'TestObject1'
- 'TestObject2'
---
# Synopsis: Ignore test objects by type.
@ -27,27 +27,27 @@ metadata:
name: SuppressWithTestType
spec:
rule:
- 'FromFile3'
- 'FromFile5'
- 'FromFile3'
- 'FromFile5'
if:
type: '.'
equals: 'TestType'
---
# Synopsis: Suppress with non-production tag
# Synopsis: Suppress with non-production tag.
apiVersion: github.com/microsoft/PSRule/v1
kind: SuppressionGroup
metadata:
name: SuppressWithNonProdTag
spec:
rule:
- '.\WithTag2'
- '.\WithTag3'
- '.\WithTag2'
- '.\WithTag3'
if:
field: 'tags.env'
in:
- 'dev'
- 'test'
- 'dev'
- 'test'
---
# Synopsis: Suppress with expiry.
@ -58,13 +58,13 @@ metadata:
spec:
expiresOn: '2022-01-01T00:00:00Z'
rule:
- '.\WithTag2'
- '.\WithTag3'
- '.\WithTag2'
- '.\WithTag3'
if:
field: 'tags.env'
in:
- 'dev'
- 'test'
- 'dev'
- 'test'
---
# Synopsis: Suppress by scope.
@ -74,8 +74,8 @@ metadata:
name: SuppressByScope
spec:
rule:
- 'FromFile1'
- 'FromFile2'
- 'FromFile1'
- 'FromFile2'
if:
scope: .
startsWith: '/'