neovim/scripts/squash_typos.py

149 lines
3.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
This script squashes a PR tagged with the "typo" label into a single, dedicated
"squash PR".
"""
import subprocess
import os
import re
def get_authors_and_emails_from_pr():
"""
For a given PR number, returns all contributing authors and their emails
for that PR. This includes co-authors, meaning that if two authors are
credited for a single commit, which is possible with GitHub, then both will
get credited.
"""
# Get a list of all authors involved in the pull request (including co-authors).
authors = subprocess.check_output(
["gh", "pr", "view", "--json", "commits", "--jq", ".[][].authors.[].name"],
text=True,
).splitlines()
# Get a list of emails of the aforementioned authors.
emails = subprocess.check_output(
["gh", "pr", "view", "--json", "commits", "--jq", ".[][].authors.[].email"],
text=True,
).splitlines()
return [(author, mail) for author, mail in zip(authors, emails)]
def rebase_onto_pr(pr, squash_branch):
"""
Add all commits from PR into current branch. This is done by rebasing
current branch onto the PR.
"""
# Check out the pull request.
subprocess.call(["gh", "pr", "checkout", pr])
pr_branch_name = subprocess.check_output(
["git", "branch", "--show-current"], text=True
).strip()
# Change back to the original branch.
subprocess.call(["git", "switch", squash_branch])
# Rebase onto the pull request, aka include the commits in the pull
# request in the current branch.
subprocess.call(["git", "rebase", pr_branch_name])
def squash_all_commits():
"""
Squash all commits into a single commit. Credit all authors by name and
email.
"""
authors_and_emails = get_authors_and_emails_from_pr()
subprocess.call(["git", "reset", "--soft", f"{os.environ['GITHUB_BASE_REF']}"])
authors_and_emails = sorted(set(authors_and_emails))
commit_message_coauthors = "\n" + "\n".join(
[f"Co-authored-by: {i[0]} <{i[1]}>" for i in authors_and_emails]
)
subprocess.call(
["git", "commit", "-m", "chore: typo fixes", "-m", commit_message_coauthors]
)
def force_push(branch):
gh_actor = os.environ["GITHUB_ACTOR"]
gh_token = os.environ["GITHUB_TOKEN"]
gh_repo = os.environ["GITHUB_REPOSITORY"]
subprocess.call(
[
"git",
"push",
"--force",
f"https://{gh_actor}:{gh_token}@github.com/{gh_repo}",
branch,
]
)
def main():
squash_branch = "marvim/squash-typos"
all_pr_urls = ""
pr_number = re.sub(r"\D", "", os.environ["GITHUB_REF"])
show_ref_output = subprocess.check_output(["git", "show-ref"], text=True).strip()
if squash_branch in show_ref_output:
subprocess.call(
["git", "checkout", "-b", squash_branch, f"origin/{squash_branch}"]
)
squash_branch_exists = True
all_pr_urls += subprocess.check_output(
["gh", "pr", "view", "--json", "body", "--jq", ".body"], text=True
)
else:
subprocess.call(["git", "checkout", "-b", squash_branch])
squash_branch_exists = False
all_pr_urls += subprocess.check_output(
["gh", "pr", "view", pr_number, "--json", "url", "--jq", ".url"], text=True
).strip()
rebase_onto_pr(pr_number, squash_branch)
force_push(squash_branch)
subprocess.call(["gh", "pr", "close", pr_number])
squash_all_commits()
force_push(squash_branch)
if not squash_branch_exists:
subprocess.call(
[
"gh",
"pr",
"create",
"--fill",
"--head",
squash_branch,
"--title",
"Dedicated PR for all typo fixes.",
]
)
subprocess.call(["gh", "pr", "edit", "--add-label", "typo", "--body", all_pr_urls])
if __name__ == "__main__":
main()