import os import time from github import Github import requests import random import string def create_github_repo(token, repo_name, private=False): """إنشاء ريبوزيتوري جديد على GitHub مع التحقق من الاسم الفريد""" g = Github(token) user = g.get_user() # Try to create repo with original name original_name = repo_name counter = 1 max_attempts = 100 # Allow up to 100 attempts (my-website-1 through my-website-100) for attempt in range(max_attempts): try: repo = user.create_repo(repo_name, private=private) return repo except Exception as e: error_str = str(e) # Check if error is due to name already existing if "name already exists" in error_str or "422" in error_str: # Add number suffix: (1), (2), (3), etc. repo_name = f"{original_name}-{counter}" counter += 1 else: # Check for 404 which usually means scope issues if "404" in error_str: raise Exception("GitHub API returned 404 (Not Found). This usually means your Token lacks the 'repo' scope or 'Administration' write permission. Please ensure your token has full repo access.") # Different error, re-raise raise # If all attempts failed, raise the last exception raise Exception(f"Failed to create repository after {max_attempts} attempts") def upload_files_to_repo(repo, local_dir, branch=None): """ Upload all files from a local directory to the repository. Automatically detects the default branch if not specified. """ # 1. Determine the target branch if not branch: try: branch = repo.default_branch except Exception: branch = "main" # 2. Upload files for root, _, files in os.walk(local_dir): for file_name in files: full_path = os.path.join(root, file_name) with open(full_path, "rb") as f: content = f.read() relative_path = os.path.relpath(full_path, local_dir).replace("\\", "/") # Retry mechanism for "Fresh" repositories (handles API propagation lag) max_retries = 3 for attempt in range(max_retries): try: # Check if file exists to update it try: contents = repo.get_contents(relative_path, ref=branch) repo.update_file( path=relative_path, message=f"Update {relative_path}", content=content, sha=contents.sha, branch=branch ) print(f"Updated {relative_path}") except Exception as e: # If not found (404), create it if "404" in str(e) or "Not Found" in str(e): repo.create_file( path=relative_path, message=f"Add {relative_path}", content=content, branch=branch ) print(f"Created {relative_path}") else: raise e break # Success, break retry loop except Exception as e: if attempt < max_retries - 1: print(f"Retrying upload for {relative_path} (Attempt {attempt+2}/{max_retries})...") time.sleep(2) else: print(f"Error with {relative_path} after {max_retries} attempts: {e}") raise e def enable_github_pages(username, repo_name, token): """تفعيل GitHub Pages""" pages_url = f"https://api.github.com/repos/{username}/{repo_name}/pages" headers = { "Authorization": f"token {token}", "Accept": "application/vnd.github.v3+json" } payload = {"source": {"branch": "main", "path": "/"}} response = requests.post(pages_url, headers=headers, json=payload) return response.status_code in [201, 204] def get_repository(username, repo_name, token): """ Retrieve an existing GitHub repository. Args: username: GitHub username repo_name: Repository name token: GitHub personal access token Returns: Repository object or None if not found """ try: g = Github(token) repo = g.get_repo(f"{username}/{repo_name}") return repo except Exception as e: if "404" in str(e): print(f"Info: Repository '{repo_name}' not found on GitHub. A new one will be created.") else: print(f"GitHub Repository lookup error: {e}") return None def update_file_in_repo(repo, file_path, content, commit_message="Update file"): """ Update an existing file in the repository. Args: repo: Repository object file_path: Path to file in repository (e.g., "index.html") content: New file content (string or bytes) commit_message: Commit message Returns: True if successful, False otherwise """ try: # Get the file from the repository file = repo.get_contents(file_path) # Update the file repo.update_file( path=file_path, message=commit_message, content=content, sha=file.sha, branch="main" ) print(f"Updated {file_path} in repository") return True except Exception as e: print(f"Error updating file: {e}") return False def resync_webcraft_file(username, repo_name, token, html_file_path, commit_message="Resync from WebCraft"): """ Resync WebCraft HTML file with existing GitHub repository. Args: username: GitHub username repo_name: Repository name token: GitHub personal access token html_file_path: Path to local HTML file commit_message: Commit message for the update Returns: dict with status and URL, or error message """ try: # Get the repository repo = get_repository(username, repo_name, token) if not repo: return { "status": "error", "message": f"Repository '{repo_name}' not found" } # Read the HTML file with open(html_file_path, 'rb') as f: content = f.read() # Find the HTML file in the repository (usually index.html) filename = os.path.basename(html_file_path) # Update the file if update_file_in_repo(repo, filename, content, commit_message): site_url = f"https://{username}.github.io/{repo_name}/" return { "status": "success", "message": "Repository synced successfully", "website_url": site_url, "repo_url": repo.html_url } else: return { "status": "error", "message": "Failed to update file in repository" } except Exception as e: return { "status": "error", "message": str(e) }