-class Application(object):
- """
- Represents an application, i.e. mediawiki or phpbb.
-
- .. note::
- Many of these methods assume a specific working
- directory; prefer using the corresponding methods
- in :class:`Deployment` and its subclasses.
- """
- #: String name of the application
- name = None
- #: Dictionary of version strings to :class:`ApplicationVersion`.
- #: See also :meth:`makeVersion`.
- versions = None
- #: List of files that need to be modified when parametrizing.
- #: This is a class-wide constant, and should not normally be modified.
- parametrized_files = []
- #: Keys that are used in older versions of the application, but
- #: not for the most recent version.
- deprecated_keys = []
- def __init__(self, name):
- self.name = name
- self.versions = {}
- # cache variables
- self._extractors = {}
- self._substitutions = {}
- def repository(self, srv_path):
- """
- Returns the Git repository that would contain this application.
- ``srv_path`` corresponds to ``options.srv_path`` from the global baton.
- """
- repo = os.path.join(srv_path, self.name + ".git")
- if not os.path.isdir(repo):
- repo = os.path.join(srv_path, self.name, ".git")
- if not os.path.isdir(repo):
- raise NoRepositoryError(self.name)
- return repo
- def makeVersion(self, version):
- """
- Creates or retrieves the :class:`ApplicationVersion` singleton for the
- specified version.
- """
- if version not in self.versions:
- self.versions[version] = ApplicationVersion(distutils.version.LooseVersion(version), self)
- return self.versions[version]
- def extract(self, deployment):
- """Extracts wizard variables from a deployment."""
- result = {}
- for k,extractor in self.extractors.items():
- result[k] = extractor(deployment)
- return result
- def parametrize(self, deployment):
- """
- Takes a generic source checkout and parametrizes
- it according to the values of deployment. This function
- operates on the current working directory.
- """
- variables = deployment.extract()
- for file in self.parametrized_files:
- try:
- contents = open(file, "r").read()
- except IOError:
- continue
- for key, value in variables.items():
- if value is None: continue
- contents = contents.replace(key, value)
- f = open(file, "w")
- f.write(contents)
- def resolveConflicts(self, deployment):
- """
- Resolves conflicted files in the current working
- directory. Returns whether or not all conflicted
- files were resolved or not. Fully resolved files are
- added to the index, but no commit is made. By default
- this is a no-op and returns ``False``.
- """
- return False
- def prepareMerge(self, deployment):
- """
- Performs various edits to files in the current working directory in
- order to make a merge go more smoothly. This is usually
- used to fix botched line-endings. If you add new files,
- you have to 'git add' them; this is not necessary for edits.
- By default this is a no-op.
- """
- pass
- def prepareConfig(self, deployment):
- """
- Takes a deployment and replaces any explicit instances
- of a configuration variable with generic ``WIZARD_*`` constants.
- The default implementation uses :attr:`substitutions`;
- you can override this method to provide arbitrary extra
- behavior.
- """
- for key, subst in self.substitutions.items():
- subs = subst(deployment)
- if not subs and key not in self.deprecated_keys:
- logging.warning("No substitutions for %s" % key)
- def install(self, version, options):
- """
- Run for 'wizard configure' (and, by proxy, 'wizard install')
- to configure an application. This assumes that the current
- working directory is a deployment. (This function does not take
- a :class:`Deployment` as a parameter, as those operations are
- not meaningful yet.)
- """
- raise NotImplemented
- def upgrade(self, deployment, version, options):
- """
- Run for 'wizard upgrade' to upgrade database schemas and other
- non-versioned data in an application. This assumes that
- the current working directory is the deployment.
- """
- raise NotImplemented
- def backup(self, deployment, options):
- """
- Run for 'wizard backup' and upgrades to backup database schemas
- and other non-versioned data in an application. This assumes
- that the current working directory is the deployment.
- """
- raise NotImplemented
- def restore(self, deployment, backup, options):
- """
- Run for 'wizard restore' and failed upgrades to restore database
- and other non-versioned data to a backed up version. This assumes
- that the current working directory is the deployment.
- """
- raise NotImplemented
- def detectVersion(self, deployment):
- """
- Checks source files to determine the version manually. This assumes
- that the current working directory is the deployment.
- """
- return None
- def checkWeb(self, deployment, output=None):
- """
- Checks if the autoinstall is viewable from the web. To get
- the HTML source that was retrieved, pass a variable containing
- an empty list to ``output``; it will be mutated to have its
- first element be the output.
- """
- raise NotImplemented
- def checkConfig(self, deployment):
- """
- Checks whether or not an autoinstall has been configured/installed
- for use. Assumes that the current working directory is the deployment.
- """
- raise NotImplemented
- @property
- def extractors(self):
- """
- Dictionary of variable names to extractor functions. These functions
- take a :class:`Deployment` as an argument and return the value of
- the variable, or ``None`` if it could not be found.
- See also :func:`wizard.app.filename_regex_extractor`.
- """
- return {}
- @property
- def substitutions(self):
- """
- Dictionary of variable names to substitution functions. These functions
- take a :class:`Deployment` as an argument and modify the deployment such
- that an explicit instance of the variable is released with the generic
- WIZARD_* constant. See also :func:`wizard.app.filename_regex_substitution`.
- """
- return {}
- @staticmethod
- def make(name):
- """Makes an application, but uses the correct subtype if available."""
- try:
- __import__("wizard.app." + name)
- return getattr(wizard.app, name).Application(name)
- except ImportError:
- return Application(name)
-
-class ApplicationVersion(object):
- """Represents an abstract notion of a version for an application, where
- ``version`` is a :class:`distutils.version.LooseVersion` and
- ``application`` is a :class:`Application`."""
- #: The :class:`distutils.version.LooseVersion` of this instance.
- version = None
- #: The :class:`Application` of this instance.
- application = None
- def __init__(self, version, application):
- self.version = version
- self.application = application
- @property
- def tag(self):
- """
- Returns the name of the git describe tag for the commit the user is
- presently on, something like mediawiki-1.2.3-scripts-4-g123abcd
- """
- return "%s-%s" % (self.application, self.version)
- @property
- def scripts_tag(self):
- """
- Returns the name of the Git tag for this version.
- """
- end = str(self.version).partition('-scripts')[2].partition('-')[0]
- return "%s-scripts%s" % (self.pristine_tag, end)
- @property
- def pristine_tag(self):
- """
- Returns the name of the Git tag for the pristine version corresponding
- to this version.
- """
- return "%s-%s" % (self.application.name, str(self.version).partition('-scripts')[0])
- def __cmp__(self, y):
- return cmp(self.version, y.version)
- @staticmethod
- def parse(value):
- """
- Parses a line from the :term:`versions store` and return
- :class:`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.
-
- The `value` to parse will vary. For old style installs, it
- will look like::
-
- /afs/athena.mit.edu/contrib/scripts/deploy/APP-x.y.z
-
- For new style installs, it will look like::
-
- APP-x.y.z-scripts
- """
- name = value.split("/")[-1]
- try:
- 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:
- raise DeploymentParseError(value)
- return ApplicationVersion.make(app, version)
- @staticmethod
- def make(app, version):
- """
- Makes/retrieves a singleton :class:`ApplicationVersion` from
- a``app`` and ``version`` string.
- """
- try:
- # defer to the application for version creation to enforce
- # singletons
- return applications()[app].makeVersion(version)
- except KeyError:
- raise NoSuchApplication(app)
-