Plain is headed towards 1.0! Subscribe for development updates →

  1import subprocess
  2import sys
  3from pathlib import Path
  4
  5import click
  6
  7from plain.cli import register_cli
  8from plain.cli.runtime import without_runtime_setup
  9
 10
 11@without_runtime_setup
 12@register_cli("contrib")
 13@click.command("contribute", hidden=True)
 14@click.option("--repo", default="../plain", help="Path to the plain repo")
 15@click.option(
 16    "--reset", is_flag=True, help="Undo any changes to pyproject.toml and uv.lock"
 17)
 18@click.option(
 19    "--all", "all_packages", is_flag=True, help="Link all installed plain packages"
 20)
 21@click.argument("packages", nargs=-1)
 22def cli(packages: tuple[str, ...], repo: str, reset: bool, all_packages: bool) -> None:
 23    """Contribute to plain by linking packages locally."""
 24
 25    if reset:
 26        click.secho("Undoing any changes to pyproject.toml and uv.lock", bold=True)
 27        result = subprocess.run(["git", "checkout", "pyproject.toml", "uv.lock"])
 28        if result.returncode:
 29            click.secho("Failed to checkout pyproject.toml and uv.lock", fg="red")
 30            sys.exit(result.returncode)
 31
 32        click.secho("Running uv sync", bold=True)
 33        result = subprocess.run(["uv", "sync", "--reinstall"])
 34        if result.returncode:
 35            click.secho("Failed to sync", fg="red")
 36            sys.exit(result.returncode)
 37
 38        return
 39
 40    packages_list = list(packages)
 41
 42    repo_path = Path(repo)
 43    if not repo_path.exists():
 44        click.secho(f"Repo not found at {repo_path}", fg="red")
 45        return
 46
 47    repo_branch = (
 48        subprocess.check_output(
 49            [
 50                "git",
 51                "rev-parse",
 52                "--abbrev-ref",
 53                "HEAD",
 54            ],
 55            cwd=repo_path,
 56        )
 57        .decode()
 58        .strip()
 59    )
 60    click.secho(f"Using repo at {repo_path} ({repo_branch} branch)", bold=True)
 61
 62    plain_packages = []
 63    plainx_packages = []
 64    skipped_plainx_packages = []
 65
 66    if all_packages:
 67        # get all installed plain packages
 68        output = subprocess.check_output(["uv", "pip", "freeze"])
 69
 70        installed_packages = output.decode()
 71        if not installed_packages:
 72            click.secho("No installed packages found", fg="red")
 73            sys.exit(1)
 74
 75        packages_list = []
 76        for line in installed_packages.splitlines():
 77            if not line.startswith("plain"):
 78                continue
 79            package = line.split("==")[0]
 80            if package.startswith("plainx-"):
 81                skipped_plainx_packages.append(package)
 82            else:
 83                packages_list.append(package)
 84
 85        if skipped_plainx_packages:
 86            click.secho(
 87                "Skipping plainx packages: "
 88                + ", ".join(sorted(skipped_plainx_packages))
 89                + " (unknown repo)",
 90                fg="yellow",
 91            )
 92
 93    for package in packages_list:
 94        package = package.replace(".", "-")
 95        click.secho(f"Linking {package} to {repo_path}", bold=True)
 96        if package == "plain" or package.startswith("plain-"):
 97            plain_packages.append(str(repo_path / package))
 98        elif package.startswith("plainx-"):
 99            plainx_packages.append(str(repo_path))
100        else:
101            raise click.UsageError(f"Unknown package {package}")
102
103    if plain_packages:
104        result = subprocess.run(["uv", "add", "--editable", "--dev"] + plain_packages)
105        if result.returncode:
106            click.secho("Failed to link plain packages", fg="red")
107            sys.exit(result.returncode)
108
109    if plainx_packages:
110        result = subprocess.run(["uv", "add", "--editable", "--dev"] + plainx_packages)
111        if result.returncode:
112            click.secho("Failed to link plainx packages", fg="red")
113            sys.exit(result.returncode)