diff options
Diffstat (limited to 'gbp/rpm/policy.py')
-rw-r--r-- | gbp/rpm/policy.py | 134 |
1 files changed, 131 insertions, 3 deletions
diff --git a/gbp/rpm/policy.py b/gbp/rpm/policy.py index 80e0abde..b1b13372 100644 --- a/gbp/rpm/policy.py +++ b/gbp/rpm/policy.py @@ -17,7 +17,10 @@ """Default packaging policy for RPM""" import re + from gbp.pkg import PkgPolicy, parse_archive_filename +from gbp.scripts.common.pq import parse_gbp_commands + class RpmPkgPolicy(PkgPolicy): """Packaging policy for RPM""" @@ -33,15 +36,15 @@ class RpmPkgPolicy(PkgPolicy): # Regexp for checking the validity of package name packagename_re = re.compile("^[%s][%s%s]+$" % - (alnum, alnum, name_whitelist_chars)) + (alnum, alnum, name_whitelist_chars)) packagename_msg = ("Package names must be at least two characters long, " "start with an alphanumeric and can only contain " "alphanumerics or characters in %s" % - list(name_whitelist_chars)) + list(name_whitelist_chars)) # Regexp for checking the validity of package (upstream) version upstreamversion_re = re.compile("^[0-9][%s%s]*$" % - (alnum, version_whitelist_chars)) + (alnum, version_whitelist_chars)) upstreamversion_msg = ("Upstream version numbers must start with a digit " "and can only containg alphanumerics or characters " "in %s" % list(version_whitelist_chars)) @@ -70,3 +73,128 @@ class RpmPkgPolicy(PkgPolicy): return True return False + class Changelog(object): + """Container for changelog related policy settings""" + + # Regexps for splitting/parsing the changelog section (of + # Tizen / Fedora style changelogs) + section_match_re = r'^\*' + section_split_re = r'^\*\s*(?P<ch_header>\S.*?)$\n(?P<ch_body>.*)' + header_split_re = r'(?P<ch_time>\S.*\s[0-9]{4})\s+(?P<ch_name>\S.*$)' + header_name_split_re = r'(?P<name>[^<]*)\s+<(?P<email>[^>]+)>((\s*-)?\s+(?P<revision>\S+))?$' + body_name_re = r'\[(?P<name>.*)\]' + + # Changelog header format (when writing out changelog) + header_format = "* %(time)s %(name)s <%(email)s> %(revision)s" + header_time_format = "%a %b %d %Y" + header_rev_format = "%(version)s" + + class ChangelogEntryFormatter(object): + """Helper class for generating changelog entries from git commits""" + + # Maximum length for a changelog entry line + max_entry_line_length = 76 + # Bug tracking system related meta tags recognized from git commit msg + bts_meta_tags = ("Close", "Closes", "Fixes", "Fix") + # Regexp for matching bug tracking system ids (e.g. "bgo#123") + bug_id_re = r'[A-Za-z0-9#_\-]+' + + @classmethod + def _parse_bts_tags(cls, lines, meta_tags): + """ + Parse and filter out bug tracking system related meta tags from + commit message. + + @param lines: commit message + @type lines: C{list} of C{str} + @param meta_tags: meta tags to look for + @type meta_tags: C{tuple} of C{str} + @return: bts-ids per meta tag and the non-mathced lines + @rtype: (C{dict}, C{list} of C{str}) + """ + tags = {} + other_lines = [] + bts_re = re.compile(r'^(?P<tag>%s):\s*(?P<ids>.*)' % + ('|'.join(meta_tags)), re.I) + bug_id_re = re.compile(cls.bug_id_re) + for line in lines: + match = bts_re.match(line) + if match: + tag = match.group('tag') + ids_str = match.group('ids') + bug_ids = [bug_id.strip() for bug_id in + bug_id_re.findall(ids_str)] + if tag in tags: + tags[tag] += bug_ids + else: + tags[tag] = bug_ids + else: + other_lines.append(line) + return (tags, other_lines) + + @classmethod + def _extra_filter(cls, lines, ignore_re): + """ + Filter out specific lines from the commit message. + + @param lines: commit message + @type lines: C{list} of C{str} + @param ignore_re: regexp for matching ignored lines + @type ignore_re: C{str} + @return: filtered commit message + @rtype: C{list} of C{str} + """ + if ignore_re: + match = re.compile(ignore_re) + return [line for line in lines if not match.match(line)] + else: + return lines + + @classmethod + def compose(cls, commit_info, **kwargs): + """ + Generate a changelog entry from a git commit. + + @param commit_info: info about the commit + @type commit_info: C{commit_info} object from + L{gbp.git.repository.GitRepository.get_commit_info()}. + @param kwargs: additional arguments to the compose() method, + currently we recognize 'full', 'id_len' and 'ignore_re' + @type kwargs: C{dict} + @return: formatted changelog entry + @rtype: C{list} of C{str} + """ + # Parse and filter out gbp command meta-tags + cmds, body = parse_gbp_commands(commit_info, 'gbp-rpm-ch', + ('ignore', 'short', 'full'), ()) + body = body.splitlines() + if 'ignore' in cmds: + return None + + # Parse and filter out bts-related meta-tags + bts_tags, body = cls._parse_bts_tags(body, cls.bts_meta_tags) + + # Additional filtering + body = cls._extra_filter(body, kwargs['ignore_re']) + + # Generate changelog entry + subject = commit_info['subject'] + commitid = commit_info['id'] + if kwargs['id_len']: + text = ["- [%s] %s" % (commitid[0:kwargs['id_len']], subject)] + else: + text = ["- %s" % subject] + + # Add all non-filtered-out lines from commit message, unless 'short' + if (kwargs['full'] or 'full' in cmds) and 'short' not in cmds: + # Add all non-blank body lines. + text.extend([" " + line for line in body if line.strip()]) + + # Add bts tags and ids in the end + for tag, ids in bts_tags.iteritems(): + bts_msg = " (%s: %s)" % (tag, ', '.join(ids)) + if len(text[-1]) + len(bts_msg) >= cls.max_entry_line_length: + text.append(" ") + text[-1] += bts_msg + + return text |