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