mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 16:35:18 -05:00
feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application - Add frontend/ directory with Vite + TypeScript setup ready for Next.js - Add comprehensive shared/ directory with: - Complete documentation and memory-bank archives - Media files and avatars (letters, park/ride images) - Deployment scripts and automation tools - Shared types and utilities - Add architecture/ directory with migration guides - Configure pnpm workspace for monorepo development - Update .gitignore to exclude .django_tailwind_cli/ build artifacts - Preserve all historical documentation in shared/docs/memory-bank/ - Set up proper structure for full-stack development with shared resources
This commit is contained in:
689
shared/scripts/vm/github-setup.py
Executable file
689
shared/scripts/vm/github-setup.py
Executable file
@@ -0,0 +1,689 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ThrillWiki GitHub PAT Setup Helper
|
||||
Interactive script for setting up GitHub Personal Access Tokens with proper validation
|
||||
and integration with the automation system.
|
||||
|
||||
Features:
|
||||
- Guided GitHub PAT creation process
|
||||
- Token validation and permission checking
|
||||
- Integration with existing github-auth.py patterns
|
||||
- Clear instructions for PAT scope requirements
|
||||
- Secure token storage with proper file permissions
|
||||
"""
|
||||
|
||||
import sys
|
||||
import getpass
|
||||
import requests
|
||||
import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
PROJECT_DIR = SCRIPT_DIR.parent.parent
|
||||
CONFIG_SCRIPT = SCRIPT_DIR / "automation-config.sh"
|
||||
GITHUB_AUTH_SCRIPT = PROJECT_DIR / "scripts" / "github-auth.py"
|
||||
TOKEN_FILE = PROJECT_DIR / ".github-pat"
|
||||
|
||||
# GitHub API Configuration
|
||||
GITHUB_API_BASE = "https://api.github.com"
|
||||
REQUEST_TIMEOUT = 30
|
||||
|
||||
# Token scope requirements for different use cases
|
||||
TOKEN_SCOPES = {
|
||||
"public": {
|
||||
"description": "Public repositories only",
|
||||
"scopes": ["public_repo"],
|
||||
"note": "Suitable for public repositories and basic automation",
|
||||
},
|
||||
"private": {
|
||||
"description": "Private repositories access",
|
||||
"scopes": ["repo"],
|
||||
"note": "Required for private repositories and full automation features",
|
||||
},
|
||||
"full": {
|
||||
"description": "Full automation capabilities",
|
||||
"scopes": ["repo", "workflow", "read:org"],
|
||||
"note": "Recommended for complete automation setup with GitHub Actions",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Colors:
|
||||
"""ANSI color codes for terminal output"""
|
||||
|
||||
RED = "\033[0;31m"
|
||||
GREEN = "\033[0;32m"
|
||||
YELLOW = "\033[1;33m"
|
||||
BLUE = "\033[0;34m"
|
||||
PURPLE = "\033[0;35m"
|
||||
CYAN = "\033[0;36m"
|
||||
BOLD = "\033[1m"
|
||||
NC = "\033[0m" # No Color
|
||||
|
||||
|
||||
def print_colored(message, color=Colors.NC):
|
||||
"""Print colored message to terminal"""
|
||||
print(f"{color}{message}{Colors.NC}")
|
||||
|
||||
|
||||
def print_error(message):
|
||||
"""Print error message"""
|
||||
print_colored(f"❌ Error: {message}", Colors.RED)
|
||||
|
||||
|
||||
def print_success(message):
|
||||
"""Print success message"""
|
||||
print_colored(f"✅ {message}", Colors.GREEN)
|
||||
|
||||
|
||||
def print_warning(message):
|
||||
"""Print warning message"""
|
||||
print_colored(f"⚠️ Warning: {message}", Colors.YELLOW)
|
||||
|
||||
|
||||
def print_info(message):
|
||||
"""Print info message"""
|
||||
print_colored(f"ℹ️ {message}", Colors.BLUE)
|
||||
|
||||
|
||||
def print_step(step, total, message):
|
||||
"""Print step progress"""
|
||||
print_colored(f"\n[{step}/{total}] {message}", Colors.CYAN)
|
||||
|
||||
|
||||
def validate_token_format(token):
|
||||
"""Validate GitHub token format"""
|
||||
if not token:
|
||||
return False
|
||||
|
||||
# GitHub token patterns
|
||||
patterns = [
|
||||
lambda t: t.startswith("ghp_") and len(t) >= 40, # Classic PAT
|
||||
lambda t: t.startswith("github_pat_") and len(t) >= 50, # Fine-grained PAT
|
||||
lambda t: t.startswith("gho_") and len(t) >= 40, # OAuth token
|
||||
lambda t: t.startswith("ghu_") and len(t) >= 40, # User token
|
||||
lambda t: t.startswith("ghs_") and len(t) >= 40, # Server token
|
||||
]
|
||||
|
||||
return any(pattern(token) for pattern in patterns)
|
||||
|
||||
|
||||
def test_github_token(token, timeout=REQUEST_TIMEOUT):
|
||||
"""Test GitHub token by making API call"""
|
||||
if not token:
|
||||
return False, "No token provided"
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
f"{GITHUB_API_BASE}/user", headers=headers, timeout=timeout
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
user_data = response.json()
|
||||
return (
|
||||
True,
|
||||
f"Valid token for user: {
|
||||
user_data.get(
|
||||
'login', 'unknown')}",
|
||||
)
|
||||
elif response.status_code == 401:
|
||||
return False, "Invalid or expired token"
|
||||
elif response.status_code == 403:
|
||||
return False, "Token lacks required permissions"
|
||||
else:
|
||||
return (
|
||||
False,
|
||||
f"API request failed with HTTP {
|
||||
response.status_code}",
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return False, f"Network error: {str(e)}"
|
||||
|
||||
|
||||
def get_token_permissions(token, timeout=REQUEST_TIMEOUT):
|
||||
"""Get token permissions and scopes"""
|
||||
if not token:
|
||||
return None, "No token provided"
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
}
|
||||
|
||||
# Get user info and check token in response headers
|
||||
response = requests.get(
|
||||
f"{GITHUB_API_BASE}/user", headers=headers, timeout=timeout
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
scopes = response.headers.get("X-OAuth-Scopes", "").split(", ")
|
||||
scopes = [scope.strip() for scope in scopes if scope.strip()]
|
||||
|
||||
return scopes, None
|
||||
else:
|
||||
return (
|
||||
None,
|
||||
f"Failed to get permissions: HTTP {
|
||||
response.status_code}",
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return None, f"Network error: {str(e)}"
|
||||
|
||||
|
||||
def check_repository_access(token, repo_url=None, timeout=REQUEST_TIMEOUT):
|
||||
"""Check if token can access the repository"""
|
||||
if not token:
|
||||
return False, "No token provided"
|
||||
|
||||
# Try to determine repository from git remote
|
||||
if not repo_url:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
cwd=PROJECT_DIR,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
repo_url = result.stdout.strip()
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
if not repo_url:
|
||||
return None, "Could not determine repository URL"
|
||||
|
||||
# Extract owner/repo from URL
|
||||
if "github.com" in repo_url:
|
||||
# Handle both SSH and HTTPS URLs
|
||||
if repo_url.startswith("git@github.com:"):
|
||||
repo_path = repo_url.replace("git@github.com:", "").replace(".git", "")
|
||||
elif "github.com/" in repo_url:
|
||||
repo_path = repo_url.split("github.com/")[-1].replace(".git", "")
|
||||
else:
|
||||
return None, "Could not parse repository URL"
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
f"{GITHUB_API_BASE}/repos/{repo_path}",
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
repo_data = response.json()
|
||||
return (
|
||||
True,
|
||||
f"Access confirmed for {
|
||||
repo_data.get(
|
||||
'full_name', repo_path)}",
|
||||
)
|
||||
elif response.status_code == 404:
|
||||
return False, "Repository not found or no access"
|
||||
elif response.status_code == 403:
|
||||
return False, "Access denied - insufficient permissions"
|
||||
else:
|
||||
return (
|
||||
False,
|
||||
f"Access check failed: HTTP {
|
||||
response.status_code}",
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return None, f"Network error: {str(e)}"
|
||||
|
||||
return None, "Not a GitHub repository"
|
||||
|
||||
|
||||
def show_pat_instructions():
|
||||
"""Show detailed PAT creation instructions"""
|
||||
print_colored("\n" + "=" * 60, Colors.BOLD)
|
||||
print_colored("GitHub Personal Access Token (PAT) Setup Guide", Colors.BOLD)
|
||||
print_colored("=" * 60, Colors.BOLD)
|
||||
|
||||
print("\n🔐 Why do you need a GitHub PAT?")
|
||||
print(" • Access private repositories")
|
||||
print(" • Avoid GitHub API rate limits")
|
||||
print(" • Enable automated repository operations")
|
||||
print(" • Secure authentication without passwords")
|
||||
|
||||
print("\n📋 Step-by-step PAT creation:")
|
||||
print(" 1. Go to: https://github.com/settings/tokens")
|
||||
print(" 2. Click 'Generate new token' → 'Generate new token (classic)'")
|
||||
print(" 3. Enter a descriptive note: 'ThrillWiki Automation'")
|
||||
print(" 4. Set expiration (recommended: 90 days for security)")
|
||||
print(" 5. Select appropriate scopes:")
|
||||
|
||||
print("\n🎯 Recommended scope configurations:")
|
||||
for scope_type, config in TOKEN_SCOPES.items():
|
||||
print(f"\n {scope_type.upper()} REPOSITORIES:")
|
||||
print(f" • Description: {config['description']}")
|
||||
print(f" • Required scopes: {', '.join(config['scopes'])}")
|
||||
print(f" • Note: {config['note']}")
|
||||
|
||||
print("\n⚡ Quick setup for most users:")
|
||||
print(" • Select 'repo' scope for full repository access")
|
||||
print(" • This enables all automation features")
|
||||
|
||||
print("\n🔒 Security best practices:")
|
||||
print(" • Use descriptive token names")
|
||||
print(" • Set reasonable expiration dates")
|
||||
print(" • Regenerate tokens regularly")
|
||||
print(" • Never share tokens in public")
|
||||
print(" • Delete unused tokens immediately")
|
||||
|
||||
print("\n📱 After creating your token:")
|
||||
print(" • Copy the token immediately (it won't be shown again)")
|
||||
print(" • Return to this script and paste it when prompted")
|
||||
print(" • The script will validate and securely store your token")
|
||||
|
||||
|
||||
def interactive_token_setup():
|
||||
"""Interactive token setup process"""
|
||||
print_colored("\n🚀 ThrillWiki GitHub PAT Setup", Colors.BOLD)
|
||||
print_colored("================================", Colors.BOLD)
|
||||
|
||||
# Check if token already exists
|
||||
if TOKEN_FILE.exists():
|
||||
try:
|
||||
existing_token = TOKEN_FILE.read_text().strip()
|
||||
if existing_token:
|
||||
print_info("Existing GitHub token found")
|
||||
|
||||
# Test existing token
|
||||
valid, message = test_github_token(existing_token)
|
||||
if valid:
|
||||
print_success(f"Current token is valid: {message}")
|
||||
|
||||
choice = (
|
||||
input("\nDo you want to replace the existing token? (y/N): ")
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if choice not in ["y", "yes"]:
|
||||
print_info("Keeping existing token")
|
||||
return True
|
||||
else:
|
||||
print_warning(f"Current token is invalid: {message}")
|
||||
print_info("Setting up new token...")
|
||||
except Exception as e:
|
||||
print_warning(f"Could not read existing token: {e}")
|
||||
|
||||
# Show instructions
|
||||
print("\n" + "=" * 50)
|
||||
choice = (
|
||||
input("Do you want to see PAT creation instructions? (Y/n): ").strip().lower()
|
||||
)
|
||||
if choice not in ["n", "no"]:
|
||||
show_pat_instructions()
|
||||
|
||||
# Get token from user
|
||||
print_step(1, 3, "Enter your GitHub Personal Access Token")
|
||||
print("📋 Please paste your GitHub PAT below:")
|
||||
print(" (Input will be hidden for security)")
|
||||
|
||||
while True:
|
||||
try:
|
||||
token = getpass.getpass("GitHub PAT: ").strip()
|
||||
|
||||
if not token:
|
||||
print_error("No token entered. Please try again.")
|
||||
continue
|
||||
|
||||
# Validate format
|
||||
if not validate_token_format(token):
|
||||
print_error(
|
||||
"Invalid token format. GitHub tokens should start with 'ghp_', 'github_pat_', etc."
|
||||
)
|
||||
retry = input("Try again? (Y/n): ").strip().lower()
|
||||
if retry in ["n", "no"]:
|
||||
return False
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nSetup cancelled by user")
|
||||
return False
|
||||
|
||||
# Test token
|
||||
print_step(2, 3, "Validating GitHub token")
|
||||
print("🔍 Testing token with GitHub API...")
|
||||
|
||||
valid, message = test_github_token(token)
|
||||
if not valid:
|
||||
print_error(f"Token validation failed: {message}")
|
||||
return False
|
||||
|
||||
print_success(message)
|
||||
|
||||
# Check permissions
|
||||
print("🔐 Checking token permissions...")
|
||||
scopes, error = get_token_permissions(token)
|
||||
if error:
|
||||
print_warning(f"Could not check permissions: {error}")
|
||||
else:
|
||||
print_success(
|
||||
f"Token scopes: {', '.join(scopes) if scopes else 'None detected'}"
|
||||
)
|
||||
|
||||
# Check for recommended scopes
|
||||
has_repo = "repo" in scopes or "public_repo" in scopes
|
||||
if not has_repo:
|
||||
print_warning("Token may lack repository access permissions")
|
||||
|
||||
# Check repository access
|
||||
print("📁 Checking repository access...")
|
||||
access, access_message = check_repository_access(token)
|
||||
if access is True:
|
||||
print_success(access_message)
|
||||
elif access is False:
|
||||
print_warning(access_message)
|
||||
else:
|
||||
print_info(access_message or "Repository access check skipped")
|
||||
|
||||
# Store token
|
||||
print_step(3, 3, "Storing GitHub token securely")
|
||||
|
||||
try:
|
||||
# Backup existing token if it exists
|
||||
if TOKEN_FILE.exists():
|
||||
backup_file = TOKEN_FILE.with_suffix(".backup")
|
||||
TOKEN_FILE.rename(backup_file)
|
||||
print_info(f"Existing token backed up to: {backup_file}")
|
||||
|
||||
# Write new token
|
||||
TOKEN_FILE.write_text(token)
|
||||
TOKEN_FILE.chmod(0o600) # Read/write for owner only
|
||||
|
||||
print_success(f"Token stored securely in: {TOKEN_FILE}")
|
||||
|
||||
# Try to update configuration via config script
|
||||
try:
|
||||
if CONFIG_SCRIPT.exists():
|
||||
subprocess.run(
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
f'source {CONFIG_SCRIPT} && store_github_token "{token}"',
|
||||
],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
)
|
||||
print_success("Token added to automation configuration")
|
||||
except Exception as e:
|
||||
print_warning(f"Could not update automation config: {e}")
|
||||
|
||||
print_success("GitHub PAT setup completed successfully!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print_error(f"Failed to store token: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def validate_existing_token():
|
||||
"""Validate existing GitHub token"""
|
||||
print_colored("\n🔍 GitHub Token Validation", Colors.BOLD)
|
||||
print_colored("===========================", Colors.BOLD)
|
||||
|
||||
if not TOKEN_FILE.exists():
|
||||
print_error("No GitHub token file found")
|
||||
print_info(f"Expected location: {TOKEN_FILE}")
|
||||
return False
|
||||
|
||||
try:
|
||||
token = TOKEN_FILE.read_text().strip()
|
||||
if not token:
|
||||
print_error("Token file is empty")
|
||||
return False
|
||||
|
||||
print_info("Validating stored token...")
|
||||
|
||||
# Format validation
|
||||
if not validate_token_format(token):
|
||||
print_error("Token format is invalid")
|
||||
return False
|
||||
|
||||
print_success("Token format is valid")
|
||||
|
||||
# API validation
|
||||
valid, message = test_github_token(token)
|
||||
if not valid:
|
||||
print_error(f"Token validation failed: {message}")
|
||||
return False
|
||||
|
||||
print_success(message)
|
||||
|
||||
# Check permissions
|
||||
scopes, error = get_token_permissions(token)
|
||||
if error:
|
||||
print_warning(f"Could not check permissions: {error}")
|
||||
else:
|
||||
print_success(
|
||||
f"Token scopes: {
|
||||
', '.join(scopes) if scopes else 'None detected'}"
|
||||
)
|
||||
|
||||
# Check repository access
|
||||
access, access_message = check_repository_access(token)
|
||||
if access is True:
|
||||
print_success(access_message)
|
||||
elif access is False:
|
||||
print_warning(access_message)
|
||||
else:
|
||||
print_info(access_message or "Repository access check inconclusive")
|
||||
|
||||
print_success("Token validation completed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print_error(f"Error reading token: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def remove_token():
|
||||
"""Remove stored GitHub token"""
|
||||
print_colored("\n🗑️ GitHub Token Removal", Colors.BOLD)
|
||||
print_colored("=========================", Colors.BOLD)
|
||||
|
||||
if not TOKEN_FILE.exists():
|
||||
print_info("No GitHub token file found")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Backup before removal
|
||||
backup_file = TOKEN_FILE.with_suffix(".removed")
|
||||
TOKEN_FILE.rename(backup_file)
|
||||
print_success(f"Token removed and backed up to: {backup_file}")
|
||||
|
||||
# Try to remove from config
|
||||
try:
|
||||
if CONFIG_SCRIPT.exists():
|
||||
subprocess.run(
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
f"source {CONFIG_SCRIPT} && remove_github_token",
|
||||
],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
)
|
||||
print_success("Token removed from automation configuration")
|
||||
except Exception as e:
|
||||
print_warning(f"Could not update automation config: {e}")
|
||||
|
||||
print_success("GitHub token removed successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print_error(f"Error removing token: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def show_token_status():
|
||||
"""Show current token status"""
|
||||
print_colored("\n📊 GitHub Token Status", Colors.BOLD)
|
||||
print_colored("======================", Colors.BOLD)
|
||||
|
||||
# Check token file
|
||||
print(f"📁 Token file: {TOKEN_FILE}")
|
||||
if TOKEN_FILE.exists():
|
||||
print_success("Token file exists")
|
||||
|
||||
# Check permissions
|
||||
perms = oct(TOKEN_FILE.stat().st_mode)[-3:]
|
||||
if perms == "600":
|
||||
print_success(f"File permissions: {perms} (secure)")
|
||||
else:
|
||||
print_warning(f"File permissions: {perms} (should be 600)")
|
||||
|
||||
# Quick validation
|
||||
try:
|
||||
token = TOKEN_FILE.read_text().strip()
|
||||
if token:
|
||||
if validate_token_format(token):
|
||||
print_success("Token format is valid")
|
||||
|
||||
# Quick API test
|
||||
valid, message = test_github_token(token, timeout=10)
|
||||
if valid:
|
||||
print_success(f"Token is valid: {message}")
|
||||
else:
|
||||
print_error(f"Token is invalid: {message}")
|
||||
else:
|
||||
print_error("Token format is invalid")
|
||||
else:
|
||||
print_error("Token file is empty")
|
||||
except Exception as e:
|
||||
print_error(f"Error reading token: {e}")
|
||||
else:
|
||||
print_warning("Token file not found")
|
||||
|
||||
# Check config integration
|
||||
print(f"\n⚙️ Configuration: {CONFIG_SCRIPT}")
|
||||
if CONFIG_SCRIPT.exists():
|
||||
print_success("Configuration script available")
|
||||
else:
|
||||
print_warning("Configuration script not found")
|
||||
|
||||
# Check existing GitHub auth script
|
||||
print(f"\n🔐 GitHub auth script: {GITHUB_AUTH_SCRIPT}")
|
||||
if GITHUB_AUTH_SCRIPT.exists():
|
||||
print_success("GitHub auth script available")
|
||||
else:
|
||||
print_warning("GitHub auth script not found")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main CLI interface"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ThrillWiki GitHub PAT Setup Helper",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s setup # Interactive token setup
|
||||
%(prog)s validate # Validate existing token
|
||||
%(prog)s status # Show token status
|
||||
%(prog)s remove # Remove stored token
|
||||
%(prog)s --help # Show this help
|
||||
|
||||
For detailed PAT creation instructions, run: %(prog)s setup
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"command",
|
||||
choices=["setup", "validate", "status", "remove", "help"],
|
||||
help="Command to execute",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--token", help="GitHub token to validate (for validate command)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--force", action="store_true", help="Force operation without prompts"
|
||||
)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
if args.command == "setup":
|
||||
success = interactive_token_setup()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
elif args.command == "validate":
|
||||
if args.token:
|
||||
# Validate provided token
|
||||
print_info("Validating provided token...")
|
||||
if validate_token_format(args.token):
|
||||
valid, message = test_github_token(args.token)
|
||||
if valid:
|
||||
print_success(message)
|
||||
sys.exit(0)
|
||||
else:
|
||||
print_error(message)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print_error("Invalid token format")
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Validate existing token
|
||||
success = validate_existing_token()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
elif args.command == "status":
|
||||
show_token_status()
|
||||
sys.exit(0)
|
||||
|
||||
elif args.command == "remove":
|
||||
if not args.force:
|
||||
confirm = (
|
||||
input("Are you sure you want to remove the GitHub token? (y/N): ")
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if confirm not in ["y", "yes"]:
|
||||
print_info("Operation cancelled")
|
||||
sys.exit(0)
|
||||
|
||||
success = remove_token()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
elif args.command == "help":
|
||||
parser.print_help()
|
||||
sys.exit(0)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nOperation cancelled by user")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print_error(f"Unexpected error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user