moinsetup

moinsetup.py

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