import re import distutils.version import os import datetime import logging import shlex import shutil from wizard import app, deploy, install, scripts, shell, util from wizard.app import php def make_filename_regex(var): 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) seed = { 'WIZARD_IP': 'IP', # obsolete, remove after we're done 'WIZARD_SITENAME': 'wgSitename', 'WIZARD_SCRIPTPATH': 'wgScriptPath', 'WIZARD_EMERGENCYCONTACT': ('wgEmergencyContact', 'wgPasswordSender'), 'WIZARD_DBSERVER': 'wgDBserver', 'WIZARD_DBNAME': 'wgDBname', 'WIZARD_DBUSER': 'wgDBuser', 'WIZARD_DBPASSWORD': 'wgDBpassword', 'WIZARD_SECRETKEY': ('wgSecretKey', 'wgProxyKey'), } class Application(deploy.Application): parametrized_files = ['LocalSettings.php', 'php.ini'] deprecated_keys = set(['WIZARD_IP']) | php.deprecated_keys @property def extractors(self): if not self._extractors: self._extractors = util.dictmap(make_extractor, seed) self._extractors.update(php.extractors) return self._extractors @property def substitutions(self): if not self._substitutions: self._substitutions = util.dictkmap(make_substitution, seed) self._substitutions.update(php.substitutions) return self._substitutions @property def install_handler(self): 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 detectVersion(self, deployment): contents = deployment.read("includes/DefaultSettings.php") regex = make_filename_regex("wgVersion")[1] match = regex.search(contents) if not match: return None return distutils.version.LooseVersion(match.group(2)[1:-1]) def install(self, version, options): try: os.unlink("LocalSettings.php") except OSError: pass os.chmod("config", 0777) # XXX: vaguely sketchy postdata = { 'Sitename': options.title, 'EmergencyContact': options.email, 'LanguageCode': 'en', 'DBserver': options.mysql_host, 'DBname': options.mysql_db, 'DBuser': options.mysql_user, 'DBpassword': options.mysql_password, 'DBpassword2': options.mysql_password, 'defaultEmail': options.email, 'SysopName': options.admin_name, 'SysopPass': options.admin_password, 'SysopPass2': options.admin_password, } result = install.fetch(options, 'config/index.php', post=postdata) if options.verbose: print result if result.find("Installation successful") == -1: raise install.Failure() os.rename('config/LocalSettings.php', 'LocalSettings.php') def upgrade(self, version, options): sh = shell.Shell() if not os.path.isfile("AdminSettings.php"): sh.call("git", "checkout", "mediawiki-" + str(version), "--", "AdminSettings.php") result = sh.eval("php", "maintenance/update.php", "--quick", log=True) if not result.rstrip().split()[-1] == "Done.": raise app.UpgradeFailure(result) def backup(self, deployment, options): sh = shell.Shell() # XXX: duplicate code, refactor, also, race condition backupdir = os.path.join(".scripts", "backups") outdir = os.path.join(backupdir, str(deployment.version) + "-" + datetime.date.today().isoformat()) if not os.path.exists(backupdir): os.mkdir(backupdir) if os.path.exists(outdir): util.safe_unlink(outdir) os.mkdir(outdir) outfile = os.path.join(outdir, "db.sql") try: sh.call("mysqldump", "--compress", "-r", outfile, *get_mysql_args(deployment)) sh.call("gzip", "--best", outfile) except shell.CallError as e: raise app.BackupFailure(e.stderr) def restore(self, deployment, backup, options): sh = shell.Shell() backup_dir = os.path.join(".scripts", "backups", backup) if not os.path.exists(backup_dir): raise app.RestoreFailure("Backup %s doesn't exist", backup) sql = open(os.path.join(backup_dir, "db.sql"), 'w+') sh.call("gunzip", "-c", os.path.join(backup_dir, "db.sql.gz"), stdout=sql) sql.seek(0) sh.call("mysql", *get_mysql_args(deployment), stdin=sql) sql.close() def get_mysql_args(d): # XXX: add support for getting these out of options vars = d.extract() if 'WIZARD_DBNAME' not in vars: raise app.BackupFailure("Could not determine database name") triplet = scripts.get_sql_credentials() args = [] if triplet is not None: server, user, password = triplet args += ["-h", server, "-u", user, "-p" + password] name = shlex.split(vars['WIZARD_DBNAME'])[0] args.append(name) return args