Working V1

main
Hugo ODY 3 days ago
commit 008cbfd586

106
.gitignore vendored

@ -0,0 +1,106 @@
# Environment variables
.env
.env.local
.env.production
.env.development
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
*.log
migration.log
# Temporary directories for migration
temp_*
migration_*/

@ -0,0 +1,192 @@
# 🚀 Gitea to GitHub Migration Tool
Ce projet fournit un outil pratique et modulable pour migrer vos repositories de Gitea vers GitHub automatiquement.
## ✨ Fonctionnalités
- **Migration automatique** : Migre tous vos repositories Gitea vers GitHub en une seule commande
- **Migration sélective** : Choisissez spécifiquement quels repositories migrer
- **Support multi-propriétaire** : Migrez des repositories d'autres utilisateurs/organisations auxquels vous avez accès
- **Interface en ligne de commande** : Interface colorée et intuitive
- **Logging complet** : Suivi détaillé des opérations avec fichier de log
- **Gestion des erreurs** : Robuste avec gestion gracieuse des erreurs
## 🛠 Installation
1. **Clonez le repository** :
```bash
git clone https://github.com/votre-username/GiteaToGithubMigrator.git
cd GiteaToGithubMigrator
```
2. **Configuration automatique** :
```bash
./run.sh --setup
```
Le script va automatiquement :
- Créer un environnement virtuel Python
- Installer toutes les dépendances
- Créer le fichier de configuration `.env`
Cela créera un fichier `.env` que vous devrez remplir avec vos informations :
```env
# Gitea Configuration
GITEA_URL=https://votre-instance-gitea.com
GITEA_TOKEN=votre_token_gitea
GITEA_USERNAME=votre_nom_utilisateur_gitea
# GitHub Configuration
GITHUB_TOKEN=votre_token_github
GITHUB_USERNAME=votre_nom_utilisateur_github
```
## 🔑 Configuration des tokens
### Token Gitea
1. Allez dans **Settings****Applications** → **Generate New Token**
2. Donnez un nom au token et sélectionnez les permissions :
- `repo` (accès complet aux repositories)
- `user` (accès aux informations utilisateur)
### Token GitHub
1. Allez dans **Settings****Developer settings****Personal access tokens** → **Tokens (classic)**
2. Cliquez sur **Generate new token (classic)**
3. Sélectionnez les permissions :
- `repo` (accès complet aux repositories privés)
- `public_repo` (accès aux repositories publics)
## 🚀 Utilisation
Après avoir configuré vos tokens dans le fichier `.env`, utilisez le script de lancement :
### Migration de tous vos repositories
```bash
./run.sh
```
### Migration de repositories spécifiques
```bash
./run.sh --repos mon-repo autre-repo
```
### Migration de repositories d'autres propriétaires
```bash
./run.sh --repos proprietaire/repo-name
```
### Lister les repositories disponibles
```bash
./run.sh --list
```
### Mode verbose (plus de détails)
```bash
./run.sh --verbose
```
> **💡 Alternative** : Vous pouvez aussi utiliser directement `python migrate.py` si vous avez activé l'environnement virtuel (`source venv/bin/activate`)
## 📋 Exemples d'utilisation
### Exemple 1 : Migration complète
```bash
# Migre tous vos repositories
./run.sh
```
### Exemple 2 : Migration sélective
```bash
# Migre seulement les repositories spécifiés
./run.sh --repos projet-web api-backend
```
### Exemple 3 : Migration depuis une organisation
```bash
# Migre un repository d'une organisation
./run.sh --repos mon-org/projet-important
```
### Exemple 4 : Premier lancement (configuration)
```bash
# 1. Setup initial
./run.sh --setup
# 2. Éditez le fichier .env avec vos credentials
nano .env
# 3. Listez vos repositories disponibles
./run.sh --list
# 4. Lancez la migration
./run.sh
```
## 📊 Résultats
L'outil affiche un résumé détaillé à la fin :
- ✅ Nombre de migrations réussies
- ❌ Nombre de migrations échouées
- 📝 Détail par repository
Tous les logs sont également sauvegardés dans `migration.log`.
## 🔧 Structure du projet
```
GiteaToGithubMigrator/
├── migrate.py # Script principal
├── config.py # Gestion de la configuration
├── gitea_client.py # Client API Gitea
├── github_client.py # Client API GitHub
├── migration_tool.py # Logique de migration
├── requirements.txt # Dépendances Python
├── .env # Configuration (à créer)
└── README.md # Documentation
```
## ⚠️ Prérequis
- Python 3.7+
- Git installé sur votre système
- Accès aux APIs Gitea et GitHub
- Tokens d'authentification valides
## 🛡 Sécurité
- Les tokens sont stockés dans un fichier `.env` (ajoutez-le à `.gitignore`)
- Les URLs d'authentification ne sont jamais loggées
- Nettoyage automatique des repositories temporaires
## 🐛 Résolution de problèmes
### Erreur d'authentification
- Vérifiez que vos tokens sont valides et ont les bonnes permissions
- Assurez-vous que les noms d'utilisateur correspondent
### Erreur de clonage
- Vérifiez votre connexion internet
- Assurez-vous que Git est installé et accessible
### Repository déjà existant
- L'outil vérifie automatiquement l'existence sur GitHub
- Les repositories existants sont ignorés avec un avertissement
## 📝 Logs
Tous les détails d'exécution sont sauvegardés dans `migration.log` :
- Timestamps des opérations
- Détails des erreurs
- Statistiques de migration
## 🤝 Contribution
Les contributions sont les bienvenues ! N'hésitez pas à :
- Signaler des bugs
- Proposer des améliorations
- Soumettre des pull requests
## 📄 Licence
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.

