SPB Git forge
29commits 1branches 0releases
684.0 KBsize
maindefault branch
2 days agolast push
Python 95.8% Shell 4.2%
4.6 KB · 87 lines python
Raw Blame History
1"""`mld import <nœud>` : brouillons de manifestes à partir des processus PM2 / launchd vivants d'un nœud.23Regroupe par préfixe de nom (lou-ka-web, lou-ka-sync, lou-ka-ngrok → lou-ka ; un vieux tunnel ngrok devient une route4MacLustr Tunnel `tunnel.domain`). Les brouillons vont dans5~/dispatch/apps/_drafts/<app>.json ; on les relit, on ajuste, puis on les déplace dans ~/dispatch/apps/.6"""7import json8import os9import re10from . import config, manifest, nodes, ssh1112SUFFIXES = ("-web", "-sync", "-ngrok", "-api", "-tunnel", "-server", "-etl", "-scheduler", "-ingest", "-market",13            "-worker", "-proxy", "-crawler", "-enrichment", "-sitemaps", "-tun-search", "-tun-embed")14ENV_DROP_PREFIX = ("PM2_", "npm_", "__", "TERM", "SSH_", "LC_", "HOMEBREW", "ITERM", "XPC_", "COLOR", "LS_", "SHELL", "PWD", "OLDPWD",15                   "PATH", "HOME", "USER", "LOGNAME", "LANG", "TMPDIR", "SECURITYSESSIONID", "COMMAND_MODE", "unique_id", "FPATH",16                   "CLAUDE", "OTEL", "DISPLAY", "MallocNanoZone", "ZSH", "EDITOR", "PAGER", "LESS", "NoDefaultCurrentDirectoryInExePath",17                   "COREPACK", "AI_AGENT", "OSLogRateLimit", "GIT_", "ANTHROPIC_MODEL", "DISABLE_", "MANPATH", "INFOPATH", "STARSHIP",18                   "TERMINFO", "WINDOWID", "NVM", "P9K", "TMUX", "SSH", "_")192021def app_of(name):22    for s in SUFFIXES:23        if name.endswith(s):24            return name[: -len(s)]25    return name262728def clean_env(env, name):29    out = {}30    for k, v in (env or {}).items():31        if k == name or k.startswith(ENV_DROP_PREFIX) or not isinstance(v, str):32            continue33        out[k] = v34    return out353637def import_node(alias, apps_filter=None):38    ip = nodes.ip_of(alias)39    rc, out, err = ssh.run(ip, "pm2 jlist 2>/dev/null; echo; echo __MEM__; pm2 jlist 2>/dev/null | python3 -c \"import json,sys; print(json.dumps({p['name']: p.get('monit',{}).get('memory',0) for p in json.load(sys.stdin)}))\"", timeout=60)40    raw, _, mem = out.partition("__MEM__")41    L = json.loads(raw.strip() or "[]")42    mems = json.loads(mem.strip() or "{}")43    groups = {}44    for p in L:45        e = p.get("pm2_env", {})46        app = app_of(p["name"])47        if apps_filter and app not in apps_filter:48            continue49        g = groups.setdefault(app, {"app": app, "processes": [], "ngrok": None, "cwd": None, "ram": 0})50        g["ram"] += int(mems.get(p["name"], 0)) // (1024 * 1024)51        args = e.get("args") or []52        script = e.get("pm_exec_path") or ""53        if "ngrok" in script or (args and "ngrok" in " ".join(map(str, args))):54            blob = " ".join([script] + [str(a) for a in args])55            mo = re.search(r"--(?:url|domain)=(?:https?://)?([^\s]+)", blob)56            port = re.search(r"\s(\d{2,5})(?:\s|$)", blob)57            g["ngrok"] = {"name": p["name"], "url": mo.group(1) if mo else None, "port": int(port.group(1)) if port else None}58            continue59        g["processes"].append({60            "name": p["name"], "manager": "pm2", "script": script, "args": args,61            "interpreter": e.get("exec_interpreter") if e.get("exec_interpreter") not in (None, "none", "N/A") else None,62            "cwd": e.get("pm_cwd"), "env": clean_env(e.get("env"), p["name"]), "cron_restart": e.get("cron_restart"),63            "autorestart": e.get("autorestart", True), "max_memory_restart": e.get("max_memory_restart"),64        })65        g["cwd"] = g["cwd"] or e.get("pm_cwd")66    drafts = os.path.join(config.APPS_DIR, "_drafts")67    os.makedirs(drafts, exist_ok=True)68    written = []69    for app, g in groups.items():70        cwd = g["cwd"] or ""71        m = json.loads(json.dumps(manifest.DEFAULTS))72        mo = re.match(r"(/Users/[^/]+/(?:apps/)?[^/]+|/opt/[^/]+)", cwd)73        d = mo.group(1) if mo else cwd74        # un tunnel ngrok encore vivant sur le nœud devient une route MacLustr Tunnel (le processus ngrok sera supprimé au 1er start)75        ng = g["ngrok"] or {}76        m.update({"app": app, "label": app, "dir": d.replace(config.home_of(alias), "~"), "processes": g["processes"], "ngrok": None,77                  "tunnel": ({"domain": ng.get("url"), "gateway": config.TUNNEL_DEFAULT} if ng.get("url") else None),78                  "legacy_pm2": [ng["name"]] if ng.get("name") else [],79                  "domain": ng.get("url"), "port": ng.get("port"), "ram_mb_observed": max(g["ram"], 128)})80        m["requires"]["ports"] = [m["port"]] if m["port"] else []81        m["placement"]["reason"] = "importé de %s" % alias82        manifest.detect_requirements(m)83        p = os.path.join(drafts, app + ".json")84        json.dump(m, open(p, "w"), indent=2, ensure_ascii=False)85        written.append(p)86    return written87