- Allow to migrate just one user (user filtering of installs, also
has userland capabilities, although it means we need some way of
selectively publishing the versions directory)
+- Make migrate script rollback if it's interrupted (especially if
+ by signal)
- Make parallel-find.pl use `sudo -u username git describe --tags`
to determine version. Make parallel-find.pl have this have greater
for mass-rollbacks).
- Have the upgrader do locking (.scripts/lock, probably)
+- Relax MediaWiki regexes to terminate on semicolon, and not
+ require its own line.
+
- Better error message if daemon/scripts-security-upd
is not on scripts-security-upd list
from wizard.app import php
def make_filename_regex(var):
- return 'LocalSettings.php', re.compile('^(\$' + app.expand_re(var) + r'''\s*=\s*)(.*)(;)$''', re.M)
+ return 'LocalSettings.php', re.compile('^(\$' + app.expand_re(var) + r'''\s*=\s*)(.*)(;)''', re.M)
make_extractor = app.filename_regex_extractor(make_filename_regex)
make_substitution = app.filename_regex_substitution(make_filename_regex)
handler = install.ArgHandler("mysql", "admin", "email")
handler.add(install.Arg("title", help="Title of your new MediaWiki install"))
return handler
+ def checkConfig(self, deployment):
+ return os.path.isfile(os.path.join(deployment.location, "LocalSettings.php"))
def install(self, options):
try:
os.unlink("LocalSettings.php")
continue
name = d.application.name
if name != app: continue
+ # check if we want to punt due to --limit
+ i += 1
+ if options.limit and i > options.limit:
+ break
if d.location in seen:
continue
# security check: see if the user's directory is the prefix of
if not my_uid:
uid = util.get_dir_uid(d.location)
real = os.path.realpath(d.location)
- if not real.startswith(pwd.getpwuid(uid).pw_dir + "/"):
- logging.error("Security check failed, owner of deployment and owner of home directory mismatch for %s" % d.location)
+ try:
+ if not real.startswith(pwd.getpwuid(uid).pw_dir + "/"):
+ logging.error("Security check failed, owner of deployment and owner of home directory mismatch for %s" % d.location)
+ continue
+ except KeyError:
+ logging.error("Security check failed, could not look up owner of %s (uid %d)" % (d.location, uid))
continue
- # check if we want to punt due to --limit
- i += 1
- if options.limit and i > options.limit:
- break
# calculate the log file, if a log dir was specified
if options.log_dir:
log_file = os.path.join(options.log_dir, shorten(i, d.location))
def on_success(stdout, stderr):
if stderr:
warnings_log.write("%s\n" % d.location)
- logging.warning("Warnings [%04d] %s:\n%s" % (d.location, i, stderr))
+ logging.warning("Warnings [%04d] %s:\n%s" % (i, d.location, stderr))
seen.add(d.location)
def on_error(e):
if e.name == "wizard.command.migrate.AlreadyMigratedError" or \
logging.debug("uid is %d" % os.getuid())
deployment = make_deployment() # uses chdir
+
+ if not deployment.configured:
+ raise NotConfiguredError(deployment.location)
+
version = deployment.app_version
repo = version.application.repository(options.srv_path)
tag = version.scripts_tag
try:
try:
- os.fdopen(os.open(".scripts-migrate-lock", os.O_CREAT | os.O_EXCL)).write(str(os.getpid()))
+ os.open(".scripts-migrate-lock", os.O_CREAT | os.O_EXCL)
except OSError as e:
if e.errno == errno.EEXIST:
raise DirectoryLockedError
name = "%s.%d" % (prefix, i)
if not os.path.exists(name):
break
+ logging.warning("Force removing %s directory (backup at %s)" % (file, name))
os.rename(file, name)
if has_git:
- logging.warning("Force removing .git directory")
if not options.dry_run:
rm_with_backup(".git")
if has_scripts:
- logging.warning("Force removing .scripts directory")
if not options.dry_run:
rm_with_backup(".scripts")
install using Git. You cannot force this case.
"""
+class NotConfiguredError(Error):
+ def __init__(self, dir):
+ self.dir = dir
+ def __str__(self):
+ return """
+
+ERROR: The install was well-formed, but not configured
+(essential configuration files were not found.)
+"""
+
class CorruptedAutoinstallError(Error):
def __init__(self, dir):
self.dir = dir
import logging
import wizard
-from wizard import git, old_log, util
+from wizard import git, old_log, shell, util
## -- Global Functions --
is replaced with generic WIZARD_* variables.
"""
return self.application.prepareConfig(self)
+ def checkConfig(self, deployment):
+ """
+ Checks if the application is configured.
+ """
+ raise NotImplemented
+ @property
+ def configured(self):
+ """Whether or not an autoinstall has been configured/installed for use."""
+ return self.application.checkConfig(self)
@property
def migrated(self):
"""Whether or not the autoinstalls has been migrated."""
Use of this is discouraged for migrated installs.
"""
- if self.migrated:
- return os.path.join(self.scripts_dir, 'old-version')
- else:
- return os.path.join(self.location, '.scripts-version')
+ return os.path.join(self.location, '.scripts-version')
@property
def version_file(self):
"""The absolute path of the ``.scripts/version`` file."""
"""The :class:`ApplicationVersion` of this deployment."""
if not self._app_version:
if os.path.isdir(os.path.join(self.location, ".git")):
- with util.ChangeDirectory(self.location):
- appname, _, version = git.describe().partition('-')
- self._app_version = ApplicationVersion.make(appname, version)
- else:
- self._app_version = self.old_log[-1].version
+ try:
+ with util.ChangeDirectory(self.location):
+ appname, _, version = git.describe().partition('-')
+ self._app_version = ApplicationVersion.make(appname, version)
+ except shell.CallError:
+ pass
+ if not self._app_version:
+ self._app_version = self.old_log[-1].version
return self._app_version
@staticmethod
def parse(line):
else:
logging.info(msg)
if self.dry:
- return
+ if kwargs["strip"]:
+ return ''
+ return None, None
if kwargs["python"] is None and is_python(args):
kwargs["python"] = True
if args[0] == "wizard":
self.stdout = stdout
self.stderr = stderr
def __str__(self):
- return "CallError [%d]\n%s" % (self.code, self.stderr)
+ compact = self.stderr.rstrip().split("\n")[-1]
+ return "%s (exited with %d)\n%s" % (compact, self.code, self.stderr)
class PythonCallError(CallError):
"""