]> scripts.mit.edu Git - wizard.git/commitdiff
Revise object model between versions and actual deploys.
authorEdward Z. Yang <ezyang@mit.edu>
Sat, 1 Aug 2009 05:16:24 +0000 (01:16 -0400)
committerEdward Z. Yang <ezyang@mit.edu>
Sat, 1 Aug 2009 05:16:24 +0000 (01:16 -0400)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
wizard/command/errors.py
wizard/command/list.py
wizard/deploy.py
wizard/log.py

index 5250254c2e191348e6c2f4e72a5a4717598a2aeb..d3ed053f3dad0cad41c227c6bdc71980d7cee7d8 100644 (file)
@@ -10,7 +10,12 @@ def main(argv, baton):
                 raise e
             continue
         if options.verbose:
-            print e
+            if isinstance(e, deploy.NoSuchApplication):
+                print "Application %s does not exist, at %s" % (e.app, e.location)
+            elif isinstance(e, deploy.DeploymentParseError):
+                print "Parse error for line '%s', at %s" % (e.value, e.location)
+            else:
+                raise e
         else:
             print e.location
 
index e8c51268fea96983b24058fb2ecaecda53b7db93..feaf5659f0a7997c931a9909c2a0940c17b3c8e6 100644 (file)
@@ -10,6 +10,7 @@ def main(argv, baton):
     for d in deploy.parse_install_lines(show, options, True):
         if isinstance(d, Exception):
             errors += 1
+            continue
         if options.exists and not os.path.exists(os.path.join(d.location, options.exists)):
             continue
         print d.location
index cc3c824ffe3ca4810d2a716ea36a35c8b342a872..2736a7cba03b21e3d4f8a48d02a9217fbd80a5ae 100644 (file)
@@ -44,71 +44,94 @@ def parse_install_lines(show, options, yield_errors = False):
 class Deployment(object):
     """Represents a deployment of an autoinstall; i.e. a concrete
     directory in web_scripts that has .scripts-version in it."""
-    def __init__(self, location, log=None, version=None):
+    def __init__(self, location, version=None):
         """ `location`  Location of the deployment
-            `version`   ApplicationVersion of the app (this is cached info)
-            `log`       DeployLog of the app"""
+            `version`   ApplicationVersion of the app.  ONLY supply this
+                        if you don't mind having stale data; generally
+                        'wizard list' and commands that operate of of the
+                        versions store will set this."""
         self.location = location
         self._app_version = version
-        self._log = log
+        # some cache variables
         self._read_cache = {}
+        self._log = None
     def read(self, file, force = False):
-        """Reads a file's contents and stuffs it in a cache"""
+        """Reads a file's contents, possibly from cache unless force is True."""
         if force or file not in self._read_cache:
             f = open(os.path.join(self.location, file))
             self._read_cache[file] = f.read()
             f.close()
         return self._read_cache[file]
     def extract(self):
+        """Extracts all the values of all variables from deployment."""
         return self.application.extract(self)
-    def updateVersion(self, version=None):
-        """`version` Version string to update to, or leave out to simply
-            force the creation of .scripts/version file"""
-        if not version:
-            version = str(self.version)
-        else:
-            self._app_version = self.application.makeVersion(version)
+    def updateVersion(self, version):
+        """Bump the version of this deployment.
+
+        This method will update the version of this deployment in memory
+        and on disk.  It doesn't actually do an upgrade.  The version
+        string you pass here should probably have '-scripts' as a suffix."""
+        self._app_version = self.application.makeVersion(version)
         f = open(os.path.join(self.scripts_dir, 'version'), 'w')
         f.write(self.application.name + '-' + version + "\n")
         f.close()
     def scriptsifyVersion(self):
-        """At the end of a migration, writes out the current version
-        with -scripts appended to .scripts/version"""
+        """Converts from v1.0 to v1.0-scripts; use at end of migration."""
         self.updateVersion(str(self.version) + '-scripts')
     @property
     def scripts_dir(self):
         return os.path.join(self.location, '.scripts')
     @property
+    def old_version_file(self):
+        """Use of this is discouraged for migrated installs."""
+        if os.path.isdir(self.scripts_dir):
+            return os.path.join(self.scripts_dir, 'old-version')
+        else:
+            return os.path.join(self.location, '.scripts-version')
+    @property
     def version_file(self):
-        return os.path.join(self.location, '.scripts-version')
+        return os.path.join(self.scripts_dir, 'version')
     @property
     def application(self):
         return self.app_version.application
     @property
     def log(self):
         if not self._log:
-            self._log = log.DeployLog.load(self.version_file)
+            self._log = log.DeployLog.load(self)
         return self._log
     @property
     def version(self):
         """Returns the distutils Version of the deployment"""
         return self.app_version.version
     @property
-    def app_version(self, force = False):
+    def app_version(self):
         """Returns the ApplicationVersion of the deployment"""
-        if self._app_version and not force: return self._app_version
-        else: return self.log[-1].version
+        if not self._app_version:
+            if os.path.isfile(self.version_file):
+                fh = open(self.version_file)
+                appname, _, version = fh.read().rstrip().partition('-')
+                fh.close()
+                self._app_version = ApplicationVersion.make(appname, version)
+            else:
+                self._app_version = self.log[-1].version
+        return self._app_version
     @staticmethod
     def parse(line):
