Repository: meltano/meltano

Issue:
bug: `meltano config meltano set ...` fails
### Meltano Version

2.6.0

### Python Version

3.8

### Bug scope

CLI (options, error messages, logging, etc.)

### Operating System

Ubuntu

### Description

Tried to set my buffer size via `meltano config` and received this error

### Code

```python
ubuntu@ip-172-31-19-214:~/git/tap-indeed$ meltano config meltano set elt.buffer_size 104857600
2022-09-27T13:08:59.348162Z [info     ] Environment 'dev' is active
2022-09-27T13:08:59.478128Z [info     ] The default environment (dev) will be ignored for `meltano config`. To configure a specific Environment, please use option `--environment=<environment name>`.
'ProjectSettingsService' object has no attribute 'plugin'
```



Discussion:
Seems related to https://github.com/meltano/meltano/pull/5892.

I can confirm with:

```console
meltano config meltano set cli.log_config foo.yml
```

<details><summary>Full traceback
</summary>

```
'ProjectSettingsService' object has no attribute 'plugin' 
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/__i │
│ nit__.py:94 in _run_cli                                                                          │
│                                                                                                  │
│    91 │   """                                                                                    │
│    92 │   try:                                                                                   │
│    93 │   │   try:  # noqa: WPS225, WPS505                                                       │
│ ❱  94 │   │   │   cli(obj={"project": None})                                                     │
│    95 │   │   except ProjectReadonly as err:                                                     │
│    96 │   │   │   raise CliError(                                                                │
│    97 │   │   │   │   f"The requested action could not be completed: {err}"                      │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ cli_error = CliError("Need help fixing this problem? Visit http://melta.no/ for              │ │
│ │             troubleshooting steps, or to\njoin our friendly Slack                            │ │
│ │             community.\n\n'ProjectSettingsService' object has no attribute 'plugin'")        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:1 │
│ 130 in __call__                                                                                  │
│                                                                                                  │
│   1127 │                                                                                         │
│   1128 │   def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any:                           │
│   1129 │   │   """Alias for :meth:`main`."""                                                     │
│ ❱ 1130 │   │   return self.main(*args, **kwargs)                                                 │
│   1131                                                                                           │
│   1132                                                                                           │
│   1133 class Command(BaseCommand):                                                               │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   args = ()                                                                                  │ │
│ │ kwargs = {                                                                                   │ │
│ │          │   'obj': {                                                                        │ │
│ │          │   │   'project': <meltano.core.project.Project object at 0x106eeb070>,            │ │
│ │          │   │   'verbosity': 0,                                                             │ │
│ │          │   │   'is_default_environment': True,                                             │ │
│ │          │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at 0x10a446e20>,   │ │
│ │          │   │   'settings': <meltano.core.project_settings_service.ProjectSettingsService   │ │
│ │          object at 0x10ad93a30>,                                                             │ │
│ │          │   │   'session': <sqlalchemy.orm.session.Session object at 0x10ad757f0>,          │ │
│ │          │   │   'invoker': None                                                             │ │
│ │          │   }                                                                               │ │
│ │          }                                                                                   │ │
│ │   self = <NoWindowsGlobbingGroup cli>                                                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/cli │
│ .py:38 in main                                                                                   │
│                                                                                                  │
│    35 │   │   │   args: Positional arguments for the Click group.                                │
│    36 │   │   │   kwargs: Keyword arguments for the Click group.                                 │
│    37 │   │   """                                                                                │
│ ❱  38 │   │   return super().main(*args, windows_expand_args=False, **kwargs)                    │
│    39                                                                                            │
│    40                                                                                            │
│    41 @click.group(                                                                              │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ __class__ = <class 'meltano.cli.cli.NoWindowsGlobbingGroup'>                                 │ │
│ │      args = ()                                                                               │ │
│ │    kwargs = {                                                                                │ │
│ │             │   'obj': {                                                                     │ │
│ │             │   │   'project': <meltano.core.project.Project object at 0x106eeb070>,         │ │
│ │             │   │   'verbosity': 0,                                                          │ │
│ │             │   │   'is_default_environment': True,                                          │ │
│ │             │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at              │ │
│ │             0x10a446e20>,                                                                    │ │
│ │             │   │   'settings':                                                              │ │
│ │             <meltano.core.project_settings_service.ProjectSettingsService object at          │ │
│ │             0x10ad93a30>,                                                                    │ │
│ │             │   │   'session': <sqlalchemy.orm.session.Session object at 0x10ad757f0>,       │ │
│ │             │   │   'invoker': None                                                          │ │
│ │             │   }                                                                            │ │
│ │             }                                                                                │ │
│ │      self = <NoWindowsGlobbingGroup cli>                                                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:1 │
│ 055 in main                                                                                      │
│                                                                                                  │
│   1052 │   │   try:                                                                              │
│   1053 │   │   │   try:                                                                          │
│   1054 │   │   │   │   with self.make_context(prog_name, args, **extra) as ctx:                  │
│ ❱ 1055 │   │   │   │   │   rv = self.invoke(ctx)                                                 │
│   1056 │   │   │   │   │   if not standalone_mode:                                               │
│   1057 │   │   │   │   │   │   return rv                                                         │
│   1058 │   │   │   │   │   # it's not safe to `ctx.exit(rv)` here!                               │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │                args = ['config', 'meltano', 'set', 'cli.log_config', 'foo.yml']              │ │
│ │        complete_var = None                                                                   │ │
│ │                 ctx = <click.core.Context object at 0x10a35fcd0>                             │ │
│ │               extra = {                                                                      │ │
│ │                       │   'obj': {                                                           │ │
│ │                       │   │   'project': <meltano.core.project.Project object at             │ │
│ │                       0x106eeb070>,                                                          │ │
│ │                       │   │   'verbosity': 0,                                                │ │
│ │                       │   │   'is_default_environment': True,                                │ │
│ │                       │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at    │ │
│ │                       0x10a446e20>,                                                          │ │
│ │                       │   │   'settings':                                                    │ │
│ │                       <meltano.core.project_settings_service.ProjectSettingsService object   │ │
│ │                       at 0x10ad93a30>,                                                       │ │
│ │                       │   │   'session': <sqlalchemy.orm.session.Session object at           │ │
│ │                       0x10ad757f0>,                                                          │ │
│ │                       │   │   'invoker': None                                                │ │
│ │                       │   }                                                                  │ │
│ │                       }                                                                      │ │
│ │           prog_name = 'meltano@latest'                                                       │ │
│ │                self = <NoWindowsGlobbingGroup cli>                                           │ │
│ │     standalone_mode = True                                                                   │ │
│ │ windows_expand_args = False                                                                  │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/uti │
│ ls.py:541 in invoke                                                                              │
│                                                                                                  │
│   538 │   │   ctx.ensure_object(dict)                                                            │
│   539 │   │   if ctx.obj.get("tracker"):                                                         │
│   540 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│ ❱ 541 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   542                                                                                            │
│   543                                                                                            │
│   544 class InstrumentedCmd(click.Command):                                                      │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │ __class__ = <class 'meltano.cli.utils.InstrumentedGroup'> │                                    │
│ │       ctx = <click.core.Context object at 0x10a35fcd0>    │                                    │
│ │      self = <NoWindowsGlobbingGroup cli>                  │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:1 │
│ 657 in invoke                                                                                    │
│                                                                                                  │
│   1654 │   │   │   │   super().invoke(ctx)                                                       │
│   1655 │   │   │   │   sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)                    │
│   1656 │   │   │   │   with sub_ctx:                                                             │
│ ❱ 1657 │   │   │   │   │   return _process_result(sub_ctx.command.invoke(sub_ctx))               │
│   1658 │   │                                                                                     │
│   1659 │   │   # In chain mode we create the contexts step by step, but after the                │
│   1660 │   │   # base command has been invoked.  Because at that point we do not                 │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮     │
│ │       __class__ = <class 'click.core.MultiCommand'>                                      │     │
│ │ _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x106e53040> │     │
│ │            args = ['meltano', 'set', 'cli.log_config', 'foo.yml']                        │     │
│ │             cmd = <InstrumentedGroup config>                                             │     │
│ │        cmd_name = 'config'                                                               │     │
│ │             ctx = <click.core.Context object at 0x10a35fcd0>                             │     │
│ │            self = <NoWindowsGlobbingGroup cli>                                           │     │
│ │         sub_ctx = <click.core.Context object at 0x106eeb4c0>                             │     │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯     │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/uti │
│ ls.py:541 in invoke                                                                              │
│                                                                                                  │
│   538 │   │   ctx.ensure_object(dict)                                                            │
│   539 │   │   if ctx.obj.get("tracker"):                                                         │
│   540 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│ ❱ 541 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   542                                                                                            │
│   543                                                                                            │
│   544 class InstrumentedCmd(click.Command):                                                      │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │ __class__ = <class 'meltano.cli.utils.InstrumentedGroup'> │                                    │
│ │       ctx = <click.core.Context object at 0x106eeb4c0>    │                                    │
│ │      self = <InstrumentedGroup config>                    │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:1 │
│ 657 in invoke                                                                                    │
│                                                                                                  │
│   1654 │   │   │   │   super().invoke(ctx)                                                       │
│   1655 │   │   │   │   sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)                    │
│   1656 │   │   │   │   with sub_ctx:                                                             │
│ ❱ 1657 │   │   │   │   │   return _process_result(sub_ctx.command.invoke(sub_ctx))               │
│   1658 │   │                                                                                     │
│   1659 │   │   # In chain mode we create the contexts step by step, but after the                │
│   1660 │   │   # base command has been invoked.  Because at that point we do not                 │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮     │
│ │       __class__ = <class 'click.core.MultiCommand'>                                      │     │
│ │ _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x10a4c2f70> │     │
│ │            args = []                                                                     │     │
│ │             cmd = <PartialInstrumentedCmd set>                                           │     │
│ │        cmd_name = 'set'                                                                  │     │
│ │             ctx = <click.core.Context object at 0x106eeb4c0>                             │     │
│ │            self = <InstrumentedGroup config>                                             │     │
│ │         sub_ctx = <click.core.Context object at 0x10a509e50>                             │     │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯     │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/uti │
│ ls.py:577 in invoke                                                                              │
│                                                                                                  │
│   574 │   │   if ctx.obj.get("tracker"):                                                         │
│   575 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│   576 │   │   │   ctx.obj["tracker"].track_command_event(CliEvent.started)                       │
│ ❱ 577 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   578                                                                                            │
│                                                                                                  │
│ ╭──────────────────────────── locals ────────────────────────────╮                               │
│ │ __class__ = <class 'meltano.cli.utils.PartialInstrumentedCmd'> │                               │
│ │       ctx = <click.core.Context object at 0x10a509e50>         │                               │
│ │      self = <PartialInstrumentedCmd set>                       │                               │
│ ╰────────────────────────────────────────────────────────────────╯                               │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:1 │
│ 404 in invoke                                                                                    │
│                                                                                                  │
│   1401 │   │   │   echo(style(message, fg="red"), err=True)                                      │
│   1402 │   │                                                                                     │
│   1403 │   │   if self.callback is not None:                                                     │
│ ❱ 1404 │   │   │   return ctx.invoke(self.callback, **ctx.params)                                │
│   1405 │                                                                                         │
│   1406 │   def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]   │
│   1407 │   │   """Return a list of completions for the incomplete value. Looks                   │
│                                                                                                  │
│ ╭───────────────────── locals ──────────────────────╮                                            │
│ │  ctx = <click.core.Context object at 0x10a509e50> │                                            │
│ │ self = <PartialInstrumentedCmd set>               │                                            │
│ ╰───────────────────────────────────────────────────╯                                            │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/core.py:7 │
│ 60 in invoke                                                                                     │
│                                                                                                  │
│    757 │   │                                                                                     │
│    758 │   │   with augment_usage_errors(__self):                                                │
│    759 │   │   │   with ctx:                                                                     │
│ ❱  760 │   │   │   │   return __callback(*args, **kwargs)                                        │
│    761 │                                                                                         │
│    762 │   def forward(                                                                          │
│    763 │   │   __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any  # noqa: B902             │
│                                                                                                  │
│ ╭────────────────────────────── locals ──────────────────────────────╮                           │
│ │ _Context__callback = <function set_ at 0x10a1814c0>                │                           │
│ │     _Context__self = <click.core.Context object at 0x10a509e50>    │                           │
│ │               args = ()                                            │                           │
│ │                ctx = <click.core.Context object at 0x10a509e50>    │                           │
│ │             kwargs = {                                             │                           │
│ │                      │   'setting_name': ('cli.log_config',),      │                           │
│ │                      │   'value': 'foo.yml',                       │                           │
│ │                      │   'interactive': False,                     │                           │
│ │                      │   'store': <SettingValueStore.AUTO: 'auto'> │                           │
│ │                      }                                             │                           │
│ ╰────────────────────────────────────────────────────────────────────╯                           │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/click/decorator │
│ s.py:26 in new_func                                                                              │
│                                                                                                  │
│    23 │   """                                                                                    │
│    24 │                                                                                          │
│    25 │   def new_func(*args, **kwargs):  # type: ignore                                         │
│ ❱  26 │   │   return f(get_current_context(), *args, **kwargs)                                   │
│    27 │                                                                                          │
│    28 │   return update_wrapper(t.cast(F, new_func), f)                                          │
│    29                                                                                            │
│                                                                                                  │
│ ╭──────────────────────── locals ────────────────────────╮                                       │
│ │   args = ()                                            │                                       │
│ │      f = <function set_ at 0x10a181430>                │                                       │
│ │ kwargs = {                                             │                                       │
│ │          │   'setting_name': ('cli.log_config',),      │                                       │
│ │          │   'value': 'foo.yml',                       │                                       │
│ │          │   'interactive': False,                     │                                       │
│ │          │   'store': <SettingValueStore.AUTO: 'auto'> │                                       │
│ │          }                                             │                                       │
│ ╰────────────────────────────────────────────────────────╯                                       │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/con │
│ fig.py:290 in set_                                                                               │
│                                                                                                  │
│   287 @click.pass_context                                                                        │
│   288 def set_(ctx, setting_name, value, store, interactive):                                    │
│   289 │   """Set the configurations' setting `<name>` to `<value>`."""                           │
│ ❱ 290 │   interaction = InteractiveConfig(ctx=ctx, store=store, extras=False)                    │
│   291 │   if interactive:                                                                        │
│   292 │   │   interaction.configure_all()                                                        │
│   293 │   else:                                                                                  │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │          ctx = <click.core.Context object at 0x10a509e50> │                                    │
│ │  interactive = False                                      │                                    │
│ │ setting_name = ('cli.log_config',)                        │                                    │
│ │        store = <SettingValueStore.AUTO: 'auto'>           │                                    │
│ │        value = 'foo.yml'                                  │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/int │
│ eractive/config.py:66 in __init__                                                                │
│                                                                                                  │
│    63 │   │   self.project = self.ctx.obj["project"]                                             │
│    64 │   │   self.settings = self.ctx.obj["settings"]                                           │
│    65 │   │   self.session = self.ctx.obj["session"]                                             │
│ ❱  66 │   │   self.plugin = self.ctx.obj["settings"].plugin                                      │
│    67 │   │   self.tracker = self.ctx.obj["tracker"]                                             │
│    68 │   │   self.environment_service = EnvironmentService(self.project)                        │
│    69 │   │   self.max_width = max_width or 75  # noqa: WPS432                                   │
│                                                                                                  │
│ ╭─────────────────────────────────────── locals ───────────────────────────────────────╮         │
│ │       ctx = <click.core.Context object at 0x10a509e50>                               │         │
│ │    extras = False                                                                    │         │
│ │ max_width = None                                                                     │         │
│ │      self = <meltano.cli.interactive.config.InteractiveConfig object at 0x10a5964c0> │         │
│ │     store = <SettingValueStore.AUTO: 'auto'>                                         │         │
│ ╰──────────────────────────────────────────────────────────────────────────────────────╯         │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'ProjectSettingsService' object has no attribute 'plugin'

The above exception was the direct cause of the following exception:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/edgarramirez/.local/pipx/venvs/meltano@latest/lib/python3.9/site-packages/meltano/cli/__i │
│ nit__.py:104 in _run_cli                                                                         │
│                                                                                                  │
│   101 │   │   except MeltanoError as err:                                                        │
│   102 │   │   │   handle_meltano_error(err)                                                      │
│   103 │   │   except Exception as err:                                                           │
│ ❱ 104 │   │   │   raise CliError(f"{troubleshooting_message}\n{err}") from err                   │
│   105 │   except CliError as cli_error:                                                          │
│   106 │   │   cli_error.print()                                                                  │
│   107 │   │   sys.exit(1)                                                                        │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ cli_error = CliError("Need help fixing this problem? Visit http://melta.no/ for              │ │
│ │             troubleshooting steps, or to\njoin our friendly Slack                            │ │
│ │             community.\n\n'ProjectSettingsService' object has no attribute 'plugin'")        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
CliError: Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'ProjectSettingsService' object has no attribute 'plugin'

Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'ProjectSettingsService' object has no attribute 'plugin'
```

