diff --git a/ack-sync b/ack-sync deleted file mode 100755 index b267806..0000000 --- a/ack-sync +++ /dev/null @@ -1,441 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007, Canonical Ltd. -# Copyright (C) 2010, Benjamin Drung -# Copyright (C) 2010, Stefano Rivera -# -# It was initial written by Daniel Holbach. -# -# 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. -# -# 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. -# -# See file /usr/share/common-licenses/GPL-3 for more details. - -import csv -import getopt -import lazr.restfulclient -import os -import re -import sys -import logging -import glob -import fnmatch - -from launchpadlib.launchpad import Launchpad - -from ubuntutools.config import UDTConfig -from ubuntutools import subprocess - -COMMAND_LINE_SYNTAX_ERROR = 1 -VERSION_DETECTION_FAILED = 2 -PRIVATE_USER_EMAIL = 3 -UPLOAD_NOT_PERMITTED = 4 - -def get_version(title): - m = re.search("[() ][0-9][0-9a-zA-Z.:+-~]*", title) - if m is None: - print >> sys.stderr, ("Version could not be detected. Please specify " - "it with -V.") - sys.exit(VERSION_DETECTION_FAILED) - return m.group(0).strip("() ") - -def strip_epoch(version): - parts = version.split(':') - if len(parts) > 1: - del parts[0] - version = ':'.join(parts) - return version - -def log_call(command): - command = map(str, command) - logging.info("Running %s", " ".join(command)) - return command - -def get_source(package, version, section, dist, uploader_name, uploader_email, - bug, key, upload): - if os.path.isdir("/tmpfs"): - workdir = "/tmpfs/ack-sync" - else: - workdir = "/tmp/ack-sync" - if not os.path.isdir(workdir): - os.makedirs(workdir) - os.chdir(workdir) - - cmd = ["syncpackage", package, "-r", dist, "-V", version, "-b", str(bug)] - if section is not None: - cmd += ["-c", section] - if uploader_email is not None: - cmd += ["-e", uploader_email] - if uploader_name is not None: - cmd += ["-n", uploader_name] - if not upload: - cmd += ['--dont-sign',] - if upload and key is not None: - cmd += ["-k", key] - subprocess.check_call(cmd) - - dsc_file = package + "_" + strip_epoch(version) + "fakesync1.dsc" - if not os.path.exists(os.path.join(workdir, dsc_file)): - dsc_file = package + "_" + strip_epoch(version) + ".dsc" - if not os.path.exists(os.path.join(workdir, dsc_file)): - print >> sys.stderr, ("E: Failed to find .dsc file created by " - "syncpackage.") - sys.exit(1) - return dsc_file - -def build_source(dist, dsc_file, pbuilder, sbuild): - try: - if sbuild: - cmd = ["sbuild", "-d", dist, "-A", dsc_file] - subprocess.check_call(log_call(cmd)) - else: - if not os.path.isdir("buildresult"): - os.makedirs("buildresult") - cmd = ["sudo", "-E", "DIST=" + dist, pbuilder, "--build", - "--buildresult", "buildresult", dsc_file] - subprocess.check_call(log_call(cmd)) - except subprocess.CalledProcessError: - print >> sys.stderr, "E: %s failed to build." % (dsc_file) - sys.exit(1) - -def test_install(dist, dsc_file, sbuild, lvm): - changes_files = glob.glob(os.path.splitext(dsc_file)[0]+"_*.changes") - changes_file = "" - - for temp_file in changes_files: - if not fnmatch.fnmatch(temp_file, '*_source.changes'): - changes_file = temp_file - - if not (os.path.isfile(changes_file)): # if no file exists at all => exit - print >> sys.stderr, "E: No .changes file has been generated." - sys.exit(1) - - try: - cmd = ["sudo", "piuparts", "-N", "-W", "--single-changes-list", - "--log-level=info", "--ignore=/var/log/apt/history.log", - "--mirror=http://archive.ubuntu.com/ubuntu main universe " - "restricted multiverse", changes_file] - if sbuild: - lvm_volume = lvm + "/" + dist + "_chroot" - subprocess.check_call(log_call(cmd + ["--lvm-volume="+lvm_volume])) - else: - subprocess.check_call(log_call(cmd + ["--pbuilder"])) - except subprocess.CalledProcessError: - print >> sys.stderr, "E: %s failed to install. Please check log" % \ - (changes_file) - -def get_email_from_file(name): - filename = os.path.expanduser("~/.ack-sync-email.list") - if os.path.isfile(filename): - csvfile = open(filename) - csv_reader = csv.reader(csvfile) - for row in csv_reader: - if row and row[0] == name: - return row[1] - return None - -def unsubscribe_sponsors(launchpad, bug): - us = launchpad.people['ubuntu-sponsors'] - bug.unsubscribe(person=us) - print "ubuntu-sponsors unsubscribed" - - -def ack_sync(bug_numbers, all_package, all_version, all_section, update, - all_uploader_email, key, upload, lpinstance, pbuilder, sbuild, lvm, - piuparts, verbose=False, silent=False): - launchpad = Launchpad.login_with("ubuntu-dev-tools", lpinstance) - # TODO: use release-info (once available) - series = launchpad.distributions["ubuntu"].current_series - dist = series.name - - # update pbuilder - if update: - if sbuild: - subprocess.call(log_call(["sbuild-update", dist])) - else: - cmd = ["sudo", "-E", "DIST=" + dist, pbuilder, "--update"] - subprocess.call(log_call(cmd)) - - for bug_number in bug_numbers: - bug = launchpad.bugs[bug_number] - uploader = bug.owner - uploader_name = uploader.display_name - if all_uploader_email is not None: - uploader_email = all_uploader_email - elif launchpad.people['ubuntumembers'] in uploader.super_teams: - uploader_email = uploader.name + '@ubuntu.com' - else: - try: - uploader_email = uploader.preferred_email_address.email - except ValueError: - uploader_email = get_email_from_file(uploader.name) - if uploader_email is None: - if not silent: - print >> sys.stderr, ("E: Bug owner '%s' does not have " - "a public email address. Specify " - "uploader with '-e'.") % \ - (uploader_name) - sys.exit(PRIVATE_USER_EMAIL) - elif not silent: - print "Taking email address from local file: " + \ - uploader_email - - # Try to find a Ubuntu bug task, against which the package is raised. - for t in bug.bug_tasks: - if t.bug_target_name.endswith(' (Ubuntu)'): - task = t - break - try: - print "Using Ubuntu bug task: %s" % task.bug_target_name - except NameError: - # We failed, use the first task (revert to previous behavior) - task = bug.bug_tasks[0] - print ("W: Could not find bug task for a Ubuntu package." - " Using task '%s'" % task.bug_target_name) - - if all_package is not None: - package = all_package - else: - package = task.bug_target_name.split(" ")[0] - if package == "ubuntu": - words = bug.title.split(" ") - # no source package was defined. Guessing that the second or - # third word in the title is the package name, because most - # titles start with "Please sync " or "Sync " - if words[0].lower() == "please": - package = words[2] - else: - package = words[1] - if all_version is not None: - version = all_version - else: - version = get_version(bug.title) - - src_pkg = series.getSourcePackage(name=package) - if src_pkg is None: - print "%s is NEW in %s." % (package, dist) - if src_pkg is not None: - # TODO: Port ack-sync to use lpapicache and reduce code-duplication. - can_upload = None - try: - series.main_archive.checkUpload( - component=src_pkg.latest_published_component_name, - distroseries=series, person=launchpad.me, pocket='Release', - sourcepackagename=package) - can_upload = True - except lazr.restfulclient.errors.HTTPError, e: - if e.response.status == 403: - can_upload = False - elif e.response.status == 400: - print ("W: Package is probably not in Ubuntu. Can't check " - "upload rights.") - can_upload = True - else: - raise e - if not can_upload: - print >> sys.stderr, ("E: Sorry, you are not allowed to upload " - 'package "%s" to %s.' % (package, dist)) - sys.exit(UPLOAD_NOT_PERMITTED) - - if task.assignee == None: - task.assignee = launchpad.me - print "assigned me" - task.lp_save() - if task.assignee != launchpad.me: - print >> sys.stderr, ("E: %s is already assigned to " - "https://launchpad.net/bugs/%i") % \ - (task.assignee.display_name, bug.id) - sys.exit(1) - old_status = task.status - task.status = 'In Progress' - unsubscribe_sponsors(launchpad, bug) - if task.importance == "Undecided": - task.importance = "Wishlist" - print "importance set to Wishlist" - task.lp_save() - - print "package:", package - print "version:", version - dsc_file = get_source(package, version, all_section, dist, - uploader_name, uploader_email, bug_number, key, - upload) - - if dsc_file.endswith('fakesync1.dsc'): - upload = True - - # extract source - env = os.environ - env['DEB_VENDOR'] = 'Ubuntu' - subprocess.check_call(["dpkg-source", "-x", dsc_file], env=env) - - build_source(dist, dsc_file, pbuilder, sbuild) - - if piuparts: - test_install(dist, dsc_file, sbuild, lvm) - - print bug.title - print '%s (was %s)' % (task.status, old_status) - print "Uploader:", uploader_name + " <" + uploader_email + ">" - if upload: - print ("Will upload sync directly, rather than subscribing " - "ubuntu-archive") - try: - raw_input('Press [Enter] to continue or [Ctrl-C] to abort.') - except KeyboardInterrupt: - continue - - bug.subscribe(person=launchpad.me) - print "subscribed me" - if upload: - task.status = 'Fix Committed' - task.assignee = None - print "unassigned me" - task.lp_save() - changes_file = dsc_file[:-4] + "_source.changes" - subprocess.check_call(["dput", "ubuntu", changes_file]) - else: - task.status = 'Confirmed' - task.assignee = None - print "unassigned me" - task.lp_save() - cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH"] - process = subprocess.Popen(cmd, stdout=subprocess.PIPE) - architecture = process.communicate()[0].strip() - content = "%s %s builds on %s. Sync request ACK'd." % \ - (package, version, architecture) - bug.newMessage(content=content, subject="ack-sync") - bug.subscribe(person=launchpad.people['ubuntu-archive']) - print "subscribed ubuntu-archive" - - -def usage(): - print """ack-sync - - -e, specify uploader email address - -h, --help displays this help - -k, --key key used to sign the package (in case of sponsoring) - --lpinstance= Launchpad instance to connect to - (default: production) - -l, --lvm lvm root dev directory, used for sbuild and piuparts - default is /dev/vg - --no-conf Don't read config files or environment variables - -p, --package= set the package - -P, --with-piuparts use piuparts to check the instalability - --section=
Debian section (one of main, contrib, non-free) - -s, --silent be more silent - -S, --with-sbuild use sbuild instead of pbuilder - -C, --pbuilder= use as pbuilder - -u, --update updates pbuilder before building - -U, --upload upload the sync immediately rather than ACK-ing it - for the archive admins - -v, --verbose be more verbosive - -V, --version= set the version""" - -def main(): - try: - long_opts = ["help", "key=", "lvm=", "package=", "section=", "silent", - "update", "upload", "verbose", "version=", "with-sbuild", - "pbuilder=", "with-piuparts", "lpinstance=", "no-conf"] - opts, args = getopt.gnu_getopt(sys.argv[1:], "e:hk:p:PsSC:uUvV:", - long_opts) - except getopt.GetoptError, e: - # will print something like "option -a not recognized" - print >> sys.stderr, str(e) - sys.exit(COMMAND_LINE_SYNTAX_ERROR) - - upload = False - package = None - sbuild = False - section = None - silent = False - update = False - uploader_email = None - verbose = False - version = None - piuparts = False - pbuilder = None - lvm = "/dev/vg" - key = None - lpinstance = None - no_conf = False - - for o, a in opts: - if o in ("-h", "--help"): - usage() - sys.exit() - elif o in ("-e"): - uploader_email = a - elif o in ("-k", "--key"): - key = a - elif o in ("--lpinstance"): - lpinstance = a - elif o in ("-l", "--lvm"): - lvm = a - elif o in ("--no-conf"): - no_conf = True - elif o in ("-p", "--package"): - package = a - elif o in ("-P", "--with-piuparts"): - piuparts = True - elif o in ("--section"): - section = a - elif o in ("-s", "--silent"): - silent = True - elif o in ("-S", "--with-sbuild"): - sbuild = True - elif o in ("-C", "--pbuilder"): - pbuilder = a - elif o in ("-u", "--update"): - update = True - elif o in ("-U", "--upload"): - upload = True - elif o in ("-v", "--verbose"): - verbose = True - elif o in ("-V", "--version"): - version = a - else: - assert False, "unhandled option" - - if len(args) == 0: - if not silent: - print >> sys.stderr, "E: You must specify at least one bug number." - sys.exit(COMMAND_LINE_SYNTAX_ERROR) - - bug_numbers = [] - for arg in args: - try: - number = int(arg) - except: - if not silent: - print >> sys.stderr, "E: '%s' is not a valid bug number." % arg - sys.exit(COMMAND_LINE_SYNTAX_ERROR) - bug_numbers.append(number) - - config = UDTConfig(no_conf) - if lpinstance is None: - lpinstance = config.get_value('LPINSTANCE') - if pbuilder is None and not sbuild: - builder = config.get_value('BUILDER') - if builder == 'pbuilder': - pbuilder = 'pbuilder' - elif builder == 'sbuild': - sbuild = True - else: - print >> sys.stderr, "E: Unsupported build-system: %s" % builder - sys.exit(COMMAND_LINE_SYNTAX_ERROR) - if not update: - update = config.get_value('UPDATE_BUILDER', boolean=True) - #TODO: Support WORKDIR - - ack_sync(bug_numbers, package, version, section, update, uploader_email, - key, upload, lpinstance, pbuilder, sbuild, lvm, piuparts, verbose, - silent) - -if __name__ == '__main__': - main() diff --git a/debian/changelog b/debian/changelog index 0019c9f..43ab057 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ ubuntu-dev-tools (0.132) UNRELEASED; urgency=low - * sponsor-patch: Refactor code. + * sponsor-patch: + - Refactor code. + - Support sync requests and make ack-sync obsolete (LP: #764763). -- Benjamin Drung Sat, 10 Sep 2011 19:58:38 +0200 diff --git a/debian/copyright b/debian/copyright index 813fb0b..c11a3ee 100644 --- a/debian/copyright +++ b/debian/copyright @@ -78,8 +78,7 @@ License: GPL-2+ On Debian systems, the complete text of the GNU General Public License version 2 can be found in the /usr/share/common-licenses/GPL-2 file. -Files: ack-sync - doc/bitesize.1 +Files: doc/bitesize.1 doc/grab-merge.1 doc/harvest.1 doc/hugdaylist.1 diff --git a/ubuntutools/sponsor_patch/source_package.py b/ubuntutools/sponsor_patch/source_package.py index 0255104..7f4b532 100644 --- a/ubuntutools/sponsor_patch/source_package.py +++ b/ubuntutools/sponsor_patch/source_package.py @@ -122,14 +122,15 @@ class SourcePackage(object): sys.exit(1) return True - def build(self, update): + def build(self, update, dist=None): """Tries to build the package. Returns true if the package was built successfully. Returns false if the user wants to change something. """ - dist = re.sub("-.*$", "", self._changelog.distributions) + if dist is None: + dist = re.sub("-.*$", "", self._changelog.distributions) build_name = self._package + "_" + strip_epoch(self._version) + \ "_" + self._builder.get_architecture() + ".build" self._build_log = os.path.join(self._buildresult, build_name) @@ -362,3 +363,22 @@ class SourcePackage(object): lintian_file.close() return lintian_filename + + def sync(self, upload, bug_number, keyid): + """Does a sync of the source package.""" + + if upload == "ubuntu": + cmd = ["syncpackage", self._package, "-b", str(bug_number), + "-V", str(self._version)] + if keyid is not None: + cmd += ["-k", keyid] + Logger.command(cmd) + if subprocess.call(cmd) != 0: + Logger.error("Syncing of %s %s failed.", self._package, + str(self._version)) + sys.exit(1) + else: + # FIXME: Support this use case! + Logger.error("Uploading a synced package other than to ubuntu " + "is not supported yet!") + sys.exit(1) diff --git a/ubuntutools/sponsor_patch/sponsor_patch.py b/ubuntutools/sponsor_patch/sponsor_patch.py index bfdfc49..818ebe5 100644 --- a/ubuntutools/sponsor_patch/sponsor_patch.py +++ b/ubuntutools/sponsor_patch/sponsor_patch.py @@ -24,11 +24,13 @@ import launchpadlib.launchpad from devscripts.logger import Logger +from distro_info import UbuntuDistroInfo + from ubuntutools import subprocess from ubuntutools.update_maintainer import update_maintainer from ubuntutools.question import input_number -from ubuntutools.sponsor_patch.bugtask import BugTask +from ubuntutools.sponsor_patch.bugtask import BugTask, is_sync from ubuntutools.sponsor_patch.patch import Patch from ubuntutools.sponsor_patch.question import ask_for_manual_fixing from ubuntutools.sponsor_patch.source_package import SourcePackage @@ -98,24 +100,26 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches): def get_patch_or_branch(bug): patch = None branch = None - attached_patches = [a for a in bug.attachments if a.type == "Patch"] - linked_branches = [b.branch for b in bug.linked_branches] - if len(attached_patches) == 0 and len(linked_branches) == 0: - if len(bug.attachments) == 0: - Logger.error(("No attachment and no linked branch found on " - "bug #%i.") % bug.id) + if not is_sync(bug): + attached_patches = [a for a in bug.attachments if a.type == "Patch"] + linked_branches = [b.branch for b in bug.linked_branches] + if len(attached_patches) == 0 and len(linked_branches) == 0: + if len(bug.attachments) == 0: + Logger.error("No attachment and no linked branch found on " + "bug #%i. Add the tag sync to the bug if it is " + "a sync request.", bug.id) + else: + Logger.error("No attached patch and no linked branch found. " + "Go to https://launchpad.net/bugs/%i and mark an " + "attachment as patch.", bug.id) + sys.exit(1) + elif len(attached_patches) == 1 and len(linked_branches) == 0: + patch = Patch(attached_patches[0]) + elif len(attached_patches) == 0 and len(linked_branches) == 1: + branch = linked_branches[0].bzr_identity else: - Logger.error(("No attached patch and no linked branch found. Go " - "to https://launchpad.net/bugs/%i and mark an " - "attachment as patch.") % bug.id) - sys.exit(1) - elif len(attached_patches) == 1 and len(linked_branches) == 0: - patch = Patch(attached_patches[0]) - elif len(attached_patches) == 0 and len(linked_branches) == 1: - branch = linked_branches[0].bzr_identity - else: - patch, branch = ask_for_patch_or_branch(bug, attached_patches, - linked_branches) + patch, branch = ask_for_patch_or_branch(bug, attached_patches, + linked_branches) return (patch, branch) def download_branch(branch): @@ -212,12 +216,21 @@ def _update_timestamp(): def _download_and_change_into(task, dsc_file, patch, branch): """Downloads the patch and branch and changes into the source directory.""" - if patch: - patch.download() + if branch: + branch_dir = download_branch(task.get_branch_link()) + + # change directory + Logger.command(["cd", branch_dir]) + os.chdir(branch_dir) + else: + if patch: + patch.download() Logger.info("Ubuntu package: %s" % (task.package)) if task.is_merge(): Logger.info("The task is a merge request.") + if task.is_sync(): + Logger.info("The task is a sync request.") extract_source(dsc_file, Logger.verbose) @@ -225,12 +238,6 @@ def _download_and_change_into(task, dsc_file, patch, branch): directory = task.package + '-' + task.get_version().upstream_version Logger.command(["cd", directory]) os.chdir(directory) - elif branch: - branch_dir = download_branch(task.get_branch_link()) - - # change directory - Logger.command(["cd", branch_dir]) - os.chdir(branch_dir) def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, upload, workdir): @@ -250,13 +257,27 @@ def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, _download_and_change_into(task, dsc_file, patch, branch) + source_package = SourcePackage(task.package, builder, workdir, branch) + + if is_sync(bug) and not edit: + successful = True + source_package.reload_changelog() + if build: + dist = UbuntuDistroInfo().devel() + successful = source_package.build(update, dist) + update = False + + if successful: + source_package.sync(upload, bug_number, keyid) + return + else: + edit = True + if patch: edit |= patch.apply(task) elif branch: edit |= merge_branch(branch) - source_package = SourcePackage(task.package, builder, workdir, branch) - while True: if edit: edit_source()