Prepare Frame Control 0.2.0 for public testing

- Without the maintainer's key, Android compatibility reports stay on the
  Mac and the UI says so; the shared database is never contacted.
- Remove personal infrastructure details from scripts and docs: the Drive
  folder and gog wrapper now come from the environment, and the Chromium
  build host is required instead of defaulted.
- Add an MIT license, tester instructions in the README, and bump to 0.2.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-26 07:37:37 +10:00
1 parent f324aac690
commit 60571dbfac
16 files changed
+192 -57

No files matched your search

+15 -2
View File
@@ -40,6 +40,16 @@ def key():
return p.stdout.strip()
def shared():
"""Whether reports reach the shared database. Without the key (anyone but the
maintainer), reports stay in this Mac's outbox and ratings come from the catalogue."""
try:
key()
return True
except DBError:
return False
class _NoRedirect(urllib.request.HTTPRedirectHandler):
"""Never follow redirects: urllib would copy the key header to the new host."""
def redirect_request(self, *args, **kwargs):
@@ -169,6 +179,8 @@ def load():
now = time.time()
if _mem['reports'] is None or now - _mem['at'] > TTL:
try:
if not shared():
raise DBError('no key')
try:
flush()
except Exception:
@@ -196,8 +208,9 @@ def add(report):
with _lock, open(OUTBOX, 'a') as f:
f.write(json.dumps(r, ensure_ascii=False) + '\n')
try:
flush()
_mem['at'] = 0 # refetch on next load
if shared():
flush()
_mem['at'] = 0 # refetch on next load
except Exception:
pass # stays queued; load() shows it and a later call sends it
return r
+7 -4
View File
@@ -434,8 +434,8 @@
<div class="panel">
<div class="shelf-head"><h2>Recent reports</h2><span class="count" id="repCount"></span></div>
<div class="list" id="repList"><div class="sub">Loading…</div></div>
<div class="hint">Reports go to Frame Control's private compatibility database and change the
verdicts in the catalogue. Any APK can be reported, including ones not on F-Droid.</div>
<div class="hint" id="repHint">Reports change the verdicts in the catalogue. Any APK can be
reported, including ones not on F-Droid.</div>
</div>
</div>
<div class="panel">
@@ -1456,9 +1456,12 @@ const RLABEL = { works: "Works", issues: "Problems", broken: "Doesn't work", run
crashes: "Crashed (test)", install_failed: "Won't install", instance_failed: "Didn't start (test)" };
const RCLASS = { works: "works", runs: "works", issues: "maybe" };
async function loadReports() {
let reps;
try { reps = (await api("/api/android/reports")).reports; }
let reps, shared;
try { ({ reports: reps, shared } = await api("/api/android/reports")); }
catch (e) { $("repList").innerHTML = `<div class="sub">${esc(e.message)}</div>`; return; }
$("repHint").textContent = shared
? "Reports go to Frame Control's shared compatibility database and change the verdicts in the catalogue. Any APK can be reported, including ones not on F-Droid."
: "Reports are saved on this Mac and change the verdicts you see. They aren't uploaded: the shared database is maintainer-only for now. Any APK can be reported, including ones not on F-Droid.";
$("repCount").textContent = reps.length ? `${reps.length} newest` : "";
$("repList").innerHTML = reps.length ? reps.slice(0, 40).map(r => {
const k = r.rating || r.result;
+4 -2
View File
@@ -438,7 +438,8 @@ def android(body):
runtime=body.get("runtime") or "instance",
label=body.get("label"), source=body.get("source"))
name = r.get("label") or pkg
return {"message": f"Saved your report for {name}", "report": r}
where = "" if frame_catalog.compat_db.shared() else " on this Mac"
return {"message": f"Saved your report for {name}{where}", "report": r}
except frame_android.FrameError as e:
raise Failure(str(e))
raise Failure("unknown action", 400)
@@ -793,7 +794,8 @@ class Handler(BaseHTTPRequestHandler):
elif path == "/api/android/displays":
self.send_json(android_displays())
elif path == "/api/android/reports":
self.send_json({"reports": frame_catalog.recent_reports()})
self.send_json({"reports": frame_catalog.recent_reports(),
"shared": frame_catalog.compat_db.shared()})
elif path == "/api/android/catalog":
self.send_json({"apps": frame_catalog.catalog()})
elif path == "/api/status":