From 87e5b8f77d4d80ad4a89d9ec7daac7d86cc30459 Mon Sep 17 00:00:00 2001 From: Szehon Ho Date: Tue, 25 Aug 2026 18:14:50 -0700 Subject: [PATCH] [SPARK-59017][INFRA] Merge script skips or hangs at the Fix Version prompt when no version can be inferred When no Fix Version can be inferred for a merge, the script either claims the JIRA is already up to date or traps the committer in the prompt loop. If the linked JIRA is already resolved, an empty inferred list was indistinguishable from "the issue already has every inferred version", so the script printed "already contains all inferred fix versions; no update needed" and returned without prompting. If the JIRA was not yet resolved, the committer reached the prompt with an empty default, where blank input became [""] and could never match a known version, so the prompt repeated indefinitely despite offering "leave blank and fix manually". --- dev/merge_spark_pr.py | 78 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/dev/merge_spark_pr.py b/dev/merge_spark_pr.py index 366181a4b380e..08441aa8cc08b 100755 --- a/dev/merge_spark_pr.py +++ b/dev/merge_spark_pr.py @@ -350,6 +350,49 @@ def additional_fix_versions(inferred_versions, existing_versions): return [version for version in inferred_versions if version not in existing] +def fix_version_additions(inferred_versions, existing_versions): + """Return (additions, all_inferred_present). + + An empty ``additions`` list is ambiguous on its own: either the issue already carries + every inferred version, or nothing was inferred. Only the first means there is nothing + to do; the second still needs the committer prompted. + + >>> fix_version_additions(["4.3.0"], ["5.0.0"]) + (['4.3.0'], False) + >>> fix_version_additions(["5.0.0"], ["5.0.0"]) + ([], True) + >>> fix_version_additions([], ["5.0.0"]) + ([], False) + """ + additions = additional_fix_versions(inferred_versions, existing_versions) + return additions, bool(inferred_versions) and not additions + + +def fix_versions_from_input(raw_input, default_fix_versions): + """Resolve the Fix Version prompt's raw input into a list of version names. + + Blank falls back to the inferred default. With no default to fall back on, blank means + skip: the empty list, not [""], which no known version can match. + + >>> fix_versions_from_input("4.2.2", "") + ['4.2.2'] + >>> fix_versions_from_input("", "5.0.0") + ['5.0.0'] + >>> fix_versions_from_input("5.0.0, 4.3.0", "") + ['5.0.0', '4.3.0'] + >>> fix_versions_from_input("", "") + [] + >>> fix_versions_from_input(" ", "") + [] + """ + if raw_input == "": + raw_input = default_fix_versions + stripped = raw_input.replace(" ", "") + if stripped == "": + return [] + return stripped.split(",") + + def red(text): return "\033[91m%s\033[0m" % text @@ -1252,19 +1295,28 @@ def resolve_jira_issue( if is_resolved: # A later backport run must preserve the versions recorded by the original merge and # only add versions inferred from the newly discovered branches. - default_fix_list = additional_fix_versions(default_fix_list, existing_fix_version_names) - if not default_fix_list: + default_fix_list, all_inferred_present = fix_version_additions( + default_fix_list, existing_fix_version_names + ) + if all_inferred_present: print( "JIRA issue %s already contains all inferred fix versions; no update needed." % issue.key ) return - print( - "JIRA issue %s has fix version(s) %s; inferred addition(s): %s" - % (issue.key, existing_fix_version_names, default_fix_list) - ) - if get_input("Add these fix version(s)? (y/N): ", ["y", "n", ""]) != "y": - return + if default_fix_list: + print( + "JIRA issue %s has fix version(s) %s; inferred addition(s): %s" + % (issue.key, existing_fix_version_names, default_fix_list) + ) + if get_input("Add these fix version(s)? (y/N): ", ["y", "n", ""]) != "y": + return + else: + # Nothing inferred, so there is nothing to confirm; fall through to the prompt. + print( + "JIRA issue %s has fix version(s) %s; no additional fix version could be " + "inferred." % (issue.key, existing_fix_version_names) + ) default_fix_versions = ",".join(default_fix_list) available_versions = set(list(map(lambda v: v.name, versions))) @@ -1273,10 +1325,12 @@ def resolve_jira_issue( prompt = "Enter comma-separated fix version(s) [%s]: " if is_resolved: prompt = "Enter comma-separated additional fix version(s) [%s]: " - fix_versions = bold_input(prompt % default_fix_versions) - if fix_versions == "": - fix_versions = default_fix_versions - fix_versions = fix_versions.replace(" ", "").split(",") + fix_versions = fix_versions_from_input( + bold_input(prompt % default_fix_versions), default_fix_versions + ) + if not fix_versions: + print("No fix version entered; update %s manually." % issue.key) + return if set(fix_versions).issubset(available_versions): break else: