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 # 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 }