-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
199 lines (167 loc) · 6.79 KB
/
main.py
File metadata and controls
199 lines (167 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import os
import sys
import subprocess
import requests
import argparse
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
GITHUB_API_URL = "https://api.github.com/user/repos"
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
def run_command(command, cwd=None, fail_on_error=True):
"""Runs a shell command and returns the output."""
try:
# Use shlex.split to safely parse command strings
import shlex
if isinstance(command, str):
command_list = shlex.split(command)
else:
command_list = command
result = subprocess.run(
command_list,
cwd=cwd,
check=True,
shell=False, # Disabled shell to prevent injection
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
if fail_on_error:
print(f"Error running command: {' '.join(command_list) if isinstance(command_list, list) else command}")
print(e.stderr)
sys.exit(1)
else:
raise e
def validate_git_ref(ref):
"""Validates Git reference names (branches, tags, etc.)"""
import re
# Git reference naming rules
if not ref or len(ref) > 255:
return False
# Basic validation - no spaces, special characters
if re.match(r'^[a-zA-Z0-9._/-]+$', ref):
return True
return False
def validate_remote_url(url):
"""Validates Git remote URL format"""
import re
# Support both HTTPS and SSH formats
https_pattern = r'^https://github\.com/[a-zA-Z0-9-]+/[a-zA-Z0-9._-]+\.git$'
ssh_pattern = r'^git@github\.com:[a-zA-Z0-9-]+/[a-zA-Z0-9._-]+\.git$'
if re.match(https_pattern, url) or re.match(ssh_pattern, url):
return True
return False
def get_repo_name(repo_path):
"""Uses the directory name as the repository name."""
return os.path.basename(os.path.abspath(repo_path))
def init_git(repo_path):
"""Initializes a git repository if one does not exist."""
git_dir = os.path.join(repo_path, ".git")
if not os.path.exists(git_dir):
print("Initializing Git repository...")
run_command("git init", cwd=repo_path)
# Rename branch to main to match modern defaults
run_command("git branch -M main", cwd=repo_path)
else:
print("Git repository already initialized.")
def create_github_repo(repo_name, private=False):
"""Creates a repository on GitHub."""
if not GITHUB_TOKEN:
print("Error: GITHUB_TOKEN not found in .env file.")
print("Please create a .env file with GITHUB_TOKEN=your_personal_access_token")
sys.exit(1)
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
data = {
"name": repo_name,
"private": private,
"description": f"Repository for {repo_name} created by RepoLauncher"
}
print(f"Creating GitHub repository '{repo_name}'...")
response = requests.post(GITHUB_API_URL, json=data, headers=headers)
if response.status_code == 201:
print("Repository created successfully!")
return response.json()["clone_url"]
elif response.status_code == 422:
print("Repository already exists on GitHub.")
# Try to construct the URL assuming the user owns it
user_resp = requests.get("https://api.github.com/user", headers=headers)
if user_resp.status_code == 200:
username = user_resp.json()["login"]
return f"https://github.com/{username}/{repo_name}.git"
else:
print("Could not retrieve username to construct remote URL.")
sys.exit(1)
else:
print(f"Failed to create repository: {response.status_code}")
print(response.json())
sys.exit(1)
def get_current_branch(repo_path):
"""Gets the current active git branch."""
try:
return run_command("git rev-parse --abbrev-ref HEAD", cwd=repo_path)
except subprocess.CalledProcessError:
# Git command failed, likely not in a git repository
return "main"
except Exception as e:
# Log unexpected errors for debugging
print(f"Warning: Unexpected error getting current branch: {e}")
return "main"
def push_to_github(remote_url, repo_path):
"""Adds remote and pushes code to GitHub."""
print("Configuring remote...")
# Validate remote URL
if not validate_remote_url(remote_url):
print(f"Error: Invalid remote URL format: {remote_url}")
sys.exit(1)
# Check if remote 'origin' already exists
try:
existing_remote = run_command("git remote get-url origin", cwd=repo_path, fail_on_error=False)
if existing_remote != remote_url:
print(f"Updating origin remote to {remote_url}")
run_command(["git", "remote", "set-url", "origin", remote_url], cwd=repo_path)
except subprocess.CalledProcessError:
# Command failed, meaning remote doesn't exist
run_command(["git", "remote", "add", "origin", remote_url], cwd=repo_path)
print("Adding files...")
run_command("git add .", cwd=repo_path)
# Check if there are changes to commit
try:
status = run_command("git status --porcelain", cwd=repo_path)
if status:
print("Committing changes...")
run_command('git commit -m "Initial commit by RepoLauncher"', cwd=repo_path)
else:
print("No changes to commit.")
except subprocess.CalledProcessError:
pass
current_branch = get_current_branch(repo_path)
# Validate branch name
if not validate_git_ref(current_branch):
print(f"Error: Invalid branch name: {current_branch}")
sys.exit(1)
print(f"Pushing branch '{current_branch}' to GitHub...")
run_command(["git", "push", "-u", "origin", current_branch], cwd=repo_path)
def main():
parser = argparse.ArgumentParser(description="Auto create and push git repo to GitHub.")
parser.add_argument("path", nargs="?", default=".", help="Path to the local repository (default: current directory)")
parser.add_argument("--private", action="store_true", help="Create a private repository")
args = parser.parse_args()
repo_path = os.path.abspath(args.path)
if not os.path.isdir(repo_path):
print(f"Error: Directory '{repo_path}' does not exist.")
sys.exit(1)
print("--- Repo Launcher ---")
print(f"Target Directory: {repo_path}")
repo_name = get_repo_name(repo_path)
print(f"Target Repository Name: {repo_name}")
init_git(repo_path)
remote_url = create_github_repo(repo_name, private=args.private)
push_to_github(remote_url, repo_path)
print("\nDone! Your repository is live.")
if __name__ == "__main__":
main()