Skip to content

Commit

Permalink
Fixes custom emitter loaded to late #2775
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite committed Feb 16, 2025
1 parent 4e04ee1 commit 90f93ed
Show file tree
Hide file tree
Showing 28 changed files with 467 additions and 127 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ What's changed since pre-release v3.0.0-B0416:
[#2768](https://github.com/microsoft/PSRule/pull/2768)
- Bump vscode engine to v1.97.0.
[#2759](https://github.com/microsoft/PSRule/pull/2759)
- Bug fix:
- Fixed custom emitter loaded to late by @BernieWhite.
[#2775](https://github.com/microsoft/PSRule/issues/2775)

## v3.0.0-B0416 (pre-release)

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Custom emitters can be created by implementing the `PSRule.Emitters.IEmitter` in
This custom type implementation will be loaded by PSRule and used to process the input object.

To use a custom emitter, it must be registered with PSRule as a service.
This can be done by a convention within the `-Initialize` script block.
This can be done by registering a runtime factory.

## Configuring formats

Expand Down
20 changes: 12 additions & 8 deletions src/PSRule.Types/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@

namespace PSRule;

#nullable enable

/// <summary>
/// A helper for accessing environment and runtime variables.
/// </summary>
public static class Environment
{
private static readonly char[] STRINGARRAYMAP_ITEMSEPARATOR = new char[] { ',' };
private static readonly char[] STRINGARRAY_SEPARATOR = new char[] { ';' };
private static readonly char[] LINUX_PATH_ENV_SEPARATOR = new char[] { ':' };
private static readonly char[] WINDOWS_PATH_ENV_SEPARATOR = new char[] { ';' };
private static readonly char[] STRING_ARRAY_MAP_ITEM_SEPARATOR = [','];
private static readonly char[] STRINGARRAY_SEPARATOR = [';'];
private static readonly char[] LINUX_PATH_ENV_SEPARATOR = [':'];
private static readonly char[] WINDOWS_PATH_ENV_SEPARATOR = [';'];

private const char BACKSLASH = '\\';
private const char SLASH = '/';

private const char STRINGARRYAMAP_PAIRSEPARATOR = '=';
private const char STRING_ARRAY_MAP_PAIR_SEPARATOR = '=';
private const string PATH_ENV = "PATH";
private const string DEFAULT_CREDENTIAL_USERNAME = "na";
private const string TF_BUILD = "TF_BUILD";
Expand Down Expand Up @@ -127,7 +129,7 @@ public static string GetRootedPath(string? path, bool normalize = false, string?
/// <remarks>
/// A base path always includes a trailing <c>/</c>.
/// </remarks>
public static string GetRootedBasePath(string path, bool normalize = false, string? basePath = null)
public static string GetRootedBasePath(string? path, bool normalize = false, string? basePath = null)
{
if (string.IsNullOrEmpty(path))
path = string.Empty;
Expand Down Expand Up @@ -250,12 +252,12 @@ public static bool TryStringArrayMap(string key, out StringArrayMap? value)
var map = new StringArrayMap();
for (var i = 0; i < pairs.Length; i++)
{
var index = pairs[i].IndexOf(STRINGARRYAMAP_PAIRSEPARATOR);
var index = pairs[i].IndexOf(STRING_ARRAY_MAP_PAIR_SEPARATOR);
if (index < 1 || index + 1 >= pairs[i].Length) continue;

var left = pairs[i].Substring(0, index);
var right = pairs[i].Substring(index + 1);
var pair = right.Split(STRINGARRAYMAP_ITEMSEPARATOR, StringSplitOptions.RemoveEmptyEntries);
var pair = right.Split(STRING_ARRAY_MAP_ITEM_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
map[left] = pair;
}
value = map;
Expand Down Expand Up @@ -380,3 +382,5 @@ private static bool IsPathSeparator(char c)
return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar || c == SLASH || c == BACKSLASH;
}
}

#nullable restore
2 changes: 1 addition & 1 deletion src/PSRule.Types/Runtime/ILogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ public interface ILogger
/// <param name="eventId">An event identifier for the diagnostic message.</param>
/// <param name="state">Additional information that describes the diagnostic state to log.</param>
/// <param name="exception">An optional exception which the diagnostic message is related to.</param>
/// <param name="formatter">A function to format the diagnostic message for the outpuyt stream.</param>
/// <param name="formatter">A function to format the diagnostic message for the outpuWt stream.</param>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
}
15 changes: 15 additions & 0 deletions src/PSRule.Types/Runtime/IRuntimeFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace PSRule.Runtime;

/// <summary>
/// An interface for implementing a runtime factory.
/// </summary>
public interface IRuntimeFactory
{
/// <summary>
/// Call the runtime factory to configure services.
/// </summary>
void Configure(IRuntimeFactoryContext context);
}
15 changes: 15 additions & 0 deletions src/PSRule.Types/Runtime/IRuntimeFactoryContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace PSRule.Runtime;

/// <summary>
/// A context for a runtime factory.
/// </summary>
public interface IRuntimeFactoryContext
{
/// <summary>
/// Configure services for the runtime factory.
/// </summary>
void ConfigureServices(Action<IRuntimeServiceCollection> configure);
}
13 changes: 13 additions & 0 deletions src/PSRule.Types/Runtime/RuntimeFactoryAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace PSRule.Runtime;

/// <summary>
/// Identifies for classes that construct runtime services.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class RuntimeFactoryAttribute : Attribute
{

}
2 changes: 1 addition & 1 deletion src/PSRule/Emitters/EmitterBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace PSRule.Emitters;
internal sealed class EmitterBuilder
{
private static readonly EventId PSR0012 = new(12, "PSR0012");
private static readonly EventId PSR0013 = new(12, "PSR0013");
private static readonly EventId PSR0013 = new(13, "PSR0013");

private readonly ILanguageScopeSet? _LanguageScopeSet;
private readonly IFormatOption _FormatOption;
Expand Down
2 changes: 1 addition & 1 deletion src/PSRule/Pipeline/Formatters/AssertFormatterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ private void Source(Source[] source)
{
if (source[i].Module != null && !list.Contains(source[i].Module.Name))
{
WriteLineFormat(FormatterStrings.ModuleVersion, source[i].Module.Name, source[i].Module.Version);
WriteLineFormat(FormatterStrings.ModuleVersion, source[i].Module.Name, source[i].Module.FullVersion);
list.Add(source[i].Module.Name);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/PSRule/Pipeline/Output/JobSummaryWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private void Source()
if (_Source[i].Module != null && !list.Contains(_Source[i].Module.Name))
{
var projectLink = string.IsNullOrEmpty(_Source[i].Module.ProjectUri) ? _Source[i].Module.Name : $"[{_Source[i].Module.Name}]({_Source[i].Module.ProjectUri})";
WriteLine($"{projectLink} | v{_Source[i].Module.Version}");
WriteLine($"{projectLink} | v{_Source[i].Module.FullVersion}");
list.Add(_Source[i].Module.Name);
}
}
Expand Down
33 changes: 17 additions & 16 deletions src/PSRule/Pipeline/Output/SarifBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,24 +514,25 @@ private Tool GetTool(Source[]? source)
var result = new List<ToolComponent>();
for (var i = 0; i < source.Length; i++)
{
if (source[i].Module != null && !_Extensions.ContainsKey(source[i].Module.Name))
var module = source[i].Module;
if (module == null || _Extensions.ContainsKey(module.Name))
continue;

var extension = new ToolComponent
{
var extension = new ToolComponent
Name = module.Name,
Version = module.FullVersion,
Guid = module.Guid,
AssociatedComponent = new ToolComponentReference
{
Name = source[i].Module.Name,
Version = source[i].Module.Version,
Guid = source[i].Module.Guid,
AssociatedComponent = new ToolComponentReference
{
Name = TOOL_NAME,
},
InformationUri = new Uri(source[i].Module.ProjectUri, UriKind.Absolute),
Organization = source[i].Module.CompanyName,
Rules = new List<ReportingDescriptor>(),
};
_Extensions.Add(extension.Name, extension);
result.Add(extension);
}
Name = TOOL_NAME,
},
InformationUri = new Uri(module.ProjectUri, UriKind.Absolute),
Organization = module.CompanyName,
Rules = [],
};
_Extensions.Add(extension.Name, extension);
result.Add(extension);
}
return result.Count > 0 ? result : null;
}
Expand Down
4 changes: 2 additions & 2 deletions src/PSRule/Pipeline/PipelineBuilderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ protected bool RequireModules()
}
for (var i = 0; Source != null && i < Source.Length; i++)
{
if (Source[i].Module != null && Option.Requires.TryGetValue(Source[i].Module.Name, out requiredVersion))
if (Source[i].Module != null && Option.Requires.TryGetValue(Source[i].Module!.Name, out requiredVersion))
{
if (GuardModuleVersion(Source[i].Module.Name, Source[i].Module.Version, requiredVersion))
if (GuardModuleVersion(Source[i].Module!.Name, Source[i].Module!.FullVersion, requiredVersion))
result = false;
}
}
Expand Down
60 changes: 15 additions & 45 deletions src/PSRule/Pipeline/Source.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections;
using System.Management.Automation;
using System.Reflection;
using PSRule.Definitions;

namespace PSRule.Pipeline;

#nullable enable

/// <summary>
/// A PSRule source containing one or more source files.
/// </summary>
public sealed class Source
{
internal bool Dependency;

internal readonly ModuleInfo Module;
internal readonly ModuleInfo? Module;

internal Source(string path, SourceFile[] file)
{
Expand All @@ -34,53 +35,20 @@ internal Source(ModuleInfo module, SourceFile[] file, bool dependency)
SetSource();
}

internal sealed class ModuleInfo
internal sealed class ModuleInfo(string path, string name, string version, string? projectUri, string? guid, string? companyName, string? prerelease, Assembly[] assemblies)
{
private const string FIELD_PRERELEASE = "Prerelease";
private const string FIELD_PSDATA = "PSData";
private const string PRERELEASE_SEPARATOR = "-";

public readonly string Path;
public readonly string Name;
public readonly string Version;
public readonly string ProjectUri;
public readonly string Guid;
public readonly string CompanyName;
public readonly string Path = path;
public readonly string Name = name;
public readonly string Version = version;
public readonly string FullVersion = string.IsNullOrEmpty(prerelease) ? version : string.Concat(version, PRERELEASE_SEPARATOR, prerelease);

public ModuleInfo(PSModuleInfo info)
{
Path = info.ModuleBase;
Name = info.Name;
Version = info.Version?.ToString();
ProjectUri = info.ProjectUri?.ToString();
Guid = info.Guid.ToString();
CompanyName = info.CompanyName;
if (TryPrivateData(info, FIELD_PSDATA, out var psData) && psData.ContainsKey(FIELD_PRERELEASE))
Version = string.Concat(Version, PRERELEASE_SEPARATOR, psData[FIELD_PRERELEASE].ToString());
}
public readonly Assembly[] Assemblies = assemblies;

public ModuleInfo(string path, string name, string version, string projectUri, string guid, string companyName, string prerelease)
{
Path = path;
Name = name;
Version = version;
ProjectUri = projectUri;
Guid = guid;
CompanyName = companyName;
if (!string.IsNullOrEmpty(prerelease))
Version = string.Concat(version, PRERELEASE_SEPARATOR, prerelease);
}

private static bool TryPrivateData(PSModuleInfo info, string propertyName, out Hashtable value)
{
value = null;
if (info.PrivateData is Hashtable privateData && privateData.ContainsKey(propertyName) && privateData[propertyName] is Hashtable data)
{
value = data;
return true;
}
return false;
}
public readonly string? ProjectUri = projectUri;
public readonly string? Guid = guid;
public readonly string? CompanyName = companyName;
}

/// <summary>
Expand All @@ -107,3 +75,5 @@ private void SetSource()
File[i].Source = this;
}
}

#nullable restore
Loading

0 comments on commit 90f93ed

Please sign in to comment.