#!/usr/bin/python3 import sys sys.path.append(sys.path[0] + "/../lib/python") import os, re, subprocess import locale from debian_linux.debian import Changelog, Version from config import Config, pattern_to_re # Convert Python glob pattern to Git patterns. Notable difference is # that in Python globbing '**/' matches a directory and its # descendants, but in Git it only matches descendants. Assume there # is at most one '**' in a pattern. def pattern_to_git_patterns(pattern): yield f':{pattern}' if '**/' in pattern: yield f':{ pattern.replace("**/", "") }' # Convert Python glob pattern to Git reegexp. This assumes # pattern_to_re() produces compatible regular expressions except for # the use of '(?:...)' which we fix up. def pattern_to_git_re(pattern): return pattern_to_re(pattern).pattern.replace('(?:', '(') def print_stable_log(log, cur_ver, new_ver, files_include): inc_git_patterns = [] inc_git_res = [] for pattern in files_include: inc_git_patterns.extend(pattern_to_git_patterns(pattern)) inc_git_res.append(pattern_to_git_re(pattern)) git_rev_range = f'{cur_ver}..{new_ver}' # List commits changing files that we include with subprocess.Popen(['git', 'log', '--no-merges', '--pretty=%s', git_rev_range, '--'] + inc_git_patterns, stdout=subprocess.PIPE, text=True) \ as proc: lines = proc.stdout.readlines() # List commits changing links that we include with subprocess.Popen(['git', 'log', '--no-merges', '--pretty=%s', '-G', f'^Link: *({ "|".join(inc_git_res) }) ->', git_rev_range, '--', 'WHENCE'], stdout=subprocess.PIPE, text=True) \ as proc: lines.extend(proc.stdout.readlines()) # Strip useless subject prefix strip_re = re.compile(r'^linux-firmware: *') lines = [strip_re.sub('', line) for line in lines] # Sort and de-dupe lines lines.sort(key=str.casefold) last_line = None for line in lines: if line != last_line: log.write(f' - {line}') last_line = line def main(repo, new_ver): locale.setlocale(locale.LC_CTYPE, "C.UTF-8") os.environ['GIT_DIR'] = repo + '/.git' changelog = Changelog(version=Version) cur_ver = changelog[0].version.upstream # Nothing to update if cur_ver == new_ver: sys.exit(0) # Get list of file patterns that we include. Don't get exclusions # because in some cases files are excluded from one binary package # so they can be included in another, and I don't think we can # construct a single pattern list that exactly matches our include/ # exclude behaviour. config = Config() files_include = sum((config['base', package]['files'] for package in config['base',]['packages']), []) new_pkg_ver = new_ver + '-1' # Three possible cases: # 1. The current version has been released so we need to add a new # version to the changelog. # 2. The current version has not been released so we're changing its # version string. # (a) There are no stable updates included in the current version, # so we need to insert an introductory line, the URL(s) and # git log(s) and a blank line at the top. # (b) One or more stable updates are already included in the current # version, so we need to insert the URL(s) and git log(s) after # them. changelog_intro = 'New upstream version:' # Case 1 if changelog[0].distribution != 'UNRELEASED': subprocess.check_call(['dch', '-v', new_pkg_ver, '-D', 'UNRELEASED', changelog_intro]) with open('debian/changelog', 'r') as old_log: with open('debian/changelog.new', 'w') as new_log: line_no = 0 inserted = False intro_line = ' * {}\n'.format(changelog_intro) for line in old_log: line_no += 1 # Case 2 if changelog[0].distribution == 'UNRELEASED' and line_no == 1: print('{} ({}) UNRELEASED; urgency={}' .format(changelog[0].source, new_pkg_ver, changelog[0].urgency), file=new_log) continue if not inserted: # Case 2(a) if line_no == 3 and line != intro_line: new_log.write(intro_line) print_stable_log(new_log, cur_ver, new_ver, files_include) new_log.write('\n') inserted = True # Case 1 or 2(b) elif line_no > 3 and line == '\n': print_stable_log(new_log, cur_ver, new_ver, files_include) inserted = True # Check that we inserted before hitting the end of the # first version entry assert not (line.startswith(' -- ') and not inserted) new_log.write(line) os.rename('debian/changelog.new', 'debian/changelog') if __name__ == '__main__': if len(sys.argv) != 3: print('''\ Usage: {} REPO VERSION REPO is the git repository to generate a changelog from VERSION is the upstream version'''.format(sys.argv[0]), file=sys.stderr) sys.exit(2) main(*sys.argv[1:])