Stop tracking workspace/; bundled-demos/ is the canonical demo source

`workspace/` is runtime state (per-user folders, no-auth dev's `code/`)
and shouldn't be in git. The same files were previously committed under
both `workspace/code/` and `src/static/bundled-demos/`, which forced a
Docker `diff -q` sync check and leaked user-scoped paths into version
control.

- /workspace/ added to .gitignore; all previously tracked files removed
  via `git rm --cached`.
- src/static/bundled-demos/ becomes the single source of truth: panel16
  demos, led_tutorial, led_patterns, neopixel demos, and main.py move
  here alongside the existing canonical demos.
- New BUNDLED_DEMOS_DIR config; user_workspace seeders read from it.
- main.py lifespan seeds WORKSPACE_ROOT/code/ on startup so a fresh
  clone running `pipenv run dev` still gets the full sample set
  (existing files never overwritten — user edits survive restarts).
- Dockerfile drops `COPY workspace` and the diff sanity check.
- README/LED_TUTORIAL repointed at the new canonical paths.
- test_led_patterns loads led_patterns.py from bundled-demos.
- test_api uses mkdir(exist_ok=True) for `code/` (startup pre-creates).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-10 06:55:59 +12:00
parent b8d62e01d9
commit 7ee15f8eac
24 changed files with 86 additions and 418 deletions

View File

@@ -67,7 +67,7 @@ def test_save_file_collapses_duplicate_scoped_prefix(client, tmp_path):
def test_lib_folder_is_read_only_for_mutations(client, tmp_path):
code_dir = tmp_path / "code"
code_dir.mkdir()
code_dir.mkdir(exist_ok=True)
(code_dir / "main.py").write_text("print('ok')\n", encoding="utf-8")
save_blocked = client.post("/api/file/lib/new.txt", json={"content": "nope"})
@@ -115,7 +115,7 @@ def test_read_file_non_utf8_returns_400(client, tmp_path):
def test_delete_file_success_and_errors(client, tmp_path):
target = tmp_path / "code" / "delete-me.txt"
target.parent.mkdir()
target.parent.mkdir(exist_ok=True)
target.write_text("x", encoding="utf-8")
ok = client.delete("/api/file/code/delete-me.txt")
@@ -219,7 +219,7 @@ def test_folder_create_and_delete(client, tmp_path):
def test_create_folder_collapses_duplicate_scoped_prefix(client, tmp_path):
(tmp_path / "code").mkdir()
(tmp_path / "code").mkdir(exist_ok=True)
create = client.post("/api/folder/new/code/code/nested", json={"path": "ignored"})
assert create.status_code == 200
assert (tmp_path / "code" / "nested").is_dir()
@@ -230,14 +230,14 @@ def test_folder_delete_errors(client, tmp_path):
missing = client.delete("/api/folder/code/missing")
assert missing.status_code == 404
(tmp_path / "code").mkdir()
(tmp_path / "code").mkdir(exist_ok=True)
(tmp_path / "code" / "file.txt").write_text("x", encoding="utf-8")
not_dir = client.delete("/api/folder/code/file.txt")
assert not_dir.status_code == 400
def test_workspace_py_sources_returns_python_files(client, tmp_path):
(tmp_path / "code").mkdir()
(tmp_path / "code").mkdir(exist_ok=True)
(tmp_path / "code" / "app.py").write_text("x = 1\n", encoding="utf-8")
response = client.get("/api/workspace/py-sources")

View File

@@ -4,7 +4,8 @@ from pathlib import Path
def _load_patterns_module():
repo_root = Path(__file__).resolve().parents[1]
module_path = repo_root / "workspace" / "code" / "led_patterns.py"
# Canonical home for shipped demos — `workspace/` is gitignored.
module_path = repo_root / "src" / "static" / "bundled-demos" / "led_patterns.py"
spec = importlib.util.spec_from_file_location("led_patterns", module_path)
module = importlib.util.module_from_spec(spec)
assert spec is not None and spec.loader is not None