feat(android): in-app updater (backend/Gitea APK), auto-bump versionCode
The app checks {backend}/api/apk/info on launch and, if a newer versionCode
than BuildConfig.VERSION_CODE is offered, shows an update dialog that
downloads {backend}/api/apk into a single wiped-each-time cache file and
hands it to the system installer (FileProvider + REQUEST_INSTALL_PACKAGES).
No APK hoarding; in-place install preserves data (stable signing key needed).
- backend /api/apk/info now returns versionCode (parsed from the release body).
- publish-apk.mjs auto-increments versionCode, sets versionName, and writes
versionCode/versionName into the Gitea release body.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
845f125d76
commit
2fd4d978d0
7 files changed
+175
-14
No files matched your search
+22
-11
@@ -17,7 +17,7 @@
|
||||
// install over each other. Add a keystore + signingConfig later to switch APK_GRADLE_TASK=assembleRelease.
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
@@ -45,13 +45,24 @@ if (!GITEA_URL || !GITEA_REPO || !GITEA_TOKEN)
|
||||
const API = `${GITEA_URL}/api/v1/repos/${GITEA_REPO}`;
|
||||
const authHeaders = { Authorization: `token ${GITEA_TOKEN}` };
|
||||
|
||||
// ── version name from the Gradle config (for the release title) ──────────────
|
||||
let version = new Date().toISOString().slice(0, 10);
|
||||
try {
|
||||
const g = readFileSync(join(root, 'android', 'app', 'build.gradle.kts'), 'utf8');
|
||||
const m = g.match(/versionName\s*=\s*"([^"]+)"/);
|
||||
if (m) version = m[1];
|
||||
} catch {}
|
||||
// ── bump versionCode + read versionName from the Gradle config ───────────────
|
||||
// The in-app updater compares versionCode, so every published build MUST increment it — we do that
|
||||
// here automatically (pass --version X.Y to also set the versionName). Commit the changed
|
||||
// build.gradle.kts afterwards so the repo reflects what's live.
|
||||
const gradlePath = join(root, 'android', 'app', 'build.gradle.kts');
|
||||
let gradle = readFileSync(gradlePath, 'utf8');
|
||||
const curCode = parseInt(gradle.match(/versionCode\s*=\s*(\d+)/)?.[1] || '0', 10);
|
||||
const versionCode = curCode + 1;
|
||||
const versionArg = process.argv.slice(2).find(a => a.startsWith('--version='))?.split('=')[1]
|
||||
|| (process.argv.includes('--version') ? process.argv[process.argv.indexOf('--version') + 1] : null);
|
||||
let version = versionArg || gradle.match(/versionName\s*=\s*"([^"]+)"/)?.[1] || `0.${versionCode}`;
|
||||
gradle = gradle
|
||||
.replace(/versionCode\s*=\s*\d+/, `versionCode = ${versionCode}`)
|
||||
.replace(/versionName\s*=\s*"[^"]*"/, `versionName = "${version}"`);
|
||||
writeFileSync(gradlePath, gradle);
|
||||
console.log(`▶ versionCode → ${versionCode}, versionName → ${version} (build.gradle.kts danach committen!)`);
|
||||
|
||||
const relBody = `versionCode=${versionCode}\nversionName=${version}`;
|
||||
|
||||
// ── 1) build ─────────────────────────────────────────────────────────────────
|
||||
const isWin = process.platform === 'win32';
|
||||
@@ -86,7 +97,7 @@ let rel;
|
||||
const cr = await api('/releases', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ tag_name: TAG, target: 'main', name: `NovaSonic ${version}`, body: 'NovaSonic Android App — neueste Version.' }),
|
||||
body: JSON.stringify({ tag_name: TAG, target: 'main', name: `NovaSonic ${version}`, body: relBody }),
|
||||
});
|
||||
if (!cr.ok) die(`Release anlegen fehlgeschlagen: ${cr.status} ${await cr.text()}`);
|
||||
rel = await cr.json();
|
||||
@@ -108,11 +119,11 @@ form.append('attachment', new Blob([apkBytes], { type: 'application/vnd.android.
|
||||
const up = await api(`/releases/${rel.id}/assets?name=NovaSonic.apk`, { method: 'POST', body: form });
|
||||
if (!up.ok) die(`Upload fehlgeschlagen: ${up.status} ${await up.text()}`);
|
||||
|
||||
// keep the release title in sync with the version
|
||||
// keep the release title + versionCode body in sync
|
||||
await api(`/releases/${rel.id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: `NovaSonic ${version}` }),
|
||||
body: JSON.stringify({ name: `NovaSonic ${version}`, body: relBody }),
|
||||
}).catch(() => {});
|
||||
|
||||
console.log(`\n✔ NovaSonic ${version} hochgeladen → Release "${TAG}". Der Web-Button serviert jetzt diese APK.`);
|
||||
Reference in new issue
Block a user