From 88a7687ca7b9453bbee7fc30848b7aab5d114e88 Mon Sep 17 00:00:00 2001 From: "Jason(Zhe-You) Liu" <68415893+jason810496@users.noreply.github.com> Date: Mon, 16 Feb 2026 03:51:04 +0800 Subject: [PATCH 1/2] [v3-1-test] Add Milestone Tag Assistant (#61626) * Add Milestone Tag Assistant * Refactor the set-milestone utils to breeze ci_group * Improves milestone assignment and adds tests for PR tagging Refines the logic for auto-assigning GitHub milestones to merged PRs, prioritizing backport labels, version branches, and latest open milestones for main branch merges. Introduces user notification comments when no suitable milestone is found and prompts manual action. Adds comprehensive tests covering milestone detection, assignment, and notification flows to ensure robust behavior and edge case handling. * Refactor milestone comment generation and improve tests for mention handling * Add CLI testing fixtures and enhance milestone comment generation in tests * Fix test scenarios * Add check to skip setting milestone if already assigned in Breeze * Remove merging into main logic * Update Milestone Tag Assistant documentation to clarify triggering conditions and rules for milestone assignment * Fix mypy error (cherry picked from commit 6d54333537f6d1311b71740f975b6194a1890fa0) Co-authored-by: Jason(Zhe-You) Liu <68415893+jason810496@users.noreply.github.com> --- .github/workflows/milestone-tag-assistant.yml | 122 ++++ dev/README_AIRFLOW3_DEV.md | 4 +- dev/breeze/doc/08_ci_tasks.rst | 18 + dev/breeze/doc/images/output_ci.svg | 16 +- dev/breeze/doc/images/output_ci.txt | 2 +- .../doc/images/output_ci_set-milestone.svg | 146 +++++ .../doc/images/output_ci_set-milestone.txt | 1 + ...utput_setup_check-all-params-in-groups.svg | 86 ++- ...utput_setup_check-all-params-in-groups.txt | 2 +- ...output_setup_regenerate-command-images.svg | 78 +-- ...output_setup_regenerate-command-images.txt | 2 +- .../airflow_breeze/commands/ci_commands.py | 331 +++++++++- .../commands/ci_commands_config.py | 20 + .../src/airflow_breeze/global_constants.py | 6 + dev/breeze/tests/test_set_milestone.py | 569 ++++++++++++++++++ 15 files changed, 1309 insertions(+), 94 deletions(-) create mode 100644 .github/workflows/milestone-tag-assistant.yml create mode 100644 dev/breeze/doc/images/output_ci_set-milestone.svg create mode 100644 dev/breeze/doc/images/output_ci_set-milestone.txt create mode 100644 dev/breeze/tests/test_set_milestone.py diff --git a/.github/workflows/milestone-tag-assistant.yml b/.github/workflows/milestone-tag-assistant.yml new file mode 100644 index 0000000000000..18fd030e9b98a --- /dev/null +++ b/.github/workflows/milestone-tag-assistant.yml @@ -0,0 +1,122 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +--- +name: Milestone Tag Assistant +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - v3-1-test + +permissions: + # Those permissions are only active for workflow dispatch (only committers can trigger it) and workflow call + # Which is triggered automatically by "automatic-backport" push workflow (only when merging by committer) + # Branch protection prevents from pushing to the "code" branches + contents: write # zizmor: ignore[excessive-permissions] + pull-requests: write # zizmor: ignore[excessive-permissions] + +jobs: + get-pr-info: + name: "Get PR information" + runs-on: ubuntu-latest + outputs: + should-run: ${{ steps.pr-info.outputs.should-run }} + pr-number: ${{ steps.pr-info.outputs.pr-number }} + pr-title: ${{ steps.pr-info.outputs.pr-title }} + pr-labels: ${{ steps.pr-info.outputs.pr-labels }} + base-branch: ${{ steps.pr-info.outputs.base-branch }} + merged-by: ${{ steps.pr-info.outputs.merged-by }} + steps: + # Adding a slight delay to allow GitHub's API to associate the merge commit with the PR. + # This is needed because GH has a consistency of 6-10+ seconds + # to process the commit and PR association after a merge based on some of our past runs. + - name: Add delay for GitHub to process PR merge + run: sleep 15 + + - name: Find PR information + id: pr-info + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + script: | + const { data: pullRequests } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: process.env.GITHUB_SHA + }); + + if (pullRequests.length === 0) { + console.log('⚠️ No pull request found for this commit.'); + core.setOutput('should-run', 'false'); + return; + } + + const pr = pullRequests[0]; + + // Skip if PR already has a milestone + if (pr.milestone !== null) { + console.log(`PR #${pr.number} already has milestone: ${pr.milestone.title}`); + core.setOutput('should-run', 'false'); + return; + } + + const labels = pr.labels.map(label => label.name); + + console.log(`Commit ${process.env.GITHUB_SHA} is associated with PR #${pr.number}`); + console.log(`Title: ${pr.title}`); + console.log(`Labels: ${JSON.stringify(labels)}`); + console.log(`Base branch: ${pr.base.ref}`); + console.log(`Merged by: ${pr.merged_by?.login || 'unknown'}`); + + core.setOutput('should-run', 'true'); + core.setOutput('pr-number', pr.number.toString()); + core.setOutput('pr-title', pr.title); + core.setOutput('pr-labels', JSON.stringify(labels)); + core.setOutput('base-branch', pr.base.ref); + core.setOutput('merged-by', pr.merged_by?.login || 'unknown'); + + set-milestone: + name: "Set milestone on merged PR" + runs-on: ubuntu-latest + needs: get-pr-info + if: ${{ needs.get-pr-info.outputs.should-run == 'true' }} + + steps: + - name: "Checkout repository" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + # Always checkout main to ensure Breeze with set-milestone command is available + ref: main + + - name: "Install Breeze" + uses: ./.github/actions/breeze + id: breeze + + - name: Check criteria and set milestone + env: + GH_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} + PR_NUMBER: ${{ needs.get-pr-info.outputs.pr-number }} + PR_TITLE: ${{ needs.get-pr-info.outputs.pr-title }} + PR_LABELS: ${{ needs.get-pr-info.outputs.pr-labels }} + BASE_BRANCH: ${{ needs.get-pr-info.outputs.base-branch }} + MERGED_BY: ${{ needs.get-pr-info.outputs.merged-by }} + run: | + breeze ci set-milestone diff --git a/dev/README_AIRFLOW3_DEV.md b/dev/README_AIRFLOW3_DEV.md index dd7c5656792c6..c97431bf042e8 100644 --- a/dev/README_AIRFLOW3_DEV.md +++ b/dev/README_AIRFLOW3_DEV.md @@ -95,7 +95,7 @@ Do not treat PR approval (Green V) as exclusion approval. ## Merging PRs targeted for Airflow 3.X -The committer who merges the PR is responsible for backporting the PRs that are 3.1 bug fixes (generally speaking) +The committer who merges the PR **is responsible for backporting the PRs that are 3.1 bug fixes (generally speaking)** to `v3-1-test` (latest active branch we release bugfixes from). See next chapter to see what kind of changes we cherry-pick. It means that they should create a new PR where the original commit from main is cherry-picked and take care for resolving conflicts. @@ -105,7 +105,7 @@ Note: tracking that the PRs merged as expected is the responsibility of committe Committer may also request from PR author to raise 2 PRs one against `main` branch and one against `v3-1-test` prior to accepting the code change. Mistakes happen, and such backport PR work might fall through cracks. Therefore, if the committer thinks -that certain PRs should be backported, they should set 3.1.x milestone for them. +that certain PRs should be backported, they **should set 3.1.x milestone for them.** This way release manager can verify (as usual) if all the "expected" PRs have been backported and cherry-pick remaining PRS. diff --git a/dev/breeze/doc/08_ci_tasks.rst b/dev/breeze/doc/08_ci_tasks.rst index cf2e8ba2060a3..7165a59875671 100644 --- a/dev/breeze/doc/08_ci_tasks.rst +++ b/dev/breeze/doc/08_ci_tasks.rst @@ -132,6 +132,24 @@ These are all available flags of ``get-workflow-info`` command: :width: 100% :alt: Breeze ci get-workflow-info +Milestone Tag Assistant +----------------------- + +The bot will only be triggered when a new push event occurs on the ``main`` or ``v3-1-test`` branches. It will leave a comment in the corresponding PR (similar to how the current ``automatic-backport`` bot behaves) and set the milestone if it matches the rules below, and it will skip if the PRs that already have a milestone set. + +There are two cases for the current rules: +- **CI-related**: no need to set milestone +- **Patch release**: default to the **latest patch-release milestone** + - has a label like ``backport-to-v3-1-test`` + - is merged to ``v3-1-test`` version branch + +If it cannot determine which milestone should be added, it also adds a comment to remind the committer who merged the PR to add the corresponding milestone. This automation ensures that all PRs that should be included in the patch release are properly tagged, making the release manager's life easier. + +.. image:: ./images/output_ci_set-milestone.svg + :target: https://raw.githubusercontent.com/apache/airflow/main/dev/breeze/images/output_milestone-tag-assistant.svg + :width: 100% + :alt: Milestone Tag Assistant + ----- Next step: Follow the `Release management tasks <09_release_management_tasks.rst>`_ guide to learn how diff --git a/dev/breeze/doc/images/output_ci.svg b/dev/breeze/doc/images/output_ci.svg index fba2a6f87ff9e..393d111e6e5c8 100644 --- a/dev/breeze/doc/images/output_ci.svg +++ b/dev/breeze/doc/images/output_ci.svg @@ -1,4 +1,4 @@ - +