fin étape 2 (dev_agent) + automatisation lancement projet/arrêt projet + séparation des docker-compose
This commit is contained in:
184
orchestrator.py
Normal file
184
orchestrator.py
Normal file
@@ -0,0 +1,184 @@
|
||||
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"
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
# 5. Application
|
||||
print(f"\n--- ÉTAPE 5 : 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.")
|
||||
Reference in New Issue
Block a user