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.
def __init__(self, name):
self.name = name
self.versions = {}
+ # cache variables
self._extractors = {}
@property
def repository(self):
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 --
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