moinsetup

moinsetup.py

7:6bf43fcaff81
2010-05-13 Paul Boddie Introduced cmdsyntax usage in order to support more convenient option parsing and selection of individual actions.
     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         # Find the version.   118    119         self.moin_version = self.get_moin_version()   120    121     def get_moin_version(self):   122    123         "Inspect the MoinMoin package information, returning the version."   124    125         this_dir = os.getcwd()   126         os.chdir(self.moin_distribution)   127    128         try:   129             try:   130                 f = open("PKG-INFO")   131                 try:   132                     for line in f.xreadlines():   133                         columns = line.split()   134                         if columns[0] == "Version:":   135                             return columns[1]   136    137                     return None   138    139                 finally:   140                     f.close()   141    142             except IOError:   143                 f = os.popen("%s -c 'from MoinMoin.version import release; print release'" % sys.executable)   144                 try:   145                     return f.read()   146                 finally:   147                     f.close()   148         finally:   149             os.chdir(this_dir)   150    151     def setup(self):   152    153         "Set up the installation."   154    155         for d in (self.conf_dir, self.instance_dir, self.web_app_dir, self.web_site_dir):   156             if not exists(d):   157                 os.makedirs(d)   158    159         self.install_moin()   160         self.install_data()   161         self.configure_moin()   162         self.edit_moin_scripts()   163         self.add_superuser()   164         self.make_site_files()   165         self.make_post_install_script()   166    167     def install_moin(self):   168    169         "Enter the distribution directory and run the setup script."   170    171         # NOTE: Possibly check for an existing installation and skip repeated   172         # NOTE: installation attempts.   173    174         this_dir = os.getcwd()   175         os.chdir(self.moin_distribution)   176    177         log_filename = "install-%s.log" % split(self.common_dir)[-1]   178    179         status("Installing MoinMoin in %s..." % self.prefix)   180    181         os.system("python setup.py --quiet install --force --prefix='%s' --install-data='%s' --record='%s'" % (   182             self.prefix, self.instance_dir, log_filename))   183    184         os.chdir(this_dir)   185    186     def install_data(self):   187    188         "Install Wiki data."   189    190         # The default wikiconfig assumes data and underlay in the same directory.   191    192         status("Installing data and underlay in %s..." % self.conf_dir)   193    194         for d in ("data", "underlay"):   195             source = join(self.moin_distribution, "wiki", d)   196             source_tar = source + os.path.extsep + "tar"   197             d_tar = source + os.path.extsep + "tar"   198    199             if os.path.exists(source):   200                 shutil.copytree(source, join(self.conf_dir, d))   201             elif os.path.exists(source_tar):   202                 shutil.copy(source_tar, self.conf_dir)   203                 os.system("tar xf %s -C %s" % (d_tar, self.conf_dir))   204             else:   205                 status("Could not copy %s into installed Wiki." % d)   206    207     def configure_moin(self):   208    209         "Edit the Wiki configuration file."   210    211         # NOTE: Single Wiki only so far.   212    213         # Static URLs seem to be different in MoinMoin 1.9.x.   214         # For earlier versions, reserve URL space alongside the Wiki.   215         # NOTE: MoinMoin usually uses an apparently common URL space associated   216         # NOTE: with the version, but more specific locations are probably   217         # NOTE: acceptable if less efficient.   218    219         if self.moin_version.startswith("1.9"):   220             self.static_url_path = self.url_path   221             url_prefix_static_sub = r"\1\2 %r + url_prefix_static" % self.static_url_path   222         else:   223             self.static_url_path = self.url_path + "-static"   224             url_prefix_static_sub = r"\1\2 %r" % self.static_url_path   225    226         # Edit the Wiki configuration file.   227    228         wikiconfig_py = join(self.moin_distribution, "wiki", "config", "wikiconfig.py")   229    230         status("Editing configuration from %s..." % wikiconfig_py)   231    232         s = readfile(wikiconfig_py)   233         s = wikiconfig_py_site_name.sub(r"\1\2 %r" % self.site_name, s)   234         s = wikiconfig_py_url_prefix_static.sub(url_prefix_static_sub, s)   235         s = wikiconfig_py_superuser.sub(r"\1\2 %r" % [self.superuser], s)   236         s = wikiconfig_py_acl_rights_before.sub(r"\1\2 %r" % (u"%s:read,write,delete,revert,admin" % self.superuser), s)   237         s = wikiconfig_py_page_front_page.sub(r"\1\2 %r" % self.front_page_name, s, count=1)   238    239         if not self.moin_version.startswith("1.9"):   240             data_dir = join(self.conf_dir, "data")   241             data_underlay_dir = join(self.conf_dir, "underlay")   242    243             s = wikiconfig_py_data_dir.sub(r"\1\2 %r" % data_dir, s)   244             s = wikiconfig_py_data_underlay_dir.sub(r"\1\2 %r" % data_underlay_dir, s)   245    246         writefile(join(self.conf_dir, "wikiconfig.py"), s)   247    248     def edit_moin_scripts(self):   249    250         "Edit the moin script and the CGI script."   251    252         moin_script = join(self.prefix, "bin", "moin")   253         prefix_site_packages = join(self.prefix, "lib", "python%s.%s" % sys.version_info[:2], "site-packages")   254    255         status("Editing moin script at %s..." % moin_script)   256    257         s = readfile(moin_script)   258         s = s.replace("#import sys", "import sys\nsys.path.insert(0, %r)" % prefix_site_packages)   259    260         writefile(moin_script, s)   261    262         # Edit and install CGI script.   263         # NOTE: CGI only so far.   264         # NOTE: Permissions should be checked.   265    266         moin_cgi = join(self.instance_dir, "share", "moin", "server", "moin.cgi")   267         moin_cgi_installed = join(self.web_app_dir, "moin.cgi")   268    269         status("Editing moin.cgi script from %s..." % moin_cgi)   270    271         s = readfile(moin_cgi)   272         s = moin_cgi_prefix.sub("sys.path.insert(0, %r)" % prefix_site_packages, s)   273         s = moin_cgi_wikiconfig.sub("sys.path.insert(0, %r)" % self.conf_dir, s)   274    275         writefile(moin_cgi_installed, s)   276         os.system("chmod a+rx '%s'" % moin_cgi_installed)   277    278     def add_superuser(self):   279    280         "Add the superuser account."   281    282         moin_script = join(self.prefix, "bin", "moin")   283    284         print "Creating superuser", self.superuser, "using..."   285         email = raw_input("E-mail address: ")   286         password = getpass("Password: ")   287    288         path = os.environ.get("PYTHONPATH", "")   289    290         if path:   291             os.environ["PYTHONPATH"] = path + ":" + self.conf_dir   292         else:   293             os.environ["PYTHONPATH"] = self.conf_dir   294    295         os.system(moin_script + " account create --name='%s' --email='%s' --password='%s'" % (self.superuser, email, password))   296    297         if path:   298             os.environ["PYTHONPATH"] = path   299         else:   300             del os.environ["PYTHONPATH"]   301    302     def make_site_files(self):   303    304         "Make the Apache site files."   305    306         # NOTE: Using local namespace for substitution.   307    308         site_def = join(self.web_site_dir, self.site_name)   309         site_def_private = join(self.web_site_dir, "%s-private" % self.site_name)   310    311         status("Writing Apache site definitions to %s and %s..." % (site_def, site_def_private))   312    313         s = apache_site % self.__dict__   314    315         if not self.moin_version.startswith("1.9"):   316             s += apache_site_extra_moin18 % self.__dict__   317    318         writefile(site_def, s)   319    320     def make_post_install_script(self):   321    322         "Write a post-install script with additional actions."   323    324         this_user = os.environ["USER"]   325         postinst_script = "moinsetup-post.sh"   326    327         s = "#!/bin/sh\n"   328    329         for d in ("data", "underlay"):   330             s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, join(self.conf_dir, d))   331             s += "chmod -R g+w '%s'\n" % join(self.conf_dir, d)   332    333         if not self.moin_version.startswith("1.9"):   334             s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, self.htdocs_dir)   335    336         writefile(postinst_script, s)   337         os.chmod(postinst_script, 0755)   338         note("Run %s as root to set file ownership and permissions." % postinst_script)   339    340 # Command line option syntax.   341    342 syntax_description = "<argument> ... [ --method=METHOD ]"   343    344 # Main program.   345    346 if __name__ == "__main__":   347     import sys, cmdsyntax   348    349     # Check the command syntax.   350    351     syntax = cmdsyntax.Syntax(syntax_description)   352     try:   353         matches = syntax.get_args(sys.argv[1:])   354         args = matches[0]   355     except IndexError:   356         print "Syntax:"   357         print sys.argv[0], syntax_description   358         print   359         print "Arguments:"   360         print Installation.__init__.__doc__   361         sys.exit(1)   362    363     # Obtain as many arguments as needed for the configuration.   364    365     arguments = args["argument"]   366    367     try:   368         installation = Installation(*arguments)   369     except TypeError:   370         print sys.argv[0], syntax_description   371         print   372         print "Arguments:"   373         print Installation.__init__.__doc__   374         sys.exit(1)   375    376     # Obtain and perform the method.   377    378     if args.has_key("method"):   379         method = getattr(installation, args["method"])   380     else:   381         method = installation.setup   382    383     method()   384    385 # vim: tabstop=4 expandtab shiftwidth=4