import os
import sys
import json
import subprocess
import argparse
import requests
from typing import Optional, Dict, Any
class GitLabRepoCreator:
def __init__(self, token: str, gitlab_url: str = "https://gitlab.com"):
self.token = token
self.gitlab_url = gitlab_url.rstrip('/')
self.api_url = f"{self.gitlab_url}/api/v4"
self.headers = {
"PRIVATE-TOKEN": token,
"Content-Type": "application/json"
}
def get_current_user(self) -> Dict[str, Any]:
response = requests.get(
f"{self.api_url}/user",
headers=self.headers
)
response.raise_for_status()
return response.json()
def check_repo_exists(self, project_name: str, username: str) -> bool:
response = requests.get(
f"{self.api_url}/projects/{username}%2F{project_name}",
headers=self.headers
)
return response.status_code == 200
def create_repository(self,
name: str = "pg-api",
description: str = None,
visibility: str = "public",
initialize_readme: bool = False) -> Dict[str, Any]:
if description is None:
description = "High-performance PostgreSQL REST API driver built with Rust"
data = {
"name": name,
"description": description,
"visibility": visibility,
"initialize_with_readme": initialize_readme,
"issues_enabled": True,
"merge_requests_enabled": True,
"wiki_enabled": True,
"snippets_enabled": False,
"container_registry_enabled": True,
"packages_enabled": True,
"auto_devops_enabled": False
}
response = requests.post(
f"{self.api_url}/projects",
headers=self.headers,
json=data
)
if response.status_code == 201:
return response.json()
elif response.status_code == 400:
error = response.json()
if "has already been taken" in str(error):
print(f"❌ Repository '{name}' already exists")
return None
response.raise_for_status()
return response.json()
def add_topics(self, project_id: int, topics: list) -> bool:
data = {"topics": topics}
response = requests.put(
f"{self.api_url}/projects/{project_id}",
headers=self.headers,
json=data
)
return response.status_code == 200
def create_branch_protection(self, project_id: int, branch: str = "main") -> bool:
data = {
"name": branch,
"push_access_level": 30, "merge_access_level": 30, "allow_force_push": False,
"code_owner_approval_required": False
}
response = requests.post(
f"{self.api_url}/projects/{project_id}/protected_branches",
headers=self.headers,
json=data
)
return response.status_code in [201, 409]
def setup_ci_variables(self, project_id: int, variables: Dict[str, str]) -> bool:
success = True
for key, value in variables.items():
data = {
"key": key,
"value": value,
"protected": False,
"masked": "TOKEN" in key or "SECRET" in key or "KEY" in key
}
response = requests.post(
f"{self.api_url}/projects/{project_id}/variables",
headers=self.headers,
json=data
)
if response.status_code not in [201, 409]: success = False
print(f"⚠️ Failed to add variable {key}")
return success
def check_ssh_key(self) -> bool:
try:
ssh_dir = os.path.expanduser("~/.ssh")
has_key = any(
os.path.exists(os.path.join(ssh_dir, key))
for key in ["id_rsa", "id_ed25519", "id_ecdsa"]
)
if not has_key:
return False
result = subprocess.run(
["ssh", "-T", "git@gitlab.com"],
capture_output=True,
text=True,
timeout=5
)
return "Welcome to GitLab" in result.stderr
except:
return False
def push_to_gitlab(self, repo_url: str, branch: str = "main", use_ssh: bool = None) -> bool:
try:
if not os.path.exists(".git"):
subprocess.run(["git", "init"], check=True)
subprocess.run(["git", "add", "."], check=True)
subprocess.run(["git", "commit", "-m", "Initial commit: pg-api PostgreSQL REST API driver"], check=True)
current_branch = subprocess.run(
["git", "branch", "--show-current"],
capture_output=True,
text=True,
check=True
).stdout.strip()
if current_branch != branch:
subprocess.run(["git", "branch", "-M", branch], check=True)
remotes = subprocess.run(
["git", "remote"],
capture_output=True,
text=True
).stdout.strip().split('\n')
if "origin" in remotes:
subprocess.run(["git", "remote", "remove", "origin"], check=True)
subprocess.run(["git", "remote", "add", "origin", repo_url], check=True)
subprocess.run(["git", "push", "-u", "origin", branch], check=True)
return True
except subprocess.CalledProcessError as e:
print(f"❌ Git operation failed: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="Create pg-api repository on GitLab")
parser.add_argument(
"--token",
required=True,
help="GitLab personal access token (needs 'api' scope)"
)
parser.add_argument(
"--gitlab-url",
default="https://gitlab.com",
help="GitLab instance URL (default: https://gitlab.com)"
)
parser.add_argument(
"--name",
default="pg-api",
help="Repository name (default: pg-api)"
)
parser.add_argument(
"--visibility",
choices=["public", "private", "internal"],
default="public",
help="Repository visibility (default: public)"
)
parser.add_argument(
"--push",
action="store_true",
help="Push local repository to GitLab after creation"
)
parser.add_argument(
"--setup-ci",
action="store_true",
help="Set up CI/CD variables"
)
parser.add_argument(
"--protect-main",
action="store_true",
help="Set up branch protection for main branch"
)
args = parser.parse_args()
creator = GitLabRepoCreator(args.token, args.gitlab_url)
try:
print("🔍 Getting user information...")
user = creator.get_current_user()
username = user["username"]
print(f"✅ Authenticated as: {username}")
if creator.check_repo_exists(args.name, username):
print(f"ℹ️ Repository '{args.name}' already exists")
repo_url = f"{args.gitlab_url}/{username}/{args.name}.git"
else:
print(f"📦 Creating repository '{args.name}'...")
project = creator.create_repository(
name=args.name,
visibility=args.visibility
)
if project:
print(f"✅ Repository created: {project['web_url']}")
project_id = project["id"]
repo_url = project["ssh_url_to_repo"]
print("🏷️ Adding topics...")
topics = [
"postgresql", "rust", "api", "rest-api",
"database", "driver", "axum", "tokio"
]
if creator.add_topics(project_id, topics):
print("✅ Topics added")
if args.protect_main:
print("🔒 Setting up branch protection...")
if creator.create_branch_protection(project_id):
print("✅ Branch protection enabled for 'main'")
if args.setup_ci:
print("⚙️ Setting up CI/CD variables...")
ci_vars = {
"CARGO_HOME": "${CI_PROJECT_DIR}/.cargo",
"RUST_BACKTRACE": "1"
}
if creator.setup_ci_variables(project_id, ci_vars):
print("✅ CI/CD variables configured")
else:
sys.exit(1)
if args.push:
print(f"📤 Preparing to push to GitLab...")
has_ssh = creator.check_ssh_key()
if has_ssh:
print("🔑 SSH key detected, using SSH for push...")
push_url = repo_url else:
print("🔐 No SSH key found, using HTTPS with token...")
https_url = repo_url.replace("git@gitlab.com:", "https://gitlab.com/").replace(".git", "")
push_url = https_url.replace("https://", f"https://oauth2:{args.token}@") + ".git"
if creator.push_to_gitlab(push_url):
print("✅ Code pushed successfully!")
web_url = repo_url.replace("git@gitlab.com:", "https://gitlab.com/").replace(".git", "")
print(f"\n🎉 Repository ready at: {web_url}")
print(f"\n📝 Next steps:")
print(f" 1. Visit your repository: {web_url}")
print(f" 2. Check the CI/CD pipeline: {web_url}/-/pipelines")
print(f" 3. Configure additional settings: {web_url}/-/settings/general")
else:
print("⚠️ Failed to push code. You can manually push with:")
if has_ssh:
print(f" git remote add origin {repo_url}")
print(f" git push -u origin main")
else:
print(" # Option 1: Configure SSH key first")
print(" ssh-keygen -t ed25519 -C 'your-email@example.com'")
print(" # Add the public key to GitLab: https://gitlab.com/-/profile/keys")
print(f" git remote add origin {repo_url}")
print(f" git push -u origin main")
print("\n # Option 2: Use HTTPS with token")
https_url = repo_url.replace("git@gitlab.com:", "https://gitlab.com/").replace(".git", "")
print(f" git remote add origin https://oauth2:{args.token}@gitlab.com/{username}/{args.name}.git")
print(f" git push -u origin main")
else:
has_ssh = creator.check_ssh_key()
print(f"\n📝 To push your code manually:")
if has_ssh:
print("🔑 SSH key detected. You can push directly:")
print(f" git remote add origin {repo_url}")
print(f" git push -u origin main")
else:
print("⚠️ No SSH key detected. You have two options:")
print("\n Option 1: Configure SSH key (recommended)")
print(" ssh-keygen -t ed25519 -C 'your-email@example.com'")
print(" # Add the public key to: https://gitlab.com/-/profile/keys")
print(f" git remote add origin {repo_url}")
print(f" git push -u origin main")
print("\n Option 2: Use HTTPS with token")
print(f" git remote add origin https://oauth2:{args.token}@gitlab.com/{username}/{args.name}.git")
print(f" git push -u origin main")
except requests.exceptions.RequestException as e:
print(f"❌ API request failed: {e}")
sys.exit(1)
except Exception as e:
print(f"❌ Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()