File size: 4,957 Bytes
0c60520
 
 
 
4162a45
 
0c60520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/env python
"""Show or set a public VARIABLE on one of the DemoPrep HF Spaces.

Variables are the per-Space switches that make ONE commit behave differently on
test and prod — e.g. MUZE_ENGINE_MAIN_TABS=false as an emergency off on one Space
(unset = ON everywhere since 2026-09-22).
They are NOT secrets: on a public Space anyone can read them. Secrets go through
scripts/setup_test_mcp_secrets.py.

    ./.venv/bin/python scripts/set_space_variable.py test-mcp --show
    ./.venv/bin/python scripts/set_space_variable.py test-mcp MUZE_ENGINE_MAIN_TABS true
    ./.venv/bin/python scripts/set_space_variable.py mcp MUZE_ENGINE_MAIN_TABS false --dry-run
    ./.venv/bin/python scripts/set_space_variable.py test-mcp MUZE_ENGINE_MAIN_TABS --delete

Space may be a short name (test-mcp, mcp, demoprep, test-demoprep) or a full
`owner/name` repo id.

Setting or deleting a variable RESTARTS the Space and kills any build in flight.
Check the MCP `status` tool for active_builds first. Set the variable BEFORE
pushing code that reads it — old code ignores the variable, so the extra
restart is harmless, and the new code comes up already in the intended mode.

Token: HF_TOKEN from the environment, else from the .env named by --env
(default: <repo root>/.env). Reading variables on a public Space needs no token.
"""

import argparse
import os
import sys
from pathlib import Path

SHORT_NAMES = {
    "test-mcp": "thoughtspot-demoprep/test-mcp",
    "mcp": "thoughtspot-demoprep/mcp",
    "demoprep": "thoughtspot-dp/demoprep",
    "test-demoprep": "thoughtspot-dp/test-demoprep",
}
DEFAULT_ENV = Path(__file__).resolve().parent.parent / ".env"


def repo_id_of(space: str) -> str:
    if "/" in space:
        return space
    if space in SHORT_NAMES:
        return SHORT_NAMES[space]
    sys.exit(f"ERROR: unknown Space {space!r}. Use one of {sorted(SHORT_NAMES)} or a full owner/name.")


def token_from(env_path: Path) -> str:
    tok = (os.getenv("HF_TOKEN") or "").strip()
    if tok:
        return tok
    if env_path.exists():
        for raw in env_path.read_text().splitlines():
            line = raw.strip()
            if line.startswith("HF_TOKEN="):
                return line.split("=", 1)[1].strip().strip('"').strip("'")
    sys.exit(f"ERROR: no HF_TOKEN in the environment or in {env_path}. "
             "A write needs one; pass --env to point at the .env that has it.")


def show(api, repo_id: str) -> None:
    variables = api.get_space_variables(repo_id=repo_id)
    if not variables:
        print(f"{repo_id}: no variables set")
        return
    print(f"{repo_id}:")
    for key, var in sorted(variables.items()):
        updated = getattr(var, "updated_at", None) or getattr(var, "updatedAt", "")
        print(f"  {key} = {var.value!r}   (updated {updated})")


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("space", help="test-mcp | mcp | demoprep | test-demoprep | owner/name")
    ap.add_argument("key", nargs="?", help="variable name")
    ap.add_argument("value", nargs="?", help="value to set")
    ap.add_argument("--show", action="store_true", help="list the Space's variables and exit")
    ap.add_argument("--delete", action="store_true", help="delete KEY instead of setting it")
    ap.add_argument("--dry-run", action="store_true", help="say what would change, touch nothing")
    ap.add_argument("--env", type=Path, default=DEFAULT_ENV, help="the .env holding HF_TOKEN")
    args = ap.parse_args()

    from huggingface_hub import HfApi

    repo_id = repo_id_of(args.space)
    if args.show or not args.key:
        # Reads work without a token on a public Space; send one if we have it.
        tok = (os.getenv("HF_TOKEN") or "").strip() or None
        show(HfApi(token=tok), repo_id)
        return

    if args.delete and args.value is not None:
        sys.exit("ERROR: --delete takes KEY only, no value.")
    if not args.delete and args.value is None:
        sys.exit("ERROR: give a VALUE to set, or --delete, or --show.")

    action = f"DELETE {args.key}" if args.delete else f"SET {args.key}={args.value!r}"
    print(f"{action} on {repo_id}  — this RESTARTS the Space and kills in-flight builds.")
    if args.dry_run:
        print("--dry-run: nothing was changed.")
        return
    if input("Proceed? [y/N] ").strip().lower() != "y":
        sys.exit("Aborted.")

    api = HfApi(token=token_from(args.env))
    if args.delete:
        api.delete_space_variable(repo_id=repo_id, key=args.key)
    else:
        api.add_space_variable(repo_id=repo_id, key=args.key, value=args.value)
    print("done. Now:")
    show(api, repo_id)
    print("\nNEXT: after the restart, call the `config_check` tool and confirm `muze.main_tabs` "
          "(or whatever the variable drives) reads as intended. A booted container is not a verified one.")


if __name__ == "__main__":
    main()