Files
git.stella-ops.org/src/StellaOps.Scanner.Analyzers.Lang/Plugin/LanguageAnalyzerPluginCatalog.cs
master 323bf5844f
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add scripts for resolving and verifying Chromium binary paths
- Implemented `chrome-path.js` to define functions for locating Chromium binaries across different platforms and nested directories.
- Added `verify-chromium.js` to check for the presence of the Chromium binary and log the results, including candidate paths checked.
- The scripts support Linux, Windows, and macOS environments, enhancing the flexibility of Chromium binary detection.
2025-10-22 09:14:36 +03:00

148 lines
4.8 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Logging;
using StellaOps.Plugin;
using StellaOps.Plugin.Hosting;
using StellaOps.Scanner.Core.Security;
namespace StellaOps.Scanner.Analyzers.Lang.Plugin;
public interface ILanguageAnalyzerPluginCatalog
{
IReadOnlyCollection<ILanguageAnalyzerPlugin> Plugins { get; }
void LoadFromDirectory(string directory, bool seal = true);
IReadOnlyList<ILanguageAnalyzer> CreateAnalyzers(IServiceProvider services);
}
public sealed class LanguageAnalyzerPluginCatalog : ILanguageAnalyzerPluginCatalog
{
private readonly ILogger<LanguageAnalyzerPluginCatalog> _logger;
private readonly IPluginCatalogGuard _guard;
private readonly ConcurrentDictionary<string, Assembly> _assemblies = new(StringComparer.OrdinalIgnoreCase);
private IReadOnlyList<ILanguageAnalyzerPlugin> _plugins = Array.Empty<ILanguageAnalyzerPlugin>();
public LanguageAnalyzerPluginCatalog(IPluginCatalogGuard guard, ILogger<LanguageAnalyzerPluginCatalog> logger)
{
_guard = guard ?? throw new ArgumentNullException(nameof(guard));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public IReadOnlyCollection<ILanguageAnalyzerPlugin> Plugins => _plugins;
public void LoadFromDirectory(string directory, bool seal = true)
{
ArgumentException.ThrowIfNullOrWhiteSpace(directory);
var fullDirectory = Path.GetFullPath(directory);
var options = new PluginHostOptions
{
PluginsDirectory = fullDirectory,
EnsureDirectoryExists = false,
RecursiveSearch = false,
};
options.SearchPatterns.Add("StellaOps.Scanner.Analyzers.*.dll");
var result = PluginHost.LoadPlugins(options, _logger);
if (result.Plugins.Count == 0)
{
_logger.LogWarning("No language analyzer plug-ins discovered under '{Directory}'.", fullDirectory);
}
foreach (var descriptor in result.Plugins)
{
try
{
_guard.EnsureRegistrationAllowed(descriptor.AssemblyPath);
_assemblies[descriptor.AssemblyPath] = descriptor.Assembly;
_logger.LogInformation(
"Registered language analyzer plug-in assembly '{Assembly}' from '{Path}'.",
descriptor.Assembly.FullName,
descriptor.AssemblyPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to register language analyzer plug-in '{Path}'.", descriptor.AssemblyPath);
}
}
RefreshPluginList();
if (seal)
{
_guard.Seal();
}
}
public IReadOnlyList<ILanguageAnalyzer> CreateAnalyzers(IServiceProvider services)
{
ArgumentNullException.ThrowIfNull(services);
if (_plugins.Count == 0)
{
_logger.LogWarning("No language analyzer plug-ins available; skipping language analysis.");
return Array.Empty<ILanguageAnalyzer>();
}
var analyzers = new List<ILanguageAnalyzer>(_plugins.Count);
foreach (var plugin in _plugins)
{
if (!IsPluginAvailable(plugin, services))
{
continue;
}
try
{
var analyzer = plugin.CreateAnalyzer(services);
if (analyzer is null)
{
continue;
}
analyzers.Add(analyzer);
}
catch (Exception ex)
{
_logger.LogError(ex, "Language analyzer plug-in '{Plugin}' failed to create analyzer instance.", plugin.Name);
}
}
if (analyzers.Count == 0)
{
_logger.LogWarning("All language analyzer plug-ins were unavailable.");
return Array.Empty<ILanguageAnalyzer>();
}
analyzers.Sort(static (a, b) => string.CompareOrdinal(a.Id, b.Id));
return new ReadOnlyCollection<ILanguageAnalyzer>(analyzers);
}
private void RefreshPluginList()
{
var assemblies = _assemblies.Values.ToArray();
var plugins = PluginLoader.LoadPlugins<ILanguageAnalyzerPlugin>(assemblies);
_plugins = plugins is IReadOnlyList<ILanguageAnalyzerPlugin> list
? list
: new ReadOnlyCollection<ILanguageAnalyzerPlugin>(plugins.ToArray());
}
private static bool IsPluginAvailable(ILanguageAnalyzerPlugin plugin, IServiceProvider services)
{
try
{
return plugin.IsAvailable(services);
}
catch
{
return false;
}
}
}