# HG changeset patch # User Georges Racinet <georges.racinet@octobus.net> # Date 1670951254 -3600 # Tue Dec 13 18:07:34 2022 +0100 # Branch stable # Node ID 311ed039b41193d393c0f5440f31caa6cd3b7ad8 # Parent 137433550a869cf67ef25036f4c87786ff8c1efb RepositoryService: implement FullPath Closes hgitaly#114 diff --git a/hgitaly/repository.py b/hgitaly/repository.py --- a/hgitaly/repository.py +++ b/hgitaly/repository.py @@ -28,6 +28,19 @@ repo.vfs.write(GITLAB_PROJECT_FULL_PATH_FILENAME, full_path) +def get_gitlab_project_full_path(repo): + """Get the full path of GitLab Project. + + See also :func:`set_gitlab_project_full_path`. + + :return: the full path, or ``None`` if not set. + """ + try: + return repo.vfs.read(GITLAB_PROJECT_FULL_PATH_FILENAME) + except FileNotFoundError: + return None + + def unbundle(repo, bundle_path: str, rails_sync=False): """Call unbundle with proper options and conversions. diff --git a/hgitaly/service/repository.py b/hgitaly/service/repository.py --- a/hgitaly/service/repository.py +++ b/hgitaly/service/repository.py @@ -51,6 +51,7 @@ validate_relative_path, ) from ..repository import ( + get_gitlab_project_full_path, set_gitlab_project_full_path, unbundle, ) @@ -80,6 +81,8 @@ FetchBundleResponse, FindMergeBaseRequest, FindMergeBaseResponse, + FullPathRequest, + FullPathResponse, GetRawChangesRequest, GetRawChangesResponse, RepositoryExistsRequest, @@ -416,6 +419,32 @@ set_gitlab_project_full_path(repo, request.path.encode('utf-8')) return SetFullPathResponse() + def FullPath(self, request: FullPathRequest, + context) -> FullPathResponse: + try: + repo = self.load_repo(request.repository, context) + except KeyError as exc: + kind, what = exc.args + if kind == 'storage': + context.abort(StatusCode.INVALID_ARGUMENT, + 'fetch config: 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) + context.abort(StatusCode.NOT_FOUND, + 'fetch config: GetRepoPath: not a Mercurial ' + 'repository: "%s"' % os.fsdecode(what)) + + path = get_gitlab_project_full_path(repo) + if not path: # None or (not probable) empty string + # Gitaly simply returns the `git config` stderr output for now. + # Pretty ridiculous to mimick it, we can hope not much to + # depend on it. + context.abort(StatusCode.INTERNAL, "Full path not set") + return FullPathResponse(path=path) + def CreateBundle(self, request: CreateBundleRequest, context) -> CreateBundleResponse: repo = self.load_repo(request.repository, context).unfiltered() diff --git a/hgitaly/service/tests/test_repository_service.py b/hgitaly/service/tests/test_repository_service.py --- a/hgitaly/service/tests/test_repository_service.py +++ b/hgitaly/service/tests/test_repository_service.py @@ -43,6 +43,7 @@ CreateBundleFromRefListRequest, FetchBundleRequest, FindMergeBaseRequest, + FullPathRequest, GetArchiveRequest, HasLocalBranchesRequest, RemoveRepositoryRequest, @@ -145,6 +146,12 @@ return self.stub.SetFullPath(SetFullPathRequest(repository=grpc_repo, path=path)) + def full_path(self, grpc_repo=None): + if grpc_repo is None: + grpc_repo = self.grpc_repo + + return self.stub.FullPath(FullPathRequest(repository=grpc_repo)).path + def create_bundle(self, save_path): with open(save_path, 'wb') as fobj: for chunk in self.stub.CreateBundle(CreateBundleRequest( @@ -558,6 +565,7 @@ assert (wrapper.path / '.hg' / GITLAB_PROJECT_FULL_PATH_FILENAME.decode('ascii') ).read_text(encoding='utf-8') == expected + assert fixture.full_path() == expected def call_then_assert(full_path): fixture.set_full_path(full_path) @@ -583,6 +591,28 @@ storage_name='unknown')) +def test_full_path_errors(fixture_with_repo): + fixture = fixture_with_repo + grpc_repo = fixture.grpc_repo + + def call_then_assert_error(error_code, grpc_repo=None): + with pytest.raises(grpc.RpcError) as exc_info: + fixture.full_path(grpc_repo=grpc_repo) + assert exc_info.value.code() == error_code + + # existing repo, but full path not set + call_then_assert_error(grpc.StatusCode.INTERNAL) + + call_then_assert_error(grpc.StatusCode.NOT_FOUND, + grpc_repo=Repository( + storage_name=grpc_repo.storage_name, + relative_path='no/such/repo')) + call_then_assert_error(grpc.StatusCode.INVALID_ARGUMENT, + grpc_repo=Repository( + relative_path=grpc_repo.relative_path, + storage_name='unknown')) + + def test_fetch_bundle(fixture_with_repo, tmpdir): fixture = fixture_with_repo wrapper = fixture.repo_wrapper diff --git a/tests_with_gitaly/comparison.py b/tests_with_gitaly/comparison.py --- a/tests_with_gitaly/comparison.py +++ b/tests_with_gitaly/comparison.py @@ -243,7 +243,7 @@ norm = self.error_details_normalizer hg_details = exc_hg.details() git_details = exc_git.details() - if norm is not None: # pragma no cover (temporary) + if norm is not None: hg_details = norm(hg_details, vcs='hg') git_details = norm(git_details, vcs='git') assert hg_details == git_details diff --git a/tests_with_gitaly/test_repository_service.py b/tests_with_gitaly/test_repository_service.py --- a/tests_with_gitaly/test_repository_service.py +++ b/tests_with_gitaly/test_repository_service.py @@ -20,6 +20,7 @@ CreateBundleFromRefListRequest, CreateRepositoryFromBundleRequest, FindMergeBaseRequest, + FullPathRequest, RemoveRepositoryRequest, SetFullPathRequest, ) @@ -187,6 +188,33 @@ relative_path='no/such/path')) +def test_full_path(gitaly_comparison, server_repos_root): + fixture = gitaly_comparison + grpc_repo = fixture.gitaly_repo + + def normalizer(msg, **kw): + return msg.replace('Mercurial', 'git') + + rpc_helper = fixture.rpc_helper( + stub_cls=RepositoryServiceStub, + method_name='FullPath', + request_cls=FullPathRequest, + error_details_normalizer=normalizer, + ) + assert_compare_errors = rpc_helper.assert_compare_errors + + # path not set on existing repo + assert_compare_errors(same_details=False) + + # unknown storage and missing repo + assert_compare_errors(same_details=False, + repository=Repository(storage_name='unknown', + relative_path='/some/path')) + assert_compare_errors( + repository=Repository(storage_name=grpc_repo.storage_name, + relative_path='no/such/path')) + + def test_set_full_path(gitaly_comparison, server_repos_root): fixture = gitaly_comparison