195 lines
6.6 KiB
Python
195 lines
6.6 KiB
Python
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.") |