From 480254c917ef58b4f142da0fc0ec72e48c63b131 Mon Sep 17 00:00:00 2001 From: Freddy Kristiansen Date: Fri, 9 Aug 2024 12:18:10 +0200 Subject: [PATCH] Support compilerfolder and online environments (#3607) When specifying BcAuthContext and Environment to Run-AlPipeline, Run-AlPipeline would always create a filesonly container, disallowing running on Linux. This PR fixes this plus some bugs found as a result of this. - Replace all occurrences of [System.Runtime.InteropServices.Marshal]::PtrToStringAuto with [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR as the Auto function doesn't always do what's expected under Linux (We do not use the Auto function in AL-Go) - Ensure correct casing of Newtonsoft.Json.dll for Linux (also not a problem in AL-Go) - Always add extensionId (when specified) to Properties section in test results xml - Also added two new overrides (PipelineInitialize and PipelineFinalize) requested by COSMO Consult. - If environment is specified as a Web Client URL, and BcAuthContext contains username/password in Run-AlPipeline, then tests will run against this environment. PublishBcContainerApp and ImportTestToolkitToBcContainer needs to be overridden for this to work with full pipeline. - Add parameter CompilerFolder to Run-TestsInBcContainer and Import-TestToolkitToBcContainer for running tests using CompilerFolder bits from the host - Including caching of appinfos in CompilerFolder cache (to save time when caching on GitHub Actions) Running Build AND Test under Linux (using CompilerFolder), using an online environment as "Service Tier" can be seen here: https://github.com/BusinessCentralDemos/bingmaps.pte/actions/runs/10313615507 Build and test here takes approx. 3 minutes. This functionality is needed by COSMO to enable using their Docker Swarm for running tests in AL-Go. COSMO is aware that AL-Go moves away from using BcContainerHelper and will subsequently have to change their integration when this has happened. --------- Co-authored-by: freddydk --- AppHandling/Compile-AppInNavContainer.ps1 | 2 +- AppHandling/Get-NavContainerApp.ps1 | 2 +- AppHandling/Get-TestsFromNavContainer.ps1 | 4 +- AppHandling/PsTestFunctions.ps1 | 14 ++- AppHandling/Publish-NavContainerApp.ps1 | 2 +- AppHandling/Run-AlPipeline.ps1 | 94 +++++++++++---- .../Run-ConnectionTestToNavContainer.ps1 | 10 +- AppHandling/Run-TestsInNavContainer.ps1 | 111 +++++++++++------ AppHandling/Sign-NavContainerApp.ps1 | 2 +- Artifacts/Download-Artifacts.ps1 | 8 +- AzureAD/Create-AadAppsForNav.ps1 | 2 +- AzureAD/Create-AadUsersInNavContainer.ps1 | 2 +- .../Export-NavContainerDatabasesAsBacpac.ps1 | 16 +-- Common/Get-PlainText.ps1 | 6 +- .../New-BcCompilerFolder.ps1 | 16 ++- ContainerHandling/New-NavContainer.ps1 | 4 +- ContainerInfo/Get-NavContainerImageLabels.ps1 | 2 +- ContainerInfo/Get-NavContainerImageTags.ps1 | 2 +- HelperFunctions.ps1 | 112 ++++++++++++------ NavContainerHelper.md | 6 +- .../Compile-ObjectsInNavContainer.ps1 | 2 +- ObjectHandling/Export-NavContainerObjects.ps1 | 2 +- .../Import-ObjectsToNavContainer.ps1 | 2 +- .../Import-TestToolkitToNavContainer.ps1 | 57 +++++---- .../Publish-BuildOutputToStorage.ps1 | 2 +- ReleaseNotes.txt | 10 +- Saas/Publish-PerTenantExtensionApps.ps1 | 2 +- .../Generate-SymbolsInNavContainer.ps1 | 2 +- UserHandling/New-NavContainerNavUser.ps1 | 2 +- UserHandling/Setup-NavContainerTestUsers.ps1 | 2 +- 30 files changed, 329 insertions(+), 171 deletions(-) diff --git a/AppHandling/Compile-AppInNavContainer.ps1 b/AppHandling/Compile-AppInNavContainer.ps1 index 2d2d36717..9b161dda3 100644 --- a/AppHandling/Compile-AppInNavContainer.ps1 +++ b/AppHandling/Compile-AppInNavContainer.ps1 @@ -427,7 +427,7 @@ try { throw "You need to specify credentials when you are not using Windows Authentication" } - $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) + $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) $basicAuthValue = "Basic $base64" diff --git a/AppHandling/Get-NavContainerApp.ps1 b/AppHandling/Get-NavContainerApp.ps1 index e4483127f..5fc739d04 100644 --- a/AppHandling/Get-NavContainerApp.ps1 +++ b/AppHandling/Get-NavContainerApp.ps1 @@ -76,7 +76,7 @@ try { throw "You need to specify credentials when you are not using Windows Authentication" } - $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) + $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) $basicAuthValue = "Basic $base64" diff --git a/AppHandling/Get-TestsFromNavContainer.ps1 b/AppHandling/Get-TestsFromNavContainer.ps1 index b31421da7..9e0801938 100644 --- a/AppHandling/Get-TestsFromNavContainer.ps1 +++ b/AppHandling/Get-TestsFromNavContainer.ps1 @@ -182,9 +182,9 @@ try { $result = Invoke-ScriptInBcContainer -containerName $containerName -usePwsh $false -scriptBlock { Param([string] $tenant, [string] $companyName, [string] $profile, [pscredential] $credential, [string] $accessToken, [string] $testSuite, [string] $testCodeunit, [string] $testCodeunitRange, [string] $PsTestFunctionsPath, [string] $ClientContextPath, $testPage, $version, $culture, $timezone, $debugMode, $ignoreGroups, $usePublicWebBaseUrl, $useUrl, $extensionId, $disabledtests) - $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\NewtonSoft.json.dll" + $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 = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll" } $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll" diff --git a/AppHandling/PsTestFunctions.ps1 b/AppHandling/PsTestFunctions.ps1 index 508255d3c..5aba59f44 100644 --- a/AppHandling/PsTestFunctions.ps1 +++ b/AppHandling/PsTestFunctions.ps1 @@ -44,7 +44,7 @@ function New-ClientContext { if ($Credential -eq $null -or $credential -eq [System.Management.Automation.PSCredential]::Empty) { throw "You need to specify credentials (Username and AccessToken) if using AAD authentication" } - $accessToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password)) + $accessToken = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password)) $clientContext = [ClientContext]::new($serviceUrl, $accessToken, $interactionTimeout, $culture, $timezone) } else { @@ -726,6 +726,13 @@ function Run-Tests { $JunitTestSuiteProperties = $JUnitDoc.CreateElement("properties") $JUnitTestSuite.AppendChild($JunitTestSuiteProperties) | Out-Null + if ($extensionid) { + $property = $JUnitDoc.CreateElement("property") + $property.SetAttribute("name","extensionid") + $property.SetAttribute("value", $extensionId) + $JunitTestSuiteProperties.AppendChild($property) | Out-Null + } + if ($process) { $property = $JUnitDoc.CreateElement("property") $property.SetAttribute("name","processinfo.start") @@ -733,11 +740,6 @@ function Run-Tests { $JunitTestSuiteProperties.AppendChild($property) | Out-Null if ($extensionid) { - $property = $JUnitDoc.CreateElement("property") - $property.SetAttribute("name","extensionid") - $property.SetAttribute("value", $extensionId) - $JunitTestSuiteProperties.AppendChild($property) | Out-Null - $appname = "$(Get-NavAppInfo -ServerInstance $serverInstance | Where-Object { "$($_.AppId)" -eq $extensionId } | ForEach-Object { $_.Name })" if ($appname) { $property = $JUnitDoc.CreateElement("property") diff --git a/AppHandling/Publish-NavContainerApp.ps1 b/AppHandling/Publish-NavContainerApp.ps1 index d274d44b1..74a515c9c 100644 --- a/AppHandling/Publish-NavContainerApp.ps1 +++ b/AppHandling/Publish-NavContainerApp.ps1 @@ -233,7 +233,7 @@ try { if (!($credential)) { throw "You need to specify credentials when you are not using Windows Authentication" } - $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) + $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Basic", $base64); diff --git a/AppHandling/Run-AlPipeline.ps1 b/AppHandling/Run-AlPipeline.ps1 index 8832b0da6..1e24a4f18 100644 --- a/AppHandling/Run-AlPipeline.ps1 +++ b/AppHandling/Run-AlPipeline.ps1 @@ -183,6 +183,10 @@ Information about which product built the app. Will be stamped into the app manifest. .Parameter BuildUrl The URL for the build job, which built the app. Will be stamped into the app manifest. + .Parameter PipelineInitialize + Override for Pipeline Initialize + .Parameter PipelineFinalize + Override for Pipeline Finalize .Parameter DockerPull Override function parameter for docker pull .Parameter NewBcContainer @@ -193,7 +197,7 @@ Override function parameter for Import-TestToolkitToBcContainer .Parameter CompileAppInBcContainer Override function parameter for Compile-AppInBcContainer - .Parameter CompileAppWithBcCompilerFolder + .Parameter CompileAppWithBcCompilerFolder Override function parameter for Compile-AppWithBcCompilerFolder .Parameter PreCompileApp Custom script to run before compiling an app. @@ -363,6 +367,7 @@ Param( [string] $sourceCommit = '', [string] $buildBy = "BcContainerHelper,$BcContainerHelperVersion", [string] $buildUrl = '', + [scriptblock] $PipelineInitialize, [scriptblock] $DockerPull, [scriptblock] $NewBcContainer, [scriptblock] $SetBcContainerKeyVaultAadAppAndCertificate, @@ -383,7 +388,8 @@ Param( [scriptblock] $RemoveBcContainer, [scriptblock] $GetBestGenericImageName, [scriptblock] $GetBcContainerEventLog, - [scriptblock] $InstallMissingDependencies + [scriptblock] $InstallMissingDependencies, + [scriptblock] $PipelineFinalize ) function CheckRelativePath([string] $baseFolder, [string] $sharedFolder, $path, $name) { @@ -475,6 +481,10 @@ function GetInstalledAppIds { $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() try { +if ($PipelineInitialize) { + Invoke-Command -ScriptBlock $PipelineInitialize +} + $warningsToShow = @() if (!$baseFolder -or !(Test-Path $baseFolder -PathType Container)) { @@ -554,13 +564,19 @@ if ($bcAuthContext) { Write-Host -ForegroundColor Yellow "Uninstalling removed apps from online environments are not supported" $uninstallRemovedApps = $false } - $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext - $bcEnvironment = Get-BcEnvironments -bcAuthContext $bcAuthContext | Where-Object { $_.name -eq $environment -and $_.type -eq "Sandbox" } - if (!($bcEnvironment)) { - throw "Environment $environment doesn't exist in the current context or it is not a Sandbox environment." + if ($environment -notlike ('https://*')) { + $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext + $bcEnvironment = Get-BcEnvironments -bcAuthContext $bcAuthContext | Where-Object { $_.name -eq $environment -and $_.type -eq "Sandbox" } + if (!($bcEnvironment)) { + throw "Environment $environment doesn't exist in the current context or it is not a Sandbox environment." + } + $parameters = @{ + bcAuthContext = $bcAuthContext + environment = $environment + } + $bcBaseApp = Get-BcPublishedApps @Parameters | Where-Object { $_.Name -eq "Base Application" -and $_.state -eq "installed" } + $artifactUrl = Get-BCArtifactUrl -type Sandbox -country $bcEnvironment.countryCode -version $bcBaseApp.Version -select Closest } - $bcBaseApp = Get-BcPublishedApps -bcAuthContext $bcauthcontext -environment $environment | Where-Object { $_.Name -eq "Base Application" -and $_.state -eq "installed" } - $artifactUrl = Get-BCArtifactUrl -type Sandbox -country $bcEnvironment.countryCode -version $bcBaseApp.Version -select Closest $filesOnly = $true } @@ -748,14 +764,16 @@ Write-Host -ForegroundColor Yellow "Custom CodeCops" if ($customCodeCops) { $customCodeCops | ForEach-Object { Write-Host "- $_" } } else { Write-Host "- None" } $vsixFile = DetermineVsixFile -vsixFile $vsixFile - $compilerFolder = '' +$createContainer = $true if ($useCompilerFolder) { # We are using CompilerFolder, no need for a filesOnly Container # If we are to create a container, it is for publishing and testing $filesOnly = $false $updateLaunchJson = '' + $createContainer = !($doNotPublishApps -or ($bcAuthContext -and $environment)) + if (!$createContainer) { $containerName = ''} } elseif ($doNotPublishApps) { # We are not using CompilerFolder, but we are not publishing apps either @@ -908,7 +926,7 @@ $signApps = ($codeSignCertPfxFile -ne "") Measure-Command { -if ( $artifactUrl -and !$reUseContainer -and !$doNotPublishApps -and !$filesOnly) { +if ( $artifactUrl -and !$reUseContainer -and $createContainer) { if ($gitHubActions) { Write-Host "::group::Pulling generic image" } Measure-Command { Write-Host -ForegroundColor Yellow @' @@ -948,19 +966,36 @@ try { $testCountry = $_.Trim() $testToolkitInstalled = $false -if ($gitHubActions) { Write-Host "::group::Creating container" } -Write-Host -ForegroundColor Yellow @' +if ($useCompilerFolder) { + if ($gitHubActions) { Write-Host "::group::Creating CompilerFolder" } + Write-Host -ForegroundColor Yellow @' + + _____ _ _ _____ _ _ ______ _ _ + / ____| | | (_) / ____| (_) | | ____| | | | | + | | _ __ ___ __ _| |_ _ _ __ __ _ | | ___ _ __ ___ _ __ _| | ___ _ __| |__ ___ | | __| | ___ _ __ + | | | '__/ _ \/ _` | __| | '_ \ / _` | | | / _ \| '_ ` _ \| '_ \| | |/ _ \ '__| __/ _ \| |/ _` |/ _ \ '__| + | |____| | | __/ (_| | |_| | | | | (_| | | |___| (_) | | | | | | |_) | | | __/ | | | | (_) | | (_| | __/ | + \_____|_| \___|\__,_|\__|_|_| |_|\__, | \_____\___/|_| |_| |_| .__/|_|_|\___|_| |_| \___/|_|\__,_|\___|_| + __/ | | | + |___/ |_| - _____ _ _ _ _ - / ____| | | (_) | | (_) - | | _ __ ___ __ _| |_ _ _ __ __ _ ___ ___ _ __ | |_ __ _ _ _ __ ___ _ __ - | | | '__/ _ \/ _` | __| | '_ \ / _` | / __/ _ \| '_ \| __/ _` | | '_ \ / _ \ '__| - | |____| | | __/ (_| | |_| | | | | (_| | | (__ (_) | | | | |_ (_| | | | | | __/ | - \_____|_| \___|\__,_|\__|_|_| |_|\__, | \___\___/|_| |_|\__\__,_|_|_| |_|\___|_| +'@ +} +else { + if ($gitHubActions) { Write-Host "::group::Creating container" } + Write-Host -ForegroundColor Yellow @' + + _____ _ _ _____ _ _ + / ____| | | (_) / ____| | | (_) + | | _ __ ___ __ _| |_ _ _ __ __ _ | | ___ _ __ | |_ __ _ _ _ __ ___ _ __ + | | | '__/ _ \/ _` | __| | '_ \ / _` | | | / _ \| '_ \| __/ _` | | '_ \ / _ \ '__| + | |____| | | __/ (_| | |_| | | | | (_| | | |___| (_) | | | | || (_| | | | | | __/ | + \_____|_| \___|\__,_|\__|_|_| |_|\__, | \_____\___/|_| |_|\__\__,_|_|_| |_|\___|_| __/ | |___/ '@ +} Measure-Command { @@ -983,7 +1018,7 @@ Measure-Command { -containerName $containerName Write-Host "CompilerFolder $compilerFolder created" } - if ($filesOnly -or !$doNotPublishApps) { + if ($createContainer -and ($filesOnly -or !$doNotPublishApps)) { # If we are going to build using a filesOnly container or we are going to publish apps, we need a container if (Test-BcContainer -containerName $containerName) { if ($bcAuthContext) { @@ -1035,7 +1070,7 @@ Measure-Command { } Invoke-Command -ScriptBlock $NewBcContainer -ArgumentList $Parameters - if (-not $bcAuthContext) { + if ($createContainer -and -not $bcAuthContext) { if ($keyVaultCertPfxFile -and $KeyVaultClientId -and $keyVaultCertPfxPassword) { $Parameters = @{ "containerName" = $containerName @@ -1329,6 +1364,7 @@ Measure-Command { Write-Host -ForegroundColor Yellow "Importing Test Toolkit for additional country $testCountry" $Parameters = @{ "containerName" = $containerName + "compilerFolder" = $compilerFolder "includeTestLibrariesOnly" = $installTestLibraries "includeTestFrameworkOnly" = !$installTestLibraries -and ($installTestFramework -or $installPerformanceToolkit) "includeTestRunnerOnly" = !$installTestLibraries -and !$installTestFramework -and ($installTestRunner -or $installPerformanceToolkit) @@ -1508,6 +1544,7 @@ Measure-Command { $measureText = ", test apps and importing test toolkit" $Parameters = @{ "containerName" = $containerName + "compilerFolder" = $compilerFolder "includeTestLibrariesOnly" = $installTestLibraries "includeTestFrameworkOnly" = !$installTestLibraries -and ($installTestFramework -or $installPerformanceToolkit) "includeTestRunnerOnly" = !$installTestLibraries -and !$installTestFramework -and ($installTestRunner -or $installPerformanceToolkit) @@ -1654,7 +1691,7 @@ Write-Host -ForegroundColor Yellow @' $Parameters = @{ } $CopParameters = @{ } - if ($bcAuthContext) { + if ($bcAuthContext -and !$useCompilerFolder) { $Parameters += @{ "bcAuthContext" = $bcAuthContext "environment" = $environment @@ -2433,6 +2470,7 @@ $testAppIds.Keys | ForEach-Object { } $Parameters = @{ "containerName" = $containerName + "compilerFolder" = $compilerFolder "tenant" = $tenant "credential" = $credential "companyName" = $companyName @@ -2461,6 +2499,7 @@ $testAppIds.Keys | ForEach-Object { $Parameters += @{ "bcAuthContext" = $bcAuthContext "environment" = $environment + "ConnectFromHost" = !$createContainer } } @@ -2615,7 +2654,11 @@ finally { $progressPreference = $prevProgressPreference } -if (!$keepContainer) { +if ($useCompilerFolder -and $compilerFolder) { + Remove-BcCompilerFolder -compilerFolder $compilerFolder +} + +if ($createContainer -and !$keepContainer) { if ($gitHubActions) { Write-Host "::group::Removing container" } if (!($err)) { Write-Host -ForegroundColor Yellow @' @@ -2631,9 +2674,6 @@ Write-Host -ForegroundColor Yellow @' } Measure-Command { - if ($useCompilerFolder -and $compilerFolder) { - Remove-BcCompilerFolder -compilerFolder $compilerFolder - } if (!$doNotPublishApps) { if (!$filesOnly -and $containerEventLogFile) { try { @@ -2667,6 +2707,10 @@ if ($err) { } | ForEach-Object { Write-Host -ForegroundColor Yellow "`nAL Pipeline finished in $([int]$_.TotalSeconds) seconds" } +if ($PipelineFinalize) { + Invoke-Command -ScriptBlock $PipelineFinalize +} + } catch { TrackException -telemetryScope $telemetryScope -errorRecord $_ diff --git a/AppHandling/Run-ConnectionTestToNavContainer.ps1 b/AppHandling/Run-ConnectionTestToNavContainer.ps1 index af70383b1..8fcc2c84c 100644 --- a/AppHandling/Run-ConnectionTestToNavContainer.ps1 +++ b/AppHandling/Run-ConnectionTestToNavContainer.ps1 @@ -135,15 +135,15 @@ try { } if ($connectFromHost) { - $newtonSoftDllPath = Join-Path $PsTestToolFolder "NewtonSoft.json.dll" + $newtonSoftDllPath = Join-Path $PsTestToolFolder "Newtonsoft.Json.dll" $clientDllPath = Join-Path $PsTestToolFolder "Microsoft.Dynamics.Framework.UI.Client.dll" 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" + $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 = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll" } $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName Copy-Item -Path $newtonSoftDllPath -Destination $myNewtonSoftDllPath @@ -202,9 +202,9 @@ try { $result = Invoke-ScriptInBcContainer -containerName $containerName -usePwsh $false -scriptBlock { Param([string] $tenant, [string] $companyName, [string] $profile, [pscredential] $credential, [string] $accessToken, [string] $PsTestFunctionsPath, [string] $ClientContextPath, [timespan] $interactionTimeout, $version, $culture, $timezone, $debugMode, $usePublicWebBaseUrl, $useUrl) - $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\NewtonSoft.json.dll" + $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 = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll" } $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll" diff --git a/AppHandling/Run-TestsInNavContainer.ps1 b/AppHandling/Run-TestsInNavContainer.ps1 index b8d6b6e0d..f46dfc783 100644 --- a/AppHandling/Run-TestsInNavContainer.ps1 +++ b/AppHandling/Run-TestsInNavContainer.ps1 @@ -87,6 +87,7 @@ function Run-TestsInBcContainer { Param ( [string] $containerName = $bcContainerHelperConfig.defaultContainerName, + [string] $compilerFolder = '', [Parameter(Mandatory=$false)] [string] $tenant = "default", [Parameter(Mandatory=$false)] @@ -141,23 +142,60 @@ function Run-TestsInBcContainer { $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() try { - - $customConfig = Get-BcContainerServerConfiguration -ContainerName $containerName - $navversion = Get-BcContainerNavversion -containerOrImageName $containerName - $version = [System.Version]($navversion.split('-')[0]) + + if ($containerName) { + Write-Host "Using Container" + $customConfig = Get-BcContainerServerConfiguration -ContainerName $containerName + $navversion = Get-BcContainerNavversion -containerOrImageName $containerName + $version = [System.Version]($navversion.split('-')[0]) + $PsTestToolFolder = Join-Path $bcContainerHelperConfig.hostHelperFolder "Extensions\$containerName\PsTestTool" + + } + elseif ($compilerFolder) { + Write-Host "Using CompilerFolder" + $customConfig = $null + $symbolsFolder = Join-Path $compilerFolder "symbols" + $baseAppInfo = Get-AppJsonFromAppFile -appFile (Get-ChildItem -Path $symbolsFolder -Filter 'Microsoft_Base Application_*.*.*.*.app').FullName + $version = [Version]$baseAppInfo.version + $PsTestToolFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())" + New-Item $PsTestToolFolder -ItemType Directory | Out-Null + $testDlls = Join-Path $compilerFolder "dlls/Test Assemblies/*.dll" + Copy-Item $testDlls -Destination $PsTestToolFolder -Force + Copy-Item -Path (Join-Path $PSScriptRoot "PsTestFunctions.ps1") -Destination $PsTestToolFolder -Force + Copy-Item -Path (Join-Path $PSScriptRoot "ClientContext.ps1") -Destination $PsTestToolFolder -Force + } + else { + throw "You must specify either containerName or compilerFolder" + } if ($bcAuthContext -and $environment) { - $response = Invoke-RestMethod -Method Get -Uri "$($bcContainerHelperConfig.baseUrl.TrimEnd('/'))/$($bcAuthContext.tenantID)/$environment/deployment/url" - if($response.status -ne 'Ready') { - throw "environment not ready, status is $($response.status)" + if ($environment -like 'https://*') { + $useUrl = $environment + if ($bcAuthContext.ContainsKey('Username') -and $bcAuthContext.ContainsKey('Password')) { + $credential = New-Object System.Management.Automation.PSCredential -ArgumentList $bcAuthContext.Username, $bcAuthContext.Password + $clientServicesCredentialType = "NavUserPassword" + } + if ($bcAuthContext.ContainsKey('ClientServicesCredentialType')) { + $clientServicesCredentialType = $bcAuthContext.ClientServicesCredentialType + } + $testPage = 130455 } - $useUrl = $response.data.Split('?')[0] - $tenant = ($response.data.Split('?')[1]).Split('=')[1] - - if ($testPage) { - throw "You cannot specify testPage when running tests in an Online tenant" + else { + $response = Invoke-RestMethod -Method Get -Uri "$($bcContainerHelperConfig.baseUrl.TrimEnd('/'))/$($bcAuthContext.tenantID)/$environment/deployment/url" + if($response.status -ne 'Ready') { + throw "environment not ready, status is $($response.status)" + } + $useUrl = $response.data + if ($testPage) { + throw "You cannot specify testPage when running tests in an Online tenant" + } + $testPage = 130455 } - $testPage = 130455 + $uri = [Uri]::new($useUrl) + $useUrl = $useUrl.Split('?')[0] + $dict = [System.Web.HttpUtility]::ParseQueryString($uri.Query) + if ($dict['tenant']) { $tenant = $dict['tenant'] } + if ($dict['testpage']) { $testpage = [int]$dict['testpage'] } } else { $clientServicesCredentialType = $customConfig.ClientServicesCredentialType @@ -220,13 +258,12 @@ try { } -argumentList $interactionTimeout.ToString() } - if ($bcAuthContext) { + if ($bcAuthContext -and ($environment -notlike 'https://*')) { $bcAuthContext = Renew-BcAuthContext $bcAuthContext $accessToken = $bcAuthContext.accessToken $credential = New-Object pscredential -ArgumentList $bcAuthContext.upn, (ConvertTo-SecureString -String $accessToken -AsPlainText -Force) } - $PsTestToolFolder = Join-Path $bcContainerHelperConfig.hostHelperFolder "Extensions\$containerName\PsTestTool" $PsTestFunctionsPath = Join-Path $PsTestToolFolder "PsTestFunctions.ps1" $ClientContextPath = Join-Path $PsTestToolFolder "ClientContext.ps1" $fobfile = Join-Path $PsTestToolFolder "PSTestToolPage.fob" @@ -273,24 +310,30 @@ try { try { if ($connectFromHost) { - $newtonSoftDllPath = Join-Path $PsTestToolFolder "NewtonSoft.json.dll" + if ($PSVersionTable.PSVersion.Major -lt 7) { + throw "Using ConnectFromHost requires PowerShell 7" + } + $newtonSoftDllPath = Join-Path $PsTestToolFolder "Newtonsoft.Json.dll" $clientDllPath = Join-Path $PsTestToolFolder "Microsoft.Dynamics.Framework.UI.Client.dll" - - 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 - } - $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll" - if (!(Test-Path $myClientDllPath)) { - Copy-Item -Path $clientDllPath -Destination $myClientDllPath + if ($containerName) { + if (!((Test-Path $newtonSoftDllPath) -and (Test-Path $clientDllPath))) { + 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 + } + $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll" + if (!(Test-Path $myClientDllPath)) { + Copy-Item -Path $clientDllPath -Destination $myClientDllPath + } + } -argumentList $newtonSoftDllPath, $clientDllPath } - } -argumentList $newtonSoftDllPath, $clientDllPath + } if ($useUrl) { $publicWebBaseUrl = $useUrl.TrimEnd('/') @@ -384,11 +427,11 @@ try { } } - $result = Invoke-ScriptInBcContainer -containerName $containerName -usePwsh $false -scriptBlock { Param([string] $tenant, [string] $companyName, [string] $profile, [pscredential] $credential, [string] $accessToken, [string] $testSuite, [string] $testGroup, [string] $testCodeunit, [string] $testCodeunitRange, [string] $testFunction, [string] $PsTestFunctionsPath, [string] $ClientContextPath, [string] $XUnitResultFileName, [bool] $AppendToXUnitResultFile, [string] $JUnitResultFileName, [bool] $AppendToJUnitResultFile, [bool] $ReRun, [string] $AzureDevOps, [string] $GitHubActions, [bool] $detailed, [timespan] $interactionTimeout, $testPage, $version, $culture, $timezone, $debugMode, $usePublicWebBaseUrl, $useUrl, $extensionId, $testRunnerCodeunitId, $disabledtests, $renewClientContextBetweenTests) + $result = Invoke-ScriptInBcContainer -containerName $containerName -usePwsh $false -scriptBlock { Param([string] $tenant, [string] $companyName, [string] $profile, [System.Management.Automation.PSCredential] $credential, [string] $accessToken, [string] $testSuite, [string] $testGroup, [string] $testCodeunit, [string] $testCodeunitRange, [string] $testFunction, [string] $PsTestFunctionsPath, [string] $ClientContextPath, [string] $XUnitResultFileName, [bool] $AppendToXUnitResultFile, [string] $JUnitResultFileName, [bool] $AppendToJUnitResultFile, [bool] $ReRun, [string] $AzureDevOps, [string] $GitHubActions, [bool] $detailed, [timespan] $interactionTimeout, $testPage, $version, $culture, $timezone, $debugMode, $usePublicWebBaseUrl, $useUrl, $extensionId, $testRunnerCodeunitId, $disabledtests, $renewClientContextBetweenTests) - $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\NewtonSoft.json.dll" + $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 = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll" } $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll" diff --git a/AppHandling/Sign-NavContainerApp.ps1 b/AppHandling/Sign-NavContainerApp.ps1 index 08e8eb38b..f242f93a7 100644 --- a/AppHandling/Sign-NavContainerApp.ps1 +++ b/AppHandling/Sign-NavContainerApp.ps1 @@ -151,7 +151,7 @@ try { } Write-Host "Signing $appFile" - $unsecurepassword = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pfxPassword))) + $unsecurepassword = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pfxPassword))) $attempt = 1 $maxAttempts = 5 do { diff --git a/Artifacts/Download-Artifacts.ps1 b/Artifacts/Download-Artifacts.ps1 index a8258fefa..01e2b24da 100644 --- a/Artifacts/Download-Artifacts.ps1 +++ b/Artifacts/Download-Artifacts.ps1 @@ -244,14 +244,14 @@ try { Download-File -sourceUrl "https://go.microsoft.com/fwlink/?LinkID=844461" -destinationFile (Join-Path $dotnetCoreFolder "DotNetCore.1.0.4_1.1.1-WindowsHosting.exe") -timeout $timeout } } - # Patch potential wrong version of NewtonSoft.json.DLL - $newtonSoftDllPath = Join-Path $platformArtifactPath 'ServiceTier\program files\Microsoft Dynamics NAV\210\Service\Newtonsoft.json.dll' + # Patch potential wrong version of Newtonsoft.Json.dll + $newtonSoftDllPath = Join-Path $platformArtifactPath 'ServiceTier\program files\Microsoft Dynamics NAV\210\Service\Newtonsoft.Json.dll' if (Test-Path $newtonSoftDllPath) { - 'Applications\testframework\TestRunner\Internal\Newtonsoft.json.dll','Test Assemblies\Newtonsoft.json.dll' | ForEach-Object { + 'Applications\testframework\TestRunner\Internal\Newtonsoft.Json.dll','Test Assemblies\Newtonsoft.Json.dll' | ForEach-Object { $dstFile = Join-Path $platformArtifactPath $_ $file = Get-item -Path $dstFile -ErrorAction SilentlyContinue if ($file -and $file.Length -eq 686000) { - Write-Host "INFO: Patching wrong version of NewtonSoft.json.DLL in $dstFile" + Write-Host "INFO: Patching wrong version of Newtonsoft.Json.dll in $dstFile" Copy-Item -Path $newtonSoftDllPath -Destination $dstFile -Force } } diff --git a/AzureAD/Create-AadAppsForNav.ps1 b/AzureAD/Create-AadAppsForNav.ps1 index 0cdec27e0..53999c43c 100644 --- a/AzureAD/Create-AadAppsForNav.ps1 +++ b/AzureAD/Create-AadAppsForNav.ps1 @@ -82,7 +82,7 @@ try { } else { if ($AadAdminCredential) { - $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AadAdminCredential.Password)) + $password = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AadAdminCredential.Password)) if ($password.Length -gt 100) { $account = Connect-AzureAD -AadAccessToken $password -AccountId $AadAdminCredential.UserName } diff --git a/AzureAD/Create-AadUsersInNavContainer.ps1 b/AzureAD/Create-AadUsersInNavContainer.ps1 index 5afe06c5c..9988c4ff1 100644 --- a/AzureAD/Create-AadUsersInNavContainer.ps1 +++ b/AzureAD/Create-AadUsersInNavContainer.ps1 @@ -62,7 +62,7 @@ try { } else { if ($AadAdminCredential) { - $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AadAdminCredential.Password)) + $password = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AadAdminCredential.Password)) if ($password.Length -gt 100) { $account = Connect-AzureAD -AadAccessToken $password -AccountId $AadAdminCredential.UserName } diff --git a/Bacpac/Export-NavContainerDatabasesAsBacpac.ps1 b/Bacpac/Export-NavContainerDatabasesAsBacpac.ps1 index 3a939d7a8..35495bdab 100644 --- a/Bacpac/Export-NavContainerDatabasesAsBacpac.ps1 +++ b/Bacpac/Export-NavContainerDatabasesAsBacpac.ps1 @@ -193,7 +193,7 @@ try { $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } Write-Host "Remove Network Service User from $DatabaseName" @@ -219,7 +219,7 @@ try { $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } Write-Host "Remove Windows Users from $DatabaseName" @@ -249,7 +249,7 @@ try { $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } Write-Host "Remove Application Roles from $DatabaseName" @@ -280,7 +280,7 @@ try { Write-Host "Checking Entitlements" $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } 'Membership Entitlement', 'Entitlement Set', 'Entitlement' | % { @@ -303,7 +303,7 @@ try { $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } Write-Host "Remove data from System Tables database $DatabaseName" @@ -336,7 +336,7 @@ try { $params = @{ 'ErrorAction' = 'Ignore'; 'ServerInstance' = $databaseServer } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } if (!($KeepUserData)) { @@ -416,7 +416,7 @@ try { if ($sqlCredential) { $arguments += @( ('/SourceUser:"'+$sqlCredential.UserName+'"'), - ('/SourcePassword:"'+([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))+'"') + ('/SourcePassword:"'+([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))+'"') ) } @@ -447,7 +447,7 @@ try { if ($sqlCredential) { $arguments += @( ('/SourceUser:"'+$sqlCredential.UserName+'"'), - ('/SourcePassword:"'+([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))+'"') + ('/SourcePassword:"'+([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))+'"') ) } diff --git a/Common/Get-PlainText.ps1 b/Common/Get-PlainText.ps1 index 734063ca8..1f13b934e 100644 --- a/Common/Get-PlainText.ps1 +++ b/Common/Get-PlainText.ps1 @@ -14,12 +14,12 @@ function Get-PlainText() { [parameter(ValueFromPipeline, Mandatory = $true)] [System.Security.SecureString] $SecureString ) - $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString); + $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) try { - return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr); + return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { - [Runtime.InteropServices.Marshal]::FreeBSTR($bstr); + [Runtime.InteropServices.Marshal]::FreeBSTR($bstr) } } Export-ModuleMember -Function Get-PlainText diff --git a/CompilerFolderHandling/New-BcCompilerFolder.ps1 b/CompilerFolderHandling/New-BcCompilerFolder.ps1 index 9091c8527..f9b7268e6 100644 --- a/CompilerFolderHandling/New-BcCompilerFolder.ps1 +++ b/CompilerFolderHandling/New-BcCompilerFolder.ps1 @@ -128,7 +128,12 @@ try { Remove-Item -Path (Join-Path $dllsPath 'Service\SideServices') -Recurse -Force -ErrorAction SilentlyContinue New-Item -Path (Join-Path $dllsPath 'OpenXML') -ItemType Directory | Out-Null Copy-Item -Path (Join-Path $dllsPath 'Service\DocumentFormat.OpenXml.dll') -Destination (Join-Path $dllsPath 'OpenXML') -Force -ErrorAction SilentlyContinue - $mockAssembliesFolder = Join-Path $platformArtifactPath "Test Assemblies\Mock Assemblies" -Resolve + $testAssembliesFolder = Join-Path $platformArtifactPath "Test Assemblies" -Resolve + $testAssembliesDestination = Join-Path $dllsPath "Test Assemblies" + New-Item -Path $testAssembliesDestination -ItemType Directory | Out-Null + Copy-Item -Path (Join-Path $testAssembliesFolder 'Newtonsoft.Json.dll') -Destination $testAssembliesDestination -Force + Copy-Item -Path (Join-Path $testAssembliesFolder 'Microsoft.Dynamics.Framework.UI.Client.dll') -Destination $testAssembliesDestination -Force + $mockAssembliesFolder = Join-Path $testAssembliesFolder "Mock Assemblies" -Resolve Copy-Item -Path $mockAssembliesFolder -Filter '*.dll' -Destination $dllsPath -Recurse $extensionsFolder = Join-Path $appArtifactPath 'Extensions' if (Test-Path $extensionsFolder -PathType Container) { @@ -225,7 +230,6 @@ try { $alcExePath = Join-Path $containerCompilerPath 'extension/bin/linux/alc' if (Test-Path $alcExePath) { # Set execute permissions on alc - Write-Host "Setting execute permissions on alc" & /usr/bin/env sudo pwsh -command "& chmod +x $alcExePath" } else { @@ -253,11 +257,13 @@ try { } } - $symbolsPath = Join-Path $compilerFolder 'symbols' - Write-Host "Enumerating Apps in CompilerFolder $symbolsPath" + Write-Host "Enumerating Apps in $symbolsPath" $compilerFolderAppFiles = @(Get-ChildItem -Path (Join-Path $symbolsPath '*.app') | Select-Object -ExpandProperty FullName) GetAppInfo -AppFiles $compilerFolderAppFiles -compilerFolder $compilerFolder -cacheAppinfoPath (Join-Path $symbolsPath 'cache_AppInfo.json') | Out-Null - + if ($cacheFolder) { + Write-Host "Copying symbols cache" + Copy-Item -Path (Join-Path $symbolsPath 'cache_AppInfo.json') -Destination (Join-Path $compilerFolder 'symbols') -Force + } $compilerFolder } catch { diff --git a/ContainerHandling/New-NavContainer.ps1 b/ContainerHandling/New-NavContainer.ps1 index d673a22af..ecd3ed217 100644 --- a/ContainerHandling/New-NavContainer.ps1 +++ b/ContainerHandling/New-NavContainer.ps1 @@ -1595,7 +1595,7 @@ if (!$restartingInstance) { winrm create winrm/config/Listener?Address=*+Transport=HTTPS (''@{Hostname="dontcare"; CertificateThumbprint="'' + $cert.Thumbprint + ''"}'') winrm set winrm/config/service/Auth ''@{Basic="true"}'' Write-Host "Creating Container user $username" - New-LocalUser -AccountNeverExpires -PasswordNeverExpires -FullName $username -Name '+$bcContainerHelperConfig.WinRmCredentials.UserName+' -Password (ConvertTo-SecureString -string "'+([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($bcContainerHelperConfig.WinRmCredentials.Password)))+'" -AsPlainText -force) | Out-Null + New-LocalUser -AccountNeverExpires -PasswordNeverExpires -FullName $username -Name '+$bcContainerHelperConfig.WinRmCredentials.UserName+' -Password (ConvertTo-SecureString -string "'+([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($bcContainerHelperConfig.WinRmCredentials.Password)))+'" -AsPlainText -force) | Out-Null Add-LocalGroupMember -Group administrators -Member '+$bcContainerHelperConfig.WinRmCredentials.UserName+' } ') | Add-Content -Path "$myfolder\AdditionalSetup.ps1" @@ -2018,7 +2018,7 @@ if (-not `$restartingInstance) { ) if ("$databaseServer" -ne "" -and $bcContainerHelperConfig.useSharedEncryptionKeys -and !$encryptionKeyExists) { - $sharedEncryptionKeyFile = Join-Path $bcContainerHelperConfig.hostHelperFolder "EncryptionKeys\$(-join [security.cryptography.sha256managed]::new().ComputeHash([Text.Encoding]::Utf8.GetBytes(([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password))))).ForEach{$_.ToString("X2")})\DynamicsNAV-v$($version.Major).key" + $sharedEncryptionKeyFile = Join-Path $bcContainerHelperConfig.hostHelperFolder "EncryptionKeys\$(-join [security.cryptography.sha256managed]::new().ComputeHash([Text.Encoding]::Utf8.GetBytes(([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password))))).ForEach{$_.ToString("X2")})\DynamicsNAV-v$($version.Major).key" if (Test-Path $sharedEncryptionKeyFile) { Write-Host "Using Shared Encryption Key file" Copy-Item -Path $sharedEncryptionKeyFile -Destination $containerEncryptionKeyFile diff --git a/ContainerInfo/Get-NavContainerImageLabels.ps1 b/ContainerInfo/Get-NavContainerImageLabels.ps1 index b56d414eb..876b03085 100644 --- a/ContainerInfo/Get-NavContainerImageLabels.ps1 +++ b/ContainerInfo/Get-NavContainerImageLabels.ps1 @@ -36,7 +36,7 @@ function Get-BcContainerImageLabels { } elseif ($registryCredential) { - $credentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($registryCredential.UserName + ":" + [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registryCredential.Password)))) + $credentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($registryCredential.UserName + ":" + [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registryCredential.Password)))) $authorization = "Basic $credentials" } elseif ("$registry" -eq "bcinsider.azurecr.io" -or "$registry" -eq "bcprivate.azurecr.io") { diff --git a/ContainerInfo/Get-NavContainerImageTags.ps1 b/ContainerInfo/Get-NavContainerImageTags.ps1 index fc8f31a30..13cb818d5 100644 --- a/ContainerInfo/Get-NavContainerImageTags.ps1 +++ b/ContainerInfo/Get-NavContainerImageTags.ps1 @@ -33,7 +33,7 @@ function Get-BcContainerImageTags { } elseif ($registryCredential) { - $credentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($registryCredential.UserName + ":" + [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registryCredential.Password)))) + $credentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($registryCredential.UserName + ":" + [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registryCredential.Password)))) $authorization = "Basic $credentials" if ($pageSize -eq -1) { $pageSize = 1000 diff --git a/HelperFunctions.ps1 b/HelperFunctions.ps1 index bbe5de7d6..fd750ee77 100644 --- a/HelperFunctions.ps1 +++ b/HelperFunctions.ps1 @@ -355,59 +355,97 @@ function Expand-7zipArchive { function GetTestToolkitApps { Param( [string] $containerName, + [string] $compilerFolder, [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 - + if ($compilerFolder) { + $symbolsFolder = Join-Path $compilerFolder "symbols" # 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") + $baseAppInfo = Get-AppJsonFromAppFile -appFile (Get-ChildItem -Path $symbolsFolder -Filter 'Microsoft_Base Application_*.*.*.*.app').FullName + $version = [Version]$baseAppInfo.version + if ($version -ge [Version]"19.0.0.0") { + $apps += @('Microsoft_Permissions Mock') } - $apps += @(get-childitem -Path "C:\Applications\TestFramework\TestRunner\*.*" -recurse -filter "*.app") - + $apps += @('Microsoft_Test Runner') if (!$includeTestRunnerOnly) { - $apps += @(get-childitem -Path "C:\Applications\TestFramework\TestLibraries\*.*" -recurse -filter "*.app") - + $apps += @('Microsoft_Any', 'Microsoft_Library Assert', 'Microsoft_Library Variable Storage') if (!$includeTestFrameworkOnly) { # Add Test Libraries - $apps += "Microsoft_System Application Test Library.app", "Microsoft_Business Foundation Test Libraries.app", "Microsoft_Tests-TestLibraries.app" | ForEach-Object { - @(get-childitem -Path "C:\Applications\*.*" -recurse -filter $_) - } - + $apps += @('Microsoft_System Application Test Library', 'Microsoft_Business Foundation Test Libraries', 'Microsoft_Tests-TestLibraries') if (!$includeTestLibrariesOnly) { # Add Tests if ($version -ge [Version]"18.0.0.0") { - $apps += "Microsoft_System Application Test.app", "Microsoft_Business Foundation Tests.app" | ForEach-Object { - @(get-childitem -Path "C:\Applications\*.*" -recurse -filter $_) - } + $apps += @('Microsoft_System Application Test', 'Microsoft_Business Foundation Tests', 'Microsoft_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") + $apps += @('Microsoft_Performance Toolkit') if (!$includeTestFrameworkOnly) { - $apps += @(get-childitem -Path "C:\Applications\TestFramework\PerformanceToolkit\*.*" -recurse -filter "*.app" -exclude "*Toolkit.app") + $apps += @('Microsoft_Performance Toolkit *') } } - + $appFiles = @() $apps | ForEach-Object { - $appFile = Get-ChildItem -path "c:\applications.*\*.*" -recurse -filter ($_.Name).Replace(".app", "_*.app") - if (!($appFile)) { - $appFile = $_ - } - $appFile.FullName + $appFiles += @(get-childitem -Path $symbolsFolder -Filter "$($_)_*.*.*.*.app" | Where-Object {($version.Major -ge 17 -or ($_.Name -notlike 'Microsoft_Tests-Marketing_*.*.*.*.app')) -and $_.Name -notlike "Microsoft_Tests-SINGLESERVER_*.*.*.*.app"} | ForEach-Object { $_.FullName }) } - } -argumentList $includeTestLibrariesOnly, $includeTestFrameworkOnly, $includeTestRunnerOnly, $includePerformanceToolkit + $appFiles | Select-Object -Unique + } + else { + 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_Business Foundation Test Libraries.app", "Microsoft_Tests-TestLibraries.app" | ForEach-Object { + @(get-childitem -Path "C:\Applications\*.*" -recurse -filter $_) + } + + if (!$includeTestLibrariesOnly) { + # Add Tests + if ($version -ge [Version]"18.0.0.0") { + $apps += "Microsoft_System Application Test.app", "Microsoft_Business Foundation Tests.app" | ForEach-Object { + @(get-childitem -Path "C:\Applications\*.*" -recurse -filter $_) + } + } + $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 { + $appFile = Get-ChildItem -path "c:\applications.*\*.*" -recurse -filter ($_.Name).Replace(".app", "_*.app") + if (!($appFile)) { + $appFile = $_ + } + $appFile.FullName + } + } -argumentList $includeTestLibrariesOnly, $includeTestFrameworkOnly, $includeTestRunnerOnly, $includePerformanceToolkit + } } function GetExtendedErrorMessage { @@ -853,7 +891,7 @@ Function CreatePsTestToolFolder { $PsTestFunctionsPath = Join-Path $PsTestToolFolder "PsTestFunctions.ps1" $ClientContextPath = Join-Path $PsTestToolFolder "ClientContext.ps1" - $newtonSoftDllPath = Join-Path $PsTestToolFolder "NewtonSoft.json.dll" + $newtonSoftDllPath = Join-Path $PsTestToolFolder "Newtonsoft.Json.dll" $clientDllPath = Join-Path $PsTestToolFolder "Microsoft.Dynamics.Framework.UI.Client.dll" if (!(Test-Path -Path $PsTestToolFolder -PathType Container)) { @@ -864,9 +902,9 @@ Function CreatePsTestToolFolder { 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" + $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 = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll" } $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName Copy-Item -Path $newtonSoftDllPath -Destination $myNewtonSoftDllPath @@ -1001,6 +1039,7 @@ function GetAppInfo { [string] $cacheAppInfoPath = '' ) + Push-Location $appInfoCache = $null $cacheUpdated = $false if ($cacheAppInfoPath) { @@ -1010,13 +1049,13 @@ function GetAppInfo { else { $appInfoCache = @{} } + Set-Location (Split-Path $cacheAppInfoPath -parent) } Write-Host "::group::Getting .app info $cacheAppInfoPath" $binPath = Join-Path $compilerFolder 'compiler/extension/bin' if ($isLinux) { $alcPath = Join-Path $binPath 'linux' $alToolExe = Join-Path $alcPath 'altool' - Write-Host "Setting execute permissions on altool" & /usr/bin/env sudo pwsh -command "& chmod +x $alToolExe" } else { @@ -1039,8 +1078,9 @@ function GetAppInfo { try { foreach($path in $appFiles) { Write-Host -NoNewline "- $([System.IO.Path]::GetFileName($path))" - if ($appInfoCache -and $appInfoCache.PSObject.Properties.Name -eq $path) { - $appInfo = $appInfoCache."$path" + $relativePath = Resolve-Path -Path $path -Relative + if ($appInfoCache -and $appInfoCache.PSObject.Properties.Name -eq $relativePath) { + $appInfo = $appInfoCache."$relativePath" Write-Host " (cached)" } else { @@ -1087,7 +1127,7 @@ function GetAppInfo { Write-Host " (succeeded using codeanalysis)" } if ($cacheAppInfoPath) { - $appInfoCache | Add-Member -MemberType NoteProperty -Name $path -Value $appInfo + $appInfoCache | Add-Member -MemberType NoteProperty -Name $relativePath -Value $appInfo $cacheUpdated = $true } } @@ -1124,6 +1164,7 @@ function GetAppInfo { if ($packageStream) { $packageStream.Dispose() } + Pop-Location } Write-Host "::endgroup::" } @@ -1234,7 +1275,6 @@ function RunAlTool { $path = DownloadLatestAlLanguageExtension -allowPrerelease:$usePrereleaseAlTool if ($isLinux) { $alToolExe = Join-Path $path 'extension/bin/linux/altool' - Write-Host "Setting execute permissions on altool" & /usr/bin/env sudo pwsh -command "& chmod +x $alToolExe" } else { diff --git a/NavContainerHelper.md b/NavContainerHelper.md index 6d5ef5c49..4d683c605 100644 --- a/NavContainerHelper.md +++ b/NavContainerHelper.md @@ -861,7 +861,7 @@ In this example, it gets the .mdf and .ldf files from the latest cumulative upda $databaseCredential = New-Object System.Management.Automation.PSCredential -argumentList "sa", (ConvertTo-SecureString -String "P@ssword1" -AsPlainText -Force) $databaseName = "CRONUS" $attach_dbs = (ConvertTo-Json -Compress -Depth 99 @(@{"dbName" = "$databaseName"; "dbFiles" = @("c:\temp\${databaseName}.mdf", "c:\temp\${databaseName}.ldf") })).replace('"',"'") - $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) + $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) $dbserverid = docker run -d -e sa_password="$dbPassword" -e ACCEPT_EULA=Y -v "${hostFolder}:C:/temp" -e attach_dbs="$attach_dbs" microsoft/mssql-server-windows-developer $databaseServer = $dbserverid.SubString(0,12) $databaseInstance = "" @@ -877,7 +877,7 @@ The following script sample, will create a new SQL Server container and restore $hostFolder = "c:\temp\navdbfiles" $databaseCredential = New-Object System.Management.Automation.PSCredential -argumentList "sa", (ConvertTo-SecureString -String "P@ssword1" -AsPlainText -Force) - $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) + $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) $dbserverid = docker run -d -e sa_password="$dbPassword" -e ACCEPT_EULA=Y -v "${hostFolder}:C:/temp" microsoft/mssql-server-windows-express $databaseServer = $dbserverid.SubString(0,12) $databaseInstance = "" @@ -903,7 +903,7 @@ Then you will have the variables $databaseServer, $databaseInstance, $databaseNa If you have created your external database through other means, please set these variables in PowerShell. Please try the following script to see whether a docker container can connect to your database: - $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) + $dbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) $databaseServerInstance = @{ $true = "$databaseServer\$databaseInstance"; $false = "$databaseServer"}["$databaseInstance" -ne ""] docker run -it --name sqlconnectiontest microsoft/mssql-server-windows-developer powershell -command "Invoke-Sqlcmd -ServerInstance '$databaseServerInstance' -Username '$($databaseCredential.Username)' -Password '$dbPassword' -Database '$databaseName' -Query 'SELECT COUNT(*) FROM [dbo].[User]'" diff --git a/ObjectHandling/Compile-ObjectsInNavContainer.ps1 b/ObjectHandling/Compile-ObjectsInNavContainer.ps1 index a93fc7705..fab2b992e 100644 --- a/ObjectHandling/Compile-ObjectsInNavContainer.ps1 +++ b/ObjectHandling/Compile-ObjectsInNavContainer.ps1 @@ -59,7 +59,7 @@ try { $params = @{} if ($sqlCredential) { - $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } Compile-NAVApplicationObject @params -Filter $filter ` -DatabaseName $databaseName ` diff --git a/ObjectHandling/Export-NavContainerObjects.ps1 b/ObjectHandling/Export-NavContainerObjects.ps1 index 278cabf5f..ecde39acd 100644 --- a/ObjectHandling/Export-NavContainerObjects.ps1 +++ b/ObjectHandling/Export-NavContainerObjects.ps1 @@ -107,7 +107,7 @@ try { $params = @{ 'ExportTxtSkipUnlicensed' = $true } if ($sqlCredential) { - $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params += @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } if ($exportTo.Contains('(new syntax)')) { $params += @{ 'ExportToNewSyntax' = $true } diff --git a/ObjectHandling/Import-ObjectsToNavContainer.ps1 b/ObjectHandling/Import-ObjectsToNavContainer.ps1 index 1cf725d0a..c5574315f 100644 --- a/ObjectHandling/Import-ObjectsToNavContainer.ps1 +++ b/ObjectHandling/Import-ObjectsToNavContainer.ps1 @@ -57,7 +57,7 @@ try { $params = @{} if ($sqlCredential) { - $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } if ($suppressBuildSearchIndex) { $params += @{ "suppressBuildSearchIndex" = $suppressBuildSearchIndex } diff --git a/ObjectHandling/Import-TestToolkitToNavContainer.ps1 b/ObjectHandling/Import-TestToolkitToNavContainer.ps1 index caf619925..8e0005163 100644 --- a/ObjectHandling/Import-TestToolkitToNavContainer.ps1 +++ b/ObjectHandling/Import-TestToolkitToNavContainer.ps1 @@ -49,6 +49,7 @@ function Import-TestToolkitToBcContainer { Param ( [string] $containerName = $bcContainerHelperConfig.defaultContainerName, + [string] $compilerFolder = '', [PSCredential] $sqlCredential = $null, [PSCredential] $credential = $null, [switch] $includeTestLibrariesOnly, @@ -89,34 +90,49 @@ try { } } - $inspect = docker inspect $containerName | ConvertFrom-Json - if ($inspect.Config.Labels.psobject.Properties.Match('maintainer').Count -eq 0 -or $inspect.Config.Labels.maintainer -ne "Dynamics SMB") { - throw "Container $containerName is not a Business Central container" - } - [System.Version]$version = $inspect.Config.Labels.version - $country = $inspect.Config.Labels.country - - $isBcSandbox = $inspect.Config.Env | Where-Object { $_ -eq "IsBcSandbox=Y" } - - $customConfig = Get-BcContainerServerConfiguration -ContainerName $containerName - if ($bcAuthContext -and $environment) { - - $appFiles = GetTestToolkitApps -containerName $containerName -includeTestRunnerOnly:$includeTestRunnerOnly -includeTestFrameworkOnly:$includeTestFrameworkOnly -includeTestLibrariesOnly:$includeTestLibrariesOnly -includePerformanceToolkit:$includePerformanceToolkit + $appFiles = GetTestToolkitApps -containerName $containerName -compilerFolder $compilerFolder -includeTestRunnerOnly:$includeTestRunnerOnly -includeTestFrameworkOnly:$includeTestFrameworkOnly -includeTestLibrariesOnly:$includeTestLibrariesOnly -includePerformanceToolkit:$includePerformanceToolkit + $installedApps = Get-BcPublishedApps -bcAuthContext $bcauthcontext -environment $environment | Where-Object { $_.state -eq "installed" } $appFiles | ForEach-Object { - $appInfo = Invoke-ScriptInBCContainer -containerName $containerName -scriptblock { Param($appFile) - Get-NAVAppInfo -Path $appFile - } -argumentList $_ | Where-Object { $_ -isnot [System.String] } + if ($compilerFolder) { + $appInfo = Get-AppJsonFromAppFile -appFile $_ + $appVersion = [Version]$appInfo.Version + $appId = $appInfo.Id + } + else { + $appInfo = Invoke-ScriptInBCContainer -containerName $containerName -scriptblock { Param($appFile) + Get-NAVAppInfo -Path $appFile + } -argumentList $_ | Where-Object { $_ -isnot [System.String] } + $appVersion = $appInfo.Version + $appId = $appInfo.AppId + } $targetVersion = "" - if ($appInfo.Version.Major -eq 18 -and $appInfo.Version.Minor -eq 0) { + if ($appVersion.Major -eq 18 -and $appVersion.Minor -eq 0) { $targetVersion = "18.0.23013.23913" } - Install-BcAppFromAppSource -bcAuthContext $bcauthcontext -environment $environment -appId $appInfo.AppId -appVersion $targetVersion -acceptIsvEula -installOrUpdateNeededDependencies + $installedApp = $installedApps | Where-Object { $_.id -eq $appId -and (($targetVersion -eq "") -or ([Version]($_.version) -ge [Version]$targetVersion)) } + if ($installedApp) { + Write-Host "Skipping app '$($installedApp.name)' as it is already installed" + } + else { + Install-BcAppFromAppSource -bcAuthContext $bcauthcontext -environment $environment -appId $appId -appVersion $targetVersion -acceptIsvEula -installOrUpdateNeededDependencies + } } Write-Host -ForegroundColor Green "TestToolkit successfully published" } else { + $inspect = docker inspect $containerName | ConvertFrom-Json + if ($inspect.Config.Labels.psobject.Properties.Match('maintainer').Count -eq 0 -or $inspect.Config.Labels.maintainer -ne "Dynamics SMB") { + throw "Container $containerName is not a Business Central container" + } + [System.Version]$version = $inspect.Config.Labels.version + $country = $inspect.Config.Labels.country + + $isBcSandbox = $inspect.Config.Env | Where-Object { $_ -eq "IsBcSandbox=Y" } + + $customConfig = Get-BcContainerServerConfiguration -ContainerName $containerName + $doNotUpdateSymbols = $doNotUpdateSymbols -or (!(([bool]($customConfig.PSobject.Properties.name -eq "EnableSymbolLoadingAtServerStartup")) -and $customConfig.EnableSymbolLoadingAtServerStartup -eq "True")) $generateSymbols = $false @@ -124,8 +140,7 @@ try { $generateSymbols = $true $doNotUpdateSymbols = $true } - - + if ($version.Major -ge 15) { if ($version -lt [Version]("15.0.35528.0")) { throw "Container $containerName (platform version $version) doesn't support the Test Toolkit yet, you need a laster version" @@ -246,7 +261,7 @@ try { $params = @{} if ($sqlCredential) { - $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } + $params = @{ 'Username' = $sqlCredential.UserName; 'Password' = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password))) } } if ($testToolkitCountry) { $fileFilter = "*.$testToolkitCountry.fob" diff --git a/PackageHandling/Publish-BuildOutputToStorage.ps1 b/PackageHandling/Publish-BuildOutputToStorage.ps1 index 4c28034ee..5e01f527a 100644 --- a/PackageHandling/Publish-BuildOutputToStorage.ps1 +++ b/PackageHandling/Publish-BuildOutputToStorage.ps1 @@ -42,7 +42,7 @@ function Publish-BuildOutputToStorage { $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() try { - if ($StorageConnectionString -is [SecureString]) { $StorageConnectionString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($StorageConnectionString)) } + if ($StorageConnectionString -is [SecureString]) { $StorageConnectionString = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($StorageConnectionString)) } if ($StorageConnectionString -isnot [string]) { throw "StorageConnectionString needs to be a SecureString or a String" } $projectName = $projectName.ToLowerInvariant() $appVersion = $appVersion.ToLowerInvariant() diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index beadbea21..f3c48fdde 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -2,6 +2,14 @@ Add trustServerCertificate is the parameter exists in various functions that might be running on the host Issue #3594 Run-AlCops says unknown platform version Add information about apps excluded due to not being referenced during Publish-BcContainerApp +Run-AlPipeline to support CompilerFolder and online environments for building and testing +Added 2 new overrides in Run-AlPipeline: PipelineInitialize and PipelineFinalize +Add parameter CompilerFolder to Run-TestsInBcContainer and Import-TestToolkitToBcContainer for running tests using CompilerFolder bits from the host +Replace all occurrences of [System.Runtime.InteropServices.Marshal]::PtrToStringAuto with [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR as the Auto function doesn't always do what's expected under Linux +Ensure correct casing of Newtonsoft.Json.dll for Linux +Always add extensionId (when specified) to Properties section in test results xml +If environment is specified as a Web Client URL, and BcAuthContext contains username/password in Run-AlPipeline, then tests will run against this environment. PublishBcContainerApp and ImportTestToolkitToBcContainer needs to be overridden for this to work with full pipeline. +Including caching of appinfos in CompilerFolder cache (to save time when caching on GitHub Actions) 6.0.19 Issue #3560 Backup-BcContainerDatabases fails - password must be marked as read only bug @@ -438,7 +446,7 @@ Use a PowerShell Job to run bcpt tests to be able to remove files afterwards Allow Get-BcContainerAppInfo to be used without explicitely specifying -containerName $containerName, allowing just $containerName Add new function Set-BcContainerServerConfiguration to directly configure settings of the Business Central Server instance (without needing Invoke-ScriptInBcContainer) Add new function Restart-BcContainerServiceTier to restart only the service tier after configuration changes (often necessary) -Patch wrong version of Newtonsoft.json.dll in current insider builds (until this is fixed in the platform) +Patch wrong version of Newtonsoft.Json.dll in current insider builds (until this is fixed in the platform) 3.0.8 Add new function Get-ALGoAuthContext to get AL-Go for GitHub compatible auth context from bcAuthContext obtained from New-BcAuthContext diff --git a/Saas/Publish-PerTenantExtensionApps.ps1 b/Saas/Publish-PerTenantExtensionApps.ps1 index 4a8acab3f..c943b226e 100644 --- a/Saas/Publish-PerTenantExtensionApps.ps1 +++ b/Saas/Publish-PerTenantExtensionApps.ps1 @@ -60,7 +60,7 @@ try { } if ($PsCmdlet.ParameterSetName -eq "CC") { - if ($clientId -is [SecureString]) { $clientID = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($clientID)) } + if ($clientId -is [SecureString]) { $clientID = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($clientID)) } if ($clientId -isnot [String]) { throw "ClientID needs to be a SecureString or a String" } if ($clientSecret -is [String]) { $clientSecret = ConvertTo-SecureString -String $clientSecret -AsPlainText -Force } if ($clientSecret -isnot [SecureString]) { throw "ClientSecret needs to be a SecureString or a String" } diff --git a/SymbolHandling/Generate-SymbolsInNavContainer.ps1 b/SymbolHandling/Generate-SymbolsInNavContainer.ps1 index b27d69f7c..693ed25c2 100644 --- a/SymbolHandling/Generate-SymbolsInNavContainer.ps1 +++ b/SymbolHandling/Generate-SymbolsInNavContainer.ps1 @@ -37,7 +37,7 @@ try { $ProcessArguments += "Command=generatesymbolreference, Database=""$databaseName"", ServerName=""$databaseServer""" if ($sqlCredential) { Write-Host "Adding SQL-Server credentials for authentication" - $ProcessArguments += ", ntauthentication=0, username=""$($sqlCredential.UserName)"", password=""$([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))""" + $ProcessArguments += ", ntauthentication=0, username=""$($sqlCredential.UserName)"", password=""$([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqlCredential.Password)))""" } Write-Host "Creating application symbols in $containerName" diff --git a/UserHandling/New-NavContainerNavUser.ps1 b/UserHandling/New-NavContainerNavUser.ps1 index 92c6b6ed8..19b943d96 100644 --- a/UserHandling/New-NavContainerNavUser.ps1 +++ b/UserHandling/New-NavContainerNavUser.ps1 @@ -101,7 +101,7 @@ try { $sqlparams += @{ "ServerInstance" = $databaseServerInstance "Username" = $databaseCredential.Username - "Password" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) + "Password" = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($databaseCredential.Password)) } } } diff --git a/UserHandling/Setup-NavContainerTestUsers.ps1 b/UserHandling/Setup-NavContainerTestUsers.ps1 index 884924a72..857c9065c 100644 --- a/UserHandling/Setup-NavContainerTestUsers.ps1 +++ b/UserHandling/Setup-NavContainerTestUsers.ps1 @@ -121,7 +121,7 @@ try { $parameters = @{ "name" = "CreateTestUsers$select" - "value" = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))) + "value" = ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))) } Invoke-BcContainerApi -containerName $containerName -tenant $tenant -credential $credential -APIPublisher "Microsoft" -APIGroup "Setup" -APIVersion "beta" -CompanyId $companyId -Method "POST" -Query "testUsers" -body $parameters | Out-Null