436 lines
16 KiB
C#
Executable File
436 lines
16 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using System.Xml.Linq;
|
|
|
|
namespace MarketAlly.ProjectDetector;
|
|
|
|
public class ProjectAnalysis
|
|
{
|
|
// Project Information
|
|
public string ProjectName { get; set; } = string.Empty;
|
|
|
|
// Solution Information
|
|
public bool HasSolutionFile { get; set; }
|
|
|
|
// SDK Information
|
|
public string Sdk { get; set; } = string.Empty;
|
|
public bool HasMauiSdk { get; set; }
|
|
public bool HasWebSdk { get; set; }
|
|
public bool HasWorkerSdk { get; set; }
|
|
public bool HasDesktopSdk { get; set; }
|
|
|
|
// Output Information
|
|
public string OutputType { get; set; } = string.Empty;
|
|
public bool IsLibrary { get; set; }
|
|
public bool IsExecutable { get; set; }
|
|
public List<string> TargetFrameworks { get; set; } = new();
|
|
|
|
// Package Management Files
|
|
public List<string> PackageReferences { get; set; } = new();
|
|
public List<string> ProjectReferences { get; set; } = new();
|
|
|
|
// Node.js Analysis
|
|
public Dictionary<string, object> PackageJsonDependencies { get; set; } = new();
|
|
public Dictionary<string, object> PackageJsonDevDependencies { get; set; } = new();
|
|
public Dictionary<string, object> PackageJsonScripts { get; set; } = new();
|
|
|
|
// Python Analysis
|
|
public List<string> PythonRequirements { get; set; } = new();
|
|
public Dictionary<string, object> PyProjectConfig { get; set; } = new();
|
|
|
|
// Java Analysis
|
|
public Dictionary<string, object> MavenConfig { get; set; } = new();
|
|
public Dictionary<string, object> GradleConfig { get; set; } = new();
|
|
|
|
// Other Language Analysis
|
|
public Dictionary<string, object> GoModConfig { get; set; } = new();
|
|
public Dictionary<string, object> CargoConfig { get; set; } = new();
|
|
|
|
// Project Structure
|
|
public List<string> Folders { get; set; } = new();
|
|
public List<string> Files { get; set; } = new();
|
|
|
|
// Specific Technology Markers
|
|
public bool HasAspNetCore { get; set; }
|
|
public bool HasBlazor { get; set; }
|
|
public bool HasMaui { get; set; }
|
|
public bool HasWpf { get; set; }
|
|
public bool HasWinForms { get; set; }
|
|
public bool HasXamarin { get; set; }
|
|
public bool HasEntityFramework { get; set; }
|
|
public bool HasTestFramework { get; set; }
|
|
public bool HasWorkerService { get; set; }
|
|
public bool HasAzureFunctions { get; set; }
|
|
public bool HasCommandLine { get; set; }
|
|
public bool HasPluginFramework { get; set; }
|
|
public bool HasInfrastructure { get; set; }
|
|
|
|
// Analysis Results
|
|
public int ConfidenceScore { get; set; }
|
|
public string DetectionReason { get; set; } = string.Empty;
|
|
public List<string> DetectionEvidence { get; set; } = new();
|
|
|
|
public void AnalyzeProjectFile(string projectFilePath)
|
|
{
|
|
try
|
|
{
|
|
var extension = Path.GetExtension(projectFilePath).ToLowerInvariant();
|
|
|
|
switch (extension)
|
|
{
|
|
case ".csproj":
|
|
case ".vbproj":
|
|
case ".fsproj":
|
|
AnalyzeDotNetProject(projectFilePath);
|
|
break;
|
|
case ".json" when Path.GetFileName(projectFilePath).Equals("package.json", StringComparison.OrdinalIgnoreCase):
|
|
AnalyzePackageJson(projectFilePath);
|
|
break;
|
|
case ".xml" when Path.GetFileName(projectFilePath).Equals("pom.xml", StringComparison.OrdinalIgnoreCase):
|
|
AnalyzePomXml(projectFilePath);
|
|
break;
|
|
case ".gradle" when Path.GetFileName(projectFilePath).Equals("build.gradle", StringComparison.OrdinalIgnoreCase):
|
|
AnalyzeBuildGradle(projectFilePath);
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error reading project file: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void AnalyzeDotNetProject(string csprojPath)
|
|
{
|
|
try
|
|
{
|
|
var content = File.ReadAllText(csprojPath);
|
|
|
|
// Try XML parsing first for better accuracy
|
|
try
|
|
{
|
|
var doc = XDocument.Parse(content);
|
|
AnalyzeProjectXml(doc);
|
|
}
|
|
catch
|
|
{
|
|
// Fallback to string parsing
|
|
AnalyzeProjectString(content);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error reading .NET project file: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void AnalyzeProjectXml(XDocument doc)
|
|
{
|
|
var project = doc.Root;
|
|
|
|
// SDK Detection
|
|
Sdk = project?.Attribute("Sdk")?.Value ?? string.Empty;
|
|
HasMauiSdk = Sdk.Contains("Microsoft.NET.Sdk.Maui");
|
|
HasWebSdk = Sdk.Contains("Microsoft.NET.Sdk.Web");
|
|
HasWorkerSdk = Sdk.Contains("Microsoft.NET.Sdk.Worker");
|
|
HasDesktopSdk = Sdk.Contains("Microsoft.NET.Sdk.WindowsDesktop");
|
|
|
|
// Properties
|
|
var properties = project?.Descendants("PropertyGroup");
|
|
foreach (var propGroup in properties ?? Enumerable.Empty<XElement>())
|
|
{
|
|
OutputType = propGroup.Element("OutputType")?.Value ?? OutputType;
|
|
|
|
// Target frameworks
|
|
var targetFramework = propGroup.Element("TargetFramework")?.Value;
|
|
if (!string.IsNullOrEmpty(targetFramework))
|
|
TargetFrameworks.Add(targetFramework);
|
|
|
|
var targetFrameworks = propGroup.Element("TargetFrameworks")?.Value;
|
|
if (!string.IsNullOrEmpty(targetFrameworks))
|
|
TargetFrameworks.AddRange(targetFrameworks.Split(';'));
|
|
|
|
// Technology markers
|
|
HasMaui = HasMaui || propGroup.Element("UseMaui")?.Value == "true";
|
|
HasWpf = HasWpf || propGroup.Element("UseWPF")?.Value == "true";
|
|
HasWinForms = HasWinForms || propGroup.Element("UseWindowsForms")?.Value == "true";
|
|
|
|
// Worker service
|
|
HasWorkerService = HasWorkerService || propGroup.Element("Worker")?.Value == "true";
|
|
|
|
// Executable/Library detection
|
|
if (propGroup.Element("GeneratePackageOnBuild")?.Value == "true")
|
|
{
|
|
DetectionEvidence.Add("Has GeneratePackageOnBuild=true");
|
|
}
|
|
|
|
var appMetadata = new[] { "ApplicationTitle", "ApplicationId", "ApplicationVersion", "ApplicationDisplayVersion" };
|
|
foreach (var metadata in appMetadata)
|
|
{
|
|
if (propGroup.Element(metadata) != null)
|
|
{
|
|
DetectionEvidence.Add($"Has {metadata} (app metadata)");
|
|
}
|
|
}
|
|
|
|
// Check for package metadata
|
|
var packageMetadata = new[] { "PackageId", "Version", "Authors", "Description", "GeneratePackageOnBuild" };
|
|
foreach (var metadata in packageMetadata)
|
|
{
|
|
if (propGroup.Element(metadata) != null)
|
|
{
|
|
DetectionEvidence.Add($"Has {metadata} (NuGet package metadata)");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Package References
|
|
var packageRefs = project?.Descendants("PackageReference");
|
|
foreach (var packageRef in packageRefs ?? Enumerable.Empty<XElement>())
|
|
{
|
|
var include = packageRef.Attribute("Include")?.Value;
|
|
if (!string.IsNullOrEmpty(include))
|
|
{
|
|
PackageReferences.Add(include);
|
|
}
|
|
}
|
|
|
|
// Project References
|
|
var projectRefs = project?.Descendants("ProjectReference");
|
|
foreach (var projectRef in projectRefs ?? Enumerable.Empty<XElement>())
|
|
{
|
|
var include = projectRef.Attribute("Include")?.Value;
|
|
if (!string.IsNullOrEmpty(include))
|
|
{
|
|
ProjectReferences.Add(include);
|
|
}
|
|
}
|
|
|
|
AnalyzePackageReferences();
|
|
|
|
IsLibrary = OutputType.Equals("Library", StringComparison.OrdinalIgnoreCase) ||
|
|
(string.IsNullOrEmpty(OutputType) && !IsExecutable);
|
|
IsExecutable = OutputType.Equals("Exe", StringComparison.OrdinalIgnoreCase) ||
|
|
OutputType.Equals("WinExe", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void AnalyzeProjectString(string content)
|
|
{
|
|
// SDK Detection
|
|
var sdkMatch = Regex.Match(content, @"<Project\s+Sdk\s*=\s*""([^""]+)""");
|
|
if (sdkMatch.Success)
|
|
{
|
|
Sdk = sdkMatch.Groups[1].Value;
|
|
HasMauiSdk = Sdk.Contains("Microsoft.NET.Sdk.Maui");
|
|
HasWebSdk = Sdk.Contains("Microsoft.NET.Sdk.Web");
|
|
HasWorkerSdk = Sdk.Contains("Microsoft.NET.Sdk.Worker");
|
|
HasDesktopSdk = Sdk.Contains("Microsoft.NET.Sdk.WindowsDesktop");
|
|
}
|
|
|
|
// Output Type
|
|
var outputTypeMatch = Regex.Match(content, @"<OutputType>([^<]+)</OutputType>");
|
|
if (outputTypeMatch.Success)
|
|
{
|
|
OutputType = outputTypeMatch.Groups[1].Value;
|
|
}
|
|
|
|
// Package References
|
|
var packageMatches = Regex.Matches(content, @"<PackageReference\s+Include\s*=\s*""([^""]+)""");
|
|
foreach (Match match in packageMatches)
|
|
{
|
|
PackageReferences.Add(match.Groups[1].Value);
|
|
}
|
|
|
|
// Technology markers
|
|
HasMaui = HasMaui || content.Contains("<UseMaui>true</UseMaui>");
|
|
HasWpf = HasWpf || content.Contains("<UseWPF>true</UseWPF>");
|
|
HasWinForms = HasWinForms || content.Contains("<UseWindowsForms>true</UseWindowsForms>");
|
|
|
|
AnalyzePackageReferences();
|
|
|
|
IsLibrary = OutputType.Equals("Library", StringComparison.OrdinalIgnoreCase);
|
|
IsExecutable = OutputType.Equals("Exe", StringComparison.OrdinalIgnoreCase) ||
|
|
OutputType.Equals("WinExe", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void AnalyzePackageReferences()
|
|
{
|
|
var packages = string.Join(" ", PackageReferences);
|
|
|
|
HasAspNetCore = PackageReferences.Any(p => p.Contains("Microsoft.AspNetCore"));
|
|
HasBlazor = PackageReferences.Any(p => p.Contains("Microsoft.AspNetCore.Components"));
|
|
HasEntityFramework = PackageReferences.Any(p => p.Contains("EntityFramework"));
|
|
HasXamarin = PackageReferences.Any(p => p.Contains("Xamarin"));
|
|
|
|
// Test frameworks
|
|
var testFrameworks = new[] { "xunit", "nunit", "mstest", "Microsoft.NET.Test.Sdk" };
|
|
HasTestFramework = PackageReferences.Any(p => testFrameworks.Any(tf => p.Contains(tf, StringComparison.OrdinalIgnoreCase)));
|
|
|
|
// Azure Functions
|
|
var azureFunctionsPackages = new[] { "Microsoft.NET.Sdk.Functions", "Microsoft.Azure.Functions", "Microsoft.Azure.WebJobs" };
|
|
HasAzureFunctions = PackageReferences.Any(p => azureFunctionsPackages.Any(af => p.Contains(af)));
|
|
|
|
// Command line packages
|
|
var commandLinePackages = new[] { "CommandLineParser", "System.CommandLine", "McMaster.Extensions.CommandLineUtils" };
|
|
HasCommandLine = PackageReferences.Any(p => commandLinePackages.Any(cl => p.Contains(cl)));
|
|
|
|
// Plugin frameworks
|
|
var pluginPackages = new[] { "Microsoft.Extensions.DependencyInjection.Abstractions", "System.Composition", "MEF" };
|
|
HasPluginFramework = PackageReferences.Any(p => pluginPackages.Any(pf => p.Contains(pf)));
|
|
|
|
// Infrastructure packages
|
|
var infraPackages = new[] { "Pulumi", "Terraform", "AWS.CDK", "Azure.ResourceManager" };
|
|
HasInfrastructure = PackageReferences.Any(p => infraPackages.Any(inf => p.Contains(inf)));
|
|
}
|
|
|
|
private void AnalyzePackageJson(string packageJsonPath)
|
|
{
|
|
try
|
|
{
|
|
var content = File.ReadAllText(packageJsonPath);
|
|
var packageJson = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(content);
|
|
|
|
if (packageJson != null)
|
|
{
|
|
// Dependencies
|
|
if (packageJson.TryGetValue("dependencies", out var deps) && deps.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var dep in deps.EnumerateObject())
|
|
{
|
|
PackageJsonDependencies[dep.Name] = dep.Value.ToString();
|
|
}
|
|
}
|
|
|
|
// Dev Dependencies
|
|
if (packageJson.TryGetValue("devDependencies", out var devDeps) && devDeps.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var dep in devDeps.EnumerateObject())
|
|
{
|
|
PackageJsonDevDependencies[dep.Name] = dep.Value.ToString();
|
|
}
|
|
}
|
|
|
|
// Scripts
|
|
if (packageJson.TryGetValue("scripts", out var scripts) && scripts.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var script in scripts.EnumerateObject())
|
|
{
|
|
PackageJsonScripts[script.Name] = script.Value.ToString();
|
|
}
|
|
}
|
|
|
|
DetectionEvidence.Add("Found package.json (Node.js project)");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error analyzing package.json: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void AnalyzePomXml(string pomPath)
|
|
{
|
|
try
|
|
{
|
|
var content = File.ReadAllText(pomPath);
|
|
MavenConfig["content"] = content;
|
|
DetectionEvidence.Add("Found pom.xml (Maven project)");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error analyzing pom.xml: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void AnalyzeBuildGradle(string gradlePath)
|
|
{
|
|
try
|
|
{
|
|
var content = File.ReadAllText(gradlePath);
|
|
GradleConfig["content"] = content;
|
|
DetectionEvidence.Add("Found build.gradle (Gradle project)");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error analyzing build.gradle: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void AnalyzeProjectStructure(string projectPath)
|
|
{
|
|
try
|
|
{
|
|
// Get folders
|
|
Folders = Directory.GetDirectories(projectPath, "*", SearchOption.TopDirectoryOnly)
|
|
.Select(d => Path.GetFileName(d))
|
|
.Where(d => !d.StartsWith("."))
|
|
.ToList();
|
|
|
|
// Get files (top level only for performance)
|
|
Files = Directory.GetFiles(projectPath, "*", SearchOption.TopDirectoryOnly)
|
|
.Select(f => Path.GetFileName(f))
|
|
.Where(f => !f.StartsWith("."))
|
|
.ToList();
|
|
|
|
// Add source file detection from subdirectories
|
|
var sourceExtensions = new[] { "*.cs", "*.js", "*.ts", "*.py", "*.java", "*.go", "*.rs", "*.cpp", "*.c", "*.h" };
|
|
foreach (var ext in sourceExtensions)
|
|
{
|
|
var sourceFiles = Directory.GetFiles(projectPath, ext, SearchOption.AllDirectories)
|
|
.Select(f => Path.GetRelativePath(projectPath, f))
|
|
.ToList();
|
|
|
|
if (sourceFiles.Any())
|
|
{
|
|
Files.AddRange(sourceFiles);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error analyzing project structure: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void AnalyzeSourceFiles(string projectPath)
|
|
{
|
|
try
|
|
{
|
|
// Python-specific files
|
|
var requirementsPath = Path.Combine(projectPath, "requirements.txt");
|
|
if (File.Exists(requirementsPath))
|
|
{
|
|
PythonRequirements = File.ReadAllLines(requirementsPath)
|
|
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
|
|
.ToList();
|
|
DetectionEvidence.Add("Found requirements.txt");
|
|
}
|
|
|
|
// Go modules
|
|
var goModPath = Path.Combine(projectPath, "go.mod");
|
|
if (File.Exists(goModPath))
|
|
{
|
|
var content = File.ReadAllText(goModPath);
|
|
GoModConfig["content"] = content;
|
|
DetectionEvidence.Add("Found go.mod");
|
|
}
|
|
|
|
// Rust cargo
|
|
var cargoPath = Path.Combine(projectPath, "Cargo.toml");
|
|
if (File.Exists(cargoPath))
|
|
{
|
|
var content = File.ReadAllText(cargoPath);
|
|
CargoConfig["content"] = content;
|
|
DetectionEvidence.Add("Found Cargo.toml");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetectionEvidence.Add($"Error analyzing source files: {ex.Message}");
|
|
}
|
|
}
|
|
} |