From 3f17bcefe903bda8eb089cb0688851c1396ed81d Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Tue, 16 Nov 2021 21:40:31 +0100 Subject: [PATCH 01/12] review partially backport file as a github action --- .github/workflows/backport.yml | 39 ++++++ .github/workflows/backport_maintenance.yml | 39 ++++++ bedevere/__main__.py | 2 +- bedevere/backport.py | 148 ++++++--------------- bedevere/backport_old.py | 134 +++++++++++++++++++ 5 files changed, 251 insertions(+), 111 deletions(-) create mode 100644 .github/workflows/backport.yml create mode 100644 .github/workflows/backport_maintenance.yml create mode 100644 bedevere/backport_old.py diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000..71407ef5 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,39 @@ + + +name: backport + +on: + pull_request: + types: [opened, edited] + + +jobs: + backport: + name: Check and update backport label of PR + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.8", "3.9", "3.9-dev"] + + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v2 + if: "!endsWith(matrix.python-version, '-dev')" + with: + python-version: ${{ matrix.python-version }} + - uses: deadsnakes/action@v2.1.1 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: python3 -m pip install coverage -U pip -r dev-requirements.txt + - name: Run code + run: python3 bedevere/backport.py \ No newline at end of file diff --git a/.github/workflows/backport_maintenance.yml b/.github/workflows/backport_maintenance.yml new file mode 100644 index 00000000..1d1cd3f1 --- /dev/null +++ b/.github/workflows/backport_maintenance.yml @@ -0,0 +1,39 @@ + + +name: backport + +on: + pull_request: + types: [opened, reopened, edited, synchronize] + + +jobs: + backport: + name: Check and update backport label of PR + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.8", "3.9", "3.9-dev"] + + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v2 + if: "!endsWith(matrix.python-version, '-dev')" + with: + python-version: ${{ matrix.python-version }} + - uses: deadsnakes/action@v2.1.1 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: python3 -m pip install coverage -U pip -r dev-requirements.txt + # - name: Run code + # run: python3 -m bedevere backport.py validate_maintenance_branch_pr \ No newline at end of file diff --git a/bedevere/__main__.py b/bedevere/__main__.py index 3a3c0740..2e6f3130 100644 --- a/bedevere/__main__.py +++ b/bedevere/__main__.py @@ -54,4 +54,4 @@ async def main(request): port = os.environ.get("PORT") if port is not None: port = int(port) - web.run_app(app, port=port) + web.run_app(app, port=port) \ No newline at end of file diff --git a/bedevere/backport.py b/bedevere/backport.py index ed248839..92a3a56e 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -1,32 +1,31 @@ -"""Automatically remove a backport label, and check backport PR validity.""" -import functools +import asyncio +import json +import os import re +import traceback -import gidgethub.routing +import aiohttp +from gidgethub.aiohttp import GitHubAPI from . import util -create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') +TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') - -router = gidgethub.routing.Router() - -TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)', re.IGNORECASE) -MAINTENANCE_BRANCH_TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+') -MAINTENANCE_BRANCH_RE = re.compile(r'\s*(?P\d+\.\d+)') BACKPORT_LABEL = 'needs backport to {branch}' MESSAGE_TEMPLATE = ('[GH-{pr}](https://github.com/python/cpython/pull/{pr}) is ' 'a backport of this pull request to the ' '[{branch} branch](https://github.com/python/cpython/tree/{branch}).') -BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/committing/#backport-pr-title" +async def issue_for_PR(gh, pull_request): + """Get the issue data for a pull request.""" + return await gh.getitem(pull_request["issue_url"]) async def _copy_over_labels(gh, original_issue, backport_issue): """Copy over relevant labels from the original PR to the backport PR.""" label_prefixes = "skip", "type", "sprint" labels = list(filter(lambda x: x.startswith(label_prefixes), - util.labels(original_issue))) + util.labels_(original_issue))) if labels: await gh.post(backport_issue["labels_url"], data=labels) @@ -37,113 +36,42 @@ async def _remove_backport_label(gh, original_issue, branch, backport_pr_number) Also leave a comment on the original PR referencing the backport PR. """ backport_label = BACKPORT_LABEL.format(branch=branch) - if backport_label not in util.labels(original_issue): + if backport_label not in util.labels_(original_issue): return await gh.delete(original_issue['labels_url'], {'name': backport_label}) message = MESSAGE_TEMPLATE.format(branch=branch, pr=backport_pr_number) await gh.post(original_issue['comments_url'], data={'body': message}) - -@router.register("pull_request", action="opened") -@router.register("pull_request", action="edited") -async def manage_labels(event, gh, *args, **kwargs): - if event.data["action"] == "edited" and "title" not in event.data["changes"]: - return - pull_request = event.data["pull_request"] - title = util.normalize_title(pull_request['title'], +async def manage_labels(gh, *args, **kwargs): + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + if event.get("action") == "edited" and "title" not in event.get("changes"): + return + pull_request = event["pull_request"] + title = util.normalize_title(pull_request['title'], pull_request['body']) - title_match = TITLE_RE.match(title) - if title_match is None: - return - branch = title_match.group('branch') - original_pr_number = title_match.group('pr') - - original_issue = await gh.getitem(event.data['repository']['issues_url'], - {'number': original_pr_number}) + title_match = TITLE_RE.match(title) + if title_match is None: + return + branch = title_match.group('branch') + original_pr_number = title_match.group('pr') + + original_issue = await gh.getitem(event['repository']['issues_url'], + {'number': original_pr_number}) await _remove_backport_label(gh, original_issue, branch, - event.data["number"]) + event["number"]) - backport_issue = await util.issue_for_PR(gh, pull_request) + backport_issue = await issue_for_PR(gh, pull_request) await _copy_over_labels(gh, original_issue, backport_issue) +async def main(): + try: + async with aiohttp.ClientSession() as session: + gh = GitHubAPI(session, "sabderemane", oauth_token=os.getenv("GH_AUTH")) + await manage_labels(gh) + except Exception: + traceback.print_exc() -def is_maintenance_branch(ref): - """ - Return True if the ref refers to a maintenance branch. - - >>> is_maintenance_branch("3.11") - True - >>> is_maintenance_branch("main") - False - >>> is_maintenance_branch("gh-1234/something-completely-different") - False - """ - maintenance_branch_pattern = r'\d+\.\d+' - return bool(re.fullmatch(maintenance_branch_pattern, ref)) - - -@router.register("pull_request", action="opened") -@router.register("pull_request", action="reopened") -@router.register("pull_request", action="edited") -@router.register("pull_request", action="synchronize") -async def validate_maintenance_branch_pr(event, gh, *args, **kwargs): - """Check the PR title for maintenance branch pull requests. - - If the PR was made against maintenance branch, and the title does not - match the maintenance branch PR pattern, then post a failure status. - - The maintenance branch PR has to start with `[X.Y]` - """ - if event.data["action"] == "edited" and "title" not in event.data["changes"]: - return - pull_request = event.data["pull_request"] - base_branch = pull_request["base"]["ref"] - - if not is_maintenance_branch(base_branch): - return - - title = util.normalize_title(pull_request["title"], - pull_request["body"]) - title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) - - if title_match is None: - status = create_status(util.StatusState.FAILURE, - description="Not a valid maintenance branch PR title.", - target_url=BACKPORT_TITLE_DEVGUIDE_URL) - else: - status = create_status(util.StatusState.SUCCESS, - description="Valid maintenance branch PR title.") - await util.post_status(gh, event, status) - - -@router.register("create", ref_type="branch") -async def maintenance_branch_created(event, gh, *args, **kwargs): - """Create the `needs backport label` when the maintenance branch is created. - - Also post a reminder to add the maintenance branch to the list of - `ALLOWED_BRANCHES` in CPython-emailer-webhook. - - If a maintenance branch was created (e.g.: 3.9, or 4.0), - automatically create the `needs backport to ` label. - - The maintenance branch PR has to start with `[X.Y]` - """ - branch_name = event.data["ref"] - - if MAINTENANCE_BRANCH_RE.match(branch_name): - await gh.post( - "/repos/python/cpython/labels", - data={"name": f"needs backport to {branch_name}", "color": "#c2e0c6"}, - ) - await gh.post( - "/repos/berkerpeksag/cpython-emailer-webhook/issues", - data={ - "title": f"Please add {branch_name} to ALLOWED_BRANCHES", - "body": ( - f"A new CPython maintenance branch `{branch_name}` has just been created.", - "\nThis is a reminder to add `{branch_name}` to the list of `ALLOWED_BRANCHES`", - "\nhttps://github.com/berkerpeksag/cpython-emailer-webhook/blob/e164cb9a6735d56012a4e557fd67dd7715c16d7b/mailer.py#L15", - ), - }, - ) +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) diff --git a/bedevere/backport_old.py b/bedevere/backport_old.py new file mode 100644 index 00000000..5f559432 --- /dev/null +++ b/bedevere/backport_old.py @@ -0,0 +1,134 @@ +"""Automatically remove a backport label, and check backport PR validity.""" +import functools +import re + +import gidgethub.routing + +from . import util + +create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') + + +router = gidgethub.routing.Router() + +TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') +MAINTENANCE_BRANCH_TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+') +MAINTENANCE_BRANCH_RE = re.compile(r'\s*(?P\d+\.\d+)') +BACKPORT_LABEL = 'needs backport to {branch}' +MESSAGE_TEMPLATE = ('[GH-{pr}](https://github.com/python/cpython/pull/{pr}) is ' + 'a backport of this pull request to the ' + '[{branch} branch](https://github.com/python/cpython/tree/{branch}).') + + +BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/committing/#backport-pr-title" + +async def _copy_over_labels(gh, original_issue, backport_issue): + """Copy over relevant labels from the original PR to the backport PR.""" + label_prefixes = "skip", "type", "sprint" + labels = list(filter(lambda x: x.startswith(label_prefixes), + util.labels(original_issue))) + if labels: + await gh.post(backport_issue["labels_url"], data=labels) + + +async def _remove_backport_label(gh, original_issue, branch, backport_pr_number): + """Remove the appropriate "backport to" label on the original PR. + + Also leave a comment on the original PR referencing the backport PR. + """ + backport_label = BACKPORT_LABEL.format(branch=branch) + if backport_label not in util.labels(original_issue): + return + await gh.delete(original_issue['labels_url'], {'name': backport_label}) + message = MESSAGE_TEMPLATE.format(branch=branch, pr=backport_pr_number) + await gh.post(original_issue['comments_url'], data={'body': message}) + + +@router.register("pull_request", action="opened") +@router.register("pull_request", action="edited") +async def manage_labels(event, gh, *args, **kwargs): + if event.data["action"] == "edited" and "title" not in event.data["changes"]: + return + pull_request = event.data["pull_request"] + title = util.normalize_title(pull_request['title'], + pull_request['body']) + title_match = TITLE_RE.match(title) + if title_match is None: + return + branch = title_match.group('branch') + original_pr_number = title_match.group('pr') + + original_issue = await gh.getitem(event.data['repository']['issues_url'], + {'number': original_pr_number}) + await _remove_backport_label(gh, original_issue, branch, + event.data["number"]) + + backport_issue = await util.issue_for_PR(gh, pull_request) + await _copy_over_labels(gh, original_issue, backport_issue) + + +@router.register("pull_request", action="opened") +@router.register("pull_request", action="reopened") +@router.register("pull_request", action="edited") +@router.register("pull_request", action="synchronize") +async def validate_maintenance_branch_pr(event, gh, *args, **kwargs): + """Check the PR title for maintenance branch pull requests. + + If the PR was made against maintenance branch, and the title does not + match the maintenance branch PR pattern, then post a failure status. + + The maintenance branch PR has to start with `[X.Y]` + """ + if event.data["action"] == "edited" and "title" not in event.data["changes"]: + return + pull_request = event.data["pull_request"] + base_branch = pull_request["base"]["ref"] + + if base_branch == "main": + return + + title = util.normalize_title(pull_request["title"], + pull_request["body"]) + title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) + + if title_match is None: + status = create_status(util.StatusState.FAILURE, + description="Not a valid maintenance branch PR title.", + target_url=BACKPORT_TITLE_DEVGUIDE_URL) + else: + status = create_status(util.StatusState.SUCCESS, + description="Valid maintenance branch PR title.") + await util.post_status(gh, event, status) + + +@router.register("create", ref_type="branch") +async def maintenance_branch_created(event, gh, *args, **kwargs): + """Create the `needs backport label` when the maintenance branch is created. + + Also post a reminder to add the maintenance branch to the list of + `ALLOWED_BRANCHES` in CPython-emailer-webhook. + + If a maintenance branch was created (e.g.: 3.9, or 4.0), + automatically create the `needs backport to ` label. + + The maintenance branch PR has to start with `[X.Y]` + """ + branch_name = event.data["ref"] + + if MAINTENANCE_BRANCH_RE.match(branch_name): + await gh.post( + "/repos/python/cpython/labels", + data={"name": f"needs backport to {branch_name}", "color": "#c2e0c6"}, + ) + + await gh.post( + "/repos/berkerpeksag/cpython-emailer-webhook/issues", + data={ + "title": f"Please add {branch_name} to ALLOWED_BRANCHES", + "body": ( + f"A new CPython maintenance branch `{branch_name}` has just been created.", + "\nThis is a reminder to add `{branch_name}` to the list of `ALLOWED_BRANCHES`", + "\nhttps://github.com/berkerpeksag/cpython-emailer-webhook/blob/e164cb9a6735d56012a4e557fd67dd7715c16d7b/mailer.py#L15", + ), + }, + ) From 53dec01c61e30c2090f9fc9a221c434132da2263 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Sat, 21 May 2022 01:37:07 +0200 Subject: [PATCH 02/12] Review backport file with maintenance part --- .github/workflows/backport.yml | 10 ++- .github/workflows/backport_maintenance.yml | 39 ---------- bedevere/backport.py | 86 ++++++++++++++++++++-- bedevere/util.py | 3 +- 4 files changed, 90 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/backport_maintenance.yml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 71407ef5..89f39d32 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -4,8 +4,10 @@ name: backport on: pull_request: - types: [opened, edited] + types: [opened, reopened, edited, synchronize] +env: + GH_AUTH: ${{ secrets.GH_AUTH }} jobs: backport: @@ -15,7 +17,7 @@ jobs: strategy: matrix: - python-version: ["3.8", "3.9", "3.9-dev"] + python-version: ["3.8"] #, "3.9", "3.9-dev"] steps: - uses: actions/checkout@v2 @@ -36,4 +38,6 @@ jobs: - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code - run: python3 bedevere/backport.py \ No newline at end of file + run: python3 -m bedevere.backport + + \ No newline at end of file diff --git a/.github/workflows/backport_maintenance.yml b/.github/workflows/backport_maintenance.yml deleted file mode 100644 index 1d1cd3f1..00000000 --- a/.github/workflows/backport_maintenance.yml +++ /dev/null @@ -1,39 +0,0 @@ - - -name: backport - -on: - pull_request: - types: [opened, reopened, edited, synchronize] - - -jobs: - backport: - name: Check and update backport label of PR - - runs-on: ubuntu-latest - - strategy: - matrix: - python-version: ["3.8", "3.9", "3.9-dev"] - - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - uses: deadsnakes/action@v2.1.1 - if: endsWith(matrix.python-version, '-dev') - with: - python-version: ${{ matrix.python-version }} - - name: Install Dependencies - run: python3 -m pip install coverage -U pip -r dev-requirements.txt - # - name: Run code - # run: python3 -m bedevere backport.py validate_maintenance_branch_pr \ No newline at end of file diff --git a/bedevere/backport.py b/bedevere/backport.py index 92a3a56e..8d4ef497 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -1,4 +1,5 @@ import asyncio +import functools import json import os import re @@ -9,13 +10,18 @@ from . import util -TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') +create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') + +TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') +MAINTENANCE_BRANCH_TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+') +MAINTENANCE_BRANCH_RE = re.compile(r'\s*(?P\d+\.\d+)') BACKPORT_LABEL = 'needs backport to {branch}' MESSAGE_TEMPLATE = ('[GH-{pr}](https://github.com/python/cpython/pull/{pr}) is ' 'a backport of this pull request to the ' '[{branch} branch](https://github.com/python/cpython/tree/{branch}).') +BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/committing/#backport-pr-title" async def issue_for_PR(gh, pull_request): """Get the issue data for a pull request.""" @@ -25,9 +31,12 @@ async def _copy_over_labels(gh, original_issue, backport_issue): """Copy over relevant labels from the original PR to the backport PR.""" label_prefixes = "skip", "type", "sprint" labels = list(filter(lambda x: x.startswith(label_prefixes), - util.labels_(original_issue))) + util.labels(original_issue))) if labels: - await gh.post(backport_issue["labels_url"], data=labels) + response = await gh.post(backport_issue["labels_url"], data=labels) + return response + return "no labels" + async def _remove_backport_label(gh, original_issue, branch, backport_pr_number): @@ -36,11 +45,12 @@ async def _remove_backport_label(gh, original_issue, branch, backport_pr_number) Also leave a comment on the original PR referencing the backport PR. """ backport_label = BACKPORT_LABEL.format(branch=branch) - if backport_label not in util.labels_(original_issue): + if backport_label not in util.labels(original_issue): return await gh.delete(original_issue['labels_url'], {'name': backport_label}) message = MESSAGE_TEMPLATE.format(branch=branch, pr=backport_pr_number) - await gh.post(original_issue['comments_url'], data={'body': message}) + response = await gh.post(original_issue['comments_url'], data={'body': message}) + return response async def manage_labels(gh, *args, **kwargs): with open(os.environ["GITHUB_EVENT_PATH"]) as f: @@ -64,10 +74,76 @@ async def manage_labels(gh, *args, **kwargs): backport_issue = await issue_for_PR(gh, pull_request) await _copy_over_labels(gh, original_issue, backport_issue) +async def validate_maintenance_branch_pr(gh, *args, **kwargs): + """Check the PR title for maintenance branch pull requests. + + If the PR was made against maintenance branch, and the title does not + match the maintenance branch PR pattern, then post a failure status. + + The maintenance branch PR has to start with `[X.Y]` + """ + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + if event.get("action") == "edited" and "title" not in event.get("changes"): + return + pull_request = event["pull_request"] + base_branch = pull_request["base"]["ref"] + + if base_branch == "main": + return + + title = util.normalize_title(pull_request["title"], + pull_request["body"]) + title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) + + if title_match is None: + status = create_status(util.StatusState.FAILURE, + description="Not a valid maintenance branch PR title.", + target_url=BACKPORT_TITLE_DEVGUIDE_URL) + else: + status = create_status(util.StatusState.SUCCESS, + description="Valid maintenance branch PR title.") + await util.post_status(gh, event, status) + +async def maintenance_branch_created(gh, *args, **kwargs): + """Create the `needs backport label` when the maintenance branch is created. + + Also post a reminder to add the maintenance branch to the list of + `ALLOWED_BRANCHES` in CPython-emailer-webhook. + + If a maintenance branch was created (e.g.: 3.9, or 4.0), + automatically create the `needs backport to ` label. + + The maintenance branch PR has to start with `[X.Y]` + """ + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + branch_name = event["pull_request"]["head"]["ref"] + + if MAINTENANCE_BRANCH_RE.match(branch_name): + await gh.post( + "/repos/python/cpython/labels", + data={"name": f"needs backport to {branch_name}", "color": "#c2e0c6"}, + ) + + await gh.post( + "/repos/berkerpeksag/cpython-emailer-webhook/issues", + data={ + "title": f"Please add {branch_name} to ALLOWED_BRANCHES", + "body": ( + f"A new CPython maintenance branch `{branch_name}` has just been created.", + "\nThis is a reminder to add `{branch_name}` to the list of `ALLOWED_BRANCHES`", + "\nhttps://github.com/berkerpeksag/cpython-emailer-webhook/blob/e164cb9a6735d56012a4e557fd67dd7715c16d7b/mailer.py#L15", + ), + }, + ) + async def main(): try: async with aiohttp.ClientSession() as session: gh = GitHubAPI(session, "sabderemane", oauth_token=os.getenv("GH_AUTH")) + await maintenance_branch_created(gh) + await validate_maintenance_branch_pr(gh) await manage_labels(gh) except Exception: traceback.print_exc() diff --git a/bedevere/util.py b/bedevere/util.py index 88b6f567..db3fee61 100644 --- a/bedevere/util.py +++ b/bedevere/util.py @@ -45,7 +45,8 @@ def create_status(context, state, *, description=None, target_url=None): async def post_status(gh, event, status): """Post a status in reaction to an event.""" - await gh.post(event.data["pull_request"]["statuses_url"], data=status) + response = await gh.post(event.data["pull_request"]["statuses_url"], data=status) + return response def skip_label(what): From 5850d4979e10fc38526342aa07e5263299c5a032 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Sat, 21 May 2022 03:23:14 +0200 Subject: [PATCH 03/12] Update backport --- .github/workflows/backport.yml | 4 ++-- bedevere/{backport_old.py => old/backport.py} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename bedevere/{backport_old.py => old/backport.py} (99%) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 89f39d32..b5874f56 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -4,7 +4,7 @@ name: backport on: pull_request: - types: [opened, reopened, edited, synchronize] + types: [opened, reopened, edited] env: GH_AUTH: ${{ secrets.GH_AUTH }} @@ -17,7 +17,7 @@ jobs: strategy: matrix: - python-version: ["3.8"] #, "3.9", "3.9-dev"] + python-version: ["3.8", "3.9", "3.9-dev"] steps: - uses: actions/checkout@v2 diff --git a/bedevere/backport_old.py b/bedevere/old/backport.py similarity index 99% rename from bedevere/backport_old.py rename to bedevere/old/backport.py index 5f559432..f47d7c24 100644 --- a/bedevere/backport_old.py +++ b/bedevere/old/backport.py @@ -4,7 +4,7 @@ import gidgethub.routing -from . import util +from .. import util create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') From da58275b88f075ad0451ecd9cc1ac6d0f3f95d00 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Sat, 21 May 2022 03:24:39 +0200 Subject: [PATCH 04/12] Review close_pr file and split into two files for gh actions --- .github/workflows/close_pr.yml | 43 +++++++++++++++++++++++ .github/workflows/review_pr.yml | 41 ++++++++++++++++++++++ bedevere/close_pr.py | 51 +++++++++++++-------------- bedevere/old/close_pr.py | 61 +++++++++++++++++++++++++++++++++ bedevere/review_pr.py | 53 ++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/close_pr.yml create mode 100644 .github/workflows/review_pr.yml create mode 100644 bedevere/old/close_pr.py create mode 100644 bedevere/review_pr.py diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml new file mode 100644 index 00000000..1c9d6cd3 --- /dev/null +++ b/.github/workflows/close_pr.yml @@ -0,0 +1,43 @@ + + +name: close PR + +on: + pull_request: + types: [opened, synchronize] + +env: + GH_AUTH: ${{ secrets.GH_AUTH }} + +jobs: + close_pr: + name: close invalid PR + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.8"] #, "3.9", "3.9-dev"] + + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v2 + if: "!endsWith(matrix.python-version, '-dev')" + with: + python-version: ${{ matrix.python-version }} + - uses: deadsnakes/action@v2.1.1 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: python3 -m pip install coverage -U pip -r dev-requirements.txt + - name: Run code + run: python3 -m bedevere.close_pr + + \ No newline at end of file diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml new file mode 100644 index 00000000..a3c0cb0d --- /dev/null +++ b/.github/workflows/review_pr.yml @@ -0,0 +1,41 @@ +name: Dissmiss review on invalid PR + +on: + pull_request: + types: [review_requested] + +env: + GH_AUTH: ${{ secrets.GH_AUTH }} + +jobs: + review_invalid_pr: + name: Check and update backport label of PR + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.8"] #, "3.9", "3.9-dev"] + + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v2 + if: "!endsWith(matrix.python-version, '-dev')" + with: + python-version: ${{ matrix.python-version }} + - uses: deadsnakes/action@v2.1.1 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: python3 -m pip install coverage -U pip -r dev-requirements.txt + - name: Run code + run: python3 -m bedevere.review_pr + + \ No newline at end of file diff --git a/bedevere/close_pr.py b/bedevere/close_pr.py index b09adc58..834b3642 100644 --- a/bedevere/close_pr.py +++ b/bedevere/close_pr.py @@ -1,7 +1,12 @@ """Automatically close PR that tries to merge maintenance branch into main.""" +import asyncio +import json +import os import re +import traceback -import gidgethub.routing +import aiohttp +from gidgethub.aiohttp import GitHubAPI PYTHON_MAINT_BRANCH_RE = re.compile(r'^\w+:\d+\.\d+$') @@ -13,49 +18,39 @@ see devguide.python.org for further instruction as needed.""" -router = gidgethub.routing.Router() - -@router.register("pull_request", action="opened") -@router.register("pull_request", action="synchronize") -async def close_invalid_pr(event, gh, *args, **kwargs): +async def close_invalid_pr(gh, *args, **kwargs): """Close the invalid PR, add 'invalid' label, and post a message. PR is considered invalid if: * base_label is 'python:main' * head_label is ':' """ - head_label = event.data["pull_request"]["head"]["label"] - base_label = event.data["pull_request"]["base"]["label"] + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + head_label = event["pull_request"]["head"]["label"] + base_label = event["pull_request"]["base"]["label"] if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ base_label == "python:main": data = {'state': 'closed'} - await gh.patch(event.data["pull_request"]["url"], data=data) + await gh.patch(event["pull_request"]["url"], data=data) await gh.post( - f'{event.data["pull_request"]["issue_url"]}/labels', + f'{event["pull_request"]["issue_url"]}/labels', data=["invalid"] ) await gh.post( - f'{event.data["pull_request"]["issue_url"]}/comments', + f'{event["pull_request"]["issue_url"]}/comments', data={'body': INVALID_PR_COMMENT} ) +async def main(): + try: + async with aiohttp.ClientSession() as session: + gh = GitHubAPI(session, "sabderemane", oauth_token=os.getenv("GH_AUTH")) + await close_invalid_pr(gh) + except Exception: + traceback.print_exc() -@router.register("pull_request", action="review_requested") -async def dismiss_invalid_pr_review_request(event, gh, *args, **kwargs): - """Dismiss review request from the invalid PR. - PR is considered invalid if: - * base_label is 'python:main' - * head_label is ':' - """ - head_label = event.data["pull_request"]["head"]["label"] - base_label = event.data["pull_request"]["base"]["label"] - - if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ - base_label == "python:main": - data = {"reviewers": [reviewer["login"] for reviewer in event.data["pull_request"]["requested_reviewers"]], - "team_reviewers": [team["name"] for team in event.data["pull_request"]["requested_teams"]] - } - await gh.delete(f'{event.data["pull_request"]["url"]}/requested_reviewers', - data=data) +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) \ No newline at end of file diff --git a/bedevere/old/close_pr.py b/bedevere/old/close_pr.py new file mode 100644 index 00000000..b09adc58 --- /dev/null +++ b/bedevere/old/close_pr.py @@ -0,0 +1,61 @@ +"""Automatically close PR that tries to merge maintenance branch into main.""" +import re + +import gidgethub.routing + + +PYTHON_MAINT_BRANCH_RE = re.compile(r'^\w+:\d+\.\d+$') + +INVALID_PR_COMMENT = """\ +PRs attempting to merge a maintenance branch into the \ +main branch are deemed to be spam and automatically closed. \ +If you were attempting to report a bug, please go to bugs.python.org; \ +see devguide.python.org for further instruction as needed.""" + + +router = gidgethub.routing.Router() + +@router.register("pull_request", action="opened") +@router.register("pull_request", action="synchronize") +async def close_invalid_pr(event, gh, *args, **kwargs): + """Close the invalid PR, add 'invalid' label, and post a message. + + PR is considered invalid if: + * base_label is 'python:main' + * head_label is ':' + """ + head_label = event.data["pull_request"]["head"]["label"] + base_label = event.data["pull_request"]["base"]["label"] + + if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ + base_label == "python:main": + data = {'state': 'closed'} + await gh.patch(event.data["pull_request"]["url"], data=data) + await gh.post( + f'{event.data["pull_request"]["issue_url"]}/labels', + data=["invalid"] + ) + await gh.post( + f'{event.data["pull_request"]["issue_url"]}/comments', + data={'body': INVALID_PR_COMMENT} + ) + + +@router.register("pull_request", action="review_requested") +async def dismiss_invalid_pr_review_request(event, gh, *args, **kwargs): + """Dismiss review request from the invalid PR. + + PR is considered invalid if: + * base_label is 'python:main' + * head_label is ':' + """ + head_label = event.data["pull_request"]["head"]["label"] + base_label = event.data["pull_request"]["base"]["label"] + + if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ + base_label == "python:main": + data = {"reviewers": [reviewer["login"] for reviewer in event.data["pull_request"]["requested_reviewers"]], + "team_reviewers": [team["name"] for team in event.data["pull_request"]["requested_teams"]] + } + await gh.delete(f'{event.data["pull_request"]["url"]}/requested_reviewers', + data=data) diff --git a/bedevere/review_pr.py b/bedevere/review_pr.py new file mode 100644 index 00000000..78426f83 --- /dev/null +++ b/bedevere/review_pr.py @@ -0,0 +1,53 @@ +"""Automatically close PR that tries to merge maintenance branch into main.""" +import asyncio +import json +import os +import re +import traceback + +import aiohttp +from gidgethub.aiohttp import GitHubAPI +# import gidgethub.routing + + +PYTHON_MAINT_BRANCH_RE = re.compile(r'^\w+:\d+\.\d+$') + +INVALID_PR_COMMENT = """\ +PRs attempting to merge a maintenance branch into the \ +main branch are deemed to be spam and automatically closed. \ +If you were attempting to report a bug, please go to bugs.python.org; \ +see devguide.python.org for further instruction as needed.""" + + +# @router.register("pull_request", action="review_requested") +async def dismiss_invalid_pr_review_request(gh, *args, **kwargs): + """Dismiss review request from the invalid PR. + + PR is considered invalid if: + * base_label is 'python:main' + * head_label is ':' + """ + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + head_label = event["pull_request"]["head"]["label"] + base_label = event["pull_request"]["base"]["label"] + + if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ + base_label == "python:main": + data = {"reviewers": [reviewer["login"] for reviewer in event["pull_request"]["requested_reviewers"]], + "team_reviewers": [team["name"] for team in event["pull_request"]["requested_teams"]] + } + await gh.delete(f'{event["pull_request"]["url"]}/requested_reviewers', + data=data) + +async def main(): + try: + async with aiohttp.ClientSession() as session: + gh = GitHubAPI(session, "sabderemane", oauth_token=os.getenv("GH_AUTH")) + await dismiss_invalid_pr_review_request(gh) + except Exception: + traceback.print_exc() + + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) \ No newline at end of file From c4b1315e671d48d3578d65018aaf51e437ca6983 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Sat, 21 May 2022 03:24:49 +0200 Subject: [PATCH 05/12] review filepaths --- .github/workflows/backport.yml | 2 +- .github/workflows/close_pr.yml | 2 +- .github/workflows/filepaths.yml | 43 +++++++++++++++++++++++++++++++++ .github/workflows/review_pr.yml | 2 +- bedevere/filepaths.py | 35 +++++++++++++++++++-------- bedevere/old/filepaths.py | 22 +++++++++++++++++ 6 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/filepaths.yml create mode 100644 bedevere/old/filepaths.py diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index b5874f56..ea62def5 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - python-version: ["3.8", "3.9", "3.9-dev"] + python-version: ["3.9"] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml index 1c9d6cd3..65e1f3af 100644 --- a/.github/workflows/close_pr.yml +++ b/.github/workflows/close_pr.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - python-version: ["3.8"] #, "3.9", "3.9-dev"] + python-version: ["3.9"] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/filepaths.yml b/.github/workflows/filepaths.yml new file mode 100644 index 00000000..74c3e1c4 --- /dev/null +++ b/.github/workflows/filepaths.yml @@ -0,0 +1,43 @@ + + +name: filepaths + +on: + pull_request: + types: [opened, reopened, edited] + +env: + GH_AUTH: ${{ secrets.GH_AUTH }} + +jobs: + filepaths: + name: Checks filepaths on a PR + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.9"] + + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v2 + if: "!endsWith(matrix.python-version, '-dev')" + with: + python-version: ${{ matrix.python-version }} + - uses: deadsnakes/action@v2.1.1 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: python3 -m pip install coverage -U pip -r dev-requirements.txt + - name: Run code + run: python3 -m bedevere.filepaths + + \ No newline at end of file diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml index a3c0cb0d..f3136478 100644 --- a/.github/workflows/review_pr.yml +++ b/.github/workflows/review_pr.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - python-version: ["3.8"] #, "3.9", "3.9-dev"] + python-version: ["3.9"] steps: - uses: actions/checkout@v2 diff --git a/bedevere/filepaths.py b/bedevere/filepaths.py index 37936fdf..46c5a0b5 100644 --- a/bedevere/filepaths.py +++ b/bedevere/filepaths.py @@ -1,24 +1,39 @@ """Checks related to filepaths on a pull request.""" -import gidgethub.routing +import asyncio +import json +import os +import traceback + +import aiohttp +from gidgethub.aiohttp import GitHubAPI from . import news from . import prtype from . import util -router = gidgethub.routing.Router() - - -@router.register('pull_request', action='opened') -@router.register('pull_request', action='synchronize') -@router.register('pull_request', action='reopened') -async def check_file_paths(event, gh, *args, **kwargs): - pull_request = event.data['pull_request'] +async def check_file_paths(gh, *args, **kwargs): + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + pull_request = event['pull_request'] files = await util.files_for_PR(gh, pull_request) filenames = [file['file_name'] for file in files] - if event.data['action'] == 'opened': + if event['action'] == 'opened': labels = await prtype.classify_by_filepaths(gh, pull_request, filenames) if prtype.Labels.skip_news not in labels: await news.check_news(gh, pull_request, files) else: await news.check_news(gh, pull_request, files) + await news.check_news(gh, pull_request, files) + +async def main(): + try: + async with aiohttp.ClientSession() as session: + gh = GitHubAPI(session, "sabderemane", oauth_token=os.getenv("GH_AUTH")) + await check_file_paths(gh) + except Exception: + traceback.print_exc() + + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) diff --git a/bedevere/old/filepaths.py b/bedevere/old/filepaths.py new file mode 100644 index 00000000..515ba8b3 --- /dev/null +++ b/bedevere/old/filepaths.py @@ -0,0 +1,22 @@ +"""Checks related to filepaths on a pull request.""" +import gidgethub.routing + +from . import news +from . import prtype +from . import util + + +router = gidgethub.routing.Router() + + +@router.register('pull_request', action='opened') +@router.register('pull_request', action='synchronize') +@router.register('pull_request', action='reopened') +async def check_file_paths(event, gh, *args, **kwargs): + pull_request = event.data['pull_request'] + files = await util.files_for_PR(gh, pull_request) + filenames = [file['file_name'] for file in files] + await news.check_news(gh, pull_request, files) + if event.data['action'] == 'opened': + await prtype.classify_by_filepaths(gh, pull_request, filenames) + From b0e9ef6d637dbaa19209436dd66bee59e00d650e Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Sat, 21 May 2022 04:20:38 +0200 Subject: [PATCH 06/12] Review python version workflows --- .github/workflows/backport.yml | 17 +++-------------- .github/workflows/close_pr.yml | 17 +++-------------- .github/workflows/filepaths.yml | 17 +++-------------- .github/workflows/review_pr.yml | 17 ++++------------- 4 files changed, 13 insertions(+), 55 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index ea62def5..469f68c4 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -1,6 +1,4 @@ - - -name: backport +name: Backport on: pull_request: @@ -15,10 +13,6 @@ jobs: runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.9"] - steps: - uses: actions/checkout@v2 - uses: actions/cache@v1 @@ -27,14 +21,9 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - uses: deadsnakes/action@v2.1.1 - if: endsWith(matrix.python-version, '-dev') + - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '3.x' - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml index 65e1f3af..19f03b68 100644 --- a/.github/workflows/close_pr.yml +++ b/.github/workflows/close_pr.yml @@ -1,6 +1,4 @@ - - -name: close PR +name: Close PR on: pull_request: @@ -15,10 +13,6 @@ jobs: runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.9"] - steps: - uses: actions/checkout@v2 - uses: actions/cache@v1 @@ -27,14 +21,9 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - uses: deadsnakes/action@v2.1.1 - if: endsWith(matrix.python-version, '-dev') + - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '3.x' - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code diff --git a/.github/workflows/filepaths.yml b/.github/workflows/filepaths.yml index 74c3e1c4..b21c32fa 100644 --- a/.github/workflows/filepaths.yml +++ b/.github/workflows/filepaths.yml @@ -1,6 +1,4 @@ - - -name: filepaths +name: Filepaths on: pull_request: @@ -15,10 +13,6 @@ jobs: runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.9"] - steps: - uses: actions/checkout@v2 - uses: actions/cache@v1 @@ -27,14 +21,9 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - uses: deadsnakes/action@v2.1.1 - if: endsWith(matrix.python-version, '-dev') + - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '3.x' - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml index f3136478..27bab0f9 100644 --- a/.github/workflows/review_pr.yml +++ b/.github/workflows/review_pr.yml @@ -1,4 +1,4 @@ -name: Dissmiss review on invalid PR +name: Review PR on: pull_request: @@ -9,14 +9,10 @@ env: jobs: review_invalid_pr: - name: Check and update backport label of PR + name: Dismiss review request from the invalid PR runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.9"] - steps: - uses: actions/checkout@v2 - uses: actions/cache@v1 @@ -25,14 +21,9 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - uses: deadsnakes/action@v2.1.1 - if: endsWith(matrix.python-version, '-dev') + - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '3.x' - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code From ea928247999f3af54091bea0c3da0d2dc90d8832 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Thu, 14 Jul 2022 14:56:19 +0200 Subject: [PATCH 07/12] Review descriptions and cache workflows --- .github/workflows/backport.yml | 2 +- .github/workflows/close_pr.yml | 2 +- .github/workflows/filepaths.yml | 2 +- .github/workflows/review_pr.yml | 2 +- CONTRIBUTING.rst | 75 +++------------------------------ README.md | 2 +- bedevere/__main__.py | 3 +- bedevere/close_pr.py | 8 ++-- bedevere/review_pr.py | 5 ++- 9 files changed, 19 insertions(+), 82 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 469f68c4..331ed2b6 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - uses: actions/setup-python@v3 diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml index 19f03b68..7ddcdf9a 100644 --- a/.github/workflows/close_pr.yml +++ b/.github/workflows/close_pr.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - uses: actions/setup-python@v3 diff --git a/.github/workflows/filepaths.yml b/.github/workflows/filepaths.yml index b21c32fa..9b101cf2 100644 --- a/.github/workflows/filepaths.yml +++ b/.github/workflows/filepaths.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - uses: actions/setup-python@v3 diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml index 27bab0f9..381deb00 100644 --- a/.github/workflows/review_pr.yml +++ b/.github/workflows/review_pr.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - uses: actions/setup-python@v3 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 191a5e1c..1e6fedf6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,83 +1,18 @@ Contributing and Maintenance Guide ================================== -Bedevere web service is deployed to Heroku, which is managed by The PSF. +Bedevere web service is currently launched with GitHub Actions, which is managed by The PSF. -Deployment ----------- +All actions are listed in actions tab of the repository: https://github.com/python/bedevere/actions -There are two ways to have bedevere deployed: automatic deployment, and -manual deployment. -Automatic Deployment (currently broken) -''''''''''''''''''''''''''''''''''''''' - -When the automatic deployment is enabled (on Heroku side), any merged PR -will automatically be deployed to Heroku. This process takes less than 5 minutes. - -If after 10 minutes you did not see the changes reflected, please ping one -of the collaborators listed below. - -To enable Automatic deployment: - -- On the Heroku dashboard for bedevere, choose the "Deploy" tab. -- Scroll down to the "Automatic deploys" section -- Enter the name of the branch to be deployed (in this case: ``main``) -- Check the "Wait for CI to pass before deploy" button -- Press the "Enable automatic deploys" button. - -Once done, merging a PR against the ``main`` branch will trigger a new -deployment using a webhook that is already set up in the repo settings. - - -.. note:: - - Due to recent `security incident`_, the Heroku GitHub integration is broken. - Automatic deployment does not currently work. Until this gets resolved, - maintainers have to deploy bedevere to Heroku manually. - - -Manual Deployment -''''''''''''''''' - -The app can be deployed manually to Heroku by collaborators and members of the ``bedevere`` app on Heroku. -Heroku admins can do it too. - -#. Install Heroku CLI - - Details at: https://devcenter.heroku.com/articles/heroku-cli - -#. Login to Heroku CLI on the command line and follow instructions:: - - heroku login - - -#. If you haven't already, get a clone of the bedevere repo:: - - git clone git@github.com:python/bedevere.git - - Or, using `GitHub CLI`_:: - - gh repo clone python/bedevere - -#. From the ``bedevere`` directory, add the ``bedevere`` Heroku app as remote branch:: - - heroku git:remote -a bedevere - - -#. From the ``bedevere`` directory, push to Heroku:: - - git push heroku main - - -After a successful push, the deployment will begin. - -Heroku app collaborators and members +App collaborators and members '''''''''''''''''''''''''''''''''''' - @Mariatta - @ambv - @brettcannon +- @sabderemane -.. _security incident: https://status.heroku.com/incidents/2413 .. _GitHub CLI: https://cli.github.com/ +.. _GitHub Actions: https://github.com/python/bedevere/actions diff --git a/README.md b/README.md index 8e4b24ed..39ded429 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This bot is meant to help identify issues with a CPython pull request. If no GitHub issue number is found the status fails and the "Details" link points to the relevant [section of the devguide](https://devguide.python.org/pullrequest/#submitting). -- ### Links to bugs.python.org +- ### Links to github.com/python/cpython/issues If an issue number is found then the "Details" link points to the relevant issue itself, making it easier to navigate from PR to issue. - ### Identifies missing news entry diff --git a/bedevere/__main__.py b/bedevere/__main__.py index 2e6f3130..56da9e08 100644 --- a/bedevere/__main__.py +++ b/bedevere/__main__.py @@ -54,4 +54,5 @@ async def main(request): port = os.environ.get("PORT") if port is not None: port = int(port) - web.run_app(app, port=port) \ No newline at end of file + web.run_app(app, port=port) + \ No newline at end of file diff --git a/bedevere/close_pr.py b/bedevere/close_pr.py index 834b3642..fa475381 100644 --- a/bedevere/close_pr.py +++ b/bedevere/close_pr.py @@ -14,9 +14,9 @@ INVALID_PR_COMMENT = """\ PRs attempting to merge a maintenance branch into the \ main branch are deemed to be spam and automatically closed. \ -If you were attempting to report a bug, please go to bugs.python.org; \ -see devguide.python.org for further instruction as needed.""" - +If you were attempting to report a bug, please go to \ + https://github.com/python/cpython/issues; \ + see devguide.python.org for further instruction as needed.""" async def close_invalid_pr(gh, *args, **kwargs): """Close the invalid PR, add 'invalid' label, and post a message. @@ -53,4 +53,4 @@ async def main(): loop = asyncio.get_event_loop() -loop.run_until_complete(main()) \ No newline at end of file +loop.run_until_complete(main()) diff --git a/bedevere/review_pr.py b/bedevere/review_pr.py index 78426f83..e7451b26 100644 --- a/bedevere/review_pr.py +++ b/bedevere/review_pr.py @@ -15,7 +15,8 @@ INVALID_PR_COMMENT = """\ PRs attempting to merge a maintenance branch into the \ main branch are deemed to be spam and automatically closed. \ -If you were attempting to report a bug, please go to bugs.python.org; \ +If you were attempting to report a bug, please go to \ +https://github.com/python/cpython/issues; \ see devguide.python.org for further instruction as needed.""" @@ -50,4 +51,4 @@ async def main(): loop = asyncio.get_event_loop() -loop.run_until_complete(main()) \ No newline at end of file +loop.run_until_complete(main()) From 195dd5fbf1f9a99d4df31e5bc75df84bc961fc13 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Thu, 14 Jul 2022 15:14:47 +0200 Subject: [PATCH 08/12] Fix indentation --- .github/workflows/backport.yml | 2 -- .github/workflows/close_pr.yml | 2 -- .github/workflows/filepaths.yml | 2 -- .github/workflows/review_pr.yml | 2 -- bedevere/backport.py | 60 ++++++++++++++++----------------- 5 files changed, 30 insertions(+), 38 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 331ed2b6..c4376124 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -28,5 +28,3 @@ jobs: run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code run: python3 -m bedevere.backport - - \ No newline at end of file diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml index 7ddcdf9a..1d4124d8 100644 --- a/.github/workflows/close_pr.yml +++ b/.github/workflows/close_pr.yml @@ -28,5 +28,3 @@ jobs: run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code run: python3 -m bedevere.close_pr - - \ No newline at end of file diff --git a/.github/workflows/filepaths.yml b/.github/workflows/filepaths.yml index 9b101cf2..93f068d1 100644 --- a/.github/workflows/filepaths.yml +++ b/.github/workflows/filepaths.yml @@ -28,5 +28,3 @@ jobs: run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code run: python3 -m bedevere.filepaths - - \ No newline at end of file diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml index 381deb00..7aeaf99d 100644 --- a/.github/workflows/review_pr.yml +++ b/.github/workflows/review_pr.yml @@ -28,5 +28,3 @@ jobs: run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code run: python3 -m bedevere.review_pr - - \ No newline at end of file diff --git a/bedevere/backport.py b/bedevere/backport.py index 8d4ef497..1d36b40e 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -55,16 +55,16 @@ async def _remove_backport_label(gh, original_issue, branch, backport_pr_number) async def manage_labels(gh, *args, **kwargs): with open(os.environ["GITHUB_EVENT_PATH"]) as f: event = json.load(f) - if event.get("action") == "edited" and "title" not in event.get("changes"): - return - pull_request = event["pull_request"] - title = util.normalize_title(pull_request['title'], - pull_request['body']) - title_match = TITLE_RE.match(title) - if title_match is None: - return - branch = title_match.group('branch') - original_pr_number = title_match.group('pr') + if event.get("action") == "edited" and "title" not in event.get("changes"): + return + pull_request = event["pull_request"] + title = util.normalize_title(pull_request['title'], + pull_request['body']) + title_match = TITLE_RE.match(title) + if title_match is None: + return + branch = title_match.group('branch') + original_pr_number = title_match.group('pr') original_issue = await gh.getitem(event['repository']['issues_url'], {'number': original_pr_number}) @@ -84,26 +84,26 @@ async def validate_maintenance_branch_pr(gh, *args, **kwargs): """ with open(os.environ["GITHUB_EVENT_PATH"]) as f: event = json.load(f) - if event.get("action") == "edited" and "title" not in event.get("changes"): - return - pull_request = event["pull_request"] - base_branch = pull_request["base"]["ref"] - - if base_branch == "main": - return - - title = util.normalize_title(pull_request["title"], - pull_request["body"]) - title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) - - if title_match is None: - status = create_status(util.StatusState.FAILURE, - description="Not a valid maintenance branch PR title.", - target_url=BACKPORT_TITLE_DEVGUIDE_URL) - else: - status = create_status(util.StatusState.SUCCESS, - description="Valid maintenance branch PR title.") - await util.post_status(gh, event, status) + if event.get("action") == "edited" and "title" not in event.get("changes"): + return + pull_request = event["pull_request"] + base_branch = pull_request["base"]["ref"] + + if base_branch == "main": + return + + title = util.normalize_title(pull_request["title"], + pull_request["body"]) + title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) + + if title_match is None: + status = create_status(util.StatusState.FAILURE, + description="Not a valid maintenance branch PR title.", + target_url=BACKPORT_TITLE_DEVGUIDE_URL) + else: + status = create_status(util.StatusState.SUCCESS, + description="Valid maintenance branch PR title.") + await util.post_status(gh, event, status) async def maintenance_branch_created(gh, *args, **kwargs): """Create the `needs backport label` when the maintenance branch is created. From 2fc8bbf6ed594068d072ac7df6a8a7f01a90af51 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Mon, 3 Oct 2022 22:50:25 +0200 Subject: [PATCH 09/12] fixes from reviews --- .github/workflows/backport.yml | 14 +++++--------- .github/workflows/close_pr.yml | 16 ++++++---------- .github/workflows/filepaths.yml | 16 ++++++---------- .github/workflows/review_pr.yml | 16 ++++++---------- CONTRIBUTING.rst | 4 ++-- 5 files changed, 25 insertions(+), 41 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index c4376124..39bc025e 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -14,16 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - uses: actions/setup-python@v3 - with: - python-version: '3.x' + python-version: "3.x" + cache: pip + cache-dependency-path: "*requirements.txt" - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - name: Run code diff --git a/.github/workflows/close_pr.yml b/.github/workflows/close_pr.yml index 1d4124d8..c6f76e7d 100644 --- a/.github/workflows/close_pr.yml +++ b/.github/workflows/close_pr.yml @@ -14,17 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - uses: actions/setup-python@v3 - with: - python-version: '3.x' + python-version: "3.x" + cache: pip + cache-dependency-path: "*requirements.txt" - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - - name: Run code + - name: Run close_pr.py file run: python3 -m bedevere.close_pr diff --git a/.github/workflows/filepaths.yml b/.github/workflows/filepaths.yml index 93f068d1..497c3a72 100644 --- a/.github/workflows/filepaths.yml +++ b/.github/workflows/filepaths.yml @@ -14,17 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - uses: actions/setup-python@v3 - with: - python-version: '3.x' + python-version: "3.x" + cache: pip + cache-dependency-path: "*requirements.txt" - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - - name: Run code + - name: Run filepaths.py file run: python3 -m bedevere.filepaths diff --git a/.github/workflows/review_pr.yml b/.github/workflows/review_pr.yml index 7aeaf99d..276723ef 100644 --- a/.github/workflows/review_pr.yml +++ b/.github/workflows/review_pr.yml @@ -14,17 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - uses: actions/setup-python@v3 - with: - python-version: '3.x' + python-version: "3.x" + cache: pip + cache-dependency-path: "*requirements.txt" - name: Install Dependencies run: python3 -m pip install coverage -U pip -r dev-requirements.txt - - name: Run code + - name: Run review_pr.py file run: python3 -m bedevere.review_pr diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 1e6fedf6..d07e58b1 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,13 +1,13 @@ Contributing and Maintenance Guide ================================== -Bedevere web service is currently launched with GitHub Actions, which is managed by The PSF. +Bedevere web service is launched with GitHub Actions, which is managed by The PSF. All actions are listed in actions tab of the repository: https://github.com/python/bedevere/actions App collaborators and members -'''''''''''''''''''''''''''''''''''' +''''''''''''''''''''''''''''' - @Mariatta - @ambv From 11a5d34ef86b7149ae776f4a4c674b20376ae209 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Mon, 3 Oct 2022 23:04:27 +0200 Subject: [PATCH 10/12] Apply suggestions from code review Co-authored-by: Ezio Melotti --- CONTRIBUTING.rst | 5 ++--- README.md | 2 +- bedevere/backport.py | 2 +- bedevere/close_pr.py | 8 ++++---- bedevere/filepaths.py | 2 +- bedevere/review_pr.py | 18 +++++++++--------- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d07e58b1..84aa8989 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -3,7 +3,7 @@ Contributing and Maintenance Guide Bedevere web service is launched with GitHub Actions, which is managed by The PSF. -All actions are listed in actions tab of the repository: https://github.com/python/bedevere/actions +All actions are listed in the `Actions`_ tab of this repository. App collaborators and members @@ -14,5 +14,4 @@ App collaborators and members - @brettcannon - @sabderemane -.. _GitHub CLI: https://cli.github.com/ -.. _GitHub Actions: https://github.com/python/bedevere/actions +.. _Actions: https://github.com/python/bedevere/actions diff --git a/README.md b/README.md index 39ded429..2aea3778 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This bot is meant to help identify issues with a CPython pull request. - ### Identifies missing GitHub issue numbers in the title If no GitHub issue number is found the status fails and the "Details" link points to the relevant -[section of the devguide](https://devguide.python.org/pullrequest/#submitting). +[section of the devguide](https://devguide.python.org/getting-started/pull-request-lifecycle/#submitting). - ### Links to github.com/python/cpython/issues If an issue number is found then the "Details" link points to the relevant issue itself, making it easier to navigate from PR to issue. diff --git a/bedevere/backport.py b/bedevere/backport.py index 1d36b40e..b117c9f7 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -21,7 +21,7 @@ 'a backport of this pull request to the ' '[{branch} branch](https://github.com/python/cpython/tree/{branch}).') -BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/committing/#backport-pr-title" +BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/core-developers/committing/#backport-pr-title" async def issue_for_PR(gh, pull_request): """Get the issue data for a pull request.""" diff --git a/bedevere/close_pr.py b/bedevere/close_pr.py index fa475381..5c178e6b 100644 --- a/bedevere/close_pr.py +++ b/bedevere/close_pr.py @@ -15,8 +15,8 @@ PRs attempting to merge a maintenance branch into the \ main branch are deemed to be spam and automatically closed. \ If you were attempting to report a bug, please go to \ - https://github.com/python/cpython/issues; \ - see devguide.python.org for further instruction as needed.""" +https://github.com/python/cpython/issues; \ +see devguide.python.org for further instruction as needed.""" async def close_invalid_pr(gh, *args, **kwargs): """Close the invalid PR, add 'invalid' label, and post a message. @@ -27,8 +27,8 @@ async def close_invalid_pr(gh, *args, **kwargs): """ with open(os.environ["GITHUB_EVENT_PATH"]) as f: event = json.load(f) - head_label = event["pull_request"]["head"]["label"] - base_label = event["pull_request"]["base"]["label"] + head_label = event["pull_request"]["head"]["label"] + base_label = event["pull_request"]["base"]["label"] if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ base_label == "python:main": diff --git a/bedevere/filepaths.py b/bedevere/filepaths.py index 46c5a0b5..a0f02e1c 100644 --- a/bedevere/filepaths.py +++ b/bedevere/filepaths.py @@ -15,7 +15,7 @@ async def check_file_paths(gh, *args, **kwargs): with open(os.environ["GITHUB_EVENT_PATH"]) as f: event = json.load(f) - pull_request = event['pull_request'] + pull_request = event['pull_request'] files = await util.files_for_PR(gh, pull_request) filenames = [file['file_name'] for file in files] if event['action'] == 'opened': diff --git a/bedevere/review_pr.py b/bedevere/review_pr.py index e7451b26..1275ed12 100644 --- a/bedevere/review_pr.py +++ b/bedevere/review_pr.py @@ -30,16 +30,16 @@ async def dismiss_invalid_pr_review_request(gh, *args, **kwargs): """ with open(os.environ["GITHUB_EVENT_PATH"]) as f: event = json.load(f) - head_label = event["pull_request"]["head"]["label"] - base_label = event["pull_request"]["base"]["label"] - - if PYTHON_MAINT_BRANCH_RE.match(head_label) and \ - base_label == "python:main": - data = {"reviewers": [reviewer["login"] for reviewer in event["pull_request"]["requested_reviewers"]], - "team_reviewers": [team["name"] for team in event["pull_request"]["requested_teams"]] - } + head_label = event["pull_request"]["head"]["label"] + base_label = event["pull_request"]["base"]["label"] + + if (PYTHON_MAINT_BRANCH_RE.match(head_label) and + base_label == "python:main"): + pr = event["pull_request"] + reviewers = [reviewer["login"] for reviewer in pr["requested_reviewers"] + team_reviewers = [team["name"] for team in pr["requested_teams"] await gh.delete(f'{event["pull_request"]["url"]}/requested_reviewers', - data=data) + data=dict(reviewers=reviewers, team_reviewers=team_reviewers)) async def main(): try: From 404949bc5e79d5047053cf6485481e306359b4aa Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Mon, 3 Oct 2022 23:13:47 +0200 Subject: [PATCH 11/12] fixes from reviews #2 --- bedevere/__main__.py | 1 - bedevere/backport.py | 22 +++++++++++++++++----- bedevere/old/backport.py | 23 +++++++++++++++++++---- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/bedevere/__main__.py b/bedevere/__main__.py index 56da9e08..3a3c0740 100644 --- a/bedevere/__main__.py +++ b/bedevere/__main__.py @@ -55,4 +55,3 @@ async def main(request): if port is not None: port = int(port) web.run_app(app, port=port) - \ No newline at end of file diff --git a/bedevere/backport.py b/bedevere/backport.py index b117c9f7..49cf1919 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -13,7 +13,7 @@ create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') -TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') +TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)', re.IGNORECASE) MAINTENANCE_BRANCH_TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+') MAINTENANCE_BRANCH_RE = re.compile(r'\s*(?P\d+\.\d+)') BACKPORT_LABEL = 'needs backport to {branch}' @@ -33,9 +33,7 @@ async def _copy_over_labels(gh, original_issue, backport_issue): labels = list(filter(lambda x: x.startswith(label_prefixes), util.labels(original_issue))) if labels: - response = await gh.post(backport_issue["labels_url"], data=labels) - return response - return "no labels" + await gh.post(backport_issue["labels_url"], data=labels) @@ -74,6 +72,20 @@ async def manage_labels(gh, *args, **kwargs): backport_issue = await issue_for_PR(gh, pull_request) await _copy_over_labels(gh, original_issue, backport_issue) +def is_maintenance_branch(ref): + """ + Return True if the ref refers to a maintenance branch. + + >>> is_maintenance_branch("3.11") + True + >>> is_maintenance_branch("main") + False + >>> is_maintenance_branch("gh-1234/something-completely-different") + False + """ + maintenance_branch_pattern = r'\d+\.\d+' + return bool(re.fullmatch(maintenance_branch_pattern, ref)) + async def validate_maintenance_branch_pr(gh, *args, **kwargs): """Check the PR title for maintenance branch pull requests. @@ -89,7 +101,7 @@ async def validate_maintenance_branch_pr(gh, *args, **kwargs): pull_request = event["pull_request"] base_branch = pull_request["base"]["ref"] - if base_branch == "main": + if not is_maintenance_branch(base_branch): return title = util.normalize_title(pull_request["title"], diff --git a/bedevere/old/backport.py b/bedevere/old/backport.py index f47d7c24..ddb743bd 100644 --- a/bedevere/old/backport.py +++ b/bedevere/old/backport.py @@ -4,14 +4,14 @@ import gidgethub.routing -from .. import util +from . import util create_status = functools.partial(util.create_status, 'bedevere/maintenance-branch-pr') router = gidgethub.routing.Router() -TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)') +TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+\((?:GH-|#)(?P\d+)\)', re.IGNORECASE) MAINTENANCE_BRANCH_TITLE_RE = re.compile(r'\s*\[(?P\d+\.\d+)\].+') MAINTENANCE_BRANCH_RE = re.compile(r'\s*(?P\d+\.\d+)') BACKPORT_LABEL = 'needs backport to {branch}' @@ -67,6 +67,21 @@ async def manage_labels(event, gh, *args, **kwargs): await _copy_over_labels(gh, original_issue, backport_issue) +def is_maintenance_branch(ref): + """ + Return True if the ref refers to a maintenance branch. + + >>> is_maintenance_branch("3.11") + True + >>> is_maintenance_branch("main") + False + >>> is_maintenance_branch("gh-1234/something-completely-different") + False + """ + maintenance_branch_pattern = r'\d+\.\d+' + return bool(re.fullmatch(maintenance_branch_pattern, ref)) + + @router.register("pull_request", action="opened") @router.register("pull_request", action="reopened") @router.register("pull_request", action="edited") @@ -84,7 +99,7 @@ async def validate_maintenance_branch_pr(event, gh, *args, **kwargs): pull_request = event.data["pull_request"] base_branch = pull_request["base"]["ref"] - if base_branch == "main": + if not is_maintenance_branch(base_branch): return title = util.normalize_title(pull_request["title"], @@ -131,4 +146,4 @@ async def maintenance_branch_created(event, gh, *args, **kwargs): "\nhttps://github.com/berkerpeksag/cpython-emailer-webhook/blob/e164cb9a6735d56012a4e557fd67dd7715c16d7b/mailer.py#L15", ), }, - ) + ) \ No newline at end of file From 77a870c3db60ede10e24b8581cc0c933ab4a45e0 Mon Sep 17 00:00:00 2001 From: Sarah Abderemane Date: Mon, 3 Oct 2022 23:42:45 +0200 Subject: [PATCH 12/12] fix formatting issue --- bedevere/backport.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/bedevere/backport.py b/bedevere/backport.py index 49cf1919..5482be38 100644 --- a/bedevere/backport.py +++ b/bedevere/backport.py @@ -20,13 +20,14 @@ MESSAGE_TEMPLATE = ('[GH-{pr}](https://github.com/python/cpython/pull/{pr}) is ' 'a backport of this pull request to the ' '[{branch} branch](https://github.com/python/cpython/tree/{branch}).') - BACKPORT_TITLE_DEVGUIDE_URL = "https://devguide.python.org/core-developers/committing/#backport-pr-title" + async def issue_for_PR(gh, pull_request): """Get the issue data for a pull request.""" return await gh.getitem(pull_request["issue_url"]) + async def _copy_over_labels(gh, original_issue, backport_issue): """Copy over relevant labels from the original PR to the backport PR.""" label_prefixes = "skip", "type", "sprint" @@ -36,7 +37,6 @@ async def _copy_over_labels(gh, original_issue, backport_issue): await gh.post(backport_issue["labels_url"], data=labels) - async def _remove_backport_label(gh, original_issue, branch, backport_pr_number): """Remove the appropriate "backport to" label on the original PR. @@ -47,8 +47,8 @@ async def _remove_backport_label(gh, original_issue, branch, backport_pr_number) return await gh.delete(original_issue['labels_url'], {'name': backport_label}) message = MESSAGE_TEMPLATE.format(branch=branch, pr=backport_pr_number) - response = await gh.post(original_issue['comments_url'], data={'body': message}) - return response + await gh.post(original_issue['comments_url'], data={'body': message}) + async def manage_labels(gh, *args, **kwargs): with open(os.environ["GITHUB_EVENT_PATH"]) as f: @@ -57,21 +57,22 @@ async def manage_labels(gh, *args, **kwargs): return pull_request = event["pull_request"] title = util.normalize_title(pull_request['title'], - pull_request['body']) + pull_request['body']) title_match = TITLE_RE.match(title) if title_match is None: return branch = title_match.group('branch') original_pr_number = title_match.group('pr') - + original_issue = await gh.getitem(event['repository']['issues_url'], - {'number': original_pr_number}) + {'number': original_pr_number}) await _remove_backport_label(gh, original_issue, branch, - event["number"]) + event["number"]) backport_issue = await issue_for_PR(gh, pull_request) await _copy_over_labels(gh, original_issue, backport_issue) + def is_maintenance_branch(ref): """ Return True if the ref refers to a maintenance branch. @@ -86,6 +87,7 @@ def is_maintenance_branch(ref): maintenance_branch_pattern = r'\d+\.\d+' return bool(re.fullmatch(maintenance_branch_pattern, ref)) + async def validate_maintenance_branch_pr(gh, *args, **kwargs): """Check the PR title for maintenance branch pull requests. @@ -105,18 +107,19 @@ async def validate_maintenance_branch_pr(gh, *args, **kwargs): return title = util.normalize_title(pull_request["title"], - pull_request["body"]) + pull_request["body"]) title_match = MAINTENANCE_BRANCH_TITLE_RE.match(title) if title_match is None: status = create_status(util.StatusState.FAILURE, - description="Not a valid maintenance branch PR title.", - target_url=BACKPORT_TITLE_DEVGUIDE_URL) + description="Not a valid maintenance branch PR title.", + target_url=BACKPORT_TITLE_DEVGUIDE_URL) else: status = create_status(util.StatusState.SUCCESS, - description="Valid maintenance branch PR title.") + description="Valid maintenance branch PR title.") await util.post_status(gh, event, status) + async def maintenance_branch_created(gh, *args, **kwargs): """Create the `needs backport label` when the maintenance branch is created. @@ -150,6 +153,7 @@ async def maintenance_branch_created(gh, *args, **kwargs): }, ) + async def main(): try: async with aiohttp.ClientSession() as session: