import time
import traceback
import shutil
+import errno
+import pkg_resources
+import urlparse
import wizard
-from wizard import app, git, old_log, scripts, shell, sql, util
+from wizard import app, git, old_log, shell, sql, util
## -- Global Functions --
d = Deployment.parse(line)
name = d.application.name
except app.NoSuchApplication as e:
+ if not e.location:
+ try:
+ e.location = line.split(':')[0]
+ except IndexError:
+ e.location = line
if yield_errors:
yield e
continue
# yield
yield d
+def web(dir, url=None):
+ """
+ Attempts to determine the URL a directory would be web-accessible at.
+ If ``url`` is specified, automatically use it. Returns a generator whic
+ produces a list of candidate urls.
+ """
+ if url:
+ yield url
+ return
+
+ for entry in pkg_resources.iter_entry_points("wizard.deploy.web"):
+ f = entry.load()
+ for r in f(dir):
+ if isinstance(r, str):
+ r = urlparse.urlparse(r)
+ yield r
+
+ # try the environment
+ host = os.getenv("WIZARD_WEB_HOST")
+ path = os.getenv("WIZARD_WEB_PATH")
+ if host is not None and path is not None:
+ yield urlparse.ParseResult(
+ "http",
+ host,
+ path.rstrip('/'),
+ "", "", "")
+
## -- Model Objects --
@decorator.decorator
self._old_log = None
self._dsn = None
self._url = None
+ self._urlGen = None
def invalidateCache(self):
"""
Invalidates all cached variables. This currently applies to
elif not has_git and not has_scripts:
if os.path.isfile(".scripts-version"):
raise NotMigratedError(self.location)
+ else:
+ raise NotAutoinstallError(self.location)
def verifyTag(self, srv_path):
"""
"""
repo = self.application.repository(srv_path)
try:
- shell.Shell().eval("git", "--git-dir", repo, "rev-parse", self.app_version.scripts_tag, '--')
+ shell.eval("git", "--git-dir", repo, "rev-parse", self.app_version.scripts_tag, '--')
except shell.CallError:
raise NoTagError(self.app_version.scripts_tag)
corresponds to the one in the remote repository.
"""
with util.ChangeDirectory(self.location):
- sh = shell.Shell()
repo = self.application.repository(srv_path)
def repo_rev_parse(tag):
- return sh.eval("git", "--git-dir", repo, "rev-parse", tag)
+ return shell.eval("git", "--git-dir", repo, "rev-parse", tag)
def self_rev_parse(tag):
try:
- return sh.safeCall("git", "rev-parse", tag, strip=True)
+ return shell.safeCall("git", "rev-parse", tag, strip=True)
except shell.CallError:
raise NoLocalTagError(tag)
def compare_tags(tag):
if not compare_tags(self.app_version.scripts_tag):
raise InconsistentScriptsTagError(self.app_version.scripts_tag)
parent = repo_rev_parse(self.app_version.scripts_tag)
- merge_base = sh.safeCall("git", "merge-base", parent, "HEAD", strip=True)
+ merge_base = shell.safeCall("git", "merge-base", parent, "HEAD", strip=True)
if merge_base != parent:
raise HeadNotDescendantError(self.app_version.scripts_tag)
Checks if our version and the version number recorded in a file
are consistent.
"""
+ real = self.detectVersion()
+ if not str(real) == self.app_version.pristine_tag.partition('-')[2]:
+ raise VersionMismatchError(real, self.version)
+
+ @chdir_to_location
+ def detectVersion(self):
+ """
+ Returns the real version, based on filesystem, of install.
+
+ Throws a :class:`VersionDetectionError` if we couldn't figure out
+ what the real version was.
+ """
real = self.application.detectVersion(self)
if not real:
raise VersionDetectionError
- elif not str(real) == self.app_version.pristine_tag.partition('-')[2]:
- raise VersionMismatchError(real, self.version)
+ return real
@property
@chdir_to_location
except old_log.ScriptsVersionNoSuchFile:
pass
if not self._app_version:
- appname = shell.Shell().eval("git", "config", "remote.origin.url").rpartition("/")[2].partition(".")[0]
+ appname = shell.eval("git", "config", "remote.origin.url").rpartition("/")[2].partition(".")[0]
self._app_version = app.ApplicationVersion.make(appname, "unknown")
return self._app_version
@property
@property
def url(self):
"""The :class:`urlparse.ParseResult` for this deployment."""
- if not self._url:
- self._url = scripts.fill_url(self.location, self.application.url(self))
- if not self._url:
- raise UnknownWebPath
+ if not self._urlGen:
+ self._urlGen = web(self.location, self.application.url(self))
+ self.nextUrl()
return self._url
+ def nextUrl(self):
+ """"""
+ try:
+ self._url = self._urlGen.next() # pylint: disable-msg=E1101
+ return self._url
+ except StopIteration:
+ raise UnknownWebPath
+ def enableOldStyleUrls(self):
+ """
+ Switches to using http://user.scripts.mit.edu/~user/app URLs.
+ No effect if they have an explicit .scripts/url override.
+ """
+ # XXX: This is pretty scripts specific
+ self._url = scripts.fill_url(self.location, self.application.url(self), old_style = True)
@staticmethod
def parse(line):
"""
this application uses.
"""
self.application.remove(self, options)
+ def verifyDatabase(self):
+ """
+ Checks if the autoinstall has a properly configured database.
+ """
+ if not self.application.checkDatabase(self):
+ raise DatabaseVerificationError
def verifyWeb(self):
"""
- Checks if the autoinstall is viewable from the web.
+ Checks if the autoinstall is viewable from the web. If you do not run
+ this, there is no guarantee that the url returned by this application
+ is the correct one.
"""
- if not self.application.checkWeb(self):
- raise WebVerificationError
+ while True:
+ if not self.application.checkWeb(self):
+ try:
+ self.nextUrl()
+ except UnknownWebPath:
+ raise WebVerificationError
+ else:
+ break
def fetch(self, path, post=None):
"""
Performs a HTTP request on the website.
"""
- return util.fetch(self.url.netloc, self.url.path, path, post)
+ return util.fetch(self.url.netloc, self.url.path, path, post) # pylint: disable-msg=E1103
class WorkingCopy(Deployment):
"""
modifications to without fear of interfering with a production
deployment. More operations are permitted on these copies.
"""
+ def setAppVersion(self, app_version):
+ """
+ Manually resets the application version; useful if the working
+ copy is off in space (i.e. not anchored to something we can
+ git describe off of.)
+ """
+ self._app_version = app_version
@chdir_to_location
def parametrize(self, deployment):
"""
but not a .git directory."""
class NotAutoinstallError(Error):
- """The directory was not an autoinstall, due to missing .scripts-version file."""
- #: Directory in question
+ """Application is not an autoinstall."""
+ #: Directory of the not autoinstall
dir = None
def __init__(self, dir):
self.dir = dir
def __str__(self):
return """
-ERROR: Could not find .scripts-version file. Are you sure
-this is an autoinstalled application?
-"""
+ERROR: The directory
+
+ %s
+
+does not appear to be an autoinstall. If you are in a
+subdirectory of an autoinstall, you need to use the root
+directory for the autoinstall.""" % self.dir
class NoTagError(Error):
"""Deployment has a tag that does not have an equivalent in upstream repository."""
authentication on the htaccess level. You can find
the contents of the page from the debug backtraces."""
+class DatabaseVerificationError(Error):
+ """Could not access the database"""
+ def __str__(self):
+ return """
+
+ERROR: We were not able to access the database for
+this application; this probably means that your database
+configuration is misconfigured."""
+
class UnknownWebPath(Error):
"""Could not determine application's web path."""
def __str__(self):
on the application. You can specify this manually using
the WIZARD_WEB_HOST and WIZARD_WEB_PATH environment
variables."""
-