diff --git a/CONTRIBUTORS b/CONTRIBUTORS --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3,6 +3,7 @@ List of contributors to Kallithea projec Mads Kiilerich 2016-2024 Aristotelis Stageiritis 2024 Poesty Li 2024 + Valentin Kleibel 2024 Manuel Jacob 2019-2020 2022-2023 Mathias De Mare 2023 qy117121 2023 diff --git a/docs/setup.rst b/docs/setup.rst --- a/docs/setup.rst +++ b/docs/setup.rst @@ -631,10 +631,7 @@ that, you'll need to: WSGIRestrictEmbedded On -- Create a WSGI dispatch script, like the one below. The ``WSGIDaemonProcess`` - ``python-home`` directive will make sure it uses the right Python Virtual - Environment and that paste thus can pick up the right Kallithea - application. +- Create a WSGI dispatch script, like `/srv/kallithea/wsgi.py`: .. code-block:: python @@ -644,6 +641,10 @@ that, you'll need to: from paste.deploy import loadapp application = loadapp('config:' + ini) + We will use the ``WSGIDaemonProcess`` directive ``python-home`` to make sure + it uses the right Python Virtual Environment where paste loadapp will pick up + the right Kallithea application. + - Add the necessary ``WSGI*`` directives to the Apache Virtual Host configuration file, like in the example below. Notice that the WSGI dispatch script created above is referred to with the ``WSGIScriptAlias`` directive. @@ -665,8 +666,13 @@ that, you'll need to: WSGIDaemonProcess kallithea processes=5 threads=1 maximum-requests=100 \ python-home=/srv/kallithea/venv lang=C.UTF-8 WSGIProcessGroup kallithea - WSGIScriptAlias / /srv/kallithea/dispatch.wsgi + WSGIScriptAlias / /srv/kallithea/wsgi.py WSGIPassAuthorization On + + + Require all granted + + Other configuration files diff --git a/kallithea/bin/kallithea_cli_base.py b/kallithea/bin/kallithea_cli_base.py --- a/kallithea/bin/kallithea_cli_base.py +++ b/kallithea/bin/kallithea_cli_base.py @@ -79,7 +79,7 @@ def register_command(needs_config_file=F if needs_config_file: annotated(*args, config=config, **kwargs) if config_file_initialize_app: - kallithea.config.application.make_app(config.global_conf, **config.local_conf) + kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf) annotated(*args, **kwargs) return cli_command(runtime_wrapper) return annotator diff --git a/kallithea/bin/kallithea_cli_celery.py b/kallithea/bin/kallithea_cli_celery.py --- a/kallithea/bin/kallithea_cli_celery.py +++ b/kallithea/bin/kallithea_cli_celery.py @@ -40,7 +40,7 @@ def celery_run(celery_args, config): kallithea.CELERY_APP.config_from_object(celery_app.make_celery_config(config)) - kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app(config.global_conf, **config.local_conf) + kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf) args = list(celery_args) # args[0] is generally ignored when prog_name is specified, but -h *needs* it to be 'worker' ... but will also suggest that users specify 'worker' explicitly diff --git a/kallithea/bin/vcs_hooks.py b/kallithea/bin/vcs_hooks.py --- a/kallithea/bin/vcs_hooks.py +++ b/kallithea/bin/vcs_hooks.py @@ -116,7 +116,7 @@ def _git_hook_environment(repo_path): path_to_ini_file = extras['config'] config = paste.deploy.appconfig('config:' + path_to_ini_file) #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging - kallithea.config.application.make_app(config.global_conf, **config.local_conf) + kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf) # fix if it's not a bare repo if repo_path.endswith(os.sep + '.git'): diff --git a/kallithea/config/application.py b/kallithea/config/application.py --- a/kallithea/config/application.py +++ b/kallithea/config/application.py @@ -20,6 +20,7 @@ from kallithea.config.middleware.simpleg from kallithea.config.middleware.simplehg import SimpleHg from kallithea.config.middleware.wrapper import RequestWrapper from kallithea.lib.utils2 import asbool +from kallithea.lib.vcs.utils import hgcompat __all__ = ['make_app'] @@ -48,6 +49,13 @@ def wrap_app(app): def make_app(global_conf, **app_conf): + """Return WSGI app with logging Mercurial stdout/stderr - to be used as + Paste or mod_wsgi entry point""" + hgcompat.redirect_stdio_to_logging() + return make_app_raw(global_conf, **app_conf) + + +def make_app_raw(global_conf, **app_conf): """ Set up Kallithea with the settings found in the PasteDeploy configuration file used. diff --git a/kallithea/config/middleware/simplehg.py b/kallithea/config/middleware/simplehg.py --- a/kallithea/config/middleware/simplehg.py +++ b/kallithea/config/middleware/simplehg.py @@ -70,6 +70,7 @@ cmd_mapping = { 'changegroupsubset': 'pull', 'changesetdata': 'pull', 'clonebundles': 'pull', + 'clonebundles_manifest': 'pull', 'debugwireargs': 'pull', 'filedata': 'pull', 'getbundle': 'pull', diff --git a/kallithea/controllers/base.py b/kallithea/controllers/base.py --- a/kallithea/controllers/base.py +++ b/kallithea/controllers/base.py @@ -456,8 +456,16 @@ class BaseController(TGController): if request.method not in ['GET', 'HEAD', 'POST']: raise webob.exc.HTTPMethodNotAllowed() + try: + params = request.params + except UnicodeDecodeError as e: + # webobj will leak UnicodeDecodeError when decoding invalid + # URLencoded byte sequences in parameters + log.error('Error decoding request parameters: %s' % e) + raise webob.exc.HTTPBadRequest() + # Also verify the _method override - no longer allowed. - if request.params.get('_method') is None: + if params.get('_method') is None: pass # no override, no problem else: raise webob.exc.HTTPMethodNotAllowed() diff --git a/kallithea/lib/feeds.py b/kallithea/lib/feeds.py --- a/kallithea/lib/feeds.py +++ b/kallithea/lib/feeds.py @@ -57,7 +57,7 @@ def get_tag_uri(url, date): "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id" tag = re.sub('^http://', '', url) if date is not None: - tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1) + tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, count=1) tag = re.sub('#', '/', tag) return 'tag:' + tag diff --git a/kallithea/lib/vcs/backends/git/changeset.py b/kallithea/lib/vcs/backends/git/changeset.py --- a/kallithea/lib/vcs/backends/git/changeset.py +++ b/kallithea/lib/vcs/backends/git/changeset.py @@ -316,7 +316,7 @@ class GitChangeset(BaseChangeset): so = self.repository.run_git_command(cmd) for i, blame_line in enumerate(so.split('\n')[:-1]): - sha, line = re.split(r' ', blame_line, 1) + sha, line = re.split(r' ', blame_line, maxsplit=1) yield (i + 1, sha, lambda sha=sha: self.repository.get_changeset(sha), line) def fill_archive(self, stream=None, kind='tgz', prefix=None, diff --git a/kallithea/lib/vcs/utils/hgcompat.py b/kallithea/lib/vcs/utils/hgcompat.py --- a/kallithea/lib/vcs/utils/hgcompat.py +++ b/kallithea/lib/vcs/utils/hgcompat.py @@ -2,10 +2,25 @@ Mercurial libs compatibility """ +import logging + import mercurial.encoding import mercurial.localrepo +class MercurialStdLogger: + def __init__(self, logger): + self.logger = logger + + def write(self, message): + try: + self.logger(message.decode().rstrip()) + except: + self.logger(message) + + def flush(self): + pass + def monkey_do(): """Apply some Mercurial monkey patching""" # workaround for 3.3 94ac64bcf6fe and not calling largefiles reposetup correctly, and test_archival failing @@ -15,3 +30,18 @@ def monkey_do(): # Minimize potential impact from custom configuration mercurial.encoding.environ[b'HGPLAIN'] = b'1' + + +hglog = logging.getLogger("mercurial") + + +def redirect_stdio_to_logging(): + # Capture Mercurial stdout/stderr and send to a 'mercurial' logger + try: + import mercurial.utils.procutil as procutil + if not isinstance(procutil.stdout, MercurialStdLogger): + procutil.stdout = MercurialStdLogger(hglog.info) + if not isinstance(procutil.stderr, MercurialStdLogger): + procutil.stderr = MercurialStdLogger(hglog.warning) + except Exception as e: + hglog.error("Exception installing procutil stdout/stderr: %s", e) diff --git a/kallithea/templates/about.html b/kallithea/templates/about.html --- a/kallithea/templates/about.html +++ b/kallithea/templates/about.html @@ -27,6 +27,7 @@
  • Copyright © 2012–2024, Mads Kiilerich
  • Copyright © 2024, Aristotelis Stageiritis
  • Copyright © 2024, Poesty Li
  • +
  • Copyright © 2024, Valentin Kleibel
  • Copyright © 2019–2020, 2022–2023, Manuel Jacob
  • Copyright © 2023, Mathias De Mare
  • Copyright © 2023, qy117121
  • diff --git a/kallithea/tests/scripts/manual_test_concurrency.py b/kallithea/tests/scripts/manual_test_concurrency.py --- a/kallithea/tests/scripts/manual_test_concurrency.py +++ b/kallithea/tests/scripts/manual_test_concurrency.py @@ -47,7 +47,7 @@ from kallithea.tests.base import HG_REPO rel_path = dirname(dirname(dirname(dirname(os.path.abspath(__file__))))) conf = appconfig('config:development.ini', relative_to=rel_path) -kallithea.config.application.make_app(conf.global_conf, **conf.local_conf) +kallithea.config.application.make_app_raw(conf.global_conf, **conf.local_conf) USER = TEST_USER_ADMIN_LOGIN PASS = TEST_USER_ADMIN_PASS