2023-12-17 15:04:52 +03:00
<#
. Synopsis
PROOF OF CONCEPT PREVIEW : Download Apps from Business Central NuGet Package to folder
. Description
Download Apps from Business Central NuGet Package to folder
. PARAMETER nuGetServerUrl
NuGet Server URL
Default : https : / / api . nuget . org / v3 / index . json
. PARAMETER nuGetToken
NuGet Token for authenticated access to the NuGet Server
If not specified , the NuGet Server is accessed anonymously ( and needs to support this )
. PARAMETER packageName
Package Name to search for .
This can be the full name or a partial name with wildcards .
If more than one package is found , matching the name , an error is thrown .
. PARAMETER version
Package Version , following the nuget versioning rules
https : / / learn . microsoft . com / en-us / nuget / concepts / package-versioning #version-ranges
. PARAMETER select
Select the package to download if more than one package is found matching the name and version
- Earliest : Select the earliest version
- Latest : Select the latest version ( default )
- LatestMatching : Select the latest version matching the already installed dependencies
- Exact : Select the exact version
- Any : Select the first version found
. PARAMETER folder
Folder where the apps are copied to
. PARAMETER copyInstalledAppsToFolder
If specified , apps are also copied to this folder
. PARAMETER installedPlatform
Version of the installed platform
. PARAMETER installedCountry
2024-01-02 10:39:59 +03:00
Country of the installed application . installedCountry is used to determine if the NuGet package is compatible with the installed application localization
2023-12-17 15:04:52 +03:00
. PARAMETER installedApps
List of installed apps
Format is an array of PSCustomObjects with properties Name , Publisher , id and Version
. PARAMETER downloadDependencies
Specifies which dependencies to download
Allowed values are :
- all : Download all dependencies
- own : Download only dependencies that has the same publisher as the package
- allButMicrosoft : Download all dependencies except packages with publisher Microsoft
- allButApplication : Download all dependencies except the Application and Platform packages ( Microsoft . Application and Microsoft . Platform )
- allButPlatform : Download all dependencies except the Platform package ( Microsoft . Platform )
- none : Do not download any dependencies
. PARAMETER allowPrerelease
Include prerelease versions in the search
#>
Function Download-BcNuGetPackageToFolder {
Param (
[ Parameter ( Mandatory = $false ) ]
[ string ] $nuGetServerUrl = " " ,
[ Parameter ( Mandatory = $false ) ]
[ string ] $nuGetToken = " " ,
[ Parameter ( Mandatory = $true ) ]
[ string ] $packageName ,
[ Parameter ( Mandatory = $false ) ]
[ string ] $version = '0.0.0.0' ,
[ Parameter ( Mandatory = $false ) ]
[ ValidateSet ( 'Earliest' , 'Latest' , 'LatestMatching' , 'Exact' , 'Any' ) ]
[ string ] $select = 'Latest' ,
[ Parameter ( Mandatory = $true ) ]
[ alias ( 'appSymbolsFolder' ) ]
[ string ] $folder ,
[ Parameter ( Mandatory = $false ) ]
[ string ] $copyInstalledAppsToFolder = " " ,
[ Parameter ( Mandatory = $false ) ]
[ System.Version ] $installedPlatform ,
[ Parameter ( Mandatory = $false ) ]
2024-01-02 10:39:59 +03:00
[ string ] $installedCountry = '' ,
2023-12-17 15:04:52 +03:00
[ Parameter ( Mandatory = $false ) ]
[ PSCustomObject[] ] $installedApps = @ ( ) ,
[ ValidateSet ( 'all' , 'own' , 'allButMicrosoft' , 'allButApplication' , 'allButPlatform' , 'none' ) ]
[ string ] $downloadDependencies = 'allButApplication' ,
2024-02-14 12:56:25 +03:00
[ switch ] $allowPrerelease ,
[ switch ] $checkLocalVersion
2023-12-17 15:04:52 +03:00
)
2024-01-31 10:03:29 +03:00
try {
2023-12-17 15:04:52 +03:00
$findSelect = $select
if ( $select -eq 'LatestMatching' ) {
$findSelect = 'Latest'
}
$excludeVersions = @ ( )
2024-02-14 12:56:25 +03:00
if ( $checkLocalVersion ) {
# Format Publisher.Name[.Country][.symbols][.AppId]
if ( $packageName -match '^(Microsoft)\.([^\.]+)(\.[^\.][^\.])?(\.symbols)?(\.[0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})?$' ) {
$publisher = $matches [ 1 ]
$name = $matches [ 2 ]
$countryPart = " $( $matches [ 3 ] ) "
$symbolsPart = " $( $matches [ 4 ] ) "
$appIdPart = " $( $matches [ 5 ] ) "
$checkPackageName = ''
if ( $name -ne 'Platform' -and $countryPart -eq '' -and $installedCountry -ne '' ) {
$countryPart = " . $installedCountry "
$checkPackageName = " $publisher . $name $countryPart $symbolsPart $appIdPart "
}
if ( $checkPackageName -and $checkPackageName -ne $packageName ) {
$downloadedPackages = Download-BcNuGetPackageToFolder -nuGetServerUrl $nuGetServerUrl -nuGetToken $nuGetToken -packageName $checkPackageName -version $version -folder $folder -copyInstalledAppsToFolder $copyInstalledAppsToFolder -installedPlatform $installedPlatform -installedCountry $installedCountry -installedApps $installedApps -downloadDependencies $downloadDependencies -verbose: ( $VerbosePreference -eq 'Continue' ) -select $select -allowPrerelease: $allowPrerelease
if ( $downloadedPackages ) {
return $downloadedPackages
}
}
return Download-BcNuGetPackageToFolder -nuGetServerUrl $nuGetServerUrl -nuGetToken $nuGetToken -packageName $packageName -version $version -folder $folder -copyInstalledAppsToFolder $copyInstalledAppsToFolder -installedPlatform $installedPlatform -installedCountry $installedCountry -installedApps $installedApps -downloadDependencies $downloadDependencies -verbose: ( $VerbosePreference -eq 'Continue' ) -select $select -allowPrerelease: $allowPrerelease
}
}
2023-12-17 15:04:52 +03:00
Write-Host " Looking for NuGet package $packageName version $version ( $select match) "
2024-02-14 12:56:25 +03:00
if ( $packageName -match '^Microsoft\.Platform(\.symbols)?$' ) {
if ( $installedPlatform ) {
$existingPlatform = $installedPlatform
}
else {
$existingPlatform = $installedApps | Where-Object { $_ -and $_ . Name -eq 'Platform' } | Select-Object -ExpandProperty Version
}
if ( $existingPlatform -and ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $existingPlatform , $version ) ) ) {
Write-Host " Microsoft.Platform version $existingPlatform is already available "
return @ ( )
}
}
elseif ( $packageName -match '^([^\.]+\.)?Application(\.[^\.]+)?(\.symbols)?$' ) {
$installedApp = $installedApps | Where-Object { $_ -and $_ . Name -eq 'Application' }
if ( $installedApp -and ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $installedApp . Version , $version ) ) ) {
Write-Host " Application version $( $installedApp . Version ) is already available "
return @ ( )
}
}
elseif ( $packageName -match '^([^\.]+)\.([^\.]+)(\.[^\.][^\.])?(\.symbols)?(\.[0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})?$' ) {
$installedApp = $installedApps | Where-Object { $_ -and $_ . id -and $packageName -like " * $( $_ . id ) * " }
if ( $installedApp -and ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $installedApp . Version , $version ) ) ) {
Write-Host " $( $installedApp . Name ) from $( $installedApp . publisher ) version $( $installedApp . Version ) is already available (AppId= $( $installedApp . id ) ) "
return @ ( )
}
}
2023-12-17 15:04:52 +03:00
while ( $true ) {
2024-02-14 12:56:25 +03:00
$returnValue = @ ( )
2023-12-17 15:04:52 +03:00
$feed , $packageId , $packageVersion = Find-BcNugetPackage -nuGetServerUrl $nuGetServerUrl -nuGetToken $nuGetToken -packageName $packageName -version $version -excludeVersions $excludeVersions -verbose: ( $VerbosePreference -eq 'Continue' ) -select $findSelect -allowPrerelease: ( $allowPrerelease . IsPresent )
if ( -not $feed ) {
Write-Host " No package found matching package name $( $packageName ) Version $( $version ) "
break
}
else {
Write-Host " Best match for package name $( $packageName ) Version $( $version ) : $packageId Version $packageVersion from $( $feed . Url ) "
$package = $feed . DownloadPackage ( $packageId , $packageVersion )
$nuspec = Get-Content ( Join-Path $package '*.nuspec' -Resolve ) -Encoding UTF8
2023-12-19 00:49:19 +03:00
Write-Verbose " NUSPEC: "
$nuspec | ForEach-Object { Write-Verbose $_ }
2023-12-17 15:04:52 +03:00
$manifest = [ xml ] $nuspec
2024-02-14 12:56:25 +03:00
$appId = ''
2024-02-21 10:23:08 +03:00
if ( $manifest . package . metadata . PSObject . Properties . Name -eq 'title' ) {
$appName = $manifest . package . metadata . title
}
elseif ( $manifest . package . metadata . PSObject . Properties . Name -eq 'description' ) {
$appName = $manifest . package . metadata . description
}
else {
$appName = $manifest . package . metadata . id
}
2024-02-14 12:56:25 +03:00
if ( $manifest . package . metadata . id -match '^.*([0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})$' ) {
# If packageId ends in a GUID (AppID) then use the AppId for the packageId
$appId = " $( $matches [ 1 ] ) "
}
elseif ( $manifest . package . metadata . id -like 'Microsoft.Platform*' ) {
# If packageId starts with Microsoft.Platform then use the packageId for the packageId
$appName = 'Platform'
}
$returnValue = @ ( [ PSCustomObject ] @ {
" Publisher " = $manifest . package . metadata . authors
" Name " = $appName
" id " = $appId
" Version " = $manifest . package . metadata . version
} )
2023-12-17 15:04:52 +03:00
$dependenciesErr = ''
2024-01-31 10:03:29 +03:00
if ( $manifest . package . metadata . PSObject . Properties . Name -eq 'Dependencies' ) {
$dependencies = $manifest . package . metadata . Dependencies . GetEnumerator ( )
}
else {
$dependencies = @ ( )
}
foreach ( $dependency in $dependencies ) {
2024-02-14 12:56:25 +03:00
if ( -not $installedPlatform ) {
$installedPlatform = $installedApps + $returnValue | Where-Object { $_ -and $_ . Name -eq 'Platform' } | Select-Object -ExpandProperty Version
}
2023-12-17 15:04:52 +03:00
$dependencyVersion = $dependency . Version
$dependencyId = $dependency . Id
2024-01-02 10:39:59 +03:00
$dependencyCountry = ''
2023-12-17 15:04:52 +03:00
$downloadIt = $false
2024-02-14 12:56:25 +03:00
if ( $dependencyId -match '^Microsoft\.Platform(\.symbols)?$' ) {
2024-01-02 10:39:59 +03:00
$dependencyPublisher = 'Microsoft'
2023-12-17 15:04:52 +03:00
# Dependency is to the platform
if ( $installedPlatform ) {
if ( ! ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $installedPlatform , $dependencyVersion ) ) ) {
# The NuGet package found isn't compatible with the installed platform
$dependenciesErr = " NuGet package $packageId (version $packageVersion ) requires platform $dependencyVersion . You cannot install it on version $installedPlatform "
}
2024-02-14 12:56:25 +03:00
$downloadIt = $false
2023-12-17 15:04:52 +03:00
}
else {
$downloadIt = ( $downloadDependencies -eq 'all' )
}
}
2024-02-14 12:56:25 +03:00
elseif ( $dependencyId -match '^([^\.]+\.)?Application(\.[^\.]+)?(\.symbols)?$' ) {
2023-12-17 15:04:52 +03:00
# Dependency is to the application
$dependencyPublisher = $matches [ 1 ] . TrimEnd ( '.' )
2024-02-14 12:56:25 +03:00
$dependencyCountry = " $( $matches [ 2 ] ) " . TrimStart ( '.' )
2023-12-17 15:04:52 +03:00
$installedApp = $installedApps | Where-Object { $_ -and $_ . Name -eq 'Application' }
if ( $installedApp ) {
if ( ! ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $installedApp . Version , $dependencyVersion ) ) ) {
$dependenciesErr = " NuGet package $packageId (version $packageVersion ) requires application $dependencyVersion . You cannot install it on version $( $installedApp . Version ) "
}
2024-02-14 12:56:25 +03:00
$downloadIt = $false
2023-12-17 15:04:52 +03:00
}
else {
$downloadIt = ( $downloadDependencies -eq 'all' -or $downloadDependencies -eq 'allButPlatform' )
}
}
else {
2024-01-02 10:39:59 +03:00
$dependencyPublisher = ''
2024-02-14 12:56:25 +03:00
if ( $dependencyId -match '^([^\.]+)\.([^\.]+)(\.[^\.][^\.])?(\.symbols)?(\.[0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})?$' ) {
# Matches publisher.name[.country][.symbols][.appId] format (country section is only for microsoft apps)
2024-01-02 10:39:59 +03:00
$dependencyPublisher = $matches [ 1 ]
2024-02-14 12:56:25 +03:00
if ( $dependencyPublisher -eq 'microsoft' ) {
$dependencyCountry = " $( $matches [ 3 ] ) " . TrimStart ( '.' )
2024-01-02 10:39:59 +03:00
}
}
2023-12-17 15:04:52 +03:00
$installedApp = $installedApps | Where-Object { $_ -and $_ . id -and $dependencyId -like " * $( $_ . id ) * " }
if ( $installedApp ) {
# Dependency is already installed, check version number
if ( ! ( [ NuGetFeed ] :: IsVersionIncludedInRange ( $installedApp . Version , $dependencyVersion ) ) ) {
# The version installed ins't compatible with the NuGet package found
$dependenciesErr = " Dependency $dependencyId is already installed with version $( $installedApp . Version ) , which is not compatible with the version $dependencyVersion required by the NuGet package $packageId (version $packageVersion )) "
}
}
2024-01-02 10:39:59 +03:00
elseif ( $downloadDependencies -eq 'own' ) {
$downloadIt = ( $dependencyPublisher -eq $manifest . package . authors )
}
elseif ( $downloadDependencies -eq 'allButMicrosoft' ) {
# Download if publisher isn't Microsoft (including if publisher is empty)
$downloadIt = ( $dependencyPublisher -ne 'Microsoft' )
2023-12-17 15:04:52 +03:00
}
else {
2024-01-02 10:39:59 +03:00
$downloadIt = ( $downloadDependencies -ne 'none' )
2023-12-17 15:04:52 +03:00
}
}
2024-02-14 12:56:25 +03:00
if ( $installedCountry -and $dependencyCountry -and ( $installedCountry -ne $dependencyCountry ) ) {
# The NuGet package found isn't compatible with the installed application
Write-Host " WARNING: NuGet package $packageId (version $packageVersion ) requires $dependencyCountry application. You have $installedCountry application installed "
}
2023-12-17 15:04:52 +03:00
if ( $dependenciesErr ) {
if ( $select -ne 'LatestMatching' ) {
throw $dependenciesErr
}
else {
2023-12-26 11:18:51 +03:00
# If we are looking for the latest matching version, then we can try to find another version
2023-12-17 15:04:52 +03:00
Write-Host " WARNING: $dependenciesErr "
break
}
}
if ( $downloadIt ) {
2024-02-14 12:56:25 +03:00
$returnValue + = Download-BcNuGetPackageToFolder -nuGetServerUrl $nuGetServerUrl -nuGetToken $nuGetToken -packageName $dependencyId -version $dependencyVersion -folder $package -copyInstalledAppsToFolder $copyInstalledAppsToFolder -installedPlatform $installedPlatform -installedCountry $installedCountry -installedApps @ ( $installedApps + $returnValue ) -downloadDependencies $downloadDependencies -verbose: ( $VerbosePreference -eq 'Continue' ) -select $select -allowPrerelease: $allowPrerelease -checkLocalVersion
2023-12-17 15:04:52 +03:00
}
}
if ( $dependenciesErr ) {
2023-12-26 11:18:51 +03:00
# If we are looking for the latest matching version, then we can try to find another version
2023-12-17 15:04:52 +03:00
$excludeVersions + = $packageVersion
2023-12-26 11:18:51 +03:00
Remove-Item -Path $package -Recurse -Force
2023-12-17 15:04:52 +03:00
continue
}
2024-01-27 00:35:50 +03:00
if ( $installedCountry -and ( Test-Path ( Join-Path $package $installedCountry ) -PathType Container ) ) {
2024-01-02 10:39:59 +03:00
# NuGet packages of Runtime packages might exist in different versions for different countries
# The runtime package might contain C# invoke calls with different methodis for different countries
# if the installedCountry doesn't have a special version, then the w1 version is used (= empty string)
# If the package contains a country specific folder, then use that
Write-Host " Using country specific folder $installedCountry "
$appFiles = Get-Item -Path ( Join-Path $package " $installedCountry /*.app " )
}
else {
$appFiles = Get-Item -Path ( Join-Path $package " *.app " )
}
foreach ( $appFile in $appFiles ) {
Copy-Item $appFile . FullName -Destination $folder -Force
2023-12-17 15:04:52 +03:00
if ( $copyInstalledAppsToFolder ) {
2024-01-02 10:39:59 +03:00
Copy-Item $appFile . FullName -Destination $copyInstalledAppsToFolder -Force
2023-12-17 15:04:52 +03:00
}
}
Remove-Item -Path $package -Recurse -Force
break
}
}
2024-01-02 10:39:59 +03:00
return $returnValue
2023-12-17 15:04:52 +03:00
}
2024-01-31 10:03:29 +03:00
catch {
Write-Host -ForegroundColor Red " Error Message: $( $_ . Exception . Message . Replace ( " `r " , '' ) . Replace ( " `n " , ' ' ) ) `r `n StackTrace: $( $_ . ScriptStackTrace ) "
throw
}
}
2023-12-17 15:04:52 +03:00
Set-Alias -Name Copy-BcNuGetPackageToFolder -Value Download-BcNuGetPackageToFolder
Export-ModuleMember -Function Download-BcNuGetPackageToFolder -Alias Copy-BcNuGetPackageToFolder