##// END OF EJS Templates
wsgi: Refactor to introduce make_app_raw function with the essentials of make_app...
Mads Kiilerich -
r8794:7c270478 stable
parent child Browse files
Show More
@@ -1,86 +1,86
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.application
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(needs_config_file=False, config_file_initialize_app=False, hidden=False):
56 def register_command(needs_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 needs_config_file flags are true, a config file must be specified
59 If one of the needs_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 needs_config_file or config_file_initialize_app:
67 if needs_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 config = paste.deploy.appconfig('config:' + path_to_ini_file)
74 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 needs_config_file:
79 if needs_config_file:
80 annotated(*args, config=config, **kwargs)
80 annotated(*args, config=config, **kwargs)
81 if config_file_initialize_app:
81 if config_file_initialize_app:
82 kallithea.config.application.make_app(config.global_conf, **config.local_conf)
82 kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
83 annotated(*args, **kwargs)
83 annotated(*args, **kwargs)
84 return cli_command(runtime_wrapper)
84 return cli_command(runtime_wrapper)
85 return annotator
85 return annotator
86 return cli_command
86 return cli_command
@@ -1,53 +1,53
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 click
15 import click
16 from celery.bin.celery import celery as celery_command
16 from celery.bin.celery import celery as celery_command
17
17
18 import kallithea
18 import kallithea
19 import kallithea.bin.kallithea_cli_base as cli_base
19 import kallithea.bin.kallithea_cli_base as cli_base
20 from kallithea.lib import celery_app
20 from kallithea.lib import celery_app
21 from kallithea.lib.utils2 import asbool
21 from kallithea.lib.utils2 import asbool
22
22
23
23
24 @cli_base.register_command(needs_config_file=True)
24 @cli_base.register_command(needs_config_file=True)
25 @click.argument('celery_args', nargs=-1)
25 @click.argument('celery_args', nargs=-1)
26 def celery_run(celery_args, config):
26 def celery_run(celery_args, config):
27 """Start Celery worker(s) for asynchronous tasks.
27 """Start Celery worker(s) for asynchronous tasks.
28
28
29 This commands starts the Celery daemon which will spawn workers to handle
29 This commands starts the Celery daemon which will spawn workers to handle
30 certain asynchronous tasks for Kallithea.
30 certain asynchronous tasks for Kallithea.
31
31
32 Any extra arguments you pass to this command will be passed through to
32 Any extra arguments you pass to this command will be passed through to
33 Celery. Use '--' before such extra arguments to avoid options to be parsed
33 Celery. Use '--' before such extra arguments to avoid options to be parsed
34 by this CLI command.
34 by this CLI command.
35 """
35 """
36
36
37 if not asbool(config.get('use_celery')):
37 if not asbool(config.get('use_celery')):
38 raise Exception('Please set use_celery = true in .ini config '
38 raise Exception('Please set use_celery = true in .ini config '
39 'file before running this command')
39 'file before running this command')
40
40
41 kallithea.CELERY_APP.config_from_object(celery_app.make_celery_config(config))
41 kallithea.CELERY_APP.config_from_object(celery_app.make_celery_config(config))
42
42
43 kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app(config.global_conf, **config.local_conf)
43 kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
44
44
45 args = list(celery_args)
45 args = list(celery_args)
46 # 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
46 # 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
47 if not args or args[0] != 'worker':
47 if not args or args[0] != 'worker':
48 args.insert(0, 'worker')
48 args.insert(0, 'worker')
49
49
50 # inline kallithea.CELERY_APP.start in order to allow specifying prog_name
50 # inline kallithea.CELERY_APP.start in order to allow specifying prog_name
51 assert celery_command.params[0].name == 'app'
51 assert celery_command.params[0].name == 'app'
52 celery_command.params[0].default = kallithea.CELERY_APP
52 celery_command.params[0].default = kallithea.CELERY_APP
53 celery_command.main(args=args, prog_name='kallithea-cli celery-run -c CONFIG_FILE --')
53 celery_command.main(args=args, prog_name='kallithea-cli celery-run -c CONFIG_FILE --')
@@ -1,203 +1,203
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.bin.vcs_hooks
15 kallithea.bin.vcs_hooks
16 ~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Entry points for Kallithea hooking into Mercurial and Git.
18 Entry points for Kallithea hooking into Mercurial and Git.
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 logging
28 import logging
29 import os
29 import os
30 import sys
30 import sys
31
31
32 import mercurial.hg
32 import mercurial.hg
33 import mercurial.scmutil
33 import mercurial.scmutil
34 import paste.deploy
34 import paste.deploy
35
35
36 import kallithea
36 import kallithea
37 import kallithea.config.application
37 import kallithea.config.application
38 from kallithea.lib import hooks, webutils
38 from kallithea.lib import hooks, webutils
39 from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str
39 from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str
40 from kallithea.lib.vcs.backends.base import EmptyChangeset
40 from kallithea.lib.vcs.backends.base import EmptyChangeset
41 from kallithea.lib.vcs.utils.helpers import get_scm_size
41 from kallithea.lib.vcs.utils.helpers import get_scm_size
42 from kallithea.model import db
42 from kallithea.model import db
43
43
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 def repo_size(ui, repo, hooktype=None, **kwargs):
48 def repo_size(ui, repo, hooktype=None, **kwargs):
49 """Show size of Mercurial repository.
49 """Show size of Mercurial repository.
50
50
51 Called as Mercurial hook changegroup.kallithea_repo_size after push.
51 Called as Mercurial hook changegroup.kallithea_repo_size after push.
52 """
52 """
53 size_hg, size_root = get_scm_size('.hg', safe_str(repo.root))
53 size_hg, size_root = get_scm_size('.hg', safe_str(repo.root))
54
54
55 last_cs = repo[len(repo) - 1]
55 last_cs = repo[len(repo) - 1]
56
56
57 msg = ('Repository size .hg: %s Checkout: %s Total: %s\n'
57 msg = ('Repository size .hg: %s Checkout: %s Total: %s\n'
58 'Last revision is now r%s:%s\n') % (
58 'Last revision is now r%s:%s\n') % (
59 webutils.format_byte_size(size_hg),
59 webutils.format_byte_size(size_hg),
60 webutils.format_byte_size(size_root),
60 webutils.format_byte_size(size_root),
61 webutils.format_byte_size(size_hg + size_root),
61 webutils.format_byte_size(size_hg + size_root),
62 last_cs.rev(),
62 last_cs.rev(),
63 ascii_str(last_cs.hex())[:12],
63 ascii_str(last_cs.hex())[:12],
64 )
64 )
65 ui.status(safe_bytes(msg))
65 ui.status(safe_bytes(msg))
66
66
67
67
68 def update(ui, repo, hooktype=None, **kwargs):
68 def update(ui, repo, hooktype=None, **kwargs):
69 """Update repo after push. The equivalent to 'hg update' but using the same
69 """Update repo after push. The equivalent to 'hg update' but using the same
70 Mercurial as everything else.
70 Mercurial as everything else.
71
71
72 Called as Mercurial hook changegroup.kallithea_update after push.
72 Called as Mercurial hook changegroup.kallithea_update after push.
73 """
73 """
74 try:
74 try:
75 ui.pushbuffer(error=True, subproc=True)
75 ui.pushbuffer(error=True, subproc=True)
76 rev = brev = None
76 rev = brev = None
77 mercurial.hg.updatetotally(ui, repo, rev, brev)
77 mercurial.hg.updatetotally(ui, repo, rev, brev)
78 finally:
78 finally:
79 s = ui.popbuffer() # usually just "x files updated, x files merged, x files removed, x files unresolved"
79 s = ui.popbuffer() # usually just "x files updated, x files merged, x files removed, x files unresolved"
80 log.info('%s update hook output: %s', safe_str(repo.root), safe_str(s).rstrip())
80 log.info('%s update hook output: %s', safe_str(repo.root), safe_str(s).rstrip())
81
81
82
82
83 def pull_action(ui, repo, **kwargs):
83 def pull_action(ui, repo, **kwargs):
84 """Logs user pull action
84 """Logs user pull action
85
85
86 Called as Mercurial hook outgoing.kallithea_pull_action.
86 Called as Mercurial hook outgoing.kallithea_pull_action.
87 """
87 """
88 hooks.log_pull_action()
88 hooks.log_pull_action()
89
89
90
90
91 def push_action(ui, repo, node, node_last, **kwargs):
91 def push_action(ui, repo, node, node_last, **kwargs):
92 """
92 """
93 Register that changes have been added to the repo - log the action *and* invalidate caches.
93 Register that changes have been added to the repo - log the action *and* invalidate caches.
94 Note: This hook is not only logging, but also the side effect invalidating
94 Note: This hook is not only logging, but also the side effect invalidating
95 caches! The function should perhaps be renamed.
95 caches! The function should perhaps be renamed.
96
96
97 Called as Mercurial hook changegroup.kallithea_push_action .
97 Called as Mercurial hook changegroup.kallithea_push_action .
98
98
99 The pushed changesets is given by the revset 'node:node_last'.
99 The pushed changesets is given by the revset 'node:node_last'.
100 """
100 """
101 revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
101 revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
102 hooks.process_pushed_raw_ids(revs)
102 hooks.process_pushed_raw_ids(revs)
103
103
104
104
105 def _git_hook_environment(repo_path):
105 def _git_hook_environment(repo_path):
106 """
106 """
107 Create a light-weight environment for stand-alone scripts and return an UI and the
107 Create a light-weight environment for stand-alone scripts and return an UI and the
108 db repository.
108 db repository.
109
109
110 Git hooks are executed as subprocess of Git while Kallithea is waiting, and
110 Git hooks are executed as subprocess of Git while Kallithea is waiting, and
111 they thus need enough info to be able to create an app environment and
111 they thus need enough info to be able to create an app environment and
112 connect to the database.
112 connect to the database.
113 """
113 """
114 extras = get_hook_environment()
114 extras = get_hook_environment()
115
115
116 path_to_ini_file = extras['config']
116 path_to_ini_file = extras['config']
117 config = paste.deploy.appconfig('config:' + path_to_ini_file)
117 config = paste.deploy.appconfig('config:' + path_to_ini_file)
118 #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
118 #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
119 kallithea.config.application.make_app(config.global_conf, **config.local_conf)
119 kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
120
120
121 # fix if it's not a bare repo
121 # fix if it's not a bare repo
122 if repo_path.endswith(os.sep + '.git'):
122 if repo_path.endswith(os.sep + '.git'):
123 repo_path = repo_path[:-5]
123 repo_path = repo_path[:-5]
124
124
125 repo = db.Repository.get_by_full_path(repo_path)
125 repo = db.Repository.get_by_full_path(repo_path)
126 if not repo:
126 if not repo:
127 raise OSError('Repository %s not found in database' % repo_path)
127 raise OSError('Repository %s not found in database' % repo_path)
128
128
129 return repo
129 return repo
130
130
131
131
132 def post_receive(repo_path, git_stdin_lines):
132 def post_receive(repo_path, git_stdin_lines):
133 """Called from Git post-receive hook.
133 """Called from Git post-receive hook.
134 The returned value is used as hook exit code and must be 0.
134 The returned value is used as hook exit code and must be 0.
135 """
135 """
136 try:
136 try:
137 repo = _git_hook_environment(repo_path)
137 repo = _git_hook_environment(repo_path)
138 except HookEnvironmentError as e:
138 except HookEnvironmentError as e:
139 sys.stderr.write("Skipping Kallithea Git post-receive hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
139 sys.stderr.write("Skipping Kallithea Git post-receive hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
140 return 0
140 return 0
141
141
142 # the post push hook should never use the cached instance
142 # the post push hook should never use the cached instance
143 scm_repo = repo.scm_instance_no_cache()
143 scm_repo = repo.scm_instance_no_cache()
144
144
145 rev_data = []
145 rev_data = []
146 for l in git_stdin_lines:
146 for l in git_stdin_lines:
147 old_rev, new_rev, ref = l.strip().split(' ')
147 old_rev, new_rev, ref = l.strip().split(' ')
148 _ref_data = ref.split('/')
148 _ref_data = ref.split('/')
149 if _ref_data[1] in ['tags', 'heads']:
149 if _ref_data[1] in ['tags', 'heads']:
150 rev_data.append({'old_rev': old_rev,
150 rev_data.append({'old_rev': old_rev,
151 'new_rev': new_rev,
151 'new_rev': new_rev,
152 'ref': ref,
152 'ref': ref,
153 'type': _ref_data[1],
153 'type': _ref_data[1],
154 'name': '/'.join(_ref_data[2:])})
154 'name': '/'.join(_ref_data[2:])})
155
155
156 git_revs = []
156 git_revs = []
157 for push_ref in rev_data:
157 for push_ref in rev_data:
158 _type = push_ref['type']
158 _type = push_ref['type']
159 if _type == 'heads':
159 if _type == 'heads':
160 if push_ref['old_rev'] == EmptyChangeset().raw_id:
160 if push_ref['old_rev'] == EmptyChangeset().raw_id:
161 # update the symbolic ref if we push new repo
161 # update the symbolic ref if we push new repo
162 if scm_repo.is_empty():
162 if scm_repo.is_empty():
163 scm_repo._repo.refs.set_symbolic_ref(
163 scm_repo._repo.refs.set_symbolic_ref(
164 b'HEAD',
164 b'HEAD',
165 b'refs/heads/%s' % safe_bytes(push_ref['name']))
165 b'refs/heads/%s' % safe_bytes(push_ref['name']))
166
166
167 # build exclude list without the ref
167 # build exclude list without the ref
168 cmd = ['for-each-ref', '--format=%(refname)', 'refs/heads/*']
168 cmd = ['for-each-ref', '--format=%(refname)', 'refs/heads/*']
169 stdout = scm_repo.run_git_command(cmd)
169 stdout = scm_repo.run_git_command(cmd)
170 ref = push_ref['ref']
170 ref = push_ref['ref']
171 heads = [head for head in stdout.splitlines() if head != ref]
171 heads = [head for head in stdout.splitlines() if head != ref]
172 # now list the git revs while excluding from the list
172 # now list the git revs while excluding from the list
173 cmd = ['log', push_ref['new_rev'], '--reverse', '--pretty=format:%H']
173 cmd = ['log', push_ref['new_rev'], '--reverse', '--pretty=format:%H']
174 cmd.append('--not')
174 cmd.append('--not')
175 cmd.extend(heads) # empty list is ok
175 cmd.extend(heads) # empty list is ok
176 stdout = scm_repo.run_git_command(cmd)
176 stdout = scm_repo.run_git_command(cmd)
177 git_revs += stdout.splitlines()
177 git_revs += stdout.splitlines()
178
178
179 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
179 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
180 # delete branch case
180 # delete branch case
181 git_revs += ['delete_branch=>%s' % push_ref['name']]
181 git_revs += ['delete_branch=>%s' % push_ref['name']]
182 else:
182 else:
183 cmd = ['log', '%(old_rev)s..%(new_rev)s' % push_ref,
183 cmd = ['log', '%(old_rev)s..%(new_rev)s' % push_ref,
184 '--reverse', '--pretty=format:%H']
184 '--reverse', '--pretty=format:%H']
185 stdout = scm_repo.run_git_command(cmd)
185 stdout = scm_repo.run_git_command(cmd)
186 git_revs += stdout.splitlines()
186 git_revs += stdout.splitlines()
187
187
188 elif _type == 'tags':
188 elif _type == 'tags':
189 git_revs += ['tag=>%s' % push_ref['name']]
189 git_revs += ['tag=>%s' % push_ref['name']]
190
190
191 hooks.process_pushed_raw_ids(git_revs)
191 hooks.process_pushed_raw_ids(git_revs)
192
192
193 return 0
193 return 0
194
194
195
195
196 # Almost exactly like Mercurial contrib/hg-ssh:
196 # Almost exactly like Mercurial contrib/hg-ssh:
197 def rejectpush(ui, **kwargs):
197 def rejectpush(ui, **kwargs):
198 """Mercurial hook to be installed as pretxnopen and prepushkey for read-only repos.
198 """Mercurial hook to be installed as pretxnopen and prepushkey for read-only repos.
199 Return value 1 will make the hook fail and reject the push.
199 Return value 1 will make the hook fail and reject the push.
200 """
200 """
201 ex = get_hook_environment()
201 ex = get_hook_environment()
202 ui.warn(safe_bytes("Push access to %r denied\n" % ex.repository))
202 ui.warn(safe_bytes("Push access to %r denied\n" % ex.repository))
203 return 1
203 return 1
@@ -1,68 +1,74
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 """WSGI middleware initialization for the Kallithea application."""
14 """WSGI middleware initialization for the Kallithea application."""
15
15
16 from kallithea.config.app_cfg import base_config
16 from kallithea.config.app_cfg import base_config
17 from kallithea.config.middleware.https_fixup import HttpsFixup
17 from kallithea.config.middleware.https_fixup import HttpsFixup
18 from kallithea.config.middleware.permanent_repo_url import PermanentRepoUrl
18 from kallithea.config.middleware.permanent_repo_url import PermanentRepoUrl
19 from kallithea.config.middleware.simplegit import SimpleGit
19 from kallithea.config.middleware.simplegit import SimpleGit
20 from kallithea.config.middleware.simplehg import SimpleHg
20 from kallithea.config.middleware.simplehg import SimpleHg
21 from kallithea.config.middleware.wrapper import RequestWrapper
21 from kallithea.config.middleware.wrapper import RequestWrapper
22 from kallithea.lib.utils2 import asbool
22 from kallithea.lib.utils2 import asbool
23
23
24
24
25 __all__ = ['make_app']
25 __all__ = ['make_app']
26
26
27
27
28 def wrap_app(app):
28 def wrap_app(app):
29 """Wrap the TG WSGI application in Kallithea middleware"""
29 """Wrap the TG WSGI application in Kallithea middleware"""
30 config = app.config
30 config = app.config
31
31
32 # we want our low level middleware to get to the request ASAP. We don't
32 # we want our low level middleware to get to the request ASAP. We don't
33 # need any stack middleware in them - especially no StatusCodeRedirect buffering
33 # need any stack middleware in them - especially no StatusCodeRedirect buffering
34 app = SimpleHg(app, config)
34 app = SimpleHg(app, config)
35 app = SimpleGit(app, config)
35 app = SimpleGit(app, config)
36
36
37 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
37 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
38 if config.get('url_scheme_variable') or asbool(config.get('force_https')) or asbool(config.get('use_htsts')):
38 if config.get('url_scheme_variable') or asbool(config.get('force_https')) or asbool(config.get('use_htsts')):
39 app = HttpsFixup(app, config)
39 app = HttpsFixup(app, config)
40
40
41 app = PermanentRepoUrl(app, config)
41 app = PermanentRepoUrl(app, config)
42
42
43 # Optional and undocumented wrapper - gives more verbose request/response logging, but has a slight overhead
43 # Optional and undocumented wrapper - gives more verbose request/response logging, but has a slight overhead
44 if asbool(config.get('use_wsgi_wrapper')):
44 if asbool(config.get('use_wsgi_wrapper')):
45 app = RequestWrapper(app, config)
45 app = RequestWrapper(app, config)
46
46
47 return app
47 return app
48
48
49
49
50 def make_app(global_conf, **app_conf):
50 def make_app(global_conf, **app_conf):
51 """Return WSGI app with logging Mercurial stdout/stderr - to be used as
52 Paste or mod_wsgi entry point"""
53 return make_app_raw(global_conf, **app_conf)
54
55
56 def make_app_raw(global_conf, **app_conf):
51 """
57 """
52 Set up Kallithea with the settings found in the PasteDeploy configuration
58 Set up Kallithea with the settings found in the PasteDeploy configuration
53 file used.
59 file used.
54
60
55 :param global_conf: The global settings for Kallithea (those
61 :param global_conf: The global settings for Kallithea (those
56 defined under the ``[DEFAULT]`` section).
62 defined under the ``[DEFAULT]`` section).
57 :return: The Kallithea application with all the relevant middleware
63 :return: The Kallithea application with all the relevant middleware
58 loaded.
64 loaded.
59
65
60 This is the PasteDeploy factory for the Kallithea application.
66 This is the PasteDeploy factory for the Kallithea application.
61
67
62 ``app_conf`` contains all the application-specific settings (those defined
68 ``app_conf`` contains all the application-specific settings (those defined
63 under ``[app:main]``.
69 under ``[app:main]``.
64 """
70 """
65 assert app_conf.get('sqlalchemy.url') # must be called with a Kallithea .ini file, which for example must have this config option
71 assert app_conf.get('sqlalchemy.url') # must be called with a Kallithea .ini file, which for example must have this config option
66 assert global_conf.get('here') and global_conf.get('__file__') # app config should be initialized the paste way ...
72 assert global_conf.get('here') and global_conf.get('__file__') # app config should be initialized the paste way ...
67
73
68 return base_config.make_wsgi_app(global_conf, app_conf, wrap_app=wrap_app)
74 return base_config.make_wsgi_app(global_conf, app_conf, wrap_app=wrap_app)
@@ -1,212 +1,212
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.tests.scripts.manual_test_concurrency
15 kallithea.tests.scripts.manual_test_concurrency
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Test suite for making push/pull operations
18 Test suite for making push/pull operations
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: Dec 30, 2010
22 :created_on: Dec 30, 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
28
29 import logging
29 import logging
30 import os
30 import os
31 import shutil
31 import shutil
32 import sys
32 import sys
33 import tempfile
33 import tempfile
34 from os.path import dirname
34 from os.path import dirname
35 from subprocess import PIPE, Popen
35 from subprocess import PIPE, Popen
36
36
37 from paste.deploy import appconfig
37 from paste.deploy import appconfig
38 from sqlalchemy import engine_from_config
38 from sqlalchemy import engine_from_config
39
39
40 import kallithea.config.application
40 import kallithea.config.application
41 from kallithea.lib.utils2 import get_crypt_password
41 from kallithea.lib.utils2 import get_crypt_password
42 from kallithea.model import db, meta
42 from kallithea.model import db, meta
43 from kallithea.model.base import init_model
43 from kallithea.model.base import init_model
44 from kallithea.model.repo import RepoModel
44 from kallithea.model.repo import RepoModel
45 from kallithea.tests.base import HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
45 from kallithea.tests.base import HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
46
46
47
47
48 rel_path = dirname(dirname(dirname(dirname(os.path.abspath(__file__)))))
48 rel_path = dirname(dirname(dirname(dirname(os.path.abspath(__file__)))))
49 conf = appconfig('config:development.ini', relative_to=rel_path)
49 conf = appconfig('config:development.ini', relative_to=rel_path)
50 kallithea.config.application.make_app(conf.global_conf, **conf.local_conf)
50 kallithea.config.application.make_app_raw(conf.global_conf, **conf.local_conf)
51
51
52 USER = TEST_USER_ADMIN_LOGIN
52 USER = TEST_USER_ADMIN_LOGIN
53 PASS = TEST_USER_ADMIN_PASS
53 PASS = TEST_USER_ADMIN_PASS
54 HOST = 'server.local'
54 HOST = 'server.local'
55 METHOD = 'pull'
55 METHOD = 'pull'
56 DEBUG = True
56 DEBUG = True
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 class Command(object):
60 class Command(object):
61
61
62 def __init__(self, cwd):
62 def __init__(self, cwd):
63 self.cwd = cwd
63 self.cwd = cwd
64
64
65 def execute(self, cmd, *args):
65 def execute(self, cmd, *args):
66 """Runs command on the system with given ``args``.
66 """Runs command on the system with given ``args``.
67 """
67 """
68
68
69 command = cmd + ' ' + ' '.join(args)
69 command = cmd + ' ' + ' '.join(args)
70 log.debug('Executing %s', command)
70 log.debug('Executing %s', command)
71 if DEBUG:
71 if DEBUG:
72 print(command)
72 print(command)
73 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
73 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
74 stdout, stderr = p.communicate()
74 stdout, stderr = p.communicate()
75 if DEBUG:
75 if DEBUG:
76 print(stdout, stderr)
76 print(stdout, stderr)
77 return stdout, stderr
77 return stdout, stderr
78
78
79
79
80 def get_session():
80 def get_session():
81 engine = engine_from_config(conf, 'sqlalchemy.')
81 engine = engine_from_config(conf, 'sqlalchemy.')
82 init_model(engine)
82 init_model(engine)
83 sa = meta.Session
83 sa = meta.Session
84 return sa
84 return sa
85
85
86
86
87 def create_test_user(force=True):
87 def create_test_user(force=True):
88 print('creating test user')
88 print('creating test user')
89 sa = get_session()
89 sa = get_session()
90
90
91 user = sa.query(db.User).filter(db.User.username == USER).scalar()
91 user = sa.query(db.User).filter(db.User.username == USER).scalar()
92
92
93 if force and user is not None:
93 if force and user is not None:
94 print('removing current user')
94 print('removing current user')
95 for repo in sa.query(db.Repository).filter(db.Repository.user == user).all():
95 for repo in sa.query(db.Repository).filter(db.Repository.user == user).all():
96 sa.delete(repo)
96 sa.delete(repo)
97 sa.delete(user)
97 sa.delete(user)
98 sa.commit()
98 sa.commit()
99
99
100 if user is None or force:
100 if user is None or force:
101 print('creating new one')
101 print('creating new one')
102 new_usr = db.User()
102 new_usr = db.User()
103 new_usr.username = USER
103 new_usr.username = USER
104 new_usr.password = get_crypt_password(PASS)
104 new_usr.password = get_crypt_password(PASS)
105 new_usr.email = 'mail@example.com'
105 new_usr.email = 'mail@example.com'
106 new_usr.name = 'test'
106 new_usr.name = 'test'
107 new_usr.lastname = 'lasttestname'
107 new_usr.lastname = 'lasttestname'
108 new_usr.active = True
108 new_usr.active = True
109 new_usr.admin = True
109 new_usr.admin = True
110 sa.add(new_usr)
110 sa.add(new_usr)
111 sa.commit()
111 sa.commit()
112
112
113 print('done')
113 print('done')
114
114
115
115
116 def create_test_repo(force=True):
116 def create_test_repo(force=True):
117 print('creating test repo')
117 print('creating test repo')
118 sa = get_session()
118 sa = get_session()
119
119
120 user = sa.query(db.User).filter(db.User.username == USER).scalar()
120 user = sa.query(db.User).filter(db.User.username == USER).scalar()
121 if user is None:
121 if user is None:
122 raise Exception('user not found')
122 raise Exception('user not found')
123
123
124 repo = sa.query(db.Repository).filter(db.Repository.repo_name == HG_REPO).scalar()
124 repo = sa.query(db.Repository).filter(db.Repository.repo_name == HG_REPO).scalar()
125
125
126 if repo is None:
126 if repo is None:
127 print('repo not found creating')
127 print('repo not found creating')
128
128
129 form_data = {'repo_name': HG_REPO,
129 form_data = {'repo_name': HG_REPO,
130 'repo_type': 'hg',
130 'repo_type': 'hg',
131 'private': False,
131 'private': False,
132 'clone_uri': ''}
132 'clone_uri': ''}
133 rm = RepoModel()
133 rm = RepoModel()
134 rm.base_path = '/home/hg'
134 rm.base_path = '/home/hg'
135 rm.create(form_data, user)
135 rm.create(form_data, user)
136
136
137 print('done')
137 print('done')
138
138
139
139
140 def set_anonymous_access(enable=True):
140 def set_anonymous_access(enable=True):
141 sa = get_session()
141 sa = get_session()
142 user = sa.query(db.User).filter(db.User.username == 'default').one()
142 user = sa.query(db.User).filter(db.User.username == 'default').one()
143 user.active = enable
143 user.active = enable
144 sa.add(user)
144 sa.add(user)
145 sa.commit()
145 sa.commit()
146
146
147
147
148 def get_anonymous_access():
148 def get_anonymous_access():
149 sa = get_session()
149 sa = get_session()
150 return sa.query(db.User).filter(db.User.username == 'default').one().active
150 return sa.query(db.User).filter(db.User.username == 'default').one().active
151
151
152
152
153 #==============================================================================
153 #==============================================================================
154 # TESTS
154 # TESTS
155 #==============================================================================
155 #==============================================================================
156 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
156 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
157 backend='hg'):
157 backend='hg'):
158 cwd = path = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, repo)
158 cwd = path = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, repo)
159
159
160 try:
160 try:
161 shutil.rmtree(path, ignore_errors=True)
161 shutil.rmtree(path, ignore_errors=True)
162 os.makedirs(path)
162 os.makedirs(path)
163 #print 'made dirs %s' % os.path.join(path)
163 #print 'made dirs %s' % os.path.join(path)
164 except OSError:
164 except OSError:
165 raise
165 raise
166
166
167 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
167 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
168 {'user': USER,
168 {'user': USER,
169 'pass': PASS,
169 'pass': PASS,
170 'host': HOST,
170 'host': HOST,
171 'cloned_repo': repo, }
171 'cloned_repo': repo, }
172
172
173 dest = tempfile.mktemp(dir=path, prefix='dest-')
173 dest = tempfile.mktemp(dir=path, prefix='dest-')
174 if method == 'pull':
174 if method == 'pull':
175 stdout, stderr = Command(cwd).execute(backend, method, '--cwd', dest, clone_url)
175 stdout, stderr = Command(cwd).execute(backend, method, '--cwd', dest, clone_url)
176 else:
176 else:
177 stdout, stderr = Command(cwd).execute(backend, method, clone_url, dest)
177 stdout, stderr = Command(cwd).execute(backend, method, clone_url, dest)
178 if not no_errors:
178 if not no_errors:
179 if backend == 'hg':
179 if backend == 'hg':
180 assert """adding file changes""" in stdout, 'no messages about cloning'
180 assert """adding file changes""" in stdout, 'no messages about cloning'
181 assert """abort""" not in stderr, 'got error from clone'
181 assert """abort""" not in stderr, 'got error from clone'
182 elif backend == 'git':
182 elif backend == 'git':
183 assert """Cloning into""" in stdout, 'no messages about cloning'
183 assert """Cloning into""" in stdout, 'no messages about cloning'
184
184
185
185
186 if __name__ == '__main__':
186 if __name__ == '__main__':
187 try:
187 try:
188 create_test_user(force=False)
188 create_test_user(force=False)
189 import time
189 import time
190
190
191 try:
191 try:
192 METHOD = sys.argv[3]
192 METHOD = sys.argv[3]
193 except IndexError:
193 except IndexError:
194 pass
194 pass
195
195
196 try:
196 try:
197 backend = sys.argv[4]
197 backend = sys.argv[4]
198 except IndexError:
198 except IndexError:
199 backend = 'hg'
199 backend = 'hg'
200
200
201 if METHOD == 'pull':
201 if METHOD == 'pull':
202 seq = next(tempfile._RandomNameSequence()) # pytype: disable=module-attr
202 seq = next(tempfile._RandomNameSequence()) # pytype: disable=module-attr
203 test_clone_with_credentials(repo=sys.argv[1], method='clone',
203 test_clone_with_credentials(repo=sys.argv[1], method='clone',
204 backend=backend)
204 backend=backend)
205 s = time.time()
205 s = time.time()
206 for i in range(1, int(sys.argv[2]) + 1):
206 for i in range(1, int(sys.argv[2]) + 1):
207 print('take', i)
207 print('take', i)
208 test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
208 test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
209 backend=backend)
209 backend=backend)
210 print('time taken %.3f' % (time.time() - s))
210 print('time taken %.3f' % (time.time() - s))
211 except Exception as e:
211 except Exception as e:
212 sys.exit('stop on %s' % e)
212 sys.exit('stop on %s' % e)
General Comments 0
You need to be logged in to leave comments. Login now