Self-hosted APK distribution
Memby is handed out from a NAS or web server rather than a store, so:
- UpdateChecker gains a static-manifest source alongside Gitea. A .json
update URL is read as a manifest ({version, apkUrl, notes}); anything
else is still treated as a Gitea host. apkUrl may be relative and is
resolved against the manifest's own URL.
- Release signing config reads memby.keystore from local.properties or
the environment. Without it the build still succeeds but warns loudly:
an unsigned APK will not install, and a changed key forces every user
to uninstall before they can update.
- release.ps1 bumps the version, runs tests, builds a signed APK and
assembles dist/out/ (landing page, latest.json, versioned APK) ready to
copy onto the NAS.
- Keystores and dist/out are gitignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2ce405c540
commit
08360b75e4
+117
@@ -0,0 +1,117 @@
|
||||
<#
|
||||
.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
|
||||
|
||||
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.
|
||||
|
||||
.EXAMPLE
|
||||
.\release.ps1 -Version 0.1.54 -Notes "Faster home screen" -BaseUrl https://nas.example.com/memby
|
||||
#>
|
||||
param(
|
||||
[string] $Version,
|
||||
[string] $Notes = '',
|
||||
[Parameter(Mandatory = $true)][string] $BaseUrl
|
||||
)
|
||||
|
||||
$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]
|
||||
|
||||
$gradle = $gradle -replace 'versionCode = \d+', "versionCode = $code"
|
||||
$gradle = $gradle -replace 'versionName = "[^"]*"', "versionName = `"$Version`""
|
||||
Set-Content -LiteralPath $gradleFile -Value $gradle -NoNewline
|
||||
Write-Host "Set version $Version (versionCode $code)" -ForegroundColor Cyan
|
||||
} else {
|
||||
if ($gradle -notmatch 'versionName = "([^"]*)"') { throw 'Could not read versionName from app/build.gradle.kts' }
|
||||
$Version = $Matches[1]
|
||||
Write-Host "Building existing version $Version" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# --- build -----------------------------------------------------------------
|
||||
|
||||
& (Join-Path $root 'gradlew.bat') --console=plain clean test assembleRelease
|
||||
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)
|
||||
|
||||
# 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).
|
||||
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"
|
||||
Reference in New Issue
Block a user