2023-05-24 22:56:46 +03:00
$useTimeOutWebClient = $false
2023-07-15 10:19:01 +03:00
if ( $PSVersionTable . PSVersion -lt " 6.0.0 " -or $useTimeOutWebClient ) {
2023-05-24 23:16:42 +03:00
$timeoutWebClientCode = @"
2023-05-24 22:53:38 +03:00
using System . Net ;
public class TimeoutWebClient : WebClient
{
int theTimeout ;
public TimeoutWebClient ( int timeout )
{
theTimeout = timeout ;
}
protected override WebRequest GetWebRequest ( System . Uri address )
{
WebRequest request = base . GetWebRequest ( address ) ;
if ( request ! = null )
{
request . Timeout = theTimeout ;
}
return request ;
}
}
" @;
2023-07-15 10:19:01 +03:00
if ( -not ( [ System.Management.Automation.PSTypeName ] " TimeoutWebClient " ) . Type ) {
Add-Type -TypeDefinition $timeoutWebClientCode -Language CSharp -WarningAction SilentlyContinue | Out-Null
$useTimeOutWebClient = $true
}
2023-05-24 22:53:38 +03:00
}
2023-05-24 22:56:46 +03:00
$sslCallbackCode = @"
2023-07-15 10:19:01 +03:00
using System . Net . Security ;
using System . Security . Cryptography . X509Certificates ;
public static class SslVerification
{
public static bool DisabledServerCertificateValidationCallback ( object sender , X509Certificate certificate , X509Chain chain , SslPolicyErrors sslPolicyErrors ) { return true ; }
public static void Disable ( ) { System . Net . ServicePointManager . ServerCertificateValidationCallback = DisabledServerCertificateValidationCallback ; }
public static void Enable ( ) { System . Net . ServicePointManager . ServerCertificateValidationCallback = null ; }
public static void DisableSsl ( System . Net . Http . HttpClientHandler handler ) { handler . ServerCertificateCustomValidationCallback = DisabledServerCertificateValidationCallback ; }
}
2023-05-24 22:56:46 +03:00
" @
2023-07-15 10:19:01 +03:00
if ( -not ( [ System.Management.Automation.PSTypeName ] " SslVerification " ) . Type ) {
if ( $isPsCore ) {
2023-05-24 22:56:46 +03:00
Add-Type -TypeDefinition $sslCallbackCode -Language CSharp -WarningAction SilentlyContinue | Out-Null
}
2023-07-15 10:19:01 +03:00
else {
Add-Type -TypeDefinition $sslCallbackCode -Language CSharp -ReferencedAssemblies @ ( 'System.Net.Http' ) -WarningAction SilentlyContinue | Out-Null
}
2023-05-24 22:56:46 +03:00
}
2023-05-24 22:53:38 +03:00
function Get-DefaultCredential {
Param (
[ string ] $Message ,
[ string ] $DefaultUserName ,
[ switch ] $doNotAskForCredential
)
if ( Test-Path " $( $bcContainerHelperConfig . hostHelperFolder ) \settings.ps1 " ) {
. " $( $bcContainerHelperConfig . hostHelperFolder ) \settings.ps1 "
if ( Test-Path " $( $bcContainerHelperConfig . hostHelperFolder ) \aes.key " ) {
$key = Get-Content -Path " $( $bcContainerHelperConfig . hostHelperFolder ) \aes.key "
New-Object System . Management . Automation . PSCredential ( $DefaultUserName , ( ConvertTo-SecureString -String $adminPassword -Key $key ) )
2023-05-24 23:16:42 +03:00
}
else {
2023-05-24 22:53:38 +03:00
New-Object System . Management . Automation . PSCredential ( $DefaultUserName , ( ConvertTo-SecureString -String $adminPassword ) )
}
2023-05-24 23:16:42 +03:00
}
else {
2023-05-24 22:53:38 +03:00
if ( ! $doNotAskForCredential ) {
Get-Credential -username $DefaultUserName -Message $Message
}
}
}
function Get-DefaultSqlCredential {
Param (
2023-05-24 23:16:42 +03:00
[ Parameter ( Mandatory = $true ) ]
2023-05-24 22:53:38 +03:00
[ string ] $containerName ,
[ System.Management.Automation.PSCredential ] $sqlCredential = $null ,
[ switch ] $doNotAskForCredential
)
if ( $sqlCredential -eq $null ) {
$containerAuth = Get-NavContainerAuth -containerName $containerName
if ( $containerAuth -ne " Windows " ) {
$sqlCredential = Get-DefaultCredential -DefaultUserName " " -Message " Please enter the SQL Server System Admin credentials for container $containerName " -doNotAskForCredential: $doNotAskForCredential
}
}
$sqlCredential
}
function CmdDo {
Param (
[ string ] $command = " " ,
[ string ] $arguments = " " ,
[ switch ] $silent ,
[ switch ] $returnValue ,
[ string ] $inputStr = " "
)
$oldNoColor = " $env:NO_COLOR "
$env:NO_COLOR = " Y "
$oldEncoding = [ Console ] :: OutputEncoding
try { [ Console ] :: OutputEncoding = [ System.Text.Encoding ] :: UTF8 } catch { }
try {
$result = $true
$pinfo = New-Object System . Diagnostics . ProcessStartInfo
$pinfo . FileName = $command
$pinfo . RedirectStandardError = $true
$pinfo . RedirectStandardOutput = $true
if ( $inputStr ) {
$pinfo . RedirectStandardInput = $true
}
$pinfo . WorkingDirectory = Get-Location
$pinfo . UseShellExecute = $false
$pinfo . Arguments = $arguments
$pinfo . WindowStyle = [ System.Diagnostics.ProcessWindowStyle ] :: Minimized
$p = New-Object System . Diagnostics . Process
$p . StartInfo = $pinfo
$p . Start ( ) | Out-Null
if ( $inputStr ) {
$p . StandardInput . WriteLine ( $inputStr )
$p . StandardInput . Close ( )
}
$outtask = $p . StandardOutput . ReadToEndAsync ( )
$errtask = $p . StandardError . ReadToEndAsync ( )
$p . WaitForExit ( ) ;
$message = $outtask . Result
$err = $errtask . Result
if ( " $err " -ne " " ) {
$message + = " $err "
}
$message = $message . Trim ( )
if ( $p . ExitCode -eq 0 ) {
if ( ! $silent ) {
Write-Host $message
}
if ( $returnValue ) {
2023-05-24 23:16:42 +03:00
$message . Replace ( " `r " , " " ) . Split ( " `n " )
2023-05-24 22:53:38 +03:00
}
}
else {
2023-05-24 23:16:42 +03:00
$message + = " `n `n ExitCode: " + $p . ExitCode + " `n Commandline: $command $arguments "
2023-05-24 22:53:38 +03:00
throw $message
}
}
finally {
try { [ Console ] :: OutputEncoding = $oldEncoding } catch { }
$env:NO_COLOR = $oldNoColor
}
}
function DockerDo {
Param (
2023-05-24 23:16:42 +03:00
[ Parameter ( Mandatory = $true ) ]
2023-05-24 22:53:38 +03:00
[ string ] $imageName ,
2023-07-18 18:21:11 +03:00
[ ValidateSet ( 'run' , 'start' , 'pull' , 'restart' , 'stop' , 'rmi' , 'build' ) ]
2023-05-24 22:53:38 +03:00
[ string ] $command = " run " ,
[ switch ] $accept_eula ,
[ switch ] $accept_outdated ,
[ switch ] $detach ,
[ switch ] $silent ,
[ string[] ] $parameters = @ ( )
)
if ( $accept_eula ) {
$parameters + = " --env accept_eula=Y "
}
if ( $accept_outdated ) {
$parameters + = " --env accept_outdated=Y "
}
if ( $detach ) {
$parameters + = " --detach "
}
$result = $true
2023-05-24 23:16:42 +03:00
$arguments = ( " $command " + [ string ] :: Join ( " " , $parameters ) + " $imageName " )
2023-05-24 22:53:38 +03:00
$pinfo = New-Object System . Diagnostics . ProcessStartInfo
$pinfo . FileName = " docker.exe "
$pinfo . RedirectStandardError = $true
$pinfo . RedirectStandardOutput = $true
$pinfo . UseShellExecute = $false
$pinfo . Arguments = $arguments
$p = New-Object System . Diagnostics . Process
$p . StartInfo = $pinfo
$p . Start ( ) | Out-Null
$outtask = $null
$errtask = $p . StandardError . ReadToEndAsync ( )
$out = " "
$err = " "
do {
if ( $outtask -eq $null ) {
$outtask = $p . StandardOutput . ReadLineAsync ( )
}
$outtask . Wait ( 100 ) | Out-Null
if ( $outtask . IsCompleted ) {
$outStr = $outtask . Result
if ( $outStr -eq $null ) {
break
}
if ( ! $silent ) {
Write-Host $outStr
}
$out + = $outStr
$outtask = $null
if ( $outStr . StartsWith ( " Please login " ) ) {
$registry = $imageName . Split ( " / " ) [ 0 ]
if ( $registry -eq " bcinsider.azurecr.io " -or $registry -eq " bcprivate.azurecr.io " ) {
throw " You need to login to $registry prior to pulling images. Get credentials through the ReadyToGo program on Microsoft Collaborate. "
2023-05-24 23:16:42 +03:00
}
else {
2023-05-24 22:53:38 +03:00
throw " You need to login to $registry prior to pulling images. "
}
}
2023-05-24 23:16:42 +03:00
}
elseif ( $outtask . IsCanceled ) {
2023-05-24 22:53:38 +03:00
break
2023-05-24 23:16:42 +03:00
}
elseif ( $outtask . IsFaulted ) {
2023-05-24 22:53:38 +03:00
break
}
2023-05-24 23:16:42 +03:00
} while ( ! ( $p . HasExited ) )
2023-05-24 22:53:38 +03:00
$err = $errtask . Result
$p . WaitForExit ( ) ;
if ( $p . ExitCode -ne 0 ) {
$result = $false
if ( ! $silent ) {
$out = $out . Trim ( )
$err = $err . Trim ( )
if ( $command -eq " run " -and " $out " -ne " " ) {
Docker rm $out -f
}
$errorMessage = " "
if ( " $err " -ne " " ) {
$errorMessage + = " $err `r `n "
if ( $err . Contains ( " authentication required " ) ) {
$registry = $imageName . Split ( " / " ) [ 0 ]
if ( $registry -eq " bcinsider.azurecr.io " -or $registry -eq " bcprivate.azurecr.io " ) {
$errorMessage + = " You need to login to $registry prior to pulling images. Get credentials through the ReadyToGo program on Microsoft Collaborate. `r `n "
2023-05-24 23:16:42 +03:00
}
else {
2023-05-24 22:53:38 +03:00
$errorMessage + = " You need to login to $registry prior to pulling images. `r `n "
}
}
}
2023-05-24 23:16:42 +03:00
$errorMessage + = " ExitCode: " + $p . ExitCode + " `r `n Commandline: docker $arguments "
2023-05-24 22:53:38 +03:00
Write-Error -Message $errorMessage
}
}
$result
}
function Get-NavContainerAuth {
Param
(
[ string ] $containerName
)
Invoke-ScriptInNavContainer -containerName $containerName -ScriptBlock {
$customConfigFile = Join-Path ( Get-Item " C:\Program Files\Microsoft Dynamics NAV\*\Service " ) . FullName " CustomSettings.config "
[ xml ] $customConfig = [ System.IO.File ] :: ReadAllText ( $customConfigFile )
$customConfig . SelectSingleNode ( " //appSettings/add[@key='ClientServicesCredentialType'] " ) . Value
}
}
function Check-BcContainerName {
Param
(
[ string ] $containerName = " "
)
if ( $containerName -eq " " ) {
throw " Container name cannot be empty "
}
if ( $containerName . Length -gt 15 ) {
Write-Host " WARNING: Container name should not exceed 15 characters "
}
$first = $containerName . ToLowerInvariant ( ) [ 0 ]
if ( $first -lt " a " -or $first -gt " z " ) {
throw " Container name should start with a letter (a-z) "
}
$containerName . ToLowerInvariant ( ) . ToCharArray ( ) | ForEach-Object {
if ( ( $_ -lt " a " -or $_ -gt " z " ) -and ( $_ -lt " 0 " -or $_ -gt " 9 " ) -and ( $_ -ne " - " ) ) {
throw " Container name contains invalid characters. Allowed characters are letters (a-z), numbers (0-9) and dashes (-) "
}
}
}
function AssumeNavContainer {
Param
(
[ string ] $containerOrImageName = " " ,
[ string ] $functionName = " "
)
$inspect = docker inspect $containerOrImageName | ConvertFrom-Json
if ( $inspect . Config . Labels . psobject . Properties . Match ( 'maintainer' ) . Count -eq 0 -or $inspect . Config . Labels . maintainer -ne " Dynamics SMB " ) {
throw " Container $containerOrImageName is not a Business Central container "
}
[ System.Version ] $version = $inspect . Config . Labels . version
if ( " $functionName " -eq " " ) {
$functionName = ( Get-Variable MyInvocation -Scope 1 ) . Value . MyCommand . Name
}
if ( $version . Major -ge 15 ) {
throw " Container $containerOrImageName does not support the function $functionName "
}
}
function TestSasToken {
Param
(
2023-09-27 16:09:36 +03:00
[ string ] $url
2023-05-24 22:53:38 +03:00
)
2023-09-27 16:09:36 +03:00
$sasToken = " ? $( " $( $url ) ? " . Split ( '?' ) [ 1 ] ) "
if ( $sasToken -eq '?' ) {
# No SAS token in URL
return
}
try {
$se = $sasToken . Split ( '?' ) [ 1 ] . Split ( '&' ) | Where-Object { $_ . StartsWith ( 'se=' ) } | ForEach-Object { [ Uri ] :: UnescapeDataString ( $_ . Substring ( 3 ) ) }
$st = $sasToken . Split ( '?' ) [ 1 ] . Split ( '&' ) | Where-Object { $_ . StartsWith ( 'st=' ) } | ForEach-Object { [ Uri ] :: UnescapeDataString ( $_ . Substring ( 3 ) ) }
$sv = $sasToken . Split ( '?' ) [ 1 ] . Split ( '&' ) | Where-Object { $_ . StartsWith ( 'sv=' ) } | ForEach-Object { [ Uri ] :: UnescapeDataString ( $_ . Substring ( 3 ) ) }
$sig = $sasToken . Split ( '?' ) [ 1 ] . Split ( '&' ) | Where-Object { $_ . StartsWith ( 'sig=' ) } | ForEach-Object { [ Uri ] :: UnescapeDataString ( $_ . Substring ( 4 ) ) }
if ( $sv -ne '2021-10-04' -or $sig -eq '' -or $se -eq '' -or $st -eq '' ) {
throw " Wrong format "
2023-05-24 22:53:38 +03:00
}
2023-09-27 16:09:36 +03:00
if ( [ DateTime ] :: Now -lt [ DateTime ] $st ) {
Write-Host " ::ERROR::The sas token provided isn't valid before $( ( [ DateTime ] $st ) . ToString ( ) ) "
}
if ( [ DateTime ] :: Now -gt [ DateTime ] $se ) {
Write-Host " ::ERROR::The sas token provided expired on $( ( [ DateTime ] $se ) . ToString ( ) ) "
2023-05-24 22:53:38 +03:00
}
2023-09-27 16:09:36 +03:00
elseif ( [ DateTime ] :: Now . AddDays ( 14 ) -gt [ DateTime ] $se ) {
Write-Host " ::WARNING::The sas token provided will expire on $( ( [ DateTime ] $se ) . ToString ( ) ) "
}
}
catch {
$message = $_ . ToString ( )
throw " The sas token provided is not valid, error message was: $message "
2023-05-24 22:53:38 +03:00
}
}
function Expand-7zipArchive {
Param (
2023-05-24 23:16:42 +03:00
[ Parameter ( Mandatory = $true ) ]
2023-05-24 22:53:38 +03:00
[ string ] $Path ,
[ string ] $DestinationPath ,
2023-12-17 15:04:52 +03:00
[ switch ] $use7zipIfAvailable = $bcContainerHelperConfig . use7zipIfAvailable ,
[ switch ] $silent
2023-05-24 22:53:38 +03:00
)
$7zipPath = " $env:ProgramFiles \7-Zip\7z.exe "
$use7zip = $false
if ( $use7zipIfAvailable -and ( Test-Path -Path $7zipPath -PathType Leaf ) ) {
try {
$use7zip = [ System.Diagnostics.FileVersionInfo ] :: GetVersionInfo ( $7zipPath ) . FileMajorPart -ge 19
}
catch {
$use7zip = $false
}
}
if ( $use7zip ) {
2023-12-17 15:04:52 +03:00
if ( ! $silent ) { Write-Host " using 7zip " }
2023-05-24 22:53:38 +03:00
Set-Alias -Name 7z -Value $7zipPath
2023-05-24 23:16:42 +03:00
$command = '7z x "{0}" -o"{1}" -aoa -r' -f $Path , $DestinationPath
2023-05-24 22:53:38 +03:00
$global:LASTEXITCODE = 0
Invoke-Expression -Command $command | Out-Null
if ( $LASTEXITCODE -ne 0 ) {
throw " Error $LASTEXITCODE extracting $path "
}
2023-05-24 23:16:42 +03:00
}
else {
2023-12-17 15:04:52 +03:00
if ( ! $silent ) { Write-Host " using Expand-Archive " }
2023-05-24 22:53:38 +03:00
if ( [ System.IO.Path ] :: GetExtension ( $path ) -eq '.zip' ) {
Expand-Archive -Path $Path -DestinationPath " $DestinationPath " -Force
}
else {
$tempZip = Join-Path ( [ System.IO.Path ] :: GetTempPath ( ) ) " $( [ guid ] :: NewGuid ( ) . ToString ( ) ) .zip "
Copy-Item -Path $Path -Destination $tempZip -Force
try {
Expand-Archive -Path $tempZip -DestinationPath " $DestinationPath " -Force
}
finally {
Remove-Item -Path $tempZip -Force
}
}
}
}
function GetTestToolkitApps {
Param (
[ string ] $containerName ,
[ switch ] $includeTestLibrariesOnly ,
[ switch ] $includeTestFrameworkOnly ,
[ switch ] $includeTestRunnerOnly ,
[ switch ] $includePerformanceToolkit
)
Invoke-ScriptInBCContainer -containerName $containerName -scriptblock { Param ( $includeTestLibrariesOnly , $includeTestFrameworkOnly , $includeTestRunnerOnly , $includePerformanceToolkit )
$version = [ Version ] ( Get-Item " C:\Program Files\Microsoft Dynamics NAV\*\Service\Microsoft.Dynamics.Nav.Server.exe " ) . VersionInfo . FileVersion
# Add Test Framework
$apps = @ ( )
if ( ( $version -ge [ Version ] " 19.0.0.0 " ) -and ( Test-Path 'C:\Applications\TestFramework\TestLibraries\permissions mock' ) ) {
$apps + = @ ( get-childitem -Path " C:\Applications\TestFramework\TestLibraries\permissions mock\*.* " -recurse -filter " *.app " )
}
$apps + = @ ( get-childitem -Path " C:\Applications\TestFramework\TestRunner\*.* " -recurse -filter " *.app " )
if ( ! $includeTestRunnerOnly ) {
$apps + = @ ( get-childitem -Path " C:\Applications\TestFramework\TestLibraries\*.* " -recurse -filter " *.app " )
if ( ! $includeTestFrameworkOnly ) {
# Add Test Libraries
$apps + = " Microsoft_System Application Test Library.app " , " Microsoft_Tests-TestLibraries.app " | ForEach-Object {
@ ( get-childitem -Path " C:\Applications\*.* " -recurse -filter $_ )
}
if ( ! $includeTestLibrariesOnly ) {
# Add Tests
$apps + = @ ( get-childitem -Path " C:\Applications\*.* " -recurse -filter " Microsoft_Tests-*.app " ) | Where-Object { $_ -notlike " *\Microsoft_Tests-TestLibraries.app " -and ( $version . Major -ge 17 -or ( $_ -notlike " *\Microsoft_Tests-Marketing.app " ) ) -and $_ -notlike " *\Microsoft_Tests-SINGLESERVER.app " }
}
}
}
if ( $includePerformanceToolkit ) {
$apps + = @ ( get-childitem -Path " C:\Applications\TestFramework\PerformanceToolkit\*.* " -recurse -filter " *Toolkit.app " )
if ( ! $includeTestFrameworkOnly ) {
$apps + = @ ( get-childitem -Path " C:\Applications\TestFramework\PerformanceToolkit\*.* " -recurse -filter " *.app " -exclude " *Toolkit.app " )
}
}
$apps | ForEach-Object {
2023-05-24 23:16:42 +03:00
$appFile = Get-ChildItem -path " c:\applications.*\*.* " -recurse -filter ( $_ . Name ) . Replace ( " .app " , " _*.app " )
2023-05-24 22:53:38 +03:00
if ( ! ( $appFile ) ) {
$appFile = $_
}
$appFile . FullName
}
} -argumentList $includeTestLibrariesOnly , $includeTestFrameworkOnly , $includeTestRunnerOnly , $includePerformanceToolkit
}
function GetExtendedErrorMessage {
Param (
$errorRecord
)
$exception = $errorRecord . Exception
$message = $exception . Message
2023-05-27 12:05:37 +03:00
try {
if ( $errorRecord . ErrorDetails ) {
2023-05-24 22:56:46 +03:00
$errorDetails = $errorRecord . ErrorDetails | ConvertFrom-Json
$message + = " $( $errorDetails . error ) `r `n $( $errorDetails . error_description ) "
}
2023-05-24 22:53:38 +03:00
}
2023-05-27 12:05:37 +03:00
catch { }
2023-05-24 22:53:38 +03:00
try {
if ( $exception -is [ System.Management.Automation.MethodInvocationException ] ) {
$exception = $exception . InnerException
}
if ( $exception -is [ System.Net.Http.HttpRequestException ] ) {
$message + = " `r `n $( $exception . Message ) "
if ( $exception . InnerException ) {
2023-07-04 08:15:57 +03:00
if ( $exception . InnerException -and $exception . InnerException . Message ) {
2023-05-24 22:53:38 +03:00
$message + = " `r `n $( $exception . InnerException . Message ) "
}
}
2023-07-04 08:15:57 +03:00
2023-05-24 22:53:38 +03:00
}
2023-07-04 08:15:57 +03:00
else {
$webException = [ System.Net.WebException ] $exception
$webResponse = $webException . Response
try {
if ( $webResponse . StatusDescription ) {
$message + = " `r `n $( $webResponse . StatusDescription ) "
}
2023-05-24 22:53:38 +03:00
}
2023-07-04 08:15:57 +03:00
catch { }
$reqstream = $webResponse . GetResponseStream ( )
$sr = new-object System . IO . StreamReader $reqstream
$result = $sr . ReadToEnd ( )
2023-05-24 23:16:42 +03:00
}
2023-05-24 22:53:38 +03:00
try {
$json = $result | ConvertFrom-Json
$message + = " `r `n $( $json . Message ) "
}
catch {
$message + = " `r `n $result "
}
try {
$correlationX = $webResponse . GetResponseHeader ( 'ms-correlation-x' )
if ( $correlationX ) {
$message + = " (ms-correlation-x = $correlationX ) "
}
}
catch { }
}
2023-05-24 23:16:42 +03:00
catch { }
2023-05-24 22:53:38 +03:00
$message
}
function CopyAppFilesToFolder {
Param (
$appFiles ,
[ string ] $folder
)
if ( $appFiles -is [ String ] ) {
if ( ! ( Test-Path $appFiles ) ) {
$appFiles = @ ( $appFiles . Split ( ',' ) . Trim ( ) | Where-Object { $_ } )
}
}
if ( ! ( Test-Path $folder ) ) {
New-Item -Path $folder -ItemType Directory | Out-Null
}
2023-12-26 11:18:51 +03:00
$appFiles | Where-Object { $_ } | ForEach-Object {
2023-05-24 22:53:38 +03:00
$appFile = $_
if ( $appFile -like " http://* " -or $appFile -like " https://* " ) {
$appUrl = $appFile
$appFile = Join-Path ( [ System.IO.Path ] :: GetTempPath ( ) ) ( [ Guid ] :: NewGuid ( ) . ToString ( ) )
Download-File -sourceUrl $appUrl -destinationFile $appFile
CopyAppFilesToFolder -appFile $appFile -folder $folder
Remove-Item -Path $appFile -Force
}
elseif ( Test-Path $appFile -PathType Container ) {
get-childitem $appFile -Filter '*.app' -Recurse | ForEach-Object {
$destFile = Join-Path $folder $_ . Name
if ( Test-Path $destFile ) {
2023-07-04 08:15:57 +03:00
Write-Host -ForegroundColor Yellow " ::WARNING:: $( [ System.IO.Path ] :: GetFileName ( $destFile ) ) already exists, it looks like you have multiple app files with the same name. App filenames must be unique. "
2023-05-24 22:53:38 +03:00
}
Copy-Item -Path $_ . FullName -Destination $destFile -Force
$destFile
}
}
elseif ( Test-Path $appFile -PathType Leaf ) {
2023-07-29 06:56:58 +03:00
Get-ChildItem $appFile | ForEach-Object {
$appFile = $_ . FullName
if ( [ string ] :: new ( [ char[] ] ( Get-Content $appFile @byteEncodingParam -TotalCount 2 ) ) -eq " PK " ) {
$tmpFolder = Join-Path ( [ System.IO.Path ] :: GetTempPath ( ) ) ( [ Guid ] :: NewGuid ( ) . ToString ( ) )
$copied = $false
try {
if ( $appFile -notlike " *.zip " ) {
$orgAppFile = $appFile
$appFile = Join-Path ( [ System.IO.Path ] :: GetTempPath ( ) ) " $( [ System.IO.Path ] :: GetFileName ( $orgAppFile ) ) .zip "
Copy-Item $orgAppFile $appFile
$copied = $true
}
Expand-Archive $appfile -DestinationPath $tmpFolder -Force
2023-12-26 11:18:51 +03:00
Get-ChildItem -Path $tmpFolder -Recurse | Where-Object { $_ . Name -like " *.app " -or $_ . Name -like " *.zip " } | ForEach-Object {
2023-07-29 06:56:58 +03:00
CopyAppFilesToFolder -appFile $_ . FullName -folder $folder
}
2023-05-24 22:53:38 +03:00
}
2023-07-29 06:56:58 +03:00
finally {
Remove-Item -Path $tmpFolder -Recurse -Force
if ( $copied ) { Remove-Item -Path $appFile -Force }
2023-05-24 22:53:38 +03:00
}
}
2023-07-29 06:56:58 +03:00
else {
$destFile = Join-Path $folder " $( [ System.IO.Path ] :: GetFileNameWithoutExtension ( $appFile ) ) .app "
if ( Test-Path $destFile ) {
Write-Host -ForegroundColor Yellow " ::WARNING:: $( [ System.IO.Path ] :: GetFileName ( $destFile ) ) already exists, it looks like you have multiple app files with the same name. App filenames must be unique. "
}
Copy-Item -Path $appFile -Destination $destFile -Force
$destFile
2023-05-24 22:53:38 +03:00
}
}
}
else {
2023-07-04 08:15:57 +03:00
Write-Host -ForegroundColor Red " ::WARNING::File not found: $appFile "
2023-05-24 22:53:38 +03:00
}
}
}
function getCountryCode {
Param (
[ string ] $countryCode
)
$countryCodes = @ {
2023-05-24 23:16:42 +03:00
" Afghanistan " = " AF "
" Åland Islands " = " AX "
" Albania " = " AL "
" Algeria " = " DZ "
" American Samoa " = " AS "
" AndorrA " = " AD "
" Angola " = " AO "
" Anguilla " = " AI "
" Antarctica " = " AQ "
" Antigua and Barbuda " = " AG "
" Argentina " = " AR "
" Armenia " = " AM "
" Aruba " = " AW "
" Australia " = " AU "
" Austria " = " AT "
" Azerbaijan " = " AZ "
" Bahamas " = " BS "
" Bahrain " = " BH "
" Bangladesh " = " BD "
" Barbados " = " BB "
" Belarus " = " BY "
" Belgium " = " BE "
" Belize " = " BZ "
" Benin " = " BJ "
" Bermuda " = " BM "
" Bhutan " = " BT "
" Bolivia " = " BO "
" Bosnia and Herzegovina " = " BA "
" Botswana " = " BW "
" Bouvet Island " = " BV "
" Brazil " = " BR "
" British Indian Ocean Territory " = " IO "
" Brunei Darussalam " = " BN "
" Bulgaria " = " BG "
" Burkina Faso " = " BF "
" Burundi " = " BI "
" Cambodia " = " KH "
" Cameroon " = " CM "
" Canada " = " CA "
" Cape Verde " = " CV "
" Cayman Islands " = " KY "
" Central African Republic " = " CF "
" Chad " = " TD "
" Chile " = " CL "
" China " = " CN "
" Christmas Island " = " CX "
" Cocos (Keeling) Islands " = " CC "
" Colombia " = " CO "
" Comoros " = " KM "
" Congo " = " CG "
" Congo, The Democratic Republic of the " = " CD "
" Cook Islands " = " CK "
" Costa Rica " = " CR "
" Cote D'Ivoire " = " CI "
" Croatia " = " HR "
" Cuba " = " CU "
" Cyprus " = " CY "
" Czech Republic " = " CZ "
" Denmark " = " DK "
" Djibouti " = " DJ "
" Dominica " = " DM "
" Dominican Republic " = " DO "
" Ecuador " = " EC "
" Egypt " = " EG "
" El Salvador " = " SV "
" Equatorial Guinea " = " GQ "
" Eritrea " = " ER "
" Estonia " = " EE "
" Ethiopia " = " ET "
" Falkland Islands (Malvinas) " = " FK "
" Faroe Islands " = " FO "
" Fiji " = " FJ "
" Finland " = " FI "
" France " = " FR "
" French Guiana " = " GF "
" French Polynesia " = " PF "
" French Southern Territories " = " TF "
" Gabon " = " GA "
" Gambia " = " GM "
" Georgia " = " GE "
" Germany " = " DE "
" Ghana " = " GH "
" Gibraltar " = " GI "
" Greece " = " GR "
" Greenland " = " GL "
" Grenada " = " GD "
" Guadeloupe " = " GP "
" Guam " = " GU "
" Guatemala " = " GT "
" Guernsey " = " GG "
" Guinea " = " GN "
" Guinea-Bissau " = " GW "
" Guyana " = " GY "
" Haiti " = " HT "
" Heard Island and Mcdonald Islands " = " HM "
" Holy See (Vatican City State) " = " VA "
" Honduras " = " HN "
" Hong Kong " = " HK "
" Hungary " = " HU "
" Iceland " = " IS "
" India " = " IN "
" Indonesia " = " ID "
" Iran, Islamic Republic Of " = " IR "
" Iraq " = " IQ "
" Ireland " = " IE "
" Isle of Man " = " IM "
" Israel " = " IL "
" Italy " = " IT "
" Jamaica " = " JM "
" Japan " = " JP "
" Jersey " = " JE "
" Jordan " = " JO "
" Kazakhstan " = " KZ "
" Kenya " = " KE "
" Kiribati " = " KI "
" Korea, Democratic People's Republic of " = " KP "
" Korea, Republic of " = " KR "
" Kuwait " = " KW "
" Kyrgyzstan " = " KG "
" Lao People's Democratic Republic " = " LA "
" Latvia " = " LV "
" Lebanon " = " LB "
" Lesotho " = " LS "
" Liberia " = " LR "
" Libyan Arab Jamahiriya " = " LY "
" Liechtenstein " = " LI "
" Lithuania " = " LT "
" Luxembourg " = " LU "
" Macao " = " MO "
" Macedonia, The Former Yugoslav Republic of " = " MK "
" Madagascar " = " MG "
" Malawi " = " MW "
" Malaysia " = " MY "
" Maldives " = " MV "
" Mali " = " ML "
" Malta " = " MT "
" Marshall Islands " = " MH "
" Martinique " = " MQ "
" Mauritania " = " MR "
" Mauritius " = " MU "
" Mayotte " = " YT "
" Mexico " = " MX "
" Micronesia, Federated States of " = " FM "
" Moldova, Republic of " = " MD "
" Monaco " = " MC "
" Mongolia " = " MN "
" Montserrat " = " MS "
" Morocco " = " MA "
" Mozambique " = " MZ "
" Myanmar " = " MM "
" Namibia " = " NA "
" Nauru " = " NR "
" Nepal " = " NP "
" Netherlands " = " NL "
" Netherlands Antilles " = " AN "
" New Caledonia " = " NC "
" New Zealand " = " NZ "
" Nicaragua " = " NI "
" Niger " = " NE "
" Nigeria " = " NG "
" Niue " = " NU "
" Norfolk Island " = " NF "
" Northern Mariana Islands " = " MP "
" Norway " = " NO "
" Oman " = " OM "
" Pakistan " = " PK "
" Palau " = " PW "
" Palestinian Territory, Occupied " = " PS "
" Panama " = " PA "
" Papua New Guinea " = " PG "
" Paraguay " = " PY "
" Peru " = " PE "
" Philippines " = " PH "
" Pitcairn " = " PN "
" Poland " = " PL "
" Portugal " = " PT "
" Puerto Rico " = " PR "
" Qatar " = " QA "
" Reunion " = " RE "
" Romania " = " RO "
" Russian Federation " = " RU "
" RWANDA " = " RW "
" Saint Helena " = " SH "
" Saint Kitts and Nevis " = " KN "
" Saint Lucia " = " LC "
" Saint Pierre and Miquelon " = " PM "
" Saint Vincent and the Grenadines " = " VC "
" Samoa " = " WS "
" San Marino " = " SM "
" Sao Tome and Principe " = " ST "
" Saudi Arabia " = " SA "
" Senegal " = " SN "
" Serbia and Montenegro " = " CS "
" Seychelles " = " SC "
" Sierra Leone " = " SL "
" Singapore " = " SG "
" Slovakia " = " SK "
" Slovenia " = " SI "
" Solomon Islands " = " SB "
" Somalia " = " SO "
" South Africa " = " ZA "
2023-05-24 22:53:38 +03:00
" South Georgia and the South Sandwich Islands " = " GS "
2023-05-24 23:16:42 +03:00
" Spain " = " ES "
" Sri Lanka " = " LK "
" Sudan " = " SD "
" Suriname " = " SR "
" Svalbard and Jan Mayen " = " SJ "
" Swaziland " = " SZ "
" Sweden " = " SE "
" Switzerland " = " CH "
" Syrian Arab Republic " = " SY "
" Taiwan, Province of China " = " TW "
" Tajikistan " = " TJ "
" Tanzania, United Republic of " = " TZ "
" Thailand " = " TH "
" Timor-Leste " = " TL "
" Togo " = " TG "
" Tokelau " = " TK "
" Tonga " = " TO "
" Trinidad and Tobago " = " TT "
" Tunisia " = " TN "
" Turkey " = " TR "
" Turkmenistan " = " TM "
" Turks and Caicos Islands " = " TC "
" Tuvalu " = " TV "
" Uganda " = " UG "
" Ukraine " = " UA "
" United Arab Emirates " = " AE "
" United Kingdom " = " GB "
" United States " = " US "
" United States Minor Outlying Islands " = " UM "
" Uruguay " = " UY "
" Uzbekistan " = " UZ "
" Vanuatu " = " VU "
" Venezuela " = " VE "
" Viet Nam " = " VN "
" Virgin Islands, British " = " VG "
" Virgin Islands, U.S. " = " VI "
" Wallis and Futuna " = " WF "
" Western Sahara " = " EH "
" Yemen " = " YE "
" Zambia " = " ZM "
" Zimbabwe " = " ZW "
" Hong Kong SAR " = " HK "
" Serbia " = " RS "
" Korea " = " KR "
" Taiwan " = " TW "
" Vietnam " = " VN "
2023-05-24 22:53:38 +03:00
}
$countryCode = $countryCode . Trim ( )
if ( $countryCodes . ContainsValue ( $countryCode . ToUpperInvariant ( ) ) ) {
return $countryCode . ToLowerInvariant ( )
}
elseif ( $countryCodes . ContainsKey ( $countryCode ) ) {
return ( $countryCodes [ $countryCode ] ) . ToLowerInvariant ( )
}
else {
throw " Country code $countryCode is illegal "
}
}
function Get-WWWRootPath {
$inetstp = Get-Item " HKLM:\SOFTWARE\Microsoft\InetStp " -ErrorAction SilentlyContinue
if ( $inetstp ) {
[ System.Environment ] :: ExpandEnvironmentVariables ( $inetstp . GetValue ( " PathWWWRoot " ) )
}
else {
" "
}
}
function Parse-JWTtoken([string]$token ) {
if ( $token . Contains ( " . " ) -and $token . StartsWith ( " eyJ " ) ) {
$tokenPayload = $token . Split ( " . " ) [ 1 ] . Replace ( '-' , '+' ) . Replace ( '_' , '/' )
while ( $tokenPayload . Length % 4 ) { $tokenPayload + = " = " }
return [ System.Text.Encoding ] :: UTF8 . GetString ( [ System.Convert ] :: FromBase64String ( $tokenPayload ) ) | ConvertFrom-Json
}
throw " Invalid token "
}
function Test-BcAuthContext {
Param (
$bcAuthContext
)
if ( ! ( ( $bcAuthContext -is [ Hashtable ] ) -and
( $bcAuthContext . ContainsKey ( 'ClientID' ) ) -and
( $bcAuthContext . ContainsKey ( 'Credential' ) ) -and
( $bcAuthContext . ContainsKey ( 'authority' ) ) -and
( $bcAuthContext . ContainsKey ( 'RefreshToken' ) ) -and
( $bcAuthContext . ContainsKey ( 'UtcExpiresOn' ) ) -and
( $bcAuthContext . ContainsKey ( 'tenantID' ) ) -and
( $bcAuthContext . ContainsKey ( 'AccessToken' ) ) -and
( $bcAuthContext . ContainsKey ( 'includeDeviceLogin' ) ) -and
( $bcAuthContext . ContainsKey ( 'deviceLoginTimeout' ) ) ) ) {
throw 'BcAuthContext should be a HashTable created by New-BcAuthContext.'
}
}
Function CreatePsTestToolFolder {
Param (
[ string ] $containerName ,
[ string ] $PsTestToolFolder
)
$PsTestFunctionsPath = Join-Path $PsTestToolFolder " PsTestFunctions.ps1 "
$ClientContextPath = Join-Path $PsTestToolFolder " ClientContext.ps1 "
$newtonSoftDllPath = Join-Path $PsTestToolFolder " NewtonSoft.json.dll "
$clientDllPath = Join-Path $PsTestToolFolder " Microsoft.Dynamics.Framework.UI.Client.dll "
if ( ! ( Test-Path -Path $PsTestToolFolder -PathType Container ) ) {
New-Item -Path $PsTestToolFolder -ItemType Directory | Out-Null
Copy-Item -Path ( Join-Path $PSScriptRoot " AppHandling\PsTestFunctions.ps1 " ) -Destination $PsTestFunctionsPath -Force
Copy-Item -Path ( Join-Path $PSScriptRoot " AppHandling\ClientContext.ps1 " ) -Destination $ClientContextPath -Force
}
Invoke-ScriptInBcContainer -containerName $containerName { Param ( [ string ] $myNewtonSoftDllPath , [ string ] $myClientDllPath )
if ( ! ( Test-Path $myNewtonSoftDllPath ) ) {
$newtonSoftDllPath = " C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\NewtonSoft.json.dll "
if ( ! ( Test-Path $newtonSoftDllPath ) ) {
$newtonSoftDllPath = " C:\Program Files\Microsoft Dynamics NAV\*\Service\NewtonSoft.json.dll "
}
$newtonSoftDllPath = ( Get-Item $newtonSoftDllPath ) . FullName
Copy-Item -Path $newtonSoftDllPath -Destination $myNewtonSoftDllPath
}
if ( ! ( Test-Path $myClientDllPath ) ) {
$clientDllPath = " C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll "
Copy-Item -Path $clientDllPath -Destination $myClientDllPath
}
} -argumentList ( Get-BcContainerPath -containerName $containerName -Path $newtonSoftDllPath ) , ( Get-BcContainerPath -containerName $containerName -Path $clientDllPath )
}
function RandomChar([string]$str ) {
$rnd = Get-Random -Maximum $str . length
[ string ] $str [ $rnd ]
}
function GetRandomPassword {
$cons = 'bcdfghjklmnpqrstvwxz'
$voc = 'aeiouy'
$numbers = '0123456789'
( ( RandomChar $cons ) . ToUpper ( ) + `
2023-05-24 23:16:42 +03:00
( RandomChar $voc ) + `
( RandomChar $cons ) + `
( RandomChar $voc ) + `
( RandomChar $numbers ) + `
( RandomChar $numbers ) + `
( RandomChar $numbers ) + `
( RandomChar $numbers ) )
2023-05-24 22:53:38 +03:00
}
function getVolumeMountParameter($volumes , $hostPath , $containerPath ) {
$volume = $volumes | Where-Object { $_ -like " *| $hostPath " -or $_ -like " $hostPath |* " }
if ( $volume ) {
$volumeName = $volume . Split ( '|' ) [ 1 ]
" --mount source= $( $volumeName ) ,target= $containerPath "
}
else {
" --volume "" $( $hostPath ) : $( $containerPath ) "" "
}
}
function testPfxCertificate([string ] $pfxFile , [ SecureString ] $pfxPassword , [ string ] $certkind ) {
if ( ! ( Test-Path $pfxFile ) ) {
throw " $certkind certificate file does not exist "
}
try {
$cert = New-Object System . Security . Cryptography . X509Certificates . X509Certificate2 ( $pfxFile , $pfxPassword )
}
catch {
throw " Unable to read $certkind certificate. Error was $( $_ . Exception . Message ) "
}
if ( [ DateTime ] :: Now -gt $cert . NotAfter ) {
throw " $certkind certificate expired on $( $cert . GetExpirationDateString ( ) ) "
}
if ( [ DateTime ] :: Now -gt $cert . NotAfter . AddDays ( -14 ) ) {
Write-Host -ForegroundColor Yellow " $certkind certificate will expire on $( $cert . GetExpirationDateString ( ) ) "
}
}
function GetHash {
param (
[ string ] $str
)
$stream = [ IO.MemoryStream ] :: new ( [ Text.Encoding ] :: UTF8 . GetBytes ( $str ) )
( Get-FileHash -InputStream $stream -Algorithm SHA256 ) . Hash
}
function Wait-Task {
param (
[ Parameter ( Mandatory , ValueFromPipeline ) ]
[ System.Threading.Tasks.Task[] ] $Task
)
Begin {
$Tasks = @ ( )
}
Process {
$Tasks + = $Task
}
End {
While ( -not [ System.Threading.Tasks.Task ] :: WaitAll ( $Tasks , 200 ) ) { }
$Tasks . ForEach ( { $_ . GetAwaiter ( ) . GetResult ( ) } )
}
}
Set-Alias -Name await -Value Wait-Task -Force
function DownloadFileLow {
Param (
[ string ] $sourceUrl ,
[ string ] $destinationFile ,
[ switch ] $dontOverwrite ,
[ switch ] $useDefaultCredentials ,
2023-05-24 22:56:46 +03:00
[ switch ] $skipCertificateCheck ,
2023-05-24 22:53:38 +03:00
[ hashtable ] $headers = @ { " UserAgent " = " BcContainerHelper $bcContainerHelperVersion " } ,
[ int ] $timeout = 100
)
if ( $useTimeOutWebClient ) {
Write-Host " Downloading using WebClient "
2023-05-24 22:56:46 +03:00
if ( $skipCertificateCheck ) {
Write-Host " Disabling SSL Verification "
[ SslVerification ] :: Disable ( )
}
2023-05-24 23:16:42 +03:00
$webClient = New-Object TimeoutWebClient -ArgumentList ( 1000 * $timeout )
2023-05-24 22:53:38 +03:00
$headers . Keys | ForEach-Object {
$webClient . Headers . Add ( $_ , $headers . " $_ " )
}
$webClient . UseDefaultCredentials = $useDefaultCredentials
if ( Test-Path $destinationFile -PathType Leaf ) {
if ( $dontOverwrite ) {
return
}
Remove-Item -Path $destinationFile -Force
}
try {
$webClient . DownloadFile ( $sourceUrl , $destinationFile )
}
finally {
$webClient . Dispose ( )
2023-05-24 22:56:46 +03:00
if ( $skipCertificateCheck ) {
Write-Host " Restoring SSL Verification "
[ SslVerification ] :: Enable ( )
}
2023-05-24 22:53:38 +03:00
}
}
else {
2023-05-24 22:56:46 +03:00
Write-Host " Downloading using HttpClient "
$handler = New-Object System . Net . Http . HttpClientHandler
if ( $skipCertificateCheck ) {
2023-07-15 10:19:01 +03:00
Write-Host " Disabling SSL Verification on HttpClient "
[ SslVerification ] :: DisableSsl ( $handler )
2023-05-24 22:56:46 +03:00
}
if ( $useDefaultCredentials ) {
$handler . UseDefaultCredentials = $true
}
$httpClient = New-Object System . Net . Http . HttpClient -ArgumentList $handler
$httpClient . Timeout = [ Timespan ] :: FromSeconds ( $timeout )
$headers . Keys | ForEach-Object {
$httpClient . DefaultRequestHeaders . Add ( $_ , $headers . " $_ " )
}
$stream = $null
$fileStream = $null
if ( $dontOverwrite ) {
$fileMode = [ System.IO.FileMode ] :: CreateNew
2023-05-24 22:53:38 +03:00
}
else {
2023-05-24 22:56:46 +03:00
$fileMode = [ System.IO.FileMode ] :: Create
}
try {
$stream = $httpClient . GetStreamAsync ( $sourceUrl ) . GetAwaiter ( ) . GetResult ( )
$fileStream = New-Object System . IO . Filestream ( $destinationFile , $fileMode )
$stream . CopyToAsync ( $fileStream ) . GetAwaiter ( ) . GetResult ( ) | Out-Null
$fileStream . Close ( )
}
finally {
if ( $fileStream ) {
$fileStream . Dispose ( )
2023-05-24 22:53:38 +03:00
}
2023-05-24 22:56:46 +03:00
if ( $stream ) {
$stream . Dispose ( )
2023-05-24 22:53:38 +03:00
}
}
}
}
2023-09-26 11:30:00 +03:00
function LoadDLL {
Param (
[ string ] $path
)
$bytes = [ System.IO.File ] :: ReadAllBytes ( $path )
[ System.Reflection.Assembly ] :: Load ( $bytes ) | Out-Null
}
2023-05-24 22:53:38 +03:00
function GetAppInfo {
Param (
[ string[] ] $appFiles ,
[ string ] $compilerFolder ,
2023-09-26 13:52:01 +03:00
[ string ] $cacheAppInfoPath = ''
2023-05-24 22:53:38 +03:00
)
2023-09-26 13:52:01 +03:00
$appInfoCache = $null
$cacheUpdated = $false
if ( $cacheAppInfoPath ) {
if ( Test-Path $cacheAppInfoPath ) {
$appInfoCache = Get-Content -Path $cacheAppInfoPath -Encoding utf8 | ConvertFrom-Json
}
else {
$appInfoCache = @ { }
}
}
2023-12-17 15:04:52 +03:00
Write-Host " ::group::Getting .app info $cacheAppInfoPath "
2023-05-24 22:53:38 +03:00
$binPath = Join-Path $compilerFolder 'compiler/extension/bin'
if ( $isLinux ) {
$alcPath = Join-Path $binPath 'linux'
2023-12-01 11:46:19 +03:00
$alToolExe = Join-Path $alcPath 'altool'
2023-05-24 22:53:38 +03:00
}
else {
$alcPath = Join-Path $binPath 'win32'
2023-12-01 11:46:19 +03:00
$alToolExe = Join-Path $alcPath 'altool.exe'
2023-05-24 22:53:38 +03:00
}
if ( -not ( Test-Path $alcPath ) ) {
$alcPath = $binPath
}
2023-12-01 11:46:19 +03:00
$alToolExists = Test-Path -Path $alToolExe -PathType Leaf
2023-05-24 22:53:38 +03:00
$alcDllPath = $alcPath
if ( ! $isLinux -and ! $isPsCore ) {
$alcDllPath = $binPath
}
2023-09-26 11:30:00 +03:00
$ErrorActionPreference = " STOP "
$assembliesAdded = $false
$packageStream = $null
$package = $null
try {
2023-09-26 13:52:01 +03:00
foreach ( $path in $appFiles ) {
2023-09-26 11:30:00 +03:00
Write-Host -NoNewline " - $( [ System.IO.Path ] :: GetFileName ( $path ) ) "
2023-09-26 13:52:01 +03:00
if ( $appInfoCache -and $appInfoCache . PSObject . Properties . Name -eq $path ) {
$appInfo = $appInfoCache . " $path "
2023-09-26 11:30:00 +03:00
Write-Host " (cached) "
}
else {
2023-12-01 11:46:19 +03:00
if ( $alToolExists ) {
$manifest = CmdDo -Command $alToolExe -arguments @ ( 'GetPackageManifest' , " "" $path "" " ) -returnValue -silent | ConvertFrom-Json
$appInfo = @ {
" appId " = $manifest . id
" publisher " = $manifest . publisher
" name " = $manifest . name
" version " = $manifest . version
" application " = " $( if ( $manifest . PSObject . Properties . Name -eq 'application' ) { $manifest . application } ) "
" platform " = " $( if ( $manifest . PSObject . Properties . Name -eq 'platform' ) { $manifest . Platform } ) "
" propagateDependencies " = ( $manifest . PSObject . Properties . Name -eq 'PropagateDependencies' ) -and $manifest . PropagateDependencies
" dependencies " = @ ( if ( $manifest . PSObject . Properties . Name -eq 'dependencies' ) { $manifest . dependencies | ForEach-Object { @ { " id " = $_ . id ; " name " = $_ . name ; " publisher " = $_ . publisher ; " version " = $_ . version } } } )
}
2023-05-24 22:53:38 +03:00
}
2023-12-01 11:46:19 +03:00
else {
if ( ! $assembliesAdded ) {
Add-Type -AssemblyName System . IO . Compression . FileSystem
Add-Type -AssemblyName System . Text . Encoding
LoadDLL -Path ( Join-Path $alcDllPath Newtonsoft . Json . dll )
LoadDLL -Path ( Join-Path $alcDllPath System . Collections . Immutable . dll )
2023-12-07 12:47:15 +03:00
if ( Test-Path ( Join-Path $alcDllPath System . IO . Packaging . dll ) ) {
LoadDLL -Path ( Join-Path $alcDllPath System . IO . Packaging . dll )
}
2023-12-01 11:46:19 +03:00
LoadDLL -Path ( Join-Path $alcDllPath Microsoft . Dynamics . Nav . CodeAnalysis . dll )
$assembliesAdded = $true
}
$packageStream = [ System.IO.File ] :: OpenRead ( $path )
$package = [ Microsoft.Dynamics.Nav.CodeAnalysis.Packaging.NavAppPackageReader ] :: Create ( $PackageStream , $true )
$manifest = $package . ReadNavAppManifest ( )
$appInfo = @ {
" appId " = $manifest . AppId
" publisher " = $manifest . AppPublisher
" name " = $manifest . AppName
" version " = " $( $manifest . AppVersion ) "
" dependencies " = @ ( $manifest . Dependencies | ForEach-Object { @ { " id " = $_ . AppId ; " name " = $_ . Name ; " publisher " = $_ . Publisher ; " version " = " $( $_ . Version ) " } } )
" application " = " $( $manifest . Application ) "
" platform " = " $( $manifest . Platform ) "
" propagateDependencies " = $manifest . PropagateDependencies
}
2023-05-24 22:53:38 +03:00
}
2023-09-26 11:30:00 +03:00
Write-Host " (succeeded) "
2023-12-17 15:04:52 +03:00
if ( $cacheAppInfoPath ) {
2023-09-26 13:52:01 +03:00
$appInfoCache | Add-Member -MemberType NoteProperty -Name $path -Value $appInfo
$cacheUpdated = $true
2023-05-24 22:53:38 +03:00
}
}
2023-09-26 11:30:00 +03:00
@ {
" Id " = $appInfo . appId
" AppId " = $appInfo . appId
" Publisher " = $appInfo . publisher
" Name " = $appInfo . name
" Version " = [ System.Version ] $appInfo . version
" Dependencies " = @ ( $appInfo . dependencies )
" Path " = $path
" Application " = $appInfo . application
" Platform " = $appInfo . platform
" PropagateDependencies " = $appInfo . propagateDependencies
2023-05-24 22:53:38 +03:00
}
}
2023-09-26 13:52:01 +03:00
if ( $cacheUpdated ) {
$appInfoCache | ConvertTo-Json -Depth 99 | Set-Content -Path $cacheAppInfoPath -Encoding UTF8 -Force
}
2023-09-26 11:30:00 +03:00
}
catch [ System.Reflection.ReflectionTypeLoadException ] {
Write-Host " (failed) "
if ( $_ . Exception . LoaderExceptions ) {
$_ . Exception . LoaderExceptions | Select-Object -Property Message | Select-Object -Unique | ForEach-Object {
Write-Host " LoaderException: $( $_ . Message ) "
2023-05-24 22:53:38 +03:00
}
}
2023-09-26 11:30:00 +03:00
throw
}
finally {
if ( $package ) {
$package . Dispose ( )
}
if ( $packageStream ) {
$packageStream . Dispose ( )
}
}
2023-12-17 15:04:52 +03:00
Write-Host " ::endgroup:: "
2023-05-24 22:53:38 +03:00
}
2023-07-27 11:03:59 +03:00
2023-12-26 11:18:51 +03:00
function GetLatestAlLanguageExtensionVersionAndUrl {
2023-07-27 11:03:59 +03:00
Param (
[ switch ] $allowPrerelease
)
$listing = Invoke-WebRequest -Method POST -UseBasicParsing `
-Uri https : / / marketplace . visualstudio . com / _apis / public / gallery / extensionquery ? api-version = 3.0 -preview . 1 `
-Body '{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"},{"filterType":12,"value":"4096"},{"filterType":7,"value":"ms-dynamics-smb.al"}],"pageNumber":1,"pageSize":50,"sortBy":0,"sortOrder":0}],"assetTypes":[],"flags":0x192}' `
-ContentType application / json | ConvertFrom-Json
2023-12-26 11:18:51 +03:00
$result = $listing . results | Select-Object -First 1 -ExpandProperty extensions `
2023-07-27 11:03:59 +03:00
| Select-Object -ExpandProperty versions `
| Where-Object { ( $allowPrerelease . IsPresent -or ! ( ( $_ . properties . Key -eq 'Microsoft.VisualStudio.Code.PreRelease' ) -and ( $_ . properties | where-object { $_ . Key -eq 'Microsoft.VisualStudio.Code.PreRelease' } ) . value -eq " true " ) ) } `
2023-12-26 11:18:51 +03:00
| Select-Object -First 1
if ( $result ) {
$vsixUrl = $result . files | Where-Object { $_ . assetType -eq " Microsoft.VisualStudio.Services.VSIXPackage " } | Select-Object -ExpandProperty source
if ( $vsixUrl ) {
return $result . version , $vsixUrl
}
2023-07-27 11:03:59 +03:00
}
2023-12-26 11:18:51 +03:00
throw " Unable to locate latest AL Language Extension from the VS Code Marketplace "
2023-07-27 11:03:59 +03:00
}
function DetermineVsixFile {
Param (
[ string ] $vsixFile
)
if ( $vsixFile -eq 'default' ) {
return ''
}
2023-12-26 11:18:51 +03:00
elseif ( $vsixFile -eq 'latest' -or $vsixFile -eq 'preview' ) {
$version , $url = GetLatestAlLanguageExtensionVersionAndUrl -allowPrerelease: ( $vsixFile -eq 'preview' )
return $url
2023-07-27 11:03:59 +03:00
}
else {
return $vsixFile
}
2023-10-20 17:19:24 +03:00
}
2023-12-26 11:18:51 +03:00
# Cached Path for latest and preview versions of AL Language Extension
$AlLanguageExtenssionPath = @ ( '' , '' )
function DownloadLatestAlLanguageExtension {
Param (
[ switch ] $allowPrerelease
)
$mutexName = " DownloadAlLanguageExtension "
$mutex = New-Object System . Threading . Mutex ( $false , $mutexName )
try {
try {
if ( ! $mutex . WaitOne ( 1000 ) ) {
Write-Host " Waiting for other process downloading AL Language Extension "
$mutex . WaitOne ( ) | Out-Null
Write-Host " Other process completed downloading "
}
}
catch [ System.Threading.AbandonedMutexException ] {
Write-Host " Other process terminated abnormally "
}
# Check if we already have the latest version downloaded and located in this session
if ( $script:AlLanguageExtenssionPath [ $allowPrerelease . IsPresent ] ) {
$path = $script:AlLanguageExtenssionPath [ $allowPrerelease . IsPresent ]
if ( Test-Path $path -PathType Container ) {
return $path
}
else {
$script:AlLanguageExtenssionPath [ $allowPrerelease . IsPresent ] = ''
}
}
$version , $url = GetLatestAlLanguageExtensionVersionAndUrl -allowPrerelease: $allowPrerelease
$path = Join-Path $bcContainerHelperConfig . hostHelperFolder " alLanguageExtension/ $version "
if ( ! ( Test-Path $path -PathType Container ) ) {
$AlLanguageExtensionsFolder = Join-Path $bcContainerHelperConfig . hostHelperFolder " alLanguageExtension "
if ( ! ( Test-Path $AlLanguageExtensionsFolder -PathType Container ) ) {
New-Item -Path $AlLanguageExtensionsFolder -ItemType Directory | Out-Null
}
$description = " AL Language Extension "
if ( $allowPrerelease ) {
$description + = " (Prerelease) "
}
$zipFile = " $path .zip "
Download-File -sourceUrl $url -destinationFile $zipFile -Description $description
Expand - 7zipArchive -Path $zipFile -DestinationPath $path
Remove-Item -Path $zipFile -Force
}
$script:AlLanguageExtenssionPath [ $allowPrerelease . IsPresent ] = $path
return $path
}
finally {
$mutex . ReleaseMutex ( )
}
}
function GetAppJsonFromAppFile {
Param (
[ string ] $appFile
)
# ALTOOL is at the moment only available in prerelease
$path = DownloadLatestAlLanguageExtension -allowPrerelease
if ( $isWindows ) {
$alToolExe = Join-Path $path 'extension/bin/win32/altool.exe'
}
else {
$alToolExe = Join-Path $path 'extension/bin/linux/altool'
}
$appJson = CmdDo -Command $alToolExe -arguments @ ( 'GetPackageManifest' , " "" $appFile "" " ) -returnValue -silent | ConvertFrom-Json
return $appJson
}