From 43a7c8c8f2477c3590153bc0a76068cfadfaf58d Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 28 Sep 2021 15:09:58 +0200 Subject: [PATCH 1/2] tools/backport_pr: add black testenv in tox Add Flake8 and Pylint configuration compatible with black --- dist/tools/backport_pr/setup.cfg | 12 ++++++++++++ dist/tools/backport_pr/tox.ini | 10 ++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 dist/tools/backport_pr/setup.cfg diff --git a/dist/tools/backport_pr/setup.cfg b/dist/tools/backport_pr/setup.cfg new file mode 100644 index 0000000000..37171e2678 --- /dev/null +++ b/dist/tools/backport_pr/setup.cfg @@ -0,0 +1,12 @@ +# Use black compatible configuration for flake8 and pylint +# flake8: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8 +# pylint: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#pylint +[flake8] +max-line-length = 88 +extend-ignore = E203 + +[pylint] +max-line-length = 88 + +[pylint.messages_control] +disable = C0330, C0326 diff --git a/dist/tools/backport_pr/tox.ini b/dist/tools/backport_pr/tox.ini index c2a23359df..d8a018c971 100644 --- a/dist/tools/backport_pr/tox.ini +++ b/dist/tools/backport_pr/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = test,lint,flake8 +envlist = test,lint,flake8,black skipsdist = True [testenv] @@ -11,6 +11,7 @@ commands = test: {[testenv:test]commands} lint: {[testenv:lint]commands} flake8: {[testenv:flake8]commands} + black: {[testenv:black]commands} [testenv:test] deps = @@ -25,10 +26,15 @@ deps = {[testenv]deps} commands = # Suppress warning about TODO in code - pylint --disable=fixme {env:script} + pylint --rcfile=setup.cfg --disable=fixme {env:script} [testenv:flake8] deps = flake8 commands = # main() is quite complex, provide enough margin flake8 --max-complexity=25 {env:script} + +[testenv:black] +deps = black +commands = + black --check --diff {env:script} From 8d4d8f207de49cdb3c0f60560a7f0464a99772ca Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 28 Sep 2021 15:10:13 +0200 Subject: [PATCH 2/2] tools/backport_pr: reformat with black --- dist/tools/backport_pr/backport_pr.py | 182 +++++++++++++++----------- 1 file changed, 107 insertions(+), 75 deletions(-) diff --git a/dist/tools/backport_pr/backport_pr.py b/dist/tools/backport_pr/backport_pr.py index 6d3ebf4b27..f2f46c13e4 100755 --- a/dist/tools/backport_pr/backport_pr.py +++ b/dist/tools/backport_pr/backport_pr.py @@ -28,10 +28,10 @@ WORKTREE_SUBDIR = "backport_temp" RELEASE_PREFIX = "" RELEASE_SUFFIX = "-branch" -LABELS_REMOVE = ['Process: needs backport', 'Reviewed: '] -LABELS_ADD = ['Process: release backport'] +LABELS_REMOVE = ["Process: needs backport", "Reviewed: "] +LABELS_ADD = ["Process: release backport"] -BACKPORT_BRANCH = 'backport/{release}/{origbranch}' +BACKPORT_BRANCH = "backport/{release}/{origbranch}" def _get_labels(pull_request): @@ -46,22 +46,22 @@ def _get_labels(pull_request): >>> _get_labels({'labels': [{'name': 'Process: needs backport'}]}) ['Process: release backport'] """ - labels = set(label['name'] for label in pull_request['labels'] - if all(not label['name'].startswith(remove) - for remove in LABELS_REMOVE)) + labels = set( + label["name"] + for label in pull_request["labels"] + if all(not label["name"].startswith(remove) for remove in LABELS_REMOVE) + ) labels.update(LABELS_ADD) return sorted(list(labels)) -def _branch_name_strip(branch_name, prefix=RELEASE_PREFIX, - suffix=RELEASE_SUFFIX): +def _branch_name_strip(branch_name, prefix=RELEASE_PREFIX, suffix=RELEASE_SUFFIX): """Strip suffix and prefix. >>> _branch_name_strip('2018.10-branch') '2018.10' """ - if (branch_name.startswith(prefix) and - branch_name.endswith(suffix)): + if branch_name.startswith(prefix) and branch_name.endswith(suffix): if prefix: branch_name = branch_name.split(prefix, maxsplit=1)[0] if suffix: @@ -83,29 +83,29 @@ def _get_latest_release(branches): ('2020.04', '2020.04-branch') """ version_latest = 0 - release_fullname = '' - release_short = '' + release_fullname = "" + release_short = "" for branch in branches: - branch_name = _branch_name_strip(branch['name']) + branch_name = _branch_name_strip(branch["name"]) branch_num = 0 try: - branch_num = int(''.join(branch_name.split('.'))) + branch_num = int("".join(branch_name.split("."))) except ValueError: pass if branch_num > version_latest: version_latest = branch_num release_short = branch_name - release_fullname = branch['name'] + release_fullname = branch["name"] return (release_short, release_fullname) def _find_remote(repo, user, repo_name): for remote in repo.remotes: - if (remote.url.endswith(f"{user}/{repo_name}.git") or - remote.url.endswith(f"{user}/{repo_name}")): + if remote.url.endswith(f"{user}/{repo_name}.git") or remote.url.endswith( + f"{user}/{repo_name}" + ): return remote - raise ValueError("Could not find remote with URL ending in " - f"{user}/{repo_name}.git") + raise ValueError(f"Could not find remote with URL ending in {user}/{repo_name}.git") def _get_upstream(repo): @@ -114,34 +114,55 @@ def _get_upstream(repo): def _delete_worktree(repo, workdir): shutil.rmtree(workdir) - repo.git.worktree('prune') + repo.git.worktree("prune") def main(): # pylint:disable=too-many-locals,too-many-branches,too-many-statements """Main function of this script.""" - keyfile = os.path.join(os.environ['HOME'], GITHUBTOKEN_FILE) + keyfile = os.path.join(os.environ["HOME"], GITHUBTOKEN_FILE) parser = argparse.ArgumentParser() - parser.add_argument("-k", "--keyfile", type=argparse.FileType('r'), - default=keyfile, - help="File containing github token") - parser.add_argument("-c", "--comment", action="store_true", - help="Put a comment with a reference under" - "the original PR") - parser.add_argument("-n", "--noop", action="store_true", - help="Limited noop mode, creates branch, but doesn't" - "push and create the PR") - parser.add_argument("-r", "--release-branch", type=str, - help="Base the backport on this branch, " - "default is the latest") - parser.add_argument("--backport-branch-fmt", type=str, - default=BACKPORT_BRANCH, - help="Backport branch format. " - "Fields '{release}' and '{origbranch} will be " - "replaced by the release name and remote branch " - "name.") - parser.add_argument('-d', '--gitdir', type=str, default=os.getcwd(), - help="Base git repo to work from") + parser.add_argument( + "-k", + "--keyfile", + type=argparse.FileType("r"), + default=keyfile, + help="File containing github token", + ) + parser.add_argument( + "-c", + "--comment", + action="store_true", + help="Put a comment with a reference under" "the original PR", + ) + parser.add_argument( + "-n", + "--noop", + action="store_true", + help="Limited noop mode, creates branch, but doesn't" "push and create the PR", + ) + parser.add_argument( + "-r", + "--release-branch", + type=str, + help="Base the backport on this branch, " "default is the latest", + ) + parser.add_argument( + "--backport-branch-fmt", + type=str, + default=BACKPORT_BRANCH, + help="Backport branch format. " + "Fields '{release}' and '{origbranch} will be " + "replaced by the release name and remote branch " + "name.", + ) + parser.add_argument( + "-d", + "--gitdir", + type=str, + default=os.getcwd(), + help="Base git repo to work from", + ) parser.add_argument("PR", type=int, help="Pull request number to backport") args = parser.parse_args() @@ -157,25 +178,27 @@ def main(): response_headers = dict(github_api.getheaders()) # agithub documentation says it's lower case header field-names but # at this moment it's not - if 'X-OAuth-Scopes' in response_headers: - scopes = response_headers['X-OAuth-Scopes'] + if "X-OAuth-Scopes" in response_headers: + scopes = response_headers["X-OAuth-Scopes"] else: - scopes = response_headers['x-oauth-scopes'] - scopes_list = [x.strip() for x in scopes.split(',')] - if not ('public_repo' in scopes_list or 'repo' in scopes_list): - print("missing public_repo scope from token settings." - " Please add it on the GitHub webinterface") + scopes = response_headers["x-oauth-scopes"] + scopes_list = [x.strip() for x in scopes.split(",")] + if not ("public_repo" in scopes_list or "repo" in scopes_list): + print( + "missing public_repo scope from token settings." + " Please add it on the GitHub webinterface" + ) sys.exit(1) - username = user['login'] + username = user["login"] status, pulldata = github_api.repos[ORG][REPO].pulls[args.PR].get() if status != 200: print(f'Commit #{args.PR} not found: {pulldata["message"]}') sys.exit(2) - if not pulldata['merged']: + if not pulldata["merged"]: print("Original PR not yet merged") sys.exit(0) print(f'Fetching for commit: #{args.PR}: {pulldata["title"]}') - orig_branch = pulldata['head']['ref'] + orig_branch = pulldata["head"]["ref"] status, commits = github_api.repos[ORG][REPO].pulls[args.PR].commits.get() if status != 200: print(f'No commits found for #{args.PR}: {commits["message"]}') @@ -190,8 +213,10 @@ def main(): else: status, branches = github_api.repos[ORG][REPO].branches.get() if status != 200: - print(f'Could not retrieve branches for {ORG}/{REPO}: ' - f'{branches["message"]}') + print( + f"Could not retrieve branches for {ORG}/{REPO}: " + f'{branches["message"]}' + ) sys.exit(4) release_shortname, release_fullname = _get_latest_release(branches) if not release_fullname: @@ -209,23 +234,27 @@ def main(): upstream_remote.fetch() # Build topic branch in temp dir - new_branch = args.backport_branch_fmt.format(release=release_shortname, - origbranch=orig_branch) + new_branch = args.backport_branch_fmt.format( + release=release_shortname, origbranch=orig_branch + ) if new_branch in repo.branches: print(f"ERROR: Branch {new_branch} already exists") sys.exit(1) worktree_dir = os.path.join(args.gitdir, WORKTREE_SUBDIR) - repo.git.worktree("add", "-b", - new_branch, - WORKTREE_SUBDIR, - f"{upstream_remote}/{release_fullname}") + repo.git.worktree( + "add", + "-b", + new_branch, + WORKTREE_SUBDIR, + f"{upstream_remote}/{release_fullname}", + ) # transform branch name into Head object for later configuring new_branch = repo.branches[new_branch] try: bp_repo = git.Repo(worktree_dir) # Apply commits for commit in commits: - bp_repo.git.cherry_pick('-x', commit['sha']) + bp_repo.git.cherry_pick("-x", commit["sha"]) # Push to github origin = _find_remote(repo, username, REPO) print(f"Pushing branch {new_branch} to {origin}") @@ -246,33 +275,36 @@ def main(): _delete_worktree(repo, worktree_dir) labels = _get_labels(pulldata) - merger = pulldata['merged_by']['login'] + merger = pulldata["merged_by"]["login"] if not args.noop: # Open new PR on github pull_request = { - 'title': f'{pulldata["title"]} [backport {release_shortname}]', - 'head': f'{username}:{new_branch}', - 'base': release_fullname, - 'body': f'# Backport of #{args.PR}\n\n{pulldata["body"]}', - 'maintainer_can_modify': True, + "title": f'{pulldata["title"]} [backport {release_shortname}]', + "head": f"{username}:{new_branch}", + "base": release_fullname, + "body": f'# Backport of #{args.PR}\n\n{pulldata["body"]}', + "maintainer_can_modify": True, } - status, new_pr = github_api.repos[ORG][REPO].pulls.post( - body=pull_request) + status, new_pr = github_api.repos[ORG][REPO].pulls.post(body=pull_request) if status != 201: - print(f'Error creating the new pr: "{new_pr["message"]}". ' - 'Is "Public Repo" access enabled for the token?') - pr_number = new_pr['number'] + print( + f'Error creating the new pr: "{new_pr["message"]}". ' + 'Is "Public Repo" access enabled for the token?' + ) + pr_number = new_pr["number"] print(f"Create PR number #{pr_number} for backport") github_api.repos[ORG][REPO].issues[pr_number].labels.post(body=labels) review_request = {"reviewers": [merger]} - github_api.repos[ORG][REPO].pulls[pr_number].\ - requested_reviewers.post(body=review_request) + github_api.repos[ORG][REPO].pulls[pr_number].requested_reviewers.post( + body=review_request + ) # Put commit under old PR if args.comment and not args.noop: comment = {"body": f"Backport provided in #{pr_number}"} - status, res = github_api.repos[ORG][REPO].\ - issues[args.PR].comments.post(body=comment) + status, res = ( + github_api.repos[ORG][REPO].issues[args.PR].comments.post(body=comment) + ) if status != 201: print(f'Something went wrong adding the comment: {res["message"]}') print(f"Added comment to #{args.PR}")