Repository: canonical/snapcraft

Issue:
Regression in handling `remote-build`
### Bug Description

When using `snapcraft remote-build` to have Snapcraft dispatch a build to Launchpad for building the snapd snap, an erroneous message is received indicating that the version of snapcraft being used to initiate the build does not support snapd's base. While this is true, this fact should have no bearing on whether or not such a build can be sent to Launchpad -- the host's version of snapcraft is unrelated to the Launchpad build environment.

### To Reproduce

```
snap refresh --channel=latest/edge snapcraft
git clone https://github.com/snapcore/snapd
cd snapd
snapcraft remote-build --launchpad-accept-public-upload
```

### Environment

A variant of Ubuntu 23.04, testing with snapcraft latest/edge. Trying to build snapd from `HEAD`, setting `SNAPCRAFT_VERBOSITY_LEVEL=verbose; SNAPCRAFT_BUILD_INFO=1`.

Builds indicated above previously succeeded on earlier revisions of `snapcraft`, at least in June. Perhaps earlier.

### snapcraft.yaml

```shell
Not especially relevant in this instance, but for the curious:
https://github.com/snapcore/snapd/blob/master/build-aux/snap/snapcraft.yaml
```


### Relevant log output

```shell
When you run the reproduction steps above, you should meet the following message:


dilyn@Ares:~/work/canonical/snapd -> snapcraft remote-build --launchpad-accept-public-upload
Starting Snapcraft 7.5.2.post81+git97911564                                                                              
Logging execution to '/home/dilyn/.local/state/snapcraft/log/snapcraft-20230907-122433.257422.log'                       
snapcraft remote-build is experimental and is subject to change - use with caution.                                      
'core' is not supported on this version of Snapcraft.                                                                    
Recommended resolution: Install from or refresh to the '4.x' channel.                                                    
For more information, check out: https://snapcraft.io/docs/base-snaps                                                    
Full execution log: '/home/dilyn/.local/state/snapcraft/log/snapcraft-20230907-122433.257422.log' 
```
```


### Additional context

This behavior is not present on 7.x/* or latest/stable, which means it is some sort of regression 
between the 7.5.2 tag and 7.5.2.post81+git97911564.


Discussion:
After some research and deliberation, this seems appropriate, core is only supported on 4.x; Snapcraft 8 removes support for core and core18

Resolving patch:
diff --git a/snapcraft/commands/remote.py b/snapcraft/commands/remote.py
--- a/snapcraft/commands/remote.py
+++ b/snapcraft/commands/remote.py
@@ -135,7 +135,12 @@ def run(self, parsed_args: argparse.Namespace) -> None:
         self._snapcraft_yaml = yaml_utils.get_snap_project().project_file
         self._parsed_args = parsed_args
         # pylint: enable=attribute-defined-outside-init
-        base = self._get_effective_base()
+        try:
+            base = self._get_effective_base()
+        except MaintenanceBase as base_err:
+            base = base_err.base
+            emit.progress(_get_esm_warning_for_base(base), permanent=True)
+
         self._run_new_or_fallback_remote_build(base)
 
     def _run_new_or_fallback_remote_build(self, base: str) -> None:
@@ -279,3 +284,22 @@ def _get_effective_base(self) -> str:
             )
 
         return base
+
+
+def _get_esm_warning_for_base(base: str) -> str:
+    """Return a warning appropriate for the base under ESM."""
+    channel: Optional[str] = None
+    match base:
+        case "core":
+            channel = "4.x"
+            version = "4"
+        case "core18":
+            channel = "7.x"
+            version = "7"
+        case _:
+            raise RuntimeError(f"Unmatched base {base!r}")
+
+    return (
+        f"WARNING: base {base!r} was last supported on Snapcraft {version} available "
+        f"on the {channel!r} channel."
+    )
diff --git a/snapcraft/errors.py b/snapcraft/errors.py
--- a/snapcraft/errors.py
+++ b/snapcraft/errors.py
@@ -138,6 +138,7 @@ def __init__(self, base: str) -> None:
             resolution=resolution,
             docs_url="https://snapcraft.io/docs/base-snaps",
         )
+        self.base = base
 
 
 class StoreCredentialsUnauthorizedError(SnapcraftError):


Test patch:
diff --git a/tests/unit/commands/test_remote.py b/tests/unit/commands/test_remote.py
--- a/tests/unit/commands/test_remote.py
+++ b/tests/unit/commands/test_remote.py
@@ -220,7 +220,7 @@ def test_get_effective_base_with_build_base(
 
 
 @pytest.mark.usefixtures("mock_argv", "mock_confirm")
-@pytest.mark.parametrize("base", CURRENT_BASES | LEGACY_BASES)
+@pytest.mark.parametrize("base", CURRENT_BASES | LEGACY_BASES | ESM_BASES)
 def test_get_effective_base_type(
     base, snapcraft_yaml, mock_run_new_or_fallback_remote_build
 ):
@@ -255,15 +255,37 @@ def test_get_effective_base_none(capsys, snapcraft_yaml):
 
 
 @pytest.mark.usefixtures("mock_argv", "mock_confirm")
-@pytest.mark.parametrize("base", ESM_BASES)
-def test_get_effective_base_esm(base, capsys, snapcraft_yaml):
-    """Raise an error if an ESM base is used."""
-    snapcraft_yaml(base=base)
+def test_get_effective_base_core_esm_warning(
+    emitter, snapcraft_yaml, mock_run_new_or_fallback_remote_build
+):
+    """Warn if core, an ESM base, is used."""
+    snapcraft_yaml(base="core")
 
     cli.run()
 
-    _, err = capsys.readouterr()
-    assert f"{base!r} is not supported on this version of Snapcraft." in err
+    mock_run_new_or_fallback_remote_build.assert_called_once_with("core")
+    emitter.assert_progress(
+        "WARNING: base 'core' was last supported on Snapcraft 4 available on the "
+        "'4.x' channel.",
+        permanent=True,
+    )
+
+
+@pytest.mark.usefixtures("mock_argv", "mock_confirm")
+def test_get_effective_base_core18_esm_warning(
+    emitter, snapcraft_yaml, mock_run_new_or_fallback_remote_build
+):
+    """Warn if core18, an ESM base, is used."""
+    snapcraft_yaml(base="core18")
+
+    cli.run()
+
+    mock_run_new_or_fallback_remote_build.assert_called_once_with("core18")
+    emitter.assert_progress(
+        "WARNING: base 'core18' was last supported on Snapcraft 7 available on the "
+        "'7.x' channel.",
+        permanent=True,
+    )
 
 
 #######################