Commits

Eric Larson  committed 3aaddab

Added a really simple package format

  • Participants
  • Parent commits 272bb97

Comments (0)

Files changed (2)

File xenv/main.py

 from subprocess import call
 
 from argparse import ArgumentParser
-from xenv.models import Xenv
+from xenv.models import Xenv, Package
 
 
-def run_cmd(args):
-    xenv = Xenv(root=args.root)
+def with_root(f):
+    def wrap(args):
+        xenv = Xenv(root=args.root)
+        f(xenv, args)
+    return wrap
+
+
+@with_root
+def run_cmd(xenv, args):
     xenv.run(args.cmd.pop())
 
+@with_root
+def create_xenv_cmd(xenv, args):
+    xenv.create()
 
-def create_xenv_cmd(args):
-    xenv = Xenv(root=args.root)
-    xenv.create()
+@with_root
+def install_package_cmd(xenv, args):
+    pkg = Package(args.name, args.version, args.repo)
+    xenv.install(pkg)
 
 
 class CLI(object):
                          help='The command and flags to run via the xenv')
         run.set_defaults(func=run_cmd)
 
+
+        # install a package
+        install_pkg = self.subs.add_parser('install',
+                                           help='Install a package in an xenv')
+        install_pkg.add_argument('name', help='The package to install')
+        install_pkg.add_argument('version', help='The version to install')    
+        install_pkg.add_argument('--repo', '-b', default='file://',
+                         help='The base URL of the package. \
+                               The default is the current directory')
+        install_pkg.set_defaults(func=install_package_cmd)
+        
+
     def run(self, args=None):
         if not args:
             args = self.parser.parse_known_args()

File xenv/models.py

 import os
 from paver.easy import path
 from subprocess import Popen, call
+from urllib2 import urlopen
+from urlparse import urljoin
 
 
 class XenvFS(object):
         self.root = path(root or '.')
 
     def create_base_env(self):
-        (self.root / 'bin').mkdir()
         (self.root / 'local').mkdir()
     
     def create_venv(self):
         return ':'.join([
                 (self.root / 'usr' / 'bin').abspath(),
                 (self.root / 'local' / 'bin').abspath(),
-                (self.root / 'bin').abspath(),
+                '/bin', '/usr/bin', '/usr/local/bin',
                 ])
 
 
 class Xenv(object):
     def __init__(self, root=None):
         self.xenv_fs = XenvFS(root)
+        self.root = self.xenv_fs.root.abspath()
 
     def run(self, cmd):
         env = {
             'PATH': self.xenv_fs.path()
         }
         try:
+            print cmd
             call(cmd, env=env)
         except KeyboardInterrupt:
             pass
         self.xenv_fs.create_base_env()
         self.xenv_fs.create_venv()
 
+
+    def install(self, pkg):
+        pkgfile = pkg.fetch(self)
+
+        call(['tar', 'xf', pkgfile], cwd=self.root)
+        install_hook = self.root / 'packages' / 'install'
+        if install_hook.isfile():
+            call(install_hook.abspath(), cwd=self.root)
+        
+
                
+class Package(object):
+    '''
+    A package is a single file that contains a directory structure to
+    untar on top a xenv. It may place files anywhere in the xenv and
+    is responsible for not corrupting the environment.
+
+    The only restriction is that it may not add any hidden (.) files
+    to the root directory.
+
+    Package Name
+    ============
+
+    The package must be a tarball (.tar) or gzipped tar (.tar.gz). It
+    must be named using the following pattern: ::
+    
+      {package-name}-{version}.tar[.gz]
+
+    The version must comply with the semver.org specification.
+
+    File Hiearchy
+    =============
+
+    The file hierarchy can be whatever the package creator likes. The
+    only restrictions are the following.
+
+
+    The /usr Directory
+    ------------------
+
+    The usr directory is where an xenv installs its core utilities and
+    executables. For example, we install a virtualenv in /usr for
+    using Python.
+
+    
+    The /local Directory
+    --------------------
+
+    If you create a /local/bin, it will be added to the PATH when
+    running something via xe.
+
+
+    Installation Hooks
+    ==================
+
+    A xenv package does not aim to be a "real" package format and as
+    such, it may wrap a more complex package such as an RPM. For this
+    reason we provide a package hook in order to allow installing
+    these sorts of packages.
+
+    When a package is installed (untarred) in the xenv, an install
+    script can be placed at /packages/install. This file must be
+    executable. It may use any language that is available on the host
+    system via a shebang.
+
+    After it is installed, the install script will be moved to: ::
+      /packages/{package-name}/{version}/install
+    '''
+
+    def __init__(self, name, version, base='file:'):
+        self.name = name
+        self.version = version
+        self.base = base
+        self.pkg_tmpl = '{self.name}-{self.version}.tar'
+
+    def fetch(self, xenv):
+        fname = self.pkg_tmpl.format(**vars())
+        if self.base.startswith('file:'):
+            fname = path(fname).abspath()
+        url = urljoin(self.base, fname)
+        print url
+        pkgfile = urlopen(url)
+        return self.save(pkgfile, xenv)
+
+    def save(self, fh, xenv):
+        fname = self.pkg_tmpl.format(**vars())
+        pkgfile = xenv.root.abspath() / fname
+        with open(pkgfile, 'w+') as pkg:
+            for line in fh:
+                pkg.write(line)
+        return pkgfile
+