makeover-webview 0.46.0

The webview renderer for makeover-layout. Emits CSS, and is the one renderer that needs no palette: var() is the late binding, so resolution stays with the browser.
Documentation
#!/usr/bin/env python3
"""Do the tree's in-house `version` requirements still resolve?

DO NOT EDIT IN PLACE. The master is _private/infra/bootstrap/githooks/internal-deps.py.

Usage:
    internal-deps.py <tree-root> [repo-root]

With a repo root, only pairs that repo is on either side of can fail the run;
everything else is reported as a note. Without one, every pair is graded, which
is the whole-tree report:

    python3 internal-deps.py ~/Code

WHAT IT GRADES. Every dependency in the tree that carries both a `git` URL on one
of our forges and a `version` requirement, against the version in the working
copy of the crate that URL names. That is the pairing cargo enforces and the one
that broke: a requirement of "0.11" against a sibling that has moved to 0.14 is
not a warning, it is a graph that will not resolve on any machine.

WHY WORKING COPIES AND NOT REMOTES. `~/Code/.cargo/config.toml` patches every one
of these dependencies to the working copy in the tree, so what is on disk here is
what every local build reads. A bump that has not been pushed yet breaks its
consumers just as thoroughly, and finding that out at push time is the point.

WHAT IT DOES NOT GRADE, on purpose:

  crates.io deps      the makeover suite and friends resolve from the registry,
                      where working ahead of a release is normal and a tree
                      version above the published one is not a finding. The
                      sweep's `coherence` check grades those against the index.
  path deps           no version requirement to be wrong about.
  ranges and wildcards  `>=`, `<`, `*` and comma lists are deliberate statements
                      about a span, not a pin that drifts. Counted as unchecked.
"""

import os
import re
import sys
import tomllib

# The forges that make a git URL ours. A dependency on somebody else's git repo
# is not something this tree can forward-fix.
OURS = re.compile(r"(makenot\.work|git\.sr\.ht/~maxmj)", re.I)

# Directories that hold code we do not grade: retired, staged for deletion, or
# not ours. Mirrors the sweep's exclusions rather than inventing a second list.
SKIP_DIRS = {
    "target", ".git", "node_modules", "dist", "vendor",
    "_archive", "_scratch", "trash", "_meta", "vtebench",
}
MAX_DEPTH = 4

DEP_SECTIONS = ("dependencies", "dev-dependencies", "build-dependencies")


def manifests(root):
    """Every Cargo.toml in the tree, shallow-walked."""
    out = []
    stack = [(root, 0)]
    while stack:
        d, depth = stack.pop()
        try:
            entries = list(os.scandir(d))
        except OSError:
            continue
        for e in entries:
            if e.is_file() and e.name == "Cargo.toml":
                out.append(e.path)
            elif e.is_dir() and e.name not in SKIP_DIRS and depth < MAX_DEPTH:
                stack.append((e.path, depth + 1))
    return out


def load(path):
    try:
        with open(path, "rb") as fh:
            return tomllib.load(fh)
    except (OSError, tomllib.TOMLDecodeError):
        return None


def dep_tables(doc):
    """Every dependency table in a manifest, including per-target and workspace."""
    for section in DEP_SECTIONS:
        table = doc.get(section)
        if isinstance(table, dict):
            yield table
    for cfg in (doc.get("target") or {}).values():
        if not isinstance(cfg, dict):
            continue
        for section in DEP_SECTIONS:
            table = cfg.get(section)
            if isinstance(table, dict):
                yield table
    ws = doc.get("workspace") or {}
    table = ws.get("dependencies")
    if isinstance(table, dict):
        yield table


def parse_version(v):
    """A version as a 3-tuple, prerelease dropped. Junk sorts as (0, 0, 0)."""
    core = str(v).split("+")[0].split("-")[0]
    parts = []
    for piece in core.split(".")[:3]:
        try:
            parts.append(int(piece))
        except ValueError:
            parts.append(0)
    while len(parts) < 3:
        parts.append(0)
    return tuple(parts)


def satisfies(req, version):
    """Cargo's default (caret) requirement semantics. None means 'not graded'.

    The rule that matters here is the 0.x one: under 0.1.0 and above, the MINOR
    is the compatibility boundary, which is why a 0.11 requirement rejects 0.14
    outright rather than treating it as a newer patch.
    """
    req = req.strip()
    if not req or any(c in req for c in "<>*,~"):
        return None
    # A prerelease satisfies nothing that does not ask for a prerelease of the
    # same version, so a plain requirement rejects it. This is the shape the
    # maturity ladder produces at beta entry: a sibling at 1.0.0-beta.1 does not
    # resolve for a consumer requiring "1.0", and cargo says so.
    if "-" in str(version).split("+")[0] and "-" not in req:
        return False
    exact = req.startswith("=")
    req = req.lstrip("^=").strip()
    if not req:
        return None
    given = req.split(".")
    try:
        r = [int(p) for p in given[:3]]
    except ValueError:
        return None
    v = parse_version(version)
    if exact:
        return tuple(v[: len(r)]) == tuple(r)
    if r[0] > 0:
        return v[0] == r[0] and v[1:] >= tuple(r[1:] + [0] * (2 - len(r[1:])))
    if len(r) == 1:
        return v[0] == 0
    if r[1] > 0:
        return v[0] == 0 and v[1] == r[1] and v[2] >= (r[2] if len(r) > 2 else 0)
    # 0.0.x: every patch is its own compatibility island.
    if len(r) > 2:
        return v[:3] == (0, 0, r[2])
    return v[0] == 0 and v[1] == 0


