Files
kooltab/make_release.ps1
2026-10-19 21:41:34 +02:00

134 lines
4.8 KiB
PowerShell

param(
[switch]$SkipTests,
[switch]$SkipUpload
)
$ErrorActionPreference = 'Stop'
Set-Location $PSScriptRoot
$updateServer = 'https://updater.kooltap.com'
$pubspecPath = Join-Path $PSScriptRoot 'pubspec.yaml'
$apkPath = Join-Path $PSScriptRoot 'build\app\outputs\flutter-apk\app-release.apk'
function Fail([string]$message) {
Write-Host $message -ForegroundColor Red
exit 1
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$pubspec = [IO.File]::ReadAllText($pubspecPath)
$match = [regex]::Match(
$pubspec,
'(?m)^version:[ \t]*([0-9]+\.[0-9]+\.[0-9]+)\+([0-9]+)'
)
if (-not $match.Success) {
Fail 'Could not find a version like 1.2.3+4 in pubspec.yaml.'
}
$currentName = $match.Groups[1].Value
$currentBuild = [int]$match.Groups[2].Value
Write-Host "Current version: $currentName+$currentBuild"
# The server picks the latest release by Android version code, so every release
# needs a higher one than anything already published
$publishedName = $null
$publishedCode = 0
try {
$releases = Invoke-RestMethod "$updateServer/api/releases" -UseBasicParsing -TimeoutSec 15
foreach ($release in $releases) {
if ($release.VersionCode -gt $publishedCode) {
$publishedName = $release.Version
$publishedCode = [int]$release.VersionCode
}
}
if ($publishedName) {
Write-Host "Latest published: $publishedName (version code $publishedCode)"
}
} catch {
Write-Host "Could not reach ${updateServer}: $($_.Exception.Message)" -ForegroundColor Yellow
Write-Host 'Using pubspec.yaml only; the upload is refused if the version is too low.' -ForegroundColor Yellow
}
$name = Read-Host 'Release version (for example 1.2.2)'
if ($name -notmatch '\A[0-9]+\.[0-9]+\.[0-9]+\z') {
Fail 'Use a version like 1.2.2.'
}
if ($publishedName -and [version]$name -le [version]$publishedName) {
Fail "$name must be higher than the published $publishedName, or tablets will not see it."
}
# The build number only counts builds of the same version and starts at 0 for
# a new one. Android refuses updates with a lower version code, so that code is
# derived from the whole version instead: 1.2.5+3 becomes 1020503
$build = if ($name -eq $currentName) { $currentBuild + 1 } else { 0 }
$major, $minor, $patch = $name.Split('.') | ForEach-Object { [int]$_ }
if ($minor -ge 100 -or $patch -ge 100 -or $build -ge 100) {
Fail 'Minor, patch and build numbers must stay below 100.'
}
$versionCode = $major * 1000000 + $minor * 10000 + $patch * 100 + $build
if ($versionCode -le $publishedCode) {
Fail "Version code $versionCode is not higher than the published $publishedCode."
}
$version = "$name+$build"
$versionStart = $match.Groups[1].Index
$versionEnd = $match.Groups[2].Index + $match.Groups[2].Length
$pubspec = $pubspec.Substring(0, $versionStart) + $version + $pubspec.Substring($versionEnd)
[IO.File]::WriteAllText($pubspecPath, $pubspec)
Write-Host "Updated pubspec.yaml to version $version (version code $versionCode)."
if (-not $SkipTests) {
Write-Host 'Running tests...'
flutter test
if ($LASTEXITCODE -ne 0) { Fail 'Tests failed. Release cancelled.' }
}
Write-Host 'Building release APK...'
flutter build apk --release --build-number=$versionCode
if ($LASTEXITCODE -ne 0) { Fail 'Release APK build failed.' }
Write-Host "Release APK created at $apkPath"
if ($SkipUpload) { exit 0 }
$token = $env:KOOLTAP_ADMIN_TOKEN
if (-not $token) {
Write-Host 'KOOLTAP_ADMIN_TOKEN is not set, skipping the upload.' -ForegroundColor Yellow
exit 0
}
$notes = Read-Host 'Release notes'
$mandatory = if ((Read-Host 'Mandatory update? (y/N)') -match '\A[yY]') { 'true' } else { 'false' }
Write-Host "Uploading $version to $updateServer..."
Add-Type -AssemblyName System.Net.Http
$client = [Net.Http.HttpClient]::new()
$client.Timeout = [TimeSpan]::FromMinutes(10)
$client.DefaultRequestHeaders.Add('X-Admin-Token', $token)
$apk = [IO.File]::OpenRead($apkPath)
$form = [Net.Http.MultipartFormDataContent]::new()
$form.Add([Net.Http.StreamContent]::new($apk), 'apk', 'app-release.apk')
$form.Add([Net.Http.StringContent]::new($notes), 'notes')
$form.Add([Net.Http.StringContent]::new($mandatory), 'mandatory')
try {
$response = $client.PostAsync("$updateServer/admin/releases", $form).GetAwaiter().GetResult()
$body = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
} finally {
$form.Dispose()
$apk.Dispose()
$client.Dispose()
}
if (-not $response.IsSuccessStatusCode) {
Fail "Upload failed (HTTP $([int]$response.StatusCode)): $body`nThe APK is still at $apkPath"
}
$release = $body | ConvertFrom-Json
Write-Host "Published $($release.Version) (build $($release.VersionCode)), sha256 $($release.SHA256)" -ForegroundColor Green
Write-Host "Don't forget to commit pubspec.yaml and tag the release: git tag v$version"