-        """Parses a line from the results of parallel-find.pl.
-        This will work out of the box with fileinput, see
-        getInstallLines()"""
+        """Parses a line from the versions directory.
+
+        Note: Use this method only when speed is of the utmost
+        importance.  You should prefer to directly create a deployment
+        using Deployment(location) when accuracy is desired."""
         line = line.rstrip()
         try:
             location, deploydir = line.split(":")
         except ValueError:
             return Deployment(line) # lazy loaded version
-        return Deployment(location, version=ApplicationVersion.parse(deploydir, location))
+        try:
+            return Deployment(location, version=ApplicationVersion.parse(deploydir))
+        except Error as e:
+            e.location = location
+            raise e
 
 class Application(object):
     """Represents the generic notion of an application, i.e.
@@ -116,6 +139,7 @@ class Application(object):
     def __init__(self, name):
         self.name = name
         self.versions = {}
+        # cache variables
         self._extractors = {}
     @property
     def repository(self):
@@ -165,28 +189,38 @@ class ApplicationVersion(object):
     def __cmp__(x, y):
         return cmp(x.version, y.version)
     @staticmethod
-    def parse(deploydir,location,applookup=None):
-        # The version of the deployment, will be:
-        #   /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z for old style installs
-        name = deploydir.split("/")[-1]
+    def parse(value):
+        """Parses a line from the versions directory and return ApplicationVersion.
+
+        Use this only for cases when speed is of primary importance;
+        the data in version is unreliable and when possible, you should
+        prefer directly instantiating a Deployment and having it query
+        the autoinstall itself for information.
+
+        value : The value to parse, will look like:
+           /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z for old style installs
+           APP-x.y.z-scripts for wizard style installs
+        """
+        name = value.split("/")[-1]
         try:
-            if name.find(" ") != -1:
-                raw_app, raw_version = name.split(" ")
-                version = raw_version[1:] # remove leading v
-                app, _ = raw_app.split(".") # remove trailing .git
-            elif name.find("-") != -1:
+            if name.find("-") != -1:
                 app, _, version = name.partition("-")
             else:
+                # kind of poor, maybe should error.  Generally this
+                # will actually result in a not found error
                 app = name
                 version = "trunk"
-        except ValueError: # mostly from the a, b = foo.split(' ')
-            raise DeploymentParseError(deploydir, location)
-        if not applookup: applookup = applications()
+        except ValueError:
+            raise DeploymentParseError(deploydir)
+        return ApplicationVersion.make(app, version)
+    @staticmethod
+    def make(app, version):
         try:
-            # defer to the application for version creation
-            return applookup[app].makeVersion(version)
+            # defer to the application for version creation to enforce
+            # singletons
+            return applications()[app].makeVersion(version)
         except KeyError:
-            raise NoSuchApplication(app, location)
+            raise NoSuchApplication(app)
 
 ## -- Exceptions --
 
@@ -195,30 +229,21 @@ class Error(Exception):
     pass
 
 class NoSuchApplication(Error):
-    def __init__(self, name, location):
-        self.name = name
-        self.location = location
-    def __str__(self):
-        return "ERROR: Unrecognized app '%s' at %s" % (self.name, self.location)
+    def __init__(self, app):
+        """app : Application that doesn't exist"""
+        self.app = app
+        self.location = None # filled in when available
 
 class DeploymentParseError(Error):
-    def __init__(self, malformed, location):
-        self.malformed = malformed
-        self.location = location
-    def __str__(self):
-        return """ERROR: Unparseable '%s' at %s""" % (self.malformed, self.location)
+    def __init__(self, value):
+        """value : Value from 'versions' that could not be parsed"""
+        self.value = value
+        self.location = None # filled in when available
 
 class NoRepositoryError(Error):
     def __init__(self, app):
+        """app : The application that doesn't have a Git repository"""
         self.app = app
-        self.location = "unknown"
-    def __str__(self):
-        return """
-
-ERROR: Could not find repository for this application. Have
-you converted the repository over? Is the name %s
-the same as the name of the .git folder?
-""" % self.app
 
 # If you want, you can wrap this up into a registry and access things
 # through that, but it's not really necessary
index f251b01a6b5144015a007d8e95b8338597bd7233..acf3dc286c581f15a32b58e7ab8e8e25cf29370e 100644 (file)
@@ -16,14 +16,12 @@ class DeployLog(list):
     def __repr__(self):
         return '<DeployLog %s>' % list.__repr__(self)
     @staticmethod
-    def load(file):
+    def load(deployment):
         """Loads a scripts version file and parses it into
         DeployLog and DeployRevision objects"""
         # XXX: DIRTY DIRTY HACK
         # What we should actually do is parse the git logs
-        scriptsdir = os.path.join(os.path.dirname(file), ".scripts")
-        if os.path.isdir(scriptsdir):
-            file = os.path.join(scriptsdir, "old-version")
+        file = deployment.old_version_file
         i = 0
         rev = DeployRevision()
         revs = []
@@ -52,7 +50,11 @@ class DeployLog(list):
             elif i == 2:
                 rev.source = DeploySource.parse(line)
             elif i == 3:
-                rev.version = wizard.deploy.ApplicationVersion.parse(line, rev.source)
+                try:
+                    rev.version = wizard.deploy.ApplicationVersion.parse(line)
+                except deploy.Error as e:
+                    e.location = deployment.location
+                    raise e
             else:
                 # ruh oh
                 raise ScriptsVersionTooManyFieldsError(file)