HFHUB-to-GITHUB / app.py
LPX55
init: space logic
acac48e
import gradio as gr
from huggingface_hub import snapshot_download
from github import Github
import shutil
import os
import base64
def clone_hf_to_github(
space_url, github_token, repo_name, make_private, enable_sync, hf_token
):
# Sanitize inputs
if not space_url or not github_token or not repo_name:
return "Error: All fields must be filled out."
if enable_sync and not hf_token:
return "Error: A Hugging Face Token is required for continuous sync."
temp_dir = f"./temp_clone/{repo_name}"
os.makedirs(temp_dir, exist_ok=True)
try:
# Step 1: Clone Hugging Face Space
# Pass the token if sync is enabled (handles public/private spaces)
token_to_use = hf_token if enable_sync else None
snapshot_download(repo_id=space_url.split("spaces/")[1], local_dir=temp_dir, token=token_to_use)
# Step 2: Authenticate with GitHub
g = Github(github_token)
user = g.get_user()
# Check if repo already exists to prevent an error
try:
github_repo = user.get_repo(repo_name)
except:
# Create the new GitHub repository, using the user's preference
github_repo = user.create_repo(repo_name, private=make_private)
# Use git commands to push the cloned content
os.system(f"cd {temp_dir} && git init && git add . && git commit -m 'Initial commit from HF Space'")
os.system(f"cd {temp_dir} && git branch -M main")
os.system(f"cd {temp_dir} && git remote add origin {github_repo.clone_url.replace('https://', f'https://{github_token}@')}")
os.system(f"cd {temp_dir} && git push -u origin main")
# Step 3 (Optional): Add HF_TOKEN as a GitHub secret and create workflow file
if enable_sync:
github_repo.create_secret("HF_TOKEN", hf_token)
workflow_content = f"""
name: Sync with Hugging Face Hub
on:
push:
branches: [main]
jobs:
sync-to-hub:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
lfs: true
- name: Push to Hugging Face Hub
uses: nateraw/huggingface-sync-action@v0.0.5
with:
huggingface_repo_id: '{space_url.split("spaces/")[1]}'
hf_token: ${{ secrets.HF_TOKEN }}
"""
encoded_content = base64.b64encode(workflow_content.encode()).decode()
github_repo.create_file(
path=".github/workflows/sync-to-hf.yml",
message="Add GitHub Actions workflow to sync with Hugging Face",
content=encoded_content
)
final_url = github_repo.html_url
if enable_sync:
return f"Success! Space '{space_url}' cloned to GitHub repo '{final_url}'. Continuous sync workflow has been added."
else:
return f"Success! Space '{space_url}' cloned to GitHub repo '{final_url}'."
except Exception as e:
return f"An error occurred: {e}"
finally:
# Clean up the temporary directory
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def toggle_hf_token(enable_sync):
"""Conditionally shows/hides the HF_TOKEN textbox based on the checkbox state."""
return gr.Textbox.update(visible=enable_sync)
# Create the Gradio interface
with gr.Blocks() as demo:
gr.Markdown("# Hugging Face Space to GitHub Cloner and Sync Setup")
gr.Markdown("Clone a Hugging Face Space and optionally set up a continuous sync with GitHub Actions.")
with gr.Row():
hf_space_url = gr.Textbox(label="Hugging Face Space URL (e.g., user/space)")
github_token = gr.Textbox(label="GitHub Personal Access Token (with 'repo' and 'workflow' scopes)", type="password")
github_repo_name = gr.Textbox(label="New GitHub Repo Name")
with gr.Row():
make_private = gr.Radio(choices=[True, False], value=False, label="Make GitHub Repo Private?")
enable_sync = gr.Checkbox(label="Enable Continuous Sync with Hugging Face Space")
hf_token = gr.Textbox(
label="Hugging Face User Access Token (with 'read' access for cloning)",
type="password",
visible=False # Initially hidden
)
# Event listener to control visibility of HF_TOKEN textbox
enable_sync.change(
fn=toggle_hf_token,
inputs=enable_sync,
outputs=hf_token
)
submit_btn = gr.Button("Clone and Set Up Sync")
output = gr.Textbox(label="Output")
submit_btn.click(
fn=clone_hf_to_github,
inputs=[hf_space_url, github_token, github_repo_name, make_private, enable_sync, hf_token],
outputs=output,
)
demo.launch()