← back to Build Feature Coverage

scan_fast.py

59 lines

import json, os, subprocess, time
SP="/private/tmp/claude-501/-Users-macstudio3/a14d95c4-e5cf-4423-9f72-c05a33fa0445/scratchpad/covmatrix"
REG=os.path.expanduser("~/Projects/dw-fleet-registry/dw-fleet-registry.json")
feats=json.load(open(f"{SP}/features.json"))
reg=json.load(open(REG))
sites=[s for s in reg["sites"] if "standalone-node" in s.get("servingModes",[])]

# flatten features -> (group, name, patterns)
flat=[]
for grp,items in feats.items():
    for name,pats in items.items():
        flat.append((grp,name,pats))

def scan_repo(repo, patterns):
    # one grep per feature (alternation), recursive, case-insensitive, list files only
    alt="|".join(patterns)
    cmd=["grep","-rilE","--include=*.html","--include=*.js","--include=*.mjs","--include=*.css",
         "--include=*.liquid","--include=*.ejs","","--include=*.htm",
         "--exclude-dir=node_modules","--exclude-dir=.git","--exclude-dir=dist","--exclude-dir=build","--exclude-dir=logs","--exclude-dir=tmp","--exclude-dir=data","--exclude-dir=backups",
         "--exclude=*.bak*","--exclude=package-lock.json","-e",alt,repo]
    try:
        r=subprocess.run(cmd,capture_output=True,text=True,timeout=20)
        hits=[l for l in r.stdout.splitlines() if l.strip()]
        return hits
    except subprocess.TimeoutExpired:
        return None  # NOT-MEASURED

matrix={}   # site -> {feature: bool}
evidence={} # site -> {feature: sample file}
site_meta={}
t0=time.time()
for i,s in enumerate(sites):
    slug=s["slug"]
    repo=(s.get("local") or {}).get("repoPath")
    live=(s.get("live") or {}).get("state","?")
    site_meta[slug]={"repo":repo,"live":live,"domain":s.get("domain"),
                     "hasRepo": bool(repo and os.path.isdir(repo))}
    matrix[slug]={}; evidence[slug]={}
    if not (repo and os.path.isdir(repo)):
        for grp,name,pats in flat: matrix[slug][name]=None  # NOT-MEASURED
        continue
    for grp,name,pats in flat:
        hits=scan_repo(repo,pats)
        if hits is None:
            matrix[slug][name]=None
        else:
            matrix[slug][name]=len(hits)>0
            if hits:
                # store a short evidence path (relative)
                ev=hits[0].replace(repo+"/","")
                evidence[slug][name]=ev
    if (i+1)%15==0: print(f"  scanned {i+1}/{len(sites)} ({time.time()-t0:.0f}s)")

out={"generatedAt":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()),
     "axis_features":[{"group":g,"name":n} for g,n,_ in flat],
     "site_meta":site_meta,"matrix":matrix,"evidence":evidence}
json.dump(out,open(f"{SP}/matrix.json","w"),indent=1)
print(f"DONE {len(sites)} sites x {len(flat)} features in {time.time()-t0:.0f}s -> matrix.json")