]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/deploy.py
Refactor, increase error coverage, more sanity checks.
[wizard.git] / wizard / deploy.py
index 1b10204fa0190001e0046d4514123b3141176883..f6292b9d091cc68f66a033b39c1559fb128f3608 100644 (file)
@@ -32,7 +32,7 @@ def parse_install_lines(show, versions_store, yield_errors = False):
     a :class:`wizard.deploy.Error` if ``yield_errors`` is ``True``.  You can
     filter out applications and versions by specifying ``app``
     or ``app-1.2.3`` in ``show``.  This function may generate
-    log output.
+    log output.  Make sure that ``show`` is a list and not a string.
     """
     if not show: show = applications()
     show = frozenset(show)
@@ -111,6 +111,66 @@ class Deployment(object):
         Checks if the application is configured.
         """
         raise NotImplemented
+
+    def verify(self):
+        """
+        Checks if this is an autoinstall, throws an exception if there
+        are problems.
+        """
+        with util.ChangeDirectory(self.location):
+            has_git = os.path.isdir(".git")
+            has_scripts = os.path.isdir(".scripts")
+            if not has_git and has_scripts:
+                raise CorruptedAutoinstallError(self.location)
+            elif has_git and not has_scripts:
+                raise AlreadyVersionedError(self.location)
+            elif not has_git and not has_scripts:
+                if os.path.isfile(".scripts-version"):
+                    raise NotMigratedError(self.location)
+
+    def verifyTag(self, srv_path):
+        """
+        Checks if the purported version has a corresponding tag
+        in the upstream repository.
+        """
+        repo = self.application.repository(srv_path)
+        try:
+            shell.Shell().eval("git", "--git-dir", repo, "rev-parse", self.app_version.scripts_tag)
+        except shell.CallError:
+            raise NoTagError(self.app_version.scripts_tag)
+
+    def verifyGit(self, srv_path):
+        """
+        Checks if the autoinstall's Git repository makes sense,
+        checking if the tag is parseable and corresponds to
+        a real application, and if the tag in this repository
+        corresponds to the one in the remote repository.
+        """
+        with util.ChangeDirectory(self.location):
+            sh = shell.Shell()
+            repo = self.application.repository(srv_path)
+            def repo_rev_parse(tag):
+                return sh.eval("git", "--git-dir", repo, "rev-parse", tag)
+            def self_rev_parse(tag):
+                return sh.safeCall("git", "rev-parse", tag, strip=True)
+            def compare_tags(tag):
+                return repo_rev_parse(tag) == self_rev_parse(tag)
+            if not compare_tags(self.app_version.pristine_tag):
+                raise InconsistentPristineTagError()
+            if not compare_tags(self.app_version.scripts_tag):
+                raise InconsistentScriptsTagError()
+            parent = repo_rev_parse(self.app_version.scripts_tag)
+            merge_base = sh.safeCall("git", "merge-base", parent, "HEAD", strip=True)
+            if merge_base != parent:
+                raise HeadNotDescendantError()
+
+    def verifyConfigured(self):
+        """
+        Checks if the autoinstall is configured running.
+        """
+        if not self.configured:
+            raise NotConfiguredError(self.location)
+
     @property
     def configured(self):
         """Whether or not an autoinstall has been configured/installed for use."""
@@ -214,8 +274,6 @@ class Application(object):
         """
         Returns the Git repository that would contain this application.
         ``srv_path`` corresponds to ``options.srv_path`` from the global baton.
-        Throws :exc:`NoRepositoryError` if the calculated path does not
-        exist.
         """
         repo = os.path.join(srv_path, self.name + ".git")
         if not os.path.isdir(repo):
@@ -246,17 +304,14 @@ class Application(object):
         for file in self.parametrized_files:
             fullpath = os.path.join(dir, file)
             try:
-                f = open(fullpath, "r")
+                contents = open(fullpath, "r").read()
             except IOError:
                 continue
-            contents = f.read()
-            f.close()
             for key, value in variables.items():
                 if value is None: continue
                 contents = contents.replace(key, value)
             tmp = tempfile.NamedTemporaryFile(delete=False)
             tmp.write(contents)
-            tmp.close()
             os.rename(tmp.name, fullpath)
     def prepareConfig(self, deployment):
         """
@@ -321,6 +376,13 @@ class ApplicationVersion(object):
         self.version = version
         self.application = application
     @property
+    def tag(self):
+        """
+        Returns the name of the git describe tag for the commit the user is
+        presently on, something like mediawiki-1.2.3-scripts-4-g123abcd
+        """
+        return "%s-%s" % (self.application, self.version)
+    @property
     def scripts_tag(self):
         """
         Returns the name of the Git tag for this version.
@@ -424,8 +486,62 @@ class NoRepositoryError(Error):
     def __str__(self):
         return """Could not find Git repository for '%s'.  If you would like to use a local version, try specifying --srv-path or WIZARD_SRV_PATH.""" % self.app
 
-# If you want, you can wrap this up into a registry and access things
-# through that, but it's not really necessary
+class NotMigratedError(Error):
+    """
+    The deployment contains a .scripts-version file, but no .git
+    or .scripts directory.
+    """
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """This installation was not migrated"""
+
+class AlreadyVersionedError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Directory contains a .git directory, but not
+a .scripts directory.  If this is not a corrupt
+migration, this means that the user was versioning their
+install using Git."""
+
+class NotConfiguredError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: The install was well-formed, but not configured
+(essential configuration files were not found.)"""
+
+class CorruptedAutoinstallError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Directory contains a .scripts directory,
+but not a .git directory."""
+
+class NotAutoinstallError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Could not find .scripts-version file. Are you sure
+this is an autoinstalled application?
+"""
+
+class NoTagError(Error):
+    def __init__(self, tag):
+        self.tag = tag
+    def __str__(self):
+        return """
+
+ERROR: Could not find tag %s in repository.""" % self.tag
 
 _application_list = [
     "mediawiki", "wordpress", "joomla", "e107", "gallery2",