SPB Git

Repository Public

merge-registries.py 4.9 KB 60 lines python

1#!/usr/bin/env python32"""Resolve the predictable conflicts produced when two chain-family branches both extend the registries.3Rule: keep both sides. Run after `git merge <branch>` reports conflicts, then `pnpm install`, typecheck and test."""4import os, re, subprocess5R = re.compile(r"<<<<<<< HEAD\n(.*?)=======\n(.*?)>>>>>>> [^\n]+\n", re.S)6ENTRY = re.compile(r"([A-Za-z_][A-Za-z0-9_]*): ('[^']*'|[A-Za-z0-9_]+)")7def union_entries(a, b):8    """Two versions of `k: v, k: v, …` lines: HEAD's text plus the entries only the other side has, as extra lines."""9    have = {k for k, _ in ENTRY.findall(a)}10    extra = [ln for ln in b.splitlines() if any(k not in have for k, _ in ENTRY.findall(ln.split('//')[0]))]11    out = a12    for ln in extra:13        new = [(k, v) for k, v in ENTRY.findall(ln.split('//')[0]) if k not in have]14        comment = ('  //' + ln.split('//', 1)[1]) if '//' in ln and len(new) == len(ENTRY.findall(ln.split('//')[0])) else ''15        out += '  ' + ', '.join(f"{k}: {v}" for k, v in new) + ',' + comment + '\n'16    return out17def fix(path, fn):18    s = open(path).read()19    if '<<<<<<< HEAD' not in s: return20    open(path, 'w').write(R.sub(fn, s)); print('resolved', path)21def networks(m):22    a, b = m.group(1), m.group(2)23    if re.search(r"^\s+id: '", a, re.M) and re.search(r"^\s+id: '", b, re.M): return a + "  },\n" + b  # two network entries24    return a + ''.join(ln + '\n' for ln in b.splitlines() if ln not in a.splitlines())                    # comment lines25fix('packages/core/src/networks.ts', networks)26fix('packages/core/src/policy.ts', lambda m: union_entries(m.group(1), m.group(2)))27fix('services/api/src/server.ts', lambda m: union_entries(m.group(1), m.group(2)))28fix('apps/web/src/i18n.ts', lambda m: m.group(1) + m.group(2))29fix('scripts/smoke-chains.mts', lambda m: m.group(1))30fix('package.json', lambda m: ''.join(sorted(set((m.group(1) + m.group(2)).splitlines(True)))))  # devDependencies: keep both, sorted31# registries that are pure functions of the files present: regenerate32plug = sorted(f[:-3] for f in os.listdir('packages/core/src/plugins') if f.endswith('.ts') and f != 'index.ts')33open('packages/core/src/plugins/index.ts', 'w').write("// Chain family plugins. Importing this module registers every plugin with the registry in families.ts.\nimport { registerFamily } from '../families.ts';\n"34    + ''.join(f"import {{ {p}Plugin }} from './{p}.ts';\n" for p in plug) + f"\nfor (const p of [{', '.join(p + 'Plugin' for p in plug)}]) registerFamily(p);\n\n" + ''.join(f"export * from './{p}.ts';\n" for p in plug))35p = 'services/api/src/chains/index.ts'; s = open(p).read()36fams = sorted(set(plug))37s = R.sub(lambda m: m.group(1), s)38s = re.sub(r"(import \{ [a-z]+Adapter \} from '\./[a-z]+\.ts';\n)+", ''.join(f"import {{ {f}Adapter }} from './{f}.ts';\n" for f in sorted(['btc', 'evm', 'sol'] + fams)), s, count=1)39s = re.sub(r"export const ADAPTER_FACTORIES: Record<string, \(n: NetworkDef\) => Adapter> = \{[^}]*\};",40    "export const ADAPTER_FACTORIES: Record<string, (n: NetworkDef) => Adapter> = { " + ', '.join(f"{f}: {f}Adapter" for f in ['evm', 'btc', 'sol'] + fams) + " };", s)41open(p, 'w').write(s)42# swap provider registry: regenerate from the adapter files present (each exports create<Name>Provider)43pdir = 'packages/swap/src/providers'44if os.path.isdir(pdir):45    facs = []46    for f in sorted(os.listdir(pdir)):47        if not f.endswith('.ts') or f == 'index.ts': continue48        names = re.findall(r"export (?:function|const) (create[A-Za-z0-9]+Provider)\b", open(os.path.join(pdir, f)).read())49        # the factory named after the file (create<File>Provider); helper factories exported by the same file are not providers50        own = [x for x in names if x.lower() == 'create' + f[:-3].replace('-', '').lower() + 'provider']51        if own: facs.append((f, own[0]))52    open(os.path.join(pdir, 'index.ts'), 'w').write("// Provider registry (regenerated by scripts/merge-registries.py). One file per provider; each exports a factory returning a SwapProvider.\n// Odos is deliberately absent: its API was permanently shut down on 2026-07-30 (docs/swap/providers/odos.md).\nimport type { SwapProvider } from '../types.ts';\n"53        + ''.join(f"import {{ {fn} }} from './{f}';\n" for f, fn in facs)54        + "\nexport interface ProviderEnv { [key: string]: string | undefined }\n/** Every real provider adapter, in a fixed order. A provider that needs a key reports `configured: false` without one. */\nexport function createProviders(env: ProviderEnv = {}): SwapProvider[] {\n  return ["55        + ', '.join(f"{fn}(env)" for _, fn in facs) + "];\n}\n")56subprocess.run(['git', 'checkout', '--theirs', 'pnpm-lock.yaml'], capture_output=True)57left = subprocess.run("git diff --name-only --diff-filter=U", shell=True, capture_output=True, text=True).stdout.split()58bad = [f for f in left if f != 'pnpm-lock.yaml' and '<<<<<<<' in open(f).read()]59print('still conflicted:', bad or 'none')60
Files main
Menu

Theme