blob: 6718729eff61488cba48ee3fe11c7a6c09054ebc [file] [log] [blame]
#!/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\.]+)'
IGNORED_PATTERNS = (
'Marking set of ebuilds as stable',
r'Incremented to version: \d+\.\d+\.\d+',
r'Increment to version R\d+-\d+\.\d+\.\d+.*',
)
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 = []
ignored_commits = []
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 not cl:
skipped_lines.append(line)
continue
ignored = False
for pattern in IGNORED_PATTERNS:
if re.fullmatch(pattern, cl.title):
ignored = True
continue
if ignored:
ignored_commits.append((cl.commit, repo))
else:
cl_list.append(cl)
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:
if not cl_list:
continue
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:
ignored = False
for pattern in IGNORED_PATTERNS:
if re.fullmatch(pattern, cl.title):
ignored = True
continue
if ignored:
continue
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 commit, repo in ignored_commits:
logger.warning('Ignore commit %s in %s', commit, repo)
for line in skipped_lines:
logger.warning('Skipping line: %s', line)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))