diff --git a/python/lean_pool/pr_advisory.py b/python/lean_pool/pr_advisory.py index ada47323c8..0baaa01888 100644 --- a/python/lean_pool/pr_advisory.py +++ b/python/lean_pool/pr_advisory.py @@ -98,13 +98,29 @@ def run_gh(*args: str, stdin: str | None = None) -> str: Returns: The command's stdout. """ + command = ["gh", *args] result = subprocess.run( - ["gh", *args], - check=True, + command, capture_output=True, text=True, input=stdin, ) + if result.returncode != 0: + print( + f"GitHub CLI command failed with exit code {result.returncode}: " + f"{' '.join(command)}", + file=sys.stderr, + ) + if result.stdout: + print(result.stdout, file=sys.stderr) + if result.stderr: + print(result.stderr, file=sys.stderr) + raise subprocess.CalledProcessError( + result.returncode, + command, + output=result.stdout, + stderr=result.stderr, + ) return result.stdout @@ -366,12 +382,22 @@ def main(argv: list[str] | None = None) -> int: metadata_cache: dict[str, UpstreamMetadata] = {} for pr_number in pr_numbers: - action = update_pr_advisory( - repo_full_name, - pr_number, - metadata_cache=metadata_cache, - dry_run=args.dry_run, - ) + try: + action = update_pr_advisory( + repo_full_name, + pr_number, + metadata_cache=metadata_cache, + dry_run=args.dry_run, + ) + except subprocess.CalledProcessError as error: + print( + f"PR #{pr_number}: advisory comment failed; " + "continuing because this workflow is non-blocking.", + file=sys.stderr, + ) + if error.stderr: + print(error.stderr, file=sys.stderr) + action = "comment-failed" print(f"PR #{pr_number}: {action}") return 0 diff --git a/python/tests/test_pr_advisory.py b/python/tests/test_pr_advisory.py index 3014595a33..600cd1ed3f 100644 --- a/python/tests/test_pr_advisory.py +++ b/python/tests/test_pr_advisory.py @@ -16,6 +16,7 @@ UpstreamMetadata, fetch_upstream_metadata, list_open_pr_numbers, + main, post_sticky_comment, project_changes, render_comment, @@ -224,3 +225,35 @@ def fake_post(*args: object, **kwargs: object) -> str: assert action == "skipped" assert not posted + + +def test_main_treats_comment_post_failure_as_nonblocking( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """GitHub comment-write failures should not fail the advisory workflow.""" + + def fake_update( + repo_full_name: str, + pr_number: int, + *, + metadata_cache: dict[str, UpstreamMetadata] | None = None, + dry_run: bool = False, + ) -> str: + assert repo_full_name == "acme/pool" + assert pr_number == 7 + assert not dry_run + raise subprocess.CalledProcessError( + 1, + ["gh", "api"], + stderr="Resource not accessible by integration\n", + ) + + monkeypatch.setattr(pr_advisory, "update_pr_advisory", fake_update) + + assert main(["--repo", "acme/pool", "--pr-number", "7"]) == 0 + + captured = capsys.readouterr() + assert "PR #7: comment-failed" in captured.out + assert "continuing because this workflow is non-blocking" in captured.err + assert "Resource not accessible by integration" in captured.err