This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit b93fd43622ace0a3743baa3f7ae2cc3f84119cd8
Author: David Smiley <[email protected]>
AuthorDate: Sun Sep 27 14:33:53 2026 -0400

    changelog changes2html.py: fix author extraction swallowing trailing title 
text, and multi-issue items not all linking
    
    extract_issue_from_text() only removed the first markdown-formatted
    issue link it found via re.search, leaving any additional issue
    references (e.g. two related JIRA issues on one change) as raw,
    unlinked markdown text in the rendered item.
    
    Separately, extract_authors() ran on the text left after removing the
    issue link, walking backward through every adjacent trailing '(...)'
    group. Since removing the issue link closed the gap between it and
    the description, a parenthetical at the end of the description itself
    (an acronym expansion, a method's '()', a quoted aside) became
    adjacent to the real author group and got swallowed into it, e.g.
    '(RRF)' or '(field:* / [* TO *])' showing up as a bogus author.
    
    Fix both by extracting every issue link (not just the first) and
    using the position of the first one as a hard boundary: everything
    before it is the description and is never touched by author
    extraction, which is now scanned only from that point on.
    
    Verified by rendering the actual 9.11.0 CHANGELOG.md before and after:
    308 entries changed, all of them fixes of these two kinds (spot-checked
    broadly, including old entries referencing many issues); identical
    <li> count and no dropped/garbled entries.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
    (cherry picked from commit c9ad9c450b3876f87db067c086b81cdbc8d5eef3)
---
 .../documentation/changes-to-html/changes2html.py  | 77 +++++++++++++++-------
 1 file changed, 55 insertions(+), 22 deletions(-)

diff --git a/gradle/documentation/changes-to-html/changes2html.py 
b/gradle/documentation/changes-to-html/changes2html.py
index 1d52618834e..287c3205145 100755
--- a/gradle/documentation/changes-to-html/changes2html.py
+++ b/gradle/documentation/changes-to-html/changes2html.py
@@ -142,21 +142,22 @@ class HTMLGenerator:
         """Format a single issue reference as an HTML anchor tag"""
         return f'<a href="{url_prefix}{issue_id}">{label}</a>'
 
-    def _extract_markdown_issue(self, text):
+    def _find_markdown_issue_matches(self, text):
         """
-        Extract markdown-formatted JIRA/GitHub issues like [SOLR-123](url) or 
[PR#123](url).
-        Returns (issue_link_html, text_without_issue) or (None, text) if not 
found.
+        Find every markdown-formatted JIRA/GitHub issue reference in text,
+        e.g. [SOLR-123](url) or [PR#123](url) -- an item can reference more
+        than one issue. Returns a list of (start, end, issue_link_html)
+        tuples in the order they appear.
         """
+        matches = []
         for pattern, url_prefix, label_fmt in self.issue_patterns:
-            match = re.search(pattern, text)
-            if match:
+            for match in re.finditer(pattern, text):
                 issue_id = match.group(1)
                 label = label_fmt.format(issue_id)
                 issue_html = self._format_issue_link(url_prefix, issue_id, 
label)
-                text_without = (text[:match.start()] + 
text[match.end():]).strip()
-                return issue_html, text_without
-
-        return None, text
+                matches.append((match.start(), match.end(), issue_html))
+        matches.sort(key=lambda m: m[0])
+        return matches
 
     def _extract_plain_pr_references(self, text):
         """
@@ -187,17 +188,39 @@ class HTMLGenerator:
 
     def extract_issue_from_text(self, text):
         """
-        Extract the first issue reference from text.
-        Tries in order: markdown JIRA/GitHub issues, plain GitHub PR 
references.
-        Returns (issue_link_html, text_without_issue) or (None, text) if not 
found.
+        Extract issue reference(s) from text.
+        Tries markdown-formatted JIRA/GitHub issues first -- there may be
+        more than one, e.g. two related JIRA issues on the same change --
+        falling back to a plain GitHub PR reference like "#123" if none
+        are found.
+
+        Returns (issue_link_html_or_None, description_or_None, tail).
+        When markdown issues were found, `description` is the text before
+        the first issue reference and `tail` is everything from there on
+        with the issue reference(s) removed (normally just the trailing
+        author "(...)" groups). Author extraction must be bounded to
+        `tail`, never `description`, so a parenthetical at the end of the
+        description (e.g. an acronym expansion) is never mistaken for an
+        author group. When `description` is None, no markdown issue was
+        found and `tail` is just `text` with any plain PR reference
+        removed, if present.
         """
-        # Try markdown-formatted issues first
-        issue_html, text_without = self._extract_markdown_issue(text)
-        if issue_html:
-            return issue_html, text_without
+        matches = self._find_markdown_issue_matches(text)
+        if matches:
+            first_start = matches[0][0]
+            parts = []
+            pieces = []
+            last_end = first_start
+            for start, end, issue_html in matches:
+                pieces.append(text[last_end:start])
+                parts.append(issue_html)
+                last_end = end
+            pieces.append(text[last_end:])
+            return ' '.join(parts), text[:first_start], ''.join(pieces)
 
         # Fall back to plain GitHub PR references
-        return self._extract_plain_pr_references(text)
+        issue_html, text_without = self._extract_plain_pr_references(text)
+        return issue_html, None, text_without
 
     def _format_single_author(self, author_text):
         """
@@ -372,11 +395,21 @@ class HTMLGenerator:
         Format: [ISSUE](url) description (author1) (author2)
         Output: <a href>ISSUE</a>: description<br><span 
class="attrib">(authors)</span>
         """
-        # Extract the issue
-        issue_html, text_after_issue = self.extract_issue_from_text(item_text)
-
-        # Always try to extract authors, whether or not we found an issue
-        authors_list, description = self.extract_authors(text_after_issue if 
issue_html else item_text)
+        # Extract the issue(s)
+        issue_html, description_head, tail = 
self.extract_issue_from_text(item_text)
+
+        if description_head is not None:
+            # Markdown issue(s) found: bound author extraction to the tail
+            # so it can't reach back into the description.
+            authors_list, tail_remainder = self.extract_authors(tail)
+            description = description_head.strip()
+            tail_remainder = tail_remainder.strip()
+            if tail_remainder:
+                description = f'{description} {tail_remainder}'.strip()
+        else:
+            # No markdown issue (plain PR reference, or no issue at all):
+            # extract authors from the whole remaining text, as before.
+            authors_list, description = self.extract_authors(tail if 
issue_html else item_text)
 
         if issue_html:
             # We have an issue link

Reply via email to