#!/usr/bin/env python3 """ Deploy Suno Share site to Vercel and return a shareable preview URL. Usage: python deploy-to-vercel.py [--prompt-file ] # Deploy entire site python deploy-to-vercel.py # Deploy and get URL for specific prompt python deploy-to-vercel.py --prompt-file src/content/prompts/my-conversation.mdx """ import argparse import re import subprocess import sys from pathlib import Path from typing import Optional def deploy_to_vercel( repo_root: Path, share_dir: Path, prompt_file: Optional[Path] = None ) -> Optional[str]: """ Deploy to Vercel and return the preview URL. Args: repo_root: Repository root directory (where to deploy from) share_dir: Share site directory (where pnpm/vercel CLI is installed) prompt_file: Optional MDX file for the prompt (to generate direct link) Returns: Preview URL (with direct link to prompt if prompt_file provided), or None if deployment fails. """ print("šŸš€ Deploying to Vercel...") try: # Run vercel deploy from repo root, using vercel CLI from share_dir # Use the .vercel config from share-site directory to get the correct team vercel_config_dir = share_dir / ".vercel" vercel_path = share_dir / "node_modules" / ".bin" / "vercel" # Copy .vercel directory to repo root temporarily import shutil temp_vercel_dir = repo_root / ".vercel" if vercel_config_dir.exists(): if temp_vercel_dir.exists(): shutil.rmtree(temp_vercel_dir) shutil.copytree(vercel_config_dir, temp_vercel_dir) # Deploy with archive mode - lefthook will be skipped automatically # because git repository won't be present in the archive result = subprocess.run( [str(vercel_path), "deploy", "--yes", "--archive=tgz"], cwd=repo_root, capture_output=True, text=True, timeout=300, # 5 minute timeout ) # Clean up temporary .vercel directory if temp_vercel_dir.exists(): shutil.rmtree(temp_vercel_dir) # Parse the preview URL from output (even if returncode != 0) # Vercel sometimes reports errors after successful upload output = result.stdout + result.stderr # Look for Preview URL (supports both .vercel.app and custom domains like .suno.run) url_pattern = r"Preview: (https://[a-zA-Z0-9-]+\.(?:vercel\.app|suno\.run))" matches = re.findall(url_pattern, output) if not matches: # Fallback: try to find any vercel/suno URL url_pattern = r"https://[a-zA-Z0-9-]+\.(?:vercel\.app|suno\.run)" matches = re.findall(url_pattern, output) if not matches: if result.returncode != 0: print(f"āŒ Deployment failed: {result.stderr}", file=sys.stderr) else: print( "āŒ Could not extract deployment URL from output", file=sys.stderr ) return None # Get the preview URL preview_url = ( matches[0] if isinstance(matches[0], str) and matches[0].startswith("http") else matches[0] ) # If prompt_file is provided, construct direct link to the prompt page if prompt_file: prompt_filename = prompt_file.stem prompt_path = f"/prompts/{prompt_filename}" full_url = f"{preview_url}{prompt_path}" return full_url return preview_url except subprocess.TimeoutExpired: print("āŒ Deployment timed out after 5 minutes", file=sys.stderr) return None except Exception as e: print(f"āŒ Deployment error: {e}", file=sys.stderr) return None def main(): parser = argparse.ArgumentParser( description="Deploy Suno Wiki to Vercel and get preview URL.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Deploy entire site python deploy-to-vercel.py # Deploy and get direct link to specific prompt python deploy-to-vercel.py --prompt-file src/content/prompts/my-conversation.mdx """, ) parser.add_argument( "--prompt-file", "-p", type=Path, help="Path to prompt MDX file (to generate direct link)", ) args = parser.parse_args() # Determine repo root and wiki directory by walking up the directory tree current_dir = Path.cwd().resolve() # Walk up the directory tree to find the repo root repo_root = None search_dir = current_dir while search_dir != search_dir.parent: # Stop at filesystem root # Check if this directory is the glockenspiel root # (has both .git and share-site) - this handles nested git repos correctly if (search_dir / ".git").exists() and (search_dir / "share-site").exists(): repo_root = search_dir break # Also accept any directory containing share-site, even without .git # (useful in archive/deployment scenarios or unusual git setups) elif (search_dir / "share-site").exists(): repo_root = search_dir break search_dir = search_dir.parent if repo_root is None: print("Error: Cannot find repository root (no share-site directory found)", file=sys.stderr) print("Please run from within the glockenspiel repository", file=sys.stderr) sys.exit(1) # Verify share-site exists share_dir = repo_root / "share-site" if not share_dir.exists(): print("Error: share-site directory not found in repository root", file=sys.stderr) print(f"Repository root: {repo_root}", file=sys.stderr) sys.exit(1) # Validate prompt file if provided if args.prompt_file and not args.prompt_file.exists(): print(f"Error: Prompt file not found: {args.prompt_file}", file=sys.stderr) sys.exit(1) # Deploy deployment_url = deploy_to_vercel(repo_root, share_dir, args.prompt_file) if deployment_url: print("\nāœ… Deployed successfully!") print(f"šŸ”— Share this link: {deployment_url}") print("\nThe preview is live and shareable.") print("To make it permanent, commit and merge to main.") sys.exit(0) else: print("\nāŒ Deployment failed.") print("You can deploy manually with: cd share-site && pnpm vercel deploy") sys.exit(1) if __name__ == "__main__": main()