Repository: PrefectHQ/prefect

Issue:
Improper error handling around missing .prefect folder: Non-informative `AttributeError`
### First check

- [X] I added a descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Prefect documentation for this issue.
- [X] I checked that this issue is related to Prefect and not one of its dependencies.

### Bug summary

When using `prefect deploy`, if one doesn't have a `.prefect` folder, a non-informative `AttributeError` is thrown and the CLI crashes.

This notably happens around this point: https://github.com/PrefectHQ/prefect/blob/a3d5713665b04bfa0ff67c97394a8c831c698a25/src/prefect/cli/deploy.py#LL423C30-L423C30

`_register_single_deploy` calls `register_flow` which raises `FileNotFoundError` if the `.prefect` directory was not found. **It does not return the flow if it raises an exception**.

This means that slightly later, when `_register_single_deploy` attempts to `flow_name = flow.name`, the non-informative `AttributeError` is thrown (`AttributeError: 'NoneType' object has no attribute 'name'`). 

This is probably more severe because the latest version stopped making the directory with `prefect init` by default.

### Reproduction

```python3
Run `prefect deploy` when not having a `.prefect` directory.
```


### Error

```python3
Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/prefect/cli/_utilities.py", line 41, in wrapper
    return fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/utilities/asyncutils.py", line 255, in coroutine_wrapper
    return call()
           ^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/_internal/concurrency/calls.py", line 383, in __call__
    return self.result()
           ^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/_internal/concurrency/calls.py", line 283, in result
    return self.future.result(timeout=timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/_internal/concurrency/calls.py", line 169, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/site-packages/prefect/_internal/concurrency/calls.py", line 346, in _run_async
    result = await coro
             ^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/cli/deploy.py", line 271, in deploy
    await _run_multi_deploy(
  File "/usr/lib/python3.11/site-packages/prefect/cli/deploy.py", line 689, in _run_multi_deploy
    await _run_single_deploy(base_deploy, project)
  File "/usr/lib/python3.11/site-packages/prefect/client/utilities.py", line 51, in with_injected_client
    return await fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/prefect/cli/deploy.py", line 423, in _run_single_deploy
    flow_name = flow.name
                ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'name'
```


### Versions

```Text
2.10.14
```


### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/src/prefect/cli/deploy.py b/src/prefect/cli/deploy.py
--- a/src/prefect/cli/deploy.py
+++ b/src/prefect/cli/deploy.py
@@ -420,6 +420,9 @@ async def _run_single_deploy(
                     "Could not find .prefect directory. Flow entrypoint will not be"
                     " registered."
                 )
+            flow = await run_sync_in_worker_thread(
+                load_flow_from_entrypoint, entrypoint
+            )
         flow_name = flow.name
     elif flow_name:
         app.console.print(
@@ -435,7 +438,7 @@ async def _run_single_deploy(
         prefect_dir = find_prefect_directory()
         if not prefect_dir:
             raise ValueError(
-                "No .prefect directory could be found - run [yellow]`prefect project"
+                "No .prefect directory could be found - run [yellow]`prefect"
                 " init`[/yellow] to create one."
             )
         if not (prefect_dir / "flows.json").exists():


Test patch:
diff --git a/tests/cli/test_deploy.py b/tests/cli/test_deploy.py
--- a/tests/cli/test_deploy.py
+++ b/tests/cli/test_deploy.py
@@ -1324,6 +1324,20 @@ async def test_migrate_from_deployment_yaml_to_prefect_yaml(self, work_pool):
         assert config["deployments"][0]["schedule"] == {"interval": 3600}
         assert config["deployments"][0]["work_pool"]["name"] == work_pool.name
 
+    @pytest.mark.usefixtures("project_dir")
+    async def test_deploy_with_entrypoint_does_not_fail_with_missing_prefect_folder(
+        self, work_pool
+    ):
+        Path(".prefect").rmdir()
+        await run_sync_in_worker_thread(
+            invoke_and_assert,
+            command=f"deploy ./flows/hello.py:my_flow -n test-name -p {work_pool.name}",
+            expected_code=0,
+            expected_output_contains=[
+                "Deployment 'An important name/test-name' successfully created"
+            ],
+        )
+
 
 class TestSchedules:
     @pytest.mark.usefixtures("project_dir")