21
21
# ##################################################################
24
import debian.debian_support
32
import debian.debian_support
30
33
from devscripts.logger import Logger
34
from lazr.restfulclient.errors import HTTPError
32
36
from ubuntutools.archive import (DebianSourcePackage, UbuntuSourcePackage,
34
38
from ubuntutools.config import UDTConfig, ubu_email
39
from ubuntutools.requestsync.common import getDebianChangelog
35
40
from ubuntutools.requestsync.mail import (getDebianSrcPkg
36
41
as requestsync_mail_getDebianSrcPkg)
37
42
from ubuntutools.requestsync.lp import getDebianSrcPkg, getUbuntuSrcPkg
38
43
from ubuntutools.lp import udtexceptions
39
from ubuntutools.lp.lpapicache import Launchpad
44
from ubuntutools.lp.lpapicache import (Distribution, Launchpad,
45
SourcePackagePublishingHistory)
46
from ubuntutools.misc import split_release_pocket
47
from ubuntutools.question import YesNoQuestion
40
48
from ubuntutools import subprocess
290
309
assert component in ('main', 'contrib', 'non-free')
292
311
return DebianSourcePackage(package, version.full_version, component,
314
def copy(src_pkg, release, bugs, simulate=False, force=False):
315
"""Copy a source package from Debian to Ubuntu using the Launchpad API."""
316
ubuntu = Distribution('ubuntu')
317
debian_archive = Distribution('debian').getArchive()
318
ubuntu_archive = ubuntu.getArchive()
320
ubuntu_series = ubuntu.getDevelopmentSeries().name
321
ubuntu_pocket = 'Release'
323
ubuntu_series, ubuntu_pocket = split_release_pocket(release)
325
# Ensure that the provided Debian version actually exists.
327
debian_spph = SourcePackagePublishingHistory(
328
debian_archive.getPublishedSources(
329
source_name=src_pkg.source,
330
version=src_pkg.version.full_version,
334
Logger.error('Debian version %s does not exist!', src_pkg.version)
338
ubuntu_spph = getUbuntuSrcPkg(src_pkg.source,
339
ubuntu_series, ubuntu_pocket)
340
ubuntu_pkg = UbuntuSourcePackage(src_pkg.source,
341
ubuntu_spph.getVersion(),
342
ubuntu_spph.getComponent(),
345
Logger.normal('Source %s -> %s/%s: current version %s, new version %s',
346
src_pkg.source, ubuntu_series, ubuntu_pocket,
347
ubuntu_pkg.version, src_pkg.version)
349
ubuntu_version = Version(ubuntu_pkg.version.full_version)
350
base_version = ubuntu_version.get_related_debian_version()
351
if not force and ubuntu_version.is_modified_in_ubuntu():
352
Logger.error('--force is required to discard Ubuntu changes.')
355
# Check whether a fakesync would be required.
357
ubuntu_pkg.pull_dsc()
358
if not src_pkg.dsc.compare_dsc(ubuntu_pkg.dsc):
359
Logger.error('The checksums of the Debian and Ubuntu packages '
360
'mismatch. A fake sync using --no-lp is required.')
362
except udtexceptions.PackageNotFoundException:
363
base_version = Version('~')
364
Logger.normal('Source %s -> %s/%s: not in Ubuntu, new version %s',
365
src_pkg.source, ubuntu_series, ubuntu_pocket,
368
changes = getDebianChangelog(debian_spph, base_version).strip()
370
Logger.normal("New changes:\n%s", changes)
375
answer = YesNoQuestion().ask("Sync this package", "no")
380
ubuntu_archive.copyPackage(
381
source_name=src_pkg.source,
382
version=src_pkg.version.full_version,
383
from_archive=debian_archive,
384
to_series=ubuntu_series,
385
to_pocket=ubuntu_pocket,
386
include_binaries=False)
387
except HTTPError, error:
388
Logger.error("HTTP Error %s: %s", error.response.status,
389
error.response.reason)
390
Logger.error(error.content)
393
Logger.normal('Request succeeded; you should get an e-mail once it is '
395
bugs = sorted(set(bugs))
397
Logger.normal("Launchpad bugs to be closed: %s",
398
', '.join(str(bug) for bug in bugs))
399
Logger.normal('Please wait for the sync to be successful before '
401
answer = YesNoQuestion().ask("Close bugs", "yes")
403
close_bugs(bugs, src_pkg.source, src_pkg.version.full_version,
406
def is_blacklisted(query):
407
""""Determine if package "query" is in the sync blacklist
408
Returns tuple of (blacklisted, comments)
409
blacklisted is one of False, 'CURRENT', 'ALWAYS'
411
series = Launchpad.distributions['ubuntu'].current_series
412
lp_comments = series.getDifferenceComments(source_package_name=query)
414
comments = [c.body_text + u' -- ' + c.comment_author.name
415
for c in lp_comments]
417
diff = series.getDifferencesTo(source_package_name_filter=query)[0]
418
if diff.status == 'Blacklisted current version':
419
blacklisted = 'CURRENT'
420
if diff.status == 'Blacklisted always':
421
blacklisted = 'ALWAYS'
424
url = 'http://people.canonical.com/~ubuntu-archive/sync-blacklist.txt'
425
with codecs.EncodedFile(urllib.urlopen(url), 'UTF-8') as f:
426
applicable_comments = []
429
applicable_comments = []
431
m = re.match(r'^\s*([a-z0-9.+-]+)?\s*(?:#\s*(.+)?)?$', line)
432
source, comment = m.groups()
433
if source and query == source:
435
applicable_comments.append(comment)
436
comments += applicable_comments
437
blacklisted = 'ALWAYS'
440
applicable_comments.append(comment)
442
return (blacklisted, comments)
444
def close_bugs(bugs, package, version, changes):
445
"""Close the correct task on all bugs, with changes"""
446
ubuntu = Launchpad.distributions['ubuntu']
447
message = ("This bug was fixed in the package %s - %s"
448
"\n\n---------------\n%s" % (
449
package, version, changes))
451
bug = Launchpad.bugs[bug]
452
if bug.duplicate_of is not None:
453
bug = bug.duplicate_of
454
for task in bug.bug_tasks:
456
if target == ubuntu or (target.name == package and
457
getattr(target, 'distribution', None) == ubuntu):
458
if task.status != 'Fix Released':
459
Logger.normal("Closed bug %s", task.web_link)
460
task.status = 'Fix Released'
462
bug.newMessage(content=message)
465
Logger.error(u"Cannot find any tasks on LP: #%i to close.", bug.id)
296
468
usage = "%prog [options] <.dsc URL/path or package name>"
312
484
parser.add_option("-v", "--verbose",
313
485
dest="verbose", action="store_true", default=False,
314
486
help="Display more progress information.")
487
parser.add_option("--no-lp",
488
dest="lp", action="store_false", default=True,
489
help="Construct sync locally rather than letting "
490
"Launchpad copy the package directly (not "
492
parser.add_option('-l', '--lpinstance', metavar='INSTANCE',
493
dest='lpinstance', default=None,
494
help='Launchpad instance to connect to '
495
'(default: production).')
315
496
parser.add_option("-n", "--uploader-name",
316
497
dest="uploader_name", default=None,
317
498
help="Use UPLOADER_NAME as the name of the maintainer "
352
541
parser.error('Multiple .dsc URLs/paths or package names specified: '
353
542
+ ', '.join(args))
355
invalid_bug_numbers = [bug for bug in options.bugs if not bug.isdigit()]
356
if len(invalid_bug_numbers) > 0:
357
parser.error('Invalid bug number(s) specified: '
358
+ ', '.join(invalid_bug_numbers))
545
options.bugs = map(int, options.bugs)
547
parser.error('Invalid bug number(s) specified.')
360
549
if options.component not in (None, "main", "contrib", "non-free"):
361
550
parser.error('%s is not a valid Debian component. '
362
551
'It should be one of main, contrib, or non-free.'
363
552
% options.component)
554
if options.lp and options.uploader_name:
555
parser.error('Uploader name can only be overridden using --no-lp.')
556
if options.lp and options.uploader_email:
557
parser.error('Uploader email address can only be overridden using '
559
# --key, --dont-sign, --debian-mirror, and --ubuntu-mirror are just
560
# ignored with options.lp, and do not require warnings.
365
562
Logger.verbose = options.verbose
366
563
config = UDTConfig(options.no_conf)
367
564
if options.debian_mirror is None:
373
570
if options.uploader_email is None:
374
571
options.uploader_email = ubu_email(export=False)[1]
376
Launchpad.login_anonymously()
574
if args[0].endswith('.dsc'):
575
parser.error('.dsc files can only be synced using --no-lp.')
577
if options.lpinstance is None:
578
options.lpinstance = config.get_value('LPINSTANCE')
580
Launchpad.login(service=options.lpinstance, api_version='devel')
377
584
if options.release is None:
378
options.release = Launchpad.distributions["ubuntu"].current_series.name
380
os.environ['DEB_VENDOR'] = 'Ubuntu'
585
ubuntu = Launchpad.distributions["ubuntu"]
586
options.release = ubuntu.current_series.name
382
588
src_pkg = fetch_source_pkg(args[0], options.dist, options.debversion,
383
589
options.component, options.release,
384
590
options.debian_mirror)
386
sync_dsc(src_pkg, options.dist, options.release, options.uploader_name,
387
options.uploader_email, options.bugs, options.ubuntu_mirror,
592
blacklisted, comments = is_blacklisted(src_pkg.source)
597
if blacklisted == 'CURRENT':
599
messages += ["Forcing override of temporary blacklising."]
602
messages += ["The blacklisting only applies to the current "
603
"versions in Debian and Ubuntu.",
604
"--force is required to override the blacklist."]
606
if options.force and not options.lp:
607
messages += ["Forcing fake-sync, overriding blacklist."]
610
messages += ["--force and --no-lp are required to override the "
611
"blacklist, if this package needs a fakesync."]
612
messages += ["If you think this package shouldn't be blacklisted, "
613
"please file a bug explaining your reasoning and "
614
"subscribe ~ubuntu-archive."]
617
Logger.error(u"Source package %s is blacklisted.", src_pkg.source)
619
Logger.normal(u"Source package %s is blacklisted.", src_pkg.source)
622
Logger.normal("Blacklist Comments:")
623
for comment in comments:
624
for line in textwrap.wrap(comment):
625
Logger.normal(u" " + line)
627
for message in messages:
628
for line in textwrap.wrap(message):
634
copy(src_pkg, options.release, options.bugs, options.simulate,
637
os.environ['DEB_VENDOR'] = 'Ubuntu'
638
sync_dsc(src_pkg, options.dist, options.release, options.uploader_name,
639
options.uploader_email, options.bugs, options.ubuntu_mirror,
640
options.keyid, options.simulate, options.force)
390
642
if __name__ == "__main__":