Modules/businessdev.ALbuild.Containers/Private/Install-BcContainerLanguageApp.ps1
|
function Install-BcContainerLanguageApp { <# .SYNOPSIS Installs Microsoft's language app for a culture into a container's default tenant. .DESCRIPTION Without its language app a culture is not available in the client, even when DefaultLanguage names it: the generic image publishes the language apps but leaves them unsynced and uninstalled. Uses the published app when there is one. Otherwise publishes it from the artifact inside the container: the compiled country folders (C:\Applications.<country>), then C:\Extensions, then the source apps under C:\Applications. Then syncs and installs it, and verifies that it is installed. Idempotent. Throws, listing the language apps that do exist, when no app matches the culture. .PARAMETER Name Container name. .PARAMETER Language Culture name, e.g. 'de-DE'. .PARAMETER ServerInstance BC server instance inside the container. Default 'BC'. .PARAMETER DockerExecutable The Docker executable to use (default 'docker'). .OUTPUTS PSCustomObject with AppName, Version, Action (Installed / AlreadyInstalled) and Source. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [Alias('ContainerName')] [string] $Name, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Language, [string] $ServerInstance = 'BC', [string] $DockerExecutable = 'docker' ) $appName = Get-BcLanguageAppName -Language $Language if (-not $PSCmdlet.ShouldProcess($Name, "Install language app '$appName'")) { return } Write-ALbuildLog "Installing language app '$appName' for '$Language' into '$Name'..." $script = { $isLanguageApp = { param($app) $app.Publisher -eq 'Microsoft' -and $app.Name -eq $AppName } $getPublished = { @(Get-NAVAppInfo -ServerInstance $ServerInstance -Tenant default -TenantSpecificProperties | Where-Object { & $isLanguageApp $_ } | Sort-Object { [version]"$($_.Version)" } -Descending) } $source = 'published' $published = & $getPublished if ($published.Count -eq 0) { # Not published: take it from the artifact. Compiled country folders first, the source app last. $roots = New-Object System.Collections.Generic.List[string] foreach ($d in (Get-ChildItem -Path 'C:\' -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '^Applications\..+' } | Sort-Object Name)) { $roots.Add($d.FullName) } $roots.Add('C:\Extensions'); $roots.Add('C:\Applications') $file = $null foreach ($root in $roots) { if (-not (Test-Path -LiteralPath $root)) { continue } $file = Get-ChildItem -LiteralPath $root -Filter "Microsoft_$AppName*.app" -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '*.runtime.app' } | Select-Object -First 1 if ($file) { break } } if ($file) { Write-Output "Publishing '$AppName' from $($file.FullName)" Publish-NAVApp -ServerInstance $ServerInstance -Path $file.FullName -SkipVerification -Scope Global -ErrorAction Stop $source = $file.FullName $published = & $getPublished } } if ($published.Count -eq 0) { $available = @(Get-NAVAppInfo -ServerInstance $ServerInstance | Where-Object { $_.Publisher -eq 'Microsoft' -and $_.Name -like '* language (*)' } | ForEach-Object { "$($_.Name)" } | Sort-Object -Unique) $list = if ($available.Count -gt 0) { $available -join ', ' } else { '(none)' } throw "No language app '$AppName' for '$Language' is published in the container or ships with its artifact. Available language apps: $list" } $app = $published[0] $action = 'AlreadyInstalled' if ("$($app.SyncState)" -ne 'Synced') { Write-Output "Syncing '$AppName' $($app.Version) (was $($app.SyncState))" Sync-NAVApp -ServerInstance $ServerInstance -Name $app.Name -Publisher $app.Publisher -Version $app.Version -ErrorAction Stop } if (-not $app.IsInstalled) { Install-NAVApp -ServerInstance $ServerInstance -Name $app.Name -Publisher $app.Publisher -Version $app.Version -ErrorAction Stop $action = 'Installed' } $check = Get-NAVAppInfo -ServerInstance $ServerInstance -Name $app.Name -Publisher $app.Publisher -Version $app.Version -Tenant default -TenantSpecificProperties if (-not $check -or -not $check.IsInstalled) { throw "Language app '$AppName' $($app.Version) is still not installed after Install-NAVApp (sync state: $($check.SyncState))." } Write-Output "ALBUILD-LANGAPP|$($app.Name)|$($app.Version)|$action|$source" } $output = @(Invoke-BcContainerCommand -ContainerName $Name -ScriptBlock $script -DockerExecutable $DockerExecutable -Variables @{ AppName = $appName Language = $Language ServerInstance = $ServerInstance }) $lines = @($output | ForEach-Object { "$_" -split "`r?`n" } | Where-Object { $_ }) foreach ($line in ($lines | Where-Object { $_ -notlike 'ALBUILD-LANGAPP|*' })) { Write-ALbuildLog " $line" } $marker = $lines | Where-Object { $_ -like 'ALBUILD-LANGAPP|*' } | Select-Object -Last 1 if (-not $marker) { throw "Installing language app '$appName' into '$Name' returned no result. Output: $($lines -join ' | ')" } $parts = $marker -split '\|' $result = [PSCustomObject]@{ AppName = $parts[1]; Version = $parts[2]; Action = $parts[3]; Source = $parts[4] } $verb = if ($result.Action -eq 'Installed') { 'Installed' } else { 'Already installed:' } Write-ALbuildLog -Level Success "$verb language app '$($result.AppName)' $($result.Version) in '$Name' (source: $($result.Source))." return $result } |