From e5f449da12a4ce2b40e5f467d5c67e90f4a49be9 Mon Sep 17 00:00:00 2001 From: aivong-openhands Date: Fri, 12 Jun 2026 10:30:37 -0500 Subject: [PATCH 1/6] fix: skip PR description validation for Dependabot PRs Dependabot-created PRs don't follow the custom PR template format (HUMAN:, AGENT:, etc.) and shouldn't be required to. This change adds a check to skip validation entirely for PRs created by the dependabot[bot] user. Fixes the PR description validation check failing on dependency update PRs. --- .github/scripts/check_pr_description.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/scripts/check_pr_description.py b/.github/scripts/check_pr_description.py index 39d362d7cb..589d9f897e 100644 --- a/.github/scripts/check_pr_description.py +++ b/.github/scripts/check_pr_description.py @@ -118,6 +118,20 @@ def body_from_event(event_path: Path) -> str: return body if isinstance(body, str) else "" +def is_dependabot_pr(event_path: Path) -> bool: + """Check if the PR is created by Dependabot.""" + try: + payload = json.loads(event_path.read_text()) + pull_request = payload.get("pull_request") + if not isinstance(pull_request, dict): + return False + user = pull_request.get("user", {}) + login = user.get("login", "") + return login == "dependabot[bot]" + except (json.JSONDecodeError, OSError): + return False + + def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description=( @@ -141,6 +155,12 @@ def parse_args() -> argparse.Namespace: def main() -> int: args = parse_args() + + # Skip validation for Dependabot PRs + if args.event_path is not None and is_dependabot_pr(args.event_path): + print("Skipping PR description validation for Dependabot PR.") + return 0 + if args.body_file is not None: body = args.body_file.read_text() elif args.event_path is not None: From d33f2e24289643f62318335d5c123f0354982865 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 12 Jun 2026 15:53:24 +0000 Subject: [PATCH 2/6] test: add tests for is_dependabot_pr function Co-authored-by: openhands --- tests/cross/test_check_pr_description.py | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/cross/test_check_pr_description.py b/tests/cross/test_check_pr_description.py index 863d607965..c910c65225 100644 --- a/tests/cross/test_check_pr_description.py +++ b/tests/cross/test_check_pr_description.py @@ -21,6 +21,13 @@ def _load_prod_module(): _prod = _load_prod_module() validate_pr_body = _prod.validate_pr_body body_from_event = _prod.body_from_event +is_dependabot_pr = _prod.is_dependabot_pr + + +DEPENDBOT_BODY = """## Bump some-dependency from 1.0.0 to 2.0.0 + +Bumps [some-dependency](https://github.com/example/some-dependency) from 1.0.0 to 2.0.0. +""" VALID_BODY = """ @@ -121,3 +128,39 @@ def test_body_from_event_reads_pull_request_body(tmp_path: Path): event_path.write_text(json.dumps({"pull_request": {"body": VALID_BODY}})) assert body_from_event(event_path) == VALID_BODY + + +def test_is_dependabot_pr_returns_true_for_dependabot_pr(tmp_path: Path): + event_path = tmp_path / "event.json" + event_path.write_text( + json.dumps( + {"pull_request": {"body": DEPENDBOT_BODY, "user": {"login": "dependabot[bot]"}}} + ) + ) + + assert is_dependabot_pr(event_path) is True + + +def test_is_dependabot_pr_returns_false_for_regular_pr(tmp_path: Path): + event_path = tmp_path / "event.json" + event_path.write_text( + json.dumps( + {"pull_request": {"body": VALID_BODY, "user": {"login": "octocat"}}} + ) + ) + + assert is_dependabot_pr(event_path) is False + + +def test_is_dependabot_pr_returns_false_for_missing_user(tmp_path: Path): + event_path = tmp_path / "event.json" + event_path.write_text(json.dumps({"pull_request": {"body": DEPENDBOT_BODY}})) + + assert is_dependabot_pr(event_path) is False + + +def test_is_dependabot_pr_returns_false_for_invalid_json(tmp_path: Path): + event_path = tmp_path / "event.json" + event_path.write_text("not valid json") + + assert is_dependabot_pr(event_path) is False From 94a0609a5c6e45f5ecb8ad40561bb24248a8a7db Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 12 Jun 2026 15:56:15 +0000 Subject: [PATCH 3/6] style: format test file with ruff Co-authored-by: openhands --- tests/cross/test_check_pr_description.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/cross/test_check_pr_description.py b/tests/cross/test_check_pr_description.py index c910c65225..01786dd91e 100644 --- a/tests/cross/test_check_pr_description.py +++ b/tests/cross/test_check_pr_description.py @@ -134,7 +134,12 @@ def test_is_dependabot_pr_returns_true_for_dependabot_pr(tmp_path: Path): event_path = tmp_path / "event.json" event_path.write_text( json.dumps( - {"pull_request": {"body": DEPENDBOT_BODY, "user": {"login": "dependabot[bot]"}}} + { + "pull_request": { + "body": DEPENDBOT_BODY, + "user": {"login": "dependabot[bot]"}, + } + } ) ) @@ -144,9 +149,7 @@ def test_is_dependabot_pr_returns_true_for_dependabot_pr(tmp_path: Path): def test_is_dependabot_pr_returns_false_for_regular_pr(tmp_path: Path): event_path = tmp_path / "event.json" event_path.write_text( - json.dumps( - {"pull_request": {"body": VALID_BODY, "user": {"login": "octocat"}}} - ) + json.dumps({"pull_request": {"body": VALID_BODY, "user": {"login": "octocat"}}}) ) assert is_dependabot_pr(event_path) is False From d9339aa1011a4b445f40a26cdd359288b1288ed0 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 12 Jun 2026 16:03:13 +0000 Subject: [PATCH 4/6] style: format check_pr_description.py with ruff Co-authored-by: openhands --- .github/scripts/check_pr_description.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/check_pr_description.py b/.github/scripts/check_pr_description.py index 589d9f897e..3a763fe343 100644 --- a/.github/scripts/check_pr_description.py +++ b/.github/scripts/check_pr_description.py @@ -155,12 +155,12 @@ def parse_args() -> argparse.Namespace: def main() -> int: args = parse_args() - + # Skip validation for Dependabot PRs if args.event_path is not None and is_dependabot_pr(args.event_path): print("Skipping PR description validation for Dependabot PR.") return 0 - + if args.body_file is not None: body = args.body_file.read_text() elif args.event_path is not None: From b36939fee40cffe44f0e031278c3599f75845f84 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 15 Jun 2026 02:28:28 +0000 Subject: [PATCH 5/6] fix: skip PR description validation for Dependabot PRs via workflow condition Simplify the Dependabot check by moving it to the GitHub Actions workflow condition instead of checking it in the Python script. This is more efficient since the job won't even start for Dependabot PRs. Co-authored-by: openhands --- .github/scripts/check_pr_description.py | 19 --------- .github/workflows/pr-description-check.yml | 1 + tests/cross/test_check_pr_description.py | 46 ---------------------- 3 files changed, 1 insertion(+), 65 deletions(-) diff --git a/.github/scripts/check_pr_description.py b/.github/scripts/check_pr_description.py index 3a763fe343..d41796c7aa 100644 --- a/.github/scripts/check_pr_description.py +++ b/.github/scripts/check_pr_description.py @@ -118,20 +118,6 @@ def body_from_event(event_path: Path) -> str: return body if isinstance(body, str) else "" -def is_dependabot_pr(event_path: Path) -> bool: - """Check if the PR is created by Dependabot.""" - try: - payload = json.loads(event_path.read_text()) - pull_request = payload.get("pull_request") - if not isinstance(pull_request, dict): - return False - user = pull_request.get("user", {}) - login = user.get("login", "") - return login == "dependabot[bot]" - except (json.JSONDecodeError, OSError): - return False - - def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description=( @@ -156,11 +142,6 @@ def parse_args() -> argparse.Namespace: def main() -> int: args = parse_args() - # Skip validation for Dependabot PRs - if args.event_path is not None and is_dependabot_pr(args.event_path): - print("Skipping PR description validation for Dependabot PR.") - return 0 - if args.body_file is not None: body = args.body_file.read_text() elif args.event_path is not None: diff --git a/.github/workflows/pr-description-check.yml b/.github/workflows/pr-description-check.yml index 680b5a9abb..cda023f58f 100644 --- a/.github/workflows/pr-description-check.yml +++ b/.github/workflows/pr-description-check.yml @@ -20,6 +20,7 @@ jobs: # the standard human-authored PR template. if: >- github.event.pull_request.draft == false && + github.event.pull_request.user.login != 'dependabot[bot]' && !(github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.event.pull_request.head.ref, 'rel-')) runs-on: ubuntu-24.04 diff --git a/tests/cross/test_check_pr_description.py b/tests/cross/test_check_pr_description.py index 01786dd91e..863d607965 100644 --- a/tests/cross/test_check_pr_description.py +++ b/tests/cross/test_check_pr_description.py @@ -21,13 +21,6 @@ def _load_prod_module(): _prod = _load_prod_module() validate_pr_body = _prod.validate_pr_body body_from_event = _prod.body_from_event -is_dependabot_pr = _prod.is_dependabot_pr - - -DEPENDBOT_BODY = """## Bump some-dependency from 1.0.0 to 2.0.0 - -Bumps [some-dependency](https://github.com/example/some-dependency) from 1.0.0 to 2.0.0. -""" VALID_BODY = """ @@ -128,42 +121,3 @@ def test_body_from_event_reads_pull_request_body(tmp_path: Path): event_path.write_text(json.dumps({"pull_request": {"body": VALID_BODY}})) assert body_from_event(event_path) == VALID_BODY - - -def test_is_dependabot_pr_returns_true_for_dependabot_pr(tmp_path: Path): - event_path = tmp_path / "event.json" - event_path.write_text( - json.dumps( - { - "pull_request": { - "body": DEPENDBOT_BODY, - "user": {"login": "dependabot[bot]"}, - } - } - ) - ) - - assert is_dependabot_pr(event_path) is True - - -def test_is_dependabot_pr_returns_false_for_regular_pr(tmp_path: Path): - event_path = tmp_path / "event.json" - event_path.write_text( - json.dumps({"pull_request": {"body": VALID_BODY, "user": {"login": "octocat"}}}) - ) - - assert is_dependabot_pr(event_path) is False - - -def test_is_dependabot_pr_returns_false_for_missing_user(tmp_path: Path): - event_path = tmp_path / "event.json" - event_path.write_text(json.dumps({"pull_request": {"body": DEPENDBOT_BODY}})) - - assert is_dependabot_pr(event_path) is False - - -def test_is_dependabot_pr_returns_false_for_invalid_json(tmp_path: Path): - event_path = tmp_path / "event.json" - event_path.write_text("not valid json") - - assert is_dependabot_pr(event_path) is False From 4fd36a416465cfaa60151e1e054f9ac48b719b4a Mon Sep 17 00:00:00 2001 From: aivong-openhands Date: Sun, 14 Jun 2026 21:29:55 -0500 Subject: [PATCH 6/6] Apply suggestion from @aivong-openhands --- .github/scripts/check_pr_description.py | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/scripts/check_pr_description.py b/.github/scripts/check_pr_description.py index d41796c7aa..39d362d7cb 100644 --- a/.github/scripts/check_pr_description.py +++ b/.github/scripts/check_pr_description.py @@ -141,7 +141,6 @@ def parse_args() -> argparse.Namespace: def main() -> int: args = parse_args() - if args.body_file is not None: body = args.body_file.read_text() elif args.event_path is not None: