paul@0 | 1 | #!/usr/bin/env python |
paul@0 | 2 | |
paul@0 | 3 | from os.path import abspath, exists, join, split |
paul@0 | 4 | import os |
paul@0 | 5 | import sys |
paul@0 | 6 | import shutil |
paul@0 | 7 | import re |
paul@0 | 8 | |
paul@1 | 9 | # Regular expressions for editing MoinMoin scripts and configuration files. |
paul@1 | 10 | |
paul@2 | 11 | def compile_definition(name): |
paul@2 | 12 | return re.compile(r"^(\s*)#?(%s =).*$" % name, re.MULTILINE) |
paul@2 | 13 | |
paul@2 | 14 | moin_cgi_prefix = re.compile("^#sys.path.insert\(0, 'PREFIX.*$", re.MULTILINE) |
paul@2 | 15 | moin_cgi_wikiconfig = re.compile("^#sys.path.insert\(0, '/path/to/wikiconfigdir.*$", re.MULTILINE) |
paul@2 | 16 | wikiconfig_py_site_name = compile_definition("site_name") |
paul@2 | 17 | wikiconfig_py_url_prefix_static = compile_definition("url_prefix_static") |
paul@2 | 18 | wikiconfig_py_superuser = compile_definition("superuser") |
paul@2 | 19 | wikiconfig_py_acl_rights_before = compile_definition("acl_rights_before") |
paul@2 | 20 | wikiconfig_py_page_front_page = compile_definition("page_front_page") |
paul@2 | 21 | wikiconfig_py_data_dir = compile_definition("data_dir") |
paul@2 | 22 | wikiconfig_py_data_underlay_dir = compile_definition("data_underlay_dir") |
paul@1 | 23 | |
paul@1 | 24 | # Templates for Apache site definitions. |
paul@0 | 25 | |
paul@0 | 26 | apache_site = """ |
paul@0 | 27 | ScriptAlias %(url_path)s "%(web_app_dir)s/moin.cgi" |
paul@0 | 28 | """ |
paul@0 | 29 | |
paul@0 | 30 | apache_site_extra_moin18 = """ |
paul@2 | 31 | Alias %(static_url_path)s "%(htdocs_dir)s/" |
paul@2 | 32 | """ |
paul@2 | 33 | |
paul@2 | 34 | apache_site_host_restriction = """ |
paul@2 | 35 | <Location "%(url_path)s"> |
paul@2 | 36 | Deny from all |
paul@2 | 37 | Allow from %(superuser_client_host)s |
paul@2 | 38 | </Location> |
paul@0 | 39 | """ |
paul@0 | 40 | |
paul@1 | 41 | # Utility functions. |
paul@1 | 42 | |
paul@0 | 43 | def readfile(filename): |
paul@0 | 44 | f = open(filename) |
paul@0 | 45 | try: |
paul@0 | 46 | return f.read() |
paul@0 | 47 | finally: |
paul@0 | 48 | f.close() |
paul@0 | 49 | |
paul@0 | 50 | def writefile(filename, s): |
paul@0 | 51 | f = open(filename, "w") |
paul@0 | 52 | try: |
paul@0 | 53 | f.write(s) |
paul@0 | 54 | finally: |
paul@0 | 55 | f.close() |
paul@0 | 56 | |
paul@0 | 57 | def status(message): |
paul@0 | 58 | print message |
paul@0 | 59 | |
paul@0 | 60 | def note(message): |
paul@0 | 61 | print message |
paul@0 | 62 | |
paul@2 | 63 | class Installation: |
paul@0 | 64 | |
paul@2 | 65 | "A class for installing and initialising MoinMoin." |
paul@0 | 66 | |
paul@1 | 67 | # NOTE: Need to detect Web server user. |
paul@1 | 68 | |
paul@1 | 69 | web_user = "www-data" |
paul@1 | 70 | web_group = "www-data" |
paul@0 | 71 | |
paul@2 | 72 | def __init__(self, moin_distribution, prefix, web_app_dir, web_site_dir, |
paul@2 | 73 | common_dir, url_path, site_name, front_page_name, superuser): |
paul@2 | 74 | |
paul@2 | 75 | """ |
paul@2 | 76 | Initialise a Wiki installation using the following: |
paul@2 | 77 | |
paul@2 | 78 | * moin_distribution - the directory containing a MoinMoin source |
paul@2 | 79 | distribution |
paul@2 | 80 | * prefix - the installation prefix (equivalent to /usr) |
paul@2 | 81 | * web_app_dir - the directory where Web applications and scripts |
paul@2 | 82 | reside (such as /home/www-user/cgi-bin) |
paul@2 | 83 | * web_site_dir - the directory where Web site definitions reside |
paul@2 | 84 | (such as /etc/apache2/sites-available) |
paul@2 | 85 | * common_dir - the directory where the Wiki configuration, |
paul@2 | 86 | resources and instance will reside (such as |
paul@2 | 87 | /home/www-user/mywiki) |
paul@2 | 88 | * url_path - the URL path at which the Wiki will be made |
paul@2 | 89 | available (such as / or /mywiki) |
paul@2 | 90 | * site_name - the name of the site (such as "My Wiki") |
paul@2 | 91 | * front_page_name - the front page name for the site (such as |
paul@2 | 92 | "FrontPage" or a specific name for the site) |
paul@2 | 93 | * superuser - the name of the site's superuser (such as |
paul@2 | 94 | "AdminUser") |
paul@2 | 95 | """ |
paul@2 | 96 | |
paul@2 | 97 | self.moin_distribution = moin_distribution |
paul@2 | 98 | self.site_name = site_name |
paul@2 | 99 | self.front_page_name = front_page_name |
paul@2 | 100 | self.superuser = superuser |
paul@2 | 101 | |
paul@2 | 102 | # NOTE: Support the detection of the Apache sites directory. |
paul@2 | 103 | |
paul@2 | 104 | self.prefix, self.web_app_dir, self.web_site_dir, self.common_dir = \ |
paul@2 | 105 | map(abspath, (prefix, web_app_dir, web_site_dir, common_dir)) |
paul@2 | 106 | |
paul@2 | 107 | # Strip any trailing "/" from the URL path. |
paul@2 | 108 | |
paul@2 | 109 | if url_path.endswith("/"): |
paul@2 | 110 | self.url_path = url_path[:-1] |
paul@2 | 111 | else: |
paul@2 | 112 | self.url_path = url_path |
paul@2 | 113 | |
paul@2 | 114 | # Define and create specific directories. |
paul@2 | 115 | |
paul@2 | 116 | self.conf_dir = join(self.common_dir, "conf") |
paul@2 | 117 | self.instance_dir = join(self.common_dir, "wikidata") |
paul@2 | 118 | |
paul@2 | 119 | # For MoinMoin 1.8.x and earlier. |
paul@2 | 120 | |
paul@2 | 121 | self.htdocs_dir = join(self.instance_dir, "share", "moin", "htdocs") |
paul@2 | 122 | |
paul@2 | 123 | # Miscellaneous configuration options. |
paul@2 | 124 | |
paul@2 | 125 | self.superuser_client_host = "127.0.0.1" |
paul@2 | 126 | |
paul@2 | 127 | # Find the version. |
paul@2 | 128 | |
paul@2 | 129 | self.moin_version = self.get_moin_version() |
paul@2 | 130 | |
paul@2 | 131 | def get_moin_version(self): |
paul@2 | 132 | |
paul@2 | 133 | "Inspect the MoinMoin package information, returning the version." |
paul@2 | 134 | |
paul@2 | 135 | this_dir = os.getcwd() |
paul@2 | 136 | os.chdir(self.moin_distribution) |
paul@2 | 137 | |
paul@2 | 138 | f = open("PKG-INFO") |
paul@2 | 139 | try: |
paul@2 | 140 | for line in f.xreadlines(): |
paul@2 | 141 | columns = line.split() |
paul@2 | 142 | if columns[0] == "Version:": |
paul@2 | 143 | return columns[1] |
paul@2 | 144 | |
paul@2 | 145 | return None |
paul@2 | 146 | |
paul@2 | 147 | finally: |
paul@2 | 148 | f.close() |
paul@2 | 149 | os.chdir(this_dir) |
paul@2 | 150 | |
paul@2 | 151 | def setup(self): |
paul@2 | 152 | |
paul@2 | 153 | "Set up the installation." |
paul@2 | 154 | |
paul@2 | 155 | for d in (self.conf_dir, self.instance_dir, self.web_app_dir, self.web_site_dir): |
paul@2 | 156 | if not exists(d): |
paul@2 | 157 | os.makedirs(d) |
paul@2 | 158 | |
paul@2 | 159 | self.install_moin() |
paul@2 | 160 | self.install_data() |
paul@2 | 161 | self.configure_moin() |
paul@2 | 162 | self.edit_moin_scripts() |
paul@2 | 163 | self.make_site_files() |
paul@2 | 164 | self.make_post_install_script() |
paul@2 | 165 | |
paul@2 | 166 | def install_moin(self): |
paul@2 | 167 | |
paul@2 | 168 | "Enter the distribution directory and run the setup script." |
paul@2 | 169 | |
paul@2 | 170 | # NOTE: Possibly check for an existing installation and skip repeated |
paul@2 | 171 | # NOTE: installation attempts. |
paul@2 | 172 | |
paul@2 | 173 | this_dir = os.getcwd() |
paul@2 | 174 | os.chdir(self.moin_distribution) |
paul@2 | 175 | |
paul@2 | 176 | log_filename = "install-%s.log" % split(self.common_dir)[-1] |
paul@2 | 177 | |
paul@2 | 178 | status("Installing MoinMoin in %s..." % self.prefix) |
paul@2 | 179 | |
paul@2 | 180 | os.system("python setup.py --quiet install --force --prefix='%s' --install-data='%s' --record='%s'" % ( |
paul@2 | 181 | self.prefix, self.instance_dir, log_filename)) |
paul@2 | 182 | |
paul@2 | 183 | os.chdir(this_dir) |
paul@2 | 184 | |
paul@2 | 185 | def install_data(self): |
paul@2 | 186 | |
paul@2 | 187 | "Install Wiki data." |
paul@2 | 188 | |
paul@2 | 189 | # The default wikiconfig assumes data and underlay in the same directory. |
paul@2 | 190 | |
paul@2 | 191 | status("Installing data and underlay in %s..." % self.conf_dir) |
paul@2 | 192 | |
paul@2 | 193 | for d in ("data", "underlay"): |
paul@2 | 194 | shutil.copytree(join(self.moin_distribution, "wiki", d), join(self.conf_dir, d)) |
paul@2 | 195 | |
paul@2 | 196 | def configure_moin(self): |
paul@2 | 197 | |
paul@2 | 198 | "Edit the Wiki configuration file." |
paul@2 | 199 | |
paul@2 | 200 | # NOTE: Single Wiki only so far. |
paul@2 | 201 | |
paul@2 | 202 | # Static URLs seem to be different in MoinMoin 1.9.x. |
paul@2 | 203 | # For earlier versions, reserve URL space alongside the Wiki. |
paul@2 | 204 | # NOTE: MoinMoin usually uses an apparently common URL space associated |
paul@2 | 205 | # NOTE: with the version, but more specific locations are probably |
paul@2 | 206 | # NOTE: acceptable if less efficient. |
paul@2 | 207 | |
paul@2 | 208 | if self.moin_version.startswith("1.9"): |
paul@2 | 209 | self.static_url_path = self.url_path |
paul@2 | 210 | url_prefix_static_sub = r"\1\2 %r + url_prefix_static" % self.static_url_path |
paul@2 | 211 | else: |
paul@2 | 212 | self.static_url_path = self.url_path + "-static" |
paul@2 | 213 | url_prefix_static_sub = r"\1\2 %r" % self.static_url_path |
paul@2 | 214 | |
paul@2 | 215 | # Edit the Wiki configuration file. |
paul@2 | 216 | |
paul@2 | 217 | wikiconfig_py = join(self.moin_distribution, "wiki", "config", "wikiconfig.py") |
paul@2 | 218 | |
paul@2 | 219 | status("Editing configuration from %s..." % wikiconfig_py) |
paul@2 | 220 | |
paul@2 | 221 | s = readfile(wikiconfig_py) |
paul@2 | 222 | s = wikiconfig_py_site_name.sub(r"\1\2 %r" % self.site_name, s) |
paul@2 | 223 | s = wikiconfig_py_url_prefix_static.sub(url_prefix_static_sub, s) |
paul@2 | 224 | s = wikiconfig_py_superuser.sub(r"\1\2 %r" % [self.superuser], s) |
paul@2 | 225 | s = wikiconfig_py_acl_rights_before.sub(r"\1\2 %r" % (u"%s:read,write,delete,revert,admin" % self.superuser), s) |
paul@2 | 226 | s = wikiconfig_py_page_front_page.sub(r"\1\2 %r" % self.front_page_name, s, count=1) |
paul@2 | 227 | |
paul@2 | 228 | if not self.moin_version.startswith("1.9"): |
paul@2 | 229 | data_dir = join(self.conf_dir, "data") |
paul@2 | 230 | data_underlay_dir = join(self.conf_dir, "underlay") |
paul@2 | 231 | |
paul@2 | 232 | s = wikiconfig_py_data_dir.sub(r"\1\2 %r" % data_dir, s) |
paul@2 | 233 | s = wikiconfig_py_data_underlay_dir.sub(r"\1\2 %r" % data_underlay_dir, s) |
paul@2 | 234 | |
paul@2 | 235 | writefile(join(self.conf_dir, "wikiconfig.py"), s) |
paul@2 | 236 | |
paul@2 | 237 | def edit_moin_scripts(self): |
paul@2 | 238 | |
paul@2 | 239 | "Edit the moin script and the CGI script." |
paul@2 | 240 | |
paul@2 | 241 | moin_script = join(self.prefix, "bin", "moin") |
paul@2 | 242 | prefix_site_packages = join(self.prefix, "lib", "python%s.%s" % sys.version_info[:2], "site-packages") |
paul@2 | 243 | |
paul@2 | 244 | status("Editing moin script at %s..." % moin_script) |
paul@2 | 245 | |
paul@2 | 246 | s = readfile(moin_script) |
paul@2 | 247 | s = s.replace("#import sys", "import sys\nsys.path.insert(0, %r)" % prefix_site_packages) |
paul@2 | 248 | |
paul@2 | 249 | writefile(moin_script, s) |
paul@2 | 250 | |
paul@2 | 251 | # Edit and install CGI script. |
paul@2 | 252 | # NOTE: CGI only so far. |
paul@2 | 253 | # NOTE: Permissions should be checked. |
paul@2 | 254 | |
paul@2 | 255 | moin_cgi = join(self.instance_dir, "share", "moin", "server", "moin.cgi") |
paul@2 | 256 | moin_cgi_installed = join(self.web_app_dir, "moin.cgi") |
paul@2 | 257 | |
paul@2 | 258 | status("Editing moin.cgi script from %s..." % moin_cgi) |
paul@2 | 259 | |
paul@2 | 260 | s = readfile(moin_cgi) |
paul@2 | 261 | s = moin_cgi_prefix.sub("sys.path.insert(0, %r)" % prefix_site_packages, s) |
paul@2 | 262 | s = moin_cgi_wikiconfig.sub("sys.path.insert(0, %r)" % self.conf_dir, s) |
paul@2 | 263 | |
paul@2 | 264 | writefile(moin_cgi_installed, s) |
paul@2 | 265 | os.system("chmod a+rx '%s'" % moin_cgi_installed) |
paul@2 | 266 | |
paul@2 | 267 | def make_site_files(self): |
paul@2 | 268 | |
paul@2 | 269 | "Make the Apache site files." |
paul@2 | 270 | |
paul@2 | 271 | # NOTE: Using local namespace for substitution. |
paul@2 | 272 | |
paul@2 | 273 | site_def = join(self.web_site_dir, self.site_name) |
paul@2 | 274 | site_def_private = join(self.web_site_dir, "%s-private" % self.site_name) |
paul@2 | 275 | |
paul@2 | 276 | status("Writing Apache site definitions to %s and %s..." % (site_def, site_def_private)) |
paul@2 | 277 | |
paul@2 | 278 | s = apache_site % self.__dict__ |
paul@2 | 279 | |
paul@2 | 280 | if not self.moin_version.startswith("1.9"): |
paul@2 | 281 | s += apache_site_extra_moin18 % self.__dict__ |
paul@2 | 282 | |
paul@2 | 283 | writefile(site_def, s) |
paul@2 | 284 | |
paul@2 | 285 | # NOTE: By making the superuser in the installed Wiki, this site file |
paul@2 | 286 | # NOTE: would no longer be required. |
paul@2 | 287 | |
paul@2 | 288 | s += apache_site_host_restriction % self.__dict__ |
paul@2 | 289 | |
paul@2 | 290 | writefile(site_def_private, s) |
paul@2 | 291 | |
paul@2 | 292 | def make_post_install_script(self): |
paul@2 | 293 | |
paul@2 | 294 | "Write a post-install script with additional actions." |
paul@2 | 295 | |
paul@2 | 296 | this_user = os.environ["USER"] |
paul@2 | 297 | postinst_script = "moinsetup-post.sh" |
paul@2 | 298 | |
paul@2 | 299 | s = "" |
paul@2 | 300 | |
paul@2 | 301 | for d in ("data", "underlay"): |
paul@2 | 302 | s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, join(self.conf_dir, d)) |
paul@2 | 303 | s += "chmod -R g+w '%s'\n" % join(self.conf_dir, d) |
paul@2 | 304 | |
paul@2 | 305 | if not self.moin_version.startswith("1.9"): |
paul@2 | 306 | s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, self.htdocs_dir) |
paul@2 | 307 | |
paul@2 | 308 | writefile(postinst_script, s) |
paul@2 | 309 | note("Run %s as root to set file ownership and permissions." % postinst_script) |
paul@1 | 310 | |
paul@1 | 311 | # Main program. |
paul@0 | 312 | |
paul@0 | 313 | if __name__ == "__main__": |
paul@1 | 314 | |
paul@1 | 315 | # Obtain as many arguments as needed for the setup function. |
paul@1 | 316 | |
paul@0 | 317 | try: |
paul@2 | 318 | n = 9 # number of Installation initialiser arguments |
paul@0 | 319 | args = sys.argv[1:n+1] |
paul@0 | 320 | args[n-1] |
paul@0 | 321 | except (IndexError, ValueError): |
paul@2 | 322 | print Installation.__init__.__doc__ |
paul@0 | 323 | sys.exit(1) |
paul@0 | 324 | |
paul@2 | 325 | installation = Installation(*args) |
paul@2 | 326 | installation.setup() |
paul@0 | 327 | |
paul@0 | 328 | # vim: tabstop=4 expandtab shiftwidth=4 |