|
|
|
|
|
""" |
|
|
List all variables and secrets for a Hugging Face Space. |
|
|
|
|
|
Usage: |
|
|
export HF_TOKEN=hf_xxx |
|
|
python backend/scripts/list_hf_space_config.py --space davidtran999/hue-portal-backend |
|
|
""" |
|
|
|
|
|
from __future__ import annotations |
|
|
|
|
|
import argparse |
|
|
import os |
|
|
import sys |
|
|
from typing import Dict, List, Optional |
|
|
|
|
|
import requests |
|
|
|
|
|
|
|
|
def get_space_variables(space_id: str, token: str) -> Dict[str, str]: |
|
|
"""Fetch all environment variables for a Space.""" |
|
|
url = f"https://huggingface.co/api/spaces/{space_id}/variables" |
|
|
headers = {"Authorization": f"Bearer {token}"} |
|
|
try: |
|
|
response = requests.get(url, headers=headers, timeout=30) |
|
|
if response.status_code == 200: |
|
|
data = response.json() |
|
|
|
|
|
if isinstance(data, list): |
|
|
return {item["key"]: item.get("value", "") for item in data} |
|
|
elif isinstance(data, dict): |
|
|
return {k: v.get("value", "") if isinstance(v, dict) else v for k, v in data.items()} |
|
|
else: |
|
|
return {} |
|
|
elif response.status_code == 404: |
|
|
return {} |
|
|
else: |
|
|
print(f"⚠️ Failed to fetch variables: {response.status_code} - {response.text}", file=sys.stderr) |
|
|
return {} |
|
|
except Exception as e: |
|
|
print(f"⚠️ Error fetching variables: {e}", file=sys.stderr) |
|
|
return {} |
|
|
|
|
|
|
|
|
def get_space_secrets(space_id: str, token: str) -> List[str]: |
|
|
"""Fetch all secret keys (values are hidden) for a Space.""" |
|
|
url = f"https://huggingface.co/api/spaces/{space_id}/secrets" |
|
|
headers = {"Authorization": f"Bearer {token}"} |
|
|
try: |
|
|
response = requests.get(url, headers=headers, timeout=30) |
|
|
if response.status_code == 200: |
|
|
data = response.json() |
|
|
|
|
|
if isinstance(data, list): |
|
|
return [item["key"] for item in data] |
|
|
elif isinstance(data, dict): |
|
|
return list(data.keys()) |
|
|
else: |
|
|
return [] |
|
|
elif response.status_code == 404: |
|
|
return [] |
|
|
else: |
|
|
print(f"⚠️ Failed to fetch secrets: {response.status_code} - {response.text}", file=sys.stderr) |
|
|
return [] |
|
|
except Exception as e: |
|
|
print(f"⚠️ Error fetching secrets: {e}", file=sys.stderr) |
|
|
return [] |
|
|
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser: |
|
|
"""Configure CLI options.""" |
|
|
parser = argparse.ArgumentParser(description="List variables and secrets for a Hugging Face Space.") |
|
|
parser.add_argument( |
|
|
"--space", |
|
|
required=True, |
|
|
help="Space identifier in the form owner/space (e.g. davidtran999/hue-portal-backend).", |
|
|
) |
|
|
parser.add_argument( |
|
|
"--token-env", |
|
|
default="HF_TOKEN", |
|
|
help="Environment variable that stores the Hugging Face access token (default: %(default)s).", |
|
|
) |
|
|
return parser |
|
|
|
|
|
|
|
|
def main() -> None: |
|
|
"""CLI entry point.""" |
|
|
parser = build_parser() |
|
|
args = parser.parse_args() |
|
|
|
|
|
token = os.environ.get(args.token_env) |
|
|
if not token: |
|
|
parser.error(f"Environment variable {args.token_env} is not set.") |
|
|
|
|
|
print(f"📋 Listing configuration for: {args.space}\n") |
|
|
|
|
|
|
|
|
variables = get_space_variables(args.space, token) |
|
|
print("🔧 Environment Variables:") |
|
|
if variables: |
|
|
for key, value in sorted(variables.items()): |
|
|
|
|
|
if "password" in key.lower() or "secret" in key.lower() or "token" in key.lower() or "key" in key.lower(): |
|
|
masked = value[:4] + "***" if len(value) > 4 else "***" |
|
|
print(f" {key} = {masked}") |
|
|
else: |
|
|
print(f" {key} = {value}") |
|
|
else: |
|
|
print(" (none)") |
|
|
|
|
|
print() |
|
|
|
|
|
|
|
|
secrets = get_space_secrets(args.space, token) |
|
|
print("🔐 Secrets:") |
|
|
if secrets: |
|
|
for key in sorted(secrets): |
|
|
print(f" {key} = <hidden>") |
|
|
else: |
|
|
print(" (none)") |
|
|
|
|
|
print() |
|
|
|
|
|
|
|
|
var_keys = set(variables.keys()) |
|
|
secret_keys = set(secrets) |
|
|
collisions = var_keys & secret_keys |
|
|
|
|
|
if collisions: |
|
|
print("❌ COLLISIONS DETECTED (same name in both Variables and Secrets):") |
|
|
for key in sorted(collisions): |
|
|
print(f" ⚠️ {key}") |
|
|
print("\n💡 Fix: Remove from Variables (keep only in Secrets)") |
|
|
sys.exit(1) |
|
|
else: |
|
|
print("✅ No collisions detected") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
try: |
|
|
main() |
|
|
except Exception as exc: |
|
|
print(f"❌ {exc}", file=sys.stderr) |
|
|
sys.exit(1) |
|
|
|
|
|
|