151 lines
6.0 KiB
PowerShell
151 lines
6.0 KiB
PowerShell
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidatePattern('^\d+\.\d+\.\d+$')]
|
|
[string]$VersionName,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateRange(1, 2147483647)]
|
|
[int]$VersionCode,
|
|
|
|
[string]$ReleaseNotes = 'Healer Man Android update.',
|
|
[string]$GiteaBaseUrl = 'http://192.168.1.180:30008',
|
|
[string]$GiteaPublicBaseUrl = 'https://git.whoagland.com',
|
|
[string]$Owner = 'phenom',
|
|
[string]$Repository = 'healer-man'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$requiredSecrets = @(
|
|
'ANDROID_KEYSTORE_FILE',
|
|
'ANDROID_KEYSTORE_PASSWORD',
|
|
'ANDROID_KEY_ALIAS',
|
|
'ANDROID_KEY_PASSWORD',
|
|
'GITEA_TOKEN'
|
|
)
|
|
foreach ($secret in $requiredSecrets) {
|
|
if (-not [Environment]::GetEnvironmentVariable($secret)) {
|
|
throw "Required environment variable $secret is not set."
|
|
}
|
|
}
|
|
|
|
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
$packageVersion = (Get-Content -LiteralPath (Join-Path $projectRoot 'package.json') -Raw | ConvertFrom-Json).version
|
|
$releaseVersionPath = Join-Path $projectRoot 'release-version.json'
|
|
if (-not (Test-Path -LiteralPath $releaseVersionPath)) {
|
|
throw "Canonical release metadata is missing: $releaseVersionPath"
|
|
}
|
|
$releaseVersion = Get-Content -LiteralPath $releaseVersionPath -Raw | ConvertFrom-Json
|
|
if ($packageVersion -ne $VersionName -or $releaseVersion.versionName -ne $VersionName) {
|
|
throw "VersionName $VersionName does not match package.json and release-version.json."
|
|
}
|
|
if ([int]$releaseVersion.versionCode -ne $VersionCode) {
|
|
throw "VersionCode $VersionCode does not match release-version.json ($($releaseVersion.versionCode))."
|
|
}
|
|
$env:ANDROID_VERSION_NAME = $VersionName
|
|
$env:ANDROID_VERSION_CODE = [string]$VersionCode
|
|
|
|
Push-Location $projectRoot
|
|
try {
|
|
$initialGitStatus = & git status --porcelain
|
|
if ($LASTEXITCODE -ne 0) { throw "Unable to inspect the Git working tree." }
|
|
if ($initialGitStatus) { throw "APK publication requires a clean Git working tree." }
|
|
|
|
& npm run android:sync
|
|
if ($LASTEXITCODE -ne 0) { throw "Android web build and Capacitor sync failed with exit code $LASTEXITCODE" }
|
|
$postSyncGitStatus = & git status --porcelain --untracked-files=no
|
|
if ($LASTEXITCODE -ne 0) { throw "Unable to inspect Android sync changes." }
|
|
if ($postSyncGitStatus) {
|
|
throw "Android sync changed tracked files. Review and commit them before publishing the APK."
|
|
}
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/runAndroidGradle.ps1 assembleRelease
|
|
if ($LASTEXITCODE -ne 0) { throw "Android release build failed with exit code $LASTEXITCODE" }
|
|
|
|
$apkName = "healer-man-$VersionName-release.apk"
|
|
$apkPath = Join-Path $projectRoot "android\app\build\outputs\apk\release\$apkName"
|
|
if (-not (Test-Path -LiteralPath $apkPath)) { throw "Expected release APK was not created: $apkPath" }
|
|
$checksumPath = "$apkPath.sha256"
|
|
$checksum = (Get-FileHash -LiteralPath $apkPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
[IO.File]::WriteAllText($checksumPath, "$checksum $apkName`n", [Text.UTF8Encoding]::new($false))
|
|
|
|
$apiRoot = $GiteaBaseUrl.TrimEnd('/') + '/api/v1'
|
|
$headers = @{ Authorization = "token $($env:GITEA_TOKEN.Trim())" }
|
|
$tag = "v$VersionName"
|
|
$releaseBody = @{
|
|
tag_name = $tag
|
|
target_commitish = 'main'
|
|
name = "Healer Man $VersionName"
|
|
body = $ReleaseNotes
|
|
draft = $false
|
|
prerelease = $false
|
|
} | ConvertTo-Json
|
|
$encodedTag = [Uri]::EscapeDataString($tag)
|
|
try {
|
|
$release = Invoke-RestMethod `
|
|
-Method Get `
|
|
-Uri "$apiRoot/repos/$Owner/$Repository/releases/tags/$encodedTag" `
|
|
-Headers $headers
|
|
Write-Host "Resuming existing Gitea release $tag."
|
|
} catch {
|
|
$statusCode = if ($_.Exception.Response) { [int]$_.Exception.Response.StatusCode } else { 0 }
|
|
if ($statusCode -ne 404) { throw }
|
|
$release = Invoke-RestMethod `
|
|
-Method Post `
|
|
-Uri "$apiRoot/repos/$Owner/$Repository/releases" `
|
|
-Headers $headers `
|
|
-ContentType 'application/json' `
|
|
-Body $releaseBody
|
|
}
|
|
|
|
foreach ($attachment in @($apkPath, $checksumPath)) {
|
|
$attachmentName = Split-Path -Leaf $attachment
|
|
$attachmentSize = (Get-Item -LiteralPath $attachment).Length
|
|
$encodedName = [Uri]::EscapeDataString($attachmentName)
|
|
$release = Invoke-RestMethod `
|
|
-Method Get `
|
|
-Uri "$apiRoot/repos/$Owner/$Repository/releases/$($release.id)" `
|
|
-Headers $headers
|
|
$existingAsset = @($release.assets | Where-Object { $_.name -eq $attachmentName }) |
|
|
Select-Object -First 1
|
|
if ($existingAsset) {
|
|
if ([long]$existingAsset.size -ne $attachmentSize) {
|
|
throw "Release asset $attachmentName exists with the wrong size ($($existingAsset.size), expected $attachmentSize)."
|
|
}
|
|
Write-Host "Release asset $attachmentName already exists with the expected size; skipping upload."
|
|
continue
|
|
}
|
|
|
|
$uploaded = $false
|
|
for ($attempt = 1; $attempt -le 3 -and -not $uploaded; $attempt++) {
|
|
try {
|
|
Invoke-RestMethod `
|
|
-Method Post `
|
|
-Uri "$apiRoot/repos/$Owner/$Repository/releases/$($release.id)/assets?name=$encodedName" `
|
|
-Headers $headers `
|
|
-ContentType 'application/octet-stream' `
|
|
-InFile $attachment | Out-Null
|
|
$uploaded = $true
|
|
} catch {
|
|
$refreshedRelease = Invoke-RestMethod `
|
|
-Method Get `
|
|
-Uri "$apiRoot/repos/$Owner/$Repository/releases/$($release.id)" `
|
|
-Headers $headers
|
|
$uploadedAsset = @($refreshedRelease.assets | Where-Object {
|
|
$_.name -eq $attachmentName -and [long]$_.size -eq $attachmentSize
|
|
}) | Select-Object -First 1
|
|
if ($uploadedAsset) {
|
|
$uploaded = $true
|
|
continue
|
|
}
|
|
if ($attempt -eq 3) { throw }
|
|
Write-Warning "Upload attempt $attempt for $attachmentName failed; retrying through the LAN API."
|
|
Start-Sleep -Seconds ([math]::Pow(2, $attempt))
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "Published $tag with $apkName and its SHA-256 checksum."
|
|
Write-Host "$($GiteaPublicBaseUrl.TrimEnd('/'))/$Owner/$Repository/releases/tag/$tag"
|
|
} finally {
|
|
Pop-Location
|
|
}
|