Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import os | |
| import re | |
| import shutil | |
| import subprocess | |
| from typing import Annotated | |
| import huggingface_hub | |
| from rich import print | |
| from typer import Option | |
| import gradio as gr | |
| repo_directory = os.getcwd() | |
| readme_file = os.path.join(repo_directory, "README.md") | |
| github_action_template = os.path.join( | |
| os.path.dirname(__file__), "deploy_space_action.yaml" | |
| ) | |
| def add_configuration_to_readme( | |
| title: str | None, | |
| app_file: str | None, | |
| ) -> dict: | |
| configuration = {} | |
| dir_name = os.path.basename(repo_directory) | |
| if title is None: | |
| title = input(f"Enter Spaces app title [{dir_name}]: ") or dir_name | |
| formatted_title = format_title(title) | |
| if formatted_title != title: | |
| print(f"Formatted to {formatted_title}. ") | |
| configuration["title"] = formatted_title | |
| if app_file is None: | |
| for file in os.listdir(repo_directory): | |
| file_path = os.path.join(repo_directory, file) | |
| if not os.path.isfile(file_path) or not file.endswith(".py"): | |
| continue | |
| with open(file_path, encoding="utf-8", errors="ignore") as f: | |
| content = f.read() | |
| if "import gradio" in content: | |
| app_file = file | |
| break | |
| app_file = ( | |
| input(f"Enter Gradio app file {f'[{app_file}]' if app_file else ''}: ") | |
| or app_file | |
| ) | |
| if not app_file or not os.path.exists(app_file): | |
| raise FileNotFoundError("Failed to find Gradio app file.") | |
| configuration["app_file"] = app_file | |
| configuration["sdk"] = "gradio" | |
| configuration["sdk_version"] = gr.__version__ | |
| huggingface_hub.metadata_save(readme_file, configuration) | |
| configuration["hardware"] = ( | |
| input( | |
| f"Enter Spaces hardware ({', '.join(hardware.value for hardware in huggingface_hub.SpaceHardware)}) [cpu-basic]: " | |
| ) | |
| or "cpu-basic" | |
| ) | |
| secrets = {} | |
| if input("Any Spaces secrets (y/n) [n]: ") == "y": | |
| while True: | |
| secret_name = input("Enter secret name (leave blank to end): ") | |
| if not secret_name: | |
| break | |
| secret_value = input(f"Enter secret value for {secret_name}: ") | |
| secrets[secret_name] = secret_value | |
| configuration["secrets"] = secrets | |
| requirements_file = os.path.join(repo_directory, "requirements.txt") | |
| if ( | |
| not os.path.exists(requirements_file) | |
| and input("Create requirements.txt file? (y/n) [n]: ").lower() == "y" | |
| ): | |
| while True: | |
| requirement = input("Enter a dependency (leave blank to end): ") | |
| if not requirement: | |
| break | |
| with open(requirements_file, "a", encoding="utf-8") as f: | |
| f.write(requirement + "\n") | |
| if ( | |
| input( | |
| "Create Github Action to automatically update Space on 'git push'? [n]: " | |
| ).lower() | |
| == "y" | |
| ): | |
| track_branch = input("Enter branch to track [main]: ") or "main" | |
| github_action_file = os.path.join( | |
| repo_directory, ".github/workflows/update_space.yml" | |
| ) | |
| os.makedirs(os.path.dirname(github_action_file), exist_ok=True) | |
| with open(github_action_template, encoding="utf-8") as f: | |
| github_action_content = f.read() | |
| github_action_content = github_action_content.replace("$branch", track_branch) | |
| with open(github_action_file, "w", encoding="utf-8") as f: | |
| f.write(github_action_content) | |
| print( | |
| "Github Action created. Add your Hugging Face write token (from https://huggingface.co/settings/tokens) as an Actions Secret named 'hf_token' to your GitHub repository. This can be set in your repository's settings page." | |
| ) | |
| return configuration | |
| def format_title(title: str): | |
| title = title.replace(" ", "_") | |
| title = re.sub(r"[^a-zA-Z0-9\-._]", "", title) | |
| title = re.sub("-+", "-", title) | |
| while title.startswith("."): | |
| title = title[1:] | |
| return title | |
| def check_gcloud_auth(): | |
| """Check if user is logged in to Google Cloud and has a project selected.""" | |
| try: | |
| auth_result = subprocess.run( | |
| ["gcloud", "auth", "list", "--filter=status:ACTIVE"], | |
| check=True, | |
| text=True, | |
| capture_output=True, | |
| ) | |
| if not auth_result.stdout.strip(): | |
| print("[bold yellow]You are not logged in to Google Cloud.[/bold yellow]") | |
| print("Running 'gcloud init' to set up authentication...") | |
| subprocess.run(["gcloud", "init"], check=True) | |
| project_result = subprocess.run( | |
| ["gcloud", "config", "get-value", "project"], | |
| check=True, | |
| text=True, | |
| capture_output=True, | |
| ) | |
| if not project_result.stdout.strip(): | |
| print("[bold yellow]No Google Cloud project is selected.[/bold yellow]") | |
| project_id = input("Enter your Google Cloud project ID: ") | |
| if not project_id: | |
| print("[red]Project ID is required for deployment.[/red]") | |
| return None | |
| return project_id | |
| print(f"[green]✓ Authenticated as: {auth_result.stdout.strip()}[/green]") | |
| print(f"[green]✓ Project: {project_result.stdout.strip()}[/green]") | |
| return project_result.stdout.strip() | |
| except subprocess.CalledProcessError as e: | |
| print(f"[bold red]Error checking Google Cloud configuration: {e}[/bold red]") | |
| print("Running 'gcloud init' to set up configuration...") | |
| try: | |
| subprocess.run(["gcloud", "init"], check=True) | |
| except subprocess.CalledProcessError as init_error: | |
| print(f"[red]Failed to run 'gcloud init': {init_error}[/red]") | |
| return False | |
| except FileNotFoundError: | |
| print( | |
| "[bold red]gcloud CLI not found. Please install Google Cloud SDK.[/bold red]" | |
| ) | |
| return False | |
| return True | |
| def deploy_to_gcloud(): | |
| """Deploy a Gradio app to Google Cloud Run. Always uses app.py as the entry point.""" | |
| if not shutil.which("gcloud"): | |
| print( | |
| "[bold red]gcloud CLI is not installed.[/bold red]\n" | |
| "Please install the Google Cloud SDK from: " | |
| "[link]https://cloud.google.com/sdk/docs/install[/link]" | |
| ) | |
| return | |
| project_id = check_gcloud_auth() | |
| if not project_id: | |
| print( | |
| "[bold red]Google Cloud configuration failed. Please run 'gcloud init' manually.[/bold red]" | |
| ) | |
| return | |
| if not os.path.exists("app.py") and not os.path.exists("main.py"): | |
| print( | |
| "[bold red]Error:[/bold red] app.py and main.py not found. Google Cloud Run deployment requires app.py or main.py as the entry point." | |
| ) | |
| return | |
| requirements_file = "requirements.txt" | |
| if not os.path.exists(requirements_file): | |
| if ( | |
| input( | |
| f"A requirements.txt file is necessary for Google Cloud Run deployment. Create requirements.txt with gradio=={gr.__version__}? (y/n) [y]: " | |
| ).lower() | |
| != "n" | |
| ): | |
| with open(requirements_file, "w", encoding="utf-8") as f: | |
| f.write(f"gradio=={gr.__version__}\n") | |
| print(f"Created requirements.txt with gradio=={gr.__version__}") | |
| else: | |
| print("\n[yellow]Deployment cancelled.[/yellow]") | |
| return | |
| else: | |
| with open(requirements_file, encoding="utf-8") as f: | |
| requirements_content = f.read() | |
| if "gradio" not in requirements_content and ( | |
| input( | |
| f"Add gradio=={gr.__version__} to requirements.txt? (y/n) [y]: " | |
| ).lower() | |
| != "n" | |
| ): | |
| with open(requirements_file, "a", encoding="utf-8") as f: | |
| f.write(f"gradio=={gr.__version__}\n") | |
| print(f"Added gradio=={gr.__version__} to requirements.txt") | |
| print("[bold]Deploying to Google Cloud Run...[/bold]") | |
| try: | |
| deploy_command = [ | |
| "gcloud", | |
| "run", | |
| "deploy", | |
| "--source=.", | |
| "--labels=created-by=gradio", | |
| ] | |
| if project_id: | |
| deploy_command.extend(["--project", project_id]) | |
| subprocess.run( | |
| deploy_command, | |
| check=True, | |
| text=True, | |
| capture_output=False, | |
| ) | |
| print("[green]✓ Deployment complete![/green]") | |
| except subprocess.CalledProcessError as e: | |
| print(f"[red]Deployment failed: {e}[/red]") | |
| return | |
| except KeyboardInterrupt: | |
| print("\n[yellow]Deployment cancelled.[/yellow]") | |
| return | |
| def deploy( | |
| title: Annotated[str | None, Option(help="Spaces app title")] = None, | |
| app_file: Annotated[ | |
| str | None, Option(help="File containing the Gradio app") | |
| ] = None, | |
| provider: Annotated[ | |
| str | None, Option(help="Deployment provider (spaces or gcloud)") | |
| ] = "spaces", | |
| ): | |
| if provider == "gcloud": | |
| if app_file and app_file != "app.py": | |
| print( | |
| "[yellow]Warning:[/yellow] --app-file is ignored for Google Cloud Run deployment. Using app.py as entry point." | |
| ) | |
| deploy_to_gcloud() | |
| return | |
| if provider != "spaces": | |
| print(f"[red]Unknown provider: {provider}. Use 'spaces' or 'gcloud'.[/red]") | |
| return | |
| if os.getenv("SYSTEM") == "spaces": | |
| return | |
| hf_api = huggingface_hub.HfApi() | |
| whoami = None | |
| login = False | |
| try: | |
| whoami = hf_api.whoami() | |
| if whoami["auth"]["accessToken"]["role"] != "write": | |
| login = True | |
| except OSError: | |
| login = True | |
| if login: | |
| print("Need 'write' access token to create a Spaces repo.") | |
| huggingface_hub.login(add_to_git_credential=False) | |
| whoami = hf_api.whoami() | |
| configuration: None | dict = None | |
| if os.path.exists(readme_file): | |
| try: | |
| configuration = huggingface_hub.metadata_load(readme_file) | |
| except ValueError: | |
| pass | |
| if configuration is None: | |
| print( | |
| f"Creating new Spaces Repo in '{repo_directory}'. Collecting metadata, press Enter to accept default value." | |
| ) | |
| configuration = add_configuration_to_readme( | |
| title, | |
| app_file, | |
| ) | |
| space_id = hf_api.create_repo( | |
| configuration["title"], | |
| space_sdk="gradio", | |
| repo_type="space", | |
| exist_ok=True, | |
| space_hardware=configuration.get("hardware"), | |
| ).repo_id | |
| hf_api.upload_folder( | |
| repo_id=space_id, | |
| repo_type="space", | |
| folder_path=repo_directory, | |
| ) | |
| if configuration.get("secrets"): | |
| for secret_name, secret_value in configuration["secrets"].items(): | |
| hf_api.add_space_secret(space_id, secret_name, secret_value) | |
| print(f"Space available at https://huggingface.co/spaces/{space_id}") | |