Pull more functions out of repoman proper and into repoman.utilities
authorAlec Warner <antarus@gentoo.org>
Sun, 27 Jan 2008 11:26:01 +0000 (11:26 -0000)
committerAlec Warner <antarus@gentoo.org>
Sun, 27 Jan 2008 11:26:01 +0000 (11:26 -0000)
svn path=/main/trunk/; revision=9226

pym/repoman/utilities.py

index 26863ecf13ec05061a2a934d164316b710b3f753..beabd9d47dd2802bf284e6280b549debcdac2df3 100644 (file)
@@ -3,15 +3,71 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id$
 
+"""This module contains utility functions to help repoman find ebuilds to
+scan"""
+
+import commands
+import itertools
 import logging
 import os
+import sys
 
-from portage import util
+from portage import output
 from portage import exception
-
+from portage import util
 normalize_path = util.normalize_path
 util.initialize_logger()
 
+
+def detect_vcs_conflicts(vcs, options):
+       """Determine if the checkout has problems like cvs conflicts.
+       
+       If you want more vcs support here just keep adding if blocks...
+       This could be better.
+       
+       TODO(antarus): Also this should probably not call sys.exit() as
+       repoman is run on >1 packages and one failure should not cause
+       subsequent packages to fail.
+       
+       Args:
+               vcs - A string identifying the version control system in use
+       Returns:
+               None (calls sys.exit on fatal problems)
+       """
+       retval = ("","")
+       if vcs == 'cvs':
+               logging.info("Performing a " + output.green("cvs -n up") + \
+                       " with a little magic grep to check for updates.")
+               retval = commands.getstatusoutput("cvs -n up 2>&1 | " + \
+                       "egrep '^[^\?] .*' | " + \
+                       "egrep -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'")
+
+               mylines = retval[1].splitlines()
+               myupdates = []
+               for line in mylines:
+                       if not line:
+                               continue
+                       if line[0] not in "UPMAR": # Updates,Patches,Modified,Added,Removed
+                               logging.error(red("!!! Please fix the following issues reported " + \
+                                       "from cvs: ")+green("(U,P,M,A,R are ok)"))
+                               logging.error(red("!!! Note: This is a pretend/no-modify pass..."))
+                               logging.error(retval[1])
+                               sys.exit(1)
+                       elif line[0] in "UP":
+                               myupdates.append(line[2:])
+
+               if myupdates:
+                       logging.info(green("Fetching trivial updates..."))
+                       if options.pretend:
+                               logging.info("(cvs up "+" ".join(myupdates)+")")
+                               retval = os.EX_OK
+                       else:
+                               retval = os.system("cvs up " + " ".join(myupdates))
+                       if retval != os.EX_OK:
+                               logging.fatal("!!! cvs exited with an error. Terminating.")
+                               sys.exit(retval)
+
+
 def have_profile_dir(path, maxdepth=3):
        """ Try to figure out if 'path' has a /profiles dir in it by checking for a package.mask file
        """
@@ -21,6 +77,7 @@ def have_profile_dir(path, maxdepth=3):
                path = normalize_path(path + "/..")
                maxdepth -= 1
 
+
 def parse_use_local_desc(mylines, usedict=None):
        """
        Records are of the form PACKAGE:FLAG - DESC
@@ -42,6 +99,7 @@ def parse_use_local_desc(mylines, usedict=None):
                usedict[pkg].add(flag)
        return usedict
 
+
 def FindPackagesToScan(settings, startdir, reposplit):
        """ Try to find packages that need to be scanned
        
