* Make MediaWiki regex more lenient with trailing newlines
* Add check to see if application is configured; if it isn't
bail out and don't migrate.
* Perform increments early enough so that numeric IDs are unique
* Fix bug in increment printing
* fdopen() doesn't work, so simply create a blank lockfile
* Make force removal log message more descriptive
* Remove .scripts/old-version code
* Prefer git describe method, but if it doesn't work switch
to .scripts-version
* Make shell return appropriate values during dry runs
* Make call error more descriptive (this messes with exception
counting, however.)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
- 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)
- 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
- 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)
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
- 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):
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)
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
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")
def install(self, options):
try:
os.unlink("LocalSettings.php")
continue
name = d.application.name
if name != app: continue
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 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 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))
- # 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))
# 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)
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 \
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
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
version = deployment.app_version
repo = version.application.repository(options.srv_path)
tag = version.scripts_tag
- 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
except OSError as e:
if e.errno == errno.EEXIST:
raise DirectoryLockedError
name = "%s.%d" % (prefix, i)
if not os.path.exists(name):
break
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:
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:
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")
if not options.dry_run:
rm_with_backup(".scripts")
install using Git. You cannot force this case.
"""
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
class CorruptedAutoinstallError(Error):
def __init__(self, dir):
self.dir = dir
import logging
import wizard
import logging
import wizard
-from wizard import git, old_log, util
+from wizard import git, old_log, shell, util
## -- Global Functions --
## -- Global Functions --
is replaced with generic WIZARD_* variables.
"""
return self.application.prepareConfig(self)
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."""
@property
def migrated(self):
"""Whether or not the autoinstalls has been migrated."""
Use of this is discouraged for migrated installs.
"""
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."""
@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")):
"""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):
return self._app_version
@staticmethod
def parse(line):
else:
logging.info(msg)
if self.dry:
else:
logging.info(msg)
if self.dry:
+ if kwargs["strip"]:
+ return ''
+ return None, None
if kwargs["python"] is None and is_python(args):
kwargs["python"] = True
if args[0] == "wizard":
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):
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):
"""
class PythonCallError(CallError):
"""