Repository: PrefectHQ/prefect

Issue:
Relative Path in prefect register CLI Not Being Converted to Absolute
## Opened from the [Prefect Public Slack Community](https://prefect.io/slack)

**philip.macmenamin**: Issue with new version of register.
Previously (and currently although I'm getting warnings) I was able to register flows with this:

```
prefect register flow --file flow.py --project proj_name
```

This works without issue, however the recommendation is to use the new format:

```
prefect register --project my-project -p myflows/
```


**philip.macmenamin**: this appears to work, however this workflow does not get triggered if I test via the web GUI for example.

**philip.macmenamin**: 
```
prefect register flow --file $MODULE_LOC/the_flow/flow.py --project the_proj
Warning: `prefect register flow` is deprecated, please transition to using `prefect register` instead.
Flow URL: <http://localhost:8080/default/flow/eb320e0d-18ab-426f-9929-1b880cf1fadb>
 └── ID: 4e9d85e9-39a0-4e72-a6fb-55941c12bac7
 └── Project: the_proj
 └── Labels: ['ip-172-20-3-23.ec2.internal']
```

(this works)

**philip.macmenamin**: 
```
prefect register --project the_proj -p the_flow/
Collecting flows...
Processing 'the_flow/flow.py':
  Building `Local` storage...
  Registering 'Flow_name'... Done
  └── ID: f9cfe0ca-848d-44b5-a739-566a5c77b387
  └── Version: 5
```

this doesn't, although it gives the impression it has.

**kevin701**: Hi <@U013XTS1ZT8>! I’ll try replicate this on my end. Thanks for the details

**kevin701**: The `prefect register --project the_proj -p the_flow/` worked for me. What Prefect version are you on?

**kevin701**: How many flows do you have in that folder?

**philip.macmenamin**: Prefect Version (0.14.19)
There is one file in that dir named flow.py, which contains a single flow.

**philip.macmenamin**: Just to be clear, the registration appears to work. The flow is created, gets an ID, is listed, and appears to be available to use. If I invoke it however it does not get triggered and eventually times out. If I use the old syntax there's no issue.

**kevin701**: Gotcha was able to replicate-ish. I noticed there was no label when registering that way, and the lack of a label makes the agent fail to pick it up. After fixing the labels, I get a `ModuleNotFoundError` . May open an issue.

**philip.macmenamin**: You're opening an issue Kevin? Or should I? Could you link here if you have opened one? :slightly_smiling_face:

**kevin701**: Hey I just spoke to the core team. This is known and I’ll be opening one and linking it

**kevin701**: <@ULVA73B9P> open “Relative Path in prefect register CLI Not Being Converted to Absolute”

Original thread can be found [here](https://prefect-community.slack.com/archives/CL09KU1K7/p1621619367293900?thread_ts=1621619367.293900&cid=CL09KU1K7).




Discussion:
We should convert these to absolute paths always

Resolving patch:
diff --git a/src/prefect/cli/build_register.py b/src/prefect/cli/build_register.py
--- a/src/prefect/cli/build_register.py
+++ b/src/prefect/cli/build_register.py
@@ -116,14 +116,17 @@ def expand_paths(paths: List[str]) -> List[str]:
 
 def load_flows_from_script(path: str) -> "List[prefect.Flow]":
     """Given a file path, load all flows found in the file"""
+    # We use abs_path for everything but logging (logging the original
+    # user-specified path provides a clearer message).
+    abs_path = os.path.abspath(path)
     # Temporarily add the flow's local directory to `sys.path` so that local
     # imports work. This ensures that `sys.path` is the same as it would be if
     # the flow script was run directly (i.e. `python path/to/flow.py`).
     orig_sys_path = sys.path.copy()
-    sys.path.insert(0, os.path.dirname(os.path.abspath(path)))
+    sys.path.insert(0, os.path.dirname(abs_path))
     try:
-        with prefect.context({"loading_flow": True, "local_script_path": path}):
-            namespace = runpy.run_path(path, run_name="<flow>")
+        with prefect.context({"loading_flow": True, "local_script_path": abs_path}):
+            namespace = runpy.run_path(abs_path, run_name="<flow>")
     except Exception as exc:
         click.secho(f"Error loading {path!r}:", fg="red")
         log_exception(exc, 2)
@@ -135,7 +138,7 @@ def load_flows_from_script(path: str) -> "List[prefect.Flow]":
     if flows:
         for f in flows:
             if f.storage is None:
-                f.storage = Local(path=path, stored_as_script=True)
+                f.storage = Local(path=abs_path, stored_as_script=True)
     return flows
 
 
@@ -300,14 +303,13 @@ def prepare_flows(flows: "List[FlowLike]", labels: List[str] = None) -> None:
                 flow.result = flow.storage.result
 
             # Add a `run_config` if not configured explicitly
-            # Also add any extra labels to the flow
-            if flow.run_config is None:
-                if flow.environment is not None:
-                    flow.environment.labels.update(labels)
-                else:
-                    flow.run_config = UniversalRun(labels=labels)
-            else:
-                flow.run_config.labels.update(labels)
+            if flow.run_config is None and flow.environment is None:
+                flow.run_config = UniversalRun()
+            # Add any extra labels to the flow (either specified via the CLI,
+            # or from the storage object).
+            obj = flow.run_config or flow.environment
+            obj.labels.update(labels)
+            obj.labels.update(flow.storage.labels)
 
             # Add the flow to storage
             flow.storage.add_flow(flow)


Test patch:
diff --git a/tests/cli/test_build_register.py b/tests/cli/test_build_register.py
--- a/tests/cli/test_build_register.py
+++ b/tests/cli/test_build_register.py
@@ -269,8 +269,19 @@ def test_register_serialized_flow(
         assert flow_version == exp_version
         assert is_new == is_new_version
 
-    def test_load_flows_from_script(self, tmpdir):
-        path = str(tmpdir.join("test.py"))
+    @pytest.mark.parametrize("relative", [False, True])
+    def test_load_flows_from_script(self, tmpdir, relative):
+        abs_path = str(tmpdir.join("test.py"))
+        if relative:
+            if os.name == "nt":
+                pytest.skip(
+                    "This test can fail on windows if the test file is on a different "
+                    "drive. This should have no effect during actual execution."
+                )
+            path = os.path.relpath(abs_path)
+        else:
+            path = abs_path
+
         source = textwrap.dedent(
             """
             from prefect import Flow
@@ -284,7 +295,7 @@ def test_load_flows_from_script(self, tmpdir):
             assert __name__ != "__main__"
             """
         )
-        with open(path, "w") as f:
+        with open(abs_path, "w") as f:
             f.write(source)
 
         tmpdir.join("my_prefect_helper_file.py").write("def helper():\n    pass")
@@ -292,9 +303,9 @@ def test_load_flows_from_script(self, tmpdir):
         flows = {f.name: f for f in load_flows_from_script(path)}
         assert len(flows) == 2
         assert isinstance(flows["f1"].storage, S3)
-        assert flows["f1"].storage.local_script_path == path
+        assert flows["f1"].storage.local_script_path == abs_path
         assert isinstance(flows["f2"].storage, Local)
-        assert flows["f2"].storage.path == path
+        assert flows["f2"].storage.path == abs_path
         assert flows["f2"].storage.stored_as_script
 
     def test_load_flows_from_script_error(self, tmpdir, capsys):
@@ -563,12 +574,18 @@ def test_register_cli(self, tmpdir, monkeypatch, mock_get_project_id, force, nam
         if not names:
             names = ["flow 1", "flow 2"]
 
+        storage_labels = Local().labels
+
         assert register_serialized_flow.call_count == len(names)
         for args, kwargs in register_serialized_flow.call_args_list:
             assert not args
             assert kwargs["project_id"] == "my-project-id"
             assert kwargs["serialized_flow"]["name"] in names
-            assert set(kwargs["serialized_flow"]["run_config"]["labels"]) == {"a", "b"}
+            assert set(kwargs["serialized_flow"]["run_config"]["labels"]) == {
+                "a",
+                "b",
+                *storage_labels,
+            }
             assert kwargs["force"] == force
 
         # Bulk of the output is tested elsewhere, only a few smoketests here
@@ -741,7 +758,8 @@ def test_build(self, tmpdir, filter_names, update):
         written_names = [f["name"] for f in out["flows"]]
         assert written_names == exp_names
 
-        assert flow2["run_config"]["labels"] == ["a", "b", "new"]
+        storage_labels = Local().labels
+        assert set(flow2["run_config"]["labels"]) == {"a", "b", "new", *storage_labels}
         assert flow2["run_config"]["type"] == "LocalRun"
 
         build_logs = "\n".join(