323 lines
15 KiB
PowerShell
323 lines
15 KiB
PowerShell
function Invoke-CategorizeCSProj {
|
|
[CmdletBinding()]
|
|
[OutputType([PSCustomObject])]
|
|
param (
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'Path')]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]$Path,
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'Project')]
|
|
[ValidateNotNullOrEmpty()]
|
|
[object]$Project
|
|
)
|
|
|
|
$logLead = Get-LogLeadName
|
|
$projectName = $Project.Name
|
|
|
|
$deprecatedProjectRegex = "^(zz|deprecated|archive|obsolete|xxx)"
|
|
|
|
# This is a magic value invented by Cole for max-length of file for discrepancy on what exists
|
|
$magicChocoFilesLengthValue = 160
|
|
|
|
if ($PSCmdlet.ParameterSetName -eq 'Project') {
|
|
if ([string]::IsNullOrWhiteSpace($Project.Path)) {
|
|
Write-Warning "$logLead : Could not find a path property for [$($Project.Name) ($($Project.Type))]$(if ($ErrorActionPreference -ne 'Stop') { ", returning `$null"})"
|
|
return $null
|
|
}
|
|
$Path = $Project.Path
|
|
}
|
|
|
|
if (Test-Path -Path $Path) {
|
|
$Path = Resolve-Path -Path $Path
|
|
} else {
|
|
Write-Error "$logLead : Could not resolve the path [$Path].$(if ($ErrorActionPreference -ne 'Stop') { " Returning `$null" })"
|
|
return $null
|
|
}
|
|
|
|
Write-Verbose "$logLead : Processing child objects for csproj files"
|
|
$item = Get-Item -Path $Path
|
|
if ($item.PSIsContainer) {
|
|
$csprojList = Get-ChildItem -Path $Path -ChildPath "*.csproj"
|
|
|
|
if (-not (Any $csprojList)) {
|
|
Write-Error "$logLead : Provided path was a Directory, and no child projects were found in the folder directly.$(if ($ErrorActionPreference -ne 'Stop') { " Returning `$null" })"
|
|
}
|
|
|
|
$Path = $csprojList[0].FullName
|
|
Write-Warning "$logLead : Provided path was a Directory, using the first found csproj in this folder, with path [$Path]"
|
|
}
|
|
|
|
if ($PSCmdlet.ParameterSetName -eq 'Path') {
|
|
$projectName = [System.IO.Path]::GetFileNameWithoutExtension($Path)
|
|
}
|
|
|
|
$baseFolder = Split-Path -Path $Path -Parent
|
|
|
|
Write-Host "$logLead : Begin processing project $($projectName)"
|
|
$csproj = @{
|
|
Name = $projectName
|
|
Path = $Path
|
|
ChocoFiles = @{
|
|
HasChocoFiles = $false
|
|
InstallFileSize = 0
|
|
InstallFileNew = $false
|
|
UninstallFileSize = 0
|
|
UninstallFileNew = $false
|
|
ChocolateyInstall = $null
|
|
ChocolateyUninstall = $null
|
|
}
|
|
Nuspec = @{
|
|
HasNuspecFile = $false
|
|
IsChocolatey = $false
|
|
PackageId = $null
|
|
Version = $null
|
|
Raw = $null
|
|
}
|
|
AlkamiManifest = @{
|
|
HasAlkamiManifest = $false
|
|
ComponentType = $null
|
|
AlkamiManifest = $null
|
|
IsLegacyInstaller = $false
|
|
IsUnkownInstaller = $false
|
|
}
|
|
HasTestReferences = $false
|
|
HasProgramMain = $false
|
|
Packages = $null
|
|
IsLikelyDeprecated = $Path -match $deprecatedProjectRegex
|
|
TargetFramework = 'Framework'
|
|
TargetFrameworkValue = $null
|
|
HasPostBuildEvent = $false
|
|
HasPreBuildEvent = $false
|
|
}
|
|
|
|
$rawCsproj = [xml](Get-Content -Path $Path -Raw)
|
|
$files = Get-ChildItem -Path $baseFolder
|
|
|
|
if (![string]::IsNullOrWhiteSpace($rawCsproj.Project.PropertyGroup.TargetFramework)) {
|
|
$csproj.TargetFrameworkValue = $rawCsproj.Project.PropertyGroup.TargetFramework
|
|
} elseif (![string]::IsNullOrWhiteSpace($rawCsproj.Project.PropertyGroup.TargetFrameworkVersion)) {
|
|
$csproj.TargetFrameworkValue = $rawCsproj.Project.PropertyGroup.TargetFrameworkVersion
|
|
} else {
|
|
$csproj.TargetFrameworkValue = 'Unknown'
|
|
}
|
|
|
|
if ($rawCsproj.Project.PropertyGroup.TargetFramework -match 'net6' -or $rawCsproj.Project.PropertyGroup.TargetFramework -match 'netcore') {
|
|
$csproj.TargetFramework = 'core'
|
|
} elseif ($rawCsproj.Project.PropertyGroup.TargetFramework -match 'netstandard') {
|
|
$csproj.TargetFramework = 'netstandard'
|
|
}
|
|
|
|
#region check for build events
|
|
if ($null -ne $rawCsproj.Project.PropertyGroup.PreBuildEvent) {
|
|
$csproj.HasPreBuildEvent = $true
|
|
}
|
|
if ($null -ne $rawCsproj.Project.PropertyGroup.PostBuildEvent) {
|
|
$csproj.HasPostBuildEvent = $true
|
|
}
|
|
#endregion check for build events
|
|
|
|
$includePackagesConfig = ($rawCsproj.Project.ItemGroup.None.Include -eq 'packages.config') -or ($rawCsproj.Project.ItemGroup.Content.Include -eq 'packages.config')
|
|
$csproj.IncludesPackagesConfig = $includePackagesConfig
|
|
|
|
Write-Verbose "$logLead : Read packages.config"
|
|
$packages = @()
|
|
$packagesConfigPath = Join-Path -Path $baseFolder -ChildPath "packages.config"
|
|
$baseFolderContainsPackagesConfig = Test-Path -Path $packagesConfigPath
|
|
if ($baseFolderContainsPackagesConfig) {
|
|
$csproj.PackagesConfigPath = $packagesConfigPath
|
|
$packagesConfig = [xml](Get-Content -Path $packagesConfigPath -Raw)
|
|
foreach ($package in $packagesConfig.packages.package) {
|
|
$packages += @{ PackageId = $package.id; Version = $package.version}
|
|
}
|
|
}
|
|
foreach ($package in $rawCsproj.Project.ItemGroup.PackageReference) {
|
|
if ([string]::IsNullOrWhiteSpace($package.Include)) {
|
|
continue
|
|
}
|
|
$packages += @{ PackageId = $package.include; Version = "$($package.version)".Trim() }
|
|
}
|
|
$csproj.Packages = $packages
|
|
|
|
Write-Verbose "$logLead : Categorize semver"
|
|
# TODO: Recurse if not found on the base to look for tools\sem.ver a-la SDK widgets
|
|
# Record the path if not found on the project root
|
|
$semVerPath = $files.Where({$_.Name -eq "sem.ver"}).FullName
|
|
if (![string]::IsNullOrWhiteSpace($semVerPath)) {
|
|
if (Test-Path -Path $semVerPath) {
|
|
$semverValueRaw = $null
|
|
$semverValueVersion = $null
|
|
try {
|
|
$semverValueRaw = (ConvertFrom-Json (Get-Content -Path $semVerPath -Raw))
|
|
if (($null -ne $semverValueRaw) -and ($null -ne $semverValueRaw.Version)) {
|
|
$semverValueVersion = "$($semverValueRaw.Version.Major).$($semverValueRaw.Version.Minor).$($semverValueRaw.Version.Patch)"
|
|
}
|
|
$csproj.SemVer = @{
|
|
Version = $semverValueVersion
|
|
Raw = $semverValueRaw
|
|
}
|
|
} catch {
|
|
Write-Warning "$logLead : Could not capture the semver from [$semverPath]"
|
|
}
|
|
}
|
|
}
|
|
|
|
#region look for specific nuget packages
|
|
Write-Verbose "$logLead : Categorize nuget packages"
|
|
$newRelicAgentApiPackage = $packages.Where({ $_.PackageId.StartsWith('NewRelic.Agent.Api', [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
if (Any $newRelicAgentApiPackage) {
|
|
$csproj.NewRelicAgentApi = @{
|
|
Version = @($newRelicAgentApiPackage)[0].Version
|
|
}
|
|
}
|
|
|
|
$msCore = $packages.Where({ $_.PackageId.Equals('Alkami.MicroServices.Core', [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
if (Any $msCore) {
|
|
$csproj.MicroservicesCore = @{
|
|
Version = @($msCore)[0].Version
|
|
}
|
|
}
|
|
|
|
$fluentMigratorPackage = $packages.Where({ $_.PackageId.StartsWith('FluentMigrator', [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
if (Any $fluentMigratorPackage) {
|
|
$csproj.FluentMigrator = @{
|
|
# only take the first one found, because there could be .Runner, .Tools, etc
|
|
Version = @($fluentMigratorPackage)[0].Version
|
|
}
|
|
}
|
|
|
|
$topshelfPackage = $packages.Where({ $_.PackageId.StartsWith('TopShelf', [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
if (Any $topshelfPackage) {
|
|
$csproj.TopShelf = @{
|
|
Version = @($topshelfPackage.Version)[0].Version
|
|
}
|
|
}
|
|
|
|
# Ends in .Tests?
|
|
foreach ($testFrameworkPrefix in @('NUnit', 'XUnit', 'MSTest')) {
|
|
$projectHasTestReferences = $packages.Where({ $_.PackageId.StartsWith($testFrameworkPrefix, [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
$csproj.HasTestReferences = Any $projectHasTestReferences
|
|
if ($csproj.HasTestReferences) {
|
|
break
|
|
}
|
|
}
|
|
#endregion look for specific nuget packages
|
|
|
|
#region look for manifest details
|
|
Write-Verbose "$logLead : Categorize Manifest"
|
|
$projectUsesLegacyServiceInstaller = $packages.Where({ $_.PackageId.StartsWith('Alkami.MicroServices.Installer', [System.StringComparison]::InvariantCultureIgnoreCase) })
|
|
if (Any $projectUsesLegacyServiceInstaller) {
|
|
$csproj.AlkamiManifest.ComponentType = "Service"
|
|
$csproj.AlkamiManifest.HasAlkamiManifest = $false
|
|
$csproj.ServiceInstaller = @{
|
|
Name = $projectUsesLegacyServiceInstaller.PackageId
|
|
Version = $projectUsesLegacyServiceInstaller.Version
|
|
IsMaster = $projectUsesLegacyServiceInstaller.PackageId -match 'Master'
|
|
Legacy = $true
|
|
}
|
|
$csproj.AlkamiManifest.IsLegacyInstaller = $true
|
|
}
|
|
|
|
# cover the case of .json or .yaml
|
|
# Take the first one we find
|
|
$projectHasAlkamiManifestPath = @($files.Where({ $_.Name.StartsWith("AlkamiManifest") -and ($_.Name.ToLower().IndexOf("explain") -eq -1) }).FullName)[0]
|
|
$projectHasAlkamiManifest = ![string]::IsNullOrWhiteSpace($projectHasAlkamiManifestPath)
|
|
if ($projectHasAlkamiManifest) {
|
|
try {
|
|
$packageManifest = (Get-PackageManifest -Path $projectHasAlkamiManifestPath)
|
|
$csproj.AlkamiManifest.ComponentType = $packageManifest.general.componentType
|
|
$csproj.AlkamiManifest.AlkamiManifest = $packageManifest
|
|
$csproj.AlkamiManifest.HasAlkamiManifest = $true
|
|
} catch {
|
|
Write-Verbose "Something failed to process on [$projectHasAlkamiManifestPath]"
|
|
}
|
|
} else {
|
|
if (!$projectUsesLegacyServiceInstaller) {
|
|
$componentType = ''
|
|
# try to determine what type of project it should be
|
|
# TODO: Shorten the lookup by consolidating left of pipe
|
|
$providers = Get-ChildItem -Path (Join-Path -Path $baseFolder -ChildPath "*.cs") -Recurse | Select-String -Pattern "public class .+:\s+(ConnectorBase|ProviderBase)\b" | Select-Object -Property Path
|
|
$webExtensions = Get-ChildItem -Path (Join-Path -Path $baseFolder -ChildPath "*.cs") -Recurse | Select-String -Pattern "public class .+:\s+(IAlkamiWebExtension|IAlkamiModule)\b" | Select-Object -Property Path
|
|
$widgets = Get-ChildItem -Path (Join-Path -Path $baseFolder -ChildPath "*.cs") -Recurse | Select-String -Pattern "public class .+:\s+(.+WidgetDescription|.+AppRegistration)\b" | Select-Object -Property Path
|
|
if ($null -ne $providers) {
|
|
$componentType = 'Provider'
|
|
}
|
|
if ($null -ne $widgets) {
|
|
$componentType = 'Widget'
|
|
}
|
|
if ($null -ne $webExtensions) {
|
|
$componentType = 'WebExtension'
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($componentType)) {
|
|
$csProj.HasProgramMain = Get-ChildItem -Path (Join-Path -Path $baseFolder -ChildPath "*.cs") -Recurse | Select-String -Pattern "(public|private)\s?.*\s+Main\s*\(" | Select-Object -Property Path
|
|
}
|
|
$csproj.AlkamiManifest.ComponentType = $componentType
|
|
$csproj.AlkamiManifest.IsUnkownInstaller = $true
|
|
}
|
|
}
|
|
|
|
$csProj.ServicePointManagerLines = Get-ChildItem -Path (Join-Path -Path $baseFolder -ChildPath "*.cs") -Recurse | Select-String -Pattern "ServicePointManager" | Where-Object { -not $_.Line.Trim().StartsWith("//") } | ForEach-Object { "$($_.Path):$($_.LineNumber) :: $($_.Line)" }
|
|
|
|
Write-Verbose "$logLead : Categorize tools"
|
|
$toolsPath = Join-Path -Path $baseFolder -ChildPath "Tools"
|
|
if (Test-Path -Path $toolsPath) {
|
|
$csproj.Tools = @{
|
|
Files = (Get-ChildItem -Path $toolsPath -File).Name
|
|
}
|
|
if ($csproj.Tools.Files -match 'choco.*\.ps1') {
|
|
$csproj.ChocoFiles.HasChocoFiles = $true
|
|
}
|
|
$chocolateyInstallPath = Join-Path -Path $toolsPath -ChildPath "chocolateyInstall.ps1"
|
|
$chocolateyUninstallPath = Join-Path -Path $toolsPath -ChildPath "chocolateyUninstall.ps1"
|
|
if (Test-Path -Path $chocolateyInstallPath) {
|
|
$csproj.ChocoFiles.InstallFileSize = (Get-Item -Path $chocolateyInstallPath).Length
|
|
if ((Get-Item -Path $chocolateyInstallPath).Length -lt $magicChocoFilesLengthValue) {
|
|
$csproj.ChocoFiles.InstallFileNew = $true
|
|
}
|
|
}
|
|
if (Test-Path -Path $chocolateyUninstallPath) {
|
|
$csproj.ChocoFiles.UninstallFileSize = (Get-Item -Path $chocolateyUninstallPath).Length
|
|
if ($csproj.ChocoFiles.UninstallFileSize -lt $magicChocoFilesLengthValue) {
|
|
$csproj.ChocoFiles.UninstallFileNew = $true
|
|
}
|
|
}
|
|
}
|
|
#endregion look for manifest details
|
|
|
|
# above checks tools folder, this checks root folder, solution level folder check is elsewhere
|
|
$chocolateyInstallPath = Join-Path -Path $baseFolder -ChildPath "chocolateyInstall.ps1"
|
|
$chocolateyUninstallPath = Join-Path -Path $baseFolder -ChildPath "chocolateyUninstall.ps1"
|
|
if (Test-Path -Path $chocolateyInstallPath) {
|
|
$csproj.ChocoFiles.InstallFileSize = (Get-Item -Path $chocolateyInstallPath).Length
|
|
if ((Get-Item -Path $chocolateyInstallPath).Length -lt $magicChocoFilesLengthValue) {
|
|
$csproj.ChocoFiles.InstallFileNew = $true
|
|
}
|
|
}
|
|
if (Test-Path -Path $chocolateyUninstallPath) {
|
|
$csproj.ChocoFiles.UninstallFileSize = (Get-Item -Path $chocolateyUninstallPath).Length
|
|
if ($csproj.ChocoFiles.UninstallFileSize -lt $magicChocoFilesLengthValue) {
|
|
$csproj.ChocoFiles.UninstallFileNew = $true
|
|
}
|
|
}
|
|
|
|
$csproj.ShouldHaveAManifestedInstaller = (
|
|
($csproj.Nuspec.IsChocolatey -and -not $csproj.AlkamiManifest.HasAlkamiManifest) -or
|
|
$true -eq $csproj.ServiceInstaller.Legacy -or
|
|
$csproj.AlkamiManifest.IsLegacyInstaller
|
|
)
|
|
|
|
# TODO: Tools folder nuspec?
|
|
$nuspecPath = @($files.Where({$_.Name.EndsWith(".nuspec")}).FullName)[0]
|
|
if (![string]::IsNullOrWhiteSpace($nuspecPath)) {
|
|
$nuspecContent = [xml](Get-Content $nuspecPath)
|
|
$projectNuspecId = $nuspecContent.package.metadata.id
|
|
$projectNuspecVersion = $nuspecContent.package.metadata.version
|
|
# If it doesn't have a chocolatey include, it's a problem
|
|
$nuspecIsChocolatey = ($nuspecContent.package.files.file.src.EndsWith('chocolateyInstall.ps1').where({$_}).Count -gt 0)
|
|
$csproj.Nuspec.HasNuspecFile = $true
|
|
$csproj.Nuspec.IsChocolatey = $nuspecIsChocolatey
|
|
$csproj.Nuspec.PackageId = $projectNuspecId
|
|
$csproj.Nuspec.Version = $projectNuspecVersion
|
|
$csproj.Nuspec.Raw = $nuspecContent
|
|
}
|
|
|
|
return (New-Object -TypeName PSCustomObject -Property $csproj)
|
|
} |