[OS-BUILD PATCH] redhat: forward-port genlog.py updates from c9s
by Jan Stancek (via Email Bridge)
From: Jan Stancek <jstancek(a)redhat.com>
redhat: forward-port genlog.py updates from c9s
This is forward-port of c9s commit:
b9e094da37a1 ("redhat: rewrite genlog and support Y- tags")
It combines various parse functions into a common function
that goes over list of tag:regex pairs, which makes parsing
and adding new tags simpler. It also updates regexes to be
aligned with ones used by webhooks.
This is primarily for future RHEL downstreams, so I kept the
script identical to c9s.
Signed-off-by: Jan Stancek <jstancek(a)redhat.com>
diff --git a/redhat/scripts/genspec/genlog.py b/redhat/scripts/genspec/genlog.py
index blahblah..blahblah 100755
--- a/redhat/scripts/genspec/genlog.py
+++ b/redhat/scripts/genspec/genlog.py
@@ -1,143 +1,132 @@
#!/usr/bin/python3
-#
-# This script parses a git log from stdin, which should be given with:
-# $ git log [<options>] -z --format="- %s (%an)%n%b" [<range>] [[--] <path>...] | ...
-# And then outputs to stdout a trimmed changelog for use with rpm packaging
-#
-# Author: Herton R. Krzesinski <herton(a)redhat.com>
-# Copyright (C) 2021 Red Hat, Inc.
-#
-# This software may be freely redistributed under the terms of the GNU
-# General Public License (GPL).
-
-"""Parses a git log from stdin, and output a log entry for an rpm."""
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2023 Red Hat, Inc.
+
+"""Parses git log from stdin, expecting format of:
+$ git log [<options>] -z --format="- %s (%an)%n%N%n^^^NOTES-END^^^%n%b" [<range>] [[--] <path>...] | ...
+and prints changelog output to stdout."""
import re
import sys
-def find_ticket_in_line(line, prefix, tkt_re, tkt_groups):
- """Return ticket referenced in the given line"""
- _tkts = set()
- if not line.startswith(f"{prefix}: "):
- return _tkts
- for match in tkt_re.finditer(line[len(f"{prefix}:"):]):
- for group in tkt_groups:
- if match.group(group):
- tid = match.group(group).strip()
- _tkts.add(tid)
- return _tkts
-
-def find_bz_in_line(line, prefix):
- bznum_re = re.compile(r'(?P<bug_ids> \d{4,8})|'
- r'( http(s)?://bugzilla\.redhat\.com/(show_bug\.cgi\?id=)?(?P<url_bugs>\d{4,8}))')
- return find_ticket_in_line(line, prefix, bznum_re, [ 'bug_ids', 'url_bugs' ])
-
-def find_ji_in_line(line, prefix):
- ji_re = re.compile(r' https://issues\.redhat\.com/(?:browse|projects/RHEL/issues)/(?P<jira_id>RHEL-\d{1,8})\s*$')
- return find_ticket_in_line(line, prefix, ji_re, [ 'jira_id' ])
-
-def find_cve_in_line(line):
- """Return cve number from properly formated CVE: line."""
- # CVEs must begin with 'CVE: '
- cve_set = set()
- if not line.startswith("CVE: "):
- return cve_set
- _cves = line[len("CVE: "):].split()
- pattern = "(?P<cve>CVE-[0-9]+-[0-9]+)"
- cve_re = re.compile(pattern)
- for cve_item in _cves:
- cve = cve_re.match(cve_item)
- if cve:
- cve_set.add(cve.group('cve'))
- return cve_set
-
-
-def parse_commit(commit):
- """Extract metadata from a commit log message."""
- lines = commit.split('\n')
-
- # Escape '%' as it will be used inside the rpm spec changelog
- log_entry = lines[0].replace("%","%%")
- cve_set = set()
- bug_set = set()
- zbug_set = set()
- jira_set = set()
- zjira_set = set()
- for line in lines[1:]:
- # Metadata in git notes has priority over commit log
- # If we found any BZ/ZBZ/JIRA/ZJIRA/CVE in git notes,
- # we ignore the commit log
- if line == "^^^NOTES-END^^^":
- if bug_set or zbug_set or jira_set or zjira_set or cve_set:
- break
+# CommitTags and parse_commit() in kmt and genlog.py should match
+class CommitTags:
+ tag_patterns = {
+ 'Bugzilla': [
+ r'(\d{4,8})\s*$',
+ r'https?://bugzilla\.redhat\.com/(?:show_bug\.cgi\?id=)?(\d{4,8})',
+ ],
+ 'JIRA': [r'https://issues\.redhat\.com/(?:browse|projects/RHEL/issues)/(RHEL-\d{1,8})'],
+ 'CVE': [r'(CVE-\d{4}-\d{4,7})'],
+ 'MR': [r'(.*)'],
+ 'Y-Commit': [r'([0-9a-fA-F]+)'],
+ 'Patchwork-id': [r'(.*)'],
+ 'Patchwork-instance': [r'(.*)'],
+ }
+ tag_patterns['Y-Bugzilla'] = tag_patterns['Bugzilla']
+ tag_patterns['Z-Bugzilla'] = tag_patterns['Bugzilla']
+ tag_patterns['Y-JIRA'] = tag_patterns['JIRA']
+ tag_patterns['Z-JIRA'] = tag_patterns['JIRA']
+ tag_patterns['O-JIRA'] = tag_patterns['JIRA']
+
+ compiled_patterns = {}
+ for tag_name, tag_pattern_list in tag_patterns.items():
+ tag_pattern_list.append(r'(N/A)')
+ compiled_patterns[tag_name] = []
+ for tag_pattern in tag_pattern_list:
+ pattern = r'^' + tag_name + ': ' + tag_pattern + r'\s*$'
+ tag_regex = re.compile(pattern, re.MULTILINE)
+ compiled_patterns[tag_name].append(tag_regex)
+
+ def __init__(self, input_str):
+ self.parse_tags(input_str)
+
+ def get_tag_values(self, tag_name):
+ if tag_name not in CommitTags.tag_patterns:
+ raise Exception('Unsupported tag name: ' + tag_name)
+ return self.tag_dict[tag_name]
+
+ def override_by(self, other_commit_tags):
+ for tag_name, tag_set in other_commit_tags.tag_dict.items():
+ if tag_set:
+ if tag_set == set(['N/A']):
+ self.tag_dict[tag_name] = set()
+ else:
+ self.tag_dict[tag_name] = set(tag_set)
+
+ def parse_tags(self, input_str):
+ tag_values = {}
+ for tag_name, tag_pattern_list in CommitTags.compiled_patterns.items():
+ tag_values[tag_name] = set()
+ for tag_regex in tag_pattern_list:
+ for value in tag_regex.findall(input_str):
+ tag_values[tag_name].add(value)
+ self.tag_dict = tag_values
+
+ def convert_to_y_tags(self):
+ if self.tag_dict['Z-Bugzilla'] or self.tag_dict['Z-JIRA']:
+ tmp = self.tag_dict['Bugzilla']
+ self.tag_dict['Bugzilla'] = self.tag_dict['Z-Bugzilla']
+ self.tag_dict['Y-Bugzilla'] = tmp
+ self.tag_dict['Z-Bugzilla'] = set()
+
+ tmp = self.tag_dict['JIRA']
+ self.tag_dict['JIRA'] = self.tag_dict['Z-JIRA']
+ self.tag_dict['Y-JIRA'] = tmp
+ self.tag_dict['Z-JIRA'] = set()
+
+ def get_changelog_str(self):
+ chnglog = []
+ tickets = sorted(self.tag_dict['Bugzilla']) + sorted(self.tag_dict['JIRA'])
+ if self.tag_dict['Y-Bugzilla'] or self.tag_dict['Y-JIRA']:
+ tickets = tickets + sorted(self.tag_dict['Y-Bugzilla']) + sorted(self.tag_dict['Y-JIRA'])
+ if tickets:
+ chnglog.append('[' + ' '.join(tickets) + ']')
+ if self.tag_dict['CVE']:
+ chnglog.append('{' + ' '.join(self.tag_dict['CVE']) + '}')
+ return ' '.join(chnglog)
+
+ def get_resolved_tickets(self):
+ ret = set()
+ for ticket in sorted(self.tag_dict['Bugzilla']) + sorted(self.tag_dict['JIRA']):
+ if ticket.isnumeric():
+ ticket = 'rhbz#' + ticket
+ ret.add(ticket)
+ return ret
- # Process Bugzilla and ZStream Bugzilla entries
- bug_set.update(find_bz_in_line(line, 'Bugzilla'))
- zbug_set.update(find_bz_in_line(line, 'Z-Bugzilla'))
- # Grab CVE tags if they are present
- cve_set.update(find_cve_in_line(line))
+def parse_commit(commit):
+ if '^^^NOTES-END^^^' in commit:
+ input_notes, input_commit = commit.split('^^^NOTES-END^^^')
+ else:
+ input_notes = ''
+ input_commit = commit
- # Process Jira issues
- jira_set.update(find_ji_in_line(line, 'JIRA'))
- zjira_set.update(find_ji_in_line(line, 'Z-JIRA'))
+ tags = CommitTags(input_commit)
+ if input_notes:
+ notes_tags = CommitTags(input_notes)
+ notes_tags.convert_to_y_tags()
+ tags.override_by(notes_tags)
- return (log_entry, cve_set, bug_set, zbug_set, jira_set, zjira_set)
+ return tags
if __name__ == "__main__":
- all_bzs = set()
- all_zbzs = set()
- all_jiras = set()
- all_zjiras = set()
+ all_resolved = set()
commits = sys.stdin.read().split('\0')
for c in commits:
if not c:
continue
- log_item, cves, bugs, zbugs, jiras, zjiras = parse_commit(c)
- entry = f"{log_item}"
- if bugs or zbugs or jiras or zjiras:
- entry += " ["
- if zbugs:
- entry += " ".join(sorted(zbugs))
- all_zbzs.update(zbugs)
- if bugs:
- entry += " " if zbugs else ""
- entry += " ".join(sorted(bugs))
- all_bzs.update(bugs)
- if jiras:
- entry += " " if bugs or zbugs else ""
- entry += " ".join(sorted(jiras))
- all_jiras.update(jiras)
- if zjiras:
- entry += " " if bugs or zbugs or jiras else ""
- entry += " ".join(sorted(zjiras))
- all_zjiras.update(zjiras)
- entry += "]"
- if cves:
- entry += " {" + " ".join(sorted(cves)) + "}"
+ # Escape '%' as it will be used inside the rpm spec changelog
+ entry_pos = c.find('\n')
+ entry = c[:entry_pos].replace("%", "%%")
+
+ tags = parse_commit(c)
+ chnglog = tags.get_changelog_str()
+ if chnglog:
+ entry = entry + ' ' + chnglog
print(entry)
+ all_resolved = all_resolved.union(tags.get_resolved_tickets())
- # If we are doing Z-Stream work, we are addressing Z-Stream tickets
- # and not Y-Stream tickets, so we must make sure to list on Resolves
- # line only the Z-Stream tickets
- resolved_bzs = set()
- resolved_jiras = set()
- if all_zbzs or all_zjiras:
- resolved_bzs = all_zbzs
- resolved_jiras = all_zjiras
- else:
- resolved_bzs = all_bzs
- resolved_jiras = all_jiras
- print("Resolves: ", end="")
- for i, bzid in enumerate(sorted(resolved_bzs)):
- if i:
- print(", ", end="")
- print(f"rhbz#{bzid}", end="")
- for j, jid in enumerate(sorted(resolved_jiras)):
- if j or resolved_bzs:
- print(", ", end="")
- print(f"{jid}", end="")
- print("\n")
-
+ print('Resolves: ' + ', '.join(sorted(all_resolved)) + '\n')
--
https://gitlab.com/cki-project/kernel-ark/-/merge_requests/2928