|
|
|
#! /usr/bin/python3
|
|
|
|
|
|
|
|
# Copyright (C) 2012 Canonical Ltd.
|
|
|
|
# Author: Colin Watson <cjwatson@ubuntu.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/>.
|
|
|
|
|
|
|
|
"""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())
|