import os.path import fileinput import dateutil.parser import distutils.version import wizard from wizard import log ## -- Global Functions -- def getInstallLines(vs): """Retrieves a list of lines from the version directory that can be passed to Deployment.parse()""" if os.path.isfile(vs): return fileinput.input([vs]) return fileinput.input([vs + "/" + f for f in os.listdir(vs)]) ## -- Model Objects -- 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): """ `location` Location of the deployment `version` ApplicationVersion of the app (this is cached info) `log` DeployLog of the app""" self.location = location self._version = version self._log = log @property def version_file(self): return os.path.join(self.location, '.scripts-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) 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): """Returns the ApplicationVersion of the deployment""" if self._version and not force: return self._version else: return self.log[-1].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()""" line = line.rstrip() try: location, deploydir = line.split(":") except ValueError: return Deployment(line) # lazy loaded version return Deployment(location, version=ApplicationVersion.parse(deploydir, location)) class Application(object): """Represents the generic notion of an application, i.e. mediawiki or phpbb.""" def __init__(self, name): self.name = name self.versions = {} @property def repository(self): """Returns the Git repository that would contain this application.""" repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/git/autoinstalls", self.name + ".git") if not os.path.isdir(repo): raise NoRepositoryError(app) return repo def makeVersion(self, version): if version not in self.versions: self.versions[version] = ApplicationVersion(distutils.version.LooseVersion(version), self) return self.versions[version] class ApplicationVersion(object): """Represents an abstract notion of a version for an application""" def __init__(self, version, application): """ `version` Instance of distutils.LooseVersion `application` Instance of Application WARNING: Please don't call this directly; instead, use getVersion() on the application you want, so that this version gets registered.""" self.version = version self.application = application @property def scripts_tag(self): """Returns the name of the Git tag for this version""" # XXX: This assumes that there's only a -scripts version # which will not be true in the future. Unfortunately, finding # the "true" latest version is computationally expensive return "v%s-scripts" % self.version 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] 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: app, _, version = name.partition("-") else: app = name version = "trunk" except ValueError: # mostly from the a, b = foo.split(' ') raise DeploymentParseError(deploydir, location) if not applookup: applookup = applications try: # defer to the application for version creation return applookup[app].makeVersion(version) except KeyError: raise NoSuchApplication(app, location) ## -- Exceptions -- class Error(Exception): """Base error class for this module""" 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) 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) class NoRepositoryError(Error): def __init__(self, app): 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 application_list = [ "mediawiki", "wordpress", "joomla", "e107", "gallery2", "phpBB", "advancedbook", "phpical", "trac", "turbogears", "django", # these are technically deprecated "advancedpoll", "gallery", ] """Hash table for looking up string application name to instance""" applications = dict([(n,Application(n)) for n in application_list ])