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()