"""Push the project to its Hugging Face Space. uv run python scripts/deploy.py # upload and report status uv run python scripts/deploy.py -m "tweak layout" # with a commit message uv run python scripts/deploy.py --watch # also wait for it to come up `hf upload` is not used here: it calls `create_repo` first, and on a free account creating a Gradio Space returns 402 Payment Required — so the CLI fails even though the Space already exists. `upload_folder` talks to the commit endpoint directly and does not care. """ import argparse import sys import time from huggingface_hub import HfApi from huggingface_hub.errors import HfHubHTTPError from requests.exceptions import RequestException SPACE_ID = "Smomitya/flight-booking-agent" # Everything that must never reach the Space. `.venv` alone is gigabytes, and # `data/` is local state the Space reseeds for itself on startup. IGNORE = [ ".venv/**", "**/__pycache__/**", ".ruff_cache/**", ".pytest_cache/**", "data/**", ".git/**", "**/.DS_Store", "uv.lock", ] SETTLED = {"RUNNING", "RUNTIME_ERROR", "BUILD_ERROR", "CONFIG_ERROR", "PAUSED"} def watch(api: HfApi, space_id: str, minutes: int = 12) -> str: """Follow the Space until it settles, printing each stage change.""" last = None deadline = time.time() + minutes * 60 while time.time() < deadline: try: runtime = api.space_info(space_id).runtime except (HfHubHTTPError, RequestException) as error: # The hub drops long polls routinely; a failed read is not a # failed deploy, so keep watching rather than reporting an error. print(f" (poll failed: {type(error).__name__}, retrying)") time.sleep(15) continue if runtime.stage != last: print(f" [{time.strftime('%H:%M:%S')}] {runtime.stage}" f" hardware={runtime.hardware}") last = runtime.stage if runtime.stage in SETTLED: return runtime.stage time.sleep(15) return last or "UNKNOWN" def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("-m", "--message", default="Update from local checkout") parser.add_argument("--space", default=SPACE_ID) parser.add_argument("--watch", action="store_true", help="wait for the Space to finish starting") args = parser.parse_args() api = HfApi() print(f"Uploading to {args.space} …") api.upload_folder( repo_id=args.space, repo_type="space", folder_path=".", ignore_patterns=IGNORE, commit_message=args.message, ) print(f" https://huggingface.co/spaces/{args.space}") if not args.watch: return 0 stage = watch(api, args.space) print(f"final: {stage}") # A Space can report RUNNING while the app inside it is broken, so this is # a floor, not a guarantee — check the logs tab if behaviour looks wrong. return 0 if stage == "RUNNING" else 1 if __name__ == "__main__": sys.exit(main())