##// END OF EJS Templates
tg: move make_app to kallithea/config/application.py per TG 2.4 convention
Mads Kiilerich -
r8287:3a02b678 default
parent child Browse files
Show More
@@ -1,216 +1,216 b''
1 .. _overview:
1 .. _overview:
2
2
3 =====================
3 =====================
4 Installation overview
4 Installation overview
5 =====================
5 =====================
6
6
7 Some overview and some details that can help understanding the options when
7 Some overview and some details that can help understanding the options when
8 installing Kallithea.
8 installing Kallithea.
9
9
10 1. **Prepare environment and external dependencies.**
10 1. **Prepare environment and external dependencies.**
11 Kallithea needs:
11 Kallithea needs:
12
12
13 * A filesystem where the Mercurial and Git repositories can be stored.
13 * A filesystem where the Mercurial and Git repositories can be stored.
14 * A database where meta data can be stored.
14 * A database where meta data can be stored.
15 * A Python environment where the Kallithea application and its dependencies
15 * A Python environment where the Kallithea application and its dependencies
16 can be installed.
16 can be installed.
17 * A web server that can host the Kallithea web application using the WSGI
17 * A web server that can host the Kallithea web application using the WSGI
18 API.
18 API.
19
19
20 2. **Install Kallithea software.**
20 2. **Install Kallithea software.**
21 This makes the ``kallithea-cli`` command line tool available.
21 This makes the ``kallithea-cli`` command line tool available.
22
22
23 3. **Create low level configuration file.**
23 3. **Create low level configuration file.**
24 Use ``kallithea-cli config-create`` to create a ``.ini`` file with database
24 Use ``kallithea-cli config-create`` to create a ``.ini`` file with database
25 connection info, mail server information, some web server configuration,
25 connection info, mail server information, some web server configuration,
26 etc.
26 etc.
27
27
28 4. **Populate the database.**
28 4. **Populate the database.**
29 Use ``kallithea-cli db-create`` with the ``.ini`` file to create the
29 Use ``kallithea-cli db-create`` with the ``.ini`` file to create the
30 database schema and insert the most basic information: the location of the
30 database schema and insert the most basic information: the location of the
31 repository store and an initial local admin user.
31 repository store and an initial local admin user.
32
32
33 5. **Configure the web server.**
33 5. **Configure the web server.**
34 The web server must invoke the WSGI entrypoint for the Kallithea software
34 The web server must invoke the WSGI entrypoint for the Kallithea software
35 using the ``.ini`` file (and thus the database). This makes the web
35 using the ``.ini`` file (and thus the database). This makes the web
36 application available so the local admin user can log in and tweak the
36 application available so the local admin user can log in and tweak the
37 configuration further.
37 configuration further.
38
38
39 6. **Configure users.**
39 6. **Configure users.**
40 The initial admin user can create additional local users, or configure how
40 The initial admin user can create additional local users, or configure how
41 users can be created and authenticated from other user directories.
41 users can be created and authenticated from other user directories.
42
42
43 See the subsequent sections, the separate OS-specific instructions, and
43 See the subsequent sections, the separate OS-specific instructions, and
44 :ref:`setup` for details on these steps.
44 :ref:`setup` for details on these steps.
45
45
46
46
47 Python environment
47 Python environment
48 ------------------
48 ------------------
49
49
50 **Kallithea** is written entirely in Python_ and requires Python version
50 **Kallithea** is written entirely in Python_ and requires Python version
51 3.6 or higher.
51 3.6 or higher.
52
52
53 Given a Python installation, there are different ways of providing the
53 Given a Python installation, there are different ways of providing the
54 environment for running Python applications. Each of them pretty much
54 environment for running Python applications. Each of them pretty much
55 corresponds to a ``site-packages`` directory somewhere where packages can be
55 corresponds to a ``site-packages`` directory somewhere where packages can be
56 installed.
56 installed.
57
57
58 Kallithea itself can be run from source or be installed, but even when running
58 Kallithea itself can be run from source or be installed, but even when running
59 from source, there are some dependencies that must be installed in the Python
59 from source, there are some dependencies that must be installed in the Python
60 environment used for running Kallithea.
60 environment used for running Kallithea.
61
61
62 - Packages *could* be installed in Python's ``site-packages`` directory ... but
62 - Packages *could* be installed in Python's ``site-packages`` directory ... but
63 that would require running pip_ as root and it would be hard to uninstall or
63 that would require running pip_ as root and it would be hard to uninstall or
64 upgrade and is probably not a good idea unless using a package manager.
64 upgrade and is probably not a good idea unless using a package manager.
65
65
66 - Packages could also be installed in ``~/.local`` ... but that is probably
66 - Packages could also be installed in ``~/.local`` ... but that is probably
67 only a good idea if using a dedicated user per application or instance.
67 only a good idea if using a dedicated user per application or instance.
68
68
69 - Finally, it can be installed in a virtualenv. That is a very lightweight
69 - Finally, it can be installed in a virtualenv. That is a very lightweight
70 "container" where each Kallithea instance can get its own dedicated and
70 "container" where each Kallithea instance can get its own dedicated and
71 self-contained virtual environment.
71 self-contained virtual environment.
72
72
73 We recommend using virtualenv for installing Kallithea.
73 We recommend using virtualenv for installing Kallithea.
74
74
75
75
76 Locale environment
76 Locale environment
77 ------------------
77 ------------------
78
78
79 In order to ensure a correct functioning of Kallithea with respect to non-ASCII
79 In order to ensure a correct functioning of Kallithea with respect to non-ASCII
80 characters in user names, file paths, commit messages, etc., it is very
80 characters in user names, file paths, commit messages, etc., it is very
81 important that Kallithea is run with a correct `locale` configuration.
81 important that Kallithea is run with a correct `locale` configuration.
82
82
83 On Unix, environment variables like ``LANG`` or ``LC_ALL`` can specify a language (like
83 On Unix, environment variables like ``LANG`` or ``LC_ALL`` can specify a language (like
84 ``en_US``) and encoding (like ``UTF-8``) to use for code points outside the ASCII
84 ``en_US``) and encoding (like ``UTF-8``) to use for code points outside the ASCII
85 range. The flexibility of supporting multiple encodings of Unicode has the flip
85 range. The flexibility of supporting multiple encodings of Unicode has the flip
86 side of having to specify which encoding to use - especially for Mercurial.
86 side of having to specify which encoding to use - especially for Mercurial.
87
87
88 It depends on the OS distribution and system configuration which locales are
88 It depends on the OS distribution and system configuration which locales are
89 available. For example, some Docker containers based on Debian default to only
89 available. For example, some Docker containers based on Debian default to only
90 supporting the ``C`` language, while other Linux environments have ``en_US`` but not
90 supporting the ``C`` language, while other Linux environments have ``en_US`` but not
91 ``C``. The ``locale -a`` command will show which values are available on the
91 ``C``. The ``locale -a`` command will show which values are available on the
92 current system. Regardless of the actual language, you should normally choose a
92 current system. Regardless of the actual language, you should normally choose a
93 locale that has the ``UTF-8`` encoding (note that spellings ``utf8``, ``utf-8``,
93 locale that has the ``UTF-8`` encoding (note that spellings ``utf8``, ``utf-8``,
94 ``UTF8``, ``UTF-8`` are all referring to the same thing)
94 ``UTF8``, ``UTF-8`` are all referring to the same thing)
95
95
96 For technical reasons, the locale configuration **must** be provided in the
96 For technical reasons, the locale configuration **must** be provided in the
97 environment in which Kallithea runs - it cannot be specified in the ``.ini`` file.
97 environment in which Kallithea runs - it cannot be specified in the ``.ini`` file.
98 How to practically do this depends on the web server that is used and the way it
98 How to practically do this depends on the web server that is used and the way it
99 is started. For example, gearbox is often started by a normal user, either
99 is started. For example, gearbox is often started by a normal user, either
100 manually or via a script. In this case, the required locale environment
100 manually or via a script. In this case, the required locale environment
101 variables can be provided directly in that user's environment or in the script.
101 variables can be provided directly in that user's environment or in the script.
102 However, web servers like Apache are often started at boot via an init script or
102 However, web servers like Apache are often started at boot via an init script or
103 service file. Modifying the environment for this case would thus require
103 service file. Modifying the environment for this case would thus require
104 root/administrator privileges. Moreover, that environment would dictate the
104 root/administrator privileges. Moreover, that environment would dictate the
105 settings for all web services running under that web server, Kallithea being
105 settings for all web services running under that web server, Kallithea being
106 just one of them. Specifically in the case of Apache with ``mod_wsgi``, the
106 just one of them. Specifically in the case of Apache with ``mod_wsgi``, the
107 locale can be set for a specific service in its ``WSGIDaemonProcess`` directive,
107 locale can be set for a specific service in its ``WSGIDaemonProcess`` directive,
108 using the ``lang`` parameter.
108 using the ``lang`` parameter.
109
109
110
110
111 Installation methods
111 Installation methods
112 --------------------
112 --------------------
113
113
114 Kallithea must be installed on a server. Kallithea is installed in a Python
114 Kallithea must be installed on a server. Kallithea is installed in a Python
115 environment so it can use packages that are installed there and make itself
115 environment so it can use packages that are installed there and make itself
116 available for other packages.
116 available for other packages.
117
117
118 Two different cases will pretty much cover the options for how it can be
118 Two different cases will pretty much cover the options for how it can be
119 installed.
119 installed.
120
120
121 - The Kallithea source repository can be cloned and used -- it is kept stable and
121 - The Kallithea source repository can be cloned and used -- it is kept stable and
122 can be used in production. The Kallithea maintainers use the development
122 can be used in production. The Kallithea maintainers use the development
123 branch in production. The advantage of installation from source and regularly
123 branch in production. The advantage of installation from source and regularly
124 updating it is that you take advantage of the most recent improvements. Using
124 updating it is that you take advantage of the most recent improvements. Using
125 it directly from a DVCS also means that it is easy to track local customizations.
125 it directly from a DVCS also means that it is easy to track local customizations.
126
126
127 Running ``pip install -e .`` in the source will use pip to install the
127 Running ``pip install -e .`` in the source will use pip to install the
128 necessary dependencies in the Python environment and create a
128 necessary dependencies in the Python environment and create a
129 ``.../site-packages/Kallithea.egg-link`` file there that points at the Kallithea
129 ``.../site-packages/Kallithea.egg-link`` file there that points at the Kallithea
130 source.
130 source.
131
131
132 - Kallithea can also be installed from ready-made packages using a package manager.
132 - Kallithea can also be installed from ready-made packages using a package manager.
133 The official released versions are available on PyPI_ and can be downloaded and
133 The official released versions are available on PyPI_ and can be downloaded and
134 installed with all dependencies using ``pip install kallithea``.
134 installed with all dependencies using ``pip install kallithea``.
135
135
136 With this method, Kallithea is installed in the Python environment as any
136 With this method, Kallithea is installed in the Python environment as any
137 other package, usually as a ``.../site-packages/Kallithea-X-py3.8.egg/``
137 other package, usually as a ``.../site-packages/Kallithea-X-py3.8.egg/``
138 directory with Python files and everything else that is needed.
138 directory with Python files and everything else that is needed.
139
139
140 (``pip install kallithea`` from a source tree will do pretty much the same
140 (``pip install kallithea`` from a source tree will do pretty much the same
141 but build the Kallithea package itself locally instead of downloading it.)
141 but build the Kallithea package itself locally instead of downloading it.)
142
142
143 .. note::
143 .. note::
144 Kallithea includes front-end code that needs to be processed first.
144 Kallithea includes front-end code that needs to be processed first.
145 The tool npm_ is used to download external dependencies and orchestrate the
145 The tool npm_ is used to download external dependencies and orchestrate the
146 processing. The ``npm`` binary must thus be available.
146 processing. The ``npm`` binary must thus be available.
147
147
148
148
149 Web server
149 Web server
150 ----------
150 ----------
151
151
152 Kallithea is (primarily) a WSGI_ application that must be run from a web
152 Kallithea is (primarily) a WSGI_ application that must be run from a web
153 server that serves WSGI applications over HTTP.
153 server that serves WSGI applications over HTTP.
154
154
155 Kallithea itself is not serving HTTP (or HTTPS); that is the web server's
155 Kallithea itself is not serving HTTP (or HTTPS); that is the web server's
156 responsibility. Kallithea does however need to know its own user facing URL
156 responsibility. Kallithea does however need to know its own user facing URL
157 (protocol, address, port and path) for each HTTP request. Kallithea will
157 (protocol, address, port and path) for each HTTP request. Kallithea will
158 usually use its own HTML/cookie based authentication but can also be configured
158 usually use its own HTML/cookie based authentication but can also be configured
159 to use web server authentication.
159 to use web server authentication.
160
160
161 There are several web server options:
161 There are several web server options:
162
162
163 - Kallithea uses the Gearbox_ tool as command line interface. Gearbox provides
163 - Kallithea uses the Gearbox_ tool as command line interface. Gearbox provides
164 ``gearbox serve`` as a convenient way to launch a Python WSGI / web server
164 ``gearbox serve`` as a convenient way to launch a Python WSGI / web server
165 from the command line. That is perfect for development and evaluation.
165 from the command line. That is perfect for development and evaluation.
166 Actual use in production might have different requirements and need extra
166 Actual use in production might have different requirements and need extra
167 work to make it manageable as a scalable system service.
167 work to make it manageable as a scalable system service.
168
168
169 Gearbox comes with its own built-in web server but Kallithea defaults to use
169 Gearbox comes with its own built-in web server but Kallithea defaults to use
170 Waitress_. Gunicorn_ is also an option. These web servers have different
170 Waitress_. Gunicorn_ is also an option. These web servers have different
171 limited feature sets.
171 limited feature sets.
172
172
173 The web server used by ``gearbox`` is configured in the ``.ini`` file passed
173 The web server used by ``gearbox`` is configured in the ``.ini`` file passed
174 to it. The entry point for the WSGI application is configured
174 to it. The entry point for the WSGI application is configured
175 in ``setup.py`` as ``kallithea.config.middleware:make_app``.
175 in ``setup.py`` as ``kallithea.config.application:make_app``.
176
176
177 - `Apache httpd`_ can serve WSGI applications directly using mod_wsgi_ and a
177 - `Apache httpd`_ can serve WSGI applications directly using mod_wsgi_ and a
178 simple Python file with the necessary configuration. This is a good option if
178 simple Python file with the necessary configuration. This is a good option if
179 Apache is an option.
179 Apache is an option.
180
180
181 - uWSGI_ is also a full web server with built-in WSGI module.
181 - uWSGI_ is also a full web server with built-in WSGI module.
182
182
183 - IIS_ can also server WSGI applications directly using isapi-wsgi_.
183 - IIS_ can also server WSGI applications directly using isapi-wsgi_.
184
184
185 - A `reverse HTTP proxy <https://en.wikipedia.org/wiki/Reverse_proxy>`_
185 - A `reverse HTTP proxy <https://en.wikipedia.org/wiki/Reverse_proxy>`_
186 can be put in front of another web server which has WSGI support.
186 can be put in front of another web server which has WSGI support.
187 Such a layered setup can be complex but might in some cases be the right
187 Such a layered setup can be complex but might in some cases be the right
188 option, for example to standardize on one internet-facing web server, to add
188 option, for example to standardize on one internet-facing web server, to add
189 encryption or special authentication or for other security reasons, to
189 encryption or special authentication or for other security reasons, to
190 provide caching of static files, or to provide load balancing or fail-over.
190 provide caching of static files, or to provide load balancing or fail-over.
191 Nginx_, Varnish_ and HAProxy_ are often used for this purpose, often in front
191 Nginx_, Varnish_ and HAProxy_ are often used for this purpose, often in front
192 of a ``gearbox serve`` that somehow is wrapped as a service.
192 of a ``gearbox serve`` that somehow is wrapped as a service.
193
193
194 The best option depends on what you are familiar with and the requirements for
194 The best option depends on what you are familiar with and the requirements for
195 performance and stability. Also, keep in mind that Kallithea mainly is serving
195 performance and stability. Also, keep in mind that Kallithea mainly is serving
196 dynamically generated pages from a relatively slow Python process. Kallithea is
196 dynamically generated pages from a relatively slow Python process. Kallithea is
197 also often used inside organizations with a limited amount of users and thus no
197 also often used inside organizations with a limited amount of users and thus no
198 continuous hammering from the internet.
198 continuous hammering from the internet.
199
199
200
200
201 .. _Python: http://www.python.org/
201 .. _Python: http://www.python.org/
202 .. _Gunicorn: http://gunicorn.org/
202 .. _Gunicorn: http://gunicorn.org/
203 .. _Waitress: http://waitress.readthedocs.org/en/latest/
203 .. _Waitress: http://waitress.readthedocs.org/en/latest/
204 .. _Gearbox: http://turbogears.readthedocs.io/en/latest/turbogears/gearbox.html
204 .. _Gearbox: http://turbogears.readthedocs.io/en/latest/turbogears/gearbox.html
205 .. _PyPI: https://pypi.python.org/pypi
205 .. _PyPI: https://pypi.python.org/pypi
206 .. _Apache httpd: http://httpd.apache.org/
206 .. _Apache httpd: http://httpd.apache.org/
207 .. _mod_wsgi: https://code.google.com/p/modwsgi/
207 .. _mod_wsgi: https://code.google.com/p/modwsgi/
208 .. _isapi-wsgi: https://github.com/hexdump42/isapi-wsgi
208 .. _isapi-wsgi: https://github.com/hexdump42/isapi-wsgi
209 .. _uWSGI: https://uwsgi-docs.readthedocs.org/en/latest/
209 .. _uWSGI: https://uwsgi-docs.readthedocs.org/en/latest/
210 .. _nginx: http://nginx.org/en/
210 .. _nginx: http://nginx.org/en/
211 .. _iis: http://en.wikipedia.org/wiki/Internet_Information_Services
211 .. _iis: http://en.wikipedia.org/wiki/Internet_Information_Services
212 .. _pip: http://en.wikipedia.org/wiki/Pip_%28package_manager%29
212 .. _pip: http://en.wikipedia.org/wiki/Pip_%28package_manager%29
213 .. _WSGI: http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface
213 .. _WSGI: http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface
214 .. _HAProxy: http://www.haproxy.org/
214 .. _HAProxy: http://www.haproxy.org/
215 .. _Varnish: https://www.varnish-cache.org/
215 .. _Varnish: https://www.varnish-cache.org/
216 .. _npm: https://www.npmjs.com/
216 .. _npm: https://www.npmjs.com/
@@ -1,84 +1,84 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
14
15 import configparser
15 import configparser
16 import functools
16 import functools
17 import logging.config
17 import logging.config
18 import os
18 import os
19 import re
19 import re
20 import sys
20 import sys
21
21
22 import click
22 import click
23 import paste.deploy
23 import paste.deploy
24
24
25 import kallithea
25 import kallithea
26 import kallithea.config.middleware
26 import kallithea.config.application
27
27
28
28
29 # kallithea_cli is usually invoked through the 'kallithea-cli' wrapper script
29 # kallithea_cli is usually invoked through the 'kallithea-cli' wrapper script
30 # that is installed by setuptools, as specified in setup.py console_scripts
30 # that is installed by setuptools, as specified in setup.py console_scripts
31 # entry_points. The script will be using the right virtualenv (if any), and for
31 # entry_points. The script will be using the right virtualenv (if any), and for
32 # Unix, it will contain #! pointing at the right python executable. The script
32 # Unix, it will contain #! pointing at the right python executable. The script
33 # also makes sure sys.argv[0] points back at the script path, and that is what
33 # also makes sure sys.argv[0] points back at the script path, and that is what
34 # can be used to invoke 'kallithea-cli' later.
34 # can be used to invoke 'kallithea-cli' later.
35 kallithea_cli_path = sys.argv[0]
35 kallithea_cli_path = sys.argv[0]
36
36
37
37
38 def read_config(ini_file_name, strip_section_prefix):
38 def read_config(ini_file_name, strip_section_prefix):
39 """Read ini_file_name content, and for all sections like '[X:Y]' where X is
39 """Read ini_file_name content, and for all sections like '[X:Y]' where X is
40 strip_section_prefix, replace the section name with '[Y]'."""
40 strip_section_prefix, replace the section name with '[Y]'."""
41
41
42 def repl(m):
42 def repl(m):
43 if m.group(1) == strip_section_prefix:
43 if m.group(1) == strip_section_prefix:
44 return '[%s]' % m.group(2)
44 return '[%s]' % m.group(2)
45 return m.group(0)
45 return m.group(0)
46
46
47 with open(ini_file_name) as f:
47 with open(ini_file_name) as f:
48 return re.sub(r'^\[([^:]+):(.*)]', repl, f.read(), flags=re.MULTILINE)
48 return re.sub(r'^\[([^:]+):(.*)]', repl, f.read(), flags=re.MULTILINE)
49
49
50
50
51 # This placeholder is the main entry point for the kallithea-cli command
51 # This placeholder is the main entry point for the kallithea-cli command
52 @click.group(context_settings=dict(help_option_names=['-h', '--help']))
52 @click.group(context_settings=dict(help_option_names=['-h', '--help']))
53 def cli():
53 def cli():
54 """Various commands to manage a Kallithea instance."""
54 """Various commands to manage a Kallithea instance."""
55
55
56 def register_command(config_file=False, config_file_initialize_app=False, hidden=False):
56 def register_command(config_file=False, config_file_initialize_app=False, hidden=False):
57 """Register a kallithea-cli subcommand.
57 """Register a kallithea-cli subcommand.
58
58
59 If one of the config_file flags are true, a config file must be specified
59 If one of the config_file flags are true, a config file must be specified
60 with -c and it is read and logging is configured. The configuration is
60 with -c and it is read and logging is configured. The configuration is
61 available in the kallithea.CONFIG dict.
61 available in the kallithea.CONFIG dict.
62
62
63 If config_file_initialize_app is true, Kallithea, TurboGears global state
63 If config_file_initialize_app is true, Kallithea, TurboGears global state
64 (including tg.config), and database access will also be fully initialized.
64 (including tg.config), and database access will also be fully initialized.
65 """
65 """
66 cli_command = cli.command(hidden=hidden)
66 cli_command = cli.command(hidden=hidden)
67 if config_file or config_file_initialize_app:
67 if config_file or config_file_initialize_app:
68 def annotator(annotated):
68 def annotator(annotated):
69 @click.option('--config_file', '-c', help="Path to .ini file with app configuration.",
69 @click.option('--config_file', '-c', help="Path to .ini file with app configuration.",
70 type=click.Path(dir_okay=False, exists=True, readable=True), required=True)
70 type=click.Path(dir_okay=False, exists=True, readable=True), required=True)
71 @functools.wraps(annotated) # reuse meta data from the wrapped function so click can see other options
71 @functools.wraps(annotated) # reuse meta data from the wrapped function so click can see other options
72 def runtime_wrapper(config_file, *args, **kwargs):
72 def runtime_wrapper(config_file, *args, **kwargs):
73 path_to_ini_file = os.path.realpath(config_file)
73 path_to_ini_file = os.path.realpath(config_file)
74 kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file)
74 kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file)
75 cp = configparser.ConfigParser(strict=False)
75 cp = configparser.ConfigParser(strict=False)
76 cp.read_string(read_config(path_to_ini_file, strip_section_prefix=annotated.__name__))
76 cp.read_string(read_config(path_to_ini_file, strip_section_prefix=annotated.__name__))
77 logging.config.fileConfig(cp,
77 logging.config.fileConfig(cp,
78 {'__file__': path_to_ini_file, 'here': os.path.dirname(path_to_ini_file)})
78 {'__file__': path_to_ini_file, 'here': os.path.dirname(path_to_ini_file)})
79 if config_file_initialize_app:
79 if config_file_initialize_app:
80 kallithea.config.middleware.make_app(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
80 kallithea.config.application.make_app(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
81 return annotated(*args, **kwargs)
81 return annotated(*args, **kwargs)
82 return cli_command(runtime_wrapper)
82 return cli_command(runtime_wrapper)
83 return annotator
83 return annotator
84 return cli_command
84 return cli_command
@@ -1,80 +1,80 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 import click
14 import click
15
15
16 import kallithea
16 import kallithea
17 import kallithea.bin.kallithea_cli_base as cli_base
17 import kallithea.bin.kallithea_cli_base as cli_base
18 from kallithea.lib.db_manage import DbManage
18 from kallithea.lib.db_manage import DbManage
19 from kallithea.model.meta import Session
19 from kallithea.model.meta import Session
20
20
21
21
22 @cli_base.register_command(config_file=True)
22 @cli_base.register_command(config_file=True)
23 @click.option('--user', help='Username of administrator account.')
23 @click.option('--user', help='Username of administrator account.')
24 @click.option('--password', help='Password for administrator account.')
24 @click.option('--password', help='Password for administrator account.')
25 @click.option('--email', help='Email address of administrator account.')
25 @click.option('--email', help='Email address of administrator account.')
26 @click.option('--repos', help='Absolute path to repositories location.')
26 @click.option('--repos', help='Absolute path to repositories location.')
27 @click.option('--force-yes', is_flag=True, help='Answer yes to every question.')
27 @click.option('--force-yes', is_flag=True, help='Answer yes to every question.')
28 @click.option('--force-no', is_flag=True, help='Answer no to every question.')
28 @click.option('--force-no', is_flag=True, help='Answer no to every question.')
29 @click.option('--public-access/--no-public-access', default=True,
29 @click.option('--public-access/--no-public-access', default=True,
30 help='Enable/disable public access on this installation (default: enable)')
30 help='Enable/disable public access on this installation (default: enable)')
31 def db_create(user, password, email, repos, force_yes, force_no, public_access):
31 def db_create(user, password, email, repos, force_yes, force_no, public_access):
32 """Initialize the database.
32 """Initialize the database.
33
33
34 Create all required tables in the database specified in the configuration
34 Create all required tables in the database specified in the configuration
35 file. Create the administrator account. Set certain settings based on
35 file. Create the administrator account. Set certain settings based on
36 values you provide.
36 values you provide.
37
37
38 You can pass the answers to all questions as options to this command.
38 You can pass the answers to all questions as options to this command.
39 """
39 """
40 dbconf = kallithea.CONFIG['sqlalchemy.url']
40 dbconf = kallithea.CONFIG['sqlalchemy.url']
41
41
42 # force_ask should be True (yes), False (no), or None (ask)
42 # force_ask should be True (yes), False (no), or None (ask)
43 if force_yes:
43 if force_yes:
44 force_ask = True
44 force_ask = True
45 elif force_no:
45 elif force_no:
46 force_ask = False
46 force_ask = False
47 else:
47 else:
48 force_ask = None
48 force_ask = None
49
49
50 cli_args = dict(
50 cli_args = dict(
51 username=user,
51 username=user,
52 password=password,
52 password=password,
53 email=email,
53 email=email,
54 repos_location=repos,
54 repos_location=repos,
55 force_ask=force_ask,
55 force_ask=force_ask,
56 public_access=public_access,
56 public_access=public_access,
57 )
57 )
58 dbmanage = DbManage(dbconf=dbconf, root=kallithea.CONFIG['here'],
58 dbmanage = DbManage(dbconf=dbconf, root=kallithea.CONFIG['here'],
59 tests=False, cli_args=cli_args)
59 tests=False, cli_args=cli_args)
60 dbmanage.create_tables(override=True)
60 dbmanage.create_tables(override=True)
61 repo_root_path = dbmanage.prompt_repo_root_path(None)
61 repo_root_path = dbmanage.prompt_repo_root_path(None)
62 dbmanage.create_settings(repo_root_path)
62 dbmanage.create_settings(repo_root_path)
63 dbmanage.create_default_user()
63 dbmanage.create_default_user()
64 dbmanage.admin_prompt()
64 dbmanage.admin_prompt()
65 dbmanage.create_permissions()
65 dbmanage.create_permissions()
66 dbmanage.populate_default_permissions()
66 dbmanage.populate_default_permissions()
67 Session().commit()
67 Session().commit()
68
68
69 # initial repository scan
69 # initial repository scan
70 kallithea.config.middleware.make_app(
70 kallithea.config.application.make_app(
71 kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
71 kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
72 added, _ = kallithea.lib.utils.repo2db_mapper(kallithea.model.scm.ScmModel().repo_scan())
72 added, _ = kallithea.lib.utils.repo2db_mapper(kallithea.model.scm.ScmModel().repo_scan())
73 if added:
73 if added:
74 click.echo('Initial repository scan: added following repositories:')
74 click.echo('Initial repository scan: added following repositories:')
75 click.echo('\t%s' % '\n\t'.join(added))
75 click.echo('\t%s' % '\n\t'.join(added))
76 else:
76 else:
77 click.echo('Initial repository scan: no repositories found.')
77 click.echo('Initial repository scan: no repositories found.')
78
78
79 click.echo('Database set up successfully.')
79 click.echo('Database set up successfully.')
80 click.echo("Don't forget to build the front-end using 'kallithea-cli front-end-build'.")
80 click.echo("Don't forget to build the front-end using 'kallithea-cli front-end-build'.")
1 NO CONTENT: file renamed from kallithea/config/middleware.py to kallithea/config/application.py
NO CONTENT: file renamed from kallithea/config/middleware.py to kallithea/config/application.py
@@ -1,404 +1,404 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.lib.hooks
15 kallithea.lib.hooks
16 ~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~
17
17
18 Hooks run by Kallithea
18 Hooks run by Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Aug 6, 2010
22 :created_on: Aug 6, 2010
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import os
28 import os
29 import sys
29 import sys
30 import time
30 import time
31
31
32 import mercurial.scmutil
32 import mercurial.scmutil
33
33
34 from kallithea.lib import helpers as h
34 from kallithea.lib import helpers as h
35 from kallithea.lib.exceptions import UserCreationError
35 from kallithea.lib.exceptions import UserCreationError
36 from kallithea.lib.utils import action_logger, make_ui
36 from kallithea.lib.utils import action_logger, make_ui
37 from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str
37 from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str
38 from kallithea.lib.vcs.backends.base import EmptyChangeset
38 from kallithea.lib.vcs.backends.base import EmptyChangeset
39 from kallithea.model.db import Repository, User
39 from kallithea.model.db import Repository, User
40
40
41
41
42 def _get_scm_size(alias, root_path):
42 def _get_scm_size(alias, root_path):
43 if not alias.startswith('.'):
43 if not alias.startswith('.'):
44 alias += '.'
44 alias += '.'
45
45
46 size_scm, size_root = 0, 0
46 size_scm, size_root = 0, 0
47 for path, dirs, files in os.walk(root_path):
47 for path, dirs, files in os.walk(root_path):
48 if path.find(alias) != -1:
48 if path.find(alias) != -1:
49 for f in files:
49 for f in files:
50 try:
50 try:
51 size_scm += os.path.getsize(os.path.join(path, f))
51 size_scm += os.path.getsize(os.path.join(path, f))
52 except OSError:
52 except OSError:
53 pass
53 pass
54 else:
54 else:
55 for f in files:
55 for f in files:
56 try:
56 try:
57 size_root += os.path.getsize(os.path.join(path, f))
57 size_root += os.path.getsize(os.path.join(path, f))
58 except OSError:
58 except OSError:
59 pass
59 pass
60
60
61 size_scm_f = h.format_byte_size(size_scm)
61 size_scm_f = h.format_byte_size(size_scm)
62 size_root_f = h.format_byte_size(size_root)
62 size_root_f = h.format_byte_size(size_root)
63 size_total_f = h.format_byte_size(size_root + size_scm)
63 size_total_f = h.format_byte_size(size_root + size_scm)
64
64
65 return size_scm_f, size_root_f, size_total_f
65 return size_scm_f, size_root_f, size_total_f
66
66
67
67
68 def repo_size(ui, repo, hooktype=None, **kwargs):
68 def repo_size(ui, repo, hooktype=None, **kwargs):
69 """Show size of Mercurial repository.
69 """Show size of Mercurial repository.
70
70
71 Called as Mercurial hook changegroup.repo_size after push.
71 Called as Mercurial hook changegroup.repo_size after push.
72 """
72 """
73 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', safe_str(repo.root))
73 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', safe_str(repo.root))
74
74
75 last_cs = repo[len(repo) - 1]
75 last_cs = repo[len(repo) - 1]
76
76
77 msg = ('Repository size .hg: %s Checkout: %s Total: %s\n'
77 msg = ('Repository size .hg: %s Checkout: %s Total: %s\n'
78 'Last revision is now r%s:%s\n') % (
78 'Last revision is now r%s:%s\n') % (
79 size_hg_f, size_root_f, size_total_f, last_cs.rev(), ascii_str(last_cs.hex())[:12]
79 size_hg_f, size_root_f, size_total_f, last_cs.rev(), ascii_str(last_cs.hex())[:12]
80 )
80 )
81 ui.status(safe_bytes(msg))
81 ui.status(safe_bytes(msg))
82
82
83
83
84 def log_pull_action(ui, repo, **kwargs):
84 def log_pull_action(ui, repo, **kwargs):
85 """Logs user last pull action
85 """Logs user last pull action
86
86
87 Called as Mercurial hook outgoing.pull_logger or from Kallithea before invoking Git.
87 Called as Mercurial hook outgoing.pull_logger or from Kallithea before invoking Git.
88
88
89 Does *not* use the action from the hook environment but is always 'pull'.
89 Does *not* use the action from the hook environment but is always 'pull'.
90 """
90 """
91 ex = get_hook_environment()
91 ex = get_hook_environment()
92
92
93 user = User.get_by_username(ex.username)
93 user = User.get_by_username(ex.username)
94 action = 'pull'
94 action = 'pull'
95 action_logger(user, action, ex.repository, ex.ip, commit=True)
95 action_logger(user, action, ex.repository, ex.ip, commit=True)
96 # extension hook call
96 # extension hook call
97 from kallithea import EXTENSIONS
97 from kallithea import EXTENSIONS
98 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
98 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
99 if callable(callback):
99 if callable(callback):
100 kw = {}
100 kw = {}
101 kw.update(ex)
101 kw.update(ex)
102 callback(**kw)
102 callback(**kw)
103
103
104 return 0
104 return 0
105
105
106
106
107 def log_push_action(ui, repo, node, node_last, **kwargs):
107 def log_push_action(ui, repo, node, node_last, **kwargs):
108 """
108 """
109 Register that changes have been added to the repo - log the action *and* invalidate caches.
109 Register that changes have been added to the repo - log the action *and* invalidate caches.
110 Note: This hook is not only logging, but also the side effect invalidating
110 Note: This hook is not only logging, but also the side effect invalidating
111 caches! The function should perhaps be renamed.
111 caches! The function should perhaps be renamed.
112
112
113 Called as Mercurial hook changegroup.kallithea_log_push_action .
113 Called as Mercurial hook changegroup.kallithea_log_push_action .
114
114
115 The pushed changesets is given by the revset 'node:node_last'.
115 The pushed changesets is given by the revset 'node:node_last'.
116 """
116 """
117 revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
117 revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
118 process_pushed_raw_ids(revs)
118 process_pushed_raw_ids(revs)
119 return 0
119 return 0
120
120
121
121
122 def process_pushed_raw_ids(revs):
122 def process_pushed_raw_ids(revs):
123 """
123 """
124 Register that changes have been added to the repo - log the action *and* invalidate caches.
124 Register that changes have been added to the repo - log the action *and* invalidate caches.
125
125
126 Called from Mercurial changegroup.kallithea_log_push_action calling hook log_push_action,
126 Called from Mercurial changegroup.kallithea_log_push_action calling hook log_push_action,
127 or from the Git post-receive hook calling handle_git_post_receive ...
127 or from the Git post-receive hook calling handle_git_post_receive ...
128 or from scm _handle_push.
128 or from scm _handle_push.
129 """
129 """
130 ex = get_hook_environment()
130 ex = get_hook_environment()
131
131
132 action = '%s:%s' % (ex.action, ','.join(revs))
132 action = '%s:%s' % (ex.action, ','.join(revs))
133 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
133 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
134
134
135 from kallithea.model.scm import ScmModel
135 from kallithea.model.scm import ScmModel
136 ScmModel().mark_for_invalidation(ex.repository)
136 ScmModel().mark_for_invalidation(ex.repository)
137
137
138 # extension hook call
138 # extension hook call
139 from kallithea import EXTENSIONS
139 from kallithea import EXTENSIONS
140 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
140 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
141 if callable(callback):
141 if callable(callback):
142 kw = {'pushed_revs': revs}
142 kw = {'pushed_revs': revs}
143 kw.update(ex)
143 kw.update(ex)
144 callback(**kw)
144 callback(**kw)
145
145
146
146
147 def log_create_repository(repository_dict, created_by, **kwargs):
147 def log_create_repository(repository_dict, created_by, **kwargs):
148 """
148 """
149 Post create repository Hook.
149 Post create repository Hook.
150
150
151 :param repository: dict dump of repository object
151 :param repository: dict dump of repository object
152 :param created_by: username who created repository
152 :param created_by: username who created repository
153
153
154 available keys of repository_dict:
154 available keys of repository_dict:
155
155
156 'repo_type',
156 'repo_type',
157 'description',
157 'description',
158 'private',
158 'private',
159 'created_on',
159 'created_on',
160 'enable_downloads',
160 'enable_downloads',
161 'repo_id',
161 'repo_id',
162 'owner_id',
162 'owner_id',
163 'enable_statistics',
163 'enable_statistics',
164 'clone_uri',
164 'clone_uri',
165 'fork_id',
165 'fork_id',
166 'group_id',
166 'group_id',
167 'repo_name'
167 'repo_name'
168
168
169 """
169 """
170 from kallithea import EXTENSIONS
170 from kallithea import EXTENSIONS
171 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
171 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
172 if callable(callback):
172 if callable(callback):
173 kw = {}
173 kw = {}
174 kw.update(repository_dict)
174 kw.update(repository_dict)
175 kw.update({'created_by': created_by})
175 kw.update({'created_by': created_by})
176 kw.update(kwargs)
176 kw.update(kwargs)
177 return callback(**kw)
177 return callback(**kw)
178
178
179 return 0
179 return 0
180
180
181
181
182 def check_allowed_create_user(user_dict, created_by, **kwargs):
182 def check_allowed_create_user(user_dict, created_by, **kwargs):
183 # pre create hooks
183 # pre create hooks
184 from kallithea import EXTENSIONS
184 from kallithea import EXTENSIONS
185 callback = getattr(EXTENSIONS, 'PRE_CREATE_USER_HOOK', None)
185 callback = getattr(EXTENSIONS, 'PRE_CREATE_USER_HOOK', None)
186 if callable(callback):
186 if callable(callback):
187 allowed, reason = callback(created_by=created_by, **user_dict)
187 allowed, reason = callback(created_by=created_by, **user_dict)
188 if not allowed:
188 if not allowed:
189 raise UserCreationError(reason)
189 raise UserCreationError(reason)
190
190
191
191
192 def log_create_user(user_dict, created_by, **kwargs):
192 def log_create_user(user_dict, created_by, **kwargs):
193 """
193 """
194 Post create user Hook.
194 Post create user Hook.
195
195
196 :param user_dict: dict dump of user object
196 :param user_dict: dict dump of user object
197
197
198 available keys for user_dict:
198 available keys for user_dict:
199
199
200 'username',
200 'username',
201 'full_name_or_username',
201 'full_name_or_username',
202 'full_contact',
202 'full_contact',
203 'user_id',
203 'user_id',
204 'name',
204 'name',
205 'firstname',
205 'firstname',
206 'short_contact',
206 'short_contact',
207 'admin',
207 'admin',
208 'lastname',
208 'lastname',
209 'ip_addresses',
209 'ip_addresses',
210 'ldap_dn',
210 'ldap_dn',
211 'email',
211 'email',
212 'api_key',
212 'api_key',
213 'last_login',
213 'last_login',
214 'full_name',
214 'full_name',
215 'active',
215 'active',
216 'password',
216 'password',
217 'emails',
217 'emails',
218
218
219 """
219 """
220 from kallithea import EXTENSIONS
220 from kallithea import EXTENSIONS
221 callback = getattr(EXTENSIONS, 'CREATE_USER_HOOK', None)
221 callback = getattr(EXTENSIONS, 'CREATE_USER_HOOK', None)
222 if callable(callback):
222 if callable(callback):
223 return callback(created_by=created_by, **user_dict)
223 return callback(created_by=created_by, **user_dict)
224
224
225 return 0
225 return 0
226
226
227
227
228 def log_delete_repository(repository_dict, deleted_by, **kwargs):
228 def log_delete_repository(repository_dict, deleted_by, **kwargs):
229 """
229 """
230 Post delete repository Hook.
230 Post delete repository Hook.
231
231
232 :param repository: dict dump of repository object
232 :param repository: dict dump of repository object
233 :param deleted_by: username who deleted the repository
233 :param deleted_by: username who deleted the repository
234
234
235 available keys of repository_dict:
235 available keys of repository_dict:
236
236
237 'repo_type',
237 'repo_type',
238 'description',
238 'description',
239 'private',
239 'private',
240 'created_on',
240 'created_on',
241 'enable_downloads',
241 'enable_downloads',
242 'repo_id',
242 'repo_id',
243 'owner_id',
243 'owner_id',
244 'enable_statistics',
244 'enable_statistics',
245 'clone_uri',
245 'clone_uri',
246 'fork_id',
246 'fork_id',
247 'group_id',
247 'group_id',
248 'repo_name'
248 'repo_name'
249
249
250 """
250 """
251 from kallithea import EXTENSIONS
251 from kallithea import EXTENSIONS
252 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
252 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
253 if callable(callback):
253 if callable(callback):
254 kw = {}
254 kw = {}
255 kw.update(repository_dict)
255 kw.update(repository_dict)
256 kw.update({'deleted_by': deleted_by,
256 kw.update({'deleted_by': deleted_by,
257 'deleted_on': time.time()})
257 'deleted_on': time.time()})
258 kw.update(kwargs)
258 kw.update(kwargs)
259 return callback(**kw)
259 return callback(**kw)
260
260
261 return 0
261 return 0
262
262
263
263
264 def log_delete_user(user_dict, deleted_by, **kwargs):
264 def log_delete_user(user_dict, deleted_by, **kwargs):
265 """
265 """
266 Post delete user Hook.
266 Post delete user Hook.
267
267
268 :param user_dict: dict dump of user object
268 :param user_dict: dict dump of user object
269
269
270 available keys for user_dict:
270 available keys for user_dict:
271
271
272 'username',
272 'username',
273 'full_name_or_username',
273 'full_name_or_username',
274 'full_contact',
274 'full_contact',
275 'user_id',
275 'user_id',
276 'name',
276 'name',
277 'firstname',
277 'firstname',
278 'short_contact',
278 'short_contact',
279 'admin',
279 'admin',
280 'lastname',
280 'lastname',
281 'ip_addresses',
281 'ip_addresses',
282 'ldap_dn',
282 'ldap_dn',
283 'email',
283 'email',
284 'api_key',
284 'api_key',
285 'last_login',
285 'last_login',
286 'full_name',
286 'full_name',
287 'active',
287 'active',
288 'password',
288 'password',
289 'emails',
289 'emails',
290
290
291 """
291 """
292 from kallithea import EXTENSIONS
292 from kallithea import EXTENSIONS
293 callback = getattr(EXTENSIONS, 'DELETE_USER_HOOK', None)
293 callback = getattr(EXTENSIONS, 'DELETE_USER_HOOK', None)
294 if callable(callback):
294 if callable(callback):
295 return callback(deleted_by=deleted_by, **user_dict)
295 return callback(deleted_by=deleted_by, **user_dict)
296
296
297 return 0
297 return 0
298
298
299
299
300 def _hook_environment(repo_path):
300 def _hook_environment(repo_path):
301 """
301 """
302 Create a light-weight environment for stand-alone scripts and return an UI and the
302 Create a light-weight environment for stand-alone scripts and return an UI and the
303 db repository.
303 db repository.
304
304
305 Git hooks are executed as subprocess of Git while Kallithea is waiting, and
305 Git hooks are executed as subprocess of Git while Kallithea is waiting, and
306 they thus need enough info to be able to create an app environment and
306 they thus need enough info to be able to create an app environment and
307 connect to the database.
307 connect to the database.
308 """
308 """
309 import paste.deploy
309 import paste.deploy
310 import kallithea.config.middleware
310 import kallithea.config.application
311
311
312 extras = get_hook_environment()
312 extras = get_hook_environment()
313
313
314 path_to_ini_file = extras['config']
314 path_to_ini_file = extras['config']
315 kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file)
315 kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file)
316 #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
316 #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
317 kallithea.config.middleware.make_app(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
317 kallithea.config.application.make_app(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf)
318
318
319 # fix if it's not a bare repo
319 # fix if it's not a bare repo
320 if repo_path.endswith(os.sep + '.git'):
320 if repo_path.endswith(os.sep + '.git'):
321 repo_path = repo_path[:-5]
321 repo_path = repo_path[:-5]
322
322
323 repo = Repository.get_by_full_path(repo_path)
323 repo = Repository.get_by_full_path(repo_path)
324 if not repo:
324 if not repo:
325 raise OSError('Repository %s not found in database' % repo_path)
325 raise OSError('Repository %s not found in database' % repo_path)
326
326
327 baseui = make_ui()
327 baseui = make_ui()
328 return baseui, repo
328 return baseui, repo
329
329
330
330
331 def handle_git_pre_receive(repo_path, git_stdin_lines):
331 def handle_git_pre_receive(repo_path, git_stdin_lines):
332 """Called from Git pre-receive hook"""
332 """Called from Git pre-receive hook"""
333 # Currently unused. TODO: remove?
333 # Currently unused. TODO: remove?
334 return 0
334 return 0
335
335
336
336
337 def handle_git_post_receive(repo_path, git_stdin_lines):
337 def handle_git_post_receive(repo_path, git_stdin_lines):
338 """Called from Git post-receive hook"""
338 """Called from Git post-receive hook"""
339 try:
339 try:
340 baseui, repo = _hook_environment(repo_path)
340 baseui, repo = _hook_environment(repo_path)
341 except HookEnvironmentError as e:
341 except HookEnvironmentError as e:
342 sys.stderr.write("Skipping Kallithea Git post-recieve hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
342 sys.stderr.write("Skipping Kallithea Git post-recieve hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
343 return 0
343 return 0
344
344
345 # the post push hook should never use the cached instance
345 # the post push hook should never use the cached instance
346 scm_repo = repo.scm_instance_no_cache()
346 scm_repo = repo.scm_instance_no_cache()
347
347
348 rev_data = []
348 rev_data = []
349 for l in git_stdin_lines:
349 for l in git_stdin_lines:
350 old_rev, new_rev, ref = l.strip().split(' ')
350 old_rev, new_rev, ref = l.strip().split(' ')
351 _ref_data = ref.split('/')
351 _ref_data = ref.split('/')
352 if _ref_data[1] in ['tags', 'heads']:
352 if _ref_data[1] in ['tags', 'heads']:
353 rev_data.append({'old_rev': old_rev,
353 rev_data.append({'old_rev': old_rev,
354 'new_rev': new_rev,
354 'new_rev': new_rev,
355 'ref': ref,
355 'ref': ref,
356 'type': _ref_data[1],
356 'type': _ref_data[1],
357 'name': '/'.join(_ref_data[2:])})
357 'name': '/'.join(_ref_data[2:])})
358
358
359 git_revs = []
359 git_revs = []
360 for push_ref in rev_data:
360 for push_ref in rev_data:
361 _type = push_ref['type']
361 _type = push_ref['type']
362 if _type == 'heads':
362 if _type == 'heads':
363 if push_ref['old_rev'] == EmptyChangeset().raw_id:
363 if push_ref['old_rev'] == EmptyChangeset().raw_id:
364 # update the symbolic ref if we push new repo
364 # update the symbolic ref if we push new repo
365 if scm_repo.is_empty():
365 if scm_repo.is_empty():
366 scm_repo._repo.refs.set_symbolic_ref(
366 scm_repo._repo.refs.set_symbolic_ref(
367 b'HEAD',
367 b'HEAD',
368 b'refs/heads/%s' % safe_bytes(push_ref['name']))
368 b'refs/heads/%s' % safe_bytes(push_ref['name']))
369
369
370 # build exclude list without the ref
370 # build exclude list without the ref
371 cmd = ['for-each-ref', '--format=%(refname)', 'refs/heads/*']
371 cmd = ['for-each-ref', '--format=%(refname)', 'refs/heads/*']
372 stdout = scm_repo.run_git_command(cmd)
372 stdout = scm_repo.run_git_command(cmd)
373 ref = push_ref['ref']
373 ref = push_ref['ref']
374 heads = [head for head in stdout.splitlines() if head != ref]
374 heads = [head for head in stdout.splitlines() if head != ref]
375 # now list the git revs while excluding from the list
375 # now list the git revs while excluding from the list
376 cmd = ['log', push_ref['new_rev'], '--reverse', '--pretty=format:%H']
376 cmd = ['log', push_ref['new_rev'], '--reverse', '--pretty=format:%H']
377 cmd.append('--not')
377 cmd.append('--not')
378 cmd.extend(heads) # empty list is ok
378 cmd.extend(heads) # empty list is ok
379 stdout = scm_repo.run_git_command(cmd)
379 stdout = scm_repo.run_git_command(cmd)
380 git_revs += stdout.splitlines()
380 git_revs += stdout.splitlines()
381
381
382 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
382 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
383 # delete branch case
383 # delete branch case
384 git_revs += ['delete_branch=>%s' % push_ref['name']]
384 git_revs += ['delete_branch=>%s' % push_ref['name']]
385 else:
385 else:
386 cmd = ['log', '%(old_rev)s..%(new_rev)s' % push_ref,
386 cmd = ['log', '%(old_rev)s..%(new_rev)s' % push_ref,
387 '--reverse', '--pretty=format:%H']
387 '--reverse', '--pretty=format:%H']
388 stdout = scm_repo.run_git_command(cmd)
388 stdout = scm_repo.run_git_command(cmd)
389 git_revs += stdout.splitlines()
389 git_revs += stdout.splitlines()
390
390
391 elif _type == 'tags':
391 elif _type == 'tags':
392 git_revs += ['tag=>%s' % push_ref['name']]
392 git_revs += ['tag=>%s' % push_ref['name']]
393
393
394 process_pushed_raw_ids(git_revs)
394 process_pushed_raw_ids(git_revs)
395
395
396 return 0
396 return 0
397
397
398
398
399 # Almost exactly like Mercurial contrib/hg-ssh:
399 # Almost exactly like Mercurial contrib/hg-ssh:
400 def rejectpush(ui, **kwargs):
400 def rejectpush(ui, **kwargs):
401 """Mercurial hook to be installed as pretxnopen and prepushkey for read-only repos"""
401 """Mercurial hook to be installed as pretxnopen and prepushkey for read-only repos"""
402 ex = get_hook_environment()
402 ex = get_hook_environment()
403 ui.warn(safe_bytes("Push access to %r denied\n" % ex.repository))
403 ui.warn(safe_bytes("Push access to %r denied\n" % ex.repository))
404 return 1
404 return 1
@@ -1,161 +1,161 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 import os
3 import os
4 import platform
4 import platform
5 import sys
5 import sys
6
6
7 import setuptools
7 import setuptools
8 # monkey patch setuptools to use distutils owner/group functionality
8 # monkey patch setuptools to use distutils owner/group functionality
9 from setuptools.command import sdist
9 from setuptools.command import sdist
10
10
11
11
12 if sys.version_info < (3, 6):
12 if sys.version_info < (3, 6):
13 raise Exception('Kallithea requires Python 3.6 or later')
13 raise Exception('Kallithea requires Python 3.6 or later')
14
14
15
15
16 here = os.path.abspath(os.path.dirname(__file__))
16 here = os.path.abspath(os.path.dirname(__file__))
17
17
18
18
19 def _get_meta_var(name, data, callback_handler=None):
19 def _get_meta_var(name, data, callback_handler=None):
20 import re
20 import re
21 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
21 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
22 if matches:
22 if matches:
23 s = eval(matches.groups()[0])
23 s = eval(matches.groups()[0])
24 if callable(callback_handler):
24 if callable(callback_handler):
25 return callback_handler(s)
25 return callback_handler(s)
26 return s
26 return s
27
27
28 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'r')
28 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'r')
29 _metadata = _meta.read()
29 _metadata = _meta.read()
30 _meta.close()
30 _meta.close()
31
31
32 def callback(V):
32 def callback(V):
33 return '.'.join(map(str, V[:3])) + '.'.join(V[3:])
33 return '.'.join(map(str, V[:3])) + '.'.join(V[3:])
34 __version__ = _get_meta_var('VERSION', _metadata, callback)
34 __version__ = _get_meta_var('VERSION', _metadata, callback)
35 __license__ = _get_meta_var('__license__', _metadata)
35 __license__ = _get_meta_var('__license__', _metadata)
36 __author__ = _get_meta_var('__author__', _metadata)
36 __author__ = _get_meta_var('__author__', _metadata)
37 __url__ = _get_meta_var('__url__', _metadata)
37 __url__ = _get_meta_var('__url__', _metadata)
38 # defines current platform
38 # defines current platform
39 __platform__ = platform.system()
39 __platform__ = platform.system()
40
40
41 is_windows = __platform__ in ['Windows']
41 is_windows = __platform__ in ['Windows']
42
42
43 requirements = [
43 requirements = [
44 "alembic >= 1.0.10, < 1.5",
44 "alembic >= 1.0.10, < 1.5",
45 "gearbox >= 0.1.0, < 1",
45 "gearbox >= 0.1.0, < 1",
46 "waitress >= 0.8.8, < 1.5",
46 "waitress >= 0.8.8, < 1.5",
47 "WebOb >= 1.8, < 1.9",
47 "WebOb >= 1.8, < 1.9",
48 "backlash >= 0.1.2, < 1",
48 "backlash >= 0.1.2, < 1",
49 "TurboGears2 >= 2.4, < 2.5",
49 "TurboGears2 >= 2.4, < 2.5",
50 "tgext.routes >= 0.2.0, < 1",
50 "tgext.routes >= 0.2.0, < 1",
51 "Beaker >= 1.10.1, < 2",
51 "Beaker >= 1.10.1, < 2",
52 "WebHelpers2 >= 2.0, < 2.1",
52 "WebHelpers2 >= 2.0, < 2.1",
53 "FormEncode >= 1.3.1, < 1.4",
53 "FormEncode >= 1.3.1, < 1.4",
54 "SQLAlchemy >= 1.2.9, < 1.4",
54 "SQLAlchemy >= 1.2.9, < 1.4",
55 "Mako >= 0.9.1, < 1.2",
55 "Mako >= 0.9.1, < 1.2",
56 "Pygments >= 2.2.0, < 2.6",
56 "Pygments >= 2.2.0, < 2.6",
57 "Whoosh >= 2.7.1, < 2.8",
57 "Whoosh >= 2.7.1, < 2.8",
58 "celery >= 4.3, < 4.5, != 4.4.4", # 4.4.4 is broken due to unexpressed dependency on 'future', see https://github.com/celery/celery/pull/6146
58 "celery >= 4.3, < 4.5, != 4.4.4", # 4.4.4 is broken due to unexpressed dependency on 'future', see https://github.com/celery/celery/pull/6146
59 "Babel >= 1.3, < 2.9",
59 "Babel >= 1.3, < 2.9",
60 "python-dateutil >= 2.1.0, < 2.9",
60 "python-dateutil >= 2.1.0, < 2.9",
61 "Markdown >= 2.2.1, < 3.2",
61 "Markdown >= 2.2.1, < 3.2",
62 "docutils >= 0.11, < 0.17",
62 "docutils >= 0.11, < 0.17",
63 "URLObject >= 2.3.4, < 2.5",
63 "URLObject >= 2.3.4, < 2.5",
64 "Routes >= 2.0, < 2.5",
64 "Routes >= 2.0, < 2.5",
65 "dulwich >= 0.19.0, < 0.20",
65 "dulwich >= 0.19.0, < 0.20",
66 "mercurial >= 5.2, < 5.5",
66 "mercurial >= 5.2, < 5.5",
67 "decorator >= 4.2.1, < 4.5",
67 "decorator >= 4.2.1, < 4.5",
68 "Paste >= 2.0.3, < 3.4",
68 "Paste >= 2.0.3, < 3.4",
69 "bleach >= 3.0, < 3.1.4",
69 "bleach >= 3.0, < 3.1.4",
70 "Click >= 7.0, < 8",
70 "Click >= 7.0, < 8",
71 "ipaddr >= 2.2.0, < 2.3",
71 "ipaddr >= 2.2.0, < 2.3",
72 "paginate >= 0.5, < 0.6",
72 "paginate >= 0.5, < 0.6",
73 "paginate_sqlalchemy >= 0.3.0, < 0.4",
73 "paginate_sqlalchemy >= 0.3.0, < 0.4",
74 "bcrypt >= 3.1.0, < 3.2",
74 "bcrypt >= 3.1.0, < 3.2",
75 "pip >= 20.0, < 999",
75 "pip >= 20.0, < 999",
76 ]
76 ]
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 :: 3.6',
88 'Programming Language :: Python :: 3.6',
89 'Programming Language :: Python :: 3.7',
89 'Programming Language :: Python :: 3.7',
90 'Programming Language :: Python :: 3.8',
90 'Programming Language :: Python :: 3.8',
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 description = ('Kallithea is a fast and powerful management tool '
99 description = ('Kallithea is a fast and powerful management tool '
100 'for Mercurial and Git with a built in push/pull server, '
100 'for Mercurial and Git with a built in push/pull server, '
101 'full text search and code-review.')
101 'full text search and code-review.')
102
102
103 keywords = ' '.join([
103 keywords = ' '.join([
104 'kallithea', 'mercurial', 'git', 'code review',
104 'kallithea', 'mercurial', 'git', 'code review',
105 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
105 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
106 'hgwebdir', 'gitweb replacement', 'serving hgweb',
106 'hgwebdir', 'gitweb replacement', 'serving hgweb',
107 ])
107 ])
108
108
109 # long description
109 # long description
110 README_FILE = 'README.rst'
110 README_FILE = 'README.rst'
111 try:
111 try:
112 long_description = open(README_FILE).read()
112 long_description = open(README_FILE).read()
113 except IOError as err:
113 except IOError as err:
114 sys.stderr.write(
114 sys.stderr.write(
115 "[WARNING] Cannot find file specified as long_description (%s): %s\n"
115 "[WARNING] Cannot find file specified as long_description (%s): %s\n"
116 % (README_FILE, err)
116 % (README_FILE, err)
117 )
117 )
118 long_description = description
118 long_description = description
119
119
120
120
121 sdist_org = sdist.sdist
121 sdist_org = sdist.sdist
122 class sdist_new(sdist_org):
122 class sdist_new(sdist_org):
123 def initialize_options(self):
123 def initialize_options(self):
124 sdist_org.initialize_options(self)
124 sdist_org.initialize_options(self)
125 self.owner = self.group = 'root'
125 self.owner = self.group = 'root'
126 sdist.sdist = sdist_new
126 sdist.sdist = sdist_new
127
127
128 packages = setuptools.find_packages(exclude=['ez_setup'])
128 packages = setuptools.find_packages(exclude=['ez_setup'])
129
129
130 setuptools.setup(
130 setuptools.setup(
131 name='Kallithea',
131 name='Kallithea',
132 version=__version__,
132 version=__version__,
133 description=description,
133 description=description,
134 long_description=long_description,
134 long_description=long_description,
135 keywords=keywords,
135 keywords=keywords,
136 license=__license__,
136 license=__license__,
137 author=__author__,
137 author=__author__,
138 author_email='kallithea@sfconservancy.org',
138 author_email='kallithea@sfconservancy.org',
139 dependency_links=dependency_links,
139 dependency_links=dependency_links,
140 url=__url__,
140 url=__url__,
141 install_requires=requirements,
141 install_requires=requirements,
142 classifiers=classifiers,
142 classifiers=classifiers,
143 data_files=data_files,
143 data_files=data_files,
144 packages=packages,
144 packages=packages,
145 include_package_data=True,
145 include_package_data=True,
146 message_extractors={'kallithea': [
146 message_extractors={'kallithea': [
147 ('**.py', 'python', None),
147 ('**.py', 'python', None),
148 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
148 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
149 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
149 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
150 ('public/**', 'ignore', None)]},
150 ('public/**', 'ignore', None)]},
151 zip_safe=False,
151 zip_safe=False,
152 entry_points="""
152 entry_points="""
153 [console_scripts]
153 [console_scripts]
154 kallithea-api = kallithea.bin.kallithea_api:main
154 kallithea-api = kallithea.bin.kallithea_api:main
155 kallithea-gist = kallithea.bin.kallithea_gist:main
155 kallithea-gist = kallithea.bin.kallithea_gist:main
156 kallithea-cli = kallithea.bin.kallithea_cli:cli
156 kallithea-cli = kallithea.bin.kallithea_cli:cli
157
157
158 [paste.app_factory]
158 [paste.app_factory]
159 main = kallithea.config.middleware:make_app
159 main = kallithea.config.application:make_app
160 """,
160 """,
161 )
161 )
General Comments 0
You need to be logged in to leave comments. Login now