2026-07-27 08:22:55 +12:00
|
|
|
<#
|
|
|
|
|
.SYNOPSIS
|
|
|
|
|
Builds a signed release APK and assembles the folder to publish on the NAS.
|
|
|
|
|
|
|
|
|
|
.DESCRIPTION
|
|
|
|
|
Produces dist/out/ containing:
|
|
|
|
|
|
|
|
|
|
index.html landing page people are sent to
|
|
|
|
|
latest.json update manifest the app polls
|
|
|
|
|
memby-<version>.apk the build itself
|
2026-08-02 22:10:19 +12:00
|
|
|
LICENSE / NOTICE licence terms and acknowledgements
|
2026-07-27 08:22:55 +12:00
|
|
|
|
|
|
|
|
Copy that folder to whatever the NAS serves, keeping the file names. Old APKs can
|
|
|
|
|
stay alongside — only latest.json decides what the app offers.
|
|
|
|
|
|
|
|
|
|
.PARAMETER Version
|
|
|
|
|
Version to build, e.g. 0.1.54. Updates versionName/versionCode in
|
|
|
|
|
app/build.gradle.kts before building. Omit to build the version already set.
|
|
|
|
|
|
|
|
|
|
.PARAMETER Notes
|
|
|
|
|
What changed, shown both on the landing page and in the app's update prompt.
|
|
|
|
|
|
|
|
|
|
.PARAMETER BaseUrl
|
|
|
|
|
Public URL of the folder on the NAS, e.g. https://nas.example.com/memby.
|
|
|
|
|
Used to build absolute links on the landing page.
|
|
|
|
|
|
2026-08-02 22:10:19 +12:00
|
|
|
.PARAMETER SourceUrl
|
|
|
|
|
Recipient-accessible URL for the exact corresponding source. This link is embedded
|
|
|
|
|
in the APK and release page. It must remain available to everyone receiving a build.
|
|
|
|
|
|
2026-07-27 08:22:55 +12:00
|
|
|
.EXAMPLE
|
|
|
|
|
.\release.ps1 -Version 0.1.54 -Notes "Faster home screen" -BaseUrl https://nas.example.com/memby
|
|
|
|
|
#>
|
|
|
|
|
param(
|
|
|
|
|
[string] $Version,
|
|
|
|
|
[string] $Notes = '',
|
2026-08-02 22:10:19 +12:00
|
|
|
[Parameter(Mandatory = $true)][string] $BaseUrl,
|
|
|
|
|
[string] $SourceUrl = 'https://g.sublogue.com/admin/memby'
|
2026-07-27 08:22:55 +12:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
|
|
|
|
|
|
$root = $PSScriptRoot
|
|
|
|
|
$gradleFile = Join-Path $root 'app\build.gradle.kts'
|
|
|
|
|
$outDir = Join-Path $root 'dist\out'
|
|
|
|
|
$templateDir = Join-Path $root 'dist\template'
|
|
|
|
|
|
|
|
|
|
$jdkHome = 'C:\Program Files\Android\Android Studio\jbr'
|
|
|
|
|
if (Test-Path $jdkHome) { $env:JAVA_HOME = $jdkHome }
|
|
|
|
|
|
|
|
|
|
# --- version ---------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
$gradle = Get-Content -Raw $gradleFile
|
|
|
|
|
|
|
|
|
|
if ($Version) {
|
|
|
|
|
if ($Version -notmatch '^\d+\.\d+\.\d+$') { throw "Version must look like 0.1.54, got '$Version'" }
|
|
|
|
|
$parts = $Version.Split('.')
|
|
|
|
|
# Same scheme as app/build.gradle.kts: major*10000 + minor*100 + patch.
|
|
|
|
|
$code = [int]$parts[0] * 10000 + [int]$parts[1] * 100 + [int]$parts[2]
|
|
|
|
|
|
2026-07-27 21:06:51 +12:00
|
|
|
$gradle = $gradle -replace 'val defaultVersionName = "[^"]*"', "val defaultVersionName = `"$Version`""
|
2026-07-27 08:22:55 +12:00
|
|
|
Set-Content -LiteralPath $gradleFile -Value $gradle -NoNewline
|
|
|
|
|
Write-Host "Set version $Version (versionCode $code)" -ForegroundColor Cyan
|
|
|
|
|
} else {
|
2026-07-27 21:06:51 +12:00
|
|
|
if ($gradle -notmatch 'val defaultVersionName = "([^"]*)"') {
|
|
|
|
|
throw 'Could not read defaultVersionName from app/build.gradle.kts'
|
|
|
|
|
}
|
2026-07-27 08:22:55 +12:00
|
|
|
$Version = $Matches[1]
|
|
|
|
|
Write-Host "Building existing version $Version" -ForegroundColor Cyan
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# --- build -----------------------------------------------------------------
|
|
|
|
|
|
2026-08-02 22:10:19 +12:00
|
|
|
& (Join-Path $root 'gradlew.bat') --console=plain clean test assembleRelease "-Pmemby.sourceUrl=$SourceUrl"
|
2026-07-27 08:22:55 +12:00
|
|
|
if ($LASTEXITCODE -ne 0) { throw 'Build failed' }
|
|
|
|
|
|
|
|
|
|
$apk = Join-Path $root 'app\build\outputs\apk\release\app-release.apk'
|
|
|
|
|
if (-not (Test-Path $apk)) {
|
|
|
|
|
# An unsigned build lands under a different name and cannot be installed.
|
|
|
|
|
$unsigned = Join-Path $root 'app\build\outputs\apk\release\app-release-unsigned.apk'
|
|
|
|
|
if (Test-Path $unsigned) {
|
|
|
|
|
throw "Release APK is UNSIGNED. Configure memby.keystore in local.properties — " +
|
|
|
|
|
"an unsigned APK will not install, and a key change breaks updates for existing users."
|
|
|
|
|
}
|
|
|
|
|
throw "No release APK found at $apk"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# --- assemble the publish folder -------------------------------------------
|
|
|
|
|
|
|
|
|
|
$apkName = "memby-$Version.apk"
|
|
|
|
|
$base = $BaseUrl.TrimEnd('/')
|
|
|
|
|
|
|
|
|
|
if (Test-Path $outDir) { Remove-Item $outDir -Recurse -Force }
|
|
|
|
|
New-Item -ItemType Directory -Force $outDir | Out-Null
|
|
|
|
|
|
|
|
|
|
Copy-Item $apk (Join-Path $outDir $apkName)
|
2026-08-02 22:10:19 +12:00
|
|
|
Copy-Item (Join-Path $root 'LICENSE') (Join-Path $outDir 'LICENSE')
|
|
|
|
|
$notice = (Get-Content -Raw (Join-Path $root 'NOTICE')).
|
|
|
|
|
Replace('https://g.sublogue.com/admin/memby', $SourceUrl)
|
|
|
|
|
Set-Content -LiteralPath (Join-Path $outDir 'NOTICE') -Value $notice -Encoding utf8
|
2026-07-27 08:22:55 +12:00
|
|
|
|
|
|
|
|
# apkUrl stays relative so the folder keeps working if the NAS is reached by another
|
|
|
|
|
# name; the app resolves it against the manifest's own URL.
|
|
|
|
|
$manifest = [ordered]@{
|
|
|
|
|
version = $Version
|
|
|
|
|
apkUrl = $apkName
|
|
|
|
|
notes = $Notes
|
|
|
|
|
} | ConvertTo-Json
|
|
|
|
|
Set-Content -LiteralPath (Join-Path $outDir 'latest.json') -Value $manifest -Encoding utf8
|
|
|
|
|
|
|
|
|
|
$size = '{0:N1} MB' -f ((Get-Item $apk).Length / 1MB)
|
|
|
|
|
$page = Get-Content -Raw (Join-Path $templateDir 'index.html')
|
|
|
|
|
$page = $page.Replace('{{VERSION}}', $Version).
|
|
|
|
|
Replace('{{APK_NAME}}', $apkName).
|
|
|
|
|
Replace('{{APK_URL}}', "$base/$apkName").
|
|
|
|
|
Replace('{{BASE_URL}}', $base).
|
2026-08-02 22:10:19 +12:00
|
|
|
Replace('{{SOURCE_URL}}', $SourceUrl).
|
2026-07-27 08:22:55 +12:00
|
|
|
Replace('{{SIZE}}', $size).
|
|
|
|
|
Replace('{{NOTES}}', $(if ($Notes) { $Notes } else { 'Various improvements.' })).
|
|
|
|
|
Replace('{{DATE}}', (Get-Date -Format 'd MMMM yyyy'))
|
|
|
|
|
Set-Content -LiteralPath (Join-Path $outDir 'index.html') -Value $page -Encoding utf8
|
|
|
|
|
|
|
|
|
|
Write-Host ''
|
|
|
|
|
Write-Host "Ready to publish:" -ForegroundColor Green
|
|
|
|
|
Get-ChildItem $outDir | ForEach-Object { " $($_.Name)" }
|
|
|
|
|
Write-Host ''
|
|
|
|
|
Write-Host "Copy the contents of dist\out\ to the folder served at $base"
|
|
|
|
|
Write-Host "Landing page: $base/"
|
|
|
|
|
Write-Host "Update manifest: $base/latest.json"
|