| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*-" |
| # |
| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Parse ChangeLog from CPCon and generate commit message. |
| |
| Usage: |
| 1. Copy ChangeLog from CPCon (everything except the MENU bar) |
| 2. xsel -b | ./gen_uprev_msg.py [-b BOARD] [--extra-repo-file FILE] |
| 3. Commit message will be printed to stdout |
| 4. Be aware of the warning messages in stderr |
| """ |
| from __future__ import print_function |
| |
| import argparse |
| import collections |
| import logging |
| import re |
| import sys |
| |
| logging.basicConfig(level=logging.INFO) |
| logger = logging.getLogger() |
| |
| MAX_LENGTH = 72 |
| CHANGES_PATTERN = r'Changes between ([0-9\.]+) and ([0-9\.]+)' |
| |
| DEFAULT_REPOS = ( |
| 'src/overlays', |
| 'src/platform/bmpblk', |
| 'src/platform/depthcharge', |
| 'src/platform/ec', |
| 'src/platform/firmware', |
| 'src/platform/vboot_reference', |
| 'src/third_party/arm-trusted-firmware', |
| 'src/third_party/chromiumos-overlay', |
| 'src/third_party/coreboot', |
| 'src/third_party/coreboot/3rdparty/blobs', |
| ) |
| |
| |
| CL = collections.namedtuple('CL', ['commit', 'cl', 'bug', 'title']) |
| |
| |
| def read_extra_repos(filename): |
| """Read extra repos from |filename|.""" |
| repos = set() |
| with open(filename) as f: |
| for line in f: |
| repo = line.strip() |
| if repo: |
| repos.add(repo) |
| return repos |
| |
| |
| def parse_cl(line): |
| """Parse CL.""" |
| tokens = line.split('\t') |
| if len(tokens) != 6: |
| return None |
| commit, cl, bug, _date, _author, title = tokens |
| return CL(commit, int(cl) if cl else None, int(bug) if bug else None, title) |
| |
| |
| def wrap_line(s, max_length): |
| """Wrap a line.""" |
| # Assume words are separated by one space |
| words = s.split() |
| lines = [[]] |
| length = 0 |
| for word in words: |
| if length + 1 + len(word) > max_length: |
| lines.append([]) |
| length = 0 |
| lines[-1].append(word) |
| length += 1 + len(word) |
| return [' '.join(line) for line in lines] |
| |
| |
| def main(args): |
| """Parse ChangeLog and print commit message.""" |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument('-b', '--board') |
| parser.add_argument('--extra-repo-file', |
| help='File containing extra repo names') |
| args = parser.parse_args(args) |
| board = args.board or 'BOARD' |
| included_repos = set(DEFAULT_REPOS) |
| if args.extra_repo_file: |
| included_repos |= read_extra_repos(args.extra_repo_file) |
| |
| changes = False |
| repos = [] |
| repo = None |
| ignored_repos = [] |
| skipped_lines = [] |
| for line in sys.stdin: |
| line = line.strip() |
| |
| # Parse "Changes between 12573.80.0 and 12573.88.0" |
| if not changes: |
| m = re.match(CHANGES_PATTERN, line) |
| if m: |
| groups = m.groups() |
| if len(groups) == 2: |
| changes = groups |
| continue |
| |
| # Parse repo |
| tokens = line.split() |
| if len(tokens) == 1 and '/' in tokens[0]: |
| repo = tokens[0] |
| if repo in included_repos: |
| cl_list = [] |
| repos.append((repo, cl_list)) |
| else: |
| ignored_repos.append(repo) |
| repo = None |
| continue |
| |
| # Parse CL |
| if not repo: |
| continue |
| |
| cl = parse_cl(line) |
| if cl: |
| cl_list.append(cl) |
| else: |
| skipped_lines.append(line) |
| continue |
| |
| if not repos: |
| logger.error('No repo found from ChangeLog') |
| return 1 |
| |
| # Output |
| if changes: |
| title = (f'chromeos-firmware-{board}: ' |
| f'Uprev firmware to {changes[1]} for {board}') |
| print(title) |
| print() |
| |
| print(f'Changes between {changes[0]} and {changes[1]}:') |
| |
| bugs = set() |
| for repo, cl_list in repos: |
| print() |
| print(repo) |
| private = 'private' in repo |
| for cl in cl_list: |
| if cl.cl: |
| cl_str = f'CL:*{cl.cl}' if private else f'CL:{cl.cl}' |
| else: |
| cl_str = 'CL:' + '?' * 7 |
| print('>>>>>>> PLEASE FILL THE CL NUMBER MANUALLY') |
| title = cl.title |
| indentation = 1 + len(cl_str) + 4 |
| for i, title_line in enumerate( |
| wrap_line(cl.title, MAX_LENGTH - indentation)): |
| if i == 0: |
| line = f' {cl_str} {title_line}' |
| else: |
| line = ' ' * indentation + f'{title_line}' |
| print(line) |
| if cl.bug: |
| bugs.add(cl.bug) |
| |
| print() |
| bugs = [f'b:{bug}' if bug >= 1e8 else f'chromium:{bug}' |
| for bug in sorted(bugs)] |
| line_bugs = [] |
| length = len('BUG=') |
| for bug in bugs: |
| if line_bugs: |
| bug = ', ' + bug |
| if length + len(bug) <= MAX_LENGTH: |
| line_bugs.append(bug) |
| length += len(bug) |
| else: |
| print('BUG=' + ''.join(line_bugs)) |
| line_bugs = [] |
| length = 0 |
| if line_bugs: |
| print('BUG=' + ''.join(line_bugs)) |
| print(f'TEST=emerge-{board} chromeos-firmware-{board}') |
| |
| # Warnings |
| for repo in ignored_repos: |
| logger.warning('Ignore repo %s', repo) |
| for line in skipped_lines: |
| logger.warning('Skipping line: %s', line) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |