65 lines
3.0 KiB
JavaScript
65 lines
3.0 KiB
JavaScript
// Publishes a live update of the Android app's web part:
|
|
// 1. builds the Capacitor web bundle (its version comes from the build time),
|
|
// 2. zips it into updates/android/bundle-<version>.zip,
|
|
// 3. writes updates/android/version.json (version, zip, SHA-256, minimum native build).
|
|
// The mobile-client dev/preview server serves updates/android at /m/android/, i.e.
|
|
// https://dev.aggtaa.com/m/android/version.json. Installed apps pick the update up on the next check.
|
|
//
|
|
// minNativeBuild is the versionCode of the latest published APK (updates/android/apk.json, see
|
|
// publish-android-apk.mjs): after an APK release, web updates go only to that APK or newer, older
|
|
// installations are offered the APK instead. Publish an APK whenever the native part changes
|
|
// (plugins, AndroidManifest, Java code).
|
|
import { execSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { zipSync } from 'fflate';
|
|
|
|
const root = path.resolve(import.meta.dirname, '..');
|
|
const distDir = path.join(root, 'dist-capacitor');
|
|
const outDir = path.join(root, 'updates', 'android');
|
|
const KEEP_BUNDLES = 3;
|
|
|
|
execSync('pnpm exec vite build --mode capacitor', { cwd: root, stdio: 'inherit' });
|
|
|
|
const { version } = JSON.parse(readFileSync(path.join(distDir, 'cardy-version.json'), 'utf8'));
|
|
|
|
function collect(dir, prefix = '') {
|
|
const files = {};
|
|
for (const name of readdirSync(dir)) {
|
|
const full = path.join(dir, name);
|
|
const rel = prefix ? `${prefix}/${name}` : name;
|
|
if (statSync(full).isDirectory()) Object.assign(files, collect(full, rel));
|
|
else files[rel] = readFileSync(full);
|
|
}
|
|
return files;
|
|
}
|
|
const zip = Buffer.from(zipSync(collect(distDir), { level: 6 }));
|
|
const checksum = createHash('sha256').update(zip).digest('hex');
|
|
|
|
let minNativeBuild = 1;
|
|
try {
|
|
minNativeBuild = Number(JSON.parse(readFileSync(path.join(outDir, 'apk.json'), 'utf8')).versionCode) || 1;
|
|
} catch {
|
|
// no APK published yet: any installed APK can take the bundle
|
|
}
|
|
|
|
mkdirSync(outDir, { recursive: true });
|
|
const zipName = `bundle-${version}.zip`;
|
|
writeFileSync(path.join(outDir, zipName), zip);
|
|
// version.json last: clients never see a version whose zip is not there yet.
|
|
writeFileSync(
|
|
path.join(outDir, 'version.json'),
|
|
JSON.stringify({ version, url: zipName, checksum, minNativeBuild, publishedAt: new Date().toISOString() }, null, 2),
|
|
);
|
|
|
|
// Keep a few recent bundles (clients may be mid-download of the previous one).
|
|
const bundles = readdirSync(outDir)
|
|
.filter((f) => /^bundle-.+\.zip$/.test(f))
|
|
.sort((a, b) => statSync(path.join(outDir, b)).mtimeMs - statSync(path.join(outDir, a)).mtimeMs);
|
|
for (const old of bundles.slice(KEEP_BUNDLES)) unlinkSync(path.join(outDir, old));
|
|
|
|
console.log(`\nPublished Android update ${version} (${(zip.length / 1024).toFixed(0)} KiB, native build >= ${minNativeBuild})`);
|
|
console.log(` ${path.join(outDir, zipName)}`);
|
|
console.log('Served at /m/android/version.json by the mobile-client dev/preview server.');
|