7 import wizard.deploy # to break circular loop
9 # This code operates off of the assumption of .scripts-version, which
12 class DeployLog(list):
13 # As per #python; if you decide to start overloading magic methods,
14 # we should remove this subclass
15 """Equivalent to .scripts-version: a series of DeployRevisions."""
16 def __init__(self, revs = []):
17 """`revs` List of DeployRevision objects"""
18 list.__init__(self, revs) # pass to list
20 return '<DeployLog %s>' % list.__repr__(self)
23 """Loads a scripts version file and parses it into
24 DeployLog and DeployRevision objects"""
25 # XXX: DIRTY DIRTY HACK
26 # What we should actually do is parse the git logs
27 file = deployment.old_version_file
29 rev = DeployRevision()
34 raise ScriptsVersionNotEnoughFieldsError(file)
39 raise ScriptsVersionNoSuchFile(file)
45 rev = DeployRevision()
48 # we need the dateutil parser in order to
49 # be able to parse time offsets
50 rev.datetime = dateutil.parser.parse(line)
54 rev.source = DeploySource.parse(line)
57 rev.version = app.ApplicationVersion.parse(line)
58 except (wizard.deploy.Error, app.Error) as e:
59 e.location = deployment.location
60 raise e, None, sys.exc_info()[2]
63 raise ScriptsVersionTooManyFieldsError(file)
66 return DeployLog(revs)
68 class DeployRevision(object):
69 """A single entry in the .scripts-version file. Contains who deployed
70 this revision, what application version this is, etc."""
71 def __init__(self, datetime=None, user=None, source=None, version=None):
72 """ `datetime` Time this revision was deployed
73 `user` Person who deployed this revision, in ``user@host`` format.
74 `source` Instance of :class:`DeploySource`
75 `version` Instance of :class:`app.ApplicationVersion`
76 Note: This object is typically built incrementally."""
77 self.datetime = datetime
80 self.version = version
82 class DeploySource(object):
83 """Source of the deployment; see subclasses for examples"""
85 raise NotImplementedError # abstract class
88 # munge out common prefix
89 rel = os.path.relpath(line, "/afs/athena.mit.edu/contrib/scripts/")
90 parts = rel.split("/")
91 if parts[0] == "wizard":
93 elif parts[0] == "deploy" or parts[0] == "deploydev":
94 isDev = ( parts[0] == "deploydev" )
96 if parts[1] == "updates":
97 return OldUpdate(isDev)
99 return TarballInstall(line, isDev)
102 return UnknownDeploySource(line)
104 class TarballInstall(DeploySource):
105 """Original installation from tarball, characterized by
106 /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z.tar.gz
108 def __init__(self, location, isDev):
109 self.location = location
112 class OldUpdate(DeploySource):
113 """Upgrade using old upgrade infrastructure, characterized by
114 /afs/athena.mit.edu/contrib/scripts/deploydev/updates/update-scripts-version.pl
116 def __init__(self, isDev):
119 class WizardUpdate(DeploySource):
120 """Upgrade using wizard infrastructure, characterized by
121 /afs/athena.mit.edu/contrib/scripts/wizard/bin/wizard HASHGOBBLEDYGOOK
126 class UnknownDeploySource(DeploySource):
127 """Deployment that we don't know the meaning of. Wot!"""
128 def __init__(self, line):
133 class Error(wizard.Error):
134 """Base error class for log errors."""
137 class ScriptsVersionError(Error):
138 """Errors specific to the parsing of a full .scripts-version file
139 (errors that could also be triggered while parsing a parallel-find
140 output should not be this subclass.)"""
143 class ScriptsVersionTooManyFieldsError(ScriptsVersionError):
147 ERROR: Could not parse .scripts-version file. It
148 contained too many fields.
151 class ScriptsVersionNotEnoughFieldsError(ScriptsVersionError):
155 ERROR: Could not parse .scripts-version file. It
156 didn't contain enough fields.
159 class ScriptsVersionNoSuchFile(ScriptsVersionError):
160 def __init__(self, file):
165 ERROR: File %s didn't exist.