3
# (C) 2007 Canonical Ltd, Steve Kowalik
2
# -*- coding: utf-8 -*-
4
# (C) 2007 Canonical Ltd., Steve Kowalik
5
# Martin Pitt <martin.pitt@ubuntu.com>
6
# Steve Kowalik <stevenk@ubuntu.com>
7
# GPLv2, see /usr/share/common-licenses/GPL
9
import os, os.path, sys, urllib, subprocess, smtplib, getopt
13
def cur_version_component(sourcepkg, release):
14
madison = subprocess.Popen(['rmadison', '-u', 'ubuntu', '-a', 'source', \
15
'-s', release, sourcepkg], \
16
stdout=subprocess.PIPE)
17
out = madison.communicate()[0]
18
assert (madison.returncode == 0)
20
for l in out.splitlines():
21
(pkg, version, rel, builds) = l.split('|')
23
if rel.find('/') != -1:
24
component = rel.split('/')[1]
25
return (version.strip(), component.strip())
27
print "%s doesn't appear to exist in %s, specify -n for a package not in Ubuntu." % (sourcepkg, release)
30
def cur_deb_version(sourcepkg):
31
''' Return the current debian version of a package in unstable '''
32
madison = subprocess.Popen(['rmadison', '-u', 'debian', '-a', 'source', \
33
'-s', 'unstable', sourcepkg], \
34
stdout=subprocess.PIPE)
35
out = madison.communicate()[0]
36
assert (madison.returncode == 0)
40
except AssertionError:
41
print "%s doesn't appear to exist in Debian." % sourcepkg
44
return out.split('|')[1].rstrip('[]''').lstrip()
48
def debian_changelog(sourcepkg, component, version):
49
'''Return the Debian changelog from the latest up to the given version
54
if sourcepkg.startswith('lib'):
55
subdir = 'lib%s' % sourcepkg[3]
56
for l in urllib.urlopen('http://packages.debian.org/changelogs/pool/%s/%s/%s/current/changelog.txt' % (component, subdir, sourcepkg)):
57
if l.startswith(sourcepkg) and l.find(version + ')') > 0:
63
def debian_component(sourcepkg):
64
'''Return the Debian component for the source package.'''
65
madison = subprocess.Popen(['rmadison', '-a', 'source', '-s', 'unstable', \
66
sourcepkg], stdout=subprocess.PIPE)
67
out = madison.communicate()[0]
68
assert (madison.returncode == 0)
72
except AssertionError:
73
print "%s doesn't appear to exist in Debian." % sourcepkg
75
raw_comp = out.split('|')[2].split('/')
77
if len(raw_comp) == 2:
78
component = raw_comp[1]
82
print """Usage: requestsync [-n|-s|-k <keyid>] <source package> <target release> [basever]
84
In some cases, the base version (fork point from Debian) cannot be determined
85
automatically, and you'll get a complete Debian changelog. Specify the correct
86
base version of the package in Ubuntu."""
6
# Martin Pitt <martin.pitt@ubuntu.com>
7
# Steve Kowalik <stevenk@ubuntu.com>
8
# Michael Bienia <geser@ubuntu.com>
9
# Daniel Hahler <ubuntu@thequod.de>
10
# Iain Lane <laney@ubuntu.com>
11
# Jonathan Davies <jpds@ubuntu.com>
12
# Markus Korn <thekorn@gmx.de> (python-launchpadlib support)
14
# ##################################################################
16
# This program is free software; you can redistribute it and/or
17
# modify it under the terms of the GNU General Public License
18
# as published by the Free Software Foundation; version 2.
20
# This program is distributed in the hope that it will be useful,
21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
# GNU General Public License for more details.
25
# See file /usr/share/common-licenses/GPL-2 for more details.
27
# ##################################################################
30
from optparse import OptionParser
31
from debian_bundle.changelog import Version
33
# ubuntu-dev-tools modules
34
from ubuntutools.lp import udtexceptions
35
from ubuntutools.requestsync.common import *
37
import ubuntutools.common
97
opts, args = getopt.getopt(sys.argv[1:], 'nsk:')
98
except getopt.GetoptError:
108
if len(args) not in (2, 3):
111
(srcpkg, release) = args[:2]
112
force_base_ver = None
114
force_base_ver = args[2]
115
(cur_ver, component) = ('0', 'universe') # Let's assume universe
117
(cur_ver, component) = cur_version_component(srcpkg, release)
119
debiancomponent = debian_component(srcpkg)
121
# generate bug report
123
subscribe = "ubuntu-archive"
124
deb_version = cur_deb_version(srcpkg)
127
if component in ['main', 'restricted']:
128
subscribe = "ubuntu-main-sponsors"
130
subscribe = "ubuntu-universe-sponsors"
132
affects = '/%s' % srcpkg
136
report = ''' affects ubuntu%s
140
Please sync %s %s (%s) from Debian unstable (%s).
141
''' % (affects, status, subscribe, srcpkg, deb_version, component, debiancomponent)
144
uidx = base_ver.find('ubuntu')
146
base_ver = base_ver[:uidx]
148
print 'Explanation of the Ubuntu delta and why it can be dropped:'
149
explanation = '\nExplanation of the Ubuntu delta and why it can be dropped:\n'
150
while (explanation[-2:] != '\n\n'):
151
explanation += sys.stdin.readline()
152
report += explanation
155
uidx = base_ver.find('build')
157
base_ver = base_ver[:uidx]
160
base_ver = force_base_ver
162
report += 'Changelog since current %s version %s:\n\n' % (release, cur_ver)
163
report += debian_changelog(srcpkg, debiancomponent, base_ver) + '\n'
167
for cmd in ('gpg2', 'gnome-gpg'):
168
if os.access('/usr/bin/%s' % cmd, os.X_OK):
171
gpg_command = [sign_command, '--clearsign']
173
gpg_command.extend(('-u', keyid))
175
gpg = subprocess.Popen(gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176
signed_report = gpg.communicate(report)[0]
177
assert gpg.returncode == 0
180
myemailaddr = os.getenv('DEBEMAIL')
182
print "The environment variable DEBEMAIL needs to be set to make use of this script."
184
to = 'new@bugs.launchpad.net'
188
Subject: Please sync %s %s (%s) from Debian unstable (%s)
190
%s''' % (myemailaddr, to, srcpkg, deb_version, component, debiancomponent, signed_report)
194
print 'Press enter to file this bug, Control-C to abort'
199
mailserver = os.getenv('DEBSMTP')
201
print 'Using custom SMTP server:', mailserver
203
mailserver = 'fiordland.ubuntu.com'
206
mailserver_port = os.getenv('DEBSMTP_PORT')
208
print 'Using custom SMTP port:', mailserver_port
212
# connect to the server
213
s = smtplib.SMTP(mailserver, mailserver_port)
215
# authenticate to the server
216
mailserver_user = os.getenv('DEBSMTP_USER')
217
mailserver_pass = os.getenv('DEBSMTP_PASS')
218
if mailserver_user and mailserver_pass:
220
s.login(mailserver_user, mailserver_pass)
221
except smtplib.SMTPAuthenticationError:
222
print 'Error authenticating to the server: invalid username and password.'
223
s.quit(); sys.exit(1)
225
print 'Unknown SMTP error.'
226
s.quit(); sys.exit(1)
229
s.sendmail(myemailaddr, to, mail)
43
if __name__ == '__main__':
45
usage = 'Usage: %prog [-d distro] [-k keyid] [-n] [--lp] [-s] [-e] ' \
46
'<source package> [<target release> [base version]]'
47
optParser = OptionParser(usage)
49
optParser.add_option('-d', type = 'string',
50
dest = 'dist', default = 'testing',
51
help = 'Debian distribution to sync from.')
52
optParser.add_option('-k', type = 'string',
53
dest = 'keyid', default = None,
54
help = 'GnuPG key ID to use for signing report (only used when emailing the sync request).')
55
optParser.add_option('-n', action = 'store_true',
56
dest = 'newpkg', default = False,
57
help = 'Whether package to sync is a new package in Ubuntu.')
58
optParser.add_option('--lp', action = 'store_true',
59
dest = 'lpapi', default = False,
60
help = 'Specify whether to use the LP API for filing the sync request (recommended).')
61
optParser.add_option('-s', action = 'store_true',
62
dest = 'sponsorship', default = False,
63
help = 'Force sponsorship')
64
optParser.add_option('-e', action = 'store_true',
65
dest = 'ffe', default = False,
66
help = 'Use this after FeatureFreeze for non-bug fix syncs, changes ' \
67
'default subscription to the appropriate release team.')
69
(options, args) = optParser.parse_args()
72
optParser.print_help()
75
# import the needed requestsync module
77
from ubuntutools.requestsync.lp import *
78
from ubuntutools.lp.lpapicache import Distribution, PersonTeam
79
# See if we have LP credentials and exit if we don't - cannot continue in this case
85
from ubuntutools.requestsync.mail import *
86
if not getEmailAddress():
89
newsource = options.newpkg
90
sponsorship = options.sponsorship
94
need_interaction = False
95
force_base_version = None
100
release = Distribution('ubuntu').getDevelopmentSeries().name
101
print >> sys.stderr, 'W: Target release missing - assuming %s' % release
103
print >> sys.stderr, 'E: Source package or target release missing. Exiting.'
109
force_base_version = Version(args[2])
111
print >> sys.stderr, 'E: Too many arguments.'
112
optParser.print_help()
115
# Get the current Ubuntu source package
117
ubuntu_srcpkg = getUbuntuSrcPkg(srcpkg, release)
118
ubuntu_version = Version(ubuntu_srcpkg.getVersion())
119
ubuntu_component = ubuntu_srcpkg.getComponent()
120
newsource = False # override the -n flag
121
except udtexceptions.PackageNotFoundException:
123
ubuntu_version = Version(0)
124
ubuntu_component = 'universe' # let's assume universe
126
print "'%s' doesn't exist in 'Ubuntu %s'.\nDo you want to sync a new package?" % \
128
raw_input_exit_on_ctrlc('Press [Enter] to continue or [Ctrl-C] to abort. ')
131
# Get the requested Debian source package
133
debian_srcpkg = getDebianSrcPkg(srcpkg, distro)
134
debian_version = Version(debian_srcpkg.getVersion())
135
debian_component = debian_srcpkg.getComponent()
136
except udtexceptions.PackageNotFoundException, e:
137
print >> sys.stderr, "E: %s" % e
140
# Debian and Ubuntu versions are the same - stop
141
if ubuntu_version == debian_version:
142
print >> sys.stderr, \
143
'E: The versions in Debian and Ubuntu are the same already (%s). Aborting.' % ubuntu_version
146
# -s flag not specified - check if we do need sponsorship
148
sponsorship = needSponsorship(srcpkg, ubuntu_component)
150
# Check for existing package reports
152
checkExistingReports(srcpkg)
154
# Generate bug report
155
pkg_to_sync = '%s %s (%s) from Debian %s (%s)' % \
156
(srcpkg, debian_version, ubuntu_component, distro, debian_component)
157
title = "Sync %s" % pkg_to_sync
159
title = "FFe: " + title
160
report = "Please sync %s\n\n" % pkg_to_sync
162
if 'ubuntu' in str(ubuntu_version):
163
need_interaction = True
165
print 'Changes have been made to the package in Ubuntu.\n' \
166
'Please edit the report and give an explanation.\n' \
167
'Not saving the report file will abort the request.'
168
report += 'Explanation of the Ubuntu delta and why it can be dropped:\n' \
169
'>>> ENTER_EXPLANATION_HERE <<<\n\n'
172
need_interaction = True
174
print 'To approve FeatureFreeze exception, you need to state\n' \
175
'the reason why you feel it is necessary.\n' \
176
'Not saving the report file will abort the request.'
177
report += 'Explanation of FeatureFreeze exception:\n' \
178
'>>> ENTER_EXPLANATION_HERE <<<\n\n'
181
raw_input_exit_on_ctrlc('Press [Enter] to continue. Press [Ctrl-C] to abort now. ')
183
# Check if they have a per-package upload permission.
185
ubuntu_archive = Distribution('ubuntu').getArchive()
186
if PersonTeam.getMe().isPerPackageUploader(ubuntu_archive, srcpkg):
187
report += 'Note that I have per-package upload permissions for %s.\n\n' % srcpkg
189
base_version = force_base_version or ubuntu_version
192
report += 'All changelog entries:\n\n'
194
report += 'Changelog entries since current %s version %s:\n\n' % (release, ubuntu_version)
195
changelog = getDebianChangelog(debian_srcpkg, base_version)
197
print >> sys.stderr, "E: Did not retrieve any changelog entries. Was the package recently uploaded? (check http://packages.debian.org/changelogs/)"
201
(title, report) = edit_report(title, report, changes_required = need_interaction)
203
# bug status and bug subscriber
205
subscribe = 'ubuntu-archive'
208
if ubuntu_component in ('main', 'restricted'):
209
subscribe = 'ubuntu-main-sponsors'
211
subscribe = 'ubuntu-universe-sponsors'
214
if ubuntu_component in ('main', 'restricted'):
215
subscribe = 'ubuntu-release'
217
subscribe = 'motu-release'
219
srcpkg = not newsource and srcpkg or None
221
# Map status to the values expected by LP API
222
mapping = {'new': 'New', 'confirmed': 'Confirmed'}
223
# Post sync request using LP API
224
postBug(srcpkg, subscribe, mapping[status], title, report)
227
mailBug(srcpkg, subscribe, status, title, report, options.keyid)