mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 20:07:18 +01:00
Everything an app needs to move from GitHub to Firebase without each one reinventing the wiring. Python: - _config.py: FirebaseConfig.load() + firebase_client_for(app) + mirror_client_for(app, github). One config file, read in one place. Credential caches stay per-app (~/.config/<app>/firebase_auth.json) -- one file written by several processes is a corruption waiting to happen. - tool/preflight_firebase.py: validates the config before any data moves. Catches a wrong-region databaseUrl (which answers 404 with a correctUrl body rather than an error, so it would otherwise pass every check and break mid-migration) and confirms the uid in the ID token matches the uid the rules pin -- sign_in() returns None and discards localId, so the uid is decoded from the JWT. - tool/interop_seed.py: the missing Python half of the cross-language check. interop_check.dart asserts it can see a record written by Python, but nothing ever wrote one, so it could only ever fail. Dart: - firebase_config.dart: the same config shape, kept free of dart:io so it works on web and Android, where the file does not exist at all and credentials belong in flutter_secure_storage. - PersistedSyncStateStore: the revision cache, persisted. Without it a phone forgets every peer revision on each cold start and re-downloads everything -- the exact traffic the revision scheme exists to avoid, and what the free-tier headroom depends on. Verified live against the real database: preflight passes all five checks, the Dart smoke test passes end to end, and Python <-> Dart interop is OK. Python 226 tests at 100% branch coverage; Dart 218 tests, both new files at 100% line coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJ9sxEw3ymzXNjL7SHNpJc
81 lines
2.3 KiB
Python
81 lines
2.3 KiB
Python
"""Write the Python half of the live cross-language interop check.
|
|
|
|
``crdt_sync_dart/tool/interop_check.dart`` merges a Dart record into
|
|
``_interop/devices`` and then asserts it can also see a record written by the
|
|
*Python* client -- but nothing wrote that record, so the check could only ever
|
|
fail. This is the missing half: it pushes ``py-rec`` through the real
|
|
:class:`FirebaseSyncClient`, so the Dart tool has something to find.
|
|
|
|
The two implementations must agree byte-for-byte, or a phone running Dart
|
|
would silently not see a PC running Python. That is the failure this pair of
|
|
tools exists to catch before any real data depends on it.
|
|
|
|
Run this first, then the Dart checker::
|
|
|
|
python3 tool/interop_seed.py
|
|
dart run tool/interop_check.dart
|
|
|
|
Writes only under ``_interop/``; the Dart side deletes both devices' data on
|
|
its way out.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
|
|
from crdt_sync import (
|
|
FirebaseConfig,
|
|
Hlc,
|
|
MemorySyncStateStore,
|
|
Record,
|
|
dump_log,
|
|
firebase_client_for,
|
|
load_log,
|
|
sync_log,
|
|
)
|
|
|
|
_PATH_PREFIX = "_interop/devices"
|
|
_DEVICE_ID = "pydev"
|
|
|
|
_logger = logging.getLogger("interop-seed")
|
|
|
|
|
|
# The library's canonical serializers, deliberately -- the point of the check
|
|
# is that what the real apps write is what the other language reads, so
|
|
# reimplementing the encoding here would test the wrong thing.
|
|
|
|
|
|
def main() -> int:
|
|
"""Push the Python-side record, returning a process exit code."""
|
|
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(message)s")
|
|
|
|
client = firebase_client_for("interop", config=FirebaseConfig.load())
|
|
merged = sync_log(
|
|
client=client,
|
|
device_id=_DEVICE_ID,
|
|
path_prefix=_PATH_PREFIX,
|
|
local_log={
|
|
"py-rec": Record(
|
|
id="py-rec",
|
|
fields={
|
|
"value": (
|
|
"written-by-python",
|
|
Hlc(wall_time_ms=1000, counter=0, node_id="node-py"),
|
|
)
|
|
},
|
|
)
|
|
},
|
|
encode=dump_log,
|
|
decode=load_log,
|
|
state_store=MemorySyncStateStore(),
|
|
)
|
|
|
|
_logger.info("python sees records: %s", sorted(merged))
|
|
_logger.info("seeded %s; now run: dart run tool/interop_check.dart", _DEVICE_ID)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|