#!/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(__name__) # # 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)