2 import distutils.version
9 from wizard import app, deploy, install, resolve, scripts, shell, util
10 from wizard.app import php
12 def make_filename_regex(var):
13 return 'LocalSettings.php', re.compile('^(\$' + app.expand_re(var) + r'''\s*=\s*)(.*)(;)''', re.M)
15 make_extractor = app.filename_regex_extractor(make_filename_regex)
16 make_substitution = app.filename_regex_substitution(make_filename_regex)
18 'WIZARD_IP': 'IP', # obsolete, remove after we're done
19 'WIZARD_SITENAME': 'wgSitename',
20 'WIZARD_SCRIPTPATH': 'wgScriptPath',
21 'WIZARD_EMERGENCYCONTACT': ('wgEmergencyContact', 'wgPasswordSender'),
22 'WIZARD_DBSERVER': 'wgDBserver',
23 'WIZARD_DBNAME': 'wgDBname',
24 'WIZARD_DBUSER': 'wgDBuser',
25 'WIZARD_DBPASSWORD': 'wgDBpassword',
26 'WIZARD_SECRETKEY': ('wgSecretKey', 'wgProxyKey'),
30 'LocalSettings.php': [
35 ## The URL base path to the directory containing the wiki;
36 ## defaults for all runtime URL paths are based off of this.
37 ## For more information on customizing the URLs please see:
38 ## http://www.mediawiki.org/wiki/Manual:Short_URL
40 $wgScriptExtension = ".php";
42 ## UPO means: this is also a user preference option
50 # MySQL specific settings
53 """, ["\n# MySQL specific settings", 1]),
56 ## is writable, then uncomment this:
59 ## is writable, then set this to true:
60 $wgEnableUploads = false;
66 $wgMathPath = "{$wgUploadPath}/math";
67 $wgMathDirectory = "{$wgUploadDirectory}/math";
68 $wgTmpDirectory = "{$wgUploadDirectory}/tmp";
73 # order of these rules is important
78 # When you make changes to this configuration file, this will make
79 # sure that cached pages are cleared.
80 $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', @filemtime( __FILE__ ) ) );
88 # When you make changes to this configuration file, this will make
89 # sure that cached pages are cleared.
90 $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', @filemtime( __FILE__ ) ) );
97 # When you make changes to this configuration file, this will make
98 # sure that cached pages are cleared.
99 $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', @filemtime( __FILE__ ) ) );
105 class Application(deploy.Application):
106 parametrized_files = ['LocalSettings.php', 'php.ini']
107 deprecated_keys = set(['WIZARD_IP']) | php.deprecated_keys
109 def extractors(self):
110 if not self._extractors:
111 self._extractors = util.dictmap(make_extractor, seed)
112 self._extractors.update(php.extractors)
113 return self._extractors
115 def substitutions(self):
116 if not self._substitutions:
117 self._substitutions = util.dictkmap(make_substitution, seed)
118 self._substitutions.update(php.substitutions)
119 return self._substitutions
121 def install_handler(self):
122 handler = install.ArgHandler("mysql", "admin", "email")
123 handler.add(install.Arg("title", help="Title of your new MediaWiki install"))
125 def checkConfig(self, deployment):
126 return os.path.isfile(os.path.join(deployment.location, "LocalSettings.php"))
127 def detectVersion(self, deployment):
128 contents = deployment.read("includes/DefaultSettings.php")
129 regex = make_filename_regex("wgVersion")[1]
130 match = regex.search(contents)
131 if not match: return None
132 return distutils.version.LooseVersion(match.group(2)[1:-1])
133 def checkWeb(self, d, out=None):
134 page = d.fetch("/index.php?title=Main_Page")
135 if type(out) is list:
137 return page.find("<!-- Served") != -1
138 def prepareMerge(self, dir):
139 with util.ChangeDirectory(dir):
140 # XXX: this should be factored out
141 contents = open("LocalSettings.php", "r").read()
142 new_contents = "\n".join(contents.splitlines())
143 if contents != new_contents:
144 open("LocalSettings.php", "w").write(contents)
145 def resolveConflicts(self, dir):
146 # XXX: this is pretty generic
148 with util.ChangeDirectory(dir):
150 for status in sh.eval("git", "ls-files", "--unmerged").splitlines():
151 file = status.split()[-1]
152 if file in resolutions:
153 contents = open(file, "r").read()
154 for spec, result in resolutions[file]:
155 old_contents = contents
156 contents = resolve.resolve(contents, spec, result)
157 if old_contents != contents:
158 logging.info("Did resolution with spec:\n" + spec)
159 open(file, "w").write(contents)
160 if not resolve.is_conflict(contents):
161 sh.call("git", "add", file)
167 def install(self, version, options):
169 os.unlink("LocalSettings.php")
173 os.chmod("config", 0777) # XXX: vaguely sketchy
176 'Sitename': options.title,
177 'EmergencyContact': options.email,
178 'LanguageCode': 'en',
179 'DBserver': options.mysql_host,
180 'DBname': options.mysql_db,
181 'DBuser': options.mysql_user,
182 'DBpassword': options.mysql_password,
183 'DBpassword2': options.mysql_password,
184 'defaultEmail': options.email,
185 'SysopName': options.admin_name,
186 'SysopPass': options.admin_password,
187 'SysopPass2': options.admin_password,
189 result = install.fetch(options, '/config/index.php', post=postdata)
190 if options.verbose: print result
191 if result.find("Installation successful") == -1:
192 raise install.Failure()
193 os.rename('config/LocalSettings.php', 'LocalSettings.php')
194 def upgrade(self, d, version, options):
196 if not os.path.isfile("AdminSettings.php"):
197 sh.call("git", "checkout", "-q", "mediawiki-" + str(version), "--", "AdminSettings.php")
199 result = sh.eval("php", "maintenance/update.php", "--quick", log=True)
200 except shell.CallError as e:
201 raise app.UpgradeFailure("Update script returned non-zero exit code\nSTDOUT: %s\nSTDERR: %s" % (e.stdout, e.stderr))
202 results = result.rstrip().split()
203 if not results or not results[-1] == "Done.":
204 raise app.UpgradeFailure(result)
205 def backup(self, deployment, options):
207 # XXX: duplicate code, refactor, also, race condition
208 backupdir = os.path.join(".scripts", "backups")
209 backup = str(deployment.version) + "-" + datetime.date.today().isoformat()
210 outdir = os.path.join(backupdir, backup)
211 if not os.path.exists(backupdir):
213 if os.path.exists(outdir):
214 util.safe_unlink(outdir)
216 outfile = os.path.join(outdir, "db.sql")
218 sh.call("mysqldump", "--compress", "-r", outfile, *get_mysql_args(deployment))
219 sh.call("gzip", "--best", outfile)
220 except shell.CallError as e:
221 shutil.rmtree(outdir)
222 raise app.BackupFailure(e.stderr)
224 def restore(self, deployment, backup, options):
226 backup_dir = os.path.join(".scripts", "backups", backup)
227 if not os.path.exists(backup_dir):
228 raise app.RestoreFailure("Backup %s doesn't exist", backup)
229 sql = open(os.path.join(backup_dir, "db.sql"), 'w+')
230 sh.call("gunzip", "-c", os.path.join(backup_dir, "db.sql.gz"), stdout=sql)
232 sh.call("mysql", *get_mysql_args(deployment), stdin=sql)
235 def get_mysql_args(d):
236 # XXX: add support for getting these out of options
238 if 'WIZARD_DBNAME' not in vars:
239 raise app.BackupFailure("Could not determine database name")
240 triplet = scripts.get_sql_credentials(vars)
242 if triplet is not None:
243 server, user, password = triplet
244 args += ["-h", server, "-u", user, "-p" + password]
245 name = shlex.split(vars['WIZARD_DBNAME'])[0]