# -*- coding: utf-8 -*- """ @author: Fabio Erculiani @contact: lxnay@sabayon.org @copyright: Fabio Erculiani @license: GPL-2 B{Entropy Infrastructure Toolkit}. """ import sys import os import errno import argparse import tempfile import codecs import time from entropy.i18n import _ from entropy.output import purple, teal from eit.commands.descriptor import EitCommandDescriptor from eit.commands.command import EitCommand class EitPkgmove(EitCommand): """ Main Eit pkgmove command. """ NAME = "pkgmove" ALIASES = [] def __init__(self, args): EitCommand.__init__(self, args) self._repository_id = None def _get_parser(self): """ Overridden from EitCommand """ descriptor = EitCommandDescriptor.obtain_descriptor( EitPkgmove.NAME) parser = argparse.ArgumentParser( description=descriptor.get_description(), formatter_class=argparse.RawDescriptionHelpFormatter, prog="%s %s" % (sys.argv[0], EitPkgmove.NAME)) parser.add_argument("repo", default=None, metavar="", help=_("repository")) return parser def parse(self): parser = self._get_parser() try: nsargs = parser.parse_args(self._args) except IOError: return parser.print_help, [] self._repository_id = nsargs.repo return self._call_locked, [self._pkgmove, self._repository_id] def _pkgmove(self, entropy_server): """ Open $EDITOR and let user add/remove package moves. """ notice_text = """\ # This is the package move metadata for repository %s, read this: # - the statements must start with either "move" or "slotmove". # - move statement syntax: # move # - slotmove statement syntax: # slotmove # - the order of the statements is taken into consideration (KEPT!). # - lines not starting with "move" or "slotmove" will be ignored. # - any line starting with "#" will be ignored as well. # # Example: # move app-foo/bar app-bar/foo # slotmove >=x11-libs/foo-2.0 0 2.0 """ % (self._repository_id,) tmp_path = None repo = entropy_server.open_server_repository( self._repository_id, read_only=False) actions = repo.retrieveTreeUpdatesActions(self._repository_id) new_actions = [] while True: if tmp_path is None: tmp_fd, tmp_path = tempfile.mkstemp( prefix = 'entropy.server.pkgmove', suffix = ".conf") with os.fdopen(tmp_fd, "w") as tmp_f: tmp_f.write(notice_text) for action in actions: tmp_f.write(action + "\n") tmp_f.flush() success = entropy_server.edit_file(tmp_path) if not success: # retry ? os.remove(tmp_path) return 1 del new_actions[:] invalid_lines = [] with codecs.open(tmp_path, "r", encoding="utf-8") as tmp_f: for line in tmp_f.readlines(): if line.startswith("#"): # skip continue strip_line = line.strip() if not strip_line: # ignore continue if strip_line.startswith("move"): split_line = strip_line.split() if len(split_line) != 3: entropy_server.output( "%s: %s !!!" % (_("invalid line"), strip_line), importance=1, level="warning") invalid_lines.append(strip_line) else: new_actions.append(strip_line) elif strip_line.startswith("slotmove"): split_line = strip_line.split() if len(split_line) != 4: entropy_server.output( "%s: %s !!!" % ( purple(_("invalid line")), strip_line), importance=1, level="warning") invalid_lines.append(strip_line) else: new_actions.append(strip_line) else: entropy_server.output( "%s: %s !!!" % ( purple(_("invalid line")), strip_line), importance=1, level="warning") invalid_lines.append(strip_line) if invalid_lines: resp = entropy_server.ask_question( _("Invalid syntax, what to do ?"), responses=(_("Repeat"), _("Abort"))) if resp == _("Abort"): os.remove(tmp_path) return 1 else: # repeat, edit same file continue # show submitted info for action in new_actions: entropy_server.output(action, level="generic") entropy_server.output("", level="generic") # ask confirmation while True: try: rc_question = entropy_server.ask_question( "[%s] %s" % ( purple(self._repository_id), teal(_("Do you agree?")) ), responses = (_("Yes"), _("Repeat"), _("No"),) ) except KeyboardInterrupt: # do not allow, we're in a critical region continue break if rc_question == _("Yes"): break elif rc_question == _("No"): return 1 # otherwise repeat everything again # keep tmp_path if tmp_path is not None: try: os.remove(tmp_path) except (OSError) as err: if err.errno != errno.ENOENT: raise # write new actions branch = self._settings()['repositories']['branch'] actions_meta = [] cur_t = time.time() # time is completely fake, no particular precision required for action in new_actions: cur_t += 1 actions_meta.append((action, branch, str(cur_t))) repo.removeTreeUpdatesActions(self._repository_id) try: repo.insertTreeUpdatesActions(actions_meta, self._repository_id) except Exception as err: repo.rollback() raise repo.commit() return 0 EitCommandDescriptor.register( EitCommandDescriptor( EitPkgmove, EitPkgmove.NAME, _('edit automatic package moves for repository')) )