mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-13 15:51:01 +01:00
966ec60f17
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 3589d166cd |
crdt-sync: split five source files — package now fully under the cap
Some checks are pending
crdt-sync pre-commit / pre-commit (push) Waiting to run
crdt-sync tests / test (3.10) (push) Waiting to run
crdt-sync tests / test (3.11) (push) Waiting to run
crdt-sync tests / test (3.12) (push) Waiting to run
gatelock pre-commit / pre-commit (push) Waiting to run
gatelock tests / test (3.10) (push) Waiting to run
gatelock tests / test (3.11) (push) Waiting to run
gatelock tests / test (3.12) (push) Waiting to run
palette drift / palette (push) Waiting to run
_sync.py 456 -> 245 + _syncstate 131 + _pull 106 + _revisions 34 _firebase_auth 412 -> 242 + _credentials 146 + _autherrors 69 _firebase.py 336 -> 250 + _fbkeys 100 google_id_token 316 -> 232 + tool/_oauth_callback 105 _github.py 255 -> 238 + _gherrors 30 revision_of became its own leaf module because both the push and pull halves need it; without that the two would have imported each other. _fbkeys holds the RTDB key escaping — the escape tables moved with the functions that read them, since the pair has to round-trip exactly or a device silently stops seeing its own writes. crdt-sync/tool/ is outside the coverage source, so the split there has no test net: verified by running it the documented way instead (`python3 -m tool.google_id_token --help` prints its usage, and `from tool.google_id_token import TokenError, fetch_id_token` — what seed_session.py does — still resolves). An AST pass over each pair caught what the suite would not have: _is_revoked needs the error type, so it stayed with it rather than following the credentials; _TERMINAL_AUTH_REASONS and _CONSENT_TIMEOUT_SECONDS had to be carried to their new callers. 259 passed at 100% branch coverage. crdt-sync/tool/seed_session.py and firebase-debug.log are pre-existing local changes and are deliberately not in this commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CFSGeWxHPY5rHUGrPqhcmG |
|||
| 2005c40baa |
Group sync_log's 11 arguments into three bundles
Some checks are pending
BREAKING: sync_log's flat keyword arguments are replaced by SyncTarget, LogCodec and RevisionTracking. Callers change from eleven loose keywords to four positional arguments; every consumer repo is updated in the same pass. Eleven parameters on one function was the last real pylint finding in the package, and the grouping is the fix rather than a workaround: the three bundles are the three things a tick actually needs -- where to sync, how to serialize, and whether to track revisions -- and each documents its own fields. SyncTarget.own_ids also moves the "this device plus its pre-migration id" rule onto the type that owns it, instead of recomputing it inline. Internals follow: _pull_remote_logs takes a _PullContext instead of eight positionals, and the push half splits into _merge_peers/_push, which is what clears the 21-locals finding. The two duplicated in-memory fakes merge into one FakeStore in conftest, as does the no-bulk-read wrapper around it -- the shared half was the fiddly prefix slicing, which is exactly where two copies drift. crdt_sync now scores a clean 10.00/10 on pylint. Two suppressions remain, both scoped and explained at the line: asserting on a private attribute IS the timeout test, and reversing merge_record's operands IS the commutativity test. 259 tests, still 100% branch coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCucBhUkHBBHHbsY7bSdGV |
|||
| 5f64a8959b |
Sign in with Google, and stop trusting a revoked session
Two changes to the shared sync credential path, both driven by devices that reported "Connected" while every sync failed. Google sign-in (signInWithGoogle, both languages) replaces typing the sync account's password on a fresh install. The account is linked once from the PC with tool/link_google.py, which asserts the pinned uid -- without that assertion an unlinked identity is *signed up* rather than refused, and the new uid is then denied every read and write by the security rules. The second change is the one with teeth. hasSession() is a presence check, so a refresh token the server had revoked still read as a live session: the desktop todo build showed "Connected" and then failed every sync with `HTTP 400 (TOKEN_EXPIRED)`. A refresh that fails for a terminal reason now clears the stored session, so hasSession() answers honestly. Deliberately narrow: only TOKEN_EXPIRED, USER_DISABLED, USER_NOT_FOUND, INVALID_REFRESH_TOKEN, INVALID_GRANT_TYPE and MISSING_REFRESH_TOKEN clear it. A network error or a 5xx must not -- a device that signs itself out whenever the wifi drops needs a manual sign-in on every device to recover, which is worse than the stale banner this fixes. Verified against the live endpoint, not just mocks: has_session flips True -> False on a real rejection. crdt_sync_dart 0.9.0 -> 0.10.0; the five apps pin the matching tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0192EkoAyFBEWTvAN5vVgmSy |
|||
| dd6e6d5571 |
crdt_sync 0.5.0: the shared config and the cutover seam
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 |
|||
| cfc4c295e4 |
crdt_sync: add the one-shot GitHub -> Firebase data migration
Reads through the GitHub API, not a local clone: the checkout on this
machine was five days stale while devices kept syncing, so seeding from it
would have silently rolled back everything written in between. Blobs are
fetched by sha rather than by path, so a concurrent push cannot swap the
content out from under the tree listing.
Writes through the real FirebaseSyncClient so the migration exercises the
same key escaping the apps will, then reads every blob back and compares
bytes before reporting success -- a migration that claimed success without
checking would be indistinguishable from one that dropped a file.
Retries connection resets and 5xx: GitHub drops keep-alive connections
routinely, and a half-completed migration is worse than one that never
started.
Three things are deliberately left behind, and stay in the GitHub repo
(archived, not deleted):
- todo-sync/changesets/ pre-migration sqlite_crdt format, unread since
2026-07-17
- diet-guard-sync/devices/desktop/ a replica that stopped syncing on
2026-07-22; verified to hold 0 records, 0 tombstones and 0 fields newer
than both `pc` and `phone`. Carrying it over would cost a full
re-download every tick forever, since a device that publishes no
revision can never be skipped
- READMEs and .gitkeep, which are not sync data
Run against the live database: 16 files, 956,272 bytes, all verified
byte-for-byte, and independently re-checked by listing the namespaces and
parsing records back out.
|