mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-10 19:27:14 +01:00
Point sync at the consolidated utils/syncs monorepos
gatelock and diet-guard-sync were folded into shared monorepos: gatelock -> github.com/kuhyx/utils (subdirectory), diet-guard-sync -> github.com/kuhyx/syncs (subdirectory). Update the gatelock pip URL, SYNC_REPO_NAME, the devices-dir path prefix (PC and phone), and add a persisted-value migration so an already-configured phone picks up 'syncs' on next load. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
c43e37b09d
commit
a121da211d
@ -92,7 +92,7 @@ repos:
|
||||
additional_dependencies:
|
||||
- pytest
|
||||
- requests
|
||||
- gatelock @ git+https://github.com/kuhyx/gatelock@v0.1.0
|
||||
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.1.0#subdirectory=gatelock
|
||||
# Test suites intentionally use patterns (protected-access, magic-value
|
||||
# comparisons) that don't belong in the source-code 10/10 gate.
|
||||
exclude: ^(\.venv/)|(^|/)(tests/|conftest\.py)
|
||||
|
||||
@ -34,7 +34,7 @@ rather than in the unit file.
|
||||
`diet-guard-sync.timer` fires `python -m diet_guard sync` every ~15 minutes
|
||||
(headless, no `DISPLAY` needed — separate from the gate timer on purpose, see
|
||||
the unit file's comment for why). It pulls every other device's pushed log
|
||||
from the private `kuhyx/diet-guard-sync` GitHub repo (used as dumb file
|
||||
from the private `kuhyx/syncs` GitHub repo (under `diet-guard-sync/`, used as dumb file
|
||||
storage via the REST Contents API, not a git clone), merges with the local
|
||||
log (`_sync_merge.merge_logs`: union by `id`, tombstone wins, legacy
|
||||
`(time, desc)` dedup for pre-`id` entries), **re-signs every persisted entry**
|
||||
@ -48,7 +48,7 @@ skipping the re-sign would silently lose every phone-logged meal on the very
|
||||
next read.
|
||||
|
||||
Requires a one-time manual setup `install.sh` does **not** automate: create a
|
||||
fine-grained GitHub PAT scoped to `diet-guard-sync`'s contents (read/write),
|
||||
fine-grained GitHub PAT scoped to `syncs`'s contents (read/write),
|
||||
then save it to `~/.config/diet_guard/sync_token`, mode 600. Until that file
|
||||
exists, every sync tick is a harmless no-op that logs `sync not configured`.
|
||||
|
||||
@ -100,7 +100,7 @@ silently does **not** reach the running service.
|
||||
cross-repo file coupling (unlike wake_alarm, which reads
|
||||
`~/screen-locker/screen_locker/workout_log.json`). Safe to reason about in
|
||||
isolation, with one exception: `diet-guard-sync.timer` reads/writes the
|
||||
private `kuhyx/diet-guard-sync` GitHub repo (see "Cross-device sync" above)
|
||||
private `kuhyx/syncs` GitHub repo (see "Cross-device sync" above)
|
||||
and `~/.config/diet_guard/sync_token`.
|
||||
|
||||
## Commands
|
||||
|
||||
@ -258,7 +258,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Authorize in your browser — no token to paste. Syncs to '
|
||||
'kuhyx/diet-guard-sync by default.',
|
||||
'kuhyx/syncs by default.',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@ -395,7 +395,7 @@ class _ClientIdSetupDialogState extends State<_ClientIdSetupDialog> {
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'When you connect below, log in with the GitHub account that '
|
||||
'has write access to kuhyx/diet-guard-sync.',
|
||||
'has write access to kuhyx/syncs.',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
|
||||
@ -63,7 +63,7 @@ class GitHubClient {
|
||||
/// The repo owner/org (e.g. `"kuhyx"`).
|
||||
final String owner;
|
||||
|
||||
/// The repo name (e.g. `"diet-guard-sync"`).
|
||||
/// The repo name (e.g. `"syncs"`).
|
||||
final String repo;
|
||||
|
||||
/// The branch to read/write against.
|
||||
|
||||
@ -19,11 +19,12 @@ import 'package:diet_guard_app/services/github_client.dart';
|
||||
import 'package:diet_guard_app/services/log_storage_service.dart';
|
||||
import 'package:diet_guard_app/services/sync_merge.dart';
|
||||
|
||||
const _devicesDir = 'devices';
|
||||
const _devicesDir = 'diet-guard-sync/devices';
|
||||
|
||||
/// This device's id in the `devices/<id>/food_log.json` layout. The PC
|
||||
/// pushes under `pc` (`SYNC_DEVICE_ID` in `diet_guard/_constants.py`); the
|
||||
/// phone is the only other device in this design.
|
||||
/// This device's id in the `diet-guard-sync/devices/<id>/food_log.json`
|
||||
/// layout. The PC pushes under `pc` (`SYNC_DEVICE_ID` in
|
||||
/// `diet_guard/_constants.py`); the phone is the only other device in this
|
||||
/// design.
|
||||
const phoneDeviceId = 'phone';
|
||||
|
||||
String _deviceLogPath(String deviceId) =>
|
||||
|
||||
@ -26,7 +26,7 @@ class SyncSettings {
|
||||
/// The repo owner/org (e.g. `"kuhyx"`).
|
||||
final String owner;
|
||||
|
||||
/// The repo name (e.g. `"diet-guard-sync"`).
|
||||
/// The repo name (e.g. `"syncs"`).
|
||||
final String repo;
|
||||
|
||||
/// A GitHub PAT with contents read/write on [owner]/[repo].
|
||||
@ -66,15 +66,25 @@ class SyncSettings {
|
||||
/// path on Android and use libsecret on Linux.
|
||||
static const _secure = FlutterSecureStorage();
|
||||
|
||||
/// Loads settings, defaulting the owner/repo to `kuhyx/diet-guard-sync`
|
||||
/// Loads settings, defaulting the owner/repo to `kuhyx/syncs`
|
||||
/// (matching the PC's `SYNC_REPO_OWNER`/`SYNC_REPO_NAME` constants) and the
|
||||
/// client id to the baked-in [defaultClientId], so a fresh install needs
|
||||
/// only "Connect GitHub" (once an OAuth App is registered) or a pasted PAT.
|
||||
///
|
||||
/// A device that already persisted the old standalone `diet-guard-sync`
|
||||
/// repo name (before it was folded into the shared `syncs` monorepo) is
|
||||
/// migrated to `syncs` here and the corrected value is written straight
|
||||
/// back to prefs, so this only ever runs once per device.
|
||||
static Future<SyncSettings> load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var repo = prefs.getString(_kRepo) ?? 'syncs';
|
||||
if (repo == 'diet-guard-sync') {
|
||||
repo = 'syncs';
|
||||
await prefs.setString(_kRepo, repo);
|
||||
}
|
||||
return SyncSettings(
|
||||
owner: prefs.getString(_kOwner) ?? 'kuhyx',
|
||||
repo: prefs.getString(_kRepo) ?? 'diet-guard-sync',
|
||||
repo: repo,
|
||||
token: await _loadToken(prefs),
|
||||
clientId: prefs.getString(_kClientId) ?? defaultClientId,
|
||||
);
|
||||
|
||||
@ -83,7 +83,7 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
testWidgets('shows the kuhyx/diet-guard-sync defaults on a fresh install', (
|
||||
testWidgets('shows the kuhyx/syncs defaults on a fresh install', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.runAsync(() async {
|
||||
@ -91,7 +91,7 @@ void main() {
|
||||
await settle(tester);
|
||||
|
||||
expect(find.widgetWithText(TextField, 'kuhyx'), findsOneWidget);
|
||||
expect(find.widgetWithText(TextField, 'diet-guard-sync'), findsOneWidget);
|
||||
expect(find.widgetWithText(TextField, 'syncs'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -10,18 +10,31 @@ void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
test(
|
||||
'load returns the kuhyx/diet-guard-sync defaults on a fresh install',
|
||||
'load returns the kuhyx/syncs defaults on a fresh install',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
installFakeSecureStorage();
|
||||
final s = await SyncSettings.load();
|
||||
expect(s.owner, 'kuhyx');
|
||||
expect(s.repo, 'diet-guard-sync');
|
||||
expect(s.repo, 'syncs');
|
||||
expect(s.token, '');
|
||||
expect(s.clientId, SyncSettings.defaultClientId);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'load migrates a persisted old diet-guard-sync repo value to syncs',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({'sync.repo': 'diet-guard-sync'});
|
||||
installFakeSecureStorage();
|
||||
final s = await SyncSettings.load();
|
||||
expect(s.repo, 'syncs');
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
expect(prefs.getString('sync.repo'), 'syncs');
|
||||
},
|
||||
);
|
||||
|
||||
test('save stores the token in the keystore, not in prefs', () async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
installFakeSecureStorage();
|
||||
|
||||
@ -70,7 +70,7 @@ GATE_LOCK_FILE: Path = DATA_DIR / ".gate.lock"
|
||||
# full current log as one file under devices/<id>/food_log.json; merging
|
||||
# happens client-side (see _sync_merge.py), never via git.
|
||||
SYNC_REPO_OWNER: str = "kuhyx"
|
||||
SYNC_REPO_NAME: str = "diet-guard-sync"
|
||||
SYNC_REPO_NAME: str = "syncs"
|
||||
SYNC_DEVICE_ID: str = "pc"
|
||||
# A fine-grained GitHub PAT, scoped to just SYNC_REPO_NAME's contents. The
|
||||
# user creates this once via github.com (see CLAUDE.md) and saves it here,
|
||||
|
||||
@ -24,7 +24,7 @@ from diet_guard._sync_merge import merge_logs
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
_DEVICES_DIR = "devices"
|
||||
_DEVICES_DIR = "diet-guard-sync/devices"
|
||||
|
||||
|
||||
class SyncError(Exception):
|
||||
@ -46,7 +46,7 @@ def _read_token() -> str:
|
||||
if not SYNC_TOKEN_FILE.exists():
|
||||
message = (
|
||||
f"no sync token at {SYNC_TOKEN_FILE} -- create a fine-grained "
|
||||
"GitHub PAT scoped to the diet-guard-sync repo's contents and "
|
||||
"GitHub PAT scoped to the syncs repo's contents and "
|
||||
f"save it there (mode 600), then re-run sync"
|
||||
)
|
||||
raise SyncError(message)
|
||||
|
||||
@ -4,7 +4,7 @@ version = "1.0.0"
|
||||
description = "Tkinter/systemd log-to-unlock meal gate with calorie/macro budget tracking"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"gatelock @ git+https://github.com/kuhyx/gatelock@v0.1.0",
|
||||
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.1.0#subdirectory=gatelock",
|
||||
"requests>=2.31.0",
|
||||
]
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
bandit>=1.7.0
|
||||
codespell>=2.2.0
|
||||
coverage>=7.4.0
|
||||
gatelock @ git+https://github.com/kuhyx/gatelock@v0.1.0
|
||||
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.1.0#subdirectory=gatelock
|
||||
mypy>=1.8.0
|
||||
pre-commit>=3.6.0
|
||||
pylint>=3.0.0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user