Skip to content
Snippets Groups Projects
Commit 3ec209c1 authored by Georges Racinet's avatar Georges Racinet
Browse files

RepositoryService: implement SetFullPath

Perhaps at some point Gitaly will decide that the proper error
code should be `NotFound` or to stop providing the full path in
the details, but we are meanhile doing the same.

The only difference in error treatment is the replacement of
'git repository` by 'Mercurial repository' but as usual I'm betting
on it not to matter.
parent 175010d2
No related branches found
No related tags found
1 merge request!79RepositoryService: implement SetFullPath
Pipeline #28239 passed
# Copyright 2021 Georges Racinet <georges.racinet@octobus.net>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
#
# SPDX-License-Identifier: GPL-2.0-or-later
GITLAB_PROJECT_FULL_PATH_FILENAME = b'gitlab.project_full_path'
def set_gitlab_project_full_path(repo, full_path: bytes):
"""Store information about the full path of GitLab Project.
In GitLab terminology, ``full_path`` is the URI path, while ``path``
its the last segment of ``full_path``.
In Git repositories, this is stored in config. We could as well use
the repo-local hgrcs, but it is simpler to use a dedicated file, and
it makes sense to consider it not part of ``store`` (``svfs``), same
as ``hgrc``.
"""
with repo.wlock():
repo.vfs.write(GITLAB_PROJECT_FULL_PATH_FILENAME, full_path)
......@@ -6,6 +6,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from grpc import StatusCode
import logging
import os
import tempfile
from mercurial import (
......@@ -35,6 +36,10 @@
parse_special_ref,
ensure_special_refs,
)
from ..repository import (
set_gitlab_project_full_path,
)
from ..revision import (
CHANGESET_HASH_BYTES_REGEXP,
gitlab_revision_changeset,
......@@ -56,6 +61,8 @@
SearchFilesByContentResponse,
SearchFilesByNameRequest,
SearchFilesByNameResponse,
SetFullPathRequest,
SetFullPathResponse,
WriteRefRequest,
WriteRefResponse,
ApplyGitattributesRequest,
......@@ -300,3 +307,32 @@
context) -> SearchFilesByContentResponse:
return not_implemented(context, SearchFilesByContentResponse,
issue=80) # pragma no cover
def SetFullPath(self, request: SetFullPathRequest,
context) -> SetFullPathResponse:
try:
repo = self.load_repo(request.repository, context)
except KeyError as exc:
kind, what = exc.args
if kind == 'storage':
message = ('writing config: rpc error: '
'code = InvalidArgument desc = GetStorageByName: '
'no such storage: "%s"' % what)
else:
# (H)Gitaly relative paths are always ASCII, but the
# root might not be (Gitaly does disclose the full expected
# path in the error message)
message = ('writing config: rpc error: code = NotFound '
'desc = GetRepoPath: not a Mercurial '
'repository: "%s"' % os.fsdecode(what))
# perhaps subject to change in future Gitaly reference impl
return internal_error(context, SetFullPathResponse,
message=message)
if not request.path:
return invalid_argument(context, SetFullPathResponse,
message='no path provided')
set_gitlab_project_full_path(repo, request.path.encode('utf-8'))
return SetFullPathResponse()
......@@ -19,6 +19,9 @@
iter_keep_arounds,
)
from hgitaly.repository import (
GITLAB_PROJECT_FULL_PATH_FILENAME,
)
from hgitaly.tests.common import make_empty_repo
from hgitaly.stub.shared_pb2 import Repository
......@@ -28,6 +31,7 @@
GetArchiveRequest,
HasLocalBranchesRequest,
RepositoryExistsRequest,
SetFullPathRequest,
WriteRefRequest,
)
from hgitaly.stub.repository_service_pb2_grpc import RepositoryServiceStub
......@@ -332,3 +336,40 @@
do_rpc(brepo_name)
assert exc_info.value.code() == grpc.StatusCode.INTERNAL
assert 'Too many levels of symbolic links' in exc_info.value.details()
def test_set_full_path(grpc_channel, server_repos_root):
repo_stub = RepositoryServiceStub(grpc_channel)
wrapper, grpc_repo = make_empty_repo(server_repos_root)
def do_rpc(full_path, grpc_repo=grpc_repo):
return repo_stub.SetFullPath(SetFullPathRequest(repository=grpc_repo,
path=full_path))
def assert_full_path(expected):
assert (wrapper.path / '.hg'
/ GITLAB_PROJECT_FULL_PATH_FILENAME.decode('ascii')
).read_text(encoding='utf-8') == expected
def call_then_assert(full_path):
do_rpc(full_path)
assert_full_path(full_path)
def call_then_assert_error(full_path, error_code, grpc_repo=grpc_repo):
with pytest.raises(grpc.RpcError) as exc_info:
do_rpc(full_path, grpc_repo=grpc_repo)
assert exc_info.value.code() == error_code
call_then_assert('group/proj')
call_then_assert('accent-lovers/projé')
call_then_assert('group/subgroup/proj')
call_then_assert_error('', grpc.StatusCode.INVALID_ARGUMENT)
call_then_assert_error('some/path', grpc.StatusCode.INTERNAL,
grpc_repo=Repository(
storage_name=grpc_repo.storage_name,
relative_path='no/such/repo'))
call_then_assert_error('some/path', grpc.StatusCode.INTERNAL,
grpc_repo=Repository(
relative_path=grpc_repo.relative_path,
storage_name='unknown'))
......@@ -16,6 +16,7 @@
from hgitaly.stub.repository_service_pb2 import (
CreateRepositoryRequest,
FindMergeBaseRequest,
SetFullPathRequest,
)
from hgitaly.stub.repository_service_pb2_grpc import RepositoryServiceStub
......@@ -138,3 +139,53 @@
assert exc_info_hg.value.code() == exc_info_git.value.code()
assert 'Too many levels of symbolic links' in exc_info_hg.value.details()
assert 'file exists' in exc_info_git.value.details()
def test_set_full_path(gitaly_comparison, server_repos_root):
fixture = gitaly_comparison
repo_stubs = dict(
hg=RepositoryServiceStub(fixture.hgitaly_channel),
git=RepositoryServiceStub(fixture.gitaly_channel)
)
def do_rpc(vcs, path, grpc_repo=fixture.gitaly_repo):
return repo_stubs[vcs].SetFullPath(
SetFullPathRequest(repository=grpc_repo, path=path))
def assert_compare(path):
assert do_rpc('hg', path) == do_rpc('git', path)
def assert_error_compare(path,
grpc_repo=None,
msg_normalizer=lambda m: m):
kwargs = dict(grpc_repo=grpc_repo) if grpc_repo is not None else {}
with pytest.raises(Exception) as hg_exc_info:
do_rpc('hg', path, **kwargs)
with pytest.raises(Exception) as git_exc_info:
do_rpc('git', path, **kwargs)
hg_exc, git_exc = hg_exc_info.value, git_exc_info.value
assert hg_exc.code() == git_exc.code()
hg_msg, git_msg = hg_exc.details(), git_exc.details()
assert msg_normalizer(hg_msg) == msg_normalizer(git_msg)
# success case
assert_compare('group/project')
# error cases
assert_error_compare('')
def normalize_repo_not_found(msg):
return msg.replace('git repo', 'Mercurial repo')
assert_error_compare('some/full/path',
grpc_repo=Repository(
storage_name=fixture.gitaly_repo.storage_name,
relative_path='no/such/repo'),
msg_normalizer=normalize_repo_not_found,
)
assert_error_compare('some/full/path',
grpc_repo=Repository(
relative_path=fixture.gitaly_repo.relative_path,
storage_name='unknown'))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment