Differences between revisions 3 and 17 (spanning 14 versions)
Revision 3 as of 2008-08-07 11:36:31
Size: 3800
Editor: TerryBoz
Comment:
Revision 17 as of 2009-05-19 19:31:02
Size: 8846
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
For a really really simple way of publishing your Mercurial repositories on the Web without the need for Apache or IIS.
Line 4: Line 6:
  !CherryPy is a pythonic, object-oriented HTTP framework. !CherryPy powered web applications are in fact stand-alone Python applications embedding their own multi-threaded web server.   !CherryPy is a pythonic, object-oriented HTTP framework.

!CherryPy powered web applications are in fact stand-alone Python applications embedding their own multi-threaded web server.
Line 7: Line 11:

!CherryPy can run WSGI compliant applications like hgweb and hgwebdir.

=== Pre-requisties ===
!CherryPy can run WSGI compliant applications like ~+`hgweb`+~ and ~+`hgwebdir`+~.

=== Pre-requisites ===
Line 15: Line 18:
Read PublishingRepositories and HgWebDirStepByStep first to get an understanding of hgweb, hgwebdir, the hgweb.config file and Mercurial Lib folder setup.

== Using HgWebDir ==
Here is my sample code that utilizes the hgwebdir module. Edit this code to suit your needs:
If you want authentication you will need Paste (http://pypi.python.org/pypi/Paste), I used version 1.7.2

Read PublishingRepositories and HgWebDirStepByStep first to get an understanding of ~+`hgweb`+~, ~+`hgwebdir`+~, the ~+`hgweb.config`+~ file and Mercurial ~+`Lib`+~ folder setup.

== Running HgWebDir ==
Here is my sample code that uses the ~+`hgwebdir`+~ module. Edit this code to suit your needs:
Line 42: Line 47:
    #'log.access_file':'access.log',
Line 51: Line 57:
sUrlHost can be IP number or domain name (usually, depending on how your network is set up).

Save this code into the same folder as hgweb.config. Unless you have added a path, then save to the root of that path.

Suggested filename is cphgwebdir.py
~+`sUrlHost`+~ can be IP number or domain name (usually, depending on how your network is set up).

Save this code into the same folder as ~+`hgweb.config`+~. Unless you have added a path, then save to the root of that path.

Suggested filename is ~+`cphgwebdir.py`+~
Line 62: Line 68:

== Using HgWeb ==
Use this if you don't want the fancy Browser front-end.

Here is my sample code that utilizes the hgweb module. Edit this code to suit your needs:
{{{
#!python
# cphgweb.py
== Running HgWebDir with Digest Authentication ==
This is a modification of the above code. Edit this code to suit your needs:
{{{
#!python
# cphgwebdir.py with digest authentication
import os
import sys
import cherrypy
from paste.auth.digest import AuthDigestHandler, digest_password
Line 75: Line 82:
# Change these to suit your repositories: # Adjust encoding to suit or comment out:
os.environ['HGENCODING']='UTF-8'

# Adjust path to your Mercurial Lib folder:
sys.path.append(r'C:\Program Files\Mercurial\Lib')
from mercurial.hgweb.hgwebdir_mod import hgwebdir

# Use same hgweb.config file as for hgwebdir.cgi
WsgiApp=hgwebdir('hgweb.config')

# Adjust realm to suit your needs
Realm='Mercurial Repositories'

# This is a sample dictionary of valid users with their digest encrypted password
dAuth={}
dAuth['Alice']=digest_password(Realm,'Alice','secret')
dAuth['Dilbert']=digest_password(Realm,'Dilbert','secret')
dAuth['Wally']=digest_password(Realm,'Wally','secret')
#dAuth['<username>']=digest_password('<realm>','<username>','<password>')

def AuthFunc(environ,realm,username):
    return dAuth.get(username,None)

WsgiApp=AuthDigestHandler(WsgiApp,Realm,AuthFunc)

cherrypy.config.update({
    # Default is development environment, uncomment below when in production
    #'environment':'production',
    'server.socket_host':sUrlHost,
    'server.socket_port':iUrlPort,
    #'log.access_file':'access.log',
    'log.error_file':'error.log',
    'log.screen':True
})
cherrypy.tree.graft(WsgiApp,script_name='/')
cherrypy.engine.start()
cherrypy.engine.block()
}}}
Note: do not try ~+`tools.wsgiapp`+~ as it does not work in this case and has been deprecated, and will be removed in Cherrypy 3.2

== Running HgWeb ==
Use this if you don't want the fancy browser front-end.

Here is my sample code that uses the ~+`hgweb`+~ module. Edit this code to suit your needs:
{{{
#!python
# cphgweb.py

# Adjust host and port to suit your Web presence:
sUrlHost='0.0.0.0'
iUrlPort=8080

# Change this to represent your repository/ies:
Line 77: Line 136:
    #('<virtual path>','<absolute path>'),
Line 107: Line 167:
sUrlHost can be IP number or domain name (usually, depending on how your network is set up).

Change tuples in lRepos list to suit, must have leading forward slash on first element of each tuple.

Suggested filename is cphgweb.py
~+`sUrlHost`+~ can be IP number or domain name (usually, depending on how your network is set up).

Change tuples in ~+`lRepos`+~ list to indicate the repositories that you want published, first element of each tuple must have a leading forward slash.

Suggested filename is ~+`cphgweb.py`+~
Line 117: Line 177:
You can make changes to the lRepos list while it is running, saving changes will initiate a restart.
You can make changes to the ~+`lRepos`+~ list while it is running, saving changes will initiate a restart.

Example command:
{{{
hg clone "http://192.168.0.10:8080/project2"
}}}

== Running HgWebDir As a Windows Service ==

Here is some example code on how to run hgwebdir and cherrypy as a windows service. It is basically a combination of the above example and the example from cherry's web site: http://tools.cherrypy.org/wiki/WindowsService.

Notice the assignments to sys.__stdout__ and sys.__stderr__ without them you'll get a 500 internal error back when running as a service. It will however run fine in debug mode. Took me a while to figure that out ;-)

{{{
#!python
# cphgwebdir.py

URL_HOST = '127.0.0.1'
URL_PORT = 4040

import sys
import os

import cherrypy
import win32serviceutil
import win32service
import win32event


os.environ['HGENCODING'] = 'UTF-8'
# Change current working dir to the path where hgweb.config is located.
os.chdir('D:/Projects/python/cphgweb')

class MercurialService(win32serviceutil.ServiceFramework):
    """Windows Service"""

    _svc_name_ = "hgservice"
    _svc_display_name_ = "Mercurial Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        # Create an event that SvcDoRun can wait on and SvcStop
        # can set.
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)
    
    def SvcDoRun(self):
        from mercurial.hgweb.hgwebdir_mod import hgwebdir
   
        # Set __stdout__ and __stderr__ to avoid mercurial/hook.py from
        # throwing an OSError in the hook function when attempting to
        # redirect stdout to stderr.
        sys.__stdout__ = open('NUL', 'a+')
        sys.__stderr__ = open('NUL', 'a+')

        cherrypy.tree.graft(hgwebdir('hgweb.config'), script_name='/')

        cherrypy.config.update({
            'global':{
                #'environment':'production',
                'engine.autoreload_on': False,
                'server.socket_host':URL_HOST,
                'server.socket_port':URL_PORT,
                'log.screen': False,
                'log.error_file':'D:/Projects/python/cphgweb/error.log',
                'engine.SIGHUP': None,
                'engine.SIGTERM': None,
                }
            })
        
        cherrypy.engine.start()

        # now, block until our event is set...
        win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        cherrypy.server.stop()
        win32event.SetEvent(self.stop_event)


if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(MercurialService)
}}}

Run:

{{{
python cphgwebdir.py install
python cphgwebdir.py start
}}}

And your service should be running.
----

Publishing Repositories Using CherryPy

Introduction

For a really really simple way of publishing your Mercurial repositories on the Web without the need for Apache or IIS.

Some quotes from the CherryPy Web site:

  • CherryPy is a pythonic, object-oriented HTTP framework.

    CherryPy powered web applications are in fact stand-alone Python applications embedding their own multi-threaded web server.

    CherryPy applications run on Windows, Linux, Mac OS X and any other platform supporting Python.

CherryPy can run WSGI compliant applications like hgweb and hgwebdir.

Pre-requisites

Python 2.4 or newer (http://www.python.org/download/)

CherryPy 3.1 or newer (http://www.cherrypy.org/)

If you want authentication you will need Paste (http://pypi.python.org/pypi/Paste), I used version 1.7.2

Read PublishingRepositories and HgWebDirStepByStep first to get an understanding of hgweb, hgwebdir, the hgweb.config file and Mercurial Lib folder setup.

Running HgWebDir

Here is my sample code that uses the hgwebdir module. Edit this code to suit your needs:

   1 # cphgwebdir.py
   2 
   3 # Adjust host and port to suit your Web presence:
   4 sUrlHost='0.0.0.0'
   5 iUrlPort=8080
   6 
   7 # Adjust encoding to suit or comment out:
   8 import os
   9 os.environ['HGENCODING']='UTF-8'
  10 
  11 import sys
  12 # Adjust path to your Mercurial Lib folder:
  13 sys.path.append(r'C:\Program Files\Mercurial\Lib')
  14 from mercurial.hgweb.hgwebdir_mod import hgwebdir
  15 
  16 import cherrypy
  17 cherrypy.config.update({
  18     # Default is development environment, uncomment below when in production
  19     #'environment':'production',
  20     'server.socket_host':sUrlHost,
  21     'server.socket_port':iUrlPort,
  22     #'log.access_file':'access.log',
  23     'log.error_file':'error.log',
  24     'log.screen':True
  25 })
  26 # Use same hgweb.config file as for hgwebdir.cgi
  27 cherrypy.tree.graft(hgwebdir('hgweb.config'),script_name='/')
  28 cherrypy.engine.start()
  29 cherrypy.engine.block()

sUrlHost can be IP number or domain name (usually, depending on how your network is set up).

Save this code into the same folder as hgweb.config. Unless you have added a path, then save to the root of that path.

Suggested filename is cphgwebdir.py

From a command line just execute the file, like this from its folder:

/path/to/python cphgwebdir.py

That's it.

Running HgWebDir with Digest Authentication

This is a modification of the above code. Edit this code to suit your needs:

   1 # cphgwebdir.py with digest authentication
   2 import os
   3 import sys
   4 import cherrypy
   5 from paste.auth.digest import AuthDigestHandler, digest_password
   6 
   7 # Adjust host and port to suit your Web presence:
   8 sUrlHost='0.0.0.0'
   9 iUrlPort=8080
  10 
  11 # Adjust encoding to suit or comment out:
  12 os.environ['HGENCODING']='UTF-8'
  13 
  14 # Adjust path to your Mercurial Lib folder:
  15 sys.path.append(r'C:\Program Files\Mercurial\Lib')
  16 from mercurial.hgweb.hgwebdir_mod import hgwebdir
  17 
  18 # Use same hgweb.config file as for hgwebdir.cgi
  19 WsgiApp=hgwebdir('hgweb.config')
  20 
  21 # Adjust realm to suit your needs
  22 Realm='Mercurial Repositories'
  23 
  24 # This is a sample dictionary of valid users with their digest encrypted password
  25 dAuth={}
  26 dAuth['Alice']=digest_password(Realm,'Alice','secret')
  27 dAuth['Dilbert']=digest_password(Realm,'Dilbert','secret')
  28 dAuth['Wally']=digest_password(Realm,'Wally','secret')
  29 #dAuth['<username>']=digest_password('<realm>','<username>','<password>')
  30 
  31 def AuthFunc(environ,realm,username):
  32     return dAuth.get(username,None)
  33 
  34 WsgiApp=AuthDigestHandler(WsgiApp,Realm,AuthFunc)
  35 
  36 cherrypy.config.update({
  37     # Default is development environment, uncomment below when in production
  38     #'environment':'production',
  39     'server.socket_host':sUrlHost,
  40     'server.socket_port':iUrlPort,
  41     #'log.access_file':'access.log',
  42     'log.error_file':'error.log',
  43     'log.screen':True
  44 })
  45 cherrypy.tree.graft(WsgiApp,script_name='/')
  46 cherrypy.engine.start()
  47 cherrypy.engine.block()

Note: do not try tools.wsgiapp as it does not work in this case and has been deprecated, and will be removed in Cherrypy 3.2

Running HgWeb

Use this if you don't want the fancy browser front-end.

Here is my sample code that uses the hgweb module. Edit this code to suit your needs:

   1 # cphgweb.py
   2 
   3 # Adjust host and port to suit your Web presence:
   4 sUrlHost='0.0.0.0'
   5 iUrlPort=8080
   6 
   7 # Change this to represent your repository/ies:
   8 lRepos=[
   9     #('<virtual path>','<absolute path>'),
  10     ('/project1',r'C:\Program Files\DemoRepos\project1'),
  11     ('/project2',r'C:\Program Files\DemoRepos\project2'),
  12     ('/project3',r'C:\Program Files\DemoRepos\project3')
  13 ]
  14 
  15 # Adjust encoding to suit or comment out:
  16 import os
  17 os.environ['HGENCODING']='UTF-8'
  18 
  19 import sys
  20 # Adjust path to your Mercurial Lib folder:
  21 sys.path.append(r'C:\Program Files\Mercurial\Lib')
  22 from mercurial.hgweb.hgweb_mod import hgweb
  23 
  24 import cherrypy
  25 cherrypy.config.update({
  26     # Default is development environment, uncomment below when in production
  27     #'environment':'production',
  28     'engine.autoreload.on':True,
  29     'server.socket_host':sUrlHost,
  30     'server.socket_port':iUrlPort,
  31     'log.error_file':'error.log',
  32     'log.screen':True
  33 })
  34 for (sName,sPath) in lRepos:
  35     cherrypy.tree.graft(hgweb(sPath),script_name=sName)
  36 cherrypy.engine.start()
  37 cherrypy.engine.block()

sUrlHost can be IP number or domain name (usually, depending on how your network is set up).

Change tuples in lRepos list to indicate the repositories that you want published, first element of each tuple must have a leading forward slash.

Suggested filename is cphgweb.py

From a command line just execute the file, like this from its folder:

/path/to/python cphgweb.py

You can make changes to the lRepos list while it is running, saving changes will initiate a restart.

Example command:

hg clone "http://192.168.0.10:8080/project2"

Running HgWebDir As a Windows Service

Here is some example code on how to run hgwebdir and cherrypy as a windows service. It is basically a combination of the above example and the example from cherry's web site: http://tools.cherrypy.org/wiki/WindowsService.

Notice the assignments to sys.stdout and sys.stderr without them you'll get a 500 internal error back when running as a service. It will however run fine in debug mode. Took me a while to figure that out ;-)

   1 # cphgwebdir.py
   2 
   3 URL_HOST = '127.0.0.1'
   4 URL_PORT = 4040
   5 
   6 import sys
   7 import os
   8 
   9 import cherrypy
  10 import win32serviceutil
  11 import win32service
  12 import win32event
  13 
  14 
  15 os.environ['HGENCODING'] = 'UTF-8'
  16 # Change current working dir to the path where hgweb.config is located.
  17 os.chdir('D:/Projects/python/cphgweb')
  18 
  19 class MercurialService(win32serviceutil.ServiceFramework):
  20     """Windows Service"""
  21 
  22     _svc_name_         = "hgservice"
  23     _svc_display_name_ = "Mercurial Service"
  24 
  25     def __init__(self, args):
  26         win32serviceutil.ServiceFramework.__init__(self, args)
  27         # Create an event that SvcDoRun can wait on and SvcStop
  28         # can set.
  29         self.stop_event = win32event.CreateEvent(None, 0, 0, None)
  30     
  31     def SvcDoRun(self):
  32         from mercurial.hgweb.hgwebdir_mod import hgwebdir
  33    
  34         # Set __stdout__ and __stderr__ to avoid mercurial/hook.py from 
  35         # throwing an OSError in the hook function when attempting to
  36         # redirect stdout to stderr.
  37         sys.__stdout__ = open('NUL', 'a+')
  38         sys.__stderr__ = open('NUL', 'a+')
  39 
  40         cherrypy.tree.graft(hgwebdir('hgweb.config'), script_name='/')
  41 
  42         cherrypy.config.update({
  43             'global':{
  44                 #'environment':'production',
  45                 'engine.autoreload_on': False,
  46                 'server.socket_host':URL_HOST,
  47                 'server.socket_port':URL_PORT,
  48                 'log.screen': False,
  49                 'log.error_file':'D:/Projects/python/cphgweb/error.log',
  50                 'engine.SIGHUP': None,
  51                 'engine.SIGTERM': None,
  52                 }
  53             })
  54         
  55         cherrypy.engine.start()
  56 
  57         # now, block until our event is set...
  58         win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
  59 
  60     def SvcStop(self):
  61         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  62         cherrypy.server.stop()
  63         win32event.SetEvent(self.stop_event)
  64 
  65 
  66 if __name__ == '__main__':
  67     win32serviceutil.HandleCommandLine(MercurialService)

Run:

python cphgwebdir.py install
python cphgwebdir.py start

And your service should be running.


CategoryHowTo

PublishingRepositoriesUsingCherryPy (last edited 2009-08-15 05:53:51 by TerryBoz)