256 lines
5.9 KiB
Python
Executable file
256 lines
5.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
""" Generate Debian package changelog from git """
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
import git
|
|
|
|
VERSION = '0.0'
|
|
DEFAULT_GIT_PATCH = './'
|
|
DEFAULT_CODE_NAME = 'unstable'
|
|
DEFAULT_URGENCY = 'medium'
|
|
|
|
parser = argparse.ArgumentParser(description=f'{__doc__} (version: {VERSION})')
|
|
|
|
parser.add_argument(
|
|
'-d', '--debug',
|
|
action='store_true',
|
|
help='Show debug messages'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-v', '--verbose',
|
|
action='store_true',
|
|
help='Show verbose messages'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-w', '--warning',
|
|
action='store_true',
|
|
help='Show warning messages'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-l',
|
|
'--log-file',
|
|
action="store",
|
|
type=str,
|
|
dest="logfile",
|
|
help="Log file path"
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-q', '--quiet',
|
|
action='store_true',
|
|
help='Quiet mode: do not log on console (only if log file is provided)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-p',
|
|
'--path',
|
|
type=str,
|
|
dest='git_path',
|
|
help=f'Git repository path (default: {DEFAULT_GIT_PATCH}',
|
|
default=DEFAULT_GIT_PATCH
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-o',
|
|
'--output',
|
|
type=str,
|
|
dest='output',
|
|
help='Generated Debian changelog output path (default: stdout)',
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-n',
|
|
'--package-name',
|
|
type=str,
|
|
dest='package_name',
|
|
help='Package name'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-V',
|
|
'--version',
|
|
type=str,
|
|
dest='version',
|
|
help=(
|
|
'Currrent version (default: autodetected using git describe '
|
|
'--always --tags)')
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--version-suffix',
|
|
type=str,
|
|
dest='version_suffix',
|
|
help='Suffix for autodetected version'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-c',
|
|
'--code-name',
|
|
type=str,
|
|
dest='code_name',
|
|
help=f'Debian code name (default: {DEFAULT_CODE_NAME})',
|
|
default=DEFAULT_CODE_NAME
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-u',
|
|
'--urgency',
|
|
type=str,
|
|
dest='urgency',
|
|
help=f'Package urgency (default: {DEFAULT_URGENCY})',
|
|
default=DEFAULT_URGENCY
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-N',
|
|
'--maintainer-name',
|
|
type=str,
|
|
dest='maintainer_name',
|
|
help='Maintainer name (default: last commit author name)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-E',
|
|
'--maintainer-email',
|
|
type=str,
|
|
dest='maintainer_email',
|
|
help='Maintainer email (default: last commit author email)'
|
|
)
|
|
|
|
options = parser.parse_args()
|
|
|
|
if not options.package_name:
|
|
parser.error(
|
|
'You must provide package name using -n/--package-name paramter')
|
|
|
|
# Initialize log
|
|
log = logging.getLogger()
|
|
logformat = logging.Formatter(
|
|
f'%(asctime)s - {os.path.basename(sys.argv[0])} - %(levelname)s '
|
|
': %(message)s'
|
|
)
|
|
# Set root logger to DEBUG (filtering done by handlers)
|
|
log.setLevel(logging.DEBUG)
|
|
|
|
log_level = None
|
|
if options.debug:
|
|
log_level = logging.DEBUG
|
|
elif options.verbose:
|
|
log_level = logging.INFO
|
|
elif options.warning:
|
|
log_level = logging.WARNING
|
|
|
|
if options.logfile:
|
|
logfile = logging.FileHandler(options.logfile)
|
|
logfile.setFormatter(logformat)
|
|
logfile.setLevel(log_level if log_level is not None else logging.INFO)
|
|
log.addHandler(logfile)
|
|
|
|
if not options.quiet or not options.logfile:
|
|
logconsole = logging.StreamHandler()
|
|
logconsole.setLevel(log_level if log_level is not None else logging.FATAL)
|
|
logconsole.setFormatter(logformat)
|
|
log.addHandler(logconsole)
|
|
|
|
repo = git.Repo(options.git_path)
|
|
|
|
log.info('Generate changelog from git commits')
|
|
|
|
versions = []
|
|
tag_commits = dict(
|
|
(tag.commit.binsha, tag)
|
|
for tag in repo.tags
|
|
)
|
|
|
|
|
|
def clean_deb_version(version_name):
|
|
""" Clean debian version name """
|
|
version_name = re.sub('^[^0-9]*', '', version_name)
|
|
if options.version_suffix:
|
|
version_name += options.version_suffix
|
|
return version_name
|
|
|
|
|
|
def add_version():
|
|
""" Add version info """
|
|
if messages:
|
|
log.info('Add version %s:\n - %s', version, '\n - '.join(messages))
|
|
versions.append({
|
|
'name': version,
|
|
'tag': tag,
|
|
'commit': version_commit,
|
|
'messages': messages,
|
|
})
|
|
|
|
|
|
tag = None
|
|
version_commit = None
|
|
version = (
|
|
options.version or
|
|
clean_deb_version(
|
|
repo.git.describe('--always', '--tags')
|
|
)
|
|
)
|
|
messages = []
|
|
for commit in repo.iter_commits():
|
|
if version_commit is None:
|
|
version_commit = commit
|
|
log.debug('Commit %s', commit)
|
|
if commit.binsha in tag_commits:
|
|
new_tag = tag_commits[commit.binsha]
|
|
log.debug('Reach new tag %s', new_tag)
|
|
|
|
add_version()
|
|
|
|
tag = new_tag
|
|
version = clean_deb_version(tag.name)
|
|
version_commit = commit
|
|
messages = []
|
|
log.debug('Iter commits for version %s')
|
|
messages.append(commit.summary)
|
|
add_version()
|
|
log.info('%d versions found', len(versions))
|
|
|
|
changelog_lines = []
|
|
for version in versions:
|
|
# pylint: disable=consider-using-f-string
|
|
changelog_lines.append(
|
|
'{package} ({version}-1) {code_name}; urgency={urgency}'.format(
|
|
package=options.package_name,
|
|
version=version['name'],
|
|
code_name=options.code_name,
|
|
urgency=options.urgency
|
|
)
|
|
)
|
|
for message in version['messages']:
|
|
changelog_lines.append(' * {0}\n'.format(message))
|
|
|
|
# pylint: disable=consider-using-f-string
|
|
changelog_lines.append(
|
|
"\n -- {name} <{email}> {date}\n\n".format(
|
|
name=(
|
|
options.maintainer_name if options.maintainer_name
|
|
else version['commit'].author.name),
|
|
email=(
|
|
options.maintainer_email if options.maintainer_email
|
|
else version['commit'].author.email),
|
|
date=version['commit'].committed_datetime.strftime(
|
|
"%a, %d %b %Y %H:%M:%S %z")
|
|
)
|
|
)
|
|
|
|
if options.output:
|
|
log.info('Write generated Debian changelog in file %s', options.output)
|
|
with open(options.output, 'w', encoding='utf8') as fd:
|
|
fd.writelines(changelog_lines)
|
|
else:
|
|
print(''.join(changelog_lines))
|