##// END OF EJS Templates
paster: add install-iis command to automate IIS handler generation...
Henrik Stuart -
r4554:2dad9708 default
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 import os
2 import sys
3 from paste.script.appinstall import AbstractInstallCommand
4 from paste.script.command import BadCommand
5
6 # Add location of top level folder to sys.path
7 from os.path import dirname as dn
8 rc_path = dn(dn(dn(os.path.realpath(__file__))))
9 sys.path.append(rc_path)
10
11 class Command(AbstractInstallCommand):
12 default_verbosity = 1
13 max_args = 1
14 min_args = 1
15 summary = 'Setup IIS given a config file'
16 usage = 'CONFIG_FILE'
17
18 description = '''
19 Script for installing into IIS using isapi-wsgi.
20 '''
21 parser = AbstractInstallCommand.standard_parser(
22 simulate=True, quiet=True, interactive=True)
23 parser.add_option('--virtualdir',
24 action='store',
25 dest='virtualdir',
26 default='/',
27 help='The virtual folder to install into on IIS')
28
29 def command(self):
30 config_spec = self.args[0]
31 if not config_spec.startswith('config:'):
32 config_spec = 'config:' + config_spec
33 config_file = config_spec[len('config:'):].split('#', 1)[0]
34 config_file = os.path.join(os.getcwd(), config_file)
35 try:
36 import isapi_wsgi
37 except ImportError:
38 raise BadCommand('missing requirement: isapi-wsgi not installed')
39
40 file = '''import sys
41
42 if hasattr(sys, "isapidllhandle"):
43 import win32traceutil
44
45 import isapi_wsgi
46 import os
47
48 def __ExtensionFactory__():
49 from paste.deploy import loadapp
50 from paste.script.util.logging_config import fileConfig
51 fileConfig('%(inifile)s')
52 application = loadapp('config:%(inifile)s')
53
54 def app(environ, start_response):
55 user = environ.get('REMOTE_USER', None)
56 if user is not None:
57 os.environ['REMOTE_USER'] = user
58 return application(environ, start_response)
59
60 return isapi_wsgi.ISAPIThreadPoolHandler(app)
61
62 if __name__=='__main__':
63 from isapi.install import *
64 params = ISAPIParameters()
65 sm = [ScriptMapParams(Extension="*", Flags=0)]
66 vd = VirtualDirParameters(Name="%(virtualdir)s",
67 Description = "Kallithea",
68 ScriptMaps = sm,
69 ScriptMapUpdate = "replace")
70 params.VirtualDirs = [vd]
71 HandleCommandLine(params)
72 '''
73
74 outdata = file % {
75 'inifile': config_file.replace('\\', '\\\\'),
76 'virtualdir': self.options.virtualdir
77 }
78
79 dispatchfile = os.path.join(os.getcwd(), 'dispatch.py')
80 self.ensure_file(dispatchfile, outdata, False)
81 print 'generating', dispatchfile
82
83 print ('run \'python "%s" install\' with administrative privileges '
84 'to generate the _dispatch.dll file and install it into the '
85 'default web site') % (dispatchfile,)
@@ -1,133 +1,107 b''
1 .. _installation_iis:
1 .. _installation_iis:
2
2
3 Installing Kallithea on Microsoft Internet Information Services (IIS)
3 Installing Kallithea on Microsoft Internet Information Services (IIS)
4 =====================================================================
4 =====================================================================
5
5
6 The following is documented using IIS 7/8 terminology. There should be nothing
6 The following is documented using IIS 7/8 terminology. There should be nothing
7 preventing you from applying this on IIS 6 well.
7 preventing you from applying this on IIS 6 well.
8
8
9 .. note::
9 .. note::
10
10
11 For the best security, it is strongly recommended to only host the site over
11 For the best security, it is strongly recommended to only host the site over
12 a secure connection, e.g. using TLS.
12 a secure connection, e.g. using TLS.
13
13
14 Prerequisites
14 Prerequisites
15 -------------
15 -------------
16
16
17 Apart from the normal requirements for Kallithea, it is also necessary to get an
17 Apart from the normal requirements for Kallithea, it is also necessary to get an
18 ISAPI-WSGI bridge module, e.g. isapi-wsgi.
18 ISAPI-WSGI bridge module, e.g. isapi-wsgi.
19
19
20 Installation
20 Installation
21 ------------
21 ------------
22
22
23 The following will assume that your Kallithea is at ``c:\inetpub\kallithea`` and
23 The following will assume that your Kallithea is at ``c:\inetpub\kallithea`` and
24 will be served from the root of its own website. The changes to serve it in its
24 will be served from the root of its own website. The changes to serve it in its
25 own virtual folder will be noted where appropriate.
25 own virtual folder will be noted where appropriate.
26
26
27 Application Pool
27 Application Pool
28 ................
28 ................
29
29
30 Make sure that there is a unique application pool for the Kallithea application
30 Make sure that there is a unique application pool for the Kallithea application
31 with an identity that has read access to the Kallithea distribution.
31 with an identity that has read access to the Kallithea distribution.
32
32
33 The application pool does not need to be able to run any managed code. If you
33 The application pool does not need to be able to run any managed code. If you
34 are using a 32-bit Python installation, then you must enable 32 bit program in
34 are using a 32-bit Python installation, then you must enable 32 bit program in
35 the advanced settings for the application pool otherwise Python will not be able
35 the advanced settings for the application pool otherwise Python will not be able
36 to run on the website and consequently, Kallithea will not be able to run.
36 to run on the website and consequently, Kallithea will not be able to run.
37
37
38 .. note::
38 .. note::
39
39
40 The application pool can be the same as an existing application pool as long
40 The application pool can be the same as an existing application pool as long
41 as the requirements to Kallithea are enabled by the existing application
41 as the requirements to Kallithea are enabled by the existing application
42 pool.
42 pool.
43
43
44 ISAPI Handler
44 ISAPI Handler
45 .............
45 .............
46
46
47 The ISAPI handler needs to be generated from a custom file. Imagining that the
47 The ISAPI handler can be generated using::
48 Kallithea installation is in ``c:\inetpub\kallithea``, we would have a file in
49 the same directory called, e.g. ``dispatch.py`` with the following contents::
50
51 import sys
52
48
53 if hasattr(sys, "isapidllhandle"):
49 paster install-iis my.ini --root=/
54 import win32traceutil
55
56 import isapi_wsgi
57
58 def __ExtensionFactory__():
59 from paste.deploy import loadapp
60 from paste.script.util.logging_config import fileConfig
61 fileConfig('c:\\inetpub\\kallithea\\production.ini')
62 application = loadapp('config:c:\\inetpub\\kallithea\\production.ini')
63
50
64 def app(environ, start_response):
51 This will generate a ``dispatch.py`` file in the current directory that contains
65 user = environ.get('REMOTE_USER', None)
52 the necessary components to finalize an installation into IIS. Once this file
66 if user is not None:
53 has been generated, it is necessary to run the following command due to the way
67 os.environ['REMOTE_USER'] = user
54 that ISAPI-WSGI is made::
68 return application(environ, start_response)
69
70 return isapi_wsgi.ISAPIThreadPoolHandler(app)
71
55
72 if __name__=='__main__':
56 python dispatch.py install
73 from isapi.install import *
74 params = ISAPIParameters()
75 sm = [ScriptMapParams(Extension="*", Flags=0)]
76 vd = VirtualDirParameters(Name="/",
77 Description = "ISAPI-WSGI Echo Test",
78 ScriptMaps = sm,
79 ScriptMapUpdate = "replace")
80 params.VirtualDirs = [vd]
81 HandleCommandLine(params)
82
57
83 This script has two parts: First, when run directly using Python, it will
58 This accomplishes two things: generating an ISAPI compliant DLL file,
84 install a script map ISAPI handler into the root application of the default
59 ``_dispatch.dll``, and installing a script map handler into IIS for the
85 website, and secondly it will be called from the ISAPI handler when invoked
60 ``--root`` specified above pointing to ``_dispatch.dll``.
86 from the website.
87
61
88 The ISAPI handler is registered to all file extensions, so it will automatically
62 The ISAPI handler is registered to all file extensions, so it will automatically
89 be the one handling all requests to the website. When the website starts the
63 be the one handling all requests to the specified root. When the website starts
90 ISAPI handler, it will start a thread pool managed wrapper around the paster
64 the ISAPI handler, it will start a thread pool managed wrapper around the paster
91 middleware WSGI handler that Kallithea runs within and each HTTP request to the
65 middleware WSGI handler that Kallithea runs within and each HTTP request to the
92 site will be processed through this logic henceforth.
66 site will be processed through this logic henceforth.
93
67
94 Authentication with Kallithea using IIS authentication modules
68 Authentication with Kallithea using IIS authentication modules
95 ..............................................................
69 ..............................................................
96
70
97 The recommended way to handle authentication with Kallithea using IIS is to let
71 The recommended way to handle authentication with Kallithea using IIS is to let
98 IIS handle all the authentication and just pass it to Kallithea.
72 IIS handle all the authentication and just pass it to Kallithea.
99
73
100 To move responsibility into IIS from Kallithea, we need to configure Kallithea
74 To move responsibility into IIS from Kallithea, we need to configure Kallithea
101 to let external systems handle authentication and then let Kallithea create the
75 to let external systems handle authentication and then let Kallithea create the
102 user automatically. To do this, access the administration's authentication page
76 user automatically. To do this, access the administration's authentication page
103 and enable the ``kallithea.lib.auth_modules.auth_container`` plugin. Once it is
77 and enable the ``kallithea.lib.auth_modules.auth_container`` plugin. Once it is
104 added, enable it with the ``REMOTE_USER`` header and check *Clean username*.
78 added, enable it with the ``REMOTE_USER`` header and check *Clean username*.
105 Finally, save the changes on this page.
79 Finally, save the changes on this page.
106
80
107 Switch to the administration's permissions page and disable anonymous access,
81 Switch to the administration's permissions page and disable anonymous access,
108 otherwise Kallithea will not attempt to use the authenticated user name. By
82 otherwise Kallithea will not attempt to use the authenticated user name. By
109 default, Kallithea will populate the list of users lazily as they log in. Either
83 default, Kallithea will populate the list of users lazily as they log in. Either
110 disable external auth account activation and ensure that you pre-populate the
84 disable external auth account activation and ensure that you pre-populate the
111 user database with an external tool, or set it to *Automatic activation of
85 user database with an external tool, or set it to *Automatic activation of
112 external account*. Finally, save the changes.
86 external account*. Finally, save the changes.
113
87
114 The last necessary step is to enable the relevant authentication in IIS, e.g.
88 The last necessary step is to enable the relevant authentication in IIS, e.g.
115 Windows authentication.
89 Windows authentication.
116
90
117 Troubleshooting
91 Troubleshooting
118 ---------------
92 ---------------
119
93
120 Typically, any issues in this setup will either be entirely in IIS or entirely
94 Typically, any issues in this setup will either be entirely in IIS or entirely
121 in Kallithea (or Kallithea's WSGI/paster middleware). Consequently, two
95 in Kallithea (or Kallithea's WSGI/paster middleware). Consequently, two
122 different options for finding issues exist: IIS' failed request tracking which
96 different options for finding issues exist: IIS' failed request tracking which
123 is great at finding issues until they exist inside Kallithea, at which point the
97 is great at finding issues until they exist inside Kallithea, at which point the
124 ISAPI-WSGI wrapper above uses ``win32traceutil``, which is part of ``pywin32``.
98 ISAPI-WSGI wrapper above uses ``win32traceutil``, which is part of ``pywin32``.
125
99
126 In order to dump output from WSGI using ``win32traceutil`` it is sufficient to
100 In order to dump output from WSGI using ``win32traceutil`` it is sufficient to
127 type the following in a console window::
101 type the following in a console window::
128
102
129 python -m win32traceutil
103 python -m win32traceutil
130
104
131 and any exceptions occurring in the WSGI layer and below (i.e. in the Kallithea
105 and any exceptions occurring in the WSGI layer and below (i.e. in the Kallithea
132 application itself) that are uncaught, will be printed here complete with stack
106 application itself) that are uncaught, will be printed here complete with stack
133 traces, making it a lot easier to identify issues.
107 traces, making it a lot easier to identify issues.
@@ -1,184 +1,185 b''
1 #!/usr/bin/env python2
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 import os
3 import os
4 import sys
4 import sys
5 import platform
5 import platform
6
6
7 if sys.version_info < (2, 6):
7 if sys.version_info < (2, 6):
8 raise Exception('Kallithea requires python 2.6 or 2.7')
8 raise Exception('Kallithea requires python 2.6 or 2.7')
9
9
10
10
11 here = os.path.abspath(os.path.dirname(__file__))
11 here = os.path.abspath(os.path.dirname(__file__))
12
12
13
13
14 def _get_meta_var(name, data, callback_handler=None):
14 def _get_meta_var(name, data, callback_handler=None):
15 import re
15 import re
16 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
16 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
17 if matches:
17 if matches:
18 if not callable(callback_handler):
18 if not callable(callback_handler):
19 callback_handler = lambda v: v
19 callback_handler = lambda v: v
20
20
21 return callback_handler(eval(matches.groups()[0]))
21 return callback_handler(eval(matches.groups()[0]))
22
22
23 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'rb')
23 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'rb')
24 _metadata = _meta.read()
24 _metadata = _meta.read()
25 _meta.close()
25 _meta.close()
26
26
27 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
27 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
28 __version__ = _get_meta_var('VERSION', _metadata, callback)
28 __version__ = _get_meta_var('VERSION', _metadata, callback)
29 __license__ = _get_meta_var('__license__', _metadata)
29 __license__ = _get_meta_var('__license__', _metadata)
30 __author__ = _get_meta_var('__author__', _metadata)
30 __author__ = _get_meta_var('__author__', _metadata)
31 __url__ = _get_meta_var('__url__', _metadata)
31 __url__ = _get_meta_var('__url__', _metadata)
32 # defines current platform
32 # defines current platform
33 __platform__ = platform.system()
33 __platform__ = platform.system()
34
34
35 is_windows = __platform__ in ['Windows']
35 is_windows = __platform__ in ['Windows']
36
36
37 requirements = [
37 requirements = [
38 "waitress==0.8.8",
38 "waitress==0.8.8",
39 "webob==1.0.8",
39 "webob==1.0.8",
40 "webtest==1.4.3",
40 "webtest==1.4.3",
41 "Pylons==1.0.0",
41 "Pylons==1.0.0",
42 "Beaker==1.6.4",
42 "Beaker==1.6.4",
43 "WebHelpers==1.3",
43 "WebHelpers==1.3",
44 "formencode>=1.2.4,<=1.2.6",
44 "formencode>=1.2.4,<=1.2.6",
45 "SQLAlchemy==0.7.10",
45 "SQLAlchemy==0.7.10",
46 "Mako>=0.9.0,<=1.0.0",
46 "Mako>=0.9.0,<=1.0.0",
47 "pygments>=1.5",
47 "pygments>=1.5",
48 "whoosh>=2.4.0,<=2.5.7",
48 "whoosh>=2.4.0,<=2.5.7",
49 "celery>=2.2.5,<2.3",
49 "celery>=2.2.5,<2.3",
50 "babel>=0.9.6,<=1.3",
50 "babel>=0.9.6,<=1.3",
51 "python-dateutil>=1.5.0,<2.0.0",
51 "python-dateutil>=1.5.0,<2.0.0",
52 "markdown==2.2.1",
52 "markdown==2.2.1",
53 "docutils>=0.8.1,<=0.11",
53 "docutils>=0.8.1,<=0.11",
54 "simplejson==2.5.2",
54 "simplejson==2.5.2",
55 "mock",
55 "mock",
56 "pycrypto>=2.6.0,<=2.6.1",
56 "pycrypto>=2.6.0,<=2.6.1",
57 "URLObject==2.3.4",
57 "URLObject==2.3.4",
58 "Routes==1.13",
58 "Routes==1.13",
59 ]
59 ]
60
60
61 if sys.version_info < (2, 7):
61 if sys.version_info < (2, 7):
62 requirements.append("importlib==1.0.1")
62 requirements.append("importlib==1.0.1")
63 requirements.append("unittest2")
63 requirements.append("unittest2")
64 requirements.append("argparse")
64 requirements.append("argparse")
65
65
66 if is_windows:
66 if is_windows:
67 requirements.append("mercurial>=2.8.2,<3.2")
67 requirements.append("mercurial>=2.8.2,<3.2")
68 else:
68 else:
69 requirements.append("py-bcrypt>=0.3.0,<=0.4")
69 requirements.append("py-bcrypt>=0.3.0,<=0.4")
70 requirements.append("mercurial>=2.8.2,<3.2")
70 requirements.append("mercurial>=2.8.2,<3.2")
71
71
72 if sys.version_info < (2, 7):
72 if sys.version_info < (2, 7):
73 # Dulwich 0.9.6 and later do not support Python2.6.
73 # Dulwich 0.9.6 and later do not support Python2.6.
74 requirements.append("dulwich>=0.9.3,<=0.9.5")
74 requirements.append("dulwich>=0.9.3,<=0.9.5")
75 else:
75 else:
76 requirements.append("dulwich>=0.9.3,<=0.9.7")
76 requirements.append("dulwich>=0.9.3,<=0.9.7")
77
77
78 dependency_links = [
78 dependency_links = [
79 ]
79 ]
80
80
81 classifiers = [
81 classifiers = [
82 'Development Status :: 4 - Beta',
82 'Development Status :: 4 - Beta',
83 'Environment :: Web Environment',
83 'Environment :: Web Environment',
84 'Framework :: Pylons',
84 'Framework :: Pylons',
85 'Intended Audience :: Developers',
85 'Intended Audience :: Developers',
86 'License :: OSI Approved :: GNU General Public License (GPL)',
86 'License :: OSI Approved :: GNU General Public License (GPL)',
87 'Operating System :: OS Independent',
87 'Operating System :: OS Independent',
88 'Programming Language :: Python',
88 'Programming Language :: Python',
89 'Programming Language :: Python :: 2.6',
89 'Programming Language :: Python :: 2.6',
90 'Programming Language :: Python :: 2.7',
90 'Programming Language :: Python :: 2.7',
91 'Topic :: Software Development :: Version Control',
91 'Topic :: Software Development :: Version Control',
92 ]
92 ]
93
93
94
94
95 # additional files from project that goes somewhere in the filesystem
95 # additional files from project that goes somewhere in the filesystem
96 # relative to sys.prefix
96 # relative to sys.prefix
97 data_files = []
97 data_files = []
98
98
99 # additional files that goes into package itself
99 # additional files that goes into package itself
100 package_data = {'kallithea': ['i18n/*/LC_MESSAGES/*.mo', ], }
100 package_data = {'kallithea': ['i18n/*/LC_MESSAGES/*.mo', ], }
101
101
102 description = ('Kallithea is a fast and powerful management tool '
102 description = ('Kallithea is a fast and powerful management tool '
103 'for Mercurial and GIT with a built in push/pull server, '
103 'for Mercurial and GIT with a built in push/pull server, '
104 'full text search and code-review.')
104 'full text search and code-review.')
105
105
106 keywords = ' '.join([
106 keywords = ' '.join([
107 'kallithea', 'mercurial', 'git', 'code review',
107 'kallithea', 'mercurial', 'git', 'code review',
108 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
108 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
109 'hgwebdir', 'gitweb replacement', 'serving hgweb',
109 'hgwebdir', 'gitweb replacement', 'serving hgweb',
110 ])
110 ])
111
111
112 # long description
112 # long description
113 README_FILE = 'README.rst'
113 README_FILE = 'README.rst'
114 CHANGELOG_FILE = 'docs/changelog.rst'
114 CHANGELOG_FILE = 'docs/changelog.rst'
115 try:
115 try:
116 long_description = open(README_FILE).read() + '\n\n' + \
116 long_description = open(README_FILE).read() + '\n\n' + \
117 open(CHANGELOG_FILE).read()
117 open(CHANGELOG_FILE).read()
118
118
119 except IOError, err:
119 except IOError, err:
120 sys.stderr.write(
120 sys.stderr.write(
121 "[WARNING] Cannot find file specified as long_description (%s)\n or "
121 "[WARNING] Cannot find file specified as long_description (%s)\n or "
122 "changelog (%s) skipping that file" % (README_FILE, CHANGELOG_FILE)
122 "changelog (%s) skipping that file" % (README_FILE, CHANGELOG_FILE)
123 )
123 )
124 long_description = description
124 long_description = description
125
125
126 try:
126 try:
127 from setuptools import setup, find_packages
127 from setuptools import setup, find_packages
128 except ImportError:
128 except ImportError:
129 from ez_setup import use_setuptools
129 from ez_setup import use_setuptools
130 use_setuptools()
130 use_setuptools()
131 from setuptools import setup, find_packages
131 from setuptools import setup, find_packages
132 # packages
132 # packages
133 packages = find_packages(exclude=['ez_setup'])
133 packages = find_packages(exclude=['ez_setup'])
134
134
135 setup(
135 setup(
136 name='Kallithea',
136 name='Kallithea',
137 version=__version__,
137 version=__version__,
138 description=description,
138 description=description,
139 long_description=long_description,
139 long_description=long_description,
140 keywords=keywords,
140 keywords=keywords,
141 license=__license__,
141 license=__license__,
142 author=__author__,
142 author=__author__,
143 author_email='kallithea@sfconservancy.org',
143 author_email='kallithea@sfconservancy.org',
144 dependency_links=dependency_links,
144 dependency_links=dependency_links,
145 url=__url__,
145 url=__url__,
146 install_requires=requirements,
146 install_requires=requirements,
147 classifiers=classifiers,
147 classifiers=classifiers,
148 setup_requires=["PasteScript>=1.6.3"],
148 setup_requires=["PasteScript>=1.6.3"],
149 data_files=data_files,
149 data_files=data_files,
150 packages=packages,
150 packages=packages,
151 include_package_data=True,
151 include_package_data=True,
152 test_suite='nose.collector',
152 test_suite='nose.collector',
153 package_data=package_data,
153 package_data=package_data,
154 message_extractors={'kallithea': [
154 message_extractors={'kallithea': [
155 ('**.py', 'python', None),
155 ('**.py', 'python', None),
156 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
156 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
157 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
157 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
158 ('public/**', 'ignore', None)]},
158 ('public/**', 'ignore', None)]},
159 zip_safe=False,
159 zip_safe=False,
160 paster_plugins=['PasteScript', 'Pylons'],
160 paster_plugins=['PasteScript', 'Pylons'],
161 entry_points="""
161 entry_points="""
162 [console_scripts]
162 [console_scripts]
163 kallithea-api = kallithea.bin.kallithea_api:main
163 kallithea-api = kallithea.bin.kallithea_api:main
164 kallithea-gist = kallithea.bin.kallithea_gist:main
164 kallithea-gist = kallithea.bin.kallithea_gist:main
165 kallithea-config = kallithea.bin.kallithea_config:main
165 kallithea-config = kallithea.bin.kallithea_config:main
166
166
167 [paste.app_factory]
167 [paste.app_factory]
168 main = kallithea.config.middleware:make_app
168 main = kallithea.config.middleware:make_app
169
169
170 [paste.app_install]
170 [paste.app_install]
171 main = pylons.util:PylonsInstaller
171 main = pylons.util:PylonsInstaller
172
172
173 [paste.global_paster_command]
173 [paste.global_paster_command]
174 setup-db=kallithea.lib.paster_commands.setup_db:Command
174 setup-db=kallithea.lib.paster_commands.setup_db:Command
175 update-repoinfo=kallithea.lib.paster_commands.update_repoinfo:Command
175 update-repoinfo=kallithea.lib.paster_commands.update_repoinfo:Command
176 make-rcext=kallithea.lib.paster_commands.make_rcextensions:Command
176 make-rcext=kallithea.lib.paster_commands.make_rcextensions:Command
177 repo-scan=kallithea.lib.paster_commands.repo_scan:Command
177 repo-scan=kallithea.lib.paster_commands.repo_scan:Command
178 cache-keys=kallithea.lib.paster_commands.cache_keys:Command
178 cache-keys=kallithea.lib.paster_commands.cache_keys:Command
179 ishell=kallithea.lib.paster_commands.ishell:Command
179 ishell=kallithea.lib.paster_commands.ishell:Command
180 make-index=kallithea.lib.paster_commands.make_index:Command
180 make-index=kallithea.lib.paster_commands.make_index:Command
181 upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
181 upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
182 celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
182 celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
183 install-iis=kallithea.lib.paster_commands.install_iis:Command
183 """,
184 """,
184 )
185 )
General Comments 0
You need to be logged in to leave comments. Login now