From 4115da76b62826095c70fb2490dee46508b56829 Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:33:18 -0400 Subject: [PATCH 1/3] Use REST API to update flaky-fix backport PRs. `gh pr edit` fails under GITHUB_TOKEN because it queries GraphQL projectCards (Projects classic). https://github.com/Altinity/ClickHouse/actions/runs/31201153430/job/92942940316 Co-authored-by: Cursor --- tests/ci/backport_flaky_fixes.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/tests/ci/backport_flaky_fixes.py b/tests/ci/backport_flaky_fixes.py index 3b16bc3eb826..a0ec7875a4a3 100755 --- a/tests/ci/backport_flaky_fixes.py +++ b/tests/ci/backport_flaky_fixes.py @@ -181,20 +181,30 @@ def update_pr(repo: str, number: int, title: str, body: str, labels: list, dry_r labels = existing_labels(repo, labels) - cmd = [ - "gh", "pr", "edit", str(number), - "--repo", repo, - "--title", title, - "--body", body, - ] - for label in labels: - cmd += ["--add-label", label] - - result = subprocess.run(cmd, text=True, capture_output=False) + # Use REST instead of `gh pr edit`: the latter queries GraphQL projectCards + # (Projects classic), which fails under GITHUB_TOKEN even with PullRequests:write. + payload = json.dumps({"title": title, "body": body}) + result = subprocess.run( + ["gh", "api", "-X", "PATCH", f"repos/{repo}/pulls/{number}", "--input", "-"], + input=payload, + text=True, + capture_output=True, + ) if result.returncode != 0: - print("gh pr edit failed", file=sys.stderr) + print(f"gh api PATCH pulls/{number} failed:\n{result.stderr or result.stdout}", file=sys.stderr) sys.exit(1) + if labels: + label_payload = json.dumps({"labels": labels}) + result = subprocess.run( + ["gh", "api", "-X", "POST", f"repos/{repo}/issues/{number}/labels", "--input", "-"], + input=label_payload, + text=True, + capture_output=True, + ) + if result.returncode != 0: + print(f"Warning: could not add labels to PR #{number}:\n{result.stderr or result.stdout}", file=sys.stderr) + def create_pr(repo: str, branch: str, base: str, title: str, body: str, labels: list, dry_run: bool) -> None: if dry_run: From d88c54ca93753037b15ae797ac6b2d34cb50bce4 Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:56:28 -0400 Subject: [PATCH 2/3] Link flaky-fix backport PRs in the Actions step summary. Co-authored-by: Cursor --- tests/ci/backport_flaky_fixes.py | 37 ++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/ci/backport_flaky_fixes.py b/tests/ci/backport_flaky_fixes.py index a0ec7875a4a3..4b0e381a862f 100755 --- a/tests/ci/backport_flaky_fixes.py +++ b/tests/ci/backport_flaky_fixes.py @@ -7,6 +7,7 @@ import argparse import json +import os import re import subprocess import sys @@ -172,11 +173,28 @@ def pr_backport_commits(repo: str, number: int): return out +def write_step_summary(text: str) -> None: + """Append a line to GITHUB_STEP_SUMMARY when running in Actions.""" + path = os.environ.get("GITHUB_STEP_SUMMARY") + if not path: + return + with open(path, "a", encoding="utf-8") as f: + f.write(text) + if not text.endswith("\n"): + f.write("\n") + + +def pr_url(repo: str, number: int) -> str: + return f"https://github.com/{repo}/pull/{number}" + + def update_pr(repo: str, number: int, title: str, body: str, labels: list, dry_run: bool) -> None: + url = pr_url(repo, number) if dry_run: print(f"DRY RUN: would update PR #{number} in {repo}:", file=sys.stderr) print(f" title: {title}", file=sys.stderr) print(f" labels: {', '.join(labels)}", file=sys.stderr) + print(f" url: {url}", file=sys.stderr) return labels = existing_labels(repo, labels) @@ -205,6 +223,9 @@ def update_pr(repo: str, number: int, title: str, body: str, labels: list, dry_r if result.returncode != 0: print(f"Warning: could not add labels to PR #{number}:\n{result.stderr or result.stdout}", file=sys.stderr) + print(f"Updated PR: {url}", file=sys.stderr) + write_step_summary(f"- Updated [{title}]({url})") + def create_pr(repo: str, branch: str, base: str, title: str, body: str, labels: list, dry_run: bool) -> None: if dry_run: @@ -228,11 +249,20 @@ def create_pr(repo: str, branch: str, base: str, title: str, body: str, labels: for label in labels: cmd += ["--label", label] - result = subprocess.run(cmd, text=True, capture_output=False) + result = subprocess.run(cmd, text=True, capture_output=True) if result.returncode != 0: - print("gh pr create failed", file=sys.stderr) + print(f"gh pr create failed:\n{result.stderr or result.stdout}", file=sys.stderr) sys.exit(1) + url = (result.stdout or "").strip().splitlines()[-1] if result.stdout else "" + if url: + print(url) + print(f"Opened PR: {url}", file=sys.stderr) + write_step_summary(f"- Opened [{title}]({url})") + else: + print("Opened PR (no URL in gh output)", file=sys.stderr) + write_step_summary(f"- Opened PR: {title}") + def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( @@ -295,7 +325,10 @@ def main() -> None: prior_applied = [(sha, subject, None) for sha, subject in prior_commits] missing = [c for c in missing if c["sha"] not in prior_shas] if not missing: + url = pr_url(args.repo, reuse_number) print(f"PR #{reuse_number} already contains all missing commits. Nothing to do.", file=sys.stderr) + print(f"Existing PR: {url}", file=sys.stderr) + write_step_summary(f"- No new commits for `{base_branch}`; existing PR: [#{reuse_number}]({url})") return prefetch_upstream_objects([c["sha"] for c in missing]) From d130f0bc1fd979c969c2fa1a132567ed284dbaf2 Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:49:42 -0400 Subject: [PATCH 3/3] Mark flaky-fix backport PR titles as automated. Co-authored-by: Cursor --- tests/ci/backport_flaky_fixes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci/backport_flaky_fixes.py b/tests/ci/backport_flaky_fixes.py index 4b0e381a862f..ebec9a0d1fad 100755 --- a/tests/ci/backport_flaky_fixes.py +++ b/tests/ci/backport_flaky_fixes.py @@ -369,7 +369,7 @@ def main() -> None: sys.exit(0) all_applied = prior_applied + applied - pr_title = f"{base_branch.title()} - Backport flaky-fix commits from upstream ({date_tag})" + pr_title = f"{base_branch.title()} - Automated backport of flaky-fix commits from upstream ({date_tag})" pr_body = build_pr_body(upstream_repo, all_applied, conflicted) pr_labels = labels_for_branch(base_branch)