From 3d959c07f89d49bc8fc19931c64addce212cdfad Mon Sep 17 00:00:00 2001 From: Freddy Kristiansen Date: Wed, 16 Oct 2024 09:51:45 +0200 Subject: [PATCH] Add resilience to publishing (#3726) This PR should fix some instabilities when publishing many apps to an online service. It fixes: - A constant renewal of the authcontext when expired - Some times the Nav.Upload API call resulted in an error in the server - but the app was still uploaded Fixes #3628 --------- Co-authored-by: freddydk --- ReleaseNotes.txt | 1 + Saas/Publish-PerTenantExtensionApps.ps1 | 44 +++++++++++++++++-------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index d835a32be..2c5de4e12 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -10,6 +10,7 @@ Issue 3621 New-AadAppsForBc shows "-includeEmailAadApp is deprecated. Use -inclu Issue 3718 Business Central Web Client Fails to Load After Updating to Windows 24H2 Issue #3703 Missing Function Download-BcEnvironmentInstalledExtensionToFolder Fix for issue 1262 in AL-Go for GitHub +Fix instabilities during Publish-PerTenantExtensionApps 6.0.25 Fix issue where generateDependencyArtifact doesn't result in dependencies if useCompilerFolder is true or filesonly containers are used in Run-AlPipeline diff --git a/Saas/Publish-PerTenantExtensionApps.ps1 b/Saas/Publish-PerTenantExtensionApps.ps1 index 5193944f9..77f3e048c 100644 --- a/Saas/Publish-PerTenantExtensionApps.ps1 +++ b/Saas/Publish-PerTenantExtensionApps.ps1 @@ -51,9 +51,11 @@ function Publish-PerTenantExtensionApps { $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() try { + $script:authContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext + function GetAuthHeaders { - $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext - return @{ "Authorization" = "Bearer $($bcAuthContext.AccessToken)" } + $script:authContext = Renew-BcAuthContext -bcAuthContext $script:authContext + return @{ "Authorization" = "Bearer $($script:authContext.AccessToken)" } } $newLine = @{} @@ -131,7 +133,7 @@ try { $streamHeader = @{ "Content-Type" = 'application/octet-stream'} try { Sort-AppFilesByDependencies -appFiles $appFiles -excludeRuntimePackages | ForEach-Object { - Write-Host -NoNewline "$([System.IO.Path]::GetFileName($_)) - " + Write-Host @newline "$([System.IO.Path]::GetFileName($_)) - " $appJson = Get-AppJsonFromAppFile -appFile $_ $existingApp = $extensions | Where-Object { $_.id -eq $appJson.id -and $_.isInstalled } @@ -185,11 +187,13 @@ try { Invoke-RestMethod ` -Method Post ` -Uri "$automationApiUrl/companies($companyId)/extensionUpload($($extensionUpload.systemId))/Microsoft.NAV.upload" ` - -Headers ((GetAuthHeaders) + $ifMatchHeader) | Out-Null + -Headers ((GetAuthHeaders) + $ifMatchHeader) ` + -ErrorAction SilentlyContinue | Out-Null Write-Host @newLine "." $completed = $false $errCount = 0 $sleepSeconds = 30 + $lastStatus = '' while (!$completed) { Start-Sleep -Seconds $sleepSeconds @@ -197,44 +201,55 @@ try { $extensionDeploymentStatusResponse = Invoke-WebRequest -Headers (GetAuthHeaders) -Method Get -Uri "$automationApiUrl/companies($companyId)/extensionDeploymentStatus" -UseBasicParsing $extensionDeploymentStatuses = (ConvertFrom-Json $extensionDeploymentStatusResponse.Content).value - $completed = $true - $extensionDeploymentStatuses | Where-Object { $_.publisher -eq $appJson.publisher -and $_.name -eq $appJson.name -and $_.appVersion -eq $appJson.version } | % { + $thisExtension = $extensionDeploymentStatuses | Where-Object { $_.publisher -eq $appJson.publisher -and $_.name -eq $appJson.name -and $_.appVersion -eq $appJson.version } + if ($null -eq $thisExtension) { + throw "Unable to find extension deployment status" + } + $thisExtension | ForEach-Object { + if ($_.status -ne $lastStatus) { + if (!$useNewLine) { Write-Host } + Write-Host @newLine $_.status + $lastStatus = $_.status + } if ($_.status -eq "InProgress") { + $errCount = 0 + $sleepSeconds = 5 Write-Host @newLine "." - $completed = $false } elseif ($_.Status -eq "Unknown") { throw "Unknown Error" } - elseif ($_.Status -ne "Completed") { + elseif ($_.Status -eq "Completed") { + if (!$useNewLine) { Write-Host } + $completed = $true + } + else { $errCount = 5 throw $_.status } } - $errCount = 0 - $sleepSeconds = 5 } catch { + if (!$useNewLine) { Write-Host } if ($errCount++ -gt 4) { Write-Host $_.Exception.Message throw "Unable to publish app. Please open the Extension Deployment Status Details page in Business Central to see the detailed error message." } $sleepSeconds += $sleepSeconds - $completed = $false + Write-Host "Error: $($_.Exception.Message). Retrying in $sleepSeconds seconds" } } - if ($completed) { - Write-Host "completed" - } } } } catch [System.Net.WebException],[System.Net.Http.HttpRequestException] { + if (!$useNewLine) { Write-Host } Write-Host "ERROR $($_.Exception.Message)" Write-Host $_.ScriptStackTrace throw (GetExtendedErrorMessage $_) } catch { + if (!$useNewLine) { Write-Host } Write-Host "ERROR: $($_.Exception.Message) [$($_.Exception.GetType().FullName)]" throw } @@ -264,6 +279,7 @@ catch { throw } finally { + $script:authContext = $null TrackTrace -telemetryScope $telemetryScope } }