git-browser – main [download] x.py [raw]

import argparse
import functools as ft
import os
import pathlib
import subprocess

self_path = pathlib.Path(__file__).parent.resolve()

_md = lambda effect: lambda f: [f, effect(f)][0]
_ps = lambda o: vars(o).setdefault("__chmp__", {})
_as = lambda o: _ps(o).setdefault("__args__", [])
cmd = lambda **kw: _md(lambda f: _ps(f).update(kw))
arg = lambda *a, **k: _md(lambda f: _as(f).insert(0, (a, k)))

with_config = lambda: lambda f: ft.wraps(f)(lambda *a, **k: _with_config(f, a, k))

_config = None


@cmd()
@arg("--backtrace", action="store_true", default=False)
def precommit(backtrace=False):
    format()
    cargo("clippy")
    test()


@cmd()
def format():
    cargo("fmt")


@cmd()
@arg("--backtrace", action="store_true", default=False)
@arg("--coverage", action="store_true", default=False)
def test(backtrace=False, coverage=False):
    if coverage:
        mkdir(f"target/coverage/html")
    rm(*self_path.joinpath("target", "coverage").glob("*.profraw"))
    cargo(
        "test",
        env=dict(
            os.environ,
            RUST_BACKTRACE="1" if backtrace else "0",
            **(
                {
                    "RUSTFLAGS": "-C instrument-coverage",
                    "LLVM_PROFILE_FILE": "target/coverage/cargo-test-%p-%m.profraw",
                }
                if coverage
                else {}
            ),
        ),
    )
    if coverage:
        run(
            "grcov",
            "./target/coverage",
            "--branch",
            *("--binary-path", "./target/debug/deps/"),
            *("-s", "."),
            *("-t", "html"),
            *("-o", "target/coverage/html"),
            *("--ignore", "*_test.rs"),
        )


@cmd(name="run")
@arg("--backtrace", action="store_true", default=False)
@arg("--release", action="store_true", default=False)
@with_config()
def cmd_run(backtrace, release, config):
    cargo(
        "run",
        *(["--release"] if release else []),
        env=dict(
            os.environ,
            RUST_BACKTRACE="1" if backtrace else "0",
            GIT_BROWSER_REPOS=config["GIT_BROWSER_REPOS"],
        ),
    )


@cmd()
@arg(
    "--only-upload",
    action="store_true",
    default=False,
    help="Skip any pre and post deployment commands",
)
@with_config()
def deploy(config, only_upload=False):
    cargo("build", "--release", "--target=x86_64-unknown-linux-musl")

    if not only_upload:
        for cmd in config.get("PRE_DEPLOY", []):
            run(*cmd)

    run(
        "scp",
        "target/x86_64-unknown-linux-musl/release/git-browser",
        config["DEPLOYMENT_TARGET"],
    )

    if not only_upload:
        for cmd in config.get("POST_DEPLOY", []):
            run(*cmd)


@cmd()
@arg("args", nargs="*")
def example(args):
    cargo("run", "--example", "minijinja", "--", *args)


@cmd()
def doc():
    cargo("doc")


def mkdir(p):
    p = self_path / p
    print(f":: mkdir {p}")
    pathlib.Path(p).mkdir(exist_ok=True, parents=True)


def rm(*paths):
    for p in paths:
        p = self_path / p
        print(f":: rm {p}")
        pathlib.Path(p).unlink()


def cargo(*args, **kwargs):
    return run("cargo", *args, **kwargs)


def _with_config(func, args, kwargs):
    global _config
    if _config is None:
        config_path = self_path / "x.env"
        if config_path.exists():
            import tomli

            with open(config_path, "rb") as fobj:
                _config = tomli.load(fobj)

        else:
            _config = {
                "GIT_BROWSER_REPOS": ".",
            }

    return func(*args, config=_config, **kwargs)


def run(*args, **kwargs):
    kwargs.setdefault("check", True)
    kwargs.setdefault("cwd", self_path)

    args = [str(arg) for arg in args]
    print("::", " ".join(args))
    return subprocess.run(args, **kwargs)


def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    for func in globals().values():
        if not hasattr(func, "__chmp__"):
            continue

        desc = dict(func.__chmp__)
        name = desc.pop("name", func.__name__.replace("_", "-"))
        args = desc.pop("__args__", [])

        subparser = subparsers.add_parser(name, **desc)
        subparser.set_defaults(__main__=func)

        for arg_args, arg_kwargs in args:
            subparser.add_argument(*arg_args, **arg_kwargs)

    args = vars(parser.parse_args())
    return args.pop("__main__")(**args) if "__main__" in args else parser.print_help()


if __name__ == "__main__":
    main()