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