prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  feature: Install multiple plugins of any type
  ### Feature scope
  
  CLI (options, error messages, logging, etc.)
  
  ### Description
  
  Currently, you can only leverage `meltano install` in parallel for all plugin types or all plugins of a specific type:
  
  ```sh
  # all plugins
  meltano install
  
  # all extractors
  meltano install [extractor|extractors]
  
  # all loaders
  meltano install [loader|loaders]
  ```
  
  It would be great if you could install multiple plugins of any type - something like:
  
  ```sh
  meltano install <extractor> <loader> <transformer> <utility>
  ```
  
  This change would remove the need to specify a plugin type at all, since a plugin name is already unique to a Meltano project. This is currently not possible without a breaking change however, since a plugin type is required as the first argument to `meltano install` when specifying plugin names. #8228 introduced the `--from-file` option for `meltano config <plugin> set`, which accepts a special character `-` to refer to stdin - `meltano install` could reuse this or a similar concept to skip the plugin type argument and leverage parallel install:
  
  ```sh
  meltano install - <extractor> <loader> <transformer> <utility>
  ```
  
  Once a convention is established, this feature could be extended to `meltano remove` as well.
  
  I have a POC of this working for `meltano install` locally, so happy to open a PR.
  
  </issue>
  <code>
  [start of src/meltano/cli/install.py]
  1 """CLI command `meltano install`."""
  2 
  3 from __future__ import annotations
  4 
  5 import typing as t
  6 
  7 import click
  8 import structlog
  9 
  10 from meltano.cli.params import pass_project
  11 from meltano.cli.utils import CliError, PartialInstrumentedCmd, install_plugins
  12 from meltano.core.block.parser import BlockParser
  13 from meltano.core.plugin import PluginType
  14 from meltano.core.schedule_service import ScheduleService
  15 from meltano.core.tracking.contexts import CliEvent, PluginsTrackingContext
  16 
  17 if t.TYPE_CHECKING:
  18     from meltano.core.project import Project
  19     from meltano.core.tracking import Tracker
  20 
  21 logger = structlog.getLogger(__name__)
  22 
  23 
  24 @click.command(cls=PartialInstrumentedCmd, short_help="Install project dependencies.")
  25 @click.argument(
  26     "plugin_type",
  27     type=click.Choice(PluginType.cli_arguments()),
  28     required=False,
  29 )
  30 @click.argument("plugin_name", nargs=-1, required=False)
  31 @click.option(
  32     "--clean",
  33     is_flag=True,
  34     help="Completely reinstall a plugin rather than simply upgrading if necessary.",
  35 )
  36 @click.option(
  37     "--parallelism",
  38     "-p",
  39     type=click.INT,
  40     default=None,
  41     help=(
  42         "Limit the number of plugins to install in parallel. "
  43         "Defaults to the number of cores."
  44     ),
  45 )
  46 @click.option(
  47     "--force",
  48     "-f",
  49     is_flag=True,
  50     help="Ignore the required Python version declared by the plugins.",
  51 )
  52 @click.option(
  53     "--schedule",
  54     "-s",
  55     "schedule_name",
  56     help="Install all plugins from the given schedule.",
  57 )
  58 @click.pass_context
  59 @pass_project(migrate=True)
  60 def install(  # noqa: C901
  61     project: Project,
  62     ctx: click.Context,
  63     plugin_type: str,
  64     plugin_name: str,
  65     clean: bool,
  66     parallelism: int,
  67     force: bool,
  68     schedule_name: str,
  69 ):
  70     """
  71     Install all the dependencies of your project based on the meltano.yml file.
  72 
  73     \b\nRead more at https://docs.meltano.com/reference/command-line-interface#install
  74     """
  75     tracker: Tracker = ctx.obj["tracker"]
  76     try:
  77         if plugin_type:
  78             plugin_type = PluginType.from_cli_argument(plugin_type)
  79             plugins = project.plugins.get_plugins_of_type(plugin_type)
  80             if plugin_name:
  81                 plugins = [plugin for plugin in plugins if plugin.name in plugin_name]
  82         else:
  83             plugins = list(project.plugins.plugins())
  84 
  85         if schedule_name:
  86             schedule_plugins = _get_schedule_plugins(
  87                 ctx.obj["project"],
  88                 schedule_name,
  89             )
  90             plugins = list(set(plugins) & set(schedule_plugins))
  91     except Exception:
  92         tracker.track_command_event(CliEvent.aborted)
  93         raise
  94 
  95     click.echo(f"Installing {len(plugins)} plugins...")
  96     tracker.add_contexts(
  97         PluginsTrackingContext([(candidate, None) for candidate in plugins]),
  98     )
  99     tracker.track_command_event(CliEvent.inflight)
  100 
  101     success = install_plugins(
  102         project,
  103         plugins,
  104         parallelism=parallelism,
  105         clean=clean,
  106         force=force,
  107     )
  108     if not success:
  109         tracker.track_command_event(CliEvent.failed)
  110         raise CliError("Failed to install plugin(s)")  # noqa: EM101
  111     tracker.track_command_event(CliEvent.completed)
  112 
  113 
  114 def _get_schedule_plugins(project: Project, schedule_name: str):
  115     schedule_service = ScheduleService(project)
  116     schedule_obj = schedule_service.find_schedule(schedule_name)
  117     schedule_plugins = set()
  118     if schedule_obj.elt_schedule:
  119         for plugin_name in (schedule_obj.extractor, schedule_obj.loader):
  120             schedule_plugins.add(project.plugins.find_plugin(plugin_name))
  121     else:
  122         task_sets = schedule_service.task_sets_service.get(schedule_obj.job)
  123         for blocks in task_sets.flat_args_per_set:
  124             parser = BlockParser(logger, project, blocks)
  125             for plugin in parser.plugins:
  126                 schedule_plugins.add(
  127                     project.plugins.find_plugin(plugin.info.get("name"))
  128                     if plugin.type == PluginType.MAPPERS
  129                     else plugin,
  130                 )
  131     return schedule_plugins
  132
  [end of src/meltano/cli/install.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/meltano/cli/install.py b/src/meltano/cli/install.py
  --- a/src/meltano/cli/install.py
  +++ b/src/meltano/cli/install.py
  @@ -18,13 +18,15 @@
       from meltano.core.project import Project
       from meltano.core.tracking import Tracker
   
  +ANY = "-"
  +
   logger = structlog.getLogger(__name__)
   
   
   @click.command(cls=PartialInstrumentedCmd, short_help="Install project dependencies.")
   @click.argument(
       "plugin_type",
  -    type=click.Choice(PluginType.cli_arguments()),
  +    type=click.Choice((*PluginType.cli_arguments(), ANY)),
       required=False,
   )
   @click.argument("plugin_name", nargs=-1, required=False)
  @@ -74,14 +76,15 @@
       """
       tracker: Tracker = ctx.obj["tracker"]
       try:
  -        if plugin_type:
  +        if plugin_type and plugin_type != ANY:
               plugin_type = PluginType.from_cli_argument(plugin_type)
               plugins = project.plugins.get_plugins_of_type(plugin_type)
  -            if plugin_name:
  -                plugins = [plugin for plugin in plugins if plugin.name in plugin_name]
           else:
               plugins = list(project.plugins.plugins())
   
  +        if plugin_name:
  +            plugins = [plugin for plugin in plugins if plugin.name in plugin_name]
  +
           if schedule_name:
               schedule_plugins = _get_schedule_plugins(
                   ctx.obj["project"],
  
verification_info: {"golden_diff": "diff --git a/src/meltano/cli/install.py b/src/meltano/cli/install.py\n--- a/src/meltano/cli/install.py\n+++ b/src/meltano/cli/install.py\n@@ -18,13 +18,15 @@\n     from meltano.core.project import Project\n     from meltano.core.tracking import Tracker\n \n+ANY = \"-\"\n+\n logger = structlog.getLogger(__name__)\n \n \n @click.command(cls=PartialInstrumentedCmd, short_help=\"Install project dependencies.\")\n @click.argument(\n     \"plugin_type\",\n-    type=click.Choice(PluginType.cli_arguments()),\n+    type=click.Choice((*PluginType.cli_arguments(), ANY)),\n     required=False,\n )\n @click.argument(\"plugin_name\", nargs=-1, required=False)\n@@ -74,14 +76,15 @@\n     \"\"\"\n     tracker: Tracker = ctx.obj[\"tracker\"]\n     try:\n-        if plugin_type:\n+        if plugin_type and plugin_type != ANY:\n             plugin_type = PluginType.from_cli_argument(plugin_type)\n             plugins = project.plugins.get_plugins_of_type(plugin_type)\n-            if plugin_name:\n-                plugins = [plugin for plugin in plugins if plugin.name in plugin_name]\n         else:\n             plugins = list(project.plugins.plugins())\n \n+        if plugin_name:\n+            plugins = [plugin for plugin in plugins if plugin.name in plugin_name]\n+\n         if schedule_name:\n             schedule_plugins = _get_schedule_plugins(\n                 ctx.obj[\"project\"],\n", "issue": "feature: Install multiple plugins of any type\n### Feature scope\r\n\r\nCLI (options, error messages, logging, etc.)\r\n\r\n### Description\r\n\r\nCurrently, you can only leverage `meltano install` in parallel for all plugin types or all plugins of a specific type:\r\n\r\n```sh\r\n# all plugins\r\nmeltano install\r\n\r\n# all extractors\r\nmeltano install [extractor|extractors]\r\n\r\n# all loaders\r\nmeltano install [loader|loaders]\r\n```\r\n\r\nIt would be great if you could install multiple plugins of any type - something like:\r\n\r\n```sh\r\nmeltano install <extractor> <loader> <transformer> <utility>\r\n```\r\n\r\nThis change would remove the need to specify a plugin type at all, since a plugin name is already unique to a Meltano project. This is currently not possible without a breaking change however, since a plugin type is required as the first argument to `meltano install` when specifying plugin names. #8228 introduced the `--from-file` option for `meltano config <plugin> set`, which accepts a special character `-` to refer to stdin - `meltano install` could reuse this or a similar concept to skip the plugin type argument and leverage parallel install:\r\n\r\n```sh\r\nmeltano install - <extractor> <loader> <transformer> <utility>\r\n```\r\n\r\nOnce a convention is established, this feature could be extended to `meltano remove` as well.\r\n\r\nI have a POC of this working for `meltano install` locally, so happy to open a PR.\n", "before_files": [{"content": "\"\"\"CLI command `meltano install`.\"\"\"\n\nfrom __future__ import annotations\n\nimport typing as t\n\nimport click\nimport structlog\n\nfrom meltano.cli.params import pass_project\nfrom meltano.cli.utils import CliError, PartialInstrumentedCmd, install_plugins\nfrom meltano.core.block.parser import BlockParser\nfrom meltano.core.plugin import PluginType\nfrom meltano.core.schedule_service import ScheduleService\nfrom meltano.core.tracking.contexts import CliEvent, PluginsTrackingContext\n\nif t.TYPE_CHECKING:\n    from meltano.core.project import Project\n    from meltano.core.tracking import Tracker\n\nlogger = structlog.getLogger(__name__)\n\n\n@click.command(cls=PartialInstrumentedCmd, short_help=\"Install project dependencies.\")\n@click.argument(\n    \"plugin_type\",\n    type=click.Choice(PluginType.cli_arguments()),\n    required=False,\n)\n@click.argument(\"plugin_name\", nargs=-1, required=False)\n@click.option(\n    \"--clean\",\n    is_flag=True,\n    help=\"Completely reinstall a plugin rather than simply upgrading if necessary.\",\n)\n@click.option(\n    \"--parallelism\",\n    \"-p\",\n    type=click.INT,\n    default=None,\n    help=(\n        \"Limit the number of plugins to install in parallel. \"\n        \"Defaults to the number of cores.\"\n    ),\n)\n@click.option(\n    \"--force\",\n    \"-f\",\n    is_flag=True,\n    help=\"Ignore the required Python version declared by the plugins.\",\n)\n@click.option(\n    \"--schedule\",\n    \"-s\",\n    \"schedule_name\",\n    help=\"Install all plugins from the given schedule.\",\n)\n@click.pass_context\n@pass_project(migrate=True)\ndef install(  # noqa: C901\n    project: Project,\n    ctx: click.Context,\n    plugin_type: str,\n    plugin_name: str,\n    clean: bool,\n    parallelism: int,\n    force: bool,\n    schedule_name: str,\n):\n    \"\"\"\n    Install all the dependencies of your project based on the meltano.yml file.\n\n    \\b\\nRead more at https://docs.meltano.com/reference/command-line-interface#install\n    \"\"\"\n    tracker: Tracker = ctx.obj[\"tracker\"]\n    try:\n        if plugin_type:\n            plugin_type = PluginType.from_cli_argument(plugin_type)\n            plugins = project.plugins.get_plugins_of_type(plugin_type)\n            if plugin_name:\n                plugins = [plugin for plugin in plugins if plugin.name in plugin_name]\n        else:\n            plugins = list(project.plugins.plugins())\n\n        if schedule_name:\n            schedule_plugins = _get_schedule_plugins(\n                ctx.obj[\"project\"],\n                schedule_name,\n            )\n            plugins = list(set(plugins) & set(schedule_plugins))\n    except Exception:\n        tracker.track_command_event(CliEvent.aborted)\n        raise\n\n    click.echo(f\"Installing {len(plugins)} plugins...\")\n    tracker.add_contexts(\n        PluginsTrackingContext([(candidate, None) for candidate in plugins]),\n    )\n    tracker.track_command_event(CliEvent.inflight)\n\n    success = install_plugins(\n        project,\n        plugins,\n        parallelism=parallelism,\n        clean=clean,\n        force=force,\n    )\n    if not success:\n        tracker.track_command_event(CliEvent.failed)\n        raise CliError(\"Failed to install plugin(s)\")  # noqa: EM101\n    tracker.track_command_event(CliEvent.completed)\n\n\ndef _get_schedule_plugins(project: Project, schedule_name: str):\n    schedule_service = ScheduleService(project)\n    schedule_obj = schedule_service.find_schedule(schedule_name)\n    schedule_plugins = set()\n    if schedule_obj.elt_schedule:\n        for plugin_name in (schedule_obj.extractor, schedule_obj.loader):\n            schedule_plugins.add(project.plugins.find_plugin(plugin_name))\n    else:\n        task_sets = schedule_service.task_sets_service.get(schedule_obj.job)\n        for blocks in task_sets.flat_args_per_set:\n            parser = BlockParser(logger, project, blocks)\n            for plugin in parser.plugins:\n                schedule_plugins.add(\n                    project.plugins.find_plugin(plugin.info.get(\"name\"))\n                    if plugin.type == PluginType.MAPPERS\n                    else plugin,\n                )\n    return schedule_plugins\n", "path": "src/meltano/cli/install.py"}]}