</details>
@tayloramurphy `meltano config meltano set` is broken so I'm adding this to the board
> Seems related to https://github.com/meltano/meltano/pull/5892.

The issue first started appearing in Meltano 2.6.0, which supports this notion.

Resolving patch:
diff --git a/src/meltano/cli/interactive/config.py b/src/meltano/cli/interactive/config.py
--- a/src/meltano/cli/interactive/config.py
+++ b/src/meltano/cli/interactive/config.py
@@ -63,7 +63,6 @@ def __init__(self, ctx, store, extras=False, max_width=None):
         self.project = self.ctx.obj["project"]
         self.settings = self.ctx.obj["settings"]
         self.session = self.ctx.obj["session"]
-        self.plugin = self.ctx.obj["settings"].plugin
         self.tracker = self.ctx.obj["tracker"]
         self.environment_service = EnvironmentService(self.project)
         self.max_width = max_width or 75  # noqa: WPS432


Test patch:
diff --git a/tests/meltano/cli/test_config.py b/tests/meltano/cli/test_config.py
--- a/tests/meltano/cli/test_config.py
+++ b/tests/meltano/cli/test_config.py
@@ -70,6 +70,22 @@ def test_config_meltano(
             assert json_config["database_uri"] == engine_uri
             assert json_config["cli"]["log_level"] == "info"
 
+    def test_config_meltano_set(
+        self, project: Project, cli_runner, project_plugins_service
+    ):
+        with mock.patch(
+            "meltano.cli.config.ProjectPluginsService",
+            return_value=project_plugins_service,
+        ):
+            result = cli_runner.invoke(
+                cli, ["config", "meltano", "set", "cli.log_config", "log_config.yml"]
+            )
+            assert_cli_runner(result)
+            assert (
+                "Meltano setting 'cli.log_config' was set in `meltano.yml`: 'log_config.yml'"
+                in result.stdout
+            )
+
     def test_config_test(
         self, project: Project, cli_runner, tap, project_plugins_service
     ):