#!/usr/bin/python # -*- coding: utf-8 -*- # # (C) 2007 Canonical Ltd., Steve Kowalik # Authors: # Martin Pitt # Steve Kowalik # Michael Bienia # Daniel Hahler # Iain Lane # Jonathan Davies # Markus Korn (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 os import subprocess import sys from debian_bundle.changelog import Version from optparse import OptionParser # ubuntu-dev-tools modules. import ubuntutools.lp.libsupport as lp_libsupport import ubuntutools.lp.udtexceptions as udtexceptions from ubuntutools.lp.lpapicache import Launchpad, LpApiWrapper, Distribution, PersonTeam # https_proxy fix import ubuntutools.common from ubuntutools.requestsync.mail import * from ubuntutools.requestsync.common import * from ubuntutools.requestsync.lp import postBug def checkExistingReports(package): """ Check existing bug reports on Launchpad for a possible sync request. If found ask for confirmation on filing a request. """ launchpad = None try: launchpad = Launchpad.login() except ImportError: print >> sys.stderr, 'Importing launchpadlib failed. Is ' \ 'python-launchpadlib installed?' except IOError, msg: # No credentials found. print msg # Failed to get Launchpad credentials. if launchpad is None: print >> sys.stderr, "Skipping existing report check, you should "\ "manually see if there are any at:" print "- https://bugs.launchpad.net/ubuntu/+source/%s" % package print "" return False # Fetch the package's bug list from Launchpad. pkg = Distribution('ubuntu').getSourcePackage(name=package) pkgBugList = pkg.searchTasks() # Search bug list for other sync requests. matchingBugs = [bug for bug in pkgBugList if "Sync %s" % package in bug.title or "Please sync %s" % package in bug.title] if len(matchingBugs) == 0: return # No sync bugs found. print "The following bugs could be possible duplicate sync bug(s) on Launchpad:" for bug in matchingBugs: print " *", bug.title print " -", lp_libsupport.translate_api_web(bug.self_link) print "Please check the above URLs to verify this before filing a " \ "possible duplicate report." print "Press Ctrl-C to stop filing the bug report now, otherwise " \ "please press enter." raw_input_exit_on_ctrlc() def cur_version_component(sourcepkg, release): try: src = getUbuntuSrcPkg(sourcepkg, release) return (src.getVersion(), src.getComponent()) except udtexceptions.PackageNotFoundException: print "%s doesn't appear to exist in %s, specify -n for a package not in Ubuntu." % (sourcepkg, release) sys.exit(1) def mail_bug(source_package, subscribe, status, bugtitle, bugtext, keyid = None): '''Submit the sync request per email. Return True if email successfully send, otherwise False.''' import smtplib import socket to = 'new@bugs.launchpad.net' myemailaddr = get_email_address() if not myemailaddr: return False # generate initial mailbody mailbody = '' if source_package: mailbody += ' affects ubuntu/%s\n' % source_package else: mailbody += ' affects ubuntu\n' mailbody = mailbody + ' status %s\n importance wishlist\n subscribe %s\n done\n\n%s' % (status, subscribe, bugtext) # prepare sign_command sign_command = 'gpg' for cmd in ('gpg2', 'gnome-gpg'): if os.access('/usr/bin/%s' % cmd, os.X_OK): sign_command = cmd gpg_command = [sign_command, '--clearsign'] if keyid: gpg_command.extend(('-u', keyid)) # sign it gpg = subprocess.Popen(gpg_command, stdin = subprocess.PIPE, stdout = subprocess.PIPE) signed_report = gpg.communicate(mailbody)[0] assert gpg.returncode == 0 # generate email mail = '''\ From: %s To: %s Subject: %s Content-Type: text/plain; charset=UTF-8 %s''' % (myemailaddr, to, bugtitle, signed_report) # ask for confirmation and allow to edit: print mail print 'Do you want to edit the report before sending [y/N]? Press Control-C to abort.' raw_input_exit_on_ctrlc() # get server address mailserver = os.getenv('DEBSMTP') if mailserver: print 'Using custom SMTP server:', mailserver else: mailserver = 'fiordland.ubuntu.com' # get server port mailserver_port = os.getenv('DEBSMTP_PORT') if mailserver_port: print 'Using custom SMTP port:', mailserver_port else: mailserver_port = 25 # connect to the server try: s = smtplib.SMTP(mailserver, mailserver_port) except socket.error, s: print >> sys.stderr, "Could not connect to mailserver %s at port %s: %s (%i)" % \ (mailserver, mailserver_port, s[1], s[0]) print "The port %s may be firewalled. Please try using requestsync with" \ % mailserver_port print "the '--lp' flag to file a sync request with the launchpadlib " \ "module." return False # authenticate to the server mailserver_user = os.getenv('DEBSMTP_USER') mailserver_pass = os.getenv('DEBSMTP_PASS') if mailserver_user and mailserver_pass: try: s.login(mailserver_user, mailserver_pass) except smtplib.SMTPAuthenticationError: print 'Error authenticating to the server: invalid username and password.' s.quit() return False except: print 'Unknown SMTP error.' s.quit() return False s.sendmail(myemailaddr, to, mail) s.quit() print 'Sync request mailed.' return True # # entry point # if __name__ == '__main__': # Our usage options. usage = "Usage: %prog [-d distro] [-k keyid] [-n] [--lp] [-s] [-e] " usage += " [ [base version]]" optParser = OptionParser(usage) optParser.add_option("-d", type = "string", dest = "dist", default = "unstable", help = "Debian distribution to sync from.") optParser.add_option("-k", type = "string", dest = "keyid", default = None, help = "GnuPG key ID to use for signing report.") optParser.add_option("-n", action = "store_true", dest = "newpkg", default = False, help = "Whether package to sync is a new package in Ubuntu.") optParser.add_option("--lp", action = "store_true", dest = "lpbugs", default = False, help = "Specify whether to use the launchpadlib module for filing " \ "report.") optParser.add_option("-s", action = "store_true", dest = "sponsor", default = False, help = "Force sponsorship requirement (shall be autodetected if not " \ "specified).") optParser.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.") (options, args) = optParser.parse_args() newsource = options.newpkg sponsorship = options.sponsor keyid = options.keyid use_lp_bugs = options.lpbugs need_interaction = False distro = options.dist ffe = options.ffe if not use_lp_bugs and not get_email_address(): sys.exit(1) if len(args) == 0: optParser.print_help() sys.exit(1) if len(args) not in (2, 3): # no release specified, assume development release release = Distribution('ubuntu').getDevelopmentSeries().name print >> sys.stderr, ("Source package / target release missing - assuming %s " % release) else: release = args[1] srcpkg = args[0] force_base_ver = None # Base version specified. if len(args) == 3: force_base_ver = args[2] (cur_ver, component) = ('0', 'universe') # Let's assume universe # Find Ubuntu release's package version. if not newsource: (cur_ver, component) = cur_version_component(srcpkg, release) debsrcpkg = getDebianSrcPkg(srcpkg, distro) debiancomponent = debsrcpkg.getComponent() # Find Debian release's package version. deb_version = debsrcpkg.getVersion() # Debian and Ubuntu versions are the same - stop. if deb_version == cur_ver: print 'The versions in Debian and Ubuntu are the same already (%s). Aborting.' % (deb_version,) sys.exit(1) # -s flag not specified - check if we do need sponsorship. if (not newsource): if (not sponsorship): sponsorship = needsSponsorship(srcpkg, component) else: sponsorship = PersonTeam.getMe().isLpTeamMember('motu') # assume going to universe # Check for existing package reports. if not newsource and use_lp_bugs: checkExistingReports(srcpkg) # Generate bug report. subscribe = 'ubuntu-archive' status = 'confirmed' if sponsorship == True: status = 'new' if component in ['main', 'restricted']: subscribe = 'ubuntu-main-sponsors' else: subscribe = 'ubuntu-universe-sponsors' if ffe == True: status = 'new' if component in ['main', 'restricted']: subscribe = 'ubuntu-release' else: subscribe = 'motu-release' pkg_to_sync = '%s %s (%s) from Debian %s (%s).' % (srcpkg, deb_version, component, distro, debiancomponent) title = "Sync %s" % pkg_to_sync if ffe == True: title = "FFe: " + title report = "Please sync %s\n\n" % pkg_to_sync base_ver = cur_ver uidx = base_ver.find('ubuntu') if uidx > 0: base_ver = base_ver[:uidx] need_interaction = True print 'Changes have been made to the package in Ubuntu.' print 'Please edit the report and give an explanation.' print 'Press ENTER to start your editor. Press Control-C to abort now.' print 'Not saving the report file will abort the request, too.' raw_input_exit_on_ctrlc() report += 'Explanation of the Ubuntu delta and why it can be dropped:\n' + \ '>>> ENTER_EXPLANATION_HERE <<<\n\n' if ffe == True: need_interaction = True print 'To approve FeatureFreeze exception, you need to state ' print 'the reason why you feel it is necessary.' print 'Press ENTER to start your editor. Press Control-C to abort now.' print 'Not saving the report file will abort the request, too.' raw_input_exit_on_ctrlc() report += 'Explanation of FeatureFreeze exception:\n' + \ '>>> ENTER_EXPLANATION_HERE <<<\n\n' # Check if they have a per-package upload permission. if LpApiWrapper.isPerPackageUploader(srcpkg): report += 'Note that I have per-package upload permissions for %s.\n\n' % srcpkg uidx = base_ver.find('build') if uidx > 0: base_ver = base_ver[:uidx] if force_base_ver: base_ver = force_base_ver if not newsource: report += 'Changelog since current %s version %s:\n\n' % (release, cur_ver) changelog = getDebianChangelog(debsrcpkg, Version(base_ver)) if not changelog: sys.exit(1) report += changelog + '\n' (title, report) = edit_report(title, report, changes_required = need_interaction) # Post sync request using Launchpad interface: srcpkg = not newsource and srcpkg or None if use_lp_bugs: # Map status to the values expected by lp-bugs mapping = {'new': 'New', 'confirmed': 'Confirmed'} if postBug(srcpkg, subscribe, mapping[status], title, report): sys.exit(0) # Abort on error: print 'Something went wrong. No sync request filed.' sys.exit(1) # Mail sync request: if mail_bug(srcpkg, subscribe, status, title, report, keyid): sys.exit(0) print 'Something went wrong. No sync request filed.' sys.exit(1)