From 413a95d16c297bef4f5cea2fe82422c88bcc8ac9 Mon Sep 17 00:00:00 2001 From: Shahar Epstein <60007259+shahar1@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:19:54 +0300 Subject: [PATCH] Handle multiple PR references in provider changelog entries Provider release issue generation was missing changelog bullets that reference more than one PR, which hid substantial changes from contributor testing checklists. This keeps release wave issue content aligned with changelog intent by extracting every PR reference from each bullet and from excluded changelog sections. --- .../commands/release_management_commands.py | 20 +++--- .../tests/test_release_management_commands.py | 69 +++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py index abe7d9248725c..c308278d2d3b2 100644 --- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py +++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py @@ -255,6 +255,7 @@ # always correct regardless of how breeze is launched. SOURCE_DIR_PATH = str(AIRFLOW_ROOT_PATH) PR_PATTERN = re.compile(r".*\(#([0-9]+)\)") +PR_REFERENCE_PATTERN = re.compile(r"#([0-9]+)") ISSUE_MATCH_IN_BODY = re.compile(r" #([0-9]+)[^0-9]") # Release-management commits (provider documentation / release preparation) are pure # release-process noise: they are not user-facing changes and existing providers already @@ -2611,8 +2612,8 @@ def get_suffix_from_package_in_dist(dist_files: list[str], package: str) -> str def get_prs_for_package(provider_id: str, current_release_version: str | None = None) -> list[int]: - pr_matcher = re.compile(r".*\(#([0-9]*)\)``$") - prs = [] + prs: list[int] = [] + seen_prs: set[int] = set() if current_release_version is None: provider_yaml_dict = get_provider_distributions_metadata().get(provider_id) if not provider_yaml_dict: @@ -2636,9 +2637,12 @@ def get_prs_for_package(provider_id: str, current_release_version: str | None = if line.startswith(".. Below changes are excluded from the changelog"): # The reminder of PRs is not important skipping it break - match_result = pr_matcher.match(line.strip()) - if match_result: - prs.append(int(match_result.group(1))) + if line.lstrip().startswith("*"): + for pr_match in PR_REFERENCE_PATTERN.findall(line): + pr_number = int(pr_match) + if pr_number not in seen_prs: + seen_prs.add(pr_number) + prs.append(pr_number) return prs @@ -2732,7 +2736,6 @@ def get_commented_out_prs_from_provider_changelogs() -> list[int]: Returns list of PRs that are commented out in the changelog. :return: list of PR numbers that appear only in comments in changelog.rst files in "providers" dir """ - pr_matcher = re.compile(r".*\(#([0-9]+)\).*") commented_prs = set() # Get all provider distributions @@ -2770,9 +2773,8 @@ def get_commented_out_prs_from_provider_changelogs() -> list[int]: # Extract PRs from excluded sections if in_excluded_section and line.strip().startswith("*"): - match_result = pr_matcher.search(line) - if match_result: - commented_prs.add(int(match_result.group(1))) + for pr_match in PR_REFERENCE_PATTERN.findall(line): + commented_prs.add(int(pr_match)) return sorted(commented_prs) diff --git a/dev/breeze/tests/test_release_management_commands.py b/dev/breeze/tests/test_release_management_commands.py index 3e735222c22e3..6c16bfa1c83c5 100644 --- a/dev/breeze/tests/test_release_management_commands.py +++ b/dev/breeze/tests/test_release_management_commands.py @@ -26,7 +26,9 @@ _ensure_default_python_for_reproducible_client, _is_initial_provider_release, _should_include_provider_in_issue, + get_commented_out_prs_from_provider_changelogs, get_package_version_possibly_from_stable_txt, + get_prs_for_package, get_prs_from_git_log_for_new_provider, get_suffix_from_package_in_dist, is_package_in_dist, @@ -101,6 +103,73 @@ def test_get_prs_from_git_log_for_new_provider(monkeypatch): assert prs == [68400, 68345, 67999, 67080] +def test_get_prs_for_package_extracts_multiple_prs_per_line(tmp_path: Path, monkeypatch): + changelog = tmp_path / "changelog.rst" + changelog.write_text( + "\n".join( + [ + "1.15.0", + "......", + "", + "Features", + "~~~~~~~~", + "", + "* ``Add Kafka Event Producer publishing DagRun and TaskInstance state-change events (#68082, #70014)``", + "* ``Add Amazon MSK IAM (OAUTHBEARER) support to Apache Kafka provider (#69427)``", + "", + ".. Below changes are excluded from the changelog. Move them to", + " appropriate section above if needed. Do not delete the lines(!):", + " * ``Release prep commit (#99999)``", + "", + "1.14.0", + "......", + ] + ) + ) + monkeypatch.setattr( + "airflow_breeze.commands.release_management_commands.get_provider_details", + lambda provider_id: SimpleNamespace(changelog_path=changelog), + ) + + prs = get_prs_for_package("apache.kafka", current_release_version="1.15.0") + + assert prs == [68082, 70014, 69427] + + +def test_get_commented_out_prs_extracts_multiple_prs_per_line(tmp_path: Path, monkeypatch): + changelog = tmp_path / "changelog.rst" + changelog.write_text( + "\n".join( + [ + "1.15.0", + "......", + "", + "Features", + "~~~~~~~~", + "", + "* ``Visible change (#11111)``", + "", + ".. Below changes are excluded from the changelog. Move them to", + " appropriate section above if needed. Do not delete the lines(!):", + " * ``Internal change (#22222, #33333)``", + "", + ] + ) + ) + monkeypatch.setattr( + "airflow_breeze.commands.release_management_commands.get_provider_distributions_metadata", + lambda: {"apache.kafka": {}}, + ) + monkeypatch.setattr( + "airflow_breeze.commands.release_management_commands.get_provider_details", + lambda provider_id: SimpleNamespace(changelog_path=changelog), + ) + + prs = get_commented_out_prs_from_provider_changelogs() + + assert prs == [22222, 33333] + + @pytest.mark.parametrize( ("subject", "is_release_commit"), [