DevOpsHandling/Get-AndInstallApp.ps1
function Get-AndInstallApp { param ( [Parameter(Mandatory = $true, ParameterSetName = "Repository")] [string]$Repository, [Parameter(Mandatory = $false, ParameterSetName = "External")] [string]$relativePath = "", [Parameter(Mandatory = $false)] [string]$Version = "latest", [Parameter(Mandatory = $false)] [pscredential] $Credential, [Parameter(Mandatory = $true)] [string]$appFolder, [switch]$useDevEndpoint, [switch]$silentlyContinue, [switch]$isClientRepo, [switch]$isExternal ) $settings = import-config $stAcc = "" $stCont = "" $stRel = "" if ($isExternal.IsPresent) { $stAcc = $settings.storageAccountExternalApps $stCont = $settings.storageContainerExternalApps $stRel = $settings.storageRelativePathExternalApps } else { $stAcc = $settings.storageAccount $stCont = $settings.storageContainer $stRel = $settings.storageRelativePath } # if any of the settings are not set, throw an error if ($stAcc -eq "" -or $stCont -eq "" -or $stRel -eq "") { if ($silentlyContinue.IsPresent) { $false return } Write-Error "Configuration is not complete" return } # create storage context using $settings and Get-Secret $storageContext = New-AzStorageContext -StorageAccountName $stAcc -StorageAccountKey (Get-Secret -SecretName $stAcc) -ErrorAction SilentlyContinue if ($null -eq $storageContext) { if ($silentlyContinue.IsPresent) { $false return } Write-Error "Storage account $($stAcc) does not exist or configuration is not complete" return } # test if container in $settings exist in storage account defined in $storageContext $container = Get-AzStorageContainer -Context $storageContext -Name $stCont -ErrorAction SilentlyContinue if ($null -eq $container) { if ($silentlyContinue.IsPresent) { $false return } Write-Error "Container $($stCont) does not exist in storage account $($stAcc)" return } # create relative path from $settings and replace {repository} and {version} if (!$isExternal.IsPresent) { if ($isClientRepo.IsPresent) { # currently only allow to use all projects in a repository $relativePath = $settings.storageRelativePathClient.Replace("{repository}", $Repository).Replace("{project}", "*").Replace("{version}", $Version) } else { $relativePath = $settings.storageRelativePath.Replace("{repository}", $Repository).Replace("{version}", $Version) } } # test if relative path exists in container $blob = Get-AzStorageBlob -Container $stCont -Context $storageContext -Blob "$($relativePath)*" -ErrorAction SilentlyContinue if ($null -eq $blob) { # if not, try to check for the same version with 0 patch level, if the version is not latest or preview and it is a semantic version if ($Version -ne "latest" -and $Version -ne "preview" -and $Version -match "^\d+\.\d+\.\d+$") { $relativePath = $settings.storageRelativePath.Replace("{repository}", $Repository).Replace("{version}", ($Version.Split(".")[0..2] -join ".") + ".0") $blob = Get-AzStorageBlob -Container $stCont -Context $storageContext -Blob $relativePath -ErrorAction SilentlyContinue } if ($null -eq $blob) { if ($silentlyContinue.IsPresent) { $false return } Write-Error "Version $Version for repository $Repository cannot be found (Relative path $relativePath does not exist in container $($stCont))" return } } if ($isExternal.IsPresent) { $apps = Get-AzStorageBlob -Container $stCont -Context $storageContext -Blob "$($relativePath)" -ErrorAction SilentlyContinue $apps = $apps | Sort-Object -Property LastModified -Descending | Select-Object -First 1 } else { # download all zip files in container that contain "-apps" in name from $relativePath and store it in $appFolder $apps = Get-AzStorageBlob -Container $stCont -Context $storageContext -Blob "$($relativePath)*-apps.zip" -ErrorAction SilentlyContinue } if ($null -eq $apps) { if ($silentlyContinue.IsPresent) { $false return } Write-Error "No apps found in for repository $Repository in $relativePath" return } # loop through all apps, save the app in the $appfolder, and unzip the downloaded app into a subdirectory $repository foreach ($app in $apps) { $appPath = Join-Path $appFolder ($app.Name.Split('/')[-1]) $app | Get-AzStorageBlobContent -Destination $appPath -Force | Out-Null $name = "" if (Split-Path $app.Name -Extension -OutVariable name -ErrorAction SilentlyContinue) { if ($name -eq ".zip") { Expand-Archive -Path $appPath -DestinationPath (Join-Path $appFolder $Repository) -Force Remove-Item -Path $appPath -Filter *.zip -Force | Out-Null } } } # loop through all apps and, if the publisher is not Microsoft, extract the app to a new temp folder $tempFolder = New-TempDirectory foreach ($appfile in (Get-ChildItem -Path (Join-Path $appFolder $Repository) -filter '*.app' -File)) { $isRuntimePackage = $false try { Extract-AppFileToFolder -appFilename $appfile.FullName -appFolder $tempFolder -generateAppJson | Out-Null } catch { if ($_.Exception.Message -eq "You cannot extract a runtime package") { $isRuntimePackage = $true } } if (!$isRuntimePackage) { $appJson = Get-Content -Path (Join-Path $tempFolder 'app.json') | ConvertFrom-Json if ($null -ne $appJson.dependencies) { foreach($dep in $appJson.dependencies) { if ($dep.Publisher -ne "Microsoft") { $tempVersion = [version]$dep.Version $depVersion = ("{0}.{1}.{2}" -f $tempVersion.Major, $tempVersion.Minor, $tempVersion.Build) # do we need to install the app or is it already installed? $appInfo = (Get-BcContainerAppInfo -containerName $containerName -tenantSpecificProperties | Where-Object { $_.Name -eq $dep.Name -and $_.Publisher -eq $dep.Publisher}) $requireInstall = $false if ($null -ne $appInfo) { if ($appInfo.Name -ne $dep.Name -or $appInfo.Version -lt $dep.Version) { $requireInstall = $true } } else { $requireInstall = $true } if ($requireInstall) { # Translate the dependency name to an existing GitHub repository $localRepo = $Repository $isExternal = $false switch ($true) { $dep.Name.StartsWith("NAV-X Allocation") { $localRepo = "nav-x-allocations" break } $dep.Name.StartsWith("NAV-X Library") { $localRepo = "nav-x-library" break } $dep.Name.StartsWith("NAV-X Base Application") { $localRepo = "nav-x-base-application" break } $dep.Name.StartsWith("NAV-X Commission Management") { $localRepo = "nav-x-commission-management" break } $dep.Name.StartsWith("NAV-X Credit Card") { $localRepo = "nav-x-credit-card" break } $dep.Name.StartsWith("NAV-X Credit Management") { $localRepo = "na-x-credit-management" break } $dep.Name.StartsWith("NAV-X National Accounts") { $localRepo = "nav-x-national-accounts" break } $dep.Name.StartsWith("NAV-X PayAssist") { $localRepo = "nav-x-payassist" break } $dep.Name.StartsWith("NAV-X Search") { $localRepo = "nav-x-search" break } default { $isExternal = $true $relativePath = Get-RelativePathForExternalApp -settings $settings -publisher $dep.Publisher -name $dep.Name -version $dep.Version if ($relativePath -eq "") { if ($silentlyContinue.IsPresent) { $false return } Write-Error "Could not find external app $($dep.Name) $($dep.Version) $($dep.Publisher)" return } break } } if ($isExternal) { $result = Get-AndInstallApp -relativePath $relativePath -appFolder $appFolder -useDevEndpoint:$useDevEndpoint -silentlyContinue -isExternal } else { $result = Get-AndInstallApp -Repository $localRepo -Version $depVersion -appFolder $appFolder -useDevEndpoint:$useDevEndpoint -silentlyContinue if (!$result) { $result = Get-AndInstallApp -Repository $localRepo -Version "latest" -appFolder $appFolder -useDevEndpoint:$useDevEndpoint -silentlyContinue if (!$result) { $result = Get-AndInstallApp -Repository $localRepo -Version "preview" -appFolder $appFolder -useDevEndpoint:$useDevEndpoint -silentlyContinue } } } if (!$result) { Write-Error "Dependency $($dep.Name) $($dep.Version) for repository $Repository cannot be found" return } } } } } } $parameters = @{} if ($useDevEndpoint.IsPresent) { $parameters.Add('useDevEndpoint', $true) $parameters.Add('credential', $credential) } try { $result = (Publish-BcContainerApp -containerName $ContainerName -appFile $appFile.FullName -sync -tenant "default" -skipVerification -ignoreIfAppexists -install @parameters 6>&1) } catch { # if the exception starts with "Cannot synchronize the extension because no synchronized extension could be found", # extract the app name from the exception after "the dependency definition for" and before "by". if ($_.Exception.Message.StartsWith("Cannot synchronize the extension because no synchronized extension could be found")) { $appName = $_.Exception.Message.Split("the dependency definition for")[1].Split("by")[0].Trim() $appVersion = $_.Exception.Message.Split(" ")[-1].Trim().Trim(".") $appPublisher = $_.Exception.Message.Split("by")[1].Split($appVersion)[0].Trim() $relativePath = Get-RelativePathForExternalApp -settings $settings -publisher $appPublisher -name $appName -version $appVersion if (Get-AndInstallApp -relativePath $relativePath -appFolder $appFolder -useDevEndpoint:$useDevEndpoint -silentlyContinue -isExternal) { $result = (Publish-BcContainerApp -containerName $ContainerName -appFile $appFile.FullName -sync -tenant "default" -skipVerification -ignoreIfAppexists -install @parameters 6>&1) } } } } Remove-Item -Path $tempFolder -Recurse -Force if ($silentlyContinue.IsPresent) { $true } } |