#! /usr/bin/python3 # Copyright (C) 2012 Canonical Ltd. # Author: Colin Watson # 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 . """Copy package publication records.""" from __future__ import print_function import argparse import sys from launchpadlib.errors import HTTPError from launchpadlib.launchpad import Launchpad try: from ubuntutools.question import YesNoQuestion except ImportError: print("No ubuntutools installed: sudo apt-get install ubuntu-dev-tools") exit() import lputils def find_publications(args, package): source = lputils.find_latest_published_source(args, package) yield source, source.source_package_version if args.include_binaries: for binary in source.getPublishedBinaries(): yield binary, binary.binary_package_version def copy_packages(args): ret = True for package in args.packages: print("Copy candidates:") try: source = lputils.find_latest_published_source(args, package) except lputils.PackageMissing as error: print(error) if args.skip_missing: print('Skipping') continue else: # Bail with exit code non-zero. return False print("\t%s" % source.display_name) num_copies = 1 if args.include_binaries: for binary in source.getPublishedBinaries(): print("\t%s" % binary.display_name) num_copies += 1 print("Candidate copy target: %s" % args.destination.archive) if args.sponsoree: print("Sponsored for: %s" % args.sponsoree) if args.dry_run: print("Dry run; no packages copied.") else: if not args.confirm_all: if YesNoQuestion().ask("Copy", "no") == "no": continue try: args.destination.archive.copyPackage( source_name=package, version=source.source_package_version, from_archive=args.archive, from_series=args.series.name, from_pocket=args.pocket, to_series=args.destination.series.name, to_pocket=args.destination.pocket, include_binaries=args.include_binaries, unembargo=args.unembargo, auto_approve=args.auto_approve, silent=args.silent, sponsored=args.sponsoree) print("%d %s requested." % ( num_copies, "copy" if num_copies == 1 else "copies")) except HTTPError as e: print(e.content, file=sys.stderr) ret = False return ret def main(): parser = argparse.ArgumentParser( epilog=lputils.ARCHIVE_REFERENCE_DESCRIPTION) parser.add_argument( "-l", "--launchpad", dest="launchpad_instance", default="production") parser.add_argument( "-n", "--dry-run", default=False, action="store_true", help="only show copies that would be performed") parser.add_argument( "-y", "--confirm-all", default=False, action="store_true", help="do not ask for confirmation") parser.add_argument( "--from", metavar="ARCHIVE", dest="archive", help="copy from ARCHIVE (default: ubuntu)") parser.add_argument( "-s", "--suite", "--from-suite", metavar="SUITE", help="copy from SUITE (default: development release pocket)") parser.add_argument( "--to", metavar="ARCHIVE", help="copy to ARCHIVE (default: copy from archive)") parser.add_argument( "--to-suite", metavar="SUITE", help="copy to SUITE (default: copy from suite)") parser.add_argument( "-e", "--version", metavar="VERSION", help="package version (default: current version)") parser.add_argument( "-b", "--include-binaries", default=False, action="store_true", help="copy related binaries") parser.add_argument( "--unembargo", default=False, action="store_true", help="allow copying from a private archive to a public archive") parser.add_argument( "--auto-approve", default=False, action="store_true", help="automatically approve copy (requires queue admin permissions)") parser.add_argument( "--silent", default=False, action="store_true", help="suppress mail notifications (requires queue admin permissions)") parser.add_argument( "--force-same-destination", default=False, action="store_true", help=( "force copy when source == destination (e.g. when reverting to " "a previous version in the same suite)")) parser.add_argument( "--skip-missing", default=False, action="store_true", help=( "When a package cannot be copied, normally this script exits " "with a non-zero status. With --skip-missing instead, the " "error is printed and copying continues")) parser.add_argument( "--sponsor", metavar="USERNAME", dest="sponsoree", default=None, help="Sponsor the sync for USERNAME (a Launchpad username).") # Deprecated in favour of --to and --from. parser.add_argument( "-d", "--distribution", default="ubuntu", help=argparse.SUPPRESS) parser.add_argument("-p", "--ppa", help=argparse.SUPPRESS) parser.add_argument("--ppa-name", help=argparse.SUPPRESS) parser.add_argument( "-j", "--partner", default=False, action="store_true", help=argparse.SUPPRESS) parser.add_argument( "--to-primary", default=False, action="store_true", help=argparse.SUPPRESS) parser.add_argument("--to-distribution", help=argparse.SUPPRESS) parser.add_argument("--to-ppa", help=argparse.SUPPRESS) parser.add_argument("--to-ppa-name", help=argparse.SUPPRESS) parser.add_argument( "--to-partner", default=False, action="store_true", help=argparse.SUPPRESS) parser.add_argument( "packages", metavar="package", nargs="+", help="name of package to copy") args = parser.parse_args() args.launchpad = Launchpad.login_with( "copy-package", args.launchpad_instance, version="devel") args.destination = argparse.Namespace() args.destination.launchpad = args.launchpad args.destination.suite = args.to_suite or args.suite if args.archive or args.to: # Use modern single-option archive references. if ((args.distribution and args.distribution != u'ubuntu') or args.ppa or args.ppa_name or args.partner or args.to_distribution or args.to_ppa or args.to_ppa_name or args.to_partner): parser.error( "cannot use --to/--from and the deprecated archive selection " "options together") args.destination.archive = args.to or args.archive else: # Use the deprecated four-option archive specifiers. if args.ppa and args.partner: parser.error( "cannot copy from partner archive and PPA simultaneously") if args.to_ppa and args.to_partner: parser.error( "cannot copy to partner archive and PPA simultaneously") args.destination.distribution = ( args.to_distribution or args.distribution) args.destination.ppa = args.to_ppa args.destination.ppa_name = args.to_ppa_name args.destination.partner = args.to_partner # In cases where source is specified, but destination is not, # default to destination = source if (args.ppa is not None and args.to_ppa is None and not args.to_primary and not args.destination.partner): args.destination.ppa = args.ppa if (args.ppa_name is not None and args.to_ppa_name is None and args.destination.ppa is not None): args.destination.ppa_name = args.ppa_name if (args.partner and not args.destination.partner and not args.ppa): args.destination.partner = args.partner if args.to_primary and args.to_ppa_name is not None: parser.error( "--to-ppa-name option set for copy to primary archive") lputils.setup_location(args) lputils.setup_location(args.destination) if args.archive.private and not args.destination.archive.private: if not args.unembargo: parser.error( "copying from a private archive to a public archive requires " "the --unembargo option") # TODO some equivalent of canModifySuite check? if (not args.force_same_destination and args.distribution == args.destination.distribution and args.suite == args.destination.suite and args.pocket == args.destination.pocket and args.archive.reference == args.destination.archive.reference): parser.error("copy destination must differ from source") if args.sponsoree: try: args.sponsoree = args.launchpad.people[args.sponsoree] except KeyError: parser.error( "Person to sponsor for not found: %s" % args.sponsoree) if copy_packages(args): return 0 else: return 1 if __name__ == '__main__': sys.exit(main())