@@ -53,6 +111,7 @@ def FindPackagesToScan(settings, startdir, reposplit):
                A list of directories to scan
        """
        
+       
        def AddPackagesInDir(path):
                """ Given a list of dirs, add any packages in it """
                ret = []
@@ -95,6 +154,123 @@ def FindPackagesToScan(settings, startdir, reposplit):
                        scanlist.append(os.path.join(catdir, pkgdir))
        return scanlist
 
+
+def format_qa_output(formatter, stats, fails, dofull, dofail, options, qawarnings):
+       """Helper function that formats output properly
+       
+       Args:
+               formatter - a subclass of Formatter
+               stats - a dict of qa status items
+               fails - a dict of qa status failures
+               dofull - boolean to print full results or a summary
+               dofail - boolean to decide if failure was hard or soft
+       
+       Returns:
+               None (modifies formatter)
+       """
+       full = options.mode in ("full", "lfull")
+       # we only want key value pairs where value > 0 
+       for category, number in \
+               itertools.ifilter(lambda myitem: myitem[1] > 0, stats.iteritems()):
+               formatter.add_literal_data("  " + category.ljust(30))
+               if category in qawarnings:
+                       formatter.push_style("WARN")
+               else:
+                       formatter.push_style("BAD")
+               formatter.add_literal_data(str(number))
+               formatter.pop_style()
+               formatter.add_line_break()
+               if not dofull:
+                       if not full and dofail and category in qawarnings:
+                               # warnings are considered noise when there are failures
+                               continue
+                       fails_list = fails[category]
+                       if not full and len(fails_list) > 12:
+                               fails_list = fails_list[:12]
+                       for failure in fails_list:
+                               formatter.add_literal_data("   " + failure)
+                               formatter.add_line_break()
+
+
+def editor_is_executable(editor):
+       """
+       Given an EDITOR string, validate that it refers to
+       an executable. This uses shlex.split() to split the
+       first component and do a PATH lookup if necessary.
+
+       @param editor: An EDITOR value from the environment.
+       @type: string
+       @rtype: bool
+       @returns: True if an executable is found, False otherwise.
+       """
+       import shlex
+       editor_split = shlex.split(editor)
+       if not editor_split:
+               return False
+       filename = editor_split[0]
+       if not os.path.isabs(filename):
+               return find_binary(filename) is not None
+       return os.access(filename, os.X_OK) and os.path.isfile(filename)
+
+
+def get_commit_message_with_editor(editor, message=None):
+       """
+       Execute editor with a temporary file as it's argument
+       and return the file content afterwards.
+
+       @param editor: An EDITOR value from the environment
+       @type: string
+       @param message: An iterable of lines to show in the editor.
+       @type: iterable
+       @rtype: string or None
+       @returns: A string on success or None if an error occurs.
+       """
+       from tempfile import mkstemp
+       fd, filename = mkstemp()
+       try:
+               os.write(fd, "\n# Please enter the commit message " + \
+                       "for your changes.\n# (Comment lines starting " + \
+                       "with '#' will not be included)\n")
+               if message:
+                       os.write(fd, "#\n")
+                       for line in message:
+                               os.write(fd, "#" + line)
+               os.close(fd)
+               retval = os.system(editor + " '%s'" % filename)
+               if not (os.WIFEXITED(retval) and os.WEXITSTATUS(retval) == os.EX_OK):
+                       return None
+               try:
+                       mylines = open(filename).readlines()
+               except OSError, e:
+                       if e.errno != errno.ENOENT:
+                               raise
+                       del e
+                       return None
+               return "".join(line for line in mylines if not line.startswith("#"))
+       finally:
+               try:
+                       os.unlink(filename)
+               except OSError:
+                       pass
+
+
+def get_commit_message_with_stdin():
+       """
+       Read a commit message from the user and return it.
+
+       @rtype: string or None
+       @returns: A string on success or None if an error occurs.
+       """
+       print "Please enter a commit message. Use Ctrl-d to finish or Ctrl-c to abort."
+       commitmessage = []
+       while True:
+               commitmessage.append(sys.stdin.readline())
+               if not commitmessage[-1]:
+                       break
+       commitmessage = "".join(commitmessage)
+       return commitmessage
+
+
 def FindPortdir(settings):
        """ Try to figure out what repo we are in and whether we are in a regular
        tree or an overlay.