202 lines
5.9 KiB
PowerShell
202 lines
5.9 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Builds, verifies, and installs a signed Memby release on an Android TV.
|
|
|
|
.DESCRIPTION
|
|
Uses the permanent signing configuration stored in the current Windows user's
|
|
MEMBY_KEYSTORE environment variables. The APK is installed directly with ADB;
|
|
no Gitea release or update server is required.
|
|
|
|
.EXAMPLE
|
|
.\deploy-tv.ps1
|
|
|
|
.EXAMPLE
|
|
.\deploy-tv.ps1 -Device 10.0.0.238:35343 -SkipTests
|
|
#>
|
|
|
|
#Requires -Version 7.2
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter()]
|
|
[ValidatePattern('^[A-Za-z0-9._:-]+$')]
|
|
[string] $Device = '10.0.0.238:35343',
|
|
|
|
[Parameter()]
|
|
[switch] $SkipTests
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$root = $PSScriptRoot
|
|
$packageName = 'com.ponzischeme89.memby'
|
|
|
|
function Invoke-Checked {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $FilePath,
|
|
|
|
[Parameter()]
|
|
[string[]] $Arguments = @()
|
|
)
|
|
|
|
& $FilePath @Arguments
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "'$FilePath' failed with exit code $LASTEXITCODE."
|
|
}
|
|
}
|
|
|
|
function Get-AndroidSdk {
|
|
if ($env:ANDROID_HOME -and (Test-Path -LiteralPath $env:ANDROID_HOME)) {
|
|
return $env:ANDROID_HOME
|
|
}
|
|
|
|
$propertiesPath = Join-Path $root 'local.properties'
|
|
if (Test-Path -LiteralPath $propertiesPath) {
|
|
$sdkLine = Get-Content -LiteralPath $propertiesPath |
|
|
Where-Object { $_ -match '^sdk\.dir=' } |
|
|
Select-Object -First 1
|
|
if ($sdkLine) {
|
|
$sdkPath = $sdkLine.Substring($sdkLine.IndexOf('=') + 1).
|
|
Replace('\:', ':').
|
|
Replace('\\', '\')
|
|
if (Test-Path -LiteralPath $sdkPath) {
|
|
return $sdkPath
|
|
}
|
|
}
|
|
}
|
|
|
|
throw 'Android SDK not found. Set ANDROID_HOME or sdk.dir in local.properties.'
|
|
}
|
|
|
|
function Import-UserSigningEnvironment {
|
|
foreach ($name in @(
|
|
'MEMBY_KEYSTORE',
|
|
'MEMBY_KEYSTORE_PASSWORD',
|
|
'MEMBY_KEY_ALIAS',
|
|
'MEMBY_KEY_PASSWORD'
|
|
)) {
|
|
$value = [Environment]::GetEnvironmentVariable($name, 'Process')
|
|
if ([string]::IsNullOrWhiteSpace($value)) {
|
|
$value = [Environment]::GetEnvironmentVariable($name, 'User')
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($value)) {
|
|
throw "Release signing variable $name is not configured."
|
|
}
|
|
[Environment]::SetEnvironmentVariable($name, $value, 'Process')
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $env:MEMBY_KEYSTORE -PathType Leaf)) {
|
|
throw "Release keystore not found: $env:MEMBY_KEYSTORE"
|
|
}
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host '╭─ Memby TV deployment' -ForegroundColor Magenta
|
|
Write-Host "│ Device $Device" -ForegroundColor DarkGray
|
|
Write-Host '│ Build signed release' -ForegroundColor DarkGray
|
|
Write-Host '╰─' -ForegroundColor Magenta
|
|
Write-Host ''
|
|
|
|
Import-UserSigningEnvironment
|
|
$sdk = Get-AndroidSdk
|
|
$adb = Join-Path $sdk 'platform-tools\adb.exe'
|
|
if (-not (Test-Path -LiteralPath $adb -PathType Leaf)) {
|
|
throw "ADB not found: $adb"
|
|
}
|
|
|
|
$buildTools = Get-ChildItem -LiteralPath (Join-Path $sdk 'build-tools') -Directory |
|
|
Sort-Object { [version]$_.Name } -Descending |
|
|
Select-Object -First 1
|
|
if (-not $buildTools) {
|
|
throw 'Android SDK Build Tools are not installed.'
|
|
}
|
|
$apkSigner = Join-Path $buildTools.FullName 'apksigner.bat'
|
|
if (-not (Test-Path -LiteralPath $apkSigner -PathType Leaf)) {
|
|
throw "APK signer not found: $apkSigner"
|
|
}
|
|
|
|
$jdkHome = 'C:\Program Files\Android\Android Studio\jbr'
|
|
if (Test-Path -LiteralPath $jdkHome) {
|
|
$env:JAVA_HOME = $jdkHome
|
|
}
|
|
|
|
Write-Host '● Building release APK' -ForegroundColor Cyan
|
|
$gradleArguments = @('--console=plain')
|
|
if (-not $SkipTests) {
|
|
$gradleArguments += 'testDebugUnitTest'
|
|
}
|
|
$gradleArguments += 'assembleRelease'
|
|
Invoke-Checked -FilePath (Join-Path $root 'gradlew.bat') -Arguments $gradleArguments
|
|
|
|
$apk = Join-Path $root 'app\build\outputs\apk\release\app-release.apk'
|
|
if (-not (Test-Path -LiteralPath $apk -PathType Leaf)) {
|
|
$unsigned = Join-Path $root 'app\build\outputs\apk\release\app-release-unsigned.apk'
|
|
if (Test-Path -LiteralPath $unsigned) {
|
|
throw 'Gradle produced an unsigned APK; deployment was stopped.'
|
|
}
|
|
throw "Release APK not found: $apk"
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host '● Verifying release signature' -ForegroundColor Cyan
|
|
Invoke-Checked -FilePath $apkSigner -Arguments @(
|
|
'verify',
|
|
'--verbose',
|
|
'--print-certs',
|
|
$apk
|
|
)
|
|
|
|
Write-Host ''
|
|
Write-Host '● Connecting to Android TV' -ForegroundColor Cyan
|
|
& $adb connect $Device
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Unable to connect to Android TV at $Device."
|
|
}
|
|
Invoke-Checked -FilePath $adb -Arguments @('-s', $Device, 'get-state')
|
|
|
|
Write-Host ''
|
|
Write-Host '● Installing release' -ForegroundColor Cyan
|
|
Invoke-Checked -FilePath $adb -Arguments @(
|
|
'-s', $Device,
|
|
'install',
|
|
'-r',
|
|
'--no-streaming',
|
|
$apk
|
|
)
|
|
|
|
$packageDetails = & $adb -s $Device shell dumpsys package $packageName
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw 'The APK installed, but its package details could not be read.'
|
|
}
|
|
$versionName = (
|
|
$packageDetails |
|
|
Select-String -Pattern 'versionName=([^\s]+)' |
|
|
Select-Object -First 1
|
|
).Matches.Groups[1].Value
|
|
if ([string]::IsNullOrWhiteSpace($versionName)) {
|
|
throw "Installed package $packageName was not found."
|
|
}
|
|
|
|
$resolveOutput = & $adb -s $Device shell cmd package resolve-activity --brief `
|
|
-a android.intent.action.MAIN `
|
|
-c android.intent.category.LEANBACK_LAUNCHER `
|
|
$packageName
|
|
$component = $resolveOutput |
|
|
Where-Object { $_.Trim() -match '^[A-Za-z0-9._]+/[A-Za-z0-9._$]+$' } |
|
|
Select-Object -Last 1
|
|
if ($LASTEXITCODE -eq 0 -and $component) {
|
|
$component = $component.Trim()
|
|
Invoke-Checked -FilePath $adb -Arguments @(
|
|
'-s', $Device,
|
|
'shell', 'am', 'start',
|
|
'-n', $component
|
|
)
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host "✓ Memby $versionName installed on $Device" -ForegroundColor Green
|
|
Write-Host " Package: $packageName" -ForegroundColor DarkGray
|
|
Write-Host " APK: $apk" -ForegroundColor DarkGray
|