diff --git a/.github/workflows/flaky_fix_sync.yml b/.github/workflows/flaky_fix_sync.yml index 75cde5d3dcfb..e28702b7aa2d 100644 --- a/.github/workflows/flaky_fix_sync.yml +++ b/.github/workflows/flaky_fix_sync.yml @@ -35,6 +35,7 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 + filter: blob:none - name: Set up Python uses: actions/setup-python@v4 @@ -72,7 +73,7 @@ jobs: - name: Check out repository uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 - name: Set up Python uses: actions/setup-python@v4 diff --git a/tests/ci/backport_flaky_fixes.py b/tests/ci/backport_flaky_fixes.py index 3fd528494d2f..d93f6fce3b24 100755 --- a/tests/ci/backport_flaky_fixes.py +++ b/tests/ci/backport_flaky_fixes.py @@ -6,6 +6,7 @@ import argparse import json +import re import subprocess import sys from datetime import datetime, timezone @@ -14,6 +15,35 @@ UPSTREAM_GIT_URL = "https://github.com/{repo}.git" UPSTREAM_COMMIT_URL = "https://github.com/{repo}/commit/{sha}" +# Branches like antalya-26.3 or stable-25.8 → family label + version label. +_BRANCH_LABEL_RE = re.compile(r'^([a-z]+)-(\d+\.\d+)$') + + +def labels_for_branch(branch: str) -> list: + labels = ["cicd"] + m = _BRANCH_LABEL_RE.match(branch) + if m: + labels.append(m.group(1)) # e.g. "antalya" or "stable" + labels.append(branch) # e.g. "antalya-26.3" or "stable-25.8" + return labels + + +def existing_labels(repo: str, labels: list) -> list: + """Return the subset of labels that already exist in the repo.""" + result = subprocess.run( + ["gh", "label", "list", "--repo", repo, "--json", "name", "--limit", "1000"], + text=True, + capture_output=True, + ) + if result.returncode != 0: + print(f"Warning: could not fetch labels from {repo}, skipping labels", file=sys.stderr) + return [] + known = {entry["name"] for entry in json.loads(result.stdout)} + skipped = [l for l in labels if l not in known] + if skipped: + print(f"Skipping non-existent labels: {', '.join(skipped)}", file=sys.stderr) + return [l for l in labels if l in known] + def run_git(*args, check=True, capture=True) -> subprocess.CompletedProcess: result = subprocess.run( @@ -32,13 +62,18 @@ def git_out(*args) -> str: def fetch_commit(upstream_repo: str, sha: str) -> bool: - """Fetch a single commit object by SHA from the upstream repo. + """Fetch a commit and its parent from upstream so cherry-pick can compute the diff. Returns True on success, False on failure.""" url = UPSTREAM_GIT_URL.format(repo=upstream_repo) result = run_git("fetch", "--no-tags", "--no-recurse-submodules", url, sha, check=False) if result.returncode != 0: print(f"Failed to fetch {sha[:12]} from upstream:\n{result.stderr}", file=sys.stderr) return False + # cherry-pick needs the parent's tree objects to compute the diff; fetch it too. + parent_result = run_git("rev-parse", "FETCH_HEAD^", check=False) + if parent_result.returncode == 0: + parent_sha = parent_result.stdout.strip() + run_git("fetch", "--no-tags", "--no-recurse-submodules", "--depth=1", url, parent_sha, check=False) return True @@ -91,26 +126,29 @@ def build_pr_body( return "\n".join(lines) -def create_pr(repo: str, branch: str, base: str, title: str, body: str, dry_run: bool) -> None: +def create_pr(repo: str, branch: str, base: str, title: str, body: str, labels: list, dry_run: bool) -> None: if dry_run: print(f"DRY RUN: would open PR in {repo}:", file=sys.stderr) - print(f" title: {title}", file=sys.stderr) - print(f" base: {base}", file=sys.stderr) - print(f" head: {branch}", file=sys.stderr) + print(f" title: {title}", file=sys.stderr) + print(f" base: {base}", file=sys.stderr) + print(f" head: {branch}", file=sys.stderr) + print(f" labels: {', '.join(labels)}", file=sys.stderr) return - result = subprocess.run( - [ - "gh", "pr", "create", - "--repo", repo, - "--base", base, - "--head", branch, - "--title", title, - "--body", body, - ], - text=True, - capture_output=False, - ) + labels = existing_labels(repo, labels) + + cmd = [ + "gh", "pr", "create", + "--repo", repo, + "--base", base, + "--head", branch, + "--title", title, + "--body", body, + ] + for label in labels: + cmd += ["--label", label] + + result = subprocess.run(cmd, text=True, capture_output=False) if result.returncode != 0: print("gh pr create failed", file=sys.stderr) sys.exit(1) @@ -196,7 +234,7 @@ def main() -> None: pr_title = f"Backport flaky-fix commits from upstream ({date_tag})" pr_body = build_pr_body(upstream_repo, applied, conflicted, fetch_failed) - print(f"\n--- PR title ---\n{pr_title}\n--- PR body ---\n{pr_body}---") + pr_labels = labels_for_branch(base_branch) if args.dry_run: print( @@ -204,6 +242,8 @@ def main() -> None: file=sys.stderr, ) print(f" Applied: {len(applied)} Conflicted: {len(conflicted)} Fetch failed: {len(fetch_failed)}", file=sys.stderr) + print(f"\n--- PR title ---\n{pr_title}\n--- PR body ---\n{pr_body}---") + create_pr(repo=args.repo, branch=backport_branch, base=base_branch, title=pr_title, body=pr_body, labels=pr_labels, dry_run=True) run_git("checkout", base_branch) run_git("branch", "-D", backport_branch) return @@ -216,6 +256,7 @@ def main() -> None: base=base_branch, title=pr_title, body=pr_body, + labels=pr_labels, dry_run=False, )