Require git provenance for Android Argus staging

This commit is contained in:
Курнат Андрей
2026-06-12 22:30:58 +03:00
parent 32f7c59bd4
commit 251391b1a9
4 changed files with 87 additions and 4 deletions
+3
View File
@@ -16,6 +16,9 @@ local.properties
argus-signing.local.properties
signing/
# Local temporary release scratch files
.codex-temp/
# Mac .DS_Store files
.DS_Store
+4 -3
View File
@@ -18,6 +18,7 @@
## Release Checklist
1. Build the stable APK with the same release key material through `QSFERA_RELEASE_KEYSTORE`, `QSFERA_RELEASE_KEYSTORE_PASSWORD`, `QSFERA_RELEASE_KEY_ALIAS`, and `QSFERA_RELEASE_KEY_PASSWORD`.
2. Verify the APK certificate before upload with `apksigner verify --print-certs`.
3. Publish to Argus only after certificate, package name, version code, and SHA-256 checks pass.
4. Verify the published manifest and download with `scripts/verify-production-state.ps1 -VerifyAndroidDownload` from the repository root.
2. Prepare Argus publication only from a clean release branch whose `HEAD` matches its upstream; `Android/scripts/prepare-argus-publication.ps1` enforces this by default and records repository root, branch, upstream, commit, and verification time in `argus-publication.json`. When the Android checkout is used from the pushed `QSfera.git` monorepo, run it from `Android/` with `-GitProvenanceRoot ..`.
3. Verify the APK certificate before upload with `apksigner verify --print-certs`.
4. Publish to Argus only after certificate, package name, version code, SHA-256, and git provenance checks pass.
5. Verify the published manifest and download with `scripts/verify-production-state.ps1 -VerifyAndroidDownload` from the repository root.
+79 -1
View File
@@ -9,8 +9,10 @@ param(
[string]$Notes,
[string]$RepositoryUrl = 'https://git.kusoft.xyz/sevenhill/QSfera.git',
[string]$HomepageUrl = 'https://qsfera.kusoft.xyz',
[string]$GitProvenanceRoot,
[switch]$Unlisted,
[switch]$SkipSigningInitialization
[switch]$SkipSigningInitialization,
[switch]$SkipGitProvenanceCheck
)
$ErrorActionPreference = 'Stop'
@@ -112,8 +114,83 @@ function Find-FirstTool([string]$AndroidHome, [string]$ToolName) {
return $tool.FullName
}
function Invoke-Git([string[]]$Arguments) {
$output = & git @Arguments 2>&1
if ($LASTEXITCODE -ne 0) {
throw "git $($Arguments -join ' ') failed with exit code $LASTEXITCODE. Output: $($output | Out-String)"
}
return @($output)
}
function Assert-GitReleaseProvenance([string]$RepositoryRoot) {
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
throw 'git was not found. Argus publication requires git provenance verification.'
}
$previousLocation = Get-Location
try {
Set-Location $RepositoryRoot
$insideWorkTree = (Invoke-Git @('rev-parse', '--is-inside-work-tree') | Select-Object -First 1).Trim()
if ($insideWorkTree -ne 'true') {
throw 'Argus publication must be prepared from inside a git work tree.'
}
$status = Invoke-Git @('status', '--porcelain')
if ($status.Count -gt 0) {
throw "Git work tree has uncommitted changes. Commit or stash all changes before preparing Argus publication."
}
$branch = (Invoke-Git @('rev-parse', '--abbrev-ref', 'HEAD') | Select-Object -First 1).Trim()
if ($branch -eq 'HEAD') {
throw 'Git HEAD is detached. Check out the release branch before preparing Argus publication.'
}
try {
$upstream = (Invoke-Git @('rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}') | Select-Object -First 1).Trim()
} catch {
throw "Git branch has no upstream. Push $branch and set its upstream before Argus publication."
}
Invoke-Git @('fetch', '--quiet')
$aheadBehind = (Invoke-Git @('rev-list', '--left-right', '--count', 'HEAD...@{u}') | Select-Object -First 1).Trim() -split '\s+'
$ahead = [int]$aheadBehind[0]
$behind = [int]$aheadBehind[1]
if ($ahead -ne 0 -or $behind -ne 0) {
throw "Git HEAD must match upstream before Argus publication. Branch $branch tracks $upstream, ahead=$ahead, behind=$behind."
}
return [ordered]@{
repositoryRoot = (Invoke-Git @('rev-parse', '--show-toplevel') | Select-Object -First 1).Trim()
branch = $branch
upstream = $upstream
commit = (Invoke-Git @('rev-parse', 'HEAD') | Select-Object -First 1).Trim()
verifiedAt = [DateTimeOffset]::UtcNow.ToString('O')
}
} finally {
Set-Location $previousLocation
}
}
$repoRoot = Split-Path -Parent $PSScriptRoot
Set-Location $repoRoot
$gitProvenanceRootPath = if ($GitProvenanceRoot) {
(Resolve-Path -LiteralPath $GitProvenanceRoot).Path
} else {
$repoRoot
}
$gitProvenance = if ($SkipGitProvenanceCheck) {
[ordered]@{
skipped = $true
repositoryRoot = $gitProvenanceRootPath
reason = 'SkipGitProvenanceCheck was set'
verifiedAt = [DateTimeOffset]::UtcNow.ToString('O')
}
} else {
Assert-GitReleaseProvenance $gitProvenanceRootPath
}
$javaHome = Resolve-JavaHome
$androidHome = Resolve-AndroidHome
@@ -251,6 +328,7 @@ $metadata = [ordered]@{
notes = if ($Notes) { $Notes } else { $null }
repositoryUrl = if ($RepositoryUrl) { $RepositoryUrl } else { $null }
homepageUrl = if ($HomepageUrl) { $HomepageUrl } else { $null }
git = $gitProvenance
androidPackageName = $packageName
androidVersionCode = [int]$versionCode
targetSdkVersion = [int]$targetSdk
+1
View File
@@ -24,6 +24,7 @@ Date: 2026-06-08.
- Android release builds now fail fast when release signing variables are missing, so unsigned release artifacts cannot be published by accident.
- Android version was raised to `1.3.12` with `versionCode` `40`.
- Android now has QSfera-specific Argus publishing scripts: local upload-keystore generation, signed APK staging, APK metadata verification, debug-certificate rejection, and Argus upload.
- Android Argus staging now enforces release git provenance by default: the work tree must be clean, the branch must have an upstream, `git fetch` must succeed, and `HEAD` must match `@{u}` before a stable APK is prepared for upload. The generated `argus-publication.json` records the verified repository root, branch, upstream, commit, and timestamp; current monorepo publication can verify the pushed root `QSfera.git` branch by running from `Android/` with `-GitProvenanceRoot ..`.
- Android `1.3.9`/`37` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `53ecc24e-106b-43a5-a39e-ffacd72a43f8`, package size `12226572`, and SHA-256 `cbd2c25328e395ea4da4e0f582feac39dec18d2d47b3e105d344904d8a24f017`.
- Android `1.3.10`/`38` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `8465ba6d-a008-45f5-9c70-e6cd2c94317a`, package size `12228464`, and SHA-256 `ce33a73ce122de6c9c797e7cb552e5493b628c60ddc98fda6a9e230e3c1ed7fe`.
- Android `1.3.11`/`39` was published to Argus as `qsfera-mobile` on 2026-06-10. Argus upload returned release id `3dbac5ad-b19c-46e6-b24f-8f22e1c466dd`, package size `12234884`, and SHA-256 `c9eae41bbaae421e1a69721e891d8431ce599684e0f655b71693a422aa83720a`.