You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@burr.apache.org by "hussain-s (via GitHub)" <gi...@apache.org> on 2026/05/02 07:33:17 UTC

[PR] feat: add milestone management workflow [burr]

hussain-s opened a new pull request, #750:
URL: https://github.com/apache/burr/pull/750

   Add milestone management automation for release tracking.
   
   ## Changes
   
   - add `.github/workflows/milestone-management.yml` to auto-assign milestones on merged PRs
   - support merged PRs targeting `main` and release/test branch patterns
   - add `workflow_dispatch` support to create or re-open upcoming `X.Y.Z` milestones
   - document milestone naming and branch mapping in `scripts/README.md`
   - add `scripts/milestone_management_preview.py` for local milestone-selection checks
   - add unit coverage for the branch-to-milestone logic in `tests/test_milestone_management_preview.py`
   
   ## How I tested this
   
   - ran `.venv311/bin/python -m pytest tests/`
   - ran `PRE_COMMIT_HOME=.pre-commit-cache .venv311/bin/pre-commit run --all-files`
   - ran `.venv311/bin/python -m pytest tests/test_milestone_management_preview.py`
   - exercised `scripts/milestone_management_preview.py` manually for `main`, release branches, and unmanaged branches
   
   ## Notes
   
   - milestone titles follow the `X.Y.Z` convention requested in issue #722
   - merged PRs to `main` pick the earliest open `X.Y.Z` milestone
   - merged PRs to release/test branches use the milestone inferred from the branch name
   - milestone creation is manual through `workflow_dispatch`; merged-PR assignment is automatic once milestones exist
   
   ## Checklist
   
   - [x] PR has an informative and human-readable title (this will be pulled into the release notes)
   - [x] Changes are limited to a single goal (no scope creep)
   - [x] Code passed the pre-commit check & code is left cleaner/nicer than when first encountered.
   - [x] Any _change_ in functionality is tested
   - [x] New functions are documented (with a description, list of inputs, and expected output)
   - [x] Placeholder code is flagged / future TODOs are captured in comments
   - [x] Project documentation has been updated if adding/changing functionality.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "andreahlert (via GitHub)" <gi...@apache.org>.
andreahlert commented on code in PR #750:
URL: https://github.com/apache/burr/pull/750#discussion_r3176421929


