Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Sync selected keys from a local .env file to Hugging Face Space secrets. | |
| Usage: | |
| python scripts/sync_hf_secrets.py --space-id <username/space-name> | |
| Optional: | |
| python scripts/sync_hf_secrets.py --space-id <username/space-name> --env-file .env | |
| python scripts/sync_hf_secrets.py --space-id <username/space-name> --token <hf_token> | |
| python scripts/sync_hf_secrets.py --space-id <username/space-name> --dry-run | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import os | |
| import sys | |
| from pathlib import Path | |
| from dotenv import dotenv_values | |
| ALLOWED_KEYS = [ | |
| "AZURE_SEARCH_ENDPOINT", | |
| "AZURE_SEARCH_KEY", | |
| "AZURE_SEARCH_INDEX_NAME", | |
| "AZURE_OPENAI_ENDPOINT", | |
| "AZURE_OPENAI_KEY", | |
| "AZURE_OPENAI_DEPLOYMENT", | |
| "AZURE_OPENAI_API_VERSION", | |
| "AZURE_VISION_ENDPOINT", | |
| "AZURE_VISION_KEY", | |
| "AZURE_VISION_MODEL_VERSION", | |
| "EDITING_API_URL", | |
| "FIVEK_SUBSET_SIZE", | |
| ] | |
| def _parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser( | |
| description="Sync selected .env keys to Hugging Face Space secrets." | |
| ) | |
| parser.add_argument( | |
| "--space-id", | |
| required=True, | |
| help="Hugging Face Space id, e.g. username/my-space", | |
| ) | |
| parser.add_argument( | |
| "--env-file", | |
| default=".env", | |
| help="Path to .env file (default: .env)", | |
| ) | |
| parser.add_argument( | |
| "--token", | |
| default=os.getenv("HF_TOKEN"), | |
| help="Hugging Face token (defaults to HF_TOKEN env var)", | |
| ) | |
| parser.add_argument( | |
| "--dry-run", | |
| action="store_true", | |
| help="Print what would be synced without updating the Space", | |
| ) | |
| return parser.parse_args() | |
| def _load_pairs(env_file: Path) -> dict[str, str]: | |
| if not env_file.exists(): | |
| raise FileNotFoundError(f".env file not found: {env_file}") | |
| raw = dotenv_values(env_file) | |
| pairs: dict[str, str] = {} | |
| for key in ALLOWED_KEYS: | |
| value = raw.get(key) | |
| if value is None: | |
| continue | |
| value = str(value).strip() | |
| if not value: | |
| continue | |
| pairs[key] = value | |
| return pairs | |
| def main() -> int: | |
| args = _parse_args() | |
| env_file = Path(args.env_file).resolve() | |
| try: | |
| pairs = _load_pairs(env_file) | |
| except Exception as exc: # pragma: no cover | |
| print(f"ERROR: {exc}") | |
| return 1 | |
| if not pairs: | |
| print("No allowed keys with non-empty values found in .env.") | |
| return 1 | |
| if args.dry_run: | |
| print(f"[dry-run] Would sync {len(pairs)} secret(s) to {args.space_id}:") | |
| for k in sorted(pairs): | |
| print(f" - {k}") | |
| return 0 | |
| if not args.token: | |
| print("ERROR: Missing token. Set HF_TOKEN or pass --token.") | |
| return 1 | |
| try: | |
| from huggingface_hub import HfApi | |
| except Exception: | |
| print("ERROR: huggingface_hub is not installed.") | |
| print("Install it with: pip install huggingface_hub") | |
| return 1 | |
| api = HfApi(token=args.token) | |
| print(f"Syncing {len(pairs)} secret(s) to {args.space_id}...") | |
| for key, value in pairs.items(): | |
| api.add_space_secret(repo_id=args.space_id, key=key, value=value) | |
| print(f" - synced {key}") | |
| print("Done. Your Space will restart to apply updated secrets.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |