# # patch.py - Internal helper class for sponsor-patch # # Copyright (C) 2010-2011, Benjamin Drung # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import logging import os import re import subprocess from functools import reduce from ubuntutools.sponsor_patch.question import ask_for_manual_fixing Logger = logging.getLogger(__name__) class Patch: """This object represents a patch that can be downloaded from Launchpad.""" def __init__(self, patch): self._patch = patch self._patch_file = re.sub(" |/", "_", patch.title) if not reduce( lambda r, x: r or self._patch.title.endswith(x), (".debdiff", ".diff", ".patch"), False ): Logger.debug("Patch %s does not have a proper file extension.", self._patch.title) self._patch_file += ".patch" self._full_path = os.path.realpath(self._patch_file) self._changed_files = None def apply(self, task): """Applies the patch in the current directory.""" assert self._changed_files is not None, "You forgot to download the patch." edit = False if self.is_debdiff(): cmd = [ "patch", "--merge", "--force", "-p", str(self.get_strip_level()), "-i", self._full_path, ] Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.error( "Failed to apply debdiff %s to %s %s.", self._patch_file, task.package, task.get_version(), ) if not edit: ask_for_manual_fixing() edit = True else: cmd = ["add-patch", self._full_path] Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.error( "Failed to apply diff %s to %s %s.", self._patch_file, task.package, task.get_version(), ) if not edit: ask_for_manual_fixing() edit = True return edit def download(self): """Downloads the patch from Launchpad.""" Logger.debug("Downloading %s.", self._patch_file) patch_f = open(self._patch_file, "wb") patch_f.write(self._patch.data.open().read()) patch_f.close() cmd = ["diffstat", "-l", "-p0", self._full_path] changed_files = subprocess.check_output(cmd, encoding="utf-8") self._changed_files = [f for f in changed_files.split("\n") if f != ""] def get_strip_level(self): """Returns the stript level for the patch.""" assert self._changed_files is not None, "You forgot to download the patch." strip_level = None if self.is_debdiff(): changelog = [f for f in self._changed_files if f.endswith("debian/changelog")][0] strip_level = len(changelog.split(os.sep)) - 2 return strip_level def is_debdiff(self): """Checks if the patch is a debdiff (= modifies debian/changelog).""" assert self._changed_files is not None, "You forgot to download the patch." return len([f for f in self._changed_files if f.endswith("debian/changelog")]) > 0