asa / scripts /lib /bootstrap.py
defenestrate's picture
ASA Space: squashed history (instructions and settings live in the private pack)
526950e
Raw History Blame Contribute Delete
4.43 kB
"""First-time machine setup: persist role + external paths + FERPA_DATA_ROOT.
Writes two files:
- paths.json (user-config dir, machine-scoped; OUTSIDE the repo)
- .me-hub/local-state.json (in-repo, gitignored)
The role declaration is explicit, never inferred from path existence.
"""
from __future__ import annotations
import json
import os
import socket
from pathlib import Path
from typing import Optional
from lib.local_state import SCHEMA_VERSION as LOCAL_STATE_SCHEMA_VERSION
from lib.paths import default_paths_json_path, repo_root as _repo_root_real
_PATHS_JSON_SCHEMA_VERSION = 1
class MissingMaintainerPathError(RuntimeError):
pass
class FerpaInsideRepoError(RuntimeError):
pass
def _paths_json_path() -> Path:
return default_paths_json_path()
def _local_state_path() -> Path:
return _repo_root_real() / ".me-hub" / "local-state.json"
def _repo_root() -> Path:
return _repo_root_real()
def configure(
*,
declared_role: str,
box_togo_path: Optional[Path],
tolk_technology_path: Optional[Path],
ferpa_data_root: Path,
machine: str,
) -> None:
"""Persist the configuration for this machine."""
if declared_role not in ("maintainer", "faculty"):
raise ValueError(
f"declared_role must be 'maintainer' or 'faculty', got {declared_role!r}"
)
if declared_role == "maintainer":
if box_togo_path is None or not box_togo_path.exists():
raise MissingMaintainerPathError("maintainer mode requires box_togo_path")
if tolk_technology_path is None or not tolk_technology_path.exists():
raise MissingMaintainerPathError(
"maintainer mode requires tolk_technology_path"
)
repo = _repo_root()
try:
ferpa_data_root.resolve().relative_to(repo.resolve())
except ValueError:
pass
else:
raise FerpaInsideRepoError(
f"FERPA_DATA_ROOT must be OUTSIDE the working tree "
f"(got {ferpa_data_root}, which is inside {repo})"
)
ferpa_data_root.mkdir(parents=True, exist_ok=True)
pj = _paths_json_path()
pj.parent.mkdir(parents=True, exist_ok=True)
pj.write_text(
json.dumps(
{
"schema_version": _PATHS_JSON_SCHEMA_VERSION,
"declared_role": declared_role,
"box_togo_path": str(box_togo_path) if box_togo_path else None,
"tolk_technology_path": str(tolk_technology_path) if tolk_technology_path else None,
"ferpa_data_root": str(ferpa_data_root),
},
indent=2,
sort_keys=True,
),
encoding="utf-8",
)
ls = _local_state_path()
ls.parent.mkdir(parents=True, exist_ok=True)
ls.write_text(
json.dumps(
{
"schema_version": LOCAL_STATE_SCHEMA_VERSION,
"machine": machine,
"declared_role": declared_role,
"last_local_refresh_utc": None,
"last_seen_state_commit": None,
},
indent=2,
sort_keys=True,
),
encoding="utf-8",
)
# Inform the maintainer about OPENAI_API_KEY (required for build-derived)
if os.environ.get("OPENAI_API_KEY"):
print("OK: OPENAI_API_KEY found in environment (will be used by build-derived).")
else:
print("")
print("INFO: OPENAI_API_KEY not set. The build-derived pipeline requires it.")
print(" - bash/zsh: export OPENAI_API_KEY=sk-...")
print(" - powershell: $env:OPENAI_API_KEY = 'sk-...'")
print(" - or set it in your shell profile so it persists across sessions.")
# Inform the maintainer about ANTHROPIC_API_KEY (required for student-chat app)
if os.environ.get("ANTHROPIC_API_KEY"):
print("OK: ANTHROPIC_API_KEY found in environment (will be used by the chat app).")
else:
print("")
print("INFO: ANTHROPIC_API_KEY not set. The student-chat application requires it.")
print(" - bash/zsh: export ANTHROPIC_API_KEY=sk-ant-...")
print(" - powershell: $env:ANTHROPIC_API_KEY = 'sk-ant-...'")
print(" - or set it in your shell profile so it persists across sessions.")
def detect_machine() -> str:
"""Return socket.gethostname(). Called by bootstrap.{ps1,sh} to populate `machine`."""
return socket.gethostname()