Repository: canonical/snapcraft

Issue:
regression: remote-build fails when project is not in a git directory
### Bug Description

In snapcraft 8.0.1, remote-build is now failing if the project is not in a git repository.  

This failure is occurring in the weekly spread tests: https://github.com/snapcore/snapcraft/actions/runs/7469897740/job/20327823910#step:5:36

[This check](https://github.com/snapcore/snapcraft/blob/ffbdabf53de21606d9d12e15f16d4bca5e4423a5/snapcraft/remote/git.py#L88) is causing snapcraft to fail and preempts all the code that automatically initializes a git repository in the temporary work directory.

### To Reproduce

```
mkdir tmp
pushd tmp
snapcraft init
snapcraft remote-build
popd
```
### Environment

n/a

### snapcraft.yaml

```shell
n/a
```


### Relevant log output

```shell
2024-01-10 03:26:29.754 remote-build error: The Git repository is invalid for remote build.
Could not find a git repository in '.'
```


### Additional context

_No response_


Discussion:
Per Sergio, this is an intentional decision for the following reason:
If a user builds destructively locally and is not in a git repo, snapcraft will add the `parts`, `stage`, and `prime` directory to the git repo it initializes.  This could cause the remote-builder to re-use these directories instead of doing a clean build.

The path forward is to adjust the failing spread test and improve the error message.

Resolving patch:
diff --git a/snapcraft/cli.py b/snapcraft/cli.py
--- a/snapcraft/cli.py
+++ b/snapcraft/cli.py
@@ -301,7 +301,12 @@ def run():  # noqa: C901 (complex-structure)
         emit.error(craft_cli.errors.CraftError(f"linter error: {err}"))
         retcode = err.exit_code
     except RemoteBuildError as err:
-        emit.error(craft_cli.errors.CraftError(f"remote-build error: {err}"))
+        emit.error(
+            craft_cli.errors.CraftError(
+                message=f"remote-build error: {err}",
+                docs_url="https://snapcraft.io/docs/remote-build",
+            )
+        )
         retcode = 1
     except errors.SnapcraftError as err:
         _emit_error(err)
diff --git a/snapcraft/remote/errors.py b/snapcraft/remote/errors.py
--- a/snapcraft/remote/errors.py
+++ b/snapcraft/remote/errors.py
@@ -107,7 +107,6 @@ def __init__(self) -> None:
 class RemoteBuildFailedError(RemoteBuildError):
     """Remote build failed.
 
-    :param brief: Brief description of error.
     :param details: Detailed information.
     """
 
@@ -120,7 +119,6 @@ def __init__(self, details: str) -> None:
 class RemoteBuildInvalidGitRepoError(RemoteBuildError):
     """The Git repository is invalid for remote build.
 
-    :param brief: Brief description of error.
     :param details: Detailed information.
     """
 
diff --git a/snapcraft/remote/git.py b/snapcraft/remote/git.py
--- a/snapcraft/remote/git.py
+++ b/snapcraft/remote/git.py
@@ -103,12 +103,14 @@ def check_git_repo_for_remote_build(path: Path) -> None:
 
     if git_type == GitType.INVALID:
         raise RemoteBuildInvalidGitRepoError(
-            f"Could not find a git repository in {str(path)!r}"
+            f"Could not find a git repository in {str(path.absolute())!r}. "
+            "The project must be in the top-level of a git repository."
         )
 
     if git_type == GitType.SHALLOW:
         raise RemoteBuildInvalidGitRepoError(
-            "Remote build for shallow cloned git repos are no longer supported"
+            "Remote builds are not supported for projects in shallowly cloned "
+            "git repositories."
         )
 
 


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
@@ -621,7 +621,10 @@ def test_run_in_shallow_repo_unsupported(capsys, new_dir):
     assert ret != 0
     _, err = capsys.readouterr()
 
-    assert "Remote build for shallow cloned git repos are no longer supported" in err
+    assert (
+        "Remote builds are not supported for projects in shallowly cloned "
+        "git repositories."
+    ) in err
 
 
 ######################
diff --git a/tests/unit/remote/test_git.py b/tests/unit/remote/test_git.py
--- a/tests/unit/remote/test_git.py
+++ b/tests/unit/remote/test_git.py
@@ -538,7 +538,11 @@ def test_push_url_push_error(new_dir):
 def test_check_git_repo_for_remote_build_invalid(new_dir):
     """Check if directory is an invalid repo."""
     with pytest.raises(
-        RemoteBuildInvalidGitRepoError, match="Could not find a git repository in"
+        RemoteBuildInvalidGitRepoError,
+        match=(
+            f"Could not find a git repository in {str(new_dir.absolute())!r}. "
+            "The project must be in the top-level of a git repository."
+        ),
     ):
         check_git_repo_for_remote_build(new_dir)
 
@@ -584,6 +588,9 @@ def test_check_git_repo_for_remote_build_shallow(new_dir):
 
     with pytest.raises(
         RemoteBuildInvalidGitRepoError,
-        match="Remote build for shallow cloned git repos are no longer supported",
+        match=(
+            "Remote builds are not supported for projects in shallowly cloned "
+            "git repositories."
+        ),
     ):
         check_git_repo_for_remote_build(git_shallow_path)