Files
DurakScore/publish-release.ps1

178 lines
4.5 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$VersionName,
[Parameter(Mandatory = $true)]
[int]$VersionCode
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$env:JAVA_HOME = "C:\Program Files\Android\Android Studio1\jbr"
$env:PATH = "$env:JAVA_HOME\bin;$env:PATH"
$Server = "root@5.188.21.226"
$BaseUrl = "https://updates.bearmsk.ru"
$RemoteDir = "/var/www/html/updates"
$SshKey = Join-Path $env:USERPROFILE ".ssh\durakometr_release"
if ($VersionName -notmatch '^[0-9A-Za-z._-]+$') {
throw "Unsafe versionName: $VersionName"
}
if ($VersionCode -le 0) {
throw "Invalid versionCode: $VersionCode"
}
$ProjectRoot = $PSScriptRoot
$Apk = Join-Path $ProjectRoot "app\build\outputs\apk\release\app-release.apk"
$NotesFile = Join-Path $ProjectRoot "release-notes.txt"
if (-not (Test-Path $Apk)) {
throw "APK not found: $Apk"
}
$ApkInfo = Get-Item $Apk
if ($ApkInfo.Length -lt 1MB) {
throw "APK is suspiciously small: $($ApkInfo.Length) bytes"
}
if (-not (Test-Path $SshKey)) {
throw "SSH key not found: $SshKey"
}
$BuildToolsRoot = Join-Path $env:LOCALAPPDATA "Android\Sdk\build-tools"
if (-not (Test-Path $BuildToolsRoot)) {
throw "Android build-tools not found: $BuildToolsRoot"
}
$ApkSigner = Get-ChildItem $BuildToolsRoot -Directory |
Sort-Object Name -Descending |
ForEach-Object {
$Candidate = Join-Path $_.FullName "apksigner.bat"
if (Test-Path $Candidate) {
$Candidate
}
} |
Select-Object -First 1
if (-not $ApkSigner) {
throw "apksigner.bat not found"
}
& $ApkSigner verify --verbose $Apk
if ($LASTEXITCODE -ne 0) {
throw "APK signature verification failed"
}
$Sha256 = (Get-FileHash -Algorithm SHA256 $Apk).Hash.ToLowerInvariant()
$Notes = "Application update"
if (Test-Path $NotesFile) {
$LoadedNotes = (Get-Content $NotesFile -Raw -Encoding UTF8).Trim()
if (-not [string]::IsNullOrWhiteSpace($LoadedNotes)) {
$Notes = $LoadedNotes
}
}
$RemoteName = "durakometr-$VersionName.apk"
$RemoteTemp = "$RemoteDir/.$RemoteName.uploading"
$RemoteFinal = "$RemoteDir/$RemoteName"
$LocalJson = Join-Path $env:TEMP "durakometr-latest-$VersionCode.json"
$RemoteJsonTemp = "$RemoteDir/.latest.json.uploading"
$RemoteJsonFinal = "$RemoteDir/latest.json"
$Metadata = [ordered]@{
versionCode = $VersionCode
versionName = $VersionName
apkUrl = "$BaseUrl/$RemoteName"
sha256 = $Sha256
mandatory = $false
notes = $Notes
}
$Json = $Metadata | ConvertTo-Json -Depth 5
[System.IO.File]::WriteAllText(
$LocalJson,
$Json,
[System.Text.UTF8Encoding]::new($false)
)
$SshArgs = @(
"-i", $SshKey,
"-o", "BatchMode=yes",
"-o", "ConnectTimeout=10"
)
Write-Host ""
Write-Host "=== Durakometr release $VersionName ($VersionCode) ==="
Write-Host "APK: $([math]::Round($ApkInfo.Length / 1MB, 2)) MB"
Write-Host "SHA256: $Sha256"
Write-Host ""
& scp.exe @SshArgs $Apk "${Server}:$RemoteTemp"
if ($LASTEXITCODE -ne 0) {
throw "APK upload failed"
}
$RemoteSizeLines = & ssh.exe @SshArgs $Server "stat -c%s '$RemoteTemp'"
if ($LASTEXITCODE -ne 0) {
throw "Failed to read remote APK size"
}
$RemoteSizeText = ($RemoteSizeLines | Select-Object -Last 1).ToString().Trim()
[long]$RemoteSize = 0
if (-not [long]::TryParse($RemoteSizeText, [ref]$RemoteSize)) {
throw "Invalid remote APK size: $RemoteSizeText"
}
if ($RemoteSize -ne $ApkInfo.Length) {
& ssh.exe @SshArgs $Server "rm -f '$RemoteTemp'"
throw "Size mismatch. Local=$($ApkInfo.Length), remote=$RemoteSize"
}
& scp.exe @SshArgs $LocalJson "${Server}:$RemoteJsonTemp"
if ($LASTEXITCODE -ne 0) {
& ssh.exe @SshArgs $Server "rm -f '$RemoteTemp'"
throw "latest.json upload failed"
}
$PublishCommand = "set -e; " +
"test -s '$RemoteTemp'; " +
"test -s '$RemoteJsonTemp'; " +
"mv -f '$RemoteTemp' '$RemoteFinal'; " +
"chmod 644 '$RemoteFinal'; " +
"mv -f '$RemoteJsonTemp' '$RemoteJsonFinal'; " +
"chmod 644 '$RemoteJsonFinal'; " +
"stat -c '%n %s bytes' '$RemoteFinal'; " +
"cat '$RemoteJsonFinal'"
& ssh.exe @SshArgs $Server $PublishCommand
if ($LASTEXITCODE -ne 0) {
throw "Server publish step failed"
}
Remove-Item $LocalJson -Force -ErrorAction SilentlyContinue
Write-Host ""
Write-Host "=============================================="
Write-Host "RELEASE PUBLISHED"
Write-Host "Version: $VersionName ($VersionCode)"
Write-Host "APK: $([math]::Round($ApkInfo.Length / 1MB, 2)) MB"
Write-Host "SHA256: $Sha256"
Write-Host "$BaseUrl/$RemoteName"
Write-Host "=============================================="