4 import wizard.deploy # otherwise circular dep is unhappy
6 # This code operates off of the assumption of .scripts-version, which
10 # As per #python; if you decide to start overloading magic methods,
11 # we should remove this subclass
12 """Equivalent to .scripts-version: a series of DeployRevisions."""
13 def __init__(self, revs = []):
14 """`revs` List of DeployRevision objects"""
15 list.__init__(self, revs) # pass to list
17 return '<DeployLog %s>' % list.__repr__(self)
20 """Loads a scripts version file and parses it into
21 DeployLog and DeployRevision objects"""
22 # XXX: DIRTY DIRTY HACK
23 # What we should actually do is parse the git logs
24 scriptsdir = os.path.join(os.path.dirname(file), ".scripts")
25 if os.path.isdir(scriptsdir):
26 file = os.path.join(scriptsdir, "old-version")
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)
55 rev.version = wizard.deploy.ApplicationVersion.parse(line, rev.source)
58 raise ScriptsVersionTooManyFieldsError(file)
61 return DeployLog(revs)
63 class DeployRevision(object):
64 """A single entry in the .scripts-version file. Contains who deployed
65 this revision, what application version this is, etc."""
66 def __init__(self, datetime=None, user=None, source=None, version=None):
67 """ `datetime` Time this revision was deployed
68 `user` Person who deployed this revision, in user@host format.
69 `source` Instance of DeploySource
70 `version` Instance of ApplicationVersion
71 Note: This object is typically built incrementally."""
72 self.datetime = datetime
75 self.version = version
77 class DeploySource(object):
78 """Source of the deployment; see subclasses for examples"""
80 raise NotImplementedError # abstract class
83 # munge out common prefix
84 rel = os.path.relpath(line, "/afs/athena.mit.edu/contrib/scripts/")
85 parts = rel.split("/")
86 if parts[0] == "wizard":
88 elif parts[0] == "deploy" or parts[0] == "deploydev":
89 isDev = ( parts[0] == "deploydev" )
91 if parts[1] == "updates":
92 return OldUpdate(isDev)
94 return TarballInstall(line, isDev)
97 return UnknownDeploySource(line)
99 class TarballInstall(DeploySource):
100 """Original installation from tarball, characterized by
101 /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z.tar.gz
103 def __init__(self, location, isDev):
104 self.location = location
107 class OldUpdate(DeploySource):
108 """Upgrade using old upgrade infrastructure, characterized by
109 /afs/athena.mit.edu/contrib/scripts/deploydev/updates/update-scripts-version.pl
111 def __init__(self, isDev):
114 class WizardUpdate(DeploySource):
115 """Upgrade using wizard infrastructure, characterized by
116 /afs/athena.mit.edu/contrib/scripts/wizard/bin/wizard HASHGOBBLEDYGOOK
121 class UnknownDeploySource(DeploySource):
122 """Deployment that we don't know the meaning of. Wot!"""
123 def __init__(self, line):
128 class Error(Exception):
129 """Base error class for log errors."""
132 class ScriptsVersionError(Error):
133 """Errors specific to the parsing of a full .scripts-version file
134 (errors that could also be triggered while parsing a parallel-find
135 output should not be this subclass.)"""
138 class ScriptsVersionTooManyFieldsError(ScriptsVersionError):
142 ERROR: Could not parse .scripts-version file. It
143 contained too many fields.
146 class ScriptsVersionNotEnoughFieldsError(ScriptsVersionError):
150 ERROR: Could not parse .scripts-version file. It
151 didn't contain enough fields.
154 class ScriptsVersionNoSuchFile(ScriptsVersionError):
155 def __init__(self, file):
160 ERROR: File %s didn't exist.