Skip to content

Commit

Permalink
recreate-dependabot-pr: re-create conflicting dependabot pr's
Browse files Browse the repository at this point in the history
This script should be run in a GitHub push workflow on main where it
would iterate over all open conflicting dependabot pull requests and
recreate the most recent conflicting one by commenting to let
dependabot recreate the pull request. Additional the `no-test` label
ensures we don't run unneeded tests as re-creating still requires us to
update the `node_modules` git submodule.

The script has to retry the pull request detail endpoint as the
`mergeable` state of a pull request can be `null` indicating GitHub is
still determining if the pull request can be merged.
  • Loading branch information
jelly committed Oct 18, 2024
1 parent 14af78e commit a707ded
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ select = [
"YTT", # flake8-2020
]

[tool.ruff.lint.flake8-builtins]
# for lib.aio.abc
# https://github.com/astral-sh/ruff/issues/12949
builtins-allowed-modules = ["abc"]

[tool.pytest.ini_options]
addopts = ["--cov-config=pyproject.toml"] # for subprocesses
pythonpath = ["."]
Expand Down
84 changes: 84 additions & 0 deletions recreate-dependabot-pr
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/python3

# This file is part of Cockpit.
#
# Copyright (C) 2024 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

# Update COCKPIT_REPO_COMMIT to cockpit HEAD automatically, defaults to
# Makefile as input optionally the full path can be provided. (For example
# Anaconda uses ui/webui/Makefile.am).

import argparse
import sys
import time
from typing import Optional

import task

Check notice

Code scanning / CodeQL

Module is imported with 'import' and 'import from' Note

Module 'task' is imported with both 'import' and 'import from'.
from lib.aio.jsonutil import get_dict, get_int, get_str
from task import github

sys.dont_write_bytecode = True


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('--repo', default='cockpit-project/cockpit',
help="The GitHub repository to re-create dependabot pull requests for")
args = parser.parse_args()

api = github.GitHub(repo=args.repo)
for pull in api.pulls(state="open"):
number = get_int(pull, 'number')
if number is None:
continue

user = get_dict(pull, 'user')
if get_str(user, 'login') != 'dependabot[bot]':
continue

pull_details = api.get(f'pulls/{number}')

# Ignore dependabot PR's with multiple commits, they might be work in progress.
if get_int(pull_details, 'commits') > 1:
print(f'Skipping pull {number}, it is being worked on')
continue

mergeable: Optional[bool] = pull_details['mergeable']
# State is unknown, retry with a timeout
if mergeable is None:
for retry in range(3):
pull_details = api.get(f'pulls/{number}')
mergeable = pull_details['mergeable']
if mergeable is not None:
break

print(f'Retrying to obtain mergeable status for pull={number}, retry={retry}')
time.sleep(5)

if mergeable:
print(f'Skipping pull {number}, it is in a mergeable state')
continue
else:
# Not mergeable and a dependabot PR, add a `no-test` label and re-create the PR.
task.label(pull_details, ['no-test', 'dependabot-recreated']) # type: ignore[no-untyped-call]
task.comment(pull_details, '@dependabot recreate') # type: ignore[no-untyped-call]

# Stop at the first dependabot PR as we can only land one at a time.
break


if __name__ == '__main__':
main()

0 comments on commit a707ded

Please sign in to comment.