##########
tests/test_milestone_management_preview.py:
##########
@@ -0,0 +1,64 @@
+# 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.
+
+import importlib.util
+from pathlib import Path
+
+import pytest
+
+
+def _load_preview_module():
+    module_path = (
+        Path(__file__).resolve().parent.parent / "scripts" / "milestone_management_preview.py"
+    )
+    spec = importlib.util.spec_from_file_location("milestone_management_preview", module_path)
+    module = importlib.util.module_from_spec(spec)
+    assert spec.loader is not None
+    spec.loader.exec_module(module)
+    return module
+
+
+preview = _load_preview_module()
+
+
+def test_main_branch_uses_earliest_open_milestone():
+    milestone = preview.choose_milestone("main", ["0.43.1", "0.43.0", "not-a-release"])
+
+    assert milestone == "0.43.0"
+
+
+def test_release_branch_maps_to_exact_version():
+    milestone = preview.choose_milestone("release-0.43.1", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.1"
+
+
+def test_minor_test_branch_maps_to_patch_zero_milestone():
+    milestone = preview.choose_milestone("v0.43-test", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.0"
+
+
+def test_unmanaged_branch_skips_assignment():
+    milestone = preview.choose_milestone("feature/example", ["0.43.0"])
+
+    assert milestone is None
+
+
+def test_missing_release_branch_milestone_raises_error():
+    with pytest.raises(ValueError, match="Expected an open milestone named '0.43.1'"):
+        preview.choose_milestone("release-0.43.1", ["0.43.0"])

Review Comment:
   the JS in the workflow has the same logic but no tests. preview script mirrors it today, will drift later. worth testing the JS too, or at least a comment in both files saying they must stay in sync.



##########
tests/test_milestone_management_preview.py:
##########
@@ -0,0 +1,64 @@
+# 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.
+
+import importlib.util
+from pathlib import Path
+
+import pytest
+
+
+def _load_preview_module():
+    module_path = (
+        Path(__file__).resolve().parent.parent / "scripts" / "milestone_management_preview.py"
+    )
+    spec = importlib.util.spec_from_file_location("milestone_management_preview", module_path)
+    module = importlib.util.module_from_spec(spec)
+    assert spec.loader is not None
+    spec.loader.exec_module(module)
+    return module
+
+
+preview = _load_preview_module()
+
+
+def test_main_branch_uses_earliest_open_milestone():
+    milestone = preview.choose_milestone("main", ["0.43.1", "0.43.0", "not-a-release"])
+
+    assert milestone == "0.43.0"
+
+
+def test_release_branch_maps_to_exact_version():
+    milestone = preview.choose_milestone("release-0.43.1", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.1"
+
+
+def test_minor_test_branch_maps_to_patch_zero_milestone():
+    milestone = preview.choose_milestone("v0.43-test", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.0"
+
+
+def test_unmanaged_branch_skips_assignment():
+    milestone = preview.choose_milestone("feature/example", ["0.43.0"])
+
+    assert milestone is None
+
+
+def test_missing_release_branch_milestone_raises_error():
+    with pytest.raises(ValueError, match="Expected an open milestone named '0.43.1'"):
+        preview.choose_milestone("release-0.43.1", ["0.43.0"])

Review Comment:
   missing coverage for `release/0.43.1` (slash variant), `apache-burr-0.43.1-release`, and the `release-0.43` minor fallback. regex handles them but nothing exercises that path.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #750:
URL: https://github.com/apache/burr/pull/750#issuecomment-4477255388

   This PR has been inactive for 16 days after receiving review feedback.
   Converting to draft.
   
   Please mark it as ready for review when you have addressed the comments.
   If no activity occurs within 90 days, it will be closed automatically.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "elijahbenizzy (via GitHub)" <gi...@apache.org>.
elijahbenizzy merged PR #750:
URL: https://github.com/apache/burr/pull/750


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "hussain-s (via GitHub)" <gi...@apache.org>.
hussain-s commented on code in PR #750:
URL: https://github.com/apache/burr/pull/750#discussion_r3370153042


##########
scripts/milestone_management_preview.py:
##########
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# 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.
+"""Preview milestone selection for the milestone-management workflow.
+
+Examples:
+  python scripts/milestone_management_preview.py --base-branch main --open-milestones 0.43.0 0.43.1
+  python scripts/milestone_management_preview.py --base-branch release-0.43.1 --open-milestones 0.43.0 0.43.1
+  python scripts/milestone_management_preview.py --base-branch v0.43-test --open-milestones 0.43.0
+"""
+
+import argparse
+import re

Review Comment:
   address in next revision



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "hussain-s (via GitHub)" <gi...@apache.org>.
hussain-s commented on code in PR #750:
URL: https://github.com/apache/burr/pull/750#discussion_r3370152729


##########
.github/workflows/milestone-management.yml:
##########
@@ -0,0 +1,228 @@
+#<!--
+#     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.
+#-->
+# Milestone management for release tracking
+#
+# - Auto-assigns milestones to merged PRs
+# - Supports main plus release/test branch naming conventions
+# - Can create/open upcoming release milestones on demand
+
+name: Milestone Management
+
+on:
+  pull_request_target:
+    types: [closed]
+  workflow_dispatch:
+    inputs:
+      versions:
+        description: "Comma- or newline-separated milestone versions in X.Y.Z format"
+        required: true
+        type: string
+
+permissions:
+  issues: write
+  pull-requests: read
+
+jobs:
+  assign-milestone:
+    if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == true
+    runs-on: ubuntu-latest
+    steps:
+      - name: Assign milestone to merged PR
+        uses: actions/github-script@v7
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
+
+            function parseVersion(version) {
+              const match = version.match(SEMVER_RE);
+              if (!match) {
+                return null;
+              }
+              return match.slice(1).map(part => Number(part));
+            }
+
+            function compareVersions(left, right) {
+              const leftParts = parseVersion(left);
+              const rightParts = parseVersion(right);
+              for (let index = 0; index < 3; index += 1) {
+                if (leftParts[index] !== rightParts[index]) {
+                  return leftParts[index] - rightParts[index];
+                }
+              }
+              return 0;
+            }
+
+            function milestoneTitleForBranch(branchName) {
+              const exactPatterns = [
+                /^release[-/](\d+\.\d+\.\d+)$/,
+                /^apache-burr-(\d+\.\d+\.\d+)-release$/,
+                /^v(\d+\.\d+\.\d+)-test$/,
+              ];
+              for (const pattern of exactPatterns) {
+                const match = branchName.match(pattern);
+                if (match) {
+                  return match[1];
+                }
+              }
+
+              const minorPatterns = [
+                /^release[-/](\d+\.\d+)$/,
+                /^v(\d+\.\d+)-test$/,
+              ];
+              for (const pattern of minorPatterns) {
+                const match = branchName.match(pattern);
+                if (match) {
+                  return `${match[1]}.0`;
+                }
+              }
+
+              return null;
+            }
+
+            async function listMilestones(state) {
+              return github.paginate(github.rest.issues.listMilestones, {
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                state,
+                per_page: 100,
+              });
+            }
+
+            async function findMilestoneByTitle(title, state = "open") {
+              const milestones = await listMilestones(state);
+              return milestones.find(milestone => milestone.title === title) || null;
+            }
+
+            async function findCurrentOpenMilestone() {
+              const openMilestones = await listMilestones("open");
+              const releaseMilestones = openMilestones
+                .filter(milestone => SEMVER_RE.test(milestone.title))
+                .sort((left, right) => compareVersions(left.title, right.title));
+
+              return releaseMilestones[0] || null;
+            }
+
+            const pr = context.payload.pull_request;
+            if (pr.milestone) {
+              console.log(`#${pr.number}: already has milestone ${pr.milestone.title}, leaving unchanged`);
+              return;
+            }
+
+            let milestone = null;
+            if (pr.base.ref === "main") {
+              milestone = await findCurrentOpenMilestone();
+              if (!milestone) {
+                throw new Error(
+                  "No open X.Y.Z milestone found for main. Create one with the workflow_dispatch path first."

Review Comment:
   address in next revision



##########
tests/test_milestone_management_preview.py:
##########
@@ -0,0 +1,64 @@
+# 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.
+
+import importlib.util
+from pathlib import Path
+
+import pytest
+
+
+def _load_preview_module():
+    module_path = (
+        Path(__file__).resolve().parent.parent / "scripts" / "milestone_management_preview.py"
+    )
+    spec = importlib.util.spec_from_file_location("milestone_management_preview", module_path)
+    module = importlib.util.module_from_spec(spec)
+    assert spec.loader is not None
+    spec.loader.exec_module(module)
+    return module
+
+
+preview = _load_preview_module()
+
+
+def test_main_branch_uses_earliest_open_milestone():
+    milestone = preview.choose_milestone("main", ["0.43.1", "0.43.0", "not-a-release"])
+
+    assert milestone == "0.43.0"
+
+
+def test_release_branch_maps_to_exact_version():
+    milestone = preview.choose_milestone("release-0.43.1", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.1"
+
+
+def test_minor_test_branch_maps_to_patch_zero_milestone():
+    milestone = preview.choose_milestone("v0.43-test", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.0"
+
+
+def test_unmanaged_branch_skips_assignment():
+    milestone = preview.choose_milestone("feature/example", ["0.43.0"])
+
+    assert milestone is None
+
+
+def test_missing_release_branch_milestone_raises_error():
+    with pytest.raises(ValueError, match="Expected an open milestone named '0.43.1'"):
+        preview.choose_milestone("release-0.43.1", ["0.43.0"])

Review Comment:
   address in next revision



##########
tests/test_milestone_management_preview.py:
##########
@@ -0,0 +1,64 @@
+# 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.
+
+import importlib.util
+from pathlib import Path
+
+import pytest
+
+
+def _load_preview_module():
+    module_path = (
+        Path(__file__).resolve().parent.parent / "scripts" / "milestone_management_preview.py"
+    )
+    spec = importlib.util.spec_from_file_location("milestone_management_preview", module_path)
+    module = importlib.util.module_from_spec(spec)
+    assert spec.loader is not None
+    spec.loader.exec_module(module)
+    return module
+
+
+preview = _load_preview_module()
+
+
+def test_main_branch_uses_earliest_open_milestone():
+    milestone = preview.choose_milestone("main", ["0.43.1", "0.43.0", "not-a-release"])
+
+    assert milestone == "0.43.0"
+
+
+def test_release_branch_maps_to_exact_version():
+    milestone = preview.choose_milestone("release-0.43.1", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.1"
+
+
+def test_minor_test_branch_maps_to_patch_zero_milestone():
+    milestone = preview.choose_milestone("v0.43-test", ["0.43.0", "0.43.1"])
+
+    assert milestone == "0.43.0"
+
+
+def test_unmanaged_branch_skips_assignment():
+    milestone = preview.choose_milestone("feature/example", ["0.43.0"])
+
+    assert milestone is None
+
+
+def test_missing_release_branch_milestone_raises_error():
+    with pytest.raises(ValueError, match="Expected an open milestone named '0.43.1'"):
+        preview.choose_milestone("release-0.43.1", ["0.43.0"])

Review Comment:
   address in next revision



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: add milestone management workflow [burr]

Posted by "andreahlert (via GitHub)" <gi...@apache.org>.
andreahlert commented on code in PR #750:
URL: https://github.com/apache/burr/pull/750#discussion_r3176421758


##########
scripts/milestone_management_preview.py:
##########
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# 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.
+"""Preview milestone selection for the milestone-management workflow.
+
+Examples:
+  python scripts/milestone_management_preview.py --base-branch main --open-milestones 0.43.0 0.43.1
+  python scripts/milestone_management_preview.py --base-branch release-0.43.1 --open-milestones 0.43.0 0.43.1
+  python scripts/milestone_management_preview.py --base-branch v0.43-test --open-milestones 0.43.0
+"""
+
+import argparse
+import re

Review Comment:
   burr supports py3.9 per `pyproject.toml` but `str | None` on line 42 needs 3.10+. import breaks on 3.9 and the test file blows up with it since it loads the module at top level.
   
   ```suggestion
   from __future__ import annotations
   
   import argparse
   import re
   import sys
   ```



##########
.github/workflows/milestone-management.yml:
##########
@@ -0,0 +1,228 @@
+#<!--
+#     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.
+#-->
+# Milestone management for release tracking
+#
+# - Auto-assigns milestones to merged PRs
+# - Supports main plus release/test branch naming conventions
+# - Can create/open upcoming release milestones on demand
+
+name: Milestone Management
+
+on:
+  pull_request_target:
+    types: [closed]
+  workflow_dispatch:
+    inputs:
+      versions:
+        description: "Comma- or newline-separated milestone versions in X.Y.Z format"
+        required: true
+        type: string
+
+permissions:
+  issues: write
+  pull-requests: read
+
+jobs:
+  assign-milestone:
+    if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == true
+    runs-on: ubuntu-latest
+    steps:
+      - name: Assign milestone to merged PR
+        uses: actions/github-script@v7
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
+
+            function parseVersion(version) {
+              const match = version.match(SEMVER_RE);
+              if (!match) {
+                return null;
+              }
+              return match.slice(1).map(part => Number(part));
+            }
+
+            function compareVersions(left, right) {
+              const leftParts = parseVersion(left);
+              const rightParts = parseVersion(right);
+              for (let index = 0; index < 3; index += 1) {
+                if (leftParts[index] !== rightParts[index]) {
+                  return leftParts[index] - rightParts[index];
+                }
+              }
+              return 0;
+            }
+
+            function milestoneTitleForBranch(branchName) {
+              const exactPatterns = [
+                /^release[-/](\d+\.\d+\.\d+)$/,
+                /^apache-burr-(\d+\.\d+\.\d+)-release$/,
+                /^v(\d+\.\d+\.\d+)-test$/,
+              ];
+              for (const pattern of exactPatterns) {
+                const match = branchName.match(pattern);
+                if (match) {
+                  return match[1];
+                }
+              }
+
+              const minorPatterns = [
+                /^release[-/](\d+\.\d+)$/,
+                /^v(\d+\.\d+)-test$/,
+              ];
+              for (const pattern of minorPatterns) {
+                const match = branchName.match(pattern);
+                if (match) {
+                  return `${match[1]}.0`;
+                }
+              }
+
+              return null;
+            }
+
+            async function listMilestones(state) {
+              return github.paginate(github.rest.issues.listMilestones, {
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                state,
+                per_page: 100,
+              });
+            }
+
+            async function findMilestoneByTitle(title, state = "open") {
+              const milestones = await listMilestones(state);
+              return milestones.find(milestone => milestone.title === title) || null;
+            }
+
+            async function findCurrentOpenMilestone() {
+              const openMilestones = await listMilestones("open");
+              const releaseMilestones = openMilestones
+                .filter(milestone => SEMVER_RE.test(milestone.title))
+                .sort((left, right) => compareVersions(left.title, right.title));
+
+              return releaseMilestones[0] || null;
+            }
+
+            const pr = context.payload.pull_request;
+            if (pr.milestone) {
+              console.log(`#${pr.number}: already has milestone ${pr.milestone.title}, leaving unchanged`);
+              return;
+            }
+
+            let milestone = null;
+            if (pr.base.ref === "main") {
+              milestone = await findCurrentOpenMilestone();
+              if (!milestone) {
+                throw new Error(
+                  "No open X.Y.Z milestone found for main. Create one with the workflow_dispatch path first."

Review Comment:
   this fails the workflow run on every merge until a milestone exists. a `console.warn` + return would skip quietly instead of spamming failed-run notifications.
   
   ```suggestion
                   console.warn(`#${pr.number}: no open X.Y.Z milestone for main, skipping assignment.`);
                   return;
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@burr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org