Skip to content

Commit d54e877

Browse files
730 local project configuration (#777)
* feat: allow not committing if empty * feat: local project configuration * feat: commit when there is a change * refactor: move cli config command out of core * refactor: remove renku --config option
1 parent 5b541a0 commit d54e877

File tree

11 files changed

+417
-174
lines changed

11 files changed

+417
-174
lines changed

conftest.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,19 @@ def instance_path(renku_path, monkeypatch):
4848

4949

5050
@pytest.fixture()
51-
def runner(monkeypatch):
51+
def runner():
5252
"""Create a runner on isolated filesystem."""
53-
from renku.core.management.config import RENKU_HOME
54-
monkeypatch.setenv('RENKU_CONFIG', RENKU_HOME)
5553
return CliRunner()
5654

5755

5856
@pytest.fixture
59-
def config_dir(monkeypatch, tmpdir_factory):
57+
def global_config_dir(monkeypatch, tmpdir_factory):
6058
"""Create a temporary renku config directory."""
6159
from renku.core.management.config import ConfigManagerMixin
6260

6361
with monkeypatch.context() as m:
64-
home_dir = tmpdir_factory.mktemp('fake_home')
65-
conf_path = home_dir / 'renku.ini'
66-
m.setattr(ConfigManagerMixin, 'config_path', conf_path)
62+
home_dir = tmpdir_factory.mktemp('fake_home').strpath
63+
m.setattr(ConfigManagerMixin, 'global_config_dir', home_dir)
6764

6865
yield m
6966

@@ -124,10 +121,8 @@ def generate(args=('update', ), cwd=None, **streams):
124121

125122

126123
@pytest.fixture()
127-
def isolated_runner(monkeypatch):
124+
def isolated_runner():
128125
"""Create a runner on isolated filesystem."""
129-
from renku.core.management.config import RENKU_HOME
130-
monkeypatch.setenv('RENKU_CONFIG', RENKU_HOME)
131126
runner_ = CliRunner()
132127
with runner_.isolated_filesystem():
133128
yield runner_

renku/cli/__init__.py

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
3434
Options:
3535
--version Print version number.
36-
--config PATH Location of client config files.
37-
--config-path Print application config path.
36+
--global-config-path Print global application's config path.
3837
--install-completion Install completion for the current shell.
3938
--path <path> Location of a Renku repository.
4039
[default: (dynamic)]
@@ -61,18 +60,7 @@
6160
``C:\Users\<user>\AppData\Roaming\Renku``
6261
6362
If in doubt where to look for the configuration file, you can display its path
64-
by running ``renku --config-path``.
65-
66-
You can specify a different location via the ``RENKU_CONFIG`` environment
67-
variable or the ``--config`` command line option. If both are specified, then
68-
the ``--config`` option value is used. For example:
69-
70-
.. code-block:: console
71-
72-
$ renku --config ~/renku/config/ init
73-
74-
instructs Renku to store the configuration files in your ``~/renku/config/``
75-
directory when running the ``init`` command.
63+
by running ``renku --global-config-path``.
7664
"""
7765

7866
import uuid
@@ -102,8 +90,7 @@
10290
option_use_external_storage
10391
from renku.core.commands.version import check_version, print_version
10492
from renku.core.management.client import LocalClient
105-
from renku.core.management.config import RENKU_HOME, default_config_dir, \
106-
print_app_config_path
93+
from renku.core.management.config import ConfigManagerMixin, RENKU_HOME
10794
from renku.core.management.repository import default_path
10895

10996
#: Monkeypatch Click application.
@@ -118,6 +105,14 @@ def _uuid_representer(dumper, data):
118105
yaml.add_representer(uuid.UUID, _uuid_representer)
119106

120107

108+
def print_global_config_path(ctx, param, value):
109+
"""Print global application's config path."""
110+
if not value or ctx.resilient_parsing:
111+
return
112+
click.echo(ConfigManagerMixin().global_config_path)
113+
ctx.exit()
114+
115+
121116
@click.group(
122117
cls=IssueFromTraceback,
123118
context_settings={
@@ -134,20 +129,12 @@ def _uuid_representer(dumper, data):
134129
help=print_version.__doc__
135130
)
136131
@click.option(
137-
'--config',
138-
envvar='RENKU_CONFIG',
139-
default=default_config_dir,
140-
type=click.Path(),
141-
expose_value=False,
142-
help='Location of client config files.'
143-
)
144-
@click.option(
145-
'--config-path',
132+
'--global-config-path',
146133
is_flag=True,
147-
callback=print_app_config_path,
134+
callback=print_global_config_path,
148135
expose_value=False,
149136
is_eager=True,
150-
help=print_app_config_path.__doc__
137+
help=print_global_config_path.__doc__
151138
)
152139
@click.option(
153140
'--install-completion',

renku/cli/config.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,79 @@
2727
2828
$ renku config registry https://registry.gitlab.com/demo/demo
2929
30+
By default, configuration is stored locally in the project's directory. Use
31+
``--global`` option to store configuration for all projects in your home
32+
directory.
33+
34+
Remove values
35+
~~~~~~~~~~~~~
36+
37+
To remove a specific key from configuration use:
38+
39+
.. code-block:: console
40+
41+
$ renku config --remove registry
42+
43+
By default, only local configuration is searched for removal. Use ``--global``
44+
option to remove a global configuration value.
45+
3046
Query values
3147
~~~~~~~~~~~~
3248
33-
You display a previously set value with:
49+
You can display all configuration values with:
50+
51+
.. code-block:: console
52+
53+
$ renku config
54+
55+
Both local and global configuration files are read. Values in local
56+
configuration take precedence over global values. Use ``--local`` or
57+
``--global`` flag to read corresponding configuration only.
58+
59+
You can provide a KEY to display only its value:
3460
3561
.. code-block:: console
3662
3763
$ renku config registry
3864
https://registry.gitlab.com/demo/demo
39-
4065
"""
4166
import click
4267

43-
from renku.core.commands.config import update_config
68+
from renku.core import errors
69+
from renku.core.commands.config import read_config, update_config
4470

4571

4672
@click.command()
47-
@click.argument('key', required=True)
73+
@click.argument('key', required=False, default=None)
4874
@click.argument('value', required=False, default=None)
75+
@click.option('--remove', is_flag=True, help='Remove specified key.')
76+
@click.option(
77+
'--local',
78+
'local_only',
79+
is_flag=True,
80+
help='Read/store from/to local configuration only.'
81+
)
4982
@click.option(
5083
'--global',
51-
'is_global',
84+
'global_only',
5285
is_flag=True,
53-
help='Store to global configuration.'
86+
help='Read/store from/to global configuration only.'
5487
)
55-
def config(key, value, is_global):
88+
def config(key, value, remove, local_only, global_only):
5689
"""Manage configuration options."""
57-
updated = update_config(key, value, is_global)
58-
click.secho(updated)
90+
is_write = value is not None
91+
92+
if is_write and remove:
93+
raise errors.UsageError('Cannot remove and set at the same time.')
94+
if remove and not key:
95+
raise errors.UsageError('KEY is missing.')
96+
if local_only and global_only:
97+
raise errors.UsageError('Cannot use --local and --global together.')
98+
99+
if remove:
100+
update_config(key, remove=remove, global_only=global_only)
101+
elif is_write:
102+
update_config(key, value=value, global_only=global_only)
103+
else:
104+
value = read_config(key, local_only, global_only)
105+
click.secho(value)

renku/core/commands/client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def pass_local_client(
4444
commit=None,
4545
commit_only=None,
4646
ignore_std_streams=True,
47-
allow_empty=True,
47+
commit_empty=True,
48+
raise_if_empty=False,
4849
lock=None,
4950
):
5051
"""Pass client from the current context to the decorated command."""
@@ -56,7 +57,8 @@ def pass_local_client(
5657
commit=commit,
5758
commit_only=commit_only,
5859
ignore_std_streams=ignore_std_streams,
59-
allow_empty=allow_empty,
60+
commit_empty=commit_empty,
61+
raise_if_empty=raise_if_empty,
6062
lock=lock,
6163
)
6264

@@ -75,7 +77,8 @@ def new_func(*args, **kwargs):
7577
commit=commit,
7678
commit_only=commit_only,
7779
ignore_std_streams=ignore_std_streams,
78-
allow_empty=allow_empty,
80+
commit_empty=commit_empty,
81+
raise_if_empty=raise_if_empty,
7982
)
8083
stack.enter_context(transaction)
8184

renku/core/commands/config.py

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
# See the License for the specific language governing permissions and
1717
# limitations under the License.
1818
"""Get and set Renku repository or global options."""
19-
import configparser
20-
21-
from renku.core.management.config import get_config
19+
from renku.core import errors
20+
from renku.core.management.config import CONFIG_LOCAL_PATH
2221

2322
from .client import pass_local_client
2423

@@ -31,18 +30,39 @@ def _split_section_and_key(key):
3130
return 'renku', key
3231

3332

34-
@pass_local_client
35-
def update_config(client, key, value, is_global):
36-
"""Manage configuration options."""
37-
write_op = value is not None
38-
config_ = get_config(client, write_op, is_global)
39-
if write_op:
40-
with config_:
41-
section, config_key = _split_section_and_key(key)
42-
config_.set_value(section, config_key, value)
43-
return value
33+
@pass_local_client(
34+
clean=False,
35+
commit=True,
36+
commit_only=CONFIG_LOCAL_PATH,
37+
commit_empty=False
38+
)
39+
def update_config(client, key, *, value=None, remove=False, global_only=False):
40+
"""Add, update, or remove configuration values."""
41+
section, section_key = _split_section_and_key(key)
42+
if remove:
43+
value = client.remove_value(
44+
section, section_key, global_only=global_only
45+
)
46+
if value is None:
47+
raise errors.ParameterError('Key "{}" not found.'.format(key))
4448
else:
45-
try:
46-
return config_.get_value(*_split_section_and_key(key))
47-
except configparser.NoSectionError:
48-
raise KeyError('Requested configuration not found')
49+
client.set_value(section, section_key, value, global_only=global_only)
50+
return value
51+
52+
53+
@pass_local_client
54+
def read_config(client, key, local_only, global_only):
55+
"""Read configuration."""
56+
if key:
57+
section, section_key = _split_section_and_key(key)
58+
value = client.get_value(
59+
section,
60+
section_key,
61+
local_only=local_only,
62+
global_only=global_only
63+
)
64+
if value is None:
65+
raise errors.ParameterError('Key "{}" not found.'.format(key))
66+
return value
67+
68+
return client.get_config(local_only=local_only, global_only=global_only)

renku/core/commands/dataset.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,18 @@ def export_dataset(
363363
if access_token is None or len(access_token) == 0:
364364
raise InvalidAccessToken()
365365

366-
client.set_value(provider_id, config_key_secret, access_token)
366+
client.set_value(
367+
provider_id, config_key_secret, access_token, global_only=True
368+
)
367369
exporter.set_access_token(access_token)
368370

369371
try:
370372
destination = exporter.export(publish, selected_tag)
371373
except HTTPError as e:
372374
if 'unauthorized' in str(e):
373-
client.remove_value(provider_id, config_key_secret)
375+
client.remove_value(
376+
provider_id, config_key_secret, global_only=True
377+
)
374378

375379
raise
376380

renku/core/commands/migrate.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222

2323

2424
@pass_local_client(
25-
clean=True,
26-
commit=True,
27-
allow_empty=False,
25+
clean=True, commit=True, commit_empty=False, raise_if_empty=True
2826
)
2927
def migrate_datasets(client):
3028
"""Migrate dataset metadata."""

0 commit comments

Comments
 (0)