From a1b18148886b865f2a8d43fc6fe6c421315f9a68 Mon Sep 17 00:00:00 2001 From: Paul Prescod Date: Fri, 12 May 2023 09:54:51 -0700 Subject: [PATCH 1/4] Script to help one adopt a branch --- tools/adopt-branch.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tools/adopt-branch.py diff --git a/tools/adopt-branch.py b/tools/adopt-branch.py new file mode 100644 index 00000000..31a02796 --- /dev/null +++ b/tools/adopt-branch.py @@ -0,0 +1,41 @@ +"""Adopt a branch from a third party""" +import os +import re + +import click + +URL_RE = ( + "(?Phttps://github.com/(?P.+)/(?P.+))/tree/(?P.+)" +) + + +def command(cmd): + print(cmd) + assert os.system(cmd) == 0 + + +@click.command() +@click.argument("url", required=True) +def adopt_branch(url): + """ + adopt-branch URL + + URL is a github:// URL to the branch on the fork" + """ + url_match = re.match(URL_RE, url) + assert url_match, f"{url} did not match {URL_RE}" + owner, repo, branch = url_match["owner"], url_match["repo"], url_match["branch"] + repo_url = url_match["repo_root"] + assert all((owner, repo, branch, repo_url)), (owner, repo, branch, repo_url) + print("Using", repo, branch) + try: + command(f"git remote show {owner} 2>/dev/null") + except AssertionError: + command(f"git remote add {owner} {repo_url}") + command(f"git fetch {owner}") + command(f"git checkout --track {owner}/{branch}") + command("git push origin") + command("gh pr create") + + +adopt_branch() From 7fb237814a7d3c4450b076871e2ef44ec2d2ca20 Mon Sep 17 00:00:00 2001 From: Paul Prescod Date: Fri, 12 May 2023 10:25:20 -0700 Subject: [PATCH 2/4] Do it based on PR instead --- tools/adopt-branch.py | 50 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/tools/adopt-branch.py b/tools/adopt-branch.py index 31a02796..f7ca51a1 100644 --- a/tools/adopt-branch.py +++ b/tools/adopt-branch.py @@ -1,6 +1,7 @@ """Adopt a branch from a third party""" +import json import os -import re +import subprocess import click @@ -14,18 +15,49 @@ def command(cmd): assert os.system(cmd) == 0 +def pr_to_branch(pr_number): + """ + adopt-branch pr_number + + pr_number is a number like "553" or whatever. + """ + result = subprocess.run( + [ + "gh", + "pr", + "view", + str(pr_number), + "--json", + "url,headRepositoryOwner,headRepository,headRefName", + ], + capture_output=True, + text=True, # This is to ensure that the output from the subprocess is text + ) + + assert result.returncode == 0, f"gh command failed: {result.stderr}" + + pr_info = json.loads(result.stdout) + + owner = pr_info["headRepositoryOwner"]["login"] + repo = pr_info["headRepository"]["name"] + branch = pr_info["headRefName"] + print(pr_info["headRepository"]) + repo_url = f"https://github.com/{owner}/{repo}" + + assert all((owner, repo, branch, repo_url)), (owner, repo, branch, repo_url) + + return owner, repo, branch, repo_url + + @click.command() -@click.argument("url", required=True) -def adopt_branch(url): +@click.argument("pr_number", required=True) +def adopt_branch(pr_number): """ - adopt-branch URL + adopt-branch pr_number - URL is a github:// URL to the branch on the fork" + pr_number is a number like "553" or whatever. """ - url_match = re.match(URL_RE, url) - assert url_match, f"{url} did not match {URL_RE}" - owner, repo, branch = url_match["owner"], url_match["repo"], url_match["branch"] - repo_url = url_match["repo_root"] + owner, repo, branch, repo_url = pr_to_branch(pr_number) assert all((owner, repo, branch, repo_url)), (owner, repo, branch, repo_url) print("Using", repo, branch) try: From 3fa8b6e658a37bb87f9dd1c5566d25699c4ce5fe Mon Sep 17 00:00:00 2001 From: Paul Prescod Date: Fri, 12 May 2023 10:46:00 -0700 Subject: [PATCH 3/4] Go from a PR, not a URL. --- tools/adopt-branch.py | 45 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/tools/adopt-branch.py b/tools/adopt-branch.py index f7ca51a1..a82e4d7a 100644 --- a/tools/adopt-branch.py +++ b/tools/adopt-branch.py @@ -2,6 +2,7 @@ import json import os import subprocess +import typing import click @@ -10,6 +11,16 @@ ) +class PRInfo(typing.NamedTuple): + owner: str + repo: str + branch: str + repo_url: str + title: str + body: str + author: str + + def command(cmd): print(cmd) assert os.system(cmd) == 0 @@ -28,7 +39,7 @@ def pr_to_branch(pr_number): "view", str(pr_number), "--json", - "url,headRepositoryOwner,headRepository,headRefName", + "url,headRepositoryOwner,headRepository,headRefName,title,body,author", ], capture_output=True, text=True, # This is to ensure that the output from the subprocess is text @@ -42,11 +53,18 @@ def pr_to_branch(pr_number): repo = pr_info["headRepository"]["name"] branch = pr_info["headRefName"] print(pr_info["headRepository"]) - repo_url = f"https://github.com/{owner}/{repo}" - assert all((owner, repo, branch, repo_url)), (owner, repo, branch, repo_url) + assert all((owner, repo, branch)), (owner, repo, branch) - return owner, repo, branch, repo_url + return PRInfo( + owner, + repo, + branch, + f"https://github.com/{owner}/{repo}", + pr_info["title"], + pr_info["title"], + pr_info["author"], + ) @click.command() @@ -57,17 +75,18 @@ def adopt_branch(pr_number): pr_number is a number like "553" or whatever. """ - owner, repo, branch, repo_url = pr_to_branch(pr_number) - assert all((owner, repo, branch, repo_url)), (owner, repo, branch, repo_url) - print("Using", repo, branch) + pr_info = pr_to_branch(pr_number) + print("Using", pr_info.repo, pr_info.branch) try: - command(f"git remote show {owner} 2>/dev/null") + command(f"git remote show {pr_info.owner} 2>/dev/null") except AssertionError: - command(f"git remote add {owner} {repo_url}") - command(f"git fetch {owner}") - command(f"git checkout --track {owner}/{branch}") - command("git push origin") - command("gh pr create") + command(f"git remote add {pr_info.owner} {pr_info.repo_url}") + command(f"git fetch {pr_info.owner}") + command(f"git checkout --track {pr_info.owner}/{pr_info.branch}") + command("git push origin --set-upstream") + title = f"{pr_info.title} -- from {pr_info.author}" + body = f"Copied from #{[pr_number]} by {pr_info.author}" + subprocess.run(["gh", "pr", "create", "--title", title, "--body", body]) adopt_branch() From c1cefa96e3e30e53405c7f86237821b85752950a Mon Sep 17 00:00:00 2001 From: Paul Prescod Date: Fri, 12 May 2023 11:01:01 -0700 Subject: [PATCH 4/4] Make a better PR. --- tools/adopt-branch.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/adopt-branch.py b/tools/adopt-branch.py index a82e4d7a..2fe9d22f 100644 --- a/tools/adopt-branch.py +++ b/tools/adopt-branch.py @@ -60,10 +60,10 @@ def pr_to_branch(pr_number): owner, repo, branch, - f"https://github.com/{owner}/{repo}", - pr_info["title"], - pr_info["title"], - pr_info["author"], + repo_url=f"https://github.com/{owner}/{repo}", + title=pr_info["title"], + body=pr_info["body"], + author=pr_info["author"]["login"], ) @@ -84,8 +84,8 @@ def adopt_branch(pr_number): command(f"git fetch {pr_info.owner}") command(f"git checkout --track {pr_info.owner}/{pr_info.branch}") command("git push origin --set-upstream") - title = f"{pr_info.title} -- from {pr_info.author}" - body = f"Copied from #{[pr_number]} by {pr_info.author}" + title = f"{pr_info.title} -- from @{pr_info.author}" + body = f"Copied from #{pr_number} by @{pr_info.author}\n\n{pr_info.body}" subprocess.run(["gh", "pr", "create", "--title", title, "--body", body])