import os import sys import asyncio import subprocess import httpx current_dir = os.path.dirname(os.path.abspath(__file__)) backend_dir = os.path.join(current_dir, "backend") if not os.path.exists(backend_dir): print(f"❌ Erreur : Le dossier '{backend_dir}' est introuvable.") sys.exit(1) os.chdir(backend_dir) sys.path.append(".") try: from app.core.config import settings except ImportError as e: print(f"❌ Erreur d'importation des settings : {e}") print(f"Vérifie que ton dossier 'app' est bien dans '{backend_dir}'") sys.exit(1) COMPOSE_DIR = "." NETWORK_NAME = "arc-network" TOKEN_FILE = ".gitea_token" PROJECTS = { "infra": "arc-infra", "ai": "arc-ai", "app": "arc-app", "sandbox": "arc-sandbox" } def run_command(command, cwd=None): """Lance une commande système de manière synchrone.""" print(f"🚀 Exécution : {' '.join(command) if isinstance(command, list) else command}") try: subprocess.run(command, cwd=cwd, check=True, capture_output=False) return True except subprocess.CalledProcessError as e: print(f"❌ Erreur : {e}") return False async def wait_for_gitea(local_api_url): """Attend que l'API HTTP de Gitea soit pleinement initialisée.""" public_ping_url = f"{local_api_url}/version" max_retries = 20 print("⏳ Attente du démarrage complet de Gitea et des migrations de la base de données...") for i in range(max_retries): try: async with httpx.AsyncClient(verify=False) as client: resp = await client.get(public_ping_url) if resp.status_code == 200: print("✅ Gitea est prêt et opérationnel.") return True except Exception: pass if i < max_retries - 1: await asyncio.sleep(3) raise Exception(f"Gitea n'a pas répondu sur {public_ping_url} après plusieurs tentatives.") async def bootstrap_gitea_agent(settings_obj, local_api_url): """Crée un token d'accès d'application pour l'admin.""" if os.path.exists(TOKEN_FILE): with open(TOKEN_FILE, "r") as f: token = f.read().strip() if token: return token token_url = f"{local_api_url}/users/{settings_obj.gitea_admin_user}/tokens" async with httpx.AsyncClient(verify=False) as client: resp = await client.post( token_url, auth=(settings_obj.gitea_admin_user, settings_obj.gitea_admin_password), json={ "name": "dev-agent-token", "scopes": ["all"] } ) if resp.status_code in [200, 201]: token_data = resp.json() token = token_data.get('token') or token_data.get('sha1') if not token: raise Exception(f"Format de réponse inconnu pour le token : {token_data}") with open(TOKEN_FILE, "w") as f: f.write(token) return token elif resp.status_code in [400, 409]: print("⚠️ Le token existe déjà dans Gitea. Réinitialisation...") subprocess.run([ "docker", "exec", "-u", "git", "git-arc", "gitea", "admin", "token", "delete", "--username", settings_obj.gitea_admin_user, "--name", "dev-agent-token" ], capture_output=True) return await bootstrap_gitea_agent(settings_obj, local_api_url) else: raise Exception(f"Échec création token : {resp.status_code} - {resp.text}") async def main(): # 1. Réseau print("--- ÉTAPE 1 : RÉSEAU ---") create_net = subprocess.run( ["docker", "network", "create", NETWORK_NAME], capture_output=True, text=True, shell=(os.name == 'nt') ) if create_net.returncode == 0: print(f"✅ Réseau '{NETWORK_NAME}' créé.") else: print(f"✅ Réseau '{NETWORK_NAME}' déjà présent ou géré.") # 2. Infrastructure print(f"\n--- ÉTAPE 2 : INFRASTRUCTURE (Projet: {PROJECTS['infra']}) ---") infra_cmd = [ "docker", "compose", "-p", PROJECTS["infra"], "-f", f"{COMPOSE_DIR}/docker-compose-infra.yml", "up", "-d" ] if not run_command(infra_cmd): sys.exit(1) # 3. Provisioning Gitea print("\n--- ÉTAPE 3 : PROVISIONING GITEA ---") local_api_url = settings.gitea_api_url.replace("git-arc", "localhost") # Étape critique : on attend d'abord que le service web et la DB soient OK try: await wait_for_gitea(local_api_url) except Exception as e: print(f"❌ Erreur critique de démarrage : {e}") sys.exit(1) print("👤 Création / Vérification du compte administrateur Gitea...") result = subprocess.run([ "docker", "exec", "-u", "git", "git-arc", "gitea", "admin", "user", "create", "--username", settings.gitea_admin_user, "--password", settings.gitea_admin_password, "--email", "admin@arc.local", "--admin" ], capture_output=True, text=True) if result.returncode == 0: print("👤 Compte administrateur créé avec succès.") elif "user already exists" in result.stderr: print("👤 Compte administrateur existant et valide.") else: print(f"⚠️ Note Gitea CLI : {result.stderr.strip()}") try: agent_token = await bootstrap_gitea_agent(settings, local_api_url) os.environ["GITEA_TOKEN"] = agent_token print("✅ Provisioning Gitea réussi, token généré.") except Exception as e: print(f"❌ Erreur critique de provisioning : {e}") sys.exit(1) # 4. IA print(f"\n--- ÉTAPE 4 : IA (Projet: {PROJECTS['ai']}) ---") ai_cmd = [ "docker", "compose", "-p", PROJECTS["ai"], "-f", f"{COMPOSE_DIR}/docker-compose-ai.yml", "up", "-d" ] if not run_command(ai_cmd): sys.exit(1) print(f"\n--- ÉTAPE 5 : Sandbox (Projet: {PROJECTS['sandbox']}) ---") sandbox_cmd = [ "docker", "compose", "-p", PROJECTS["sandbox"], "-f", f"{COMPOSE_DIR}/docker-compose-sandbox.yml", "up", "-d", "--build" ] if not run_command(sandbox_cmd): sys.exit(1) # 5. Application print(f"\n--- ÉTAPE 6 : APPLICATION (Projet: {PROJECTS['app']}) ---") app_cmd = [ "docker", "compose", "-p", PROJECTS["app"], "-f", f"{COMPOSE_DIR}/docker-compose.yml", "up", "-d", "--build" ] run_command(app_cmd) if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: print("\n🛑 Arrêt demandé par l'utilisateur.")