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

MercurialRepositoryService: implement SetManagedConfig

The implemenation for `inherit` field is actually
the first time we actually make use of the
`gl_project_full_path` field of the `Repository` message,
perhaps even with Gitaly, as the comments in protocol definition
hint (`SetProjectFullPath` counts as debugging).

The appending logic for the main HGRC found its limit in tests,
since the consequence is that the group inclusion occurs *after*
the managed HGRC file inclusion, we should perhaps absorb that with
implementation of `GetManagedConfig`.
parent 4e6a6ec0
No related branches found
No related tags found
1 merge request!133Mercurial config management methods
Pipeline #62672 passed
......@@ -22,6 +22,7 @@
GITLAB_PROJECT_FULL_PATH_FILENAME = b'gitlab.project_full_path'
MAIN_HGRC_FILE = b'hgrc'
MANAGED_HGRC_FILE = b'hgrc.managed'
INCLUDE_MANAGED_HGRC_LINE = b'%include ' + MANAGED_HGRC_FILE
HGRC_INCL_INHERIT_RX = re.compile(br'^%include (.*)/hgrc$', re.MULTILINE)
HEPTAPOD_CONFIG_SECTION = b'heptapod'
# TODO would be nice to import these from hgext3rd.heptapod
......@@ -33,6 +34,7 @@
b'nothing': AutoPublish.NOTHING,
b'all': AutoPublish.ALL,
}
AUTO_PUBLISH_REVERSE_MAPPING = {v: k for k, v in AUTO_PUBLISH_MAPPING.items()}
def set_gitlab_project_full_path(repo, full_path: bytes):
......@@ -133,3 +135,107 @@
ui = uimod.ui()
ui.readconfig(repo.vfs.join(MANAGED_HGRC_FILE), trust=True)
return heptapod_ui_config(ui, as_recorded=True)
def replace_heptapod_managed_config(repo, items, by_line):
"""Entirely replace the managed HGRC file with the given items.
:param items: a dict, whose keys and values are as fields of the
:class:`HeptapodConfigSection` message
"""
with repo.wlock():
with repo.vfs(MANAGED_HGRC_FILE,
mode=b'wb',
atomictemp=True,
checkambig=True) as fobj:
fobj.write(b"# This file is entirely managed by Heptapod \n")
fobj.write(b"# latest update ")
fobj.write(by_line.encode('utf-8'))
fobj.write(b"\n\n")
if not items:
return
fobj.write(b"[heptapod]\n")
auto_pub = items.get('auto_publish')
if auto_pub is not None:
fobj.write(b"auto-publish = ")
fobj.write(AUTO_PUBLISH_REVERSE_MAPPING[auto_pub])
fobj.write(b"\n")
for field in ('allow_bookmarks', 'allow_multiple_heads'):
val = items.get(field)
if val is None:
continue
hg_key = field.replace('_', '-')
fobj.write(f'{hg_key} = {val}\n'.encode('ascii'))
def ensure_managed_config_inclusion(repo):
with repo.wlock():
for line in repo.vfs.tryread(MAIN_HGRC_FILE).splitlines():
if line.strip() == INCLUDE_MANAGED_HGRC_LINE:
return
with repo.vfs(MAIN_HGRC_FILE,
mode=b'ab',
atomictemp=True,
checkambig=True) as fobj:
fobj.write(INCLUDE_MANAGED_HGRC_LINE)
fobj.write(b"\n")
def set_managed_config(repo, heptapod: HeptapodConfigSection,
remove_items, by_line):
existing = heptapod_local_config(repo)
items = {}
for field in ('allow_bookmarks', 'allow_multiple_heads', 'auto_publish'):
if field in remove_items:
continue
if heptapod.HasField(field):
items[field] = getattr(heptapod, field)
elif existing.HasField(field):
items[field] = getattr(existing, field)
replace_heptapod_managed_config(repo, items, by_line)
ensure_managed_config_inclusion(repo)
def set_config_inheritance(repo, hgrc_dir, by_line):
"""Set inheritance from an HGRC file in given hgrc_dir.
:param hgrc_dir: if None, the inheritance is removed if present.
Otherwise has to be a relative path from the repo's wdir and
if inheritance is absent, it is set to the file named ``hgrc``
in the given ``hgrc_dir``.
Tis currrenl
"""
by_line = by_line.encode('utf-8')
changed = False
with repo.wlock():
lines = repo.vfs.tryread(MAIN_HGRC_FILE).splitlines()
for i, line in enumerate(lines):
if HGRC_INCL_INHERIT_RX.search(line) is not None:
if hgrc_dir is None:
lines[i] = b'# inheritance removed ' + by_line
changed = True
break
else:
if hgrc_dir is not None:
lines.insert(0,
b'%%include %s/hgrc' % hgrc_dir.encode('utf-8'))
lines.insert(0, b'# inheritance restored ' + by_line)
changed = True
if not changed:
return
with repo.vfs(MAIN_HGRC_FILE,
mode=b'wb',
atomictemp=True,
checkambig=True) as fobj:
for line in lines:
fobj.write(line)
fobj.write(b'\n')
......@@ -27,6 +27,8 @@
config_inherits,
heptapod_config,
heptapod_local_config,
set_config_inheritance,
set_managed_config,
)
from ..errors import (
not_implemented,
......@@ -39,6 +41,8 @@
GetManagedConfigResponse,
PushRequest,
PushResponse,
SetManagedConfigRequest,
SetManagedConfigResponse,
)
from ..stub.mercurial_repository_pb2_grpc import (
MercurialRepositoryServiceServicer,
......@@ -80,6 +84,28 @@
return GetManagedConfigResponse(inherit=config_inherits(repo),
heptapod=heptapod_section)
def SetManagedConfig(self,
request: SetManagedConfigRequest,
context) -> SetManagedConfigResponse:
repo = self.load_repo(request.repository, context)
set_managed_config(repo,
heptapod=request.heptapod,
remove_items=request.remove_items,
by_line=request.by_line)
if request.HasField('inherit'):
if not request.inherit:
hgrc_dir = None # remove
else:
project_path = request.repository.gl_project_path
group_path = project_path.rsplit('/', 1)[0]
hgrc_dir = os.path.relpath(
group_path,
request.repository.relative_path + '/.hg'
)
set_config_inheritance(repo, hgrc_dir, request.by_line)
return SetManagedConfigResponse()
def Push(self, request: PushRequest, context) -> PushResponse:
repo = self.load_repo(request.repository, context)
repo.ui.setconfig(b'hooks', b'pretxnclose.heptapod_sync', b'')
......
......@@ -24,6 +24,7 @@
ConfigItemType,
GetConfigItemRequest,
GetManagedConfigRequest,
SetManagedConfigRequest,
HeptapodConfigSection,
MercurialPeer,
PushRequest,
......@@ -56,6 +57,10 @@
return self.stub.GetManagedConfig(
GetManagedConfigRequest(repository=self.grpc_repo, **kw))
def set_managed_config(self, **kw):
return self.stub.SetManagedConfig(
SetManagedConfigRequest(repository=self.grpc_repo, **kw))
def hgrc_path(self, main=False):
hgrc_name = 'hgrc' if main else 'hgrc.managed'
return self.repo_wrapper.path / '.hg' / hgrc_name
......@@ -67,6 +72,15 @@
with open(self.hgrc_path(main=True), 'a') as hgrcf:
hgrcf.write('\n'.join(lines))
def write_main_hgrc(self, *lines, include_managed=True):
with open(self.hgrc_path(main=True), 'w') as hgrcf:
hgrcf.write('\n'.join(lines))
if include_managed:
self.include_managed_hgrc()
def read_main_hgrc_lines(self):
return self.hgrc_path(main=True).read_text().splitlines()
def include_managed_hgrc(self):
self.append_main_hgrc('', '%include hgrc.managed', '')
......@@ -76,6 +90,7 @@
with ConfigFixture(grpc_channel, server_repos_root) as fixture:
# this is normally done upon repository creation:
fixture.include_managed_hgrc()
setattr(fixture.grpc_repo, 'gl_project_path', 'mygroup/myproject')
yield fixture
......@@ -140,6 +155,96 @@
assert get_config(local=False).heptapod.auto_publish == AutoPublish.ALL
def test_set_managed_config(config_fixture):
get_config = config_fixture.get_managed_config
set_config = config_fixture.set_managed_config
config_fixture.write_main_hgrc('[heptapod]',
'allow-multiple-heads = yes',
'')
set_config(
heptapod=HeptapodConfigSection(allow_bookmarks=True,
auto_publish=AutoPublish.NOTHING,
),
by_line="by user foo"
)
section = get_config(local=True).heptapod
assert not section.HasField('allow_multiple_heads')
assert section.HasField('allow_bookmarks')
assert section.allow_bookmarks
assert section.HasField('auto_publish')
assert section.auto_publish == AutoPublish.NOTHING
assert get_config().heptapod.allow_multiple_heads is True # control
# to check whether the managed file actually overrides a setting
# and even if inclusion is missing
config_fixture.write_main_hgrc('[heptapod]', 'allow-bookmarks = yes', '',
include_managed=False)
set_config(heptapod=HeptapodConfigSection(allow_bookmarks=False))
section = get_config(local=True).heptapod
assert not section.HasField('allow_multiple_heads')
assert section.HasField('allow_bookmarks')
assert not section.allow_bookmarks
# field not mentioned in call is not affected
assert section.HasField('auto_publish')
assert section.auto_publish == AutoPublish.NOTHING
# proof of override
assert not get_config().heptapod.allow_bookmarks
set_config(heptapod=HeptapodConfigSection(),
remove_items=['allow_bookmarks'])
section = get_config(local=True).heptapod
assert not section.HasField('allow_multiple_heads')
assert not section.HasField('allow_bookmarks')
assert not section.allow_multiple_heads
assert not section.allow_bookmarks
# proof that override has been removed
assert get_config().heptapod.allow_bookmarks
# removing everything and testing by_line
set_config(remove_items=('allow_bookmarks', 'auto_publish'),
by_line='by erasor')
managed_lines = config_fixture.hgrc_path().read_text().splitlines()
assert not managed_lines[0].startswith('[')
assert managed_lines[1:] == ['# latest update by erasor', '']
def test_set_managed_config_inherit(config_fixture):
get_config = config_fixture.get_managed_config
set_config = config_fixture.set_managed_config
config_fixture.write_main_hgrc("# An unrelated line",
"%include some/path/hgrc",
"# Another unrelated line",
'')
assert get_config().inherit is True
set_config(inherit=False, by_line="by user foo1")
assert get_config().inherit is False
hgrc_lines = config_fixture.read_main_hgrc_lines()
assert hgrc_lines[:3] == ["# An unrelated line",
"# inheritance removed by user foo1",
"# Another unrelated line",
]
set_config(inherit=True, by_line="by user foo2")
assert get_config().inherit is True
hgrc_lines = config_fixture.read_main_hgrc_lines()
assert hgrc_lines[:5] == ["# inheritance restored by user foo2",
"%include ../../mygroup/hgrc",
"# An unrelated line",
"# inheritance removed by user foo1",
"# Another unrelated line",
]
# in case of no-op, nothing is changed (even by-line)
set_config(inherit=True, by_line="by user foo3")
assert get_config().inherit is True
hgrc_lines = config_fixture.read_main_hgrc_lines()
assert hgrc_lines[:2] == ["# inheritance restored by user foo2",
"%include ../../mygroup/hgrc",
]
@pytest.fixture
def push_fixture(grpc_channel, server_repos_root):
hg_repo_stub = MercurialRepositoryServiceStub(grpc_channel)
......
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