simplegit.py
156 lines
| 5.2 KiB
| text/x-python
|
PythonLexer
r1 | ||||
r5088 | # Copyright (C) 2010-2023 RhodeCode GmbH | |||
r1 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
""" | ||||
SimpleGit middleware for handling git protocol request (push/clone etc.) | ||||
It's implemented with basic auth function | ||||
""" | ||||
r1565 | import os | |||
r1 | import re | |||
r753 | import logging | |||
r4919 | import urllib.parse | |||
r1 | ||||
import rhodecode | ||||
r1571 | from rhodecode.lib import utils | |||
r1 | from rhodecode.lib import utils2 | |||
from rhodecode.lib.middleware import simplevcs | ||||
r5032 | from rhodecode.lib.middleware.utils import get_path_info | |||
r1 | ||||
r753 | log = logging.getLogger(__name__) | |||
r1 | ||||
GIT_PROTO_PAT = re.compile( | ||||
r1565 | r'^/(.+)/(info/refs|info/lfs/(.+)|git-upload-pack|git-receive-pack)') | |||
GIT_LFS_PROTO_PAT = re.compile(r'^/(.+)/(info/lfs/(.+))') | ||||
def default_lfs_store(): | ||||
""" | ||||
Default lfs store location, it's consistent with Mercurials large file | ||||
store which is in .cache/largefiles | ||||
""" | ||||
from rhodecode.lib.vcs.backends.git import lfs_store | ||||
user_home = os.path.expanduser("~") | ||||
return lfs_store(user_home) | ||||
r1 | ||||
class SimpleGit(simplevcs.SimpleVCS): | ||||
SCM = 'git' | ||||
def _get_repository_name(self, environ): | ||||
""" | ||||
Gets repository name out of PATH_INFO header | ||||
:param environ: environ where PATH_INFO is stored | ||||
""" | ||||
r5032 | path_info = get_path_info(environ) | |||
repo_name = GIT_PROTO_PAT.match(path_info).group(1) | ||||
r1565 | # for GIT LFS, and bare format strip .git suffix from names | |||
if repo_name.endswith('.git'): | ||||
repo_name = repo_name[:-4] | ||||
r1 | return repo_name | |||
r1565 | def _get_lfs_action(self, path, request_method): | |||
""" | ||||
return an action based on LFS requests type. | ||||
Those routes are handled inside vcsserver app. | ||||
batch -> POST to /info/lfs/objects/batch => PUSH/PULL | ||||
batch is based on the `operation. | ||||
that could be download or upload, but those are only | ||||
instructions to fetch so we return pull always | ||||
download -> GET to /info/lfs/{oid} => PULL | ||||
upload -> PUT to /info/lfs/{oid} => PUSH | ||||
verification -> POST to /info/lfs/verify => PULL | ||||
""" | ||||
match_obj = GIT_LFS_PROTO_PAT.match(path) | ||||
_parts = match_obj.groups() | ||||
repo_name, path, operation = _parts | ||||
log.debug( | ||||
'LFS: detecting operation based on following ' | ||||
'data: %s, req_method:%s', _parts, request_method) | ||||
if operation == 'verify': | ||||
return 'pull' | ||||
elif operation == 'objects/batch': | ||||
# batch sends back instructions for API to dl/upl we report it | ||||
# as pull | ||||
if request_method == 'POST': | ||||
return 'pull' | ||||
elif operation: | ||||
# probably a OID, upload is PUT, download a GET | ||||
if request_method == 'GET': | ||||
return 'pull' | ||||
else: | ||||
return 'push' | ||||
# if default not found require push, as action | ||||
return 'push' | ||||
r1 | _ACTION_MAPPING = { | |||
'git-receive-pack': 'push', | ||||
'git-upload-pack': 'pull', | ||||
} | ||||
def _get_action(self, environ): | ||||
""" | ||||
Maps git request commands into a pull or push command. | ||||
In case of unknown/unexpected data, it returns 'pull' to be safe. | ||||
:param environ: | ||||
""" | ||||
r5032 | path = get_path_info(environ) | |||
r1 | ||||
if path.endswith('/info/refs'): | ||||
r4973 | query = urllib.parse.parse_qs(environ['QUERY_STRING']) | |||
r1 | service_cmd = query.get('service', [''])[0] | |||
return self._ACTION_MAPPING.get(service_cmd, 'pull') | ||||
r1565 | ||||
r5032 | elif GIT_LFS_PROTO_PAT.match(path): | |||
return self._get_lfs_action(path, environ['REQUEST_METHOD']) | ||||
r1565 | ||||
r1 | elif path.endswith('/git-receive-pack'): | |||
return 'push' | ||||
elif path.endswith('/git-upload-pack'): | ||||
return 'pull' | ||||
return 'pull' | ||||
def _create_wsgi_app(self, repo_path, repo_name, config): | ||||
Martin Bornhold
|
r848 | return self.scm_app.create_git_wsgi_app( | ||
Martin Bornhold
|
r951 | repo_path, repo_name, config) | ||
r1 | ||||
r3781 | def _create_config(self, extras, repo_name, scheme='http'): | |||
r1 | extras['git_update_server_info'] = utils2.str2bool( | |||
rhodecode.CONFIG.get('git_update_server_info')) | ||||
r1565 | ||||
r1571 | config = utils.make_db_config(repo=repo_name) | |||
custom_store = config.get('vcs_git_lfs', 'store_location') | ||||
r1565 | ||||
r1571 | extras['git_lfs_enabled'] = utils2.str2bool( | |||
config.get('vcs_git_lfs', 'enabled')) | ||||
extras['git_lfs_store_path'] = custom_store or default_lfs_store() | ||||
r3781 | extras['git_lfs_http_scheme'] = scheme | |||
r1 | return extras | |||