diff --git a/datalad_dataverse/add_sibling_dataverse.py b/datalad_dataverse/add_sibling_dataverse.py index 7bb96b7..7afb7f2 100644 --- a/datalad_dataverse/add_sibling_dataverse.py +++ b/datalad_dataverse/add_sibling_dataverse.py @@ -91,7 +91,9 @@ class AddSiblingDataverse(ValidatedInterface): existing=EnsureChoice('skip', 'error', 'reconfigure'), mode=EnsureChoice( 'annex', 'filetree', 'annex-only', 'filetree-only', - 'git-only')), + 'git-only'), + trust_level=EnsureChoice('trusted', 'semitrusted', 'untrusted') | EnsureNone() + ), validate_defaults=("dataset",) ) @@ -179,6 +181,12 @@ class AddSiblingDataverse(ValidatedInterface): together, a publication dependency on the storage sibling is configured for the regular sibling in the local dataset clone. """), + trust_level=Parameter( + args=("--trust-level",), + metavar="TRUST-LEVEL", + doc="""specify a trust level for the storage sibling. If not + specified, the default git-annex trust level is used. 'trust' + should be used with care (see the git-annex-trust man page).""", ) ) @staticmethod @@ -196,6 +204,7 @@ def __call__( existing: str = 'error', recursive: bool = False, recursion_limit: Optional[int] = None, + trust_level: Optional[str] = None, ): # dataset is a next' DatasetParameter ds = dataset.ds @@ -246,6 +255,7 @@ def _dummy(ds, refds, **kwargs): name=name, storage_name=storage_name, existing=existing, + trust_level=trust_level, ) for res in ds.foreach_dataset( _dummy, @@ -312,6 +322,7 @@ def _add_sibling_dataverse( name=None, storage_name=None, existing='error', + trust_level=None, ): """ meant to be executed via foreach-dataset @@ -347,6 +358,7 @@ def _add_sibling_dataverse( name=storage_name, export=export_storage, existing=existing, + trust_level=trust_level, known=storage_name in existing_siblings, ) @@ -445,7 +457,8 @@ def _add_git_sibling(ds, url, doi, name, credential_name, export, def _add_storage_sibling( - ds, url, doi, name, export, existing, known=False): + ds, url, doi, name, export, existing, trust_level=None, + known=False): """ Parameters ---------- @@ -458,6 +471,9 @@ def _add_storage_sibling( known: bool Flag whether the sibling is a known remote (no implied necessary existance of content on the remote). + trust_level: {trusted, semitrusted, untrusted, None} + git annex trust level for the storage remote. Uses git-annex + default when set to None. """ if known and existing == 'skip': yield _get_skip_sibling_result(name, ds, 'storage') @@ -479,6 +495,8 @@ def _add_storage_sibling( #"autoenable=true" ] ds.repo.call_annex(cmd_args) + if trust_level is not None: + ds.config.set(f'remote.{name}.annex-trustlevel', trust_level, scope='local') yield get_status_dict( ds=ds, status='ok', diff --git a/datalad_dataverse/tests/test_add_sibling_dataverse.py b/datalad_dataverse/tests/test_add_sibling_dataverse.py index 43050bc..ffe0c70 100644 --- a/datalad_dataverse/tests/test_add_sibling_dataverse.py +++ b/datalad_dataverse/tests/test_add_sibling_dataverse.py @@ -113,3 +113,61 @@ def test_workflow(dataverse_admin_api, cloned_repo.enable_remote('special_remote') cloned_ds.get(str(cloned_ds.pathobj / "subdir" / "newname.md"), **ckwa) + +@pytest.mark.parametrize("trust_level", ["trusted", "semitrusted", "untrusted", None]) +def test_trustlevel(dataverse_admin_api, + dataverse_admin_credential_setup, + dataverse_demoinstance_url, + dataverse_instance_url, + dataverse_dataset, + existing_dataset, + *, trust_level): + ds = existing_dataset + payload = 'content' + payload_fname = 'somefile.txt' + (ds.pathobj / payload_fname).write_text(payload) + ds.save(**ckwa) + + dspid = dataverse_dataset + # smoke test for trust levels + results = ds.add_sibling_dataverse( + dv_url=dataverse_instance_url, + ds_pid=dspid, + name='git_remote', + storage_name='special_remote', + mode='annex', + existing='error', + recursive=False, + recursion_limit=None, + credential="dataverse", + trust_level=trust_level, + **ckwa) + # make sure this worked + assert_result_count(results, 0, status='error') + assert_result_count(results, 1, + status='ok', + action='add_sibling_dataverse') + assert_result_count(results, 1, + status='ok', + action='add_sibling_dataverse.storage') + ds.push(to='git_remote') + # is the config as we expect it? + if trust_level is not None: + assert ds.config.get(f'remote.special_remote.annex-trustlevel', + trust_level) + else: + # if trust_level is None, there shouldn't be a config entry + assert not ds.config.get(f'remote.special_remote.annex-trustlevel', + trust_level) + # now check if drop has the expected safety mechanisms + if trust_level == 'untrusted': + res = ds.drop('somefile.txt', on_failure='ignore', **ckwa) + assert_result_count(res, 1, status='error', action='drop') + # now with reckless + res = ds.drop('somefile.txt', reckless='availability', + on_failure='ignore', **ckwa) + assert_result_count(res, 1, status='ok', action='drop') + else: + # dropping should work fine + res = ds.drop('somefile.txt', on_failure='ignore', **ckwa) + assert_result_count(res, 1, status='ok', action='drop')