Files
Linus Rath 365ffefd33 chore: add Android release CI and sync local work
CI:
- .github/workflows/release-android.yml builds APK on release publish
  and via workflow_dispatch, attaches it to the release.
- android/app/build.gradle: optional release signingConfig driven by
  Gradle properties (ANDROID_KEYSTORE_BASE64 secret etc.); falls back
  to the debug keystore when no secrets are set.
- .gitignore: untrack /android so the runner can build directly;
  ignore build outputs (build/, .cxx/), Metro/Expo dev logs, scratch.

Track android/ sources including BulwarkFcmPackage.kt and
google-services.json (Firebase Android keys are public per Google's
docs and intended to ship in the APK).

JS sync: push notifications + background task, vacation responder
(api + store), contact activity component, search utilities, settings
polish, calendar/contacts/email store and screen tweaks.

Tooling: scripts/poll-reload.mjs — Windows polling-watcher that hits
Metro /reload on file change (workaround for unreliable fs.watch
without Watchman).
2026-04-26 04:12:08 +02:00

49 lines
1.4 KiB
JavaScript

import { readdir, stat } from 'node:fs/promises';
import { watchFile } from 'node:fs';
import { join, extname } from 'node:path';
const ROOTS = ['src', 'App.tsx', 'index.ts', 'app.json'];
const EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.json']);
const INTERVAL = 500;
const METRO = 'http://localhost:8081/reload';
async function* walk(p) {
const s = await stat(p).catch(() => null);
if (!s) return;
if (s.isFile()) { yield p; return; }
for (const e of await readdir(p, { withFileTypes: true })) {
const f = join(p, e.name);
if (e.isDirectory()) {
if (e.name === 'node_modules' || e.name.startsWith('.')) continue;
yield* walk(f);
} else if (e.isFile() && EXTS.has(extname(e.name))) {
yield f;
}
}
}
let pending = false;
async function reload(file) {
if (pending) return;
pending = true;
try {
console.log(`change: ${file}`);
const r = await fetch(METRO, { method: 'GET' });
console.log(`reload -> ${r.status}`);
} catch (e) {
console.log(`reload failed: ${e.message}`);
}
setTimeout(() => { pending = false; }, 500);
}
const files = [];
for (const r of ROOTS) {
for await (const f of walk(r)) files.push(f);
}
console.log(`watching ${files.length} files (poll ${INTERVAL}ms)`);
for (const f of files) {
watchFile(f, { interval: INTERVAL }, (curr, prev) => {
if (curr.mtimeMs !== prev.mtimeMs) reload(f);
});
}