6 import wizard.deploy # to break circular loop
8 # This code operates off of the assumption of .scripts-version, which
11 class DeployLog(list):
12 # As per #python; if you decide to start overloading magic methods,
13 # we should remove this subclass
14 """Equivalent to .scripts-version: a series of DeployRevisions."""
15 def __init__(self, revs = []):
16 """`revs` List of DeployRevision objects"""
17 list.__init__(self, revs) # pass to list
19 return '<DeployLog %s>' % list.__repr__(self)
22 """Loads a scripts version file and parses it into
23 DeployLog and DeployRevision objects"""
24 # XXX: DIRTY DIRTY HACK
25 # What we should actually do is parse the git logs
26 file = deployment.old_version_file
28 rev = DeployRevision()
33 raise ScriptsVersionNotEnoughFieldsError(file)
38 raise ScriptsVersionNoSuchFile(file)
44 rev = DeployRevision()
47 # we need the dateutil parser in order to
48 # be able to parse time offsets
49 rev.datetime = dateutil.parser.parse(line)
53 rev.source = DeploySource.parse(line)
56 rev.version = wizard.deploy.ApplicationVersion.parse(line)
57 except wizard.deploy.Error as e:
58 e.location = deployment.location
59 raise e, None, sys.exc_info()[2]
62 raise ScriptsVersionTooManyFieldsError(file)
65 return DeployLog(revs)
67 class DeployRevision(object):
68 """A single entry in the .scripts-version file. Contains who deployed
69 this revision, what application version this is, etc."""
70 def __init__(self, datetime=None, user=None, source=None, version=None):
71 """ `datetime` Time this revision was deployed
72 `user` Person who deployed this revision, in user@host format.
73 `source` Instance of DeploySource
74 `version` Instance of ApplicationVersion
75 Note: This object is typically built incrementally."""
76 self.datetime = datetime
79 self.version = version
81 class DeploySource(object):
82 """Source of the deployment; see subclasses for examples"""
84 raise NotImplementedError # abstract class
87 # munge out common prefix
88 rel = os.path.relpath(line, "/afs/athena.mit.edu/contrib/scripts/")
89 parts = rel.split("/")
90 if parts[0] == "wizard":
92 elif parts[0] == "deploy" or parts[0] == "deploydev":
93 isDev = ( parts[0] == "deploydev" )
95 if parts[1] == "updates":
96 return OldUpdate(isDev)
98 return TarballInstall(line, isDev)
101 return UnknownDeploySource(line)
103 class TarballInstall(DeploySource):
104 """Original installation from tarball, characterized by
105 /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z.tar.gz
107 def __init__(self, location, isDev):
108 self.location = location
111 class OldUpdate(DeploySource):
112 """Upgrade using old upgrade infrastructure, characterized by
113 /afs/athena.mit.edu/contrib/scripts/deploydev/updates/update-scripts-version.pl
115 def __init__(self, isDev):
118 class WizardUpdate(DeploySource):
119 """Upgrade using wizard infrastructure, characterized by
120 /afs/athena.mit.edu/contrib/scripts/wizard/bin/wizard HASHGOBBLEDYGOOK
125 class UnknownDeploySource(DeploySource):
126 """Deployment that we don't know the meaning of. Wot!"""
127 def __init__(self, line):
132 class Error(wizard.Error):
133 """Base error class for log errors."""
136 class ScriptsVersionError(Error):
137 """Errors specific to the parsing of a full .scripts-version file
138 (errors that could also be triggered while parsing a parallel-find
139 output should not be this subclass.)"""
142 class ScriptsVersionTooManyFieldsError(ScriptsVersionError):
146 ERROR: Could not parse .scripts-version file. It
147 contained too many fields.
150 class ScriptsVersionNotEnoughFieldsError(ScriptsVersionError):
154 ERROR: Could not parse .scripts-version file. It
155 didn't contain enough fields.
158 class ScriptsVersionNoSuchFile(ScriptsVersionError):
159 def __init__(self, file):
164 ERROR: File %s didn't exist.