prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  remote-build: add control logic for envvar `SNAPCRAFT_REMOTE_BUILD_STRATEGY`
  ### What needs to get done
  
  This adds control logic to determine whether to execute the new or legacy remote-build code.
  
  There are four possibilities with `SNAPCRAFT_REMOTE_BUILD_STRATEGY`:
  
  - `disable-fallback` - use new remote-build code
  - `force-fallback` - use legacy remote-build code
  - unset - continue on to next control logic step
  - unknown - raise an error
  
  ![image](https://github.com/snapcore/snapcraft/assets/60674096/298ea1f2-96b2-4337-b2cc-3d3c3e2848b8)
  
  
  ### Why it needs to get done
  
  remote-build needs to be migrated because it does not leverage the new craft libraries, has issues with building core22 snaps, and has issues related to how the local project is bundled.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `snapcraft/commands/remote.py`
  Content:
  ```
  1 # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
  2 #
  3 # Copyright 2022-2023 Canonical Ltd.
  4 #
  5 # This program is free software: you can redistribute it and/or modify
  6 # it under the terms of the GNU General Public License version 3 as
  7 # published by the Free Software Foundation.
  8 #
  9 # This program is distributed in the hope that it will be useful,
  10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 # GNU General Public License for more details.
  13 #
  14 # You should have received a copy of the GNU General Public License
  15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16 
  17 """Snapcraft remote build command."""
  18 
  19 import argparse
  20 import os
  21 import textwrap
  22 
  23 from craft_cli import BaseCommand, emit
  24 from craft_cli.helptexts import HIDDEN
  25 from overrides import overrides
  26 
  27 from snapcraft.errors import MaintenanceBase, SnapcraftError
  28 from snapcraft.legacy_cli import run_legacy
  29 from snapcraft.parts import yaml_utils
  30 from snapcraft.utils import confirm_with_user
  31 from snapcraft_legacy.internal.remote_build.errors import AcceptPublicUploadError
  32 
  33 _CONFIRMATION_PROMPT = (
  34     "All data sent to remote builders will be publicly available. "
  35     "Are you sure you want to continue?"
  36 )
  37 
  38 
  39 class RemoteBuildCommand(BaseCommand):
  40     """Command passthrough for the remote-build command."""
  41 
  42     name = "remote-build"
  43     help_msg = "Dispatch a snap for remote build"
  44     overview = textwrap.dedent(
  45         """
  46         Command remote-build sends the current project to be built
  47         remotely.  After the build is complete, packages for each
  48         architecture are retrieved and will be available in the
  49         local filesystem.
  50 
  51         If not specified in the snapcraft.yaml file, the list of
  52         architectures to build can be set using the --build-on option.
  53         If both are specified, an error will occur.
  54 
  55         Interrupted remote builds can be resumed using the --recover
  56         option, followed by the build number informed when the remote
  57         build was originally dispatched. The current state of the
  58         remote build for each architecture can be checked using the
  59         --status option."""
  60     )
  61 
  62     @overrides
  63     def fill_parser(self, parser: argparse.ArgumentParser) -> None:
  64         parser.add_argument(
  65             "--recover", action="store_true", help="recover an interrupted build"
  66         )
  67         parser.add_argument(
  68             "--status", action="store_true", help="display remote build status"
  69         )
  70         parser_target = parser.add_mutually_exclusive_group()
  71         parser_target.add_argument(
  72             "--build-on",
  73             metavar="arch",
  74             nargs="+",
  75             help=HIDDEN,
  76         )
  77         parser_target.add_argument(
  78             "--build-for",
  79             metavar="arch",
  80             nargs="+",
  81             help="architecture to build for",
  82         )
  83         parser.add_argument(
  84             "--build-id", metavar="build-id", help="specific build id to retrieve"
  85         )
  86         parser.add_argument(
  87             "--launchpad-accept-public-upload",
  88             action="store_true",
  89             help="acknowledge that uploaded code will be publicly available.",
  90         )
  91 
  92     def _get_effective_base(self) -> str:
  93         """Get a valid effective base from the project's snapcraft.yaml.
  94 
  95         :returns: The project's effective base.
  96 
  97         :raises SnapcraftError: If the base is unknown or missing or if the
  98         snapcraft.yaml cannot be loaded.
  99         :raises MaintenanceBase: If the base is not supported
  100         """
  101         snapcraft_yaml = yaml_utils.get_snap_project().project_file
  102 
  103         with open(snapcraft_yaml, encoding="utf-8") as file:
  104             base = yaml_utils.get_base(file)
  105 
  106         if base is None:
  107             raise SnapcraftError(
  108                 f"Could not determine base from {str(snapcraft_yaml)!r}."
  109             )
  110 
  111         emit.debug(f"Got base {base!r} from {str(snapcraft_yaml)!r}.")
  112 
  113         if base in yaml_utils.ESM_BASES:
  114             raise MaintenanceBase(base)
  115 
  116         if base not in yaml_utils.BASES:
  117             raise SnapcraftError(f"Unknown base {base!r} in {str(snapcraft_yaml)!r}.")
  118 
  119         return base
  120 
  121     def _run_remote_build(self, base: str) -> None:
  122         # bases newer than core22 must use the new remote-build
  123         if base in yaml_utils.CURRENT_BASES - {"core22"}:
  124             emit.debug(
  125                 "Using fallback remote-build because new remote-build is not available."
  126             )
  127             # TODO: use new remote-build code (#4323)
  128             run_legacy()
  129             return
  130 
  131         emit.debug("Running fallback remote-build.")
  132         run_legacy()
  133 
  134     @overrides
  135     def run(self, parsed_args) -> None:
  136         if os.getenv("SUDO_USER") and os.geteuid() == 0:
  137             emit.message(
  138                 "Running with 'sudo' may cause permission errors and is discouraged."
  139             )
  140 
  141         emit.message(
  142             "snapcraft remote-build is experimental and is subject to change "
  143             "- use with caution."
  144         )
  145 
  146         if parsed_args.build_on:
  147             emit.message("Use --build-for instead of --build-on")
  148             parsed_args.build_for = parsed_args.build_on
  149 
  150         if not parsed_args.launchpad_accept_public_upload and not confirm_with_user(
  151             _CONFIRMATION_PROMPT
  152         ):
  153             raise AcceptPublicUploadError()
  154 
  155         base = self._get_effective_base()
  156         self._run_remote_build(base)
  157
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/snapcraft/commands/remote.py b/snapcraft/commands/remote.py
  --- a/snapcraft/commands/remote.py
  +++ b/snapcraft/commands/remote.py
  @@ -19,6 +19,8 @@
   import argparse
   import os
   import textwrap
  +from enum import Enum
  +from typing import Optional
   
   from craft_cli import BaseCommand, emit
   from craft_cli.helptexts import HIDDEN
  @@ -27,7 +29,7 @@
   from snapcraft.errors import MaintenanceBase, SnapcraftError
   from snapcraft.legacy_cli import run_legacy
   from snapcraft.parts import yaml_utils
  -from snapcraft.utils import confirm_with_user
  +from snapcraft.utils import confirm_with_user, humanize_list
   from snapcraft_legacy.internal.remote_build.errors import AcceptPublicUploadError
   
   _CONFIRMATION_PROMPT = (
  @@ -36,6 +38,16 @@
   )
   
   
  +_STRATEGY_ENVVAR = "SNAPCRAFT_REMOTE_BUILD_STRATEGY"
  +
  +
  +class _Strategies(Enum):
  +    """Possible values of the build strategy."""
  +
  +    DISABLE_FALLBACK = "disable-fallback"
  +    FORCE_FALLBACK = "force-fallback"
  +
  +
   class RemoteBuildCommand(BaseCommand):
       """Command passthrough for the remote-build command."""
   
  @@ -89,6 +101,29 @@
               help="acknowledge that uploaded code will be publicly available.",
           )
   
  +    def _get_build_strategy(self) -> Optional[_Strategies]:
  +        """Get the build strategy from the envvar `SNAPCRAFT_REMOTE_BUILD_STRATEGY`.
  +
  +        :returns: The strategy or None.
  +
  +        :raises SnapcraftError: If the variable is set to an invalid value.
  +        """
  +        strategy = os.getenv(_STRATEGY_ENVVAR)
  +
  +        if not strategy:
  +            return None
  +
  +        try:
  +            return _Strategies(strategy)
  +        except ValueError as err:
  +            valid_strategies = humanize_list(
  +                (strategy.value for strategy in _Strategies), "and"
  +            )
  +            raise SnapcraftError(
  +                f"Unknown value {strategy!r} in environment variable "
  +                f"{_STRATEGY_ENVVAR!r}. Valid values are {valid_strategies}."
  +            ) from err
  +
       def _get_effective_base(self) -> str:
           """Get a valid effective base from the project's snapcraft.yaml.
   
  @@ -128,6 +163,25 @@
               run_legacy()
               return
   
  +        strategy = self._get_build_strategy()
  +
  +        if strategy == _Strategies.DISABLE_FALLBACK:
  +            emit.debug(
  +                f"Environment variable {_STRATEGY_ENVVAR!r} is "
  +                f"{_Strategies.DISABLE_FALLBACK.value!r} but running fallback "
  +                "remote-build because new remote-build is not available."
  +            )
  +            run_legacy()
  +            return
  +
  +        if strategy == _Strategies.FORCE_FALLBACK:
  +            emit.debug(
  +                "Running fallback remote-build because environment variable "
  +                f"{_STRATEGY_ENVVAR!r} is {_Strategies.FORCE_FALLBACK.value!r}."
  +            )
  +            run_legacy()
  +            return
  +
           emit.debug("Running fallback remote-build.")
           run_legacy()
  
verification_info: {"golden_diff": "diff --git a/snapcraft/commands/remote.py b/snapcraft/commands/remote.py\n--- a/snapcraft/commands/remote.py\n+++ b/snapcraft/commands/remote.py\n@@ -19,6 +19,8 @@\n import argparse\n import os\n import textwrap\n+from enum import Enum\n+from typing import Optional\n \n from craft_cli import BaseCommand, emit\n from craft_cli.helptexts import HIDDEN\n@@ -27,7 +29,7 @@\n from snapcraft.errors import MaintenanceBase, SnapcraftError\n from snapcraft.legacy_cli import run_legacy\n from snapcraft.parts import yaml_utils\n-from snapcraft.utils import confirm_with_user\n+from snapcraft.utils import confirm_with_user, humanize_list\n from snapcraft_legacy.internal.remote_build.errors import AcceptPublicUploadError\n \n _CONFIRMATION_PROMPT = (\n@@ -36,6 +38,16 @@\n )\n \n \n+_STRATEGY_ENVVAR = \"SNAPCRAFT_REMOTE_BUILD_STRATEGY\"\n+\n+\n+class _Strategies(Enum):\n+    \"\"\"Possible values of the build strategy.\"\"\"\n+\n+    DISABLE_FALLBACK = \"disable-fallback\"\n+    FORCE_FALLBACK = \"force-fallback\"\n+\n+\n class RemoteBuildCommand(BaseCommand):\n     \"\"\"Command passthrough for the remote-build command.\"\"\"\n \n@@ -89,6 +101,29 @@\n             help=\"acknowledge that uploaded code will be publicly available.\",\n         )\n \n+    def _get_build_strategy(self) -> Optional[_Strategies]:\n+        \"\"\"Get the build strategy from the envvar `SNAPCRAFT_REMOTE_BUILD_STRATEGY`.\n+\n+        :returns: The strategy or None.\n+\n+        :raises SnapcraftError: If the variable is set to an invalid value.\n+        \"\"\"\n+        strategy = os.getenv(_STRATEGY_ENVVAR)\n+\n+        if not strategy:\n+            return None\n+\n+        try:\n+            return _Strategies(strategy)\n+        except ValueError as err:\n+            valid_strategies = humanize_list(\n+                (strategy.value for strategy in _Strategies), \"and\"\n+            )\n+            raise SnapcraftError(\n+                f\"Unknown value {strategy!r} in environment variable \"\n+                f\"{_STRATEGY_ENVVAR!r}. Valid values are {valid_strategies}.\"\n+            ) from err\n+\n     def _get_effective_base(self) -> str:\n         \"\"\"Get a valid effective base from the project's snapcraft.yaml.\n \n@@ -128,6 +163,25 @@\n             run_legacy()\n             return\n \n+        strategy = self._get_build_strategy()\n+\n+        if strategy == _Strategies.DISABLE_FALLBACK:\n+            emit.debug(\n+                f\"Environment variable {_STRATEGY_ENVVAR!r} is \"\n+                f\"{_Strategies.DISABLE_FALLBACK.value!r} but running fallback \"\n+                \"remote-build because new remote-build is not available.\"\n+            )\n+            run_legacy()\n+            return\n+\n+        if strategy == _Strategies.FORCE_FALLBACK:\n+            emit.debug(\n+                \"Running fallback remote-build because environment variable \"\n+                f\"{_STRATEGY_ENVVAR!r} is {_Strategies.FORCE_FALLBACK.value!r}.\"\n+            )\n+            run_legacy()\n+            return\n+\n         emit.debug(\"Running fallback remote-build.\")\n         run_legacy()\n", "issue": "remote-build: add control logic for envvar `SNAPCRAFT_REMOTE_BUILD_STRATEGY`\n### What needs to get done\n\nThis adds control logic to determine whether to execute the new or legacy remote-build code.\r\n\r\nThere are four possibilities with `SNAPCRAFT_REMOTE_BUILD_STRATEGY`:\r\n\r\n- `disable-fallback` - use new remote-build code\r\n- `force-fallback` - use legacy remote-build code\r\n- unset - continue on to next control logic step\r\n- unknown - raise an error\r\n\r\n![image](https://github.com/snapcore/snapcraft/assets/60674096/298ea1f2-96b2-4337-b2cc-3d3c3e2848b8)\r\n\n\n### Why it needs to get done\n\nremote-build needs to be migrated because it does not leverage the new craft libraries, has issues with building core22 snaps, and has issues related to how the local project is bundled.\n", "before_files": [{"content": "# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-\n#\n# Copyright 2022-2023 Canonical Ltd.\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License version 3 as\n# published by the Free Software Foundation.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Snapcraft remote build command.\"\"\"\n\nimport argparse\nimport os\nimport textwrap\n\nfrom craft_cli import BaseCommand, emit\nfrom craft_cli.helptexts import HIDDEN\nfrom overrides import overrides\n\nfrom snapcraft.errors import MaintenanceBase, SnapcraftError\nfrom snapcraft.legacy_cli import run_legacy\nfrom snapcraft.parts import yaml_utils\nfrom snapcraft.utils import confirm_with_user\nfrom snapcraft_legacy.internal.remote_build.errors import AcceptPublicUploadError\n\n_CONFIRMATION_PROMPT = (\n    \"All data sent to remote builders will be publicly available. \"\n    \"Are you sure you want to continue?\"\n)\n\n\nclass RemoteBuildCommand(BaseCommand):\n    \"\"\"Command passthrough for the remote-build command.\"\"\"\n\n    name = \"remote-build\"\n    help_msg = \"Dispatch a snap for remote build\"\n    overview = textwrap.dedent(\n        \"\"\"\n        Command remote-build sends the current project to be built\n        remotely.  After the build is complete, packages for each\n        architecture are retrieved and will be available in the\n        local filesystem.\n\n        If not specified in the snapcraft.yaml file, the list of\n        architectures to build can be set using the --build-on option.\n        If both are specified, an error will occur.\n\n        Interrupted remote builds can be resumed using the --recover\n        option, followed by the build number informed when the remote\n        build was originally dispatched. The current state of the\n        remote build for each architecture can be checked using the\n        --status option.\"\"\"\n    )\n\n    @overrides\n    def fill_parser(self, parser: argparse.ArgumentParser) -> None:\n        parser.add_argument(\n            \"--recover\", action=\"store_true\", help=\"recover an interrupted build\"\n        )\n        parser.add_argument(\n            \"--status\", action=\"store_true\", help=\"display remote build status\"\n        )\n        parser_target = parser.add_mutually_exclusive_group()\n        parser_target.add_argument(\n            \"--build-on\",\n            metavar=\"arch\",\n            nargs=\"+\",\n            help=HIDDEN,\n        )\n        parser_target.add_argument(\n            \"--build-for\",\n            metavar=\"arch\",\n            nargs=\"+\",\n            help=\"architecture to build for\",\n        )\n        parser.add_argument(\n            \"--build-id\", metavar=\"build-id\", help=\"specific build id to retrieve\"\n        )\n        parser.add_argument(\n            \"--launchpad-accept-public-upload\",\n            action=\"store_true\",\n            help=\"acknowledge that uploaded code will be publicly available.\",\n        )\n\n    def _get_effective_base(self) -> str:\n        \"\"\"Get a valid effective base from the project's snapcraft.yaml.\n\n        :returns: The project's effective base.\n\n        :raises SnapcraftError: If the base is unknown or missing or if the\n        snapcraft.yaml cannot be loaded.\n        :raises MaintenanceBase: If the base is not supported\n        \"\"\"\n        snapcraft_yaml = yaml_utils.get_snap_project().project_file\n\n        with open(snapcraft_yaml, encoding=\"utf-8\") as file:\n            base = yaml_utils.get_base(file)\n\n        if base is None:\n            raise SnapcraftError(\n                f\"Could not determine base from {str(snapcraft_yaml)!r}.\"\n            )\n\n        emit.debug(f\"Got base {base!r} from {str(snapcraft_yaml)!r}.\")\n\n        if base in yaml_utils.ESM_BASES:\n            raise MaintenanceBase(base)\n\n        if base not in yaml_utils.BASES:\n            raise SnapcraftError(f\"Unknown base {base!r} in {str(snapcraft_yaml)!r}.\")\n\n        return base\n\n    def _run_remote_build(self, base: str) -> None:\n        # bases newer than core22 must use the new remote-build\n        if base in yaml_utils.CURRENT_BASES - {\"core22\"}:\n            emit.debug(\n                \"Using fallback remote-build because new remote-build is not available.\"\n            )\n            # TODO: use new remote-build code (#4323)\n            run_legacy()\n            return\n\n        emit.debug(\"Running fallback remote-build.\")\n        run_legacy()\n\n    @overrides\n    def run(self, parsed_args) -> None:\n        if os.getenv(\"SUDO_USER\") and os.geteuid() == 0:\n            emit.message(\n                \"Running with 'sudo' may cause permission errors and is discouraged.\"\n            )\n\n        emit.message(\n            \"snapcraft remote-build is experimental and is subject to change \"\n            \"- use with caution.\"\n        )\n\n        if parsed_args.build_on:\n            emit.message(\"Use --build-for instead of --build-on\")\n            parsed_args.build_for = parsed_args.build_on\n\n        if not parsed_args.launchpad_accept_public_upload and not confirm_with_user(\n            _CONFIRMATION_PROMPT\n        ):\n            raise AcceptPublicUploadError()\n\n        base = self._get_effective_base()\n        self._run_remote_build(base)\n", "path": "snapcraft/commands/remote.py"}], "after_files": [{"content": "# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-\n#\n# Copyright 2022-2023 Canonical Ltd.\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License version 3 as\n# published by the Free Software Foundation.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Snapcraft remote build command.\"\"\"\n\nimport argparse\nimport os\nimport textwrap\nfrom enum import Enum\nfrom typing import Optional\n\nfrom craft_cli import BaseCommand, emit\nfrom craft_cli.helptexts import HIDDEN\nfrom overrides import overrides\n\nfrom snapcraft.errors import MaintenanceBase, SnapcraftError\nfrom snapcraft.legacy_cli import run_legacy\nfrom snapcraft.parts import yaml_utils\nfrom snapcraft.utils import confirm_with_user, humanize_list\nfrom snapcraft_legacy.internal.remote_build.errors import AcceptPublicUploadError\n\n_CONFIRMATION_PROMPT = (\n    \"All data sent to remote builders will be publicly available. \"\n    \"Are you sure you want to continue?\"\n)\n\n\n_STRATEGY_ENVVAR = \"SNAPCRAFT_REMOTE_BUILD_STRATEGY\"\n\n\nclass _Strategies(Enum):\n    \"\"\"Possible values of the build strategy.\"\"\"\n\n    DISABLE_FALLBACK = \"disable-fallback\"\n    FORCE_FALLBACK = \"force-fallback\"\n\n\nclass RemoteBuildCommand(BaseCommand):\n    \"\"\"Command passthrough for the remote-build command.\"\"\"\n\n    name = \"remote-build\"\n    help_msg = \"Dispatch a snap for remote build\"\n    overview = textwrap.dedent(\n        \"\"\"\n        Command remote-build sends the current project to be built\n        remotely.  After the build is complete, packages for each\n        architecture are retrieved and will be available in the\n        local filesystem.\n\n        If not specified in the snapcraft.yaml file, the list of\n        architectures to build can be set using the --build-on option.\n        If both are specified, an error will occur.\n\n        Interrupted remote builds can be resumed using the --recover\n        option, followed by the build number informed when the remote\n        build was originally dispatched. The current state of the\n        remote build for each architecture can be checked using the\n        --status option.\"\"\"\n    )\n\n    @overrides\n    def fill_parser(self, parser: argparse.ArgumentParser) -> None:\n        parser.add_argument(\n            \"--recover\", action=\"store_true\", help=\"recover an interrupted build\"\n        )\n        parser.add_argument(\n            \"--status\", action=\"store_true\", help=\"display remote build status\"\n        )\n        parser_target = parser.add_mutually_exclusive_group()\n        parser_target.add_argument(\n            \"--build-on\",\n            metavar=\"arch\",\n            nargs=\"+\",\n            help=HIDDEN,\n        )\n        parser_target.add_argument(\n            \"--build-for\",\n            metavar=\"arch\",\n            nargs=\"+\",\n            help=\"architecture to build for\",\n        )\n        parser.add_argument(\n            \"--build-id\", metavar=\"build-id\", help=\"specific build id to retrieve\"\n        )\n        parser.add_argument(\n            \"--launchpad-accept-public-upload\",\n            action=\"store_true\",\n            help=\"acknowledge that uploaded code will be publicly available.\",\n        )\n\n    def _get_build_strategy(self) -> Optional[_Strategies]:\n        \"\"\"Get the build strategy from the envvar `SNAPCRAFT_REMOTE_BUILD_STRATEGY`.\n\n        :returns: The strategy or None.\n\n        :raises SnapcraftError: If the variable is set to an invalid value.\n        \"\"\"\n        strategy = os.getenv(_STRATEGY_ENVVAR)\n\n        if not strategy:\n            return None\n\n        try:\n            return _Strategies(strategy)\n        except ValueError as err:\n            valid_strategies = humanize_list(\n                (strategy.value for strategy in _Strategies), \"and\"\n            )\n            raise SnapcraftError(\n                f\"Unknown value {strategy!r} in environment variable \"\n                f\"{_STRATEGY_ENVVAR!r}. Valid values are {valid_strategies}.\"\n            ) from err\n\n    def _get_effective_base(self) -> str:\n        \"\"\"Get a valid effective base from the project's snapcraft.yaml.\n\n        :returns: The project's effective base.\n\n        :raises SnapcraftError: If the base is unknown or missing or if the\n        snapcraft.yaml cannot be loaded.\n        :raises MaintenanceBase: If the base is not supported\n        \"\"\"\n        snapcraft_yaml = yaml_utils.get_snap_project().project_file\n\n        with open(snapcraft_yaml, encoding=\"utf-8\") as file:\n            base = yaml_utils.get_base(file)\n\n        if base is None:\n            raise SnapcraftError(\n                f\"Could not determine base from {str(snapcraft_yaml)!r}.\"\n            )\n\n        emit.debug(f\"Got base {base!r} from {str(snapcraft_yaml)!r}.\")\n\n        if base in yaml_utils.ESM_BASES:\n            raise MaintenanceBase(base)\n\n        if base not in yaml_utils.BASES:\n            raise SnapcraftError(f\"Unknown base {base!r} in {str(snapcraft_yaml)!r}.\")\n\n        return base\n\n    def _run_remote_build(self, base: str) -> None:\n        # bases newer than core22 must use the new remote-build\n        if base in yaml_utils.CURRENT_BASES - {\"core22\"}:\n            emit.debug(\n                \"Using fallback remote-build because new remote-build is not available.\"\n            )\n            # TODO: use new remote-build code (#4323)\n            run_legacy()\n            return\n\n        strategy = self._get_build_strategy()\n\n        if strategy == _Strategies.DISABLE_FALLBACK:\n            emit.debug(\n                f\"Environment variable {_STRATEGY_ENVVAR!r} is \"\n                f\"{_Strategies.DISABLE_FALLBACK.value!r} but running fallback \"\n                \"remote-build because new remote-build is not available.\"\n            )\n            run_legacy()\n            return\n\n        if strategy == _Strategies.FORCE_FALLBACK:\n            emit.debug(\n                \"Running fallback remote-build because environment variable \"\n                f\"{_STRATEGY_ENVVAR!r} is {_Strategies.FORCE_FALLBACK.value!r}.\"\n            )\n            run_legacy()\n            return\n\n        emit.debug(\"Running fallback remote-build.\")\n        run_legacy()\n\n    @overrides\n    def run(self, parsed_args) -> None:\n        if os.getenv(\"SUDO_USER\") and os.geteuid() == 0:\n            emit.message(\n                \"Running with 'sudo' may cause permission errors and is discouraged.\"\n            )\n\n        emit.message(\n            \"snapcraft remote-build is experimental and is subject to change \"\n            \"- use with caution.\"\n        )\n\n        if parsed_args.build_on:\n            emit.message(\"Use --build-for instead of --build-on\")\n            parsed_args.build_for = parsed_args.build_on\n\n        if not parsed_args.launchpad_accept_public_upload and not confirm_with_user(\n            _CONFIRMATION_PROMPT\n        ):\n            raise AcceptPublicUploadError()\n\n        base = self._get_effective_base()\n        self._run_remote_build(base)\n", "path": "snapcraft/commands/remote.py"}]}