mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
538 lines
22 KiB
PowerShell
538 lines
22 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
KeePassXC Release Tool
|
|
|
|
.DESCRIPTION
|
|
Commands:
|
|
merge Merge release branch into main branch and create release tags
|
|
build Build and package binary release from sources
|
|
sign Sign previously compiled release packages
|
|
|
|
.NOTES
|
|
The following are descriptions of certain parameters:
|
|
-Vcpkg Specify VCPKG toolchain file (example: C:\vcpkg\scripts\buildsystems\vcpkg.cmake)
|
|
-Tag Release tag to check out (defaults to version number)
|
|
-Snapshot Build current HEAD without checkout out Tag
|
|
-CMakeGenerator Override the default CMake generator
|
|
-CMakeOptions Additional CMake options for compiling the sources
|
|
-CPackGenerators Set CPack generators (default: WIX;ZIP)
|
|
-Compiler Compiler to use (example: g++, clang, msbuild)
|
|
-MakeOptions Options to pass to the make program
|
|
-SignBuild Perform platform specific App Signing before packaging
|
|
-SignKey Specify the App Signing Key/Identity
|
|
-TimeStamp Explicitly set the timestamp server to use for appsign
|
|
-SourceBranch Source branch to merge from (default: 'release/$Version')
|
|
-TargetBranch Target branch to merge to (default: master)
|
|
-VSToolChain Specify Visual Studio Toolchain by name if more than one is available
|
|
#>
|
|
|
|
param(
|
|
[Parameter(ParameterSetName = "merge", Mandatory, Position = 0)]
|
|
[switch] $Merge,
|
|
[Parameter(ParameterSetName = "build", Mandatory, Position = 0)]
|
|
[switch] $Build,
|
|
[Parameter(ParameterSetName = "sign", Mandatory, Position = 0)]
|
|
[switch] $Sign,
|
|
|
|
[Parameter(ParameterSetName = "merge", Mandatory, Position = 1)]
|
|
[Parameter(ParameterSetName = "build", Mandatory, Position = 1)]
|
|
[Parameter(ParameterSetName = "sign", Mandatory, Position = 1)]
|
|
[string] $Version,
|
|
|
|
[Parameter(ParameterSetName = "build", Mandatory)]
|
|
[string] $Vcpkg,
|
|
|
|
[Parameter(ParameterSetName = "sign", Mandatory)]
|
|
[SupportsWildcards()]
|
|
[string[]] $SignFiles,
|
|
|
|
# [Parameter(ParameterSetName = "build")]
|
|
# [switch] $DryRun,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[switch] $Snapshot,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[switch] $SignBuild,
|
|
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $CMakeGenerator = "Ninja",
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $CMakeOptions,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $CPackGenerators = "WIX;ZIP",
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $Compiler,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $MakeOptions,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[Parameter(ParameterSetName = "sign")]
|
|
[string] $SignKey,
|
|
[Parameter(ParameterSetName = "build")]
|
|
[Parameter(ParameterSetName = "sign")]
|
|
[string] $Timestamp = "http://timestamp.sectigo.com",
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[Parameter(ParameterSetName = "build")]
|
|
[Parameter(ParameterSetName = "sign")]
|
|
[string] $GpgKey = "CFB4C2166397D0D2",
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $SourceDir = ".",
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $OutDir = ".\release",
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $Tag,
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[string] $SourceBranch,
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[string] $TargetBranch = "master",
|
|
[Parameter(ParameterSetName = "build")]
|
|
[string] $VSToolChain,
|
|
[Parameter(ParameterSetName = "merge")]
|
|
[Parameter(ParameterSetName = "build")]
|
|
[Parameter(ParameterSetName = "sign")]
|
|
[string] $ExtraPath
|
|
)
|
|
|
|
# Helper function definitions
|
|
function Test-RequiredPrograms {
|
|
# If any of these fail they will throw an exception terminating the script
|
|
if ($Build) {
|
|
Get-Command git | Out-Null
|
|
Get-Command cmake | Out-Null
|
|
}
|
|
if ($Merge) {
|
|
Get-Command git | Out-Null
|
|
Get-Command tx | Out-Null
|
|
Get-Command lupdate | Out-Null
|
|
}
|
|
if ($Sign -or $SignBuild) {
|
|
if ($SignKey.Length) {
|
|
Get-Command signtool | Out-Null
|
|
}
|
|
Get-Command gpg | Out-Null
|
|
}
|
|
}
|
|
|
|
function Test-VersionInFiles {
|
|
# Check CMakeLists.txt
|
|
$Major, $Minor, $Patch = $Version.split(".", 3)
|
|
if (!(Select-String "$SourceDir\CMakeLists.txt" -pattern "KEEPASSXC_VERSION_MAJOR `"$Major`"" -Quiet) `
|
|
-or !(Select-String "$SourceDir\CMakeLists.txt" -pattern "KEEPASSXC_VERSION_MINOR `"$Minor`"" -Quiet) `
|
|
-or !(Select-String "$SourceDir\CMakeLists.txt" -pattern "KEEPASSXC_VERSION_PATCH `"$Patch`"" -Quiet)) {
|
|
throw "CMakeLists.txt has not been updated to $Version."
|
|
}
|
|
|
|
# Check Changelog
|
|
if (!(Select-String "$SourceDir\CHANGELOG.md" -pattern "^## $Version \(\d{4}-\d{2}-\d{2}\)$" -Quiet)) {
|
|
throw "CHANGELOG.md does not contain a section for $Version."
|
|
}
|
|
|
|
# Check AppStreamInfo
|
|
if (!(Select-String "$SourceDir\share\linux\org.keepassxc.KeePassXC.appdata.xml" `
|
|
-pattern "<release version=`"$Version`" date=`"\d{4}-\d{2}-\d{2}`">" -Quiet)) {
|
|
throw "share/linux/org.keepassxc.KeePassXC.appdata.xml does not contain a section for $Version."
|
|
}
|
|
}
|
|
|
|
function Test-WorkingTreeClean {
|
|
& git diff-index --quiet HEAD --
|
|
if ($LASTEXITCODE) {
|
|
throw "Current working tree is not clean! Please commit or unstage any changes."
|
|
}
|
|
}
|
|
|
|
function Invoke-VSToolchain([String] $Toolchain, [String] $Path, [String] $Arch) {
|
|
# Find Visual Studio installations
|
|
$vs = Get-CimInstance MSFT_VSInstance
|
|
if ($vs.count -eq 0) {
|
|
$err = "No Visual Studio installations found, download one from https://visualstudio.com/downloads."
|
|
$err = "$err`nIf Visual Studio is installed, you may need to repair the install then restart."
|
|
throw $err
|
|
}
|
|
|
|
$VSBaseDir = $vs[0].InstallLocation
|
|
if ($Toolchain) {
|
|
# Try to find the specified toolchain by name
|
|
foreach ($_ in $vs) {
|
|
if ($_.Name -eq $Toolchain) {
|
|
$VSBaseDir = $_.InstallLocation
|
|
break
|
|
}
|
|
}
|
|
} elseif ($vs.count -gt 1) {
|
|
# Ask the user which install to use
|
|
$i = 0
|
|
foreach ($_ in $vs) {
|
|
$i = $i + 1
|
|
$i.ToString() + ") " + $_.Name | Write-Host
|
|
}
|
|
$i = Read-Host -Prompt "Which Visual Studio installation do you want to use?"
|
|
$i = [Convert]::ToInt32($i, 10) - 1
|
|
if ($i -lt 0 -or $i -ge $vs.count) {
|
|
throw "Invalid selection made"
|
|
}
|
|
$VSBaseDir = $vs[$i].InstallLocation
|
|
}
|
|
|
|
# Bootstrap the specified VS Toolchain
|
|
Import-Module "$VSBaseDir\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
|
Enter-VsDevShell -VsInstallPath $VSBaseDir -Arch $Arch -StartInPath $Path | Write-Host
|
|
Write-Host # Newline after command output
|
|
}
|
|
|
|
function Invoke-Cmd([string] $command, [string[]] $options = @(), [switch] $maskargs, [switch] $quiet) {
|
|
$call = ('{0} {1}' -f $command, ($options -Join ' '))
|
|
if ($maskargs) {
|
|
Write-Host "$command <masked>" -ForegroundColor DarkGray
|
|
}
|
|
else {
|
|
Write-Host $call -ForegroundColor DarkGray
|
|
}
|
|
if ($quiet) {
|
|
Invoke-Expression $call > $null
|
|
} else {
|
|
Invoke-Expression $call
|
|
}
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to run command: {0}" -f $command
|
|
}
|
|
Write-Host #insert newline after command output
|
|
}
|
|
|
|
function Invoke-SignFiles([string[]] $files, [string] $key, [string] $time) {
|
|
if (!(Test-Path -Path "$key" -PathType leaf)) {
|
|
throw "Appsign key file was not found! ($key)"
|
|
}
|
|
if ($files.Length -eq 0) {
|
|
return
|
|
}
|
|
|
|
Write-Host "Signing files using $key" -ForegroundColor Cyan
|
|
$KeyPassword = Read-Host "Key password: " -MaskInput
|
|
|
|
foreach ($_ in $files) {
|
|
Write-Host "Signing file '$_' using Microsoft signtool..."
|
|
Invoke-Cmd "signtool" "sign -f `"$key`" -p `"$KeyPassword`" -d `"KeePassXC`" -td sha256 -fd sha256 -tr `"$time`" `"$_`"" -maskargs
|
|
}
|
|
}
|
|
|
|
function Invoke-GpgSignFiles([string[]] $files, [string] $key) {
|
|
if ($files.Length -eq 0) {
|
|
return
|
|
}
|
|
|
|
Write-Host "Signing files using GPG key $key" -ForegroundColor Cyan
|
|
|
|
foreach ($_ in $files) {
|
|
Write-Host "Signing file '$_' and creating DIGEST..."
|
|
if (Test-Path "$_.sig") {
|
|
Remove-Item "$_.sig"
|
|
}
|
|
Invoke-Cmd "gpg" "--output `"$_.sig`" --armor --local-user `"$key`" --detach-sig `"$_`""
|
|
$FileName = (Get-Item $_).Name
|
|
(Get-FileHash "$_" SHA256).Hash + " *$FileName" | Out-File "$_.DIGEST" -NoNewline
|
|
}
|
|
}
|
|
|
|
|
|
# Handle errors and restore state
|
|
$OrigDir = (Get-Location).Path
|
|
$OrigBranch = & git rev-parse --abbrev-ref HEAD
|
|
$ErrorActionPreference = 'Stop'
|
|
trap {
|
|
Write-Host "Restoring state..." -ForegroundColor Yellow
|
|
& git checkout $OrigBranch
|
|
Set-Location "$OrigDir"
|
|
}
|
|
|
|
Write-Host "KeePassXC Release Preparation Helper" -ForegroundColor Green
|
|
Write-Host "Copyright (C) 2022 KeePassXC Team <https://keepassxc.org/>`n" -ForegroundColor Green
|
|
|
|
# Prepend extra PATH locations as specified
|
|
if ($ExtraPath) {
|
|
$env:Path = "$ExtraPath;$env:Path"
|
|
}
|
|
|
|
# Resolve absolute directory for paths
|
|
$SourceDir = (Resolve-Path $SourceDir).Path
|
|
|
|
# Check format of -Version
|
|
if ($Version -notmatch "^\d+\.\d+\.\d+(-Beta\d*)?$") {
|
|
throw "Invalid format for -Version input"
|
|
}
|
|
|
|
# Check platform
|
|
if (!$IsWindows) {
|
|
throw "The PowerShell release tool is not available for Linux or macOS at this time."
|
|
}
|
|
|
|
if ($Merge) {
|
|
Test-RequiredPrograms
|
|
|
|
# Change to SourceDir
|
|
Set-Location "$SourceDir"
|
|
|
|
Test-VersionInFiles
|
|
Test-WorkingTreeClean
|
|
|
|
if (!$SourceBranch.Length) {
|
|
$SourceBranch = & git branch --show-current
|
|
}
|
|
|
|
if ($SourceBranch -notmatch "^release/.*|develop$") {
|
|
throw "Must be on develop or a release/* branch to continue merging."
|
|
}
|
|
|
|
# Update translation files
|
|
Write-Host "Updating source translation file..."
|
|
Invoke-Cmd "lupdate" "-no-ui-lines -disable-heuristic similartext -locations none", `
|
|
"-no-obsolete ./src -ts share/translations/keepassxc_en.ts"
|
|
|
|
Write-Host "Pulling updated translations from Transifex..."
|
|
Invoke-Cmd "tx" "pull -af --minimum-perc=60 --parallel -r keepassxc.share-translations-keepassxc-en-ts--develop"
|
|
|
|
# Only commit if there are changes
|
|
& git diff-index --quiet HEAD --
|
|
if ($LASTEXITCODE) {
|
|
Write-Host "Committing translation updates..."
|
|
Invoke-Cmd "git" "add -A ./share/translations/" -quiet
|
|
Invoke-Cmd "git" "commit -m `"Update translations`"" -quiet
|
|
}
|
|
|
|
# Read the version release notes from CHANGELOG
|
|
$Changelog = ""
|
|
$ReadLine = $false
|
|
Get-Content "CHANGELOG.md" | ForEach-Object {
|
|
if ($ReadLine) {
|
|
if ($_ -match "^## ") {
|
|
$ReadLine = $false
|
|
} else {
|
|
$Changelog += $_ + "`n"
|
|
}
|
|
} elseif ($_ -match "$Version \(\d{4}-\d{2}-\d{2}\)") {
|
|
$ReadLine = $true
|
|
}
|
|
}
|
|
|
|
Write-Host "Checking out target branch '$TargetBranch'..."
|
|
Invoke-Cmd "git" "checkout `"$TargetBranch`"" -quiet
|
|
|
|
Write-Host "Merging '$SourceBranch' into '$TargetBranch'..."
|
|
Invoke-Cmd "git" "merge `"$SourceBranch`" --no-ff -m `"Release $Version`" -m `"$Changelog`" `"$SourceBranch`" -S" -quiet
|
|
|
|
Write-Host "Creating tag for '$Version'..."
|
|
Invoke-Cmd "git" "tag -a `"$Version`" -m `"Release $Version`" -m `"$Changelog`" -s" -quiet
|
|
|
|
Write-Host "All done!"
|
|
Write-Host "Please merge the release branch back into the develop branch now and then push your changes."
|
|
Write-Host "Don't forget to also push the tags using 'git push --tags'."
|
|
} elseif ($Build) {
|
|
$Vcpkg = (Resolve-Path $Vcpkg).Path
|
|
|
|
# Find Visual Studio and establish build environment
|
|
Invoke-VSToolchain $VSToolChain $SourceDir -Arch "amd64"
|
|
|
|
Test-RequiredPrograms
|
|
|
|
if ($Snapshot) {
|
|
$Tag = "HEAD"
|
|
$SourceBranch = & git rev-parse --abbrev-ref HEAD
|
|
$ReleaseName = "$Version-snapshot"
|
|
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=Snapshot -DOVERRIDE_VERSION=`"$ReleaseName`""
|
|
Write-Host "Using current branch '$SourceBranch' to build." -ForegroundColor Cyan
|
|
} else {
|
|
Test-WorkingTreeClean
|
|
|
|
# Clear output directory
|
|
if (Test-Path $OutDir) {
|
|
Remove-Item $OutDir -Recurse
|
|
}
|
|
|
|
if ($Version -match "-beta\d*$") {
|
|
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=PreRelease"
|
|
} else {
|
|
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=Release"
|
|
}
|
|
|
|
# Setup Tag if not defined then checkout tag
|
|
if ($Tag -eq "" -or $Tag -eq $null) {
|
|
$Tag = $Version
|
|
}
|
|
Write-Host "Checking out tag 'tags/$Tag' to build." -ForegroundColor Cyan
|
|
Invoke-Cmd "git" "checkout `"tags/$Tag`""
|
|
}
|
|
|
|
# Create directories
|
|
New-Item "$OutDir" -ItemType Directory -Force | Out-Null
|
|
$OutDir = (Resolve-Path $OutDir).Path
|
|
|
|
$BuildDir = "$OutDir\build-release"
|
|
New-Item "$BuildDir" -ItemType Directory -Force | Out-Null
|
|
|
|
# Enter build directory
|
|
Set-Location "$BuildDir"
|
|
|
|
# Setup CMake options
|
|
$CMakeOptions = "$CMakeOptions -DWITH_XC_ALL=ON -DWITH_TESTS=OFF -DCMAKE_BUILD_TYPE=Release"
|
|
$CMakeOptions = "$CMakeOptions -DCMAKE_TOOLCHAIN_FILE:FILEPATH=`"$Vcpkg`" -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON"
|
|
|
|
Write-Host "Configuring build..." -ForegroundColor Cyan
|
|
Invoke-Cmd "cmake" "$CMakeOptions -G `"$CMakeGenerator`" `"$SourceDir`""
|
|
|
|
Write-Host "Compiling sources..." -ForegroundColor Cyan
|
|
Invoke-Cmd "cmake" "--build . --config Release -- $MakeOptions"
|
|
|
|
if ($SignBuild) {
|
|
$files = Get-ChildItem "$BuildDir\src" -Include "*keepassxc*.exe", "*keepassxc*.dll" -Recurse -File | ForEach-Object { $_.FullName }
|
|
Invoke-SignFiles $files $SignKey $Timestamp
|
|
}
|
|
|
|
Write-Host "Create deployment packages..." -ForegroundColor Cyan
|
|
Invoke-Cmd "cpack" "-G `"$CPackGenerators`""
|
|
Move-Item "$BuildDir\keepassxc-*" -Destination "$OutDir" -Force
|
|
|
|
if ($SignBuild) {
|
|
# Enter output directory
|
|
Set-Location -Path "$OutDir"
|
|
|
|
# Sign MSI files using AppSign key
|
|
$files = Get-ChildItem $OutDir -Include "*.msi" -Name
|
|
Invoke-SignFiles $files $SignKey $Timestamp
|
|
|
|
# Sign all output files using the GPG key then hash them
|
|
$files = Get-ChildItem $OutDir -Include "*.msi", "*.zip" -Name
|
|
Invoke-GpgSignFiles $files $GpgKey
|
|
}
|
|
|
|
# Restore state
|
|
Invoke-Command {git checkout $OrigBranch}
|
|
Set-Location "$OrigDir"
|
|
} elseif ($Sign) {
|
|
if (Test-Path $SignKey) {
|
|
# Need to include path to signtool program
|
|
Invoke-VSToolchain $VSToolChain $SourceDir -Arch "amd64"
|
|
}
|
|
|
|
Test-RequiredPrograms
|
|
|
|
# Resolve wildcard paths
|
|
$ResolvedFiles = @()
|
|
foreach ($_ in $SignFiles) {
|
|
$ResolvedFiles += (Get-ChildItem $_ -File | ForEach-Object { $_.FullName })
|
|
}
|
|
|
|
$AppSignFiles = $ResolvedFiles.Where({ $_ -match "\.(msi|exe|dll)$" })
|
|
Invoke-SignFiles $AppSignFiles $SignKey $Timestamp
|
|
|
|
$GpgSignFiles = $ResolvedFiles.Where({ $_ -match "\.(msi|zip|gz|xz|dmg|appimage)$" })
|
|
Invoke-GpgSignFiles $GpgSignFiles $GpgKey
|
|
}
|
|
|
|
# SIG # Begin signature block
|
|
# MIIThAYJKoZIhvcNAQcCoIITdTCCE3ECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
|
|
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
|
|
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUyaXWK5K1LP2TD/IgGb5Tfs8v
|
|
# C2GgghC8MIIFOjCCBCKgAwIBAgIQWKLXLYzA/YnM/yHg1O3HSjANBgkqhkiG9w0B
|
|
# AQsFADB8MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVy
|
|
# MRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxJDAi
|
|
# BgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2lnbmluZyBDQTAeFw0yMTAzMTUwMDAw
|
|
# MDBaFw0yNDAzMTQyMzU5NTlaMIGhMQswCQYDVQQGEwJVUzEOMAwGA1UEEQwFMjIz
|
|
# MTUxETAPBgNVBAgMCFZpcmdpbmlhMRIwEAYDVQQHDAlGcmFuY29uaWExGzAZBgNV
|
|
# BAkMEjY2NTMgQXVkcmV5IEtheSBDdDEeMBwGA1UECgwVRHJvaWRNb25rZXkgQXBw
|
|
# cywgTExDMR4wHAYDVQQDDBVEcm9pZE1vbmtleSBBcHBzLCBMTEMwggEiMA0GCSqG
|
|
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwB9L/+1zlcXOQLoYvdrYAWS9B5ui+7E9c
|
|
# XCn6wcB4NdmaRbNM3kdWc8nbjOOHeOct2jVzVu/pJR1SagI+V1R1BfzgfzuW55Yy
|
|
# iHrqXQGfL9xhqJAWSvdQRinvlkZ+WY3QxnOhzcQk+BTLYdUwq04O3jMv7vnH6fuL
|
|
# q/HXEsgDObZC7EyKEtVbWVo4nqY0tUTviJXvRI/sFDN8DvULefwZWIvF7G11NFeK
|
|
# It24+hDCzvVBKtEn7DNmFGO1CJAB7Sz4jFewV4MP1gviMAfGbSBqavyRDBOG7eda
|
|
# SVb1Zq482yoHNAs+mpIQK2SGvUKKAJK2wCDbzgpvu5sfzwStpc0hAgMBAAGjggGQ
|
|
# MIIBjDAfBgNVHSMEGDAWgBQO4TqoUzox1Yq+wbutZxoDha00DjAdBgNVHQ4EFgQU
|
|
# 7u2WZ7fqJiaM3u9SlzAwGBhoWH0wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC
|
|
# MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJYIZIAYb4QgEBBAQDAgQQMEoGA1Ud
|
|
# IARDMEEwNQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2Vj
|
|
# dGlnby5jb20vQ1BTMAgGBmeBDAEEATBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v
|
|
# Y3JsLnNlY3RpZ28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNybDBzBggr
|
|
# BgEFBQcBAQRnMGUwPgYIKwYBBQUHMAKGMmh0dHA6Ly9jcnQuc2VjdGlnby5jb20v
|
|
# U2VjdGlnb1JTQUNvZGVTaWduaW5nQ0EuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8v
|
|
# b2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAD2w/Tt5KyPbX2M+h
|
|
# WVwgqpKm42nk6aN2HvSp+KWlrB2t+ziL+1IRXwq7S0V7p2e1ZK8uXLzBjUDVGjBc
|
|
# ugh5hGG95MGVltxCJrr/bk1He62L7MwVxfH5b5MrE/vC/cHcSxEB1AZwZxYKjDPf
|
|
# R81biDVch++XeKmvUxfT4XGo7McJqT4K/TcLwijSb/AWsXR+r2BXEAqgsoG37kk/
|
|
# fbPKimpJ07hxd/RNYVpE33E93zCQ1Tjc1tP3DaLq8cpS6jGUY5NNOzRgp2mGcGHy
|
|
# lv6Q/xf45qNvHiqFVctdvY9of0QFjg5eYDr4rLDa+mks9f1Jd8aDWKcsfCBnlohT
|
|
# KIffbTCCBYEwggRpoAMCAQICEDlyRDr5IrdR19NsEN0xNZUwDQYJKoZIhvcNAQEM
|
|
# BQAwezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQ
|
|
# MA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAf
|
|
# BgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0xOTAzMTIwMDAwMDBa
|
|
# Fw0yODEyMzEyMzU5NTlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEpl
|
|
# cnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJV
|
|
# U1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9u
|
|
# IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIASZRc2
|
|
# DsPbCLPQrFcNdu3NJ9NMrVCDYeKqIE0JLWQJ3M6Jn8w9qez2z8Hc8dOx1ns3KBEr
|
|
# R9o5xrw6GbRfpr19naNjQrZ28qk7K5H44m/Q7BYgkAk+4uh0yRi0kdRiZNt/owbx
|
|
# iBhqkCI8vP4T8IcUe/bkH47U5FHGEWdGCFHLhhRUP7wz/n5snP8WnRi9UY41pqdm
|
|
# yHJn2yFmsdSbeAPAUDrozPDcvJ5M/q8FljUfV1q3/875PbcstvZU3cjnEjpNrkyK
|
|
# t1yatLcgPcp/IjSufjtoZgFE5wFORlObM2D3lL5TN5BzQ/Myw1Pv26r+dE5px2uM
|
|
# YJPexMcM3+EyrsyTO1F4lWeL7j1W/gzQaQ8bD/MlJmszbfduR/pzQ+V+DqVmsSl8
|
|
# MoRjVYnEDcGTVDAZE6zTfTen6106bDVc20HXEtqpSQvf2ICKCZNijrVmzyWIzYS4
|
|
# sT+kOQ/ZAp7rEkyVfPNrBaleFoPMuGfi6BOdzFuC00yz7Vv/3uVzrCM7LQC/NVV0
|
|
# CUnYSVgaf5I25lGSDvMmfRxNF7zJ7EMm0L9BX0CpRET0medXh55QH1dUqD79dGMv
|
|
# sVBlCeZYQi5DGky08CVHWfoEHpPUJkZKUIGy3r54t/xnFeHJV4QeD2PW6WK61l9V
|
|
# LupcxigIBCU5uA4rqfJMlxwHPw1S9e3vL4IPAgMBAAGjgfIwge8wHwYDVR0jBBgw
|
|
# FoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYDVR0OBBYEFFN5v1qqK0rPVIDh2JvA
|
|
# nfKyA2bLMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MBEGA1UdIAQK
|
|
# MAgwBgYEVR0gADBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2Nh
|
|
# LmNvbS9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYw
|
|
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0B
|
|
# AQwFAAOCAQEAGIdR3HQhPZyK4Ce3M9AuzOzw5steEd4ib5t1jp5y/uTW/qofnJYt
|
|
# 7wNKfq70jW9yPEM7wD/ruN9cqqnGrvL82O6je0P2hjZ8FODN9Pc//t64tIrwkZb+
|
|
# /UNkfv3M0gGhfX34GRnJQisTv1iLuqSiZgR2iJFODIkUzqJNyTKzuugUGrxx8Vvw
|
|
# QQuYAAoiAxDlDLH5zZI3Ge078eQ6tvlFEyZ1r7uq7z97dzvSxAKRPRkA0xdcOds/
|
|
# exgNRc2ThZYvXd9ZFk8/Ub3VRRg/7UqO6AZhdCMWtQ1QcydER38QXYkqa4UxFMTo
|
|
# qWpMgLxqeM+4f452cpkMnf7XkQgWoaNflTCCBfUwggPdoAMCAQICEB2iSDBvmyYY
|
|
# 0ILgln0z02owDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
|
# EwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhl
|
|
# IFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRp
|
|
# ZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEwMjAwMDAwMFoXDTMwMTIzMTIzNTk1
|
|
# OVowfDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQ
|
|
# MA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYD
|
|
# VQQDExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEB
|
|
# AQUAA4IBDwAwggEKAoIBAQCGIo0yhXoYn0nwli9jCB4t3HyfFM/jJrYlZilAhlRG
|
|
# dDFixRDtsocnppnLlTDAVvWkdcapDlBipVGREGrgS2Ku/fD4GKyn/+4uMyD6DBmJ
|
|
# qGx7rQDDYaHcaWVtH24nlteXUYam9CflfGqLlR5bYNV+1xaSnAAvaPeX7Wpyvjg7
|
|
# Y96Pv25MQV0SIAhZ6DnNj9LWzwa0VwW2TqE+V2sfmLzEYtYbC43HZhtKn52BxHJA
|
|
# teJf7wtF/6POF6YtVbC3sLxUap28jVZTxvC6eVBJLPcDuf4vZTXyIuosB69G2flG
|
|
# HNyMfHEo8/6nxhTdVZFuihEN3wYklX0Pp6F8OtqGNWHTAgMBAAGjggFkMIIBYDAf
|
|
# BgNVHSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUDuE6qFM6
|
|
# MdWKvsG7rWcaA4WtNA4wDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8C
|
|
# AQAwHQYDVR0lBBYwFAYIKwYBBQUHAwMGCCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYE
|
|
# VR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20v
|
|
# VVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUH
|
|
# AQEEajBoMD8GCCsGAQUFBzAChjNodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNF
|
|
# UlRydXN0UlNBQWRkVHJ1c3RDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3Nw
|
|
# LnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggIBAE1jUO1HNEphpNveaiqM
|
|
# m/EAAB4dYns61zLC9rPgY7P7YQCImhttEAcET7646ol4IusPRuzzRl5ARokS9At3
|
|
# WpwqQTr81vTr5/cVlTPDoYMot94v5JT3hTODLUpASL+awk9KsY8k9LOBN9O3ZLCm
|
|
# I2pZaFJCX/8E6+F0ZXkI9amT3mtxQJmWunjxucjiwwgWsatjWsgVgG10Xkp1fqW4
|
|
# w2y1z99KeYdcx0BNYzX2MNPPtQoOCwR/oEuuu6Ol0IQAkz5TXTSlADVpbL6fICUQ
|
|
# DRn7UJBhvjmPeo5N9p8OHv4HURJmgyYZSJXOSsnBf/M6BZv5b9+If8AjntIeQ3pF
|
|
# McGcTanwWbJZGehqjSkEAnd8S0vNcL46slVaeD68u28DECV3FTSK+TbMQ5Lkuk/x
|
|
# YpMoJVcp+1EZx6ElQGqEV8aynbG8HArafGd+fS7pKEwYfsR7MUFxmksp7As9V1DS
|
|
# yt39ngVR5UR43QHesXWYDVQk/fBO4+L4g71yuss9Ou7wXheSaG3IYfmm8SoKC6W5
|
|
# 9J7umDIFhZ7r+YMp08Ysfb06dy6LN0KgaoLtO0qqlBCk4Q34F8W2WnkzGJLjtXX4
|
|
# oemOCiUe5B7xn1qHI/+fpFGe+zmAEc3btcSnqIBv5VPU4OOiwtJbGvoyJi1qV3Ac
|
|
# PKRYLqPzW0sH3DJZ84enGm1YMYICMjCCAi4CAQEwgZAwfDELMAkGA1UEBhMCR0Ix
|
|
# GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEY
|
|
# MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJTQSBD
|
|
# b2RlIFNpZ25pbmcgQ0ECEFii1y2MwP2JzP8h4NTtx0owCQYFKw4DAhoFAKB4MBgG
|
|
# CisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
|
|
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYE
|
|
# FPvoURlVLtMyc41aoH1W7jNXhNkUMA0GCSqGSIb3DQEBAQUABIIBACa4ISoVYuy4
|
|
# LQD5f2XzRDboWCOwR2ClFczB/vOn7uX+RKpbW+vZwllcL0wk0kA4Iotk12yKLAni
|
|
# K0DkhX8P/Gt5B4hMFaWYKkwTZljITgCEHoAy8vQzpfDUdfJF40R7IIEQLzr2/n5q
|
|
# Iztv/ApXsPX8SkgEGdikFbBA0i/xtzI8+3sI1QINiRig8xEH/1eOZlR54YHwClvS
|
|
# 8QhXueb9NbqNN9oKBwx5gRWcOE4I2E5mYAppDDQyhqitbeeY2Pw4Eo5koLM3zTDy
|
|
# 4/zc+A9lNkAa5eDTavxMHQVqKgO5KomzIYHAdIFnKs85SdntIOr5nSAHnAl6svTh
|
|
# iJXqSEggdX8=
|
|
# SIG # End signature block
|