|
from __future__ import annotations |
|
|
|
import os |
|
import re |
|
from typing import Annotated, Optional |
|
|
|
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 deploy( |
|
title: Annotated[Optional[str], Option(help="Spaces app title")] = None, |
|
app_file: Annotated[ |
|
Optional[str], Option(help="File containing the Gradio app") |
|
] = None, |
|
): |
|
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 = huggingface_hub.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(): |
|
huggingface_hub.add_space_secret(space_id, secret_name, secret_value) |
|
print(f"Space available at https://huggingface.co/spaces/{space_id}") |
|
|