ps/Modules/Alkami.PowerShell.Configuration/Public/Get-PackageManifest.ps1

193 lines
8.1 KiB
PowerShell
Raw Normal View History

2023-05-30 22:51:22 -07:00
function Get-PackageManifest {
<#
.SYNOPSIS
Given a path, find the AlkamiManifest in that location.
However, this should return either an xml, json, etc formatted value as a dotted object
This also validates the manifest to be valid
.PARAMETER Path
This can be the path to the known manifest file, or to a folder.
This will find the first manifest preferring xml over json etc, in a given folder.
This folder does not expect to find manifests lower than the current file, but may look in the parent folder for a manifest
.PARAMETER RawContent
Can be used to pass in raw content to let it be parsed for us
.PARAMETER SkipTests
Use this to skip tests when reading the package. Use sparingly.
.PARAMETER PackageName
Optional package name.
.PARAMETER ManifestSource
Optional information including the feed source, name, and version
#>
[CmdletBinding(DefaultParameterSetName = 'Path')]
[OutputType([object])]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'Path')]
[ValidateNotNullOrEmpty()]
$Path,
[Parameter(Mandatory = $true, ParameterSetName = 'RawContent')]
[ValidateNotNullOrEmpty()]
$RawContent,
[switch]$SkipTests,
[Parameter(Mandatory = $false)]
[string]$PackageName,
[Parameter(Mandatory = $false)]
[string]$ManifestSource
)
$logLead = Get-LogLeadName
$fileContent = $RawContent
$extension = $null
if ($PSCmdlet.ParameterSetName -eq 'Path') {
$isPackage = $false
$packagePath = (Get-ChocolateyInstallPath)
$validManifestFilenames = (Get-ValidPackageManifestFilenames)
if (!(Test-Path $Path)) {
throw "$logLead : Provided path was not valid, or is not found. Please ensure your value is a valid path on disk. Path: [$Path]"
}
# Normally this would write an error to indicate a problem
# We don't need that, we're just curious if it matches a package location so we can treat it like a package
if (Test-PathIsInApprovedPackageLocation -Path $Path -ErrorAction SilentlyContinue) {
$isPackage = $true
}
$item = (Get-Item $Path)
$leafName = $null
if (!$item.PSIsContainer) {
# path was a folder
$leafName = (Split-Path -Path $Path -Leaf)
# make $Path be a folder only
$Path = (Split-Path -Path $Path -Parent)
} else {
# Otherwise $Path is already a folder
}
if (![string]::IsNullOrWhiteSpace($leafName)) {
if (!$validManifestFilenames.Contains($leafName)) {
Write-Warning "$logLead : Supplied file is not a valid manifest name. Defaulting to a manifest filename lookup"
$leafName = $null
}
}
if ([string]::IsNullOrWhiteSpace($leafName)) {
# this means "label the loop that you want to exit early in the break statement later"
:fileLookup do {
foreach ($candidateName in $validManifestFilenames) {
$candidatePath = (Join-Path -Path $Path -ChildPath $candidateName)
Write-Verbose "$logLead : Looking for a manifest candidate at [$candidatePath]"
$item = (Get-Item -Path $candidatePath -ErrorAction Ignore)
if ($null -ne $item) {
# we found an item, this is good
$leafName = $candidateName
break fileLookup
}
}
$Path = (Split-Path $Path -Parent)
if ([string]::IsNullOrWhiteSpace($Path)) {
Write-Error "Could not find an AlkamiManifest in the specified folder or parent folders"
return $null
}
if ($isPackage -and (Test-PathsAreEqual -Source $Path -Target $packagePath)) {
Write-Error "$logLead : Parent package folder found, no manifest found. Please verify expected folder contains a manifest."
return $null
}
} while ($true)
}
$manifestPath = (Join-Path -Path $Path -ChildPath $leafName)
if (!(Test-Path $manifestPath)) {
Write-Error "$logLead : Could not find manifest file in path [$Path]"
}
$fileContent = (Get-Content -Path $manifestPath -Raw)
if ([string]::IsNullOrWhiteSpace($fileContent)) {
throw "$logLead : Content of [$manifestPath] is empty or whitespace. This file can not be empty for validation to continue"
}
$extension = [System.IO.Path]::GetExtension($manifestPath)
} else {
# fileContent was set to rawContent
if ($null -ne $fileContent) {
if ($fileContent -is [string]) {
$firstCharacter = $fileContent[0]
if ($firstCharacter -eq '<') {
$extension = '.xml'
}
if ($firstCharacter -eq '{') {
$extension = '.json'
}
} elseif ($fileContent -is [xml]) {
$extension = '.xml'
} else {
# heck, idk, let's pretend it's json and try it and see what happens
$extension = '.json'
}
if (!(Test-StringIsNullOrWhitespace -Value $ManifestSource)) {
$manifestPath = $ManifestSource
} elseif (!(Test-StringIsNullOrWhitespace -Value $PackageName)) {
$manifestPath = $PackageName
} else {
$manifestPath = "File content was provided with no name"
}
}
}
$targetObject = $null
switch ($extension) {
'.json' { $targetObject = (ConvertFrom-Json $fileContent) }
'.xml' {
try {
$targetObject = (ConvertFrom-Xml ([xml]($fileContent))).packageManifest
} catch [System.Management.Automation.RuntimeException] {
if ($null -ne $_.FullyQualifiedErrorId -and $_.FullyQualifiedErrorId -eq "InvalidCastToXmlDocument") {
if($null -eq $PackageName)
{
# Append a random 4 digit number so we don't get identity collisions
$date = Get-Date
$random = Get-Random -Maximum 10000 -SetSeed $date.Millisecond
$PackageName = "UnknownPackageName_$random"
}
$problemMessage = "Couldn't parse manifest XML for [$PackageName]. Please contact the appropriate dev team."
Write-Warning "$loglead : $problemMessage. Exception was:"
Write-Warning $_.Exception
$sanitizedProblemMessage = ConvertTo-SafeTeamCityMessage -InputText $problemMessage
Write-Host "##teamcity[buildProblem description='$sanitizedProblemMessage' identity='$PackageName']"
}
}
# even if there was a content there, it may not have had a root called <packageManifest>
# anchoring to remove the .packageManifest reduces a lot of other complexities elsewhere
if ($null -eq $targetObject) {
throw "$logLead : No valid packageManifest found in [$manifestPath]"
}
}
default {throw "$logLead : The content passed in appears to not be xml or json. Please verify the source data is an AlkamiManifest content."}
}
# Can't combine the two jumps because it doesn't early-abort on comparisons
# Use skipping tests sparingly, prefer to only when they are already installed.
# That does not mean "comparing the path is in the package repository location"
# because for chocolatey it always will be, it's small sub-paths differentiation there.
if (!$SkipTests) {
if (!(Test-AlkamiManifest -TargetObject $targetObject)) {
Write-Warning "$logLead : Manifest at [$manifestPath] failed processing. Please resolve errors and reprocess."
throw "$logLead : Can not parse manifest properly."
}
}
return $targetObject
}