Source

gorilla / lib / gorilla / guts / shelf.py

import os, re, shutil, subprocess
from gorilla.guts import util
from gorilla.guts.catalog import Card
from gorilla.guts.repo import MercurialRepo, StaticRepo


NUMERIC_TAG = re.compile('^\d.*')

class Shelf(object):
    """Shelves handle packages as a whole."""
    
    def __init__(self, card):
        self.card = card
        self.path = os.path.join(util.SHELF_PATH, self.card.name)
        
        if not self.vcs:
            Repo = MercurialRepo
        elif self.vcs == 'hg':
            Repo = MercurialRepo
        elif self.vcs == 'git':
            # Repo = GitRepo
            pass
        elif self.vcs == 'static':
            Repo = StaticRepo
            pass
        
        self.repo = Repo(path=self.path, sources=self.card.sources)
    
    
    @property
    def present(self):
        return os.path.isdir(self.path)
    
    
    @property
    def vcs(self):
        if not self.present:
            return None
        elif os.path.isdir(os.path.join(self.path, '.hg')):
            return 'hg'
        elif os.path.isdir(os.path.join(self.path, '.git')):
            return 'git'
        else:
            return 'static'
    
    
    @property
    def installed(self):
        packages = [os.path.join(util.LIB_PATH, p) for p in self.card.packages]
        scripts = [os.path.join(util.BIN_PATH, s.split('/')[-1]) for s in self.card.scripts]
        
        packages_installed = all(map(lambda p: os.path.isdir(p), packages))
        scripts_installed = all(map(lambda s: os.path.isfile(s), scripts))
        
        return packages_installed and scripts_installed
    
    
    @property
    def versions(self):
        if hasattr(self.card, 'parse_tags'):
            tags = self.card.parse_tags(self.repo.tags)
        else:
            tags = [t for t in self.repo.tags if NUMERIC_TAG.match(t)]
            tags.sort()
            tags.reverse()
        
        tags.insert(0, 'latest')
        return tags
    
    def grab(self):
        """Download the latest version of the package (clone if necessary).
        
        This does *not* update the working directory if the shelf is already
        present at the start.
        
        """
        if not os.path.exists(util.SHELF_PATH):
            os.mkdir(util.SHELF_PATH)
        
        if not self.present:
            self.repo.clone()
        else:
            self.repo.pull()
    
    def install(self, version):
        """Install the package."""
        path = os.path.join(self.path, version) if self.vcs == 'static' else self.path
        
        if version == 'latest' or version not in self.versions:
            self.grab()
        
        self.repo.update(version)
        
        for command in self.card.build_commands:
            proc = subprocess.Popen(command, shell=True, cwd=self.path,
                stdout=subprocess.PIPE)
            proc.wait()
        
        if not self.installed:
            for package in self.card.packages:
                source = os.path.join(path, package)
                dest = os.path.join(util.LIB_PATH, package)
                if not os.path.islink(dest):
                    os.symlink(source, dest)
            for script in self.card.scripts:
                source = os.path.join(path, *script.split('/'))
                dest = os.path.join(util.BIN_PATH, script.split('/')[-1])
                if not os.path.islink(dest):
                    os.symlink(source, dest)
        
        for test, expected_output in self.card.tests.iteritems():
            expected_output = re.compile(expected_output, re.MULTILINE)
            proc = subprocess.Popen(test, shell=True, stdout=subprocess.PIPE)
            
            output = proc.communicate()[0]
            if not expected_output.match(output):
                return False
        
        return True
    
    def remove(self):
        packages = [os.path.join(util.LIB_PATH, p) for p in self.card.packages]
        scripts = [os.path.join(util.BIN_PATH, s.split('/')[-1]) for s in self.card.scripts]
        
        if any(map(lambda p: not os.path.islink(p)
                             or not os.path.isdir(p), packages)):
            return False
        if any(map(lambda s: not os.path.islink(s)
                             or not os.path.isfile(s), scripts)):
            return False
        
        for package in packages:
            os.unlink(package)
        for script in scripts:
            os.unlink(script)
        
        return True
    
    def smash(self):
        shutil.rmtree(self.path)
        return True