You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
8.5 KiB

#!/usr/bin/python2.7
# Copyright (C) 2009, 2010, 2011, 2012 Canonical Ltd.
# Copyright (C) 2010 Scott Kitterman <scott@kitterman.com>
# Author: Martin Pitt <martin.pitt@canonical.com>
# 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 3 of the License.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
'''Show changes in an unapproved upload.
Generate a debdiff between current source package in a given release and the
version in the unapproved queue.
USAGE:
queuediff -s hardy -b hal | view -
'''
from __future__ import print_function
import gzip
import optparse
import re
import sys
try:
from urllib.parse import quote
from urllib.request import urlopen, urlretrieve
except ImportError:
from urllib import quote, urlopen, urlretrieve
import webbrowser
from launchpadlib.launchpad import Launchpad
default_release = 'focal'
lp = None
queue_url = 'https://launchpad.net/ubuntu/%s/+queue?queue_state=1&batch=300'
ppa_url = ('https://launchpad.net/~%s/+archive/ubuntu/%s/+packages?'
'field.series_filter=%s')
def parse_options():
'''Parse command line arguments.
Return (options, source_package) tuple.
'''
parser = optparse.OptionParser(
usage='Usage: %prog [options] source_package')
parser.add_option(
"-l", "--launchpad", dest="launchpad_instance", default="production")
parser.add_option(
"-s", dest="release", default=default_release, metavar="RELEASE",
help="release (default: %s)" % default_release)
parser.add_option(
"-p", dest="ppa", metavar="LP_USER/PPA_NAME",
help="Check a PPA instead of the Ubuntu unapproved queue")
parser.add_option(
"-b", "--browser", dest="browser", action="store_true",
default=False, help="Open Launchpad bugs in browser")
(opts, args) = parser.parse_args()
if len(args) != 1:
parser.error('Need to specify one source package name')
return (opts, args[0])
def parse_changes(changes_url):
'''Parse .changes file.
Return dictionary with interesting information: 'bugs' (list),
'distribution'.
'''
info = {'bugs': []}
for l in urlopen(changes_url):
if l.startswith('Distribution:'):
info['distribution'] = l.split()[1]
if l.startswith('Launchpad-Bugs-Fixed:'):
info['bugs'] = sorted(set(l.split()[1:]))
if l.startswith('Version:'):
info['version'] = l.split()[1]
return info
def from_queue(sourcepkg, release):
'''Get .changes and debdiff from queue page.
Return (changes URL, debdiff URL) pair.
'''
oops_re = re.compile('class="oopsid">(OOPS[a-zA-Z0-9-]+)<')
changes_re = re.compile(
'href="(http://launchpadlibrarian.net/\d+/%s_[^"]+_source.changes)"' %
re.escape(quote(sourcepkg)))
debdiff_re = re.compile(
'href="(http://launchpadlibrarian.net/'
'\d+/%s_[^"_]+_[^_"]+\.diff\.gz)">\s*diff from' %
re.escape(quote(sourcepkg)))
queue_html = urlopen(queue_url % release).read()
m = oops_re.search(queue_html)
if m:
print('ERROR: Launchpad failure:', m.group(1), file=sys.stderr)
sys.exit(1)
changes_url = None
for m in changes_re.finditer(queue_html):
# ensure that there's only one upload
if changes_url:
print('ERROR: Queue has more than one upload of this source, '
'please handle manually', file=sys.stderr)
sys.exit(1)
changes_url = m.group(1)
#print('changes URL:', changes_url, file=sys.stderr)
m = debdiff_re.search(queue_html)
if not m:
print('ERROR: queue does not have a debdiff', file=sys.stderr)
sys.exit(1)
debdiff_url = m.group(1)
#print('debdiff URL:', debdiff_url, file=sys.stderr)
return (changes_url, debdiff_url)
def from_ppa(sourcepkg, release, user, ppaname):
'''Get .changes and debdiff from a PPA.
Return (changes URL, debdiff URL) pair.
'''
changes_re = re.compile(
'href="(https://launchpad.net/[^ "]+/%s_[^"]+_source.changes)"' %
re.escape(quote(sourcepkg)))
sourcepub_re = re.compile(
'href="(\+sourcepub/\d+/\+listing-archive-extra)"')
#debdiff_re = re.compile(
# 'href="(https://launchpad.net/.[^ "]+.diff.gz)">diff from')
changes_url = None
changes_sourcepub = None
last_sourcepub = None
for line in urlopen(ppa_url % (user, ppaname, release)):
m = sourcepub_re.search(line)
if m:
last_sourcepub = m.group(1)
continue
m = changes_re.search(line)
if m:
# ensure that there's only one upload
if changes_url:
print('ERROR: PPA has more than one upload of this source, '
'please handle manually', file=sys.stderr)
sys.exit(1)
changes_url = m.group(1)
assert changes_sourcepub is None, (
'got two sourcepubs before .changes')
changes_sourcepub = last_sourcepub
#print('changes URL:', changes_url, file=sys.stderr)
# the code below works, but the debdiffs generated by Launchpad are rather
# useless, as they are against the final version, not what is in
# -updates/-security; so disable
## now open the sourcepub and get the URL for the debdiff
#changes_sourcepub = changes_url.rsplit('+', 1)[0] + changes_sourcepub
##print('sourcepub URL:', changes_sourcepub, file=sys.stderr)
#sourcepub_html = urlopen(changes_sourcepub).read()
#m = debdiff_re.search(sourcepub_html)
#if not m:
# print('ERROR: PPA does not have a debdiff', file=sys.stderr)
# sys.exit(1)
#debdiff_url = m.group(1)
##print('debdiff URL:', debdiff_url, file=sys.stderr)
debdiff_url = None
return (changes_url, debdiff_url)
#
# main
#
(opts, sourcepkg) = parse_options()
if opts.ppa:
(user, ppaname) = opts.ppa.split('/', 1)
(changes_url, debdiff_url) = from_ppa(
sourcepkg, opts.release, user, ppaname)
else:
(changes_url, debdiff_url) = from_queue(sourcepkg, opts.release)
# print diff
if debdiff_url:
print(gzip.open(urlretrieve(debdiff_url)[0]).read())
else:
print('No debdiff available')
# parse changes and open bugs
changes = parse_changes(changes_url)
if opts.browser:
for b in changes['bugs']:
webbrowser.open('https://bugs.launchpad.net/bugs/' + b)
# print matching sru-accept command
if changes['bugs']:
# Check for existing version in proposed
lp = Launchpad.login_anonymously('queuediff', opts.launchpad_instance)
ubuntu = lp.distributions['ubuntu']
series = ubuntu.getSeries(name_or_version=opts.release)
if series != ubuntu.current_series:
archive = ubuntu.main_archive
existing = [
pkg.source_package_version for pkg in archive.getPublishedSources(
exact_match=True, distro_series=series, pocket='Proposed',
source_name=sourcepkg, status='Published')]
updates = [
pkg.source_package_version for pkg in archive.getPublishedSources(
exact_match=True, distro_series=series, pocket='Updates',
source_name=sourcepkg, status='Published')]
for pkg in existing:
if pkg not in updates:
msg = '''\
*******************************************************
*
* WARNING: %s already published in Proposed (%s)
*
*******************************************************''' % (sourcepkg, pkg)
# show it in the diff as well as in the terminal
print(msg)
print(msg, file=sys.stderr)
print('''After accepting this SRU from the queue, run:
sru-accept -v %s -s %s -p %s %s''' %
(changes['version'], changes['distribution'].split('-')[0],
sourcepkg, ' '.join(changes['bugs'])), file=sys.stderr)
# for PPAs, print matching copy command
if opts.ppa:
print('\nTo copy from PPA to distribution, run:\n'
' copy-package -b --from=~%s/ubuntu/%s -s %s --to=ubuntu '
'--to-suite %s-proposed -y %s\n' %
(user, ppaname, opts.release, opts.release, sourcepkg),
file=sys.stderr)