def main():
    if len(sys.argv) < 2:
        print(__doc__.strip(), file=sys.stderr)
        return 2
    tree = os.path.realpath(sys.argv[1])
    repo = os.path.realpath(sys.argv[2]) if len(sys.argv) > 2 else None

    paths = manifests(tree)
    docs = {p: load(p) for p in paths}

    # Workspace versions first: a member saying `version.workspace = true` gets
    # its number from the root, and reporting it as 0.0.0 would be a false break.
    ws_version = {}
    for p, doc in docs.items():
        if not doc:
            continue
        v = ((doc.get("workspace") or {}).get("package") or {}).get("version")
        if isinstance(v, str):
            ws_version[os.path.dirname(p)] = v

    def resolve_version(manifest_path, pkg):
        v = pkg.get("version")
        if isinstance(v, str):
            return v
        d = os.path.dirname(manifest_path)
        while d.startswith(tree):
            if d in ws_version:
                return ws_version[d]
            d = os.path.dirname(d)
        return None

    # crate name -> (version, manifest path)
    versions = {}
    for p, doc in docs.items():
        if not doc:
            continue
        pkg = doc.get("package")
        if not isinstance(pkg, dict) or not isinstance(pkg.get("name"), str):
            continue
        v = resolve_version(p, pkg)
        if v:
            versions[pkg["name"]] = (v, p)

    broken, unchecked, absent, graded = [], 0, set(), 0
    for p, doc in docs.items():
        if not doc:
            continue
        for table in dep_tables(doc):
            for key, spec in table.items():
                if not isinstance(spec, dict):
                    continue
                git = spec.get("git")
                req = spec.get("version")
                if not isinstance(git, str) or not isinstance(req, str):
                    continue
                if not OURS.search(git):
                    continue
                name = spec.get("package") if isinstance(spec.get("package"), str) else key
                known = versions.get(name)
                if known is None:
                    # A repo that is not on this machine (ripgrow lives on mbp
                    # only). Not a finding: nothing here can be wrong about it.
                    absent.add(name)
                    continue
                verdict = satisfies(req, known[0])
                if verdict is None:
                    unchecked += 1
                    continue
                graded += 1
                if not verdict:
                    broken.append((p, name, req, known[0], known[1]))

    if not broken:
        print(
            f"pre-push: internal deps coherent ({graded} requirements"
            + (f", {unchecked} unchecked" if unchecked else "")
            + (f", {len(absent)} crates not in this tree" if absent else "")
            + ")."
        )
        return 0

    def rel(path):
        return os.path.relpath(path, tree)

    ours, theirs = [], []
    for item in broken:
        consumer_manifest, name, req, have, provider_manifest = item
        mine = repo is not None and (
            consumer_manifest.startswith(repo + os.sep)
            or provider_manifest.startswith(repo + os.sep)
        )
        (ours if mine else theirs).append(item)

    for consumer_manifest, name, req, have, provider_manifest in ours + theirs:
        print(
            f"  {rel(consumer_manifest)}: requires {name} \"{req}\", "
            f"the tree has {have} ({rel(provider_manifest)})",
            file=sys.stderr,
        )

    if repo is None:
        print(f"internal deps: {len(broken)} unresolvable requirements.", file=sys.stderr)
        return 1

    if not ours:
        # Somebody else's skew. Worth seeing, never worth blocking this push on:
        # a gate that fails for a reason the pusher cannot fix is a gate that
        # gets bypassed by reflex, and then it is not a gate.
        print(
            f"pre-push: {len(theirs)} unresolvable requirements elsewhere in the "
            "tree (listed above, not this push's).",
        )
        return 0

    print("", file=sys.stderr)
    print(
        "pre-push: this push leaves a dependency that cannot resolve.\n"
        "  A version requirement states which major a consumer was written against,\n"
        "  so bumping a library and fixing its consumers is one pass (CLAUDE.md,\n"
        "  \"a breaking bump of an in-house crate is forward-fixed, in the same pass\").\n"
        "  Fix: bump the requirement in the manifests above, make the consumers\n"
        "  compile, and push them with this one.",
        file=sys.stderr,
    )
    return 1


if __name__ == "__main__":
    sys.exit(main())