From df6671d729d2f4321a661b44f1af83fadd5e75df Mon Sep 17 00:00:00 2001 From: jessikitty Date: Wed, 29 Apr 2026 09:36:17 +1000 Subject: [PATCH] fix: add AssemblyResolve handler for reflection diagnostics Previous version failed GetExportedTypes() because Assembly.Load(bytes) couldn't resolve Disco.Services.dll from the PowerShell process. Now uses LoadFrom() with an AssemblyResolve handler that probes both the build output and Disco bin directories. Also adds ReflectionTypeLoad exception detail logging and proper handler cleanup in finally block. --- Build-Plugin.ps1 | 480 +---------------------------------------------- 1 file changed, 1 insertion(+), 479 deletions(-) diff --git a/Build-Plugin.ps1 b/Build-Plugin.ps1 index decf6c2..97ed4d5 100644 --- a/Build-Plugin.ps1 +++ b/Build-Plugin.ps1 @@ -1,479 +1 @@ -# Build-Plugin.ps1 -# Builds and packages the AD Compare plugin for Disco ICT import -# -# Usage: -# .\Build-Plugin.ps1 -DiscoBinPath "C:\Program Files\Disco\WebApp\bin" - -param( - [Parameter(Mandatory=$true)] - [string]$DiscoBinPath, - - [string]$Configuration = "Release" -) - -$ErrorActionPreference = "Stop" -$PluginDir = $PSScriptRoot -$PluginName = "Disco.Plugins.ADCompare" - -Write-Host "=== AD Compare Plugin Builder ===" -ForegroundColor Cyan -Write-Host "Disco bin path: $DiscoBinPath" -ForegroundColor Gray - -# --- Validate --- -if (-not (Test-Path $DiscoBinPath)) { - Write-Error "Disco bin path not found: $DiscoBinPath" - exit 1 -} - -$requiredDlls = @("Disco.Services.dll", "Disco.Models.dll", "Disco.Data.dll") -foreach ($dll in $requiredDlls) { - $dllPath = Join-Path $DiscoBinPath $dll - if (-not (Test-Path $dllPath)) { - Write-Error "Required assembly not found: $dllPath" - exit 1 - } -} -Write-Host "All required Disco assemblies found" -ForegroundColor Green - -# --- Show Disco Assembly Versions --- -Write-Host "`n--- Disco Assembly Versions ---" -ForegroundColor DarkGray -foreach ($dll in $requiredDlls) { - $dllPath = Join-Path $DiscoBinPath $dll - try { - $asmName = [System.Reflection.AssemblyName]::GetAssemblyName($dllPath) - Write-Host " $dll : v$($asmName.Version)" -ForegroundColor Gray - } catch { - Write-Warning " $dll : could not read version - $($_.Exception.Message)" - } -} - -# --- Find MSBuild --- -$msbuild = $null -$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -if (Test-Path $vsWhere) { - $msbuild = & $vsWhere -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" 2>$null | Select-Object -First 1 -} -if (-not $msbuild -or -not (Test-Path $msbuild)) { - $msbuild = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe" -} -if (-not (Test-Path $msbuild)) { - Write-Error "MSBuild not found." - exit 1 -} -Write-Host "Using MSBuild: $msbuild" -ForegroundColor Gray - -# --- Resolve all HintPaths to Disco bin --- -$csproj = Join-Path $PluginDir "$PluginName.csproj" -$csprojContent = Get-Content $csproj -Raw -$csprojContent = [regex]::Replace($csprojContent, '[^<]+\\([^\\<]+\.dll)', "$DiscoBinPath\`$1") - -$tempCsproj = Join-Path $PluginDir "$PluginName.build.csproj" -$csprojContent | Set-Content $tempCsproj -Encoding UTF8 -Write-Host "Assembly references resolved to $DiscoBinPath" -ForegroundColor Green - -# --- Build --- -Write-Host "`nBuilding $PluginName ($Configuration)..." -ForegroundColor Yellow -$buildOutput = Join-Path $PluginDir "bin\$Configuration" - -& $msbuild $tempCsproj /p:Configuration=$Configuration /p:OutputPath=$buildOutput /t:Build /v:minimal - -if ($LASTEXITCODE -ne 0) { - Remove-Item $tempCsproj -ErrorAction SilentlyContinue - Write-Error "Build failed!" - exit 1 -} -Remove-Item $tempCsproj -ErrorAction SilentlyContinue - -$pluginDll = Join-Path $buildOutput "$PluginName.dll" -if (-not (Test-Path $pluginDll)) { - Write-Error "Build output not found: $pluginDll" - exit 1 -} -Write-Host "Build successful: $pluginDll" -ForegroundColor Green - -# --- Copy Dependencies for Reflection and ManifestGenerator --- -foreach ($dll in $requiredDlls) { - Copy-Item (Join-Path $DiscoBinPath $dll) $buildOutput -Force -ErrorAction SilentlyContinue -} -$extraDlls = @("EntityFramework.dll", "System.Web.Mvc.dll", "System.Web.WebPages.dll", "System.Web.Razor.dll", "RazorGenerator.Mvc.dll", "Newtonsoft.Json.dll") -foreach ($dep in $extraDlls) { - $depPath = Join-Path $DiscoBinPath $dep - if (Test-Path $depPath) { Copy-Item $depPath $buildOutput -Force -ErrorAction SilentlyContinue } -} - -# --- Reflection-Based Feature Diagnostics --- -Write-Host "`n--- Plugin Assembly Diagnostics ---" -ForegroundColor Yellow - -$reflectionOk = $true -try { - $pluginAsmBytes = [System.IO.File]::ReadAllBytes($pluginDll) - $pluginAsm = [System.Reflection.Assembly]::Load($pluginAsmBytes) - - Write-Host " Plugin assembly loaded: $($pluginAsm.FullName)" -ForegroundColor Gray - - # Find all types that extend UIExtensionFeature - $uiExtTypes = @() - foreach ($t in $pluginAsm.GetExportedTypes()) { - $baseType = $t.BaseType - if ($baseType -ne $null -and $baseType.IsGenericType) { - $genDef = $baseType.GetGenericTypeDefinition() - if ($genDef.FullName -eq 'Disco.Services.Plugins.Features.UIExtension.UIExtensionFeature`1') { - $uiExtTypes += $t - } - } - } - - if ($uiExtTypes.Count -eq 0) { - Write-Warning " NO UIExtensionFeature implementations found in assembly!" - Write-Warning " ExecuteAction will never be called without UIExtension features." - $reflectionOk = $false - } else { - Write-Host " Found $($uiExtTypes.Count) UIExtension feature(s):" -ForegroundColor Green - } - - foreach ($t in $uiExtTypes) { - $baseType = $t.BaseType - $genArgs = $baseType.GetGenericArguments() - $modelType = $genArgs[0] - $genDefFullName = $baseType.GetGenericTypeDefinition().FullName - - Write-Host "`n Feature: $($t.FullName)" -ForegroundColor Cyan - Write-Host " Base type: $($baseType.FullName)" -ForegroundColor Gray - Write-Host " Generic definition: $genDefFullName" -ForegroundColor Gray - Write-Host " Model type: $($modelType.FullName)" -ForegroundColor Gray - Write-Host " Model assembly: $($modelType.Assembly.GetName().Name) v$($modelType.Assembly.GetName().Version)" -ForegroundColor Gray - - # Verify [PluginFeature] attribute - $attrs = $t.GetCustomAttributes($true) | Where-Object { $_.GetType().Name -eq 'PluginFeatureAttribute' } - if ($attrs) { - foreach ($attr in $attrs) { - Write-Host " [PluginFeature] Id: $($attr.Id)" -ForegroundColor Gray - Write-Host " [PluginFeature] Name: $($attr.Name)" -ForegroundColor Gray - } - } else { - Write-Warning " MISSING [PluginFeature] attribute! Disco cannot discover this feature." - $reflectionOk = $false - } - - # Verify ExecuteAction is overridden (not just the abstract base) - $execMethod = $t.GetMethod('ExecuteAction', [System.Reflection.BindingFlags]'Public,Instance') - if ($execMethod -and $execMethod.DeclaringType -eq $t) { - Write-Host " ExecuteAction: overridden (OK)" -ForegroundColor Green - } elseif ($execMethod) { - Write-Warning " ExecuteAction: NOT overridden (declared in $($execMethod.DeclaringType.Name))" - $reflectionOk = $false - } else { - Write-Warning " ExecuteAction: NOT FOUND" - $reflectionOk = $false - } - - # Verify Initialize is overridden and calls Register() - $initMethod = $t.GetMethod('Initialize', [System.Reflection.BindingFlags]'Public,Instance', $null, @([Type]'Disco.Data.Repository.DiscoDataContext'), $null) - if ($initMethod -and $initMethod.DeclaringType -eq $t) { - Write-Host " Initialize: overridden (OK)" -ForegroundColor Green - } else { - Write-Warning " Initialize: NOT overridden - Register() will never be called!" - $reflectionOk = $false - } - - # Check that the model type implements BaseUIModel - $baseUIModel = $modelType.GetInterface('BaseUIModel') - if ($baseUIModel) { - Write-Host " BaseUIModel: $($modelType.Name) implements BaseUIModel (OK)" -ForegroundColor Green - } else { - Write-Warning " BaseUIModel: $($modelType.Name) does NOT implement BaseUIModel!" - $reflectionOk = $false - } - } - - # Check the main plugin class - $pluginClass = $pluginAsm.GetExportedTypes() | Where-Object { - $_.BaseType -ne $null -and $_.BaseType.FullName -eq 'Disco.Services.Plugins.Plugin' - } - if ($pluginClass) { - Write-Host "`n Plugin class: $($pluginClass.FullName)" -ForegroundColor Cyan - $pluginAttrs = $pluginClass.GetCustomAttributes($true) | Where-Object { $_.GetType().Name -eq 'PluginAttribute' } - if ($pluginAttrs) { - foreach ($attr in $pluginAttrs) { - Write-Host " [Plugin] Id: $($attr.Id)" -ForegroundColor Gray - Write-Host " [Plugin] Name: $($attr.Name)" -ForegroundColor Gray - } - } else { - Write-Warning " MISSING [Plugin] attribute!" - } - } - -} catch { - Write-Warning " Reflection diagnostics failed: $($_.Exception.Message)" - Write-Warning " This may indicate assembly load issues (missing dependencies, version mismatches)." - $reflectionOk = $false -} - -if ($reflectionOk) { - Write-Host "`n All reflection checks PASSED" -ForegroundColor Green -} else { - Write-Host "`n Some reflection checks FAILED - see warnings above" -ForegroundColor Red -} - -# --- Generate Manifest --- -Write-Host "`n--- Manifest Generation ---" -ForegroundColor Yellow - -$useManifestGen = $false -$manifestGenExe = Join-Path $DiscoBinPath "Disco.Services.Plugins.ManifestGenerator.exe" -if (-not (Test-Path $manifestGenExe)) { - $manifestGenExe = Join-Path (Split-Path $DiscoBinPath -Parent) "Disco.Services.Plugins.ManifestGenerator.exe" -} -if (Test-Path $manifestGenExe) { - Write-Host "Found ManifestGenerator: $manifestGenExe" -ForegroundColor Gray - $useManifestGen = $true - - # Capture stdout and stderr separately for diagnostics - $manifestGenOutput = & $manifestGenExe $pluginDll 2>&1 - $manifestGenExit = $LASTEXITCODE - - if ($manifestGenOutput) { - Write-Host " ManifestGenerator output:" -ForegroundColor DarkGray - $manifestGenOutput | ForEach-Object { - $line = $_.ToString() - if ($_ -is [System.Management.Automation.ErrorRecord]) { - Write-Host " [STDERR] $line" -ForegroundColor Red - } else { - Write-Host " $line" -ForegroundColor DarkGray - } - } - } - - if ($manifestGenExit -ne 0) { - Write-Warning "ManifestGenerator exited with code $manifestGenExit, falling back to manual manifest..." - $useManifestGen = $false - } else { - Write-Host "ManifestGenerator completed successfully" -ForegroundColor Green - } -} else { - Write-Host "ManifestGenerator not found at:" -ForegroundColor DarkGray - Write-Host " $DiscoBinPath" -ForegroundColor DarkGray - Write-Host " $(Split-Path $DiscoBinPath -Parent)" -ForegroundColor DarkGray - Write-Host "Falling back to manual manifest generation..." -ForegroundColor Yellow -} - -if (-not $useManifestGen) { - $version = [System.Reflection.AssemblyName]::GetAssemblyName($pluginDll).Version - - # CategoryTypeName must match the .NET generic type definition FullName. - # For UIExtensionFeature, this is UIExtensionFeature`1 (one backtick, then 1). - # In PowerShell double-quoted strings, `` (two backticks) produces one literal backtick. - $uiExtCategoryTypeName = "Disco.Services.Plugins.Features.UIExtension.UIExtensionFeature``1" - - $manifest = [ordered]@{ - Id = $PluginName - Name = "AD Compare" - Author = "Jess Rogerson" - Url = "https://gitea.hideawaygaming.com.au/jessikitty/disco-ad-compare-plugin" - Version = $version.ToString() - AssemblyPath = "$PluginName.dll" - TypeName = "Disco.Plugins.ADCompare.ADComparePlugin" - ConfigurationHandlerTypeName = "Disco.Plugins.ADCompare.ConfigurationHandler.ADCompareConfigurationHandler" - WebHandlerTypeName = "Disco.Plugins.ADCompare.WebHandler.ADCompareWebHandler" - Features = @( - [ordered]@{ - Id = "ADCompareDeviceUI" - Name = "Device Page - AD Compare" - TypeName = "Disco.Plugins.ADCompare.Features.DeviceUIExtension" - PrimaryFeature = $false - CategoryTypeName = $uiExtCategoryTypeName - }, - [ordered]@{ - Id = "ADCompareUserUI" - Name = "User Page - AD Compare" - TypeName = "Disco.Plugins.ADCompare.Features.UserUIExtension" - PrimaryFeature = $false - CategoryTypeName = $uiExtCategoryTypeName - } - ) - } - $manifestJson = $manifest | ConvertTo-Json -Depth 5 - $manifestJson | Set-Content (Join-Path $buildOutput "manifest.json") -Encoding UTF8 - Write-Host "Manual manifest.json created" -ForegroundColor Green -} - -# --- Validate Manifest --- -Write-Host "`n--- Manifest Validation ---" -ForegroundColor Yellow - -$manifestPath = Join-Path $buildOutput "manifest.json" -if (-not (Test-Path $manifestPath)) { - Write-Error "manifest.json NOT FOUND in build output! Plugin cannot be loaded by Disco." - exit 1 -} - -$manifestContent = Get-Content $manifestPath -Raw -$manifestObj = $manifestContent | ConvertFrom-Json - -# Basic structure checks -$manifestErrors = @() -$manifestWarnings = @() - -if (-not $manifestObj.Id) { $manifestErrors += "Missing 'Id'" } -if (-not $manifestObj.TypeName) { $manifestErrors += "Missing 'TypeName'" } -if (-not $manifestObj.AssemblyPath) { $manifestErrors += "Missing 'AssemblyPath'" } -if (-not $manifestObj.Version) { $manifestWarnings += "Missing 'Version'" } - -Write-Host " Id: $($manifestObj.Id)" -ForegroundColor Gray -Write-Host " Version: $($manifestObj.Version)" -ForegroundColor Gray -Write-Host " TypeName: $($manifestObj.TypeName)" -ForegroundColor Gray - -if (-not $manifestObj.Features -or $manifestObj.Features.Count -eq 0) { - $manifestErrors += "Features array is EMPTY or MISSING - no UIExtensions will be loaded!" -} else { - Write-Host " Features: $($manifestObj.Features.Count) found" -ForegroundColor Green - - # Expected CategoryTypeName (with literal backtick) - $expectedCategory = 'Disco.Services.Plugins.Features.UIExtension.UIExtensionFeature`1' - - foreach ($feature in $manifestObj.Features) { - Write-Host "`n Feature: $($feature.Id)" -ForegroundColor Cyan - Write-Host " Name: $($feature.Name)" -ForegroundColor Gray - Write-Host " TypeName: $($feature.TypeName)" -ForegroundColor Gray - Write-Host " CategoryTypeName: $($feature.CategoryTypeName)" -ForegroundColor Gray - Write-Host " PrimaryFeature: $($feature.PrimaryFeature)" -ForegroundColor Gray - - if (-not $feature.TypeName) { - $manifestErrors += "Feature '$($feature.Id)' is missing TypeName" - } - - if (-not $feature.CategoryTypeName) { - $manifestErrors += "Feature '$($feature.Id)' is missing CategoryTypeName" - } elseif ($feature.CategoryTypeName -ne $expectedCategory) { - $manifestErrors += "Feature '$($feature.Id)' CategoryTypeName MISMATCH!" - Write-Host " Expected: $expectedCategory" -ForegroundColor Red - Write-Host " Got: $($feature.CategoryTypeName)" -ForegroundColor Red - - # Char-by-char comparison to identify the exact difference - $expChars = $expectedCategory.ToCharArray() - $actChars = $feature.CategoryTypeName.ToCharArray() - for ($i = 0; $i -lt [Math]::Max($expChars.Length, $actChars.Length); $i++) { - $ec = if ($i -lt $expChars.Length) { $expChars[$i] } else { $null } - $ac = if ($i -lt $actChars.Length) { $actChars[$i] } else { $null } - if ($ec -ne $ac) { - $ecDisplay = if ($ec) { "'$ec' (0x{0:X2})" -f [int][char]$ec } else { '[END]' } - $acDisplay = if ($ac) { "'$ac' (0x{0:X2})" -f [int][char]$ac } else { '[END]' } - Write-Host " First difference at position ${i}: expected $ecDisplay vs got $acDisplay" -ForegroundColor Red - break - } - } - } else { - Write-Host " CategoryTypeName: VALID" -ForegroundColor Green - } - } -} - -# Show raw manifest for manual inspection -Write-Host "`n --- Raw manifest.json ---" -ForegroundColor DarkGray -$manifestContent -split "`n" | ForEach-Object { - Write-Host " $_" -ForegroundColor DarkGray -} -Write-Host " --- End manifest.json ---" -ForegroundColor DarkGray - -if ($manifestErrors.Count -gt 0) { - Write-Host "`n MANIFEST ERRORS:" -ForegroundColor Red - foreach ($err in $manifestErrors) { - Write-Host " [ERROR] $err" -ForegroundColor Red - } -} -if ($manifestWarnings.Count -gt 0) { - foreach ($warn in $manifestWarnings) { - Write-Host " [WARN] $warn" -ForegroundColor Yellow - } -} -if ($manifestErrors.Count -eq 0) { - Write-Host "`n All manifest checks PASSED" -ForegroundColor Green -} else { - Write-Host "`n Manifest has errors - UIExtensions will NOT render!" -ForegroundColor Red - Write-Host " Fix the errors above before installing the plugin." -ForegroundColor Red -} - -# --- Package --- -Write-Host "`nPackaging .discoPlugin file..." -ForegroundColor Yellow - -$includeFiles = @("$PluginName.dll", "$PluginName.pdb", "manifest.json") - -if (-not $version) { - $version = [System.Reflection.AssemblyName]::GetAssemblyName($pluginDll).Version -} - -$packageName = "$PluginName-$($version.ToString()).discoPlugin" -$packagePath = Join-Path $PluginDir $packageName - -Get-ChildItem $PluginDir -Filter "*.discoPlugin" | Remove-Item -Force -Get-ChildItem $PluginDir -Filter "*.zip" | Where-Object { $_.Name -like "$PluginName*" } | Remove-Item -Force - -$tempPkg = Join-Path $env:TEMP "discoplugin_$([guid]::NewGuid().ToString('N'))" -New-Item -ItemType Directory -Path $tempPkg -Force | Out-Null -$missingFiles = @() -foreach ($f in $includeFiles) { - $src = Join-Path $buildOutput $f - if (Test-Path $src) { - Copy-Item $src $tempPkg -Force - } else { - $missingFiles += $f - } -} - -if ($missingFiles.Count -gt 0) { - Write-Warning "Missing files not included in package: $($missingFiles -join ', ')" -} - -Write-Host "Files in package:" -ForegroundColor Gray -Get-ChildItem $tempPkg -File | ForEach-Object { - $size = [math]::Round($_.Length / 1KB, 1) - Write-Host " $($_.Name) ($size KB)" -ForegroundColor Gray -} - -$zipPath = Join-Path $PluginDir "$PluginName-$($version.ToString()).zip" -Compress-Archive -Path "$tempPkg\*" -DestinationPath $zipPath -Force -Rename-Item $zipPath $packageName -Force -Remove-Item $tempPkg -Recurse -Force - -# --- Final Package Verification --- -Write-Host "`n--- Package Verification ---" -ForegroundColor Yellow -try { - Add-Type -AssemblyName System.IO.Compression.FileSystem - $zip = [System.IO.Compression.ZipFile]::OpenRead($packagePath) - $entries = $zip.Entries | Select-Object -ExpandProperty Name - $zip.Dispose() - - $requiredEntries = @("$PluginName.dll", "manifest.json") - foreach ($req in $requiredEntries) { - if ($entries -contains $req) { - Write-Host " [OK] $req" -ForegroundColor Green - } else { - Write-Host " [MISSING] $req" -ForegroundColor Red - } - } - # Show any extras - foreach ($entry in $entries) { - if ($entry -notin $requiredEntries) { - Write-Host " [+] $entry" -ForegroundColor Gray - } - } -} catch { - Write-Warning " Could not verify package contents: $($_.Exception.Message)" -} - -$packageSize = [math]::Round((Get-Item $packagePath).Length / 1KB, 1) -Write-Host "`n=== Package created: $packagePath ($($packageSize) KB) ===" -ForegroundColor Green -Write-Host "Import into Disco ICT via: Configuration > Plugins > Install Plugin" -ForegroundColor Cyan - -# --- Summary --- -$hasIssues = (-not $reflectionOk) -or ($manifestErrors.Count -gt 0) -if ($hasIssues) { - Write-Host "`n=== BUILD COMPLETED WITH WARNINGS ===" -ForegroundColor Yellow - Write-Host "Review the diagnostics above before installing." -ForegroundColor Yellow - Write-Host "If UIExtensions still don't render after install:" -ForegroundColor Yellow - Write-Host " 1. Check Disco logs for feature initialization messages" -ForegroundColor Yellow - Write-Host " 2. View page source and search for 'layout_uiExtensions'" -ForegroundColor Yellow - Write-Host " - Present but empty = ExecuteAction returned Nothing()/null" -ForegroundColor Yellow - Write-Host " - Missing entirely = Feature not registered (Register() not called)" -ForegroundColor Yellow - Write-Host " 3. Check the installed manifest at:" -ForegroundColor Yellow - Write-Host " App_Data\Plugins\$PluginName\manifest.json" -ForegroundColor Yellow -} else { - Write-Host "`n=== BUILD COMPLETED SUCCESSFULLY ===" -ForegroundColor Green -} +IyBCdWlsZC1QbHVnaW4ucHMxCiMgQnVpbGRzIGFuZCBwYWNrYWdlcyB0aGUgQUQgQ29tcGFyZSBwbHVnaW4gZm9yIERpc2NvIElDVCBpbXBvcnQKIwojIFVzYWdlOgojICAgLlxCdWlsZC1QbHVnaW4ucHMxIC1EaXNjb0JpblBhdGggIkM6XFByb2dyYW0gRmlsZXNcRGlzY29cV2ViQXBwXGJpbiIKCnBhcmFtKAogICAgW1BhcmFtZXRlcihNYW5kYXRvcnk9JHRydWUpXQogICAgW3N0cmluZ10kRGlzY29CaW5QYXRoLAoKICAgIFtzdHJpbmddJENvbmZpZ3VyYXRpb24gPSAiUmVsZWFzZSIKKQoKJEVycm9yQWN0aW9uUHJlZmVyZW5jZSA9ICJTdG9wIgokUGx1Z2luRGlyID0gJFBTU2NyaXB0Um9vdAokUGx1Z2luTmFtZSA9ICJEaXNjby5QbHVnaW5zLkFEQ29tcGFyZSIKCldyaXRlLUhvc3QgIj09PSBBRCBDb21wYXJlIFBsdWdpbiBCdWlsZGVyID09PSIgLUZvcmVncm91bmRDb2xvciBDeWFuCldyaXRlLUhvc3QgIkRpc2NvIGJpbiBwYXRoOiAkRGlzY29CaW5QYXRoIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKCiMgLS0tIFZhbGlkYXRlIC0tLQppZiAoLW5vdCAoVGVzdC1QYXRoICREaXNjb0JpblBhdGgpKSB7CiAgICBXcml0ZS1FcnJvciAiRGlzY28gYmluIHBhdGggbm90IGZvdW5kOiAkRGlzY29CaW5QYXRoIgogICAgZXhpdCAxCn0KCiRyZXF1aXJlZERsbHMgPSBAKCJEaXNjby5TZXJ2aWNlcy5kbGwiLCAiRGlzY28uTW9kZWxzLmRsbCIsICJEaXNjby5EYXRhLmRsbCIpCmZvcmVhY2ggKCRkbGwgaW4gJHJlcXVpcmVkRGxscykgewogICAgJGRsbFBhdGggPSBKb2luLVBhdGggJERpc2NvQmluUGF0aCAkZGxsCiAgICBpZiAoLW5vdCAoVGVzdC1QYXRoICRkbGxQYXRoKSkgewogICAgICAgIFdyaXRlLUVycm9yICJSZXF1aXJlZCBhc3NlbWJseSBub3QgZm91bmQ6ICRkbGxQYXRoIgogICAgICAgIGV4aXQgMQogICAgfQp9CldyaXRlLUhvc3QgIkFsbCByZXF1aXJlZCBEaXNjbyBhc3NlbWJsaWVzIGZvdW5kIiAtRm9yZWdyb3VuZENvbG9yIEdyZWVuCgojIC0tLSBTaG93IERpc2NvIEFzc2VtYmx5IFZlcnNpb25zIC0tLQpXcml0ZS1Ib3N0ICJgbi0tLSBEaXNjbyBBc3NlbWJseSBWZXJzaW9ucyAtLS0iIC1Gb3JlZ3JvdW5kQ29sb3IgRGFya0dyYXkKZm9yZWFjaCAoJGRsbCBpbiAkcmVxdWlyZWREbGxzKSB7CiAgICAkZGxsUGF0aCA9IEpvaW4tUGF0aCAkRGlzY29CaW5QYXRoICRkbGwKICAgIHRyeSB7CiAgICAgICAgJGFzbU5hbWUgPSBbU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHlOYW1lXTo6R2V0QXNzZW1ibHlOYW1lKCRkbGxQYXRoKQogICAgICAgIFdyaXRlLUhvc3QgIiAgJGRsbCA6IHYkKCRhc21OYW1lLlZlcnNpb24pIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgIH0gY2F0Y2ggewogICAgICAgIFdyaXRlLVdhcm5pbmcgIiAgJGRsbCA6IGNvdWxkIG5vdCByZWFkIHZlcnNpb24gLSAkKCRfLkV4Y2VwdGlvbi5NZXNzYWdlKSIKICAgIH0KfQoKIyAtLS0gRmluZCBNU0J1aWxkIC0tLQokbXNidWlsZCA9ICRudWxsCiR2c1doZXJlID0gIiR7ZW52OlByb2dyYW1GaWxlcyh4ODYpfVxNaWNyb3NvZnQgVmlzdWFsIFN0dWRpb1xJbnN0YWxsZXJcdnN3aGVyZS5leGUiCmlmIChUZXN0LVBhdGggJHZzV2hlcmUpIHsKICAgICRtc2J1aWxkID0gJiAkdnNXaGVyZSAtbGF0ZXN0IC1yZXF1aXJlcyBNaWNyb3NvZnQuQ29tcG9uZW50Lk1TQnVpbGQgLWZpbmQgIk1TQnVpbGRcKipcQmluXE1TQnVpbGQuZXhlIiAyPiRudWxsIHwgU2VsZWN0LU9iamVjdCAtRmlyc3QgMQp9CmlmICgtbm90ICRtc2J1aWxkIC1vciAtbm90IChUZXN0LVBhdGggJG1zYnVpbGQpKSB7CiAgICAkbXNidWlsZCA9ICJDOlxXaW5kb3dzXE1pY3Jvc29mdC5ORVRcRnJhbWV3b3JrNjRcdjQuMC4zMDMxOVxNU0J1aWxkLmV4ZSIKfQppZiAoLW5vdCAoVGVzdC1QYXRoICRtc2J1aWxkKSkgewogICAgV3JpdGUtRXJyb3IgIk1TQnVpbGQgbm90IGZvdW5kLiIKICAgIGV4aXQgMQp9CldyaXRlLUhvc3QgIlVzaW5nIE1TQnVpbGQ6ICRtc2J1aWxkIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKCiMgLS0tIFJlc29sdmUgYWxsIEhpbnRQYXRocyB0byBEaXNjbyBiaW4gLS0tCiRjc3Byb2ogPSBKb2luLVBhdGggJFBsdWdpbkRpciAiJFBsdWdpbk5hbWUuY3Nwcm9qIgokY3Nwcm9qQ29udGVudCA9IEdldC1Db250ZW50ICRjc3Byb2ogLVJhdwokY3Nwcm9qQ29udGVudCA9IFtyZWdleF06OlJlcGxhY2UoJGNzcHJvakNvbnRlbnQsICc8SGludFBhdGg+W148XStcXChbXlxcPF0rXC5kbGwpPC9IaW50UGF0aD4nLCAiPEhpbnRQYXRoPiREaXNjb0JpblBhdGhcYCQxPC9IaW50UGF0aD4iKQoKJHRlbXBDc3Byb2ogPSBKb2luLVBhdGggJFBsdWdpbkRpciAiJFBsdWdpbk5hbWUuYnVpbGQuY3Nwcm9qIgokY3Nwcm9qQ29udGVudCB8IFNldC1Db250ZW50ICR0ZW1wQ3Nwcm9qIC1FbmNvZGluZyBVVEY4CldyaXRlLUhvc3QgIkFzc2VtYmx5IHJlZmVyZW5jZXMgcmVzb2x2ZWQgdG8gJERpc2NvQmluUGF0aCIgLUZvcmVncm91bmRDb2xvciBHcmVlbgoKIyAtLS0gQnVpbGQgLS0tCldyaXRlLUhvc3QgImBuQnVpbGRpbmcgJFBsdWdpbk5hbWUgKCRDb25maWd1cmF0aW9uKS4uLiIgLUZvcmVncm91bmRDb2xvciBZZWxsb3cKJGJ1aWxkT3V0cHV0ID0gSm9pbi1QYXRoICRQbHVnaW5EaXIgImJpblwkQ29uZmlndXJhdGlvbiIKCiYgJG1zYnVpbGQgJHRlbXBDc3Byb2ogL3A6Q29uZmlndXJhdGlvbj0kQ29uZmlndXJhdGlvbiAvcDpPdXRwdXRQYXRoPSRidWlsZE91dHB1dCAvdDpCdWlsZCAvdjptaW5pbWFsCgppZiAoJExBU1RFWElUQ09ERSAtbmUgMCkgewogICAgUmVtb3ZlLUl0ZW0gJHRlbXBDc3Byb2ogLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUKICAgIFdyaXRlLUVycm9yICJCdWlsZCBmYWlsZWQhIgogICAgZXhpdCAxCn0KUmVtb3ZlLUl0ZW0gJHRlbXBDc3Byb2ogLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUKCiRwbHVnaW5EbGwgPSBKb2luLVBhdGggJGJ1aWxkT3V0cHV0ICIkUGx1Z2luTmFtZS5kbGwiCmlmICgtbm90IChUZXN0LVBhdGggJHBsdWdpbkRsbCkpIHsKICAgIFdyaXRlLUVycm9yICJCdWlsZCBvdXRwdXQgbm90IGZvdW5kOiAkcGx1Z2luRGxsIgogICAgZXhpdCAxCn0KV3JpdGUtSG9zdCAiQnVpbGQgc3VjY2Vzc2Z1bDogJHBsdWdpbkRsbCIgLUZvcmVncm91bmRDb2xvciBHcmVlbgoKIyAtLS0gQ29weSBEZXBlbmRlbmNpZXMgZm9yIFJlZmxlY3Rpb24gYW5kIE1hbmlmZXN0R2VuZXJhdG9yIC0tLQpmb3JlYWNoICgkZGxsIGluICRyZXF1aXJlZERsbHMpIHsKICAgIENvcHktSXRlbSAoSm9pbi1QYXRoICREaXNjb0JpblBhdGggJGRsbCkgJGJ1aWxkT3V0cHV0IC1Gb3JjZSAtRXJyb3JBY3Rpb24gU2lsZW50bHlDb250aW51ZQp9CiRleHRyYURsbHMgPSBAKCJFbnRpdHlGcmFtZXdvcmsuZGxsIiwgIlN5c3RlbS5XZWIuTXZjLmRsbCIsICJTeXN0ZW0uV2ViLldlYlBhZ2VzLmRsbCIsICJTeXN0ZW0uV2ViLlJhem9yLmRsbCIsICJSYXpvckdlbmVyYXRvci5NdmMuZGxsIiwgIk5ld3RvbnNvZnQuSnNvbi5kbGwiKQpmb3JlYWNoICgkZGVwIGluICRleHRyYURsbHMpIHsKICAgICRkZXBQYXRoID0gSm9pbi1QYXRoICREaXNjb0JpblBhdGggJGRlcAogICAgaWYgKFRlc3QtUGF0aCAkZGVwUGF0aCkgeyBDb3B5LUl0ZW0gJGRlcFBhdGggJGJ1aWxkT3V0cHV0IC1Gb3JjZSAtRXJyb3JBY3Rpb24gU2lsZW50bHlDb250aW51ZSB9Cn0KCiMgLS0tIFJlZmxlY3Rpb24tQmFzZWQgRmVhdHVyZSBEaWFnbm9zdGljcyAtLS0KV3JpdGUtSG9zdCAiYG4tLS0gUGx1Z2luIEFzc2VtYmx5IERpYWdub3N0aWNzIC0tLSIgLUZvcmVncm91bmRDb2xvciBZZWxsb3cKCiRyZWZsZWN0aW9uT2sgPSAkdHJ1ZQp0cnkgewogICAgIyBTZXQgdXAgQXNzZW1ibHlSZXNvbHZlIGhhbmRsZXIgc28gdGhlIENMUiBjYW4gZmluZCBEaXNjby5TZXJ2aWNlcy5kbGwgZXRjLgogICAgIyB3aGVuIHdlIGxvYWQgdGhlIHBsdWdpbiBhc3NlbWJseSBmb3IgcmVmbGVjdGlvbgogICAgJHJlc29sdmVIYW5kbGVyID0gW1N5c3RlbS5SZXNvbHZlRXZlbnRIYW5kbGVyXXsKICAgICAgICBwYXJhbSgkc2VuZGVyLCAkYXJncykKICAgICAgICAkc2hvcnROYW1lID0gKCRhcmdzLk5hbWUgLXNwbGl0ICcsJylbMF0uVHJpbSgpCiAgICAgICAgJHByb2JlUGF0aHMgPSBAKCRidWlsZE91dHB1dCwgJERpc2NvQmluUGF0aCkKICAgICAgICBmb3JlYWNoICgkcHJvYmUgaW4gJHByb2JlUGF0aHMpIHsKICAgICAgICAgICAgJGNhbmRpZGF0ZSA9IEpvaW4tUGF0aCAkcHJvYmUgIiRzaG9ydE5hbWUuZGxsIgogICAgICAgICAgICBpZiAoVGVzdC1QYXRoICRjYW5kaWRhdGUpIHsKICAgICAgICAgICAgICAgIHJldHVybiBbU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHldOjpMb2FkRnJvbSgkY2FuZGlkYXRlKQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiAkbnVsbAogICAgfQogICAgW1N5c3RlbS5BcHBEb21haW5dOjpDdXJyZW50RG9tYWluLmFkZF9Bc3NlbWJseVJlc29sdmUoJHJlc29sdmVIYW5kbGVyKQoKICAgICRwbHVnaW5Bc20gPSBbU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHldOjpMb2FkRnJvbSgkcGx1Z2luRGxsKQoKICAgIFdyaXRlLUhvc3QgIiAgUGx1Z2luIGFzc2VtYmx5IGxvYWRlZDogJCgkcGx1Z2luQXNtLkZ1bGxOYW1lKSIgLUZvcmVncm91bmRDb2xvciBHcmF5CgogICAgIyBGaW5kIGFsbCB0eXBlcyB0aGF0IGV4dGVuZCBVSUV4dGVuc2lvbkZlYXR1cmU8VD4KICAgICR1aUV4dFR5cGVzID0gQCgpCiAgICBmb3JlYWNoICgkdCBpbiAkcGx1Z2luQXNtLkdldEV4cG9ydGVkVHlwZXMoKSkgewogICAgICAgICRiYXNlVHlwZSA9ICR0LkJhc2VUeXBlCiAgICAgICAgaWYgKCRiYXNlVHlwZSAtbmUgJG51bGwgLWFuZCAkYmFzZVR5cGUuSXNHZW5lcmljVHlwZSkgewogICAgICAgICAgICAkZ2VuRGVmID0gJGJhc2VUeXBlLkdldEdlbmVyaWNUeXBlRGVmaW5pdGlvbigpCiAgICAgICAgICAgIGlmICgkZ2VuRGVmLkZ1bGxOYW1lIC1lcSAnRGlzY28uU2VydmljZXMuUGx1Z2lucy5GZWF0dXJlcy5VSUV4dGVuc2lvbi5VSUV4dGVuc2lvbkZlYXR1cmVgMScpIHsKICAgICAgICAgICAgICAgICR1aUV4dFR5cGVzICs9ICR0CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgaWYgKCR1aUV4dFR5cGVzLkNvdW50IC1lcSAwKSB7CiAgICAgICAgV3JpdGUtV2FybmluZyAiICBOTyBVSUV4dGVuc2lvbkZlYXR1cmU8VD4gaW1wbGVtZW50YXRpb25zIGZvdW5kIGluIGFzc2VtYmx5ISIKICAgICAgICBXcml0ZS1XYXJuaW5nICIgIEV4ZWN1dGVBY3Rpb24gd2lsbCBuZXZlciBiZSBjYWxsZWQgd2l0aG91dCBVSUV4dGVuc2lvbiBmZWF0dXJlcy4iCiAgICAgICAgJHJlZmxlY3Rpb25PayA9ICRmYWxzZQogICAgfSBlbHNlIHsKICAgICAgICBXcml0ZS1Ib3N0ICIgIEZvdW5kICQoJHVpRXh0VHlwZXMuQ291bnQpIFVJRXh0ZW5zaW9uIGZlYXR1cmUocyk6IiAtRm9yZWdyb3VuZENvbG9yIEdyZWVuCiAgICB9CgogICAgZm9yZWFjaCAoJHQgaW4gJHVpRXh0VHlwZXMpIHsKICAgICAgICAkYmFzZVR5cGUgPSAkdC5CYXNlVHlwZQogICAgICAgICRnZW5BcmdzID0gJGJhc2VUeXBlLkdldEdlbmVyaWNBcmd1bWVudHMoKQogICAgICAgICRtb2RlbFR5cGUgPSAkZ2VuQXJnc1swXQogICAgICAgICRnZW5EZWZGdWxsTmFtZSA9ICRiYXNlVHlwZS5HZXRHZW5lcmljVHlwZURlZmluaXRpb24oKS5GdWxsTmFtZQoKICAgICAgICBXcml0ZS1Ib3N0ICJgbiAgRmVhdHVyZTogJCgkdC5GdWxsTmFtZSkiIC1Gb3JlZ3JvdW5kQ29sb3IgQ3lhbgogICAgICAgIFdyaXRlLUhvc3QgIiAgICBCYXNlIHR5cGU6ICAgICAgICAgICAkKCRiYXNlVHlwZS5GdWxsTmFtZSkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgICAgIFdyaXRlLUhvc3QgIiAgICBHZW5lcmljIGRlZmluaXRpb246ICAkZ2VuRGVmRnVsbE5hbWUiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgICAgIFdyaXRlLUhvc3QgIiAgICBNb2RlbCB0eXBlOiAgICAgICAgICAkKCRtb2RlbFR5cGUuRnVsbE5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICBXcml0ZS1Ib3N0ICIgICAgTW9kZWwgYXNzZW1ibHk6ICAgICAgJCgkbW9kZWxUeXBlLkFzc2VtYmx5LkdldE5hbWUoKS5OYW1lKSB2JCgkbW9kZWxUeXBlLkFzc2VtYmx5LkdldE5hbWUoKS5WZXJzaW9uKSIgLUZvcmVncm91bmRDb2xvciBHcmF5CgogICAgICAgICMgVmVyaWZ5IFtQbHVnaW5GZWF0dXJlXSBhdHRyaWJ1dGUKICAgICAgICAkYXR0cnMgPSAkdC5HZXRDdXN0b21BdHRyaWJ1dGVzKCR0cnVlKSB8IFdoZXJlLU9iamVjdCB7ICRfLkdldFR5cGUoKS5OYW1lIC1lcSAnUGx1Z2luRmVhdHVyZUF0dHJpYnV0ZScgfQogICAgICAgIGlmICgkYXR0cnMpIHsKICAgICAgICAgICAgZm9yZWFjaCAoJGF0dHIgaW4gJGF0dHJzKSB7CiAgICAgICAgICAgICAgICBXcml0ZS1Ib3N0ICIgICAgW1BsdWdpbkZlYXR1cmVdIElkOiAgJCgkYXR0ci5JZCkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgICAgICAgICAgICAgV3JpdGUtSG9zdCAiICAgIFtQbHVnaW5GZWF0dXJlXSBOYW1lOiAkKCRhdHRyLk5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICAgICAgfQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIFdyaXRlLVdhcm5pbmcgIiAgICBNSVNTSU5HIFtQbHVnaW5GZWF0dXJlXSBhdHRyaWJ1dGUhIERpc2NvIGNhbm5vdCBkaXNjb3ZlciB0aGlzIGZlYXR1cmUuIgogICAgICAgICAgICAkcmVmbGVjdGlvbk9rID0gJGZhbHNlCiAgICAgICAgfQoKICAgICAgICAjIFZlcmlmeSBFeGVjdXRlQWN0aW9uIGlzIG92ZXJyaWRkZW4gKG5vdCBqdXN0IHRoZSBhYnN0cmFjdCBiYXNlKQogICAgICAgICRleGVjTWV0aG9kID0gJHQuR2V0TWV0aG9kKCdFeGVjdXRlQWN0aW9uJywgW1N5c3RlbS5SZWZsZWN0aW9uLkJpbmRpbmdGbGFnc10nUHVibGljLEluc3RhbmNlJykKICAgICAgICBpZiAoJGV4ZWNNZXRob2QgLWFuZCAkZXhlY01ldGhvZC5EZWNsYXJpbmdUeXBlIC1lcSAkdCkgewogICAgICAgICAgICBXcml0ZS1Ib3N0ICIgICAgRXhlY3V0ZUFjdGlvbjogICAgICAgb3ZlcnJpZGRlbiAoT0spIiAtRm9yZWdyb3VuZENvbG9yIEdyZWVuCiAgICAgICAgfSBlbHNlaWYgKCRleGVjTWV0aG9kKSB7CiAgICAgICAgICAgIFdyaXRlLVdhcm5pbmcgIiAgICBFeGVjdXRlQWN0aW9uOiAgICAgICBOT1Qgb3ZlcnJpZGRlbiAoZGVjbGFyZWQgaW4gJCgkZXhlY01ldGhvZC5EZWNsYXJpbmdUeXBlLk5hbWUpKSIKICAgICAgICAgICAgJHJlZmxlY3Rpb25PayA9ICRmYWxzZQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIFdyaXRlLVdhcm5pbmcgIiAgICBFeGVjdXRlQWN0aW9uOiAgICAgICBOT1QgRk9VTkQiCiAgICAgICAgICAgICRyZWZsZWN0aW9uT2sgPSAkZmFsc2UKICAgICAgICB9CgogICAgICAgICMgVmVyaWZ5IEluaXRpYWxpemUgaXMgb3ZlcnJpZGRlbiBhbmQgY2FsbHMgUmVnaXN0ZXIoKQogICAgICAgICRpbml0TWV0aG9kID0gJHQuR2V0TWV0aG9kKCdJbml0aWFsaXplJywgW1N5c3RlbS5SZWZsZWN0aW9uLkJpbmRpbmdGbGFnc10nUHVibGljLEluc3RhbmNlJywgJG51bGwsIEAoW1R5cGVdJ0Rpc2NvLkRhdGEuUmVwb3NpdG9yeS5EaXNjb0RhdGFDb250ZXh0JyksICRudWxsKQogICAgICAgIGlmICgkaW5pdE1ldGhvZCAtYW5kICRpbml0TWV0aG9kLkRlY2xhcmluZ1R5cGUgLWVxICR0KSB7CiAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICBJbml0aWFsaXplOiAgICAgICAgICBvdmVycmlkZGVuIChPSykiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JlZW4KICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBXcml0ZS1XYXJuaW5nICIgICAgSW5pdGlhbGl6ZTogICAgICAgICAgTk9UIG92ZXJyaWRkZW4gLSBSZWdpc3RlcigpIHdpbGwgbmV2ZXIgYmUgY2FsbGVkISIKICAgICAgICAgICAgJHJlZmxlY3Rpb25PayA9ICRmYWxzZQogICAgICAgIH0KCiAgICAgICAgIyBDaGVjayB0aGF0IHRoZSBtb2RlbCB0eXBlIGltcGxlbWVudHMgQmFzZVVJTW9kZWwKICAgICAgICAkYmFzZVVJTW9kZWwgPSAkbW9kZWxUeXBlLkdldEludGVyZmFjZSgnQmFzZVVJTW9kZWwnKQogICAgICAgIGlmICgkYmFzZVVJTW9kZWwpIHsKICAgICAgICAgICAgV3JpdGUtSG9zdCAiICAgIEJhc2VVSU1vZGVsOiAgICAgICAgICQoJG1vZGVsVHlwZS5OYW1lKSBpbXBsZW1lbnRzIEJhc2VVSU1vZGVsIChPSykiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JlZW4KICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBXcml0ZS1XYXJuaW5nICIgICAgQmFzZVVJTW9kZWw6ICAgICAgICAgJCgkbW9kZWxUeXBlLk5hbWUpIGRvZXMgTk9UIGltcGxlbWVudCBCYXNlVUlNb2RlbCEiCiAgICAgICAgICAgICRyZWZsZWN0aW9uT2sgPSAkZmFsc2UKICAgICAgICB9CiAgICB9CgogICAgIyBDaGVjayB0aGUgbWFpbiBwbHVnaW4gY2xhc3MKICAgICRwbHVnaW5DbGFzcyA9ICRwbHVnaW5Bc20uR2V0RXhwb3J0ZWRUeXBlcygpIHwgV2hlcmUtT2JqZWN0IHsKICAgICAgICAkXy5CYXNlVHlwZSAtbmUgJG51bGwgLWFuZCAkXy5CYXNlVHlwZS5GdWxsTmFtZSAtZXEgJ0Rpc2NvLlNlcnZpY2VzLlBsdWdpbnMuUGx1Z2luJwogICAgfQogICAgaWYgKCRwbHVnaW5DbGFzcykgewogICAgICAgIFdyaXRlLUhvc3QgImBuICBQbHVnaW4gY2xhc3M6ICQoJHBsdWdpbkNsYXNzLkZ1bGxOYW1lKSIgLUZvcmVncm91bmRDb2xvciBDeWFuCiAgICAgICAgJHBsdWdpbkF0dHJzID0gJHBsdWdpbkNsYXNzLkdldEN1c3RvbUF0dHJpYnV0ZXMoJHRydWUpIHwgV2hlcmUtT2JqZWN0IHsgJF8uR2V0VHlwZSgpLk5hbWUgLWVxICdQbHVnaW5BdHRyaWJ1dGUnIH0KICAgICAgICBpZiAoJHBsdWdpbkF0dHJzKSB7CiAgICAgICAgICAgIGZvcmVhY2ggKCRhdHRyIGluICRwbHVnaW5BdHRycykgewogICAgICAgICAgICAgICAgV3JpdGUtSG9zdCAiICAgIFtQbHVnaW5dIElkOiAgICQoJGF0dHIuSWQpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICBbUGx1Z2luXSBOYW1lOiAkKCRhdHRyLk5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICAgICAgfQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIFdyaXRlLVdhcm5pbmcgIiAgICBNSVNTSU5HIFtQbHVnaW5dIGF0dHJpYnV0ZSEiCiAgICAgICAgfQogICAgfQoKfSBjYXRjaCB7CiAgICBXcml0ZS1XYXJuaW5nICIgIFJlZmxlY3Rpb24gZGlhZ25vc3RpY3MgZmFpbGVkOiAkKCRfLkV4Y2VwdGlvbi5NZXNzYWdlKSIKICAgIGlmICgkXy5FeGNlcHRpb24uSW5uZXJFeGNlcHRpb24pIHsKICAgICAgICBXcml0ZS1XYXJuaW5nICIgIElubmVyIGV4Y2VwdGlvbjogJCgkXy5FeGNlcHRpb24uSW5uZXJFeGNlcHRpb24uTWVzc2FnZSkiCiAgICB9CiAgICAjIEZvciBSZWZsZWN0aW9uVHlwZUxvYWRFeGNlcHRpb24sIHNob3cgdGhlIGxvYWRlciBleGNlcHRpb25zCiAgICBpZiAoJF8uRXhjZXB0aW9uLklubmVyRXhjZXB0aW9uIC1hbmQgJF8uRXhjZXB0aW9uLklubmVyRXhjZXB0aW9uLkdldFR5cGUoKS5OYW1lIC1lcSAnUmVmbGVjdGlvblR5cGVMb2FkRXhjZXB0aW9uJykgewogICAgICAgICRsb2FkZXJFeGNlcHRpb25zID0gJF8uRXhjZXB0aW9uLklubmVyRXhjZXB0aW9uLkxvYWRlckV4Y2VwdGlvbnMKICAgICAgICBpZiAoJGxvYWRlckV4Y2VwdGlvbnMpIHsKICAgICAgICAgICAgV3JpdGUtV2FybmluZyAiICBMb2FkZXIgZXhjZXB0aW9uczoiCiAgICAgICAgICAgICRsb2FkZXJFeGNlcHRpb25zIHwgU2VsZWN0LU9iamVjdCAtVW5pcXVlIHwgRm9yRWFjaC1PYmplY3QgewogICAgICAgICAgICAgICAgV3JpdGUtV2FybmluZyAiICAgIC0gJCgkXy5NZXNzYWdlKSIKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KICAgIFdyaXRlLVdhcm5pbmcgIiAgVGhpcyBtYXkgaW5kaWNhdGUgYXNzZW1ibHkgbG9hZCBpc3N1ZXMgKG1pc3NpbmcgZGVwZW5kZW5jaWVzLCB2ZXJzaW9uIG1pc21hdGNoZXMpLiIKICAgICRyZWZsZWN0aW9uT2sgPSAkZmFsc2UKfSBmaW5hbGx5IHsKICAgICMgQ2xlYW4gdXAgdGhlIEFzc2VtYmx5UmVzb2x2ZSBoYW5kbGVyCiAgICBpZiAoJHJlc29sdmVIYW5kbGVyKSB7CiAgICAgICAgW1N5c3RlbS5BcHBEb21haW5dOjpDdXJyZW50RG9tYWluLnJlbW92ZV9Bc3NlbWJseVJlc29sdmUoJHJlc29sdmVIYW5kbGVyKQogICAgfQp9CgppZiAoJHJlZmxlY3Rpb25PaykgewogICAgV3JpdGUtSG9zdCAiYG4gIEFsbCByZWZsZWN0aW9uIGNoZWNrcyBQQVNTRUQiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JlZW4KfSBlbHNlIHsKICAgIFdyaXRlLUhvc3QgImBuICBTb21lIHJlZmxlY3Rpb24gY2hlY2tzIEZBSUxFRCAtIHNlZSB3YXJuaW5ncyBhYm92ZSIgLUZvcmVncm91bmRDb2xvciBSZWQKfQoKIyAtLS0gR2VuZXJhdGUgTWFuaWZlc3QgLS0tCldyaXRlLUhvc3QgImBuLS0tIE1hbmlmZXN0IEdlbmVyYXRpb24gLS0tIiAtRm9yZWdyb3VuZENvbG9yIFllbGxvdwoKJHVzZU1hbmlmZXN0R2VuID0gJGZhbHNlCiRtYW5pZmVzdEdlbkV4ZSA9IEpvaW4tUGF0aCAkRGlzY29CaW5QYXRoICJEaXNjby5TZXJ2aWNlcy5QbHVnaW5zLk1hbmlmZXN0R2VuZXJhdG9yLmV4ZSIKaWYgKC1ub3QgKFRlc3QtUGF0aCAkbWFuaWZlc3RHZW5FeGUpKSB7CiAgICAkbWFuaWZlc3RHZW5FeGUgPSBKb2luLVBhdGggKFNwbGl0LVBhdGggJERpc2NvQmluUGF0aCAtUGFyZW50KSAiRGlzY28uU2VydmljZXMuUGx1Z2lucy5NYW5pZmVzdEdlbmVyYXRvci5leGUiCn0KaWYgKFRlc3QtUGF0aCAkbWFuaWZlc3RHZW5FeGUpIHsKICAgIFdyaXRlLUhvc3QgIkZvdW5kIE1hbmlmZXN0R2VuZXJhdG9yOiAkbWFuaWZlc3RHZW5FeGUiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgJHVzZU1hbmlmZXN0R2VuID0gJHRydWUKCiAgICAjIENhcHR1cmUgc3Rkb3V0IGFuZCBzdGRlcnIgc2VwYXJhdGVseSBmb3IgZGlhZ25vc3RpY3MKICAgICRtYW5pZmVzdEdlbk91dHB1dCA9ICYgJG1hbmlmZXN0R2VuRXhlICRwbHVnaW5EbGwgMj4mMQogICAgJG1hbmlmZXN0R2VuRXhpdCA9ICRMQVNURVhJVENPREUKCiAgICBpZiAoJG1hbmlmZXN0R2VuT3V0cHV0KSB7CiAgICAgICAgV3JpdGUtSG9zdCAiICBNYW5pZmVzdEdlbmVyYXRvciBvdXRwdXQ6IiAtRm9yZWdyb3VuZENvbG9yIERhcmtHcmF5CiAgICAgICAgJG1hbmlmZXN0R2VuT3V0cHV0IHwgRm9yRWFjaC1PYmplY3QgewogICAgICAgICAgICAkbGluZSA9ICRfLlRvU3RyaW5nKCkKICAgICAgICAgICAgaWYgKCRfIC1pcyBbU3lzdGVtLk1hbmFnZW1lbnQuQXV0b21hdGlvbi5FcnJvclJlY29yZF0pIHsKICAgICAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICBbU1RERVJSXSAkbGluZSIgLUZvcmVncm91bmRDb2xvciBSZWQKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICAkbGluZSIgLUZvcmVncm91bmRDb2xvciBEYXJrR3JheQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIGlmICgkbWFuaWZlc3RHZW5FeGl0IC1uZSAwKSB7CiAgICAgICAgV3JpdGUtV2FybmluZyAiTWFuaWZlc3RHZW5lcmF0b3IgZXhpdGVkIHdpdGggY29kZSAkbWFuaWZlc3RHZW5FeGl0LCBmYWxsaW5nIGJhY2sgdG8gbWFudWFsIG1hbmlmZXN0Li4uIgogICAgICAgICR1c2VNYW5pZmVzdEdlbiA9ICRmYWxzZQogICAgfSBlbHNlIHsKICAgICAgICBXcml0ZS1Ib3N0ICJNYW5pZmVzdEdlbmVyYXRvciBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IiAtRm9yZWdyb3VuZENvbG9yIEdyZWVuCiAgICB9Cn0gZWxzZSB7CiAgICBXcml0ZS1Ib3N0ICJNYW5pZmVzdEdlbmVyYXRvciBub3QgZm91bmQgYXQ6IiAtRm9yZWdyb3VuZENvbG9yIERhcmtHcmF5CiAgICBXcml0ZS1Ib3N0ICIgICREaXNjb0JpblBhdGgiIC1Gb3JlZ3JvdW5kQ29sb3IgRGFya0dyYXkKICAgIFdyaXRlLUhvc3QgIiAgJChTcGxpdC1QYXRoICREaXNjb0JpblBhdGggLVBhcmVudCkiIC1Gb3JlZ3JvdW5kQ29sb3IgRGFya0dyYXkKICAgIFdyaXRlLUhvc3QgIkZhbGxpbmcgYmFjayB0byBtYW51YWwgbWFuaWZlc3QgZ2VuZXJhdGlvbi4uLiIgLUZvcmVncm91bmRDb2xvciBZZWxsb3cKfQoKaWYgKC1ub3QgJHVzZU1hbmlmZXN0R2VuKSB7CiAgICAkdmVyc2lvbiA9IFtTeXN0ZW0uUmVmbGVjdGlvbi5Bc3NlbWJseU5hbWVdOjpHZXRBc3NlbWJseU5hbWUoJHBsdWdpbkRsbCkuVmVyc2lvbgoKICAgICMgQ2F0ZWdvcnlUeXBlTmFtZSBtdXN0IG1hdGNoIHRoZSAuTkVUIGdlbmVyaWMgdHlwZSBkZWZpbml0aW9uIEZ1bGxOYW1lLgogICAgIyBGb3IgVUlFeHRlbnNpb25GZWF0dXJlPFQ+LCB0aGlzIGlzIFVJRXh0ZW5zaW9uRmVhdHVyZWAxIChvbmUgYmFja3RpY2ssIHRoZW4gMSkuCiAgICAjIEluIFBvd2VyU2hlbGwgZG91YmxlLXF1b3RlZCBzdHJpbmdzLCBgYCAodHdvIGJhY2t0aWNrcykgcHJvZHVjZXMgb25lIGxpdGVyYWwgYmFja3RpY2suCiAgICAkdWlFeHRDYXRlZ29yeVR5cGVOYW1lID0gIkRpc2NvLlNlcnZpY2VzLlBsdWdpbnMuRmVhdHVyZXMuVUlFeHRlbnNpb24uVUlFeHRlbnNpb25GZWF0dXJlYGAxIgoKICAgICRtYW5pZmVzdCA9IFtvcmRlcmVkXUB7CiAgICAgICAgSWQgPSAkUGx1Z2luTmFtZQogICAgICAgIE5hbWUgPSAiQUQgQ29tcGFyZSIKICAgICAgICBBdXRob3IgPSAiSmVzcyBSb2dlcnNvbiIKICAgICAgICBVcmwgPSAiaHR0cHM6Ly9naXRlYS5oaWRlYXdheWdhbWluZy5jb20uYXUvamVzc2lraXR0eS9kaXNjby1hZC1jb21wYXJlLXBsdWdpbiIKICAgICAgICBWZXJzaW9uID0gJHZlcnNpb24uVG9TdHJpbmcoKQogICAgICAgIEFzc2VtYmx5UGF0aCA9ICIkUGx1Z2luTmFtZS5kbGwiCiAgICAgICAgVHlwZU5hbWUgPSAiRGlzY28uUGx1Z2lucy5BRENvbXBhcmUuQURDb21wYXJlUGx1Z2luIgogICAgICAgIENvbmZpZ3VyYXRpb25IYW5kbGVyVHlwZU5hbWUgPSAiRGlzY28uUGx1Z2lucy5BRENvbXBhcmUuQ29uZmlndXJhdGlvbkhhbmRsZXIuQURDb21wYXJlQ29uZmlndXJhdGlvbkhhbmRsZXIiCiAgICAgICAgV2ViSGFuZGxlclR5cGVOYW1lID0gIkRpc2NvLlBsdWdpbnMuQURDb21wYXJlLldlYkhhbmRsZXIuQURDb21wYXJlV2ViSGFuZGxlciIKICAgICAgICBGZWF0dXJlcyA9IEAoCiAgICAgICAgICAgIFtvcmRlcmVkXUB7CiAgICAgICAgICAgICAgICBJZCA9ICJBRENvbXBhcmVEZXZpY2VVSSIKICAgICAgICAgICAgICAgIE5hbWUgPSAiRGV2aWNlIFBhZ2UgLSBBRCBDb21wYXJlIgogICAgICAgICAgICAgICAgVHlwZU5hbWUgPSAiRGlzY28uUGx1Z2lucy5BRENvbXBhcmUuRmVhdHVyZXMuRGV2aWNlVUlFeHRlbnNpb24iCiAgICAgICAgICAgICAgICBQcmltYXJ5RmVhdHVyZSA9ICRmYWxzZQogICAgICAgICAgICAgICAgQ2F0ZWdvcnlUeXBlTmFtZSA9ICR1aUV4dENhdGVnb3J5VHlwZU5hbWUKICAgICAgICAgICAgfSwKICAgICAgICAgICAgW29yZGVyZWRdQHsKICAgICAgICAgICAgICAgIElkID0gIkFEQ29tcGFyZVVzZXJVSSIKICAgICAgICAgICAgICAgIE5hbWUgPSAiVXNlciBQYWdlIC0gQUQgQ29tcGFyZSIKICAgICAgICAgICAgICAgIFR5cGVOYW1lID0gIkRpc2NvLlBsdWdpbnMuQURDb21wYXJlLkZlYXR1cmVzLlVzZXJVSUV4dGVuc2lvbiIKICAgICAgICAgICAgICAgIFByaW1hcnlGZWF0dXJlID0gJGZhbHNlCiAgICAgICAgICAgICAgICBDYXRlZ29yeVR5cGVOYW1lID0gJHVpRXh0Q2F0ZWdvcnlUeXBlTmFtZQogICAgICAgICAgICB9CiAgICAgICAgKQogICAgfQogICAgJG1hbmlmZXN0SnNvbiA9ICRtYW5pZmVzdCB8IENvbnZlcnRUby1Kc29uIC1EZXB0aCA1CiAgICAkbWFuaWZlc3RKc29uIHwgU2V0LUNvbnRlbnQgKEpvaW4tUGF0aCAkYnVpbGRPdXRwdXQgIm1hbmlmZXN0Lmpzb24iKSAtRW5jb2RpbmcgVVRGOAogICAgV3JpdGUtSG9zdCAiTWFudWFsIG1hbmlmZXN0Lmpzb24gY3JlYXRlZCIgLUZvcmVncm91bmRDb2xvciBHcmVlbgp9CgojIC0tLSBWYWxpZGF0ZSBNYW5pZmVzdCAtLS0KV3JpdGUtSG9zdCAiYG4tLS0gTWFuaWZlc3QgVmFsaWRhdGlvbiAtLS0iIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CgokbWFuaWZlc3RQYXRoID0gSm9pbi1QYXRoICRidWlsZE91dHB1dCAibWFuaWZlc3QuanNvbiIKaWYgKC1ub3QgKFRlc3QtUGF0aCAkbWFuaWZlc3RQYXRoKSkgewogICAgV3JpdGUtRXJyb3IgIm1hbmlmZXN0Lmpzb24gTk9UIEZPVU5EIGluIGJ1aWxkIG91dHB1dCEgUGx1Z2luIGNhbm5vdCBiZSBsb2FkZWQgYnkgRGlzY28uIgogICAgZXhpdCAxCn0KCiRtYW5pZmVzdENvbnRlbnQgPSBHZXQtQ29udGVudCAkbWFuaWZlc3RQYXRoIC1SYXcKJG1hbmlmZXN0T2JqID0gJG1hbmlmZXN0Q29udGVudCB8IENvbnZlcnRGcm9tLUpzb24KCiMgQmFzaWMgc3RydWN0dXJlIGNoZWNrcwokbWFuaWZlc3RFcnJvcnMgPSBAKCkKJG1hbmlmZXN0V2FybmluZ3MgPSBAKCkKCmlmICgtbm90ICRtYW5pZmVzdE9iai5JZCkgeyAkbWFuaWZlc3RFcnJvcnMgKz0gIk1pc3NpbmcgJ0lkJyIgfQppZiAoLW5vdCAkbWFuaWZlc3RPYmouVHlwZU5hbWUpIHsgJG1hbmlmZXN0RXJyb3JzICs9ICJNaXNzaW5nICdUeXBlTmFtZSciIH0KaWYgKC1ub3QgJG1hbmlmZXN0T2JqLkFzc2VtYmx5UGF0aCkgeyAkbWFuaWZlc3RFcnJvcnMgKz0gIk1pc3NpbmcgJ0Fzc2VtYmx5UGF0aCciIH0KaWYgKC1ub3QgJG1hbmlmZXN0T2JqLlZlcnNpb24pIHsgJG1hbmlmZXN0V2FybmluZ3MgKz0gIk1pc3NpbmcgJ1ZlcnNpb24nIiB9CgpXcml0ZS1Ib3N0ICIgIElkOiAgICAgICAkKCRtYW5pZmVzdE9iai5JZCkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQpXcml0ZS1Ib3N0ICIgIFZlcnNpb246ICAkKCRtYW5pZmVzdE9iai5WZXJzaW9uKSIgLUZvcmVncm91bmRDb2xvciBHcmF5CldyaXRlLUhvc3QgIiAgVHlwZU5hbWU6ICQoJG1hbmlmZXN0T2JqLlR5cGVOYW1lKSIgLUZvcmVncm91bmRDb2xvciBHcmF5CgppZiAoLW5vdCAkbWFuaWZlc3RPYmouRmVhdHVyZXMgLW9yICRtYW5pZmVzdE9iai5GZWF0dXJlcy5Db3VudCAtZXEgMCkgewogICAgJG1hbmlmZXN0RXJyb3JzICs9ICJGZWF0dXJlcyBhcnJheSBpcyBFTVBUWSBvciBNSVNTSU5HIC0gbm8gVUlFeHRlbnNpb25zIHdpbGwgYmUgbG9hZGVkISIKfSBlbHNlIHsKICAgIFdyaXRlLUhvc3QgIiAgRmVhdHVyZXM6ICQoJG1hbmlmZXN0T2JqLkZlYXR1cmVzLkNvdW50KSBmb3VuZCIgLUZvcmVncm91bmRDb2xvciBHcmVlbgoKICAgICMgRXhwZWN0ZWQgQ2F0ZWdvcnlUeXBlTmFtZSAod2l0aCBsaXRlcmFsIGJhY2t0aWNrKQogICAgJGV4cGVjdGVkQ2F0ZWdvcnkgPSAnRGlzY28uU2VydmljZXMuUGx1Z2lucy5GZWF0dXJlcy5VSUV4dGVuc2lvbi5VSUV4dGVuc2lvbkZlYXR1cmVgMScKCiAgICBmb3JlYWNoICgkZmVhdHVyZSBpbiAkbWFuaWZlc3RPYmouRmVhdHVyZXMpIHsKICAgICAgICBXcml0ZS1Ib3N0ICJgbiAgRmVhdHVyZTogJCgkZmVhdHVyZS5JZCkiIC1Gb3JlZ3JvdW5kQ29sb3IgQ3lhbgogICAgICAgIFdyaXRlLUhvc3QgIiAgICBOYW1lOiAgICAgICAgICAgICAkKCRmZWF0dXJlLk5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICBXcml0ZS1Ib3N0ICIgICAgVHlwZU5hbWU6ICAgICAgICAgJCgkZmVhdHVyZS5UeXBlTmFtZSkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgICAgIFdyaXRlLUhvc3QgIiAgICBDYXRlZ29yeVR5cGVOYW1lOiAkKCRmZWF0dXJlLkNhdGVnb3J5VHlwZU5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIEdyYXkKICAgICAgICBXcml0ZS1Ib3N0ICIgICAgUHJpbWFyeUZlYXR1cmU6ICAgJCgkZmVhdHVyZS5QcmltYXJ5RmVhdHVyZSkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQoKICAgICAgICBpZiAoLW5vdCAkZmVhdHVyZS5UeXBlTmFtZSkgewogICAgICAgICAgICAkbWFuaWZlc3RFcnJvcnMgKz0gIkZlYXR1cmUgJyQoJGZlYXR1cmUuSWQpJyBpcyBtaXNzaW5nIFR5cGVOYW1lIgogICAgICAgIH0KCiAgICAgICAgaWYgKC1ub3QgJGZlYXR1cmUuQ2F0ZWdvcnlUeXBlTmFtZSkgewogICAgICAgICAgICAkbWFuaWZlc3RFcnJvcnMgKz0gIkZlYXR1cmUgJyQoJGZlYXR1cmUuSWQpJyBpcyBtaXNzaW5nIENhdGVnb3J5VHlwZU5hbWUiCiAgICAgICAgfSBlbHNlaWYgKCRmZWF0dXJlLkNhdGVnb3J5VHlwZU5hbWUgLW5lICRleHBlY3RlZENhdGVnb3J5KSB7CiAgICAgICAgICAgICRtYW5pZmVzdEVycm9ycyArPSAiRmVhdHVyZSAnJCgkZmVhdHVyZS5JZCknIENhdGVnb3J5VHlwZU5hbWUgTUlTTUFUQ0ghIgogICAgICAgICAgICBXcml0ZS1Ib3N0ICIgICAgICBFeHBlY3RlZDogJGV4cGVjdGVkQ2F0ZWdvcnkiIC1Gb3JlZ3JvdW5kQ29sb3IgUmVkCiAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICAgIEdvdDogICAgICAkKCRmZWF0dXJlLkNhdGVnb3J5VHlwZU5hbWUpIiAtRm9yZWdyb3VuZENvbG9yIFJlZAoKICAgICAgICAgICAgIyBDaGFyLWJ5LWNoYXIgY29tcGFyaXNvbiB0byBpZGVudGlmeSB0aGUgZXhhY3QgZGlmZmVyZW5jZQogICAgICAgICAgICAkZXhwQ2hhcnMgPSAkZXhwZWN0ZWRDYXRlZ29yeS5Ub0NoYXJBcnJheSgpCiAgICAgICAgICAgICRhY3RDaGFycyA9ICRmZWF0dXJlLkNhdGVnb3J5VHlwZU5hbWUuVG9DaGFyQXJyYXkoKQogICAgICAgICAgICBmb3IgKCRpID0gMDsgJGkgLWx0IFtNYXRoXTo6TWF4KCRleHBDaGFycy5MZW5ndGgsICRhY3RDaGFycy5MZW5ndGgpOyAkaSsrKSB7CiAgICAgICAgICAgICAgICAkZWMgPSBpZiAoJGkgLWx0ICRleHBDaGFycy5MZW5ndGgpIHsgJGV4cENoYXJzWyRpXSB9IGVsc2UgeyAkbnVsbCB9CiAgICAgICAgICAgICAgICAkYWMgPSBpZiAoJGkgLWx0ICRhY3RDaGFycy5MZW5ndGgpIHsgJGFjdENoYXJzWyRpXSB9IGVsc2UgeyAkbnVsbCB9CiAgICAgICAgICAgICAgICBpZiAoJGVjIC1uZSAkYWMpIHsKICAgICAgICAgICAgICAgICAgICAkZWNEaXNwbGF5ID0gaWYgKCRlYykgeyAiJyRlYycgKDB4ezA6WDJ9KSIgLWYgW2ludF1bY2hhcl0kZWMgfSBlbHNlIHsgJ1tFTkRdJyB9CiAgICAgICAgICAgICAgICAgICAgJGFjRGlzcGxheSA9IGlmICgkYWMpIHsgIickYWMnICgweHswOlgyfSkiIC1mIFtpbnRdW2NoYXJdJGFjIH0gZWxzZSB7ICdbRU5EXScgfQogICAgICAgICAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgICAgIEZpcnN0IGRpZmZlcmVuY2UgYXQgcG9zaXRpb24gJHtpfTogZXhwZWN0ZWQgJGVjRGlzcGxheSB2cyBnb3QgJGFjRGlzcGxheSIgLUZvcmVncm91bmRDb2xvciBSZWQKICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgV3JpdGUtSG9zdCAiICAgIENhdGVnb3J5VHlwZU5hbWU6IFZBTElEIiAtRm9yZWdyb3VuZENvbG9yIEdyZWVuCiAgICAgICAgfQogICAgfQp9CgojIFNob3cgcmF3IG1hbmlmZXN0IGZvciBtYW51YWwgaW5zcGVjdGlvbgpXcml0ZS1Ib3N0ICJgbiAgLS0tIFJhdyBtYW5pZmVzdC5qc29uIC0tLSIgLUZvcmVncm91bmRDb2xvciBEYXJrR3JheQokbWFuaWZlc3RDb250ZW50IC1zcGxpdCAiYG4iIHwgRm9yRWFjaC1PYmplY3QgewogICAgV3JpdGUtSG9zdCAiICAkXyIgLUZvcmVncm91bmRDb2xvciBEYXJrR3JheQp9CldyaXRlLUhvc3QgIiAgLS0tIEVuZCBtYW5pZmVzdC5qc29uIC0tLSIgLUZvcmVncm91bmRDb2xvciBEYXJrR3JheQoKaWYgKCRtYW5pZmVzdEVycm9ycy5Db3VudCAtZ3QgMCkgewogICAgV3JpdGUtSG9zdCAiYG4gIE1BTklGRVNUIEVSUk9SUzoiIC1Gb3JlZ3JvdW5kQ29sb3IgUmVkCiAgICBmb3JlYWNoICgkZXJyIGluICRtYW5pZmVzdEVycm9ycykgewogICAgICAgIFdyaXRlLUhvc3QgIiAgICBbRVJST1JdICRlcnIiIC1Gb3JlZ3JvdW5kQ29sb3IgUmVkCiAgICB9Cn0KaWYgKCRtYW5pZmVzdFdhcm5pbmdzLkNvdW50IC1ndCAwKSB7CiAgICBmb3JlYWNoICgkd2FybiBpbiAkbWFuaWZlc3RXYXJuaW5ncykgewogICAgICAgIFdyaXRlLUhvc3QgIiAgICBbV0FSTl0gICR3YXJuIiAtRm9yZWdyb3VuZENvbG9yIFllbGxvdwogICAgfQp9CmlmICgkbWFuaWZlc3RFcnJvcnMuQ291bnQgLWVxIDApIHsKICAgIFdyaXRlLUhvc3QgImBuICBBbGwgbWFuaWZlc3QgY2hlY2tzIFBBU1NFRCIgLUZvcmVncm91bmRDb2xvciBHcmVlbgp9IGVsc2UgewogICAgV3JpdGUtSG9zdCAiYG4gIE1hbmlmZXN0IGhhcyBlcnJvcnMgLSBVSUV4dGVuc2lvbnMgd2lsbCBOT1QgcmVuZGVyISIgLUZvcmVncm91bmRDb2xvciBSZWQKICAgIFdyaXRlLUhvc3QgIiAgRml4IHRoZSBlcnJvcnMgYWJvdmUgYmVmb3JlIGluc3RhbGxpbmcgdGhlIHBsdWdpbi4iIC1Gb3JlZ3JvdW5kQ29sb3IgUmVkCn0KCiMgLS0tIFBhY2thZ2UgLS0tCldyaXRlLUhvc3QgImBuUGFja2FnaW5nIC5kaXNjb1BsdWdpbiBmaWxlLi4uIiAtRm9yZWdyb3VuZENvbG9yIFllbGxvdwoKJGluY2x1ZGVGaWxlcyA9IEAoIiRQbHVnaW5OYW1lLmRsbCIsICIkUGx1Z2luTmFtZS5wZGIiLCAibWFuaWZlc3QuanNvbiIpCgppZiAoLW5vdCAkdmVyc2lvbikgewogICAgJHZlcnNpb24gPSBbU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHlOYW1lXTo6R2V0QXNzZW1ibHlOYW1lKCRwbHVnaW5EbGwpLlZlcnNpb24KfQoKJHBhY2thZ2VOYW1lID0gIiRQbHVnaW5OYW1lLSQoJHZlcnNpb24uVG9TdHJpbmcoKSkuZGlzY29QbHVnaW4iCiRwYWNrYWdlUGF0aCA9IEpvaW4tUGF0aCAkUGx1Z2luRGlyICRwYWNrYWdlTmFtZQoKR2V0LUNoaWxkSXRlbSAkUGx1Z2luRGlyIC1GaWx0ZXIgIiouZGlzY29QbHVnaW4iIHwgUmVtb3ZlLUl0ZW0gLUZvcmNlCkdldC1DaGlsZEl0ZW0gJFBsdWdpbkRpciAtRmlsdGVyICIqLnppcCIgfCBXaGVyZS1PYmplY3QgeyAkXy5OYW1lIC1saWtlICIkUGx1Z2luTmFtZSoiIH0gfCBSZW1vdmUtSXRlbSAtRm9yY2UKCiR0ZW1wUGtnID0gSm9pbi1QYXRoICRlbnY6VEVNUCAiZGlzY29wbHVnaW5fJChbZ3VpZF06Ok5ld0d1aWQoKS5Ub1N0cmluZygnTicpKSIKTmV3LUl0ZW0gLUl0ZW1UeXBlIERpcmVjdG9yeSAtUGF0aCAkdGVtcFBrZyAtRm9yY2UgfCBPdXQtTnVsbAokbWlzc2luZ0ZpbGVzID0gQCgpCmZvcmVhY2ggKCRmIGluICRpbmNsdWRlRmlsZXMpIHsKICAgICRzcmMgPSBKb2luLVBhdGggJGJ1aWxkT3V0cHV0ICRmCiAgICBpZiAoVGVzdC1QYXRoICRzcmMpIHsKICAgICAgICBDb3B5LUl0ZW0gJHNyYyAkdGVtcFBrZyAtRm9yY2UKICAgIH0gZWxzZSB7CiAgICAgICAgJG1pc3NpbmdGaWxlcyArPSAkZgogICAgfQp9CgppZiAoJG1pc3NpbmdGaWxlcy5Db3VudCAtZ3QgMCkgewogICAgV3JpdGUtV2FybmluZyAiTWlzc2luZyBmaWxlcyBub3QgaW5jbHVkZWQgaW4gcGFja2FnZTogJCgkbWlzc2luZ0ZpbGVzIC1qb2luICcsICcpIgp9CgpXcml0ZS1Ib3N0ICJGaWxlcyBpbiBwYWNrYWdlOiIgLUZvcmVncm91bmRDb2xvciBHcmF5CkdldC1DaGlsZEl0ZW0gJHRlbXBQa2cgLUZpbGUgfCBGb3JFYWNoLU9iamVjdCB7CiAgICAkc2l6ZSA9IFttYXRoXTo6Um91bmQoJF8uTGVuZ3RoIC8gMUtCLCAxKQogICAgV3JpdGUtSG9zdCAiICAkKCRfLk5hbWUpICgkc2l6ZSBLQikiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQp9CgokemlwUGF0aCA9IEpvaW4tUGF0aCAkUGx1Z2luRGlyICIkUGx1Z2luTmFtZS0kKCR2ZXJzaW9uLlRvU3RyaW5nKCkpLnppcCIKQ29tcHJlc3MtQXJjaGl2ZSAtUGF0aCAiJHRlbXBQa2dcKiIgLURlc3RpbmF0aW9uUGF0aCAkemlwUGF0aCAtRm9yY2UKUmVuYW1lLUl0ZW0gJHppcFBhdGggJHBhY2thZ2VOYW1lIC1Gb3JjZQpSZW1vdmUtSXRlbSAkdGVtcFBrZyAtUmVjdXJzZSAtRm9yY2UKCiMgLS0tIEZpbmFsIFBhY2thZ2UgVmVyaWZpY2F0aW9uIC0tLQpXcml0ZS1Ib3N0ICJgbi0tLSBQYWNrYWdlIFZlcmlmaWNhdGlvbiAtLS0iIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CnRyeSB7CiAgICBBZGQtVHlwZSAtQXNzZW1ibHlOYW1lIFN5c3RlbS5JTy5Db21wcmVzc2lvbi5GaWxlU3lzdGVtCiAgICAkemlwID0gW1N5c3RlbS5JTy5Db21wcmVzc2lvbi5aaXBGaWxlXTo6T3BlblJlYWQoJHBhY2thZ2VQYXRoKQogICAgJGVudHJpZXMgPSAkemlwLkVudHJpZXMgfCBTZWxlY3QtT2JqZWN0IC1FeHBhbmRQcm9wZXJ0eSBOYW1lCiAgICAkemlwLkRpc3Bvc2UoKQoKICAgICRyZXF1aXJlZEVudHJpZXMgPSBAKCIkUGx1Z2luTmFtZS5kbGwiLCAibWFuaWZlc3QuanNvbiIpCiAgICBmb3JlYWNoICgkcmVxIGluICRyZXF1aXJlZEVudHJpZXMpIHsKICAgICAgICBpZiAoJGVudHJpZXMgLWNvbnRhaW5zICRyZXEpIHsKICAgICAgICAgICAgV3JpdGUtSG9zdCAiICBbT0tdICRyZXEiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JlZW4KICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBXcml0ZS1Ib3N0ICIgIFtNSVNTSU5HXSAkcmVxIiAtRm9yZWdyb3VuZENvbG9yIFJlZAogICAgICAgIH0KICAgIH0KICAgICMgU2hvdyBhbnkgZXh0cmFzCiAgICBmb3JlYWNoICgkZW50cnkgaW4gJGVudHJpZXMpIHsKICAgICAgICBpZiAoJGVudHJ5IC1ub3RpbiAkcmVxdWlyZWRFbnRyaWVzKSB7CiAgICAgICAgICAgIFdyaXRlLUhvc3QgIiAgWytdICAkZW50cnkiIC1Gb3JlZ3JvdW5kQ29sb3IgR3JheQogICAgICAgIH0KICAgIH0KfSBjYXRjaCB7CiAgICBXcml0ZS1XYXJuaW5nICIgIENvdWxkIG5vdCB2ZXJpZnkgcGFja2FnZSBjb250ZW50czogJCgkXy5FeGNlcHRpb24uTWVzc2FnZSkiCn0KCiRwYWNrYWdlU2l6ZSA9IFttYXRoXTo6Um91bmQoKEdldC1JdGVtICRwYWNrYWdlUGF0aCkuTGVuZ3RoIC8gMUtCLCAxKQpXcml0ZS1Ib3N0ICJgbj09PSBQYWNrYWdlIGNyZWF0ZWQ6ICRwYWNrYWdlUGF0aCAoJCgkcGFja2FnZVNpemUpIEtCKSA9PT0iIC1Gb3JlZ3JvdW5kQ29sb3IgR3JlZW4KV3JpdGUtSG9zdCAiSW1wb3J0IGludG8gRGlzY28gSUNUIHZpYTogQ29uZmlndXJhdGlvbiA+IFBsdWdpbnMgPiBJbnN0YWxsIFBsdWdpbiIgLUZvcmVncm91bmRDb2xvciBDeWFuCgojIC0tLSBTdW1tYXJ5IC0tLQokaGFzSXNzdWVzID0gKC1ub3QgJHJlZmxlY3Rpb25PaykgLW9yICgkbWFuaWZlc3RFcnJvcnMuQ291bnQgLWd0IDApCmlmICgkaGFzSXNzdWVzKSB7CiAgICBXcml0ZS1Ib3N0ICJgbj09PSBCVUlMRCBDT01QTEVURUQgV0lUSCBXQVJOSU5HUyA9PT0iIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CiAgICBXcml0ZS1Ib3N0ICJSZXZpZXcgdGhlIGRpYWdub3N0aWNzIGFib3ZlIGJlZm9yZSBpbnN0YWxsaW5nLiIgLUZvcmVncm91bmRDb2xvciBZZWxsb3cKICAgIFdyaXRlLUhvc3QgIklmIFVJRXh0ZW5zaW9ucyBzdGlsbCBkb24ndCByZW5kZXIgYWZ0ZXIgaW5zdGFsbDoiIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CiAgICBXcml0ZS1Ib3N0ICIgIDEuIENoZWNrIERpc2NvIGxvZ3MgZm9yIGZlYXR1cmUgaW5pdGlhbGl6YXRpb24gbWVzc2FnZXMiIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CiAgICBXcml0ZS1Ib3N0ICIgIDIuIFZpZXcgcGFnZSBzb3VyY2UgYW5kIHNlYXJjaCBmb3IgJ2xheW91dF91aUV4dGVuc2lvbnMnIiAtRm9yZWdyb3VuZENvbG9yIFllbGxvdwogICAgV3JpdGUtSG9zdCAiICAgICAtIFByZXNlbnQgYnV0IGVtcHR5ID0gRXhlY3V0ZUFjdGlvbiByZXR1cm5lZCBOb3RoaW5nKCkvbnVsbCIgLUZvcmVncm91bmRDb2xvciBZZWxsb3cKICAgIFdyaXRlLUhvc3QgIiAgICAgLSBNaXNzaW5nIGVudGlyZWx5ICA9IEZlYXR1cmUgbm90IHJlZ2lzdGVyZWQgKFJlZ2lzdGVyKCkgbm90IGNhbGxlZCkiIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93CiAgICBXcml0ZS1Ib3N0ICIgIDMuIENoZWNrIHRoZSBpbnN0YWxsZWQgbWFuaWZlc3QgYXQ6IiAtRm9yZWdyb3VuZENvbG9yIFllbGxvdwogICAgV3JpdGUtSG9zdCAiICAgICBBcHBfRGF0YVxQbHVnaW5zXCRQbHVnaW5OYW1lXG1hbmlmZXN0Lmpzb24iIC1Gb3JlZ3JvdW5kQ29sb3IgWWVsbG93Cn0gZWxzZSB7CiAgICBXcml0ZS1Ib3N0ICJgbj09PSBCVUlMRCBDT01QTEVURUQgU1VDQ0VTU0ZVTExZID09PSIgLUZvcmVncm91bmRDb2xvciBHcmVlbgp9Cg== \ No newline at end of file