Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 116 additions & 7 deletions etc/libc-util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import argparse
import copy
import functools
import json
import os
import pprint
import re
Expand All @@ -14,6 +15,9 @@
from multiprocessing import Pool
from pathlib import Path

REPO_OWNER = "rust-lang"
REPO = "libc"


def main() -> None:
p = argparse.ArgumentParser(
Expand All @@ -24,7 +28,7 @@ def main() -> None:
p_cat = sub.add_parser(
"check-all-targets",
aliases=["cat"],
help="Run `cargo check` on some or all targets (see subcommand help for more)",
help="run `cargo check` on some or all targets (see subcommand help for more)",
description=cleandoc(
"""
Run `cargo check` on all targets, possibly with filtering.
Expand Down Expand Up @@ -53,6 +57,22 @@ def main() -> None:
)
p_cat.set_defaults(func=do_check_all_targets)

p_rel = sub.add_parser(
"relabel",
help="replace the stable-nominated label with stable-applied",
description=cleandoc(
"""
Replace the stable-nominated label with stable-applied, given the number of
a PR listing backported PRs.
""",
),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
p_rel.add_argument(
"pr_number", metavar="pr-number", help="pull request for the backport"
)
p_rel.set_defaults(func=do_relabel)

args = p.parse_args()
args.func(args)

Expand All @@ -64,6 +84,86 @@ def do_check_all_targets(args: argparse.Namespace) -> None:
cat.check_all_targets(args.package, args.only, args.skip, args.cargo_args)


def do_relabel(args: argparse.Namespace) -> None:
"""Take a backport PR with a list of of items like `* http://.../libc/pull/1234`
and swap `stable-nominated` to `stable-applied`.
"""

num = args.pr_number
j = check_output(
[
"gh",
"pr",
"view",
f"https://github.com/{REPO_OWNER}/{REPO}/pull/{num}",
"--json=baseRefName,body,state,title",
]
)
d = json.loads(j)
base: str = d["baseRefName"]
body: str = d["body"]
state: str = d["state"]
title: str = d["title"]

if state != "MERGED":
print(f'expected MERGED state; got {state} for "{title}" (#{num})')
exit(1)
if base != "libc-0.2":
print(f'expected libc-0.2 base ref; got {base} for "{title}" (#{num})')
exit(1)

print(f'Relabling PRs listed in {num} "{title}"')

to_relabel = []
for line in body.splitlines():
re.match(r"^[-*]\s*http", line)
if not re.match(r"^[-*]\s*http", line):
continue

match = re.match(r"^[-*]\s*https?://github.com/rust-lang/libc/pull/(\d+)", line)
if match is None:
print(f"{E.YEL}Line `{line}` does not match expected link pattern{E.RST}")
continue

num = int(match.group(1))
to_relabel.append(num)

# `gh` requests can be pretty slow, parallelize to help with large lists
with Pool() as p:
p.map(do_relabel_inner, to_relabel)

print("Finished relabeling")


def do_relabel_inner(num: int):
j = check_output(["gh", "pr", "view", pr_url(num), "--json=state,title,labels"])
d = json.loads(j)
state: str = d["state"]
title: str = d["title"]
labels: list[str] = [i["name"] for i in d["labels"]]

if state != "MERGED":
print(
f'{E.YEL}expected MERGED state; got {state} for "{title}" (#{num}){E.RST}'
)
return
if "stable-nominated" not in labels:
print(f'{E.YEL}`stable-nominated` not in labels for "{title}" (#{num}){E.RST}')

# Use check_output to eat the stdout since otherwise `gh` draws a spinner that
# messes up interleaved stdout.
check_output(
[
"gh",
"pr",
"edit",
"--remove-label=stable-nominated",
"--add-label=stable-applied",
pr_url(num),
]
)


@dataclass
class CheckAllTargets:
"""Config to run `cargo check` on all targets."""
Expand Down Expand Up @@ -236,11 +336,11 @@ def check_all_targets(
fulldesc = f"{t.name} ({ran + 1} / {total})"

if len(t.skip) > 0:
print(f"{E.YEL_D}Skipping {fulldesc} ({", ".join(t.skip)}){E.RST}")
print(f"{E.YEL}Skipping {fulldesc} ({", ".join(t.skip)}){E.RST}")
skipped += 1
continue

print(f"{E.CY}Checking {fulldesc}{E.RST}")
print(f"{E.CY_B}Checking {fulldesc}{E.RST}")

extra_args = [] if t.installed else ["-Zbuild-std=core"]
common_args = [
Expand Down Expand Up @@ -280,7 +380,7 @@ def check_all_targets(
if ok:
passed += 1
else:
print(f"{E.RED}failed: {t.target}{E.RST}")
print(f"{E.RED_B}failed: {t.target}{E.RST}")
failures.append(t.name)

if len(failures) > self.failure_limit:
Expand Down Expand Up @@ -363,13 +463,18 @@ def fetch_all(toolchain: str) -> list["RustcTarget"]:
class E:
"""ANSI escapes."""

YEL = "\033[33m"
GRN = "\033[32m"

# Bright colors
CY = "\033[1;36m"
YEL = "\033[1;33m"
RED = "\033[1;31m"
CY_B = "\033[1;36m"
YEL_B = "\033[1;33m"
RED_B = "\033[1;31m"

# Dimmed colors
YEL_D = "\033[2;33m"
GRN_D = "\033[2;32m"

RST = "\033[0m"


Expand All @@ -381,6 +486,10 @@ def cache_dir() -> Path:
return Path.home() / ".cache"


def pr_url(num: int) -> str:
return f"https://github.com/{REPO_OWNER}/{REPO}/pull/{num}"


def check_output(args: list[str], *, quiet: bool = False, **kw) -> str:
if not quiet:
xtrace(args, env=kw.get("env"))
Expand Down