@ -0,0 +1,45 @@
"""
Configuration module for Gitea to GitHub migration tool
"""
import os
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
class Config:
"""Configuration class for migration settings"""
def __init__(self):
self.gitea_url = os.getenv('GITEA_URL', 'https://codefirst.iut.uca.fr/git')
self.gitea_token = os.getenv('GITEA_TOKEN')
self.github_token = os.getenv('GITHUB_TOKEN')
self.github_username = os.getenv('GITHUB_USERNAME')
self.gitea_username = os.getenv('GITEA_USERNAME')
# Validate required configuration
self._validate_config()
def _validate_config(self):
"""Validate that all required configuration is present"""
missing = []
if not self.gitea_token:
missing.append('GITEA_TOKEN')
if not self.github_token:
missing.append('GITHUB_TOKEN')
if not self.github_username:
missing.append('GITHUB_USERNAME')
if not self.gitea_username:
missing.append('GITEA_USERNAME')
if missing:
raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
def is_valid(self):
"""Check if configuration is valid"""
try:
self._validate_config()
return True
except ValueError:
return False

@ -0,0 +1,87 @@
"""
Gitea API client for repository operations
"""
import requests
import json
from typing import List, Dict, Optional
class GiteaClient:
"""Client for interacting with Gitea API"""
def __init__(self, base_url: str, token: str, username: str):
self.base_url = base_url.rstrip('/')
self.token = token
self.username = username
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'token {token}',
'Content-Type': 'application/json'
})
def get_user_repos(self) -> List[Dict]:
"""Get all repositories owned by the user"""
url = f"{self.base_url}/api/v1/user/repos"
params = {
'limit': 100, # Adjust as needed
'page': 1
}
all_repos = []
while True:
response = self.session.get(url, params=params)
response.raise_for_status()
repos = response.json()
if not repos:
break
all_repos.extend(repos)
params['page'] += 1
# Break if we got less than the limit (last page)
if len(repos) < params.get('limit', 100):
break
return all_repos
def get_repo_info(self, owner: str, repo_name: str) -> Optional[Dict]:
"""Get information about a specific repository"""
url = f"{self.base_url}/api/v1/repos/{owner}/{repo_name}"
try:
response = self.session.get(url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException:
return None
def get_repo_clone_url(self, owner: str, repo_name: str) -> str:
"""Get the clone URL for a repository with authentication"""
return f"{self.base_url}/{owner}/{repo_name}.git"
def list_accessible_repos(self) -> List[Dict]:
"""List all repositories the user has access to (including organizations)"""
url = f"{self.base_url}/api/v1/user/repos"
params = {
'limit': 100,
'page': 1
}
all_repos = []
while True:
response = self.session.get(url, params=params)
response.raise_for_status()
repos = response.json()
if not repos:
break
all_repos.extend(repos)
params['page'] += 1
if len(repos) < params.get('limit', 100):
break
return all_repos

@ -0,0 +1,54 @@
"""
GitHub API client for repository operations
"""
from github import Github
from github.GithubException import GithubException
from typing import Optional, Dict
import logging
logger = logging.getLogger(__name__)
class GitHubClient:
"""Client for interacting with GitHub API"""
def __init__(self, token: str, username: str):
self.github = Github(token)
self.username = username
self.user = self.github.get_user()
def create_repository(self, repo_name: str, description: str = "", private: bool = False) -> bool:
"""Create a new repository on GitHub"""
try:
# Check if repository already exists
if self.repository_exists(repo_name):
logger.warning(f"Repository {repo_name} already exists on GitHub")
return True
self.user.create_repo(
name=repo_name,
description=description,
private=private,
auto_init=False # Don't auto-init since we'll push existing content
)
logger.info(f"Created repository: {repo_name}")
return True
except GithubException as e:
logger.error(f"Failed to create repository {repo_name}: {e}")
return False
def repository_exists(self, repo_name: str) -> bool:
"""Check if a repository exists"""
try:
self.user.get_repo(repo_name)
return True
except GithubException:
return False
def get_repo_clone_url(self, repo_name: str) -> str:
"""Get the clone URL for a GitHub repository"""
return f"https://github.com/{self.username}/{repo_name}.git"
def get_authenticated_clone_url(self, repo_name: str, token: str) -> str:
"""Get the authenticated clone URL for pushing"""
return f"https://{token}@github.com/{self.username}/{repo_name}.git"

@ -0,0 +1,189 @@
#!/usr/bin/env python3
"""
Gitea to GitHub Migration Tool
This script migrates repositories from Gitea to GitHub.
It can migrate all user repositories or specific ones.
"""
import argparse
import logging
import sys
from colorama import init, Fore, Style
from pathlib import Path
from config import Config
from migration_tool import MigrationTool
# Initialize colorama for cross-platform colored output
init()
def setup_logging(verbose: bool = False):
"""Setup logging configuration"""
level = logging.DEBUG if verbose else logging.INFO
format_str = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(
level=level,
format=format_str,
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('migration.log')
]
)
def print_banner():
"""Print application banner"""
banner = f"""
{Fore.CYAN}
🚀 Gitea to GitHub Migration Tool 🚀
Migrates your repositories from Gitea to GitHub seamlessly
{Style.RESET_ALL}
"""
print(banner)
def print_success_summary(results: dict):
"""Print migration results summary"""
successful = sum(1 for success in results.values() if success)
total = len(results)
print(f"\n{Fore.GREEN}{'='*60}")
print(f" MIGRATION SUMMARY")
print(f"{'='*60}{Style.RESET_ALL}")
print(f"{Fore.GREEN}✅ Successful migrations: {successful}/{total}{Style.RESET_ALL}")
if successful < total:
print(f"{Fore.RED}❌ Failed migrations: {total - successful}/{total}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}Detailed results:{Style.RESET_ALL}")
for repo, success in results.items():
status = f"{Fore.GREEN}✅ SUCCESS" if success else f"{Fore.RED}❌ FAILED"
print(f" {repo}: {status}{Style.RESET_ALL}")
def create_env_template():
"""Create a .env template file if it doesn't exist"""
env_file = Path('.env')
if not env_file.exists():
template = """# Gitea Configuration
GITEA_URL=https://your-gitea-instance.com
GITEA_TOKEN=your_gitea_personal_access_token
GITEA_USERNAME=your_gitea_username
# GitHub Configuration
GITHUB_TOKEN=your_github_personal_access_token
GITHUB_USERNAME=your_github_username
"""
env_file.write_text(template)
print(f"{Fore.YELLOW}📝 Created .env template file. Please fill it with your credentials.{Style.RESET_ALL}")
return False
return True
def main():
"""Main application entry point"""
parser = argparse.ArgumentParser(
description="Migrate repositories from Gitea to GitHub",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s # Migrate all your repositories
%(prog)s --repos repo1 repo2 # Migrate specific repositories
%(prog)s --repos owner/repo1 # Migrate repositories from other owners
%(prog)s --list # List available repositories
%(prog)s --verbose # Enable verbose logging
"""
)
parser.add_argument(
'--repos',
nargs='+',
help='Specific repositories to migrate (format: repo_name or owner/repo_name)'
)
parser.add_argument(
'--list',
action='store_true',
help='List all available repositories and exit'
)
parser.add_argument(
'--verbose', '-v',
action='store_true',
help='Enable verbose logging'
)
parser.add_argument(
'--setup',
action='store_true',
help='Create .env template file'
)
args = parser.parse_args()
print_banner()
setup_logging(args.verbose)
# Handle setup command
if args.setup:
create_env_template()
return
# Check if .env file exists
if not create_env_template():
return
try:
# Initialize configuration
config = Config()
# Initialize migration tool
migration_tool = MigrationTool(config)
# Handle list command
if args.list:
print(f"{Fore.CYAN}📋 Available repositories:{Style.RESET_ALL}")
repos = migration_tool.list_available_repos()
for repo in repos:
owner = repo['owner']['login']
name = repo['name']
private = "🔒 Private" if repo.get('private', False) else "🌐 Public"
description = repo.get('description', 'No description')
print(f" {Fore.BLUE}{owner}/{name}{Style.RESET_ALL} - {private}")
if description:
print(f" 📝 {description}")
print(f"\n{Fore.GREEN}Total repositories: {len(repos)}{Style.RESET_ALL}")
return
# Perform migration
if args.repos:
print(f"{Fore.CYAN}🎯 Migrating specific repositories: {', '.join(args.repos)}{Style.RESET_ALL}")
results = migration_tool.migrate_specific_repos(args.repos)
else:
print(f"{Fore.CYAN}🚀 Migrating all your repositories...{Style.RESET_ALL}")
results = migration_tool.migrate_all_user_repos()
# Print results
if results:
print_success_summary(results)
else:
print(f"{Fore.YELLOW}⚠️ No repositories found to migrate.{Style.RESET_ALL}")
except ValueError as e:
print(f"{Fore.RED}❌ Configuration error: {e}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}💡 Run '{sys.argv[0]} --setup' to create a configuration template.{Style.RESET_ALL}")
sys.exit(1)
except Exception as e:
logging.error(f"Unexpected error: {e}")
print(f"{Fore.RED}❌ An unexpected error occurred. Check migration.log for details.{Style.RESET_ALL}")
sys.exit(1)
if __name__ == '__main__':
main()

@ -0,0 +1,161 @@
"""
Main migration tool for transferring repositories from Gitea to GitHub
"""
import os
import subprocess
import tempfile
import shutil
import logging
from typing import List, Dict, Optional
from pathlib import Path
from config import Config
from gitea_client import GiteaClient
from github_client import GitHubClient
logger = logging.getLogger(__name__)
class MigrationTool:
"""Main tool for migrating repositories from Gitea to GitHub"""
def __init__(self, config: Config):
self.config = config
self.gitea_client = GiteaClient(
config.gitea_url,
config.gitea_token,
config.gitea_username
)
self.github_client = GitHubClient(
config.github_token,
config.github_username
)
def migrate_all_user_repos(self) -> Dict[str, bool]:
"""Migrate all repositories owned by the user"""
logger.info("Fetching user repositories from Gitea...")
repos = self.gitea_client.get_user_repos()
results = {}
for repo in repos:
if repo['owner']['login'] == self.config.gitea_username:
repo_name = repo['name']
logger.info(f"Migrating repository: {repo_name}")
success = self.migrate_repository(repo)
results[repo_name] = success
return results
def migrate_specific_repos(self, repo_specs: List[str]) -> Dict[str, bool]:
"""
Migrate specific repositories
repo_specs: List of repository specifications in format 'owner/repo' or just 'repo'
"""
results = {}
for repo_spec in repo_specs:
if '/' in repo_spec:
owner, repo_name = repo_spec.split('/', 1)
else:
owner = self.config.gitea_username
repo_name = repo_spec
logger.info(f"Migrating repository: {owner}/{repo_name}")
repo_info = self.gitea_client.get_repo_info(owner, repo_name)
if repo_info:
success = self.migrate_repository(repo_info)
results[f"{owner}/{repo_name}"] = success
else:
logger.error(f"Repository {owner}/{repo_name} not found or not accessible")
results[f"{owner}/{repo_name}"] = False
return results
def migrate_repository(self, repo_info: Dict) -> bool:
"""Migrate a single repository"""
repo_name = repo_info['name']
repo_owner = repo_info['owner']['login']
try:
# Create GitHub repository
success = self.github_client.create_repository(
repo_name=repo_name,
description=repo_info.get('description', ''),
private=repo_info.get('private', False)
)
if not success:
return False
# Clone and push repository
return self._clone_and_push_repo(repo_owner, repo_name)
except Exception as e:
logger.error(f"Failed to migrate repository {repo_name}: {e}")
return False
def _clone_and_push_repo(self, repo_owner: str, repo_name: str) -> bool:
"""Clone repository from Gitea and push to GitHub"""
temp_dir = None
try:
# Create temporary directory
temp_dir = tempfile.mkdtemp(prefix=f"migration_{repo_name}_")
repo_path = Path(temp_dir) / repo_name
# Clone from Gitea
gitea_url = self._get_authenticated_gitea_url(repo_owner, repo_name)
clone_cmd = ['git', 'clone', '--mirror', gitea_url, str(repo_path)]
logger.info(f"Cloning repository from Gitea: {repo_owner}/{repo_name}")
result = subprocess.run(clone_cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"Failed to clone repository: {result.stderr}")
return False
# Change to repository directory
os.chdir(repo_path)
# Add GitHub remote
github_url = self.github_client.get_authenticated_clone_url(
repo_name,
self.config.github_token
)
add_remote_cmd = ['git', 'remote', 'add', 'github', github_url]
result = subprocess.run(add_remote_cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"Failed to add GitHub remote: {result.stderr}")
return False
# Push to GitHub
logger.info(f"Pushing repository to GitHub: {repo_name}")
push_cmd = ['git', 'push', '--mirror', 'github']
result = subprocess.run(push_cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"Failed to push to GitHub: {result.stderr}")
return False
logger.info(f"Successfully migrated repository: {repo_name}")
return True
except Exception as e:
logger.error(f"Error during repository migration: {e}")
return False
finally:
# Clean up temporary directory
if temp_dir and os.path.exists(temp_dir):
shutil.rmtree(temp_dir, ignore_errors=True)
def _get_authenticated_gitea_url(self, owner: str, repo_name: str) -> str:
"""Get authenticated Gitea URL for cloning"""
base_url = self.config.gitea_url.replace('https://', '').replace('http://', '')
return f"https://{self.config.gitea_username}:{self.config.gitea_token}@{base_url}/{owner}/{repo_name}.git"
def list_available_repos(self) -> List[Dict]:
"""List all repositories available for migration"""
return self.gitea_client.list_accessible_repos()

@ -0,0 +1,4 @@
requests==2.31.0
PyGithub==1.59.1
python-dotenv==1.0.0
colorama==0.4.6

@ -0,0 +1,15 @@
#!/bin/bash
# Gitea to GitHub Migration Tool Launcher
# Check if virtual environment exists
if [ ! -d "venv" ]; then
echo "🔧 Setting up virtual environment..."
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
else
source venv/bin/activate
fi
# Run the migration tool with all passed arguments
python migrate.py "$@"
Loading…
Cancel
Save