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