fix: never let a source checkout default to the production data directory - #517
Merged
Merged
Conversation
…tory Dev/production data isolation was opt-in: get_data_dir() defaulted to the real ~/.tuttle unless TUTTLE_DATA_DIR was set, and the only place setting it was the `just dev` recipe. Anything that ran the core another way — `uv run python -m tuttle.rpc_server`, a one-off `uv run python -c`, an unpackaged Electron build launched outside `just dev` — silently opened the user's real database and, because ensure_schema() upgrades to head on open, could stamp it with an unreleased migration. An installed release then refuses to start with "Can't locate revision identified by ...". Derive the default from how the code is running instead. Only a PyInstaller-frozen build (the sole shipping artifact — see tuttle-rpc.spec and the pack-electron / core-smoke workflows) defaults to ~/.tuttle; everything unfrozen defaults to ~/.tuttle-dev. TUTTLE_DATA_DIR still wins, so pointing dev tooling at real data stays possible but has to be deliberate. This reuses the sys.frozen test db_schema.py already makes for the same dev-vs-production distinction. Also log the resolved data directory at RPC startup: nothing ever announced which database was in use, which is what made this invisible. `just calendar-setup` genuinely needs the real ~/.tuttle (the macOS calendar permission grant is tied to that path, and it already opened the helper from there), so it now says so explicitly rather than relying on the old ambient default. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A dev run from this repo silently migrated the real
~/.tuttledatabase, after which the installed release refused to start withSchema migration failed (transient): Can't locate revision identified by 'f87515d1d068'. That revision ships in #511, which is onmainbut not in any released build, so the packaged app's bundled migrations don't contain it.The root cause is that dev/production isolation was opt-in:
get_data_dir()defaulted to the real~/.tuttleunlessTUTTLE_DATA_DIRwas set, and the only thing setting it was thejust devrecipe. Anything running the core another way —uv run python -m tuttle.rpc_server, a one-offuv run python -c "...", an unpackaged Electron build launched outsidejust dev— opened the real database, andensure_schema()upgrades to head on open.tuttle/data_dir.pynow derives the default from how the code is running rather than from an env var somebody has to remember. Only a PyInstaller-frozen build defaults to~/.tuttle; everything unfrozen defaults to~/.tuttle-dev.TUTTLE_DATA_DIRstill takes precedence, so aiming dev tooling at real data (e.g. afterjust sync-data) stays possible but has to be deliberate. This reuses thesys.frozentesttuttle/db_schema.pyalready makes for the same distinction.tuttle/rpc_server.pylogs the resolved data directory at startup. Nothing ever announced which database was in use, which is what made this invisible until a release build choked on it.justfile—just calendar-setupgenuinely needs the real~/.tuttle(the macOS calendar permission grant is tied to that path, and the recipe already opened the helper from there), so it now setsTUTTLE_DATA_DIRexplicitly instead of relying on the old ambient default.Why
sys.frozenis a trustworthy production signal:tuttle-rpc.specis PyInstaller, and both.github/workflows/pack-electron.ymland.github/workflows/core-smoke.ymlbuild the core through it, so every shipping artifact is frozen.Notes for the reviewer
just demo-resetpreviously reset the demo user inside the real~/.tuttle; it now hits~/.tuttle-dev. That matches the neighbouringjust resetand is the intended direction, but it is a change rather than a no-op.[project.scripts] tuttle-rpcconsole script is an unfrozen entry point. Nothing ships through it today, but a future Linux package that installs the script instead of the frozen binary would needTUTTLE_DATA_DIR=~/.tuttle.ui/electron/python-bridge.tsstill passes an ambientTUTTLE_DATA_DIRthrough to the frozen binary, so launching the installed app from a shell exporting the dev dir points production at dev data. Pre-existing and the opposite direction from this bug; happy to lock it down too.~/.tuttle. That needs a build containingf87515d1d068, or the.bak-*snapshotensure_schema()takes before every upgrade.Test plan
uv run pytest— 571 passed, 1 skippedtuttle_tests/test_data_dir.pyrewritten to pin both defaults: unfrozen resolves to~/.tuttle-dev, frozen to~/.tuttle, explicit env var overrides bothsys.frozen: unfrozen with no env var resolves~/.tuttle-dev;sys.frozen = Trueresolves~/.tuttle;TUTTLE_DATA_DIRoverrides bothruff checkandnpx tsc --noEmitclean; pre-commit hooks passjust devstill lands in~/.tuttle-devand thatjust calendar-setupstill gets its permission grantNo product UI surface changed — this is core/tooling only, so no Electron screenshots.
Checklist
just test).just precommit).CONTRIBUTING.md, module docstring).tuttle/model.py), so no migration is needed..tuttle-devdefault intopython-bridge.ts, which was dropped as a second source of truth).Made with Cursor