ubuntu-dev-tools/requestsync
Dan Streetman 0eeb93ee0c logging: update ubuntutools.getLogger() to output to stdout/stderr correctly
Python logging by default sends all output to stderr, but that's not what
normal programs usually do and is not expected for these scripts.

Change the ubuntutools.getLogger() method to return a logger with handlers
correctly set up to send INFO level (or lower) logs to stdout and WARNING
level (or higher; technically INFO+1 level or higher) logs to stderr.

This results in normally logged output going to stdout and warnings/errors
going to stderr, as expected.
2021-02-02 06:25:12 -05:00

364 lines
14 KiB
Python
Executable File

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# (C) 2007 Canonical Ltd., Steve Kowalik
# Authors:
# Martin Pitt <martin.pitt@ubuntu.com>
# Steve Kowalik <stevenk@ubuntu.com>
# Michael Bienia <geser@ubuntu.com>
# Daniel Hahler <ubuntu@thequod.de>
# Iain Lane <laney@ubuntu.com>
# Jonathan Davies <jpds@ubuntu.com>
# Markus Korn <thekorn@gmx.de> (python-launchpadlib support)
#
# ##################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# See file /usr/share/common-licenses/GPL-2 for more details.
#
# ##################################################################
import optparse
import os
import sys
from distro_info import UbuntuDistroInfo
from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.lp import udtexceptions
from ubuntutools.misc import require_utf8
from ubuntutools.question import confirmation_prompt, EditBugReport
from ubuntutools.version import Version
from ubuntutools import getLogger
Logger = getLogger()
#
# entry point
#
def main():
# Our usage options.
usage = ('Usage: %prog [options] '
'<source package> [<target release> [base version]]')
parser = optparse.OptionParser(usage)
parser.add_option('-d', type='string',
dest='dist', default='unstable',
help='Debian distribution to sync from.')
parser.add_option('-k', type='string',
dest='keyid', default=None,
help='GnuPG key ID to use for signing report '
'(only used when emailing the sync request).')
parser.add_option('-n', action='store_true',
dest='newpkg', default=False,
help='Whether package to sync is a new package in '
'Ubuntu.')
parser.add_option('--email', action='store_true', default=False,
help='Use a PGP-signed email for filing the sync '
'request, rather than the LP API.')
parser.add_option('--lp', dest='deprecated_lp_flag',
action='store_true', default=False,
help=optparse.SUPPRESS_HELP)
parser.add_option('-l', '--lpinstance', metavar='INSTANCE',
dest='lpinstance', default=None,
help='Launchpad instance to connect to '
'(default: production).')
parser.add_option('-s', action='store_true',
dest='sponsorship', default=False,
help='Force sponsorship')
parser.add_option('-C', action='store_true',
dest='missing_changelog_ok', default=False,
help='Allow changelog to be manually filled in '
'when missing')
parser.add_option('-e', action='store_true',
dest='ffe', default=False,
help='Use this after FeatureFreeze for non-bug fix '
'syncs, changes default subscription to the '
'appropriate release team.')
parser.add_option('--no-conf', action='store_true',
dest='no_conf', default=False,
help="Don't read config files or environment variables")
(options, args) = parser.parse_args()
if not len(args):
parser.print_help()
sys.exit(1)
require_utf8()
config = UDTConfig(options.no_conf)
if options.deprecated_lp_flag:
Logger.info("The --lp flag is now default, ignored.")
if options.email:
options.lpapi = False
else:
options.lpapi = config.get_value('USE_LPAPI', default=True,
boolean=True)
if options.lpinstance is None:
options.lpinstance = config.get_value('LPINSTANCE')
if options.keyid is None:
options.keyid = config.get_value('KEYID')
if not options.lpapi:
if options.lpinstance == 'production':
bug_mail_domain = 'bugs.launchpad.net'
elif options.lpinstance == 'staging':
bug_mail_domain = 'bugs.staging.launchpad.net'
else:
Logger.error('Error: Unknown launchpad instance: %s'
% options.lpinstance)
sys.exit(1)
mailserver_host = config.get_value('SMTP_SERVER',
default=None,
compat_keys=['UBUSMTP', 'DEBSMTP'])
if not options.lpapi and not mailserver_host:
try:
import DNS
DNS.DiscoverNameServers()
mxlist = DNS.mxlookup(bug_mail_domain)
firstmx = mxlist[0]
mailserver_host = firstmx[1]
except ImportError:
Logger.error('Please install python-dns to support '
'Launchpad mail server lookup.')
sys.exit(1)
mailserver_port = config.get_value('SMTP_PORT', default=25,
compat_keys=['UBUSMTP_PORT',
'DEBSMTP_PORT'])
mailserver_user = config.get_value('SMTP_USER',
compat_keys=['UBUSMTP_USER',
'DEBSMTP_USER'])
mailserver_pass = config.get_value('SMTP_PASS',
compat_keys=['UBUSMTP_PASS',
'DEBSMTP_PASS'])
# import the needed requestsync module
if options.lpapi:
from ubuntutools.requestsync.lp import (check_existing_reports,
get_debian_srcpkg,
get_ubuntu_srcpkg,
get_ubuntu_delta_changelog,
need_sponsorship, post_bug)
from ubuntutools.lp.lpapicache import Distribution, Launchpad
# See if we have LP credentials and exit if we don't -
# cannot continue in this case
try:
# devel for changelogUrl()
Launchpad.login(service=options.lpinstance, api_version='devel')
except IOError:
sys.exit(1)
else:
from ubuntutools.requestsync.mail import (check_existing_reports,
get_debian_srcpkg,
get_ubuntu_srcpkg,
get_ubuntu_delta_changelog,
mail_bug, need_sponsorship)
if not any(x in os.environ for x in ('UBUMAIL', 'DEBEMAIL', 'EMAIL')):
Logger.error('The environment variable UBUMAIL, DEBEMAIL or EMAIL needs '
'to be set to let this script mail the sync request.')
sys.exit(1)
newsource = options.newpkg
sponsorship = options.sponsorship
distro = options.dist
ffe = options.ffe
lpapi = options.lpapi
need_interaction = False
force_base_version = None
srcpkg = args[0]
if len(args) == 1:
if lpapi:
release = Distribution('ubuntu').getDevelopmentSeries().name
else:
ubu_info = UbuntuDistroInfo()
release = ubu_info.devel()
Logger.warning('Target release missing - assuming %s' % release)
elif len(args) == 2:
release = args[1]
elif len(args) == 3:
release = args[1]
force_base_version = Version(args[2])
else:
Logger.error('Too many arguments.')
parser.print_help()
sys.exit(1)
# Get the current Ubuntu source package
try:
ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release, 'Proposed')
ubuntu_version = Version(ubuntu_srcpkg.getVersion())
ubuntu_component = ubuntu_srcpkg.getComponent()
newsource = False # override the -n flag
except udtexceptions.PackageNotFoundException:
ubuntu_srcpkg = None
ubuntu_version = Version('~')
ubuntu_component = None # Set after getting the Debian info
if not newsource:
Logger.info("'%s' doesn't exist in 'Ubuntu %s'." % (srcpkg, release))
Logger.info("Do you want to sync a new package?")
confirmation_prompt()
newsource = True
except udtexceptions.SeriesNotFoundException as error:
Logger.error(error)
sys.exit(1)
# Get the requested Debian source package
try:
debian_srcpkg = get_debian_srcpkg(srcpkg, distro)
debian_version = Version(debian_srcpkg.getVersion())
debian_component = debian_srcpkg.getComponent()
except udtexceptions.PackageNotFoundException as error:
Logger.error(error)
sys.exit(1)
except udtexceptions.SeriesNotFoundException as error:
Logger.error(error)
sys.exit(1)
if ubuntu_component is None:
if debian_component == 'main':
ubuntu_component = 'universe'
else:
ubuntu_component = 'multiverse'
# Stop if Ubuntu has already the version from Debian or a newer version
if (ubuntu_version >= debian_version) and options.lpapi:
# try rmadison
import ubuntutools.requestsync.mail
try:
debian_srcpkg = ubuntutools.requestsync.mail.get_debian_srcpkg(srcpkg, distro)
debian_version = Version(debian_srcpkg.getVersion())
debian_component = debian_srcpkg.getComponent()
except udtexceptions.PackageNotFoundException as error:
Logger.error(error)
sys.exit(1)
if ubuntu_version == debian_version:
Logger.error('The versions in Debian and Ubuntu are the '
'same already (%s). Aborting.' % ubuntu_version)
sys.exit(1)
if ubuntu_version > debian_version:
Logger.error('The version in Ubuntu (%s) is newer than '
'the version in Debian (%s). Aborting.'
% (ubuntu_version, debian_version))
sys.exit(1)
# -s flag not specified - check if we do need sponsorship
if not sponsorship:
sponsorship = need_sponsorship(srcpkg, ubuntu_component, release)
if not sponsorship and not ffe:
Logger.error('Consider using syncpackage(1) for syncs that '
'do not require feature freeze exceptions.')
# Check for existing package reports
if not newsource:
check_existing_reports(srcpkg)
# Generate bug report
pkg_to_sync = ('%s %s (%s) from Debian %s (%s)'
% (srcpkg, debian_version, ubuntu_component,
distro, debian_component))
title = "Sync %s" % pkg_to_sync
if ffe:
title = "FFe: " + title
report = "Please sync %s\n\n" % pkg_to_sync
if 'ubuntu' in str(ubuntu_version):
need_interaction = True
Logger.info('Changes have been made to the package in Ubuntu.')
Logger.info('Please edit the report and give an explanation.')
Logger.info('Not saving the report file will abort the request.')
report += ('Explanation of the Ubuntu delta and why it can be '
'dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n'
% get_ubuntu_delta_changelog(ubuntu_srcpkg))
if ffe:
need_interaction = True
Logger.info('To approve FeatureFreeze exception, you need to state')
Logger.info('the reason why you feel it is necessary.')
Logger.info('Not saving the report file will abort the request.')
report += ('Explanation of FeatureFreeze exception:\n'
'>>> ENTER_EXPLANATION_HERE <<<\n\n')
if need_interaction:
confirmation_prompt()
base_version = force_base_version or ubuntu_version
if newsource:
report += 'All changelog entries:\n\n'
else:
report += ('Changelog entries since current %s version %s:\n\n'
% (release, ubuntu_version))
changelog = debian_srcpkg.getChangelog(since_version=base_version)
if not changelog:
if not options.missing_changelog_ok:
Logger.error("Did not retrieve any changelog entries. "
"Do you need to specify '-C'? "
"Was the package recently uploaded? (check "
"http://packages.debian.org/changelogs/)")
sys.exit(1)
else:
need_interaction = True
changelog = "XXX FIXME: add changelog here XXX"
report += changelog
editor = EditBugReport(title, report)
editor.edit(optional=not need_interaction)
title, report = editor.get_report()
if 'XXX FIXME' in report:
Logger.error("changelog boilerplate found in report, "
"please manually add changelog when using '-C'")
sys.exit(1)
# bug status and bug subscriber
status = 'confirmed'
subscribe = 'ubuntu-archive'
if sponsorship:
status = 'new'
subscribe = 'ubuntu-sponsors'
if ffe:
status = 'new'
subscribe = 'ubuntu-release'
srcpkg = not newsource and srcpkg or None
if lpapi:
# Map status to the values expected by LP API
mapping = {'new': 'New', 'confirmed': 'Confirmed'}
# Post sync request using LP API
post_bug(srcpkg, subscribe, mapping[status], title, report)
else:
email_from = ubu_email(export=False)[1]
# Mail sync request
mail_bug(srcpkg, subscribe, status, title, report, bug_mail_domain,
options.keyid, email_from, mailserver_host, mailserver_port,
mailserver_user, mailserver_pass)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
Logger.error("User abort.")
sys.exit(2)