We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Undefined name: make_env in ./server/src/prefect_server/cli/dev.py
## Description
*A clear description of the bug*

An _undefined name_ like #2235 and #1199
https://github.com/PrefectHQ/prefect/blob/master/server/src/prefect_server/cli/dev.py#L88

`make_env` is an undefined name in this context which will raise a `NameError` at runtime.  Should this be `make_dev_env()` defined on line 36 or is `from prefect.cli.server import make_env` the right solution?

[flake8](http://flake8.pycqa.org) testing of https://github.com/PrefectHQ/prefect on Python 3.8.3

$ __flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics__
```
./server/src/prefect_server/cli/dev.py:88:11: F821 undefined name 'make_env'
    env = make_env()
          ^
1     F821 undefined name 'make_env'
1
```
https://flake8.pycqa.org/en/latest/user/error-codes.html

On the flake8 test selection, this PR does _not_ focus on "_style violations_" (the majority of flake8 error codes that [__psf/black__](https://github.com/psf/black) can autocorrect).  Instead these tests are focus on runtime safety and correctness:
* E9 tests are about Python syntax errors usually raised because flake8 can not build an Abstract Syntax Tree (AST).  Often these issues are a sign of unused code or code that has not been ported to Python 3.  These would be compile-time errors in a compiled language but in a dynamic language like Python they result in the script halting/crashing on the user.
* F63 tests are usually about the confusion between identity and equality in Python.  Use ==/!= to compare str, bytes, and int literals is the classic case.  These are areas where __a == b__ is True but __a is b__ is False (or vice versa).  Python >= 3.8 will raise SyntaxWarnings on these instances.
* F7 tests logic errors and syntax errors in type hints
* F82 tests are almost always _undefined names_ which are usually a sign of a typo, missing imports, or code that has not been ported to Python 3.  These also would be compile-time errors in a compiled language but in Python a __NameError__ is raised which will halt/crash the script on the user.




## Expected Behavior
*What did you expect to happen instead?*




## Reproduction
*A minimal example that exhibits the behavior.*




## Environment
*Any additional information about your environment*

*Optionally run `prefect diagnostics` from the command line and paste the information here*


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `server/src/prefect_server/cli/dev.py`
Content:
```
1 # Licensed under the Prefect Community License, available at
2 # https://www.prefect.io/legal/prefect-community-license
3 
4 
5 import glob
6 import os
7 import shutil
8 import signal
9 import subprocess
10 import time
11 from pathlib import Path
12 
13 import click
14 
15 import prefect
16 import prefect_server
17 from prefect_server import api, cli, config, utilities
18 from prefect_server.database import models
19 
20 
21 @click.group()
22 def dev():
23     """
24     Commands for developing Server
25 
26     \b
27     Usage:
28         $ prefect-server ...
29     
30     \b
31     Arguments:
32         build   builds prefect server, ui, apollo from source
33     """
34 
35 
36 def make_dev_env(fname=None):
37 
38     # replace localhost with postgres to use docker-compose dns
39     PREFECT_ENV = dict(
40         DB_CONNECTION_URL=config.database.connection_url.replace(
41             "localhost", "postgres"
42         )
43     )
44 
45     APOLLO_ENV = dict(
46         HASURA_API_URL=f"http://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql",
47         HASURA_WS_URL=f"ws://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql",
48         PREFECT_API_URL=f"http://{config.services.graphql.host}:{config.services.graphql.port}{config.services.graphql.path}",
49         PREFECT_API_HEALTH_URL=f"http://{config.services.graphql.host}:{config.services.graphql.port}/health",
50     )
51 
52     POSTGRES_ENV = dict(
53         POSTGRES_USER=config.database.username,
54         POSTGRES_PASSWORD=config.database.password,
55         POSTGRES_DB=config.database.name,
56     )
57 
58     HASURA_ENV = dict()
59 
60     UI_ENV = dict(GRAPHQL_URL=config.services.ui.graphql_url)
61 
62     ENV = os.environ.copy()
63     ENV.update(**PREFECT_ENV, **APOLLO_ENV, **POSTGRES_ENV, **UI_ENV, **HASURA_ENV)
64 
65     if fname is not None:
66         list_of_pairs = [
67             f"{k}={repr(v)}" if "\n" in v else f"{k}={v}" for k, v in ENV.items()
68         ]
69         with open(fname, "w") as f:
70             f.write("\n".join(list_of_pairs))
71     return ENV.copy()
72 
73 
74 @dev.command(hidden=True)
75 @click.option(
76     "--version",
77     "-v",
78     help="The server image versions to build (for example, '0.10.0' or 'master')",
79     # TODO: update this default to use prefect.__version__ logic
80     default="latest",
81 )
82 def build(version):
83     """
84     foobar
85     """
86     docker_dir = Path(prefect_server.__file__).parents[2] / "docker"
87 
88     env = make_env()
89 
90     if "PREFECT_SERVER_TAG" not in env:
91         env.update(PREFECT_SERVER_TAG=version)
92 
93     proc = None
94     cmd = ["docker-compose", "build"]
95     proc = subprocess.Popen(cmd, cwd=docker_dir, env=env)
96 
97 
98 @dev.command()
99 @click.option("--tag", "-t", help="The server image/tag to use", default="latest")
100 @click.option(
101     "--skip-pull",
102     help="Pass this flag to skip pulling new images (if available)",
103     is_flag=True,
104 )
105 def infrastructure(tag, skip_pull):
106     """
107     This command:
108         - starts a PostgreSQL database
109         - starts Hasura
110     """
111     docker_dir = Path(prefect_server.__file__).parents[2] / "docker"
112 
113     env = make_dev_env()
114 
115     proc = None
116     try:
117         if not skip_pull:
118             subprocess.check_call(
119                 ["docker-compose", "pull", "postgres", "hasura"],
120                 cwd=docker_dir,
121                 env=env,
122             )
123         proc = subprocess.Popen(
124             ["docker-compose", "up", "postgres", "hasura"], cwd=docker_dir, env=env
125         )
126 
127         # if not initialize, just run hasura (and dependencies), which will skip the init step
128         while True:
129             time.sleep(0.5)
130     except:
131         click.secho(
132             "Exception caught; killing services (press ctrl-C to force)",
133             fg="white",
134             bg="red",
135         )
136         subprocess.check_output(["docker-compose", "down"], cwd=docker_dir, env=env)
137         if proc:
138             proc.kill()
139         raise
140 
141 
142 @dev.command()
143 @click.option("--skip-ui", help="Pass this flag to skip UI dependencies", is_flag=True)
144 @click.option(
145     "--skip-apollo", help="Pass this flag to skip Apollo dependencies", is_flag=True
146 )
147 def install_dependencies(skip_ui, skip_apollo):
148     """
149     This command:
150         - installs Apollo dependencies
151         - install UI dependencies
152     """
153     if not skip_ui:
154         click.secho("Installing UI dependencies...")
155         time.sleep(0.5)
156         install_ui_dependencies()
157 
158     if not skip_apollo:
159         click.secho("Installing Apollo dependencies...")
160         time.sleep(0.5)
161         install_apollo_dependencies()
162 
163     if skip_ui and skip_apollo:
164         click.secho("No dependencies were installed because all were skipped.")
165 
166 
167 def install_apollo_dependencies():
168     apollo_dir = Path(prefect_server.__file__).parents[2] / "services" / "apollo"
169 
170     proc = None
171     try:
172         proc = subprocess.check_call(["npm", "install"], cwd=apollo_dir)
173         click.secho("Apollo dependencies installed! 🚀🚀🚀")
174     except:
175         click.secho(
176             "Exception caught while installing Apollo dependencies.",
177             fg="white",
178             bg="red",
179         )
180         if proc:
181             proc.kill()
182         raise
183 
184 
185 def install_ui_dependencies():
186     ui_dir = Path(prefect_server.__file__).parents[2] / "services" / "ui"
187 
188     proc = None
189     try:
190         proc = subprocess.check_call(["npm", "install"], cwd=ui_dir)
191         click.secho("UI dependencies installed! 🕹🕹🕹")
192     except:
193         click.secho(
194             "Exception caught while installing UI dependencies.", fg="white", bg="red"
195         )
196         if proc:
197             proc.kill()
198         raise
199 
200 
201 def is_process_group_empty(pgid: int):
202     proc = subprocess.Popen(
203         ["pgrep", "-g", str(pgid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
204     )
205     proc.wait()
206     return proc.returncode != 0
207 
208 
209 def kill_process_group(proc, timeout: int = 3):
210     try:
211         pgid = os.getpgid(proc.pid)
212         os.killpg(pgid, signal.SIGTERM)
213         proc.terminate()
214 
215         for _ in range(timeout):
216             if is_process_group_empty(pgid):
217                 return
218             click.secho("Waiting for process group to exit...", fg="white", bg="blue")
219             time.sleep(1)
220 
221         click.secho("Timeout while shutting down, killing!", fg="white", bg="red")
222         os.killpg(pgid, signal.SIGKILL)
223         proc.kill()
224     except Exception as exc:
225         click.secho(exc)
226 
227 
228 @dev.command()
229 @click.option(
230     "--include", "-i", help="A comma-seperated list of serivces that should be run"
231 )
232 @click.option(
233     "--exclude", "-e", help="A comma-seperated list of services that should not be run"
234 )
235 def services(include, exclude):
236     """
237     This command starts services
238     """
239 
240     all_services = ["graphql", "scheduler", "apollo", "ui"]
241     if not include:
242         include = all_services
243     else:
244         include = include.split(",")
245     if not exclude:
246         exclude = ""
247     run_services = sorted(set(include).difference(exclude.split(",")))
248 
249     click.secho(
250         f"\n\nStarting Prefect Server services: {' '.join(run_services)}\n\n",
251         fg="green",
252     )
253 
254     procs = []
255     for service in run_services:
256         procs.append(
257             subprocess.Popen(
258                 ["prefect-server", "services", service],
259                 env=make_dev_env(),
260                 preexec_fn=os.setsid,
261             )
262         )
263 
264     try:
265         while True:
266             time.sleep(1)
267     except:
268         click.secho("Exception caught; shutting down!", fg="white", bg="red")
269         for proc in procs:
270             kill_process_group(proc)
271 
272 
273 def config_to_dict(config):
274     if isinstance(config, (list, tuple, set)):
275         return type(config)([config_to_dict(d) for d in config])
276     elif isinstance(config, prefect.configuration.Config):
277         return dict({k: config_to_dict(v) for k, v in config.items()})
278     return config
279 
280 
281 def set_nested(dictionary, path: str, value: str):
282     path = path.split(".")
283     for level in path[:-1]:
284         dictionary = dictionary.setdefault(level, {})
285     dictionary[path[-1]] = value
286 
287 
288 @dev.command()
289 @click.option("-m", "--migration-message", required=True)
290 def generate_migration(migration_message):
291     # ensure this is called from the root server directory
292     if Path(prefect_server.__file__).parents[2] != Path(os.getcwd()):
293         raise click.ClickException(
294             "generate-migration must be run from the server root directory."
295         )
296     # find the most recent revision
297     alembic_migrations_path = "../../../services/postgres/alembic/versions"
298     versions = glob.glob(
299         os.path.join(os.path.dirname(__file__), alembic_migrations_path, "*.py")
300     )
301     versions.sort()
302     most_recent_migration = versions[-1]
303     with open(
304         os.path.join(
305             os.path.dirname(__file__), alembic_migrations_path, most_recent_migration
306         )
307     ) as migration:
308         for line in migration.readlines():
309             if line.startswith("Revision ID:"):
310                 revision = line.split(": ")[1].strip()
311     click.echo(f"Most recent Alembic revision is {revision}")
312     # copy metadata to a backup for corresponding revision
313     hasura_migrations_path = "../../../services/hasura/migrations"
314     backup_metadata_file = f"metadata-{revision}.yaml"
315     backup_metadata_destination = os.path.abspath(
316         os.path.join(
317             os.path.dirname(__file__),
318             hasura_migrations_path,
319             "versions",
320             backup_metadata_file,
321         )
322     )
323     shutil.copy(
324         os.path.join(
325             os.path.dirname(__file__), hasura_migrations_path, "metadata.yaml"
326         ),
327         backup_metadata_destination,
328     )
329     click.echo(f"Copied metadata to {backup_metadata_destination}")
330     # create a new revision
331     click.echo(
332         subprocess.check_output(["alembic", "revision", "-m", migration_message])
333     )
334     click.secho("Prefect Server migration generated!", fg="green")
335
```
--- 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()

```

diff --git a/server/src/prefect_server/cli/dev.py b/server/src/prefect_server/cli/dev.py
--- a/server/src/prefect_server/cli/dev.py
+++ b/server/src/prefect_server/cli/dev.py
@@ -85,7 +85,7 @@
     """
     docker_dir = Path(prefect_server.__file__).parents[2] / "docker"
 
-    env = make_env()
+    env = make_dev_env()
 
     if "PREFECT_SERVER_TAG" not in env:
         env.update(PREFECT_SERVER_TAG=version)

{"golden_diff": "diff --git a/server/src/prefect_server/cli/dev.py b/server/src/prefect_server/cli/dev.py\n--- a/server/src/prefect_server/cli/dev.py\n+++ b/server/src/prefect_server/cli/dev.py\n@@ -85,7 +85,7 @@\n     \"\"\"\n     docker_dir = Path(prefect_server.__file__).parents[2] / \"docker\"\n \n-    env = make_env()\n+    env = make_dev_env()\n \n     if \"PREFECT_SERVER_TAG\" not in env:\n         env.update(PREFECT_SERVER_TAG=version)\n", "issue": "Undefined name: make_env in ./server/src/prefect_server/cli/dev.py\n## Description\r\n*A clear description of the bug*\r\n\r\nAn _undefined name_ like #2235 and #1199\r\nhttps://github.com/PrefectHQ/prefect/blob/master/server/src/prefect_server/cli/dev.py#L88\r\n\r\n`make_env` is an undefined name in this context which will raise a `NameError` at runtime.  Should this be `make_dev_env()` defined on line 36 or is `from prefect.cli.server import make_env` the right solution?\r\n\r\n[flake8](http://flake8.pycqa.org) testing of https://github.com/PrefectHQ/prefect on Python 3.8.3\r\n\r\n$ __flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics__\r\n```\r\n./server/src/prefect_server/cli/dev.py:88:11: F821 undefined name 'make_env'\r\n    env = make_env()\r\n          ^\r\n1     F821 undefined name 'make_env'\r\n1\r\n```\r\nhttps://flake8.pycqa.org/en/latest/user/error-codes.html\r\n\r\nOn the flake8 test selection, this PR does _not_ focus on \"_style violations_\" (the majority of flake8 error codes that [__psf/black__](https://github.com/psf/black) can autocorrect).  Instead these tests are focus on runtime safety and correctness:\r\n* E9 tests are about Python syntax errors usually raised because flake8 can not build an Abstract Syntax Tree (AST).  Often these issues are a sign of unused code or code that has not been ported to Python 3.  These would be compile-time errors in a compiled language but in a dynamic language like Python they result in the script halting/crashing on the user.\r\n* F63 tests are usually about the confusion between identity and equality in Python.  Use ==/!= to compare str, bytes, and int literals is the classic case.  These are areas where __a == b__ is True but __a is b__ is False (or vice versa).  Python >= 3.8 will raise SyntaxWarnings on these instances.\r\n* F7 tests logic errors and syntax errors in type hints\r\n* F82 tests are almost always _undefined names_ which are usually a sign of a typo, missing imports, or code that has not been ported to Python 3.  These also would be compile-time errors in a compiled language but in Python a __NameError__ is raised which will halt/crash the script on the user.\r\n\r\n\r\n\r\n\r\n## Expected Behavior\r\n*What did you expect to happen instead?*\r\n\r\n\r\n\r\n\r\n## Reproduction\r\n*A minimal example that exhibits the behavior.*\r\n\r\n\r\n\r\n\r\n## Environment\r\n*Any additional information about your environment*\r\n\r\n*Optionally run `prefect diagnostics` from the command line and paste the information here*\r\n\n", "before_files": [{"content": "# Licensed under the Prefect Community License, available at\n# https://www.prefect.io/legal/prefect-community-license\n\n\nimport glob\nimport os\nimport shutil\nimport signal\nimport subprocess\nimport time\nfrom pathlib import Path\n\nimport click\n\nimport prefect\nimport prefect_server\nfrom prefect_server import api, cli, config, utilities\nfrom prefect_server.database import models\n\n\n@click.group()\ndef dev():\n    \"\"\"\n    Commands for developing Server\n\n    \\b\n    Usage:\n        $ prefect-server ...\n    \n    \\b\n    Arguments:\n        build   builds prefect server, ui, apollo from source\n    \"\"\"\n\n\ndef make_dev_env(fname=None):\n\n    # replace localhost with postgres to use docker-compose dns\n    PREFECT_ENV = dict(\n        DB_CONNECTION_URL=config.database.connection_url.replace(\n            \"localhost\", \"postgres\"\n        )\n    )\n\n    APOLLO_ENV = dict(\n        HASURA_API_URL=f\"http://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql\",\n        HASURA_WS_URL=f\"ws://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql\",\n        PREFECT_API_URL=f\"http://{config.services.graphql.host}:{config.services.graphql.port}{config.services.graphql.path}\",\n        PREFECT_API_HEALTH_URL=f\"http://{config.services.graphql.host}:{config.services.graphql.port}/health\",\n    )\n\n    POSTGRES_ENV = dict(\n        POSTGRES_USER=config.database.username,\n        POSTGRES_PASSWORD=config.database.password,\n        POSTGRES_DB=config.database.name,\n    )\n\n    HASURA_ENV = dict()\n\n    UI_ENV = dict(GRAPHQL_URL=config.services.ui.graphql_url)\n\n    ENV = os.environ.copy()\n    ENV.update(**PREFECT_ENV, **APOLLO_ENV, **POSTGRES_ENV, **UI_ENV, **HASURA_ENV)\n\n    if fname is not None:\n        list_of_pairs = [\n            f\"{k}={repr(v)}\" if \"\\n\" in v else f\"{k}={v}\" for k, v in ENV.items()\n        ]\n        with open(fname, \"w\") as f:\n            f.write(\"\\n\".join(list_of_pairs))\n    return ENV.copy()\n\n\n@dev.command(hidden=True)\n@click.option(\n    \"--version\",\n    \"-v\",\n    help=\"The server image versions to build (for example, '0.10.0' or 'master')\",\n    # TODO: update this default to use prefect.__version__ logic\n    default=\"latest\",\n)\ndef build(version):\n    \"\"\"\n    foobar\n    \"\"\"\n    docker_dir = Path(prefect_server.__file__).parents[2] / \"docker\"\n\n    env = make_env()\n\n    if \"PREFECT_SERVER_TAG\" not in env:\n        env.update(PREFECT_SERVER_TAG=version)\n\n    proc = None\n    cmd = [\"docker-compose\", \"build\"]\n    proc = subprocess.Popen(cmd, cwd=docker_dir, env=env)\n\n\n@dev.command()\n@click.option(\"--tag\", \"-t\", help=\"The server image/tag to use\", default=\"latest\")\n@click.option(\n    \"--skip-pull\",\n    help=\"Pass this flag to skip pulling new images (if available)\",\n    is_flag=True,\n)\ndef infrastructure(tag, skip_pull):\n    \"\"\"\n    This command:\n        - starts a PostgreSQL database\n        - starts Hasura\n    \"\"\"\n    docker_dir = Path(prefect_server.__file__).parents[2] / \"docker\"\n\n    env = make_dev_env()\n\n    proc = None\n    try:\n        if not skip_pull:\n            subprocess.check_call(\n                [\"docker-compose\", \"pull\", \"postgres\", \"hasura\"],\n                cwd=docker_dir,\n                env=env,\n            )\n        proc = subprocess.Popen(\n            [\"docker-compose\", \"up\", \"postgres\", \"hasura\"], cwd=docker_dir, env=env\n        )\n\n        # if not initialize, just run hasura (and dependencies), which will skip the init step\n        while True:\n            time.sleep(0.5)\n    except:\n        click.secho(\n            \"Exception caught; killing services (press ctrl-C to force)\",\n            fg=\"white\",\n            bg=\"red\",\n        )\n        subprocess.check_output([\"docker-compose\", \"down\"], cwd=docker_dir, env=env)\n        if proc:\n            proc.kill()\n        raise\n\n\n@dev.command()\n@click.option(\"--skip-ui\", help=\"Pass this flag to skip UI dependencies\", is_flag=True)\n@click.option(\n    \"--skip-apollo\", help=\"Pass this flag to skip Apollo dependencies\", is_flag=True\n)\ndef install_dependencies(skip_ui, skip_apollo):\n    \"\"\"\n    This command:\n        - installs Apollo dependencies\n        - install UI dependencies\n    \"\"\"\n    if not skip_ui:\n        click.secho(\"Installing UI dependencies...\")\n        time.sleep(0.5)\n        install_ui_dependencies()\n\n    if not skip_apollo:\n        click.secho(\"Installing Apollo dependencies...\")\n        time.sleep(0.5)\n        install_apollo_dependencies()\n\n    if skip_ui and skip_apollo:\n        click.secho(\"No dependencies were installed because all were skipped.\")\n\n\ndef install_apollo_dependencies():\n    apollo_dir = Path(prefect_server.__file__).parents[2] / \"services\" / \"apollo\"\n\n    proc = None\n    try:\n        proc = subprocess.check_call([\"npm\", \"install\"], cwd=apollo_dir)\n        click.secho(\"Apollo dependencies installed! \ud83d\ude80\ud83d\ude80\ud83d\ude80\")\n    except:\n        click.secho(\n            \"Exception caught while installing Apollo dependencies.\",\n            fg=\"white\",\n            bg=\"red\",\n        )\n        if proc:\n            proc.kill()\n        raise\n\n\ndef install_ui_dependencies():\n    ui_dir = Path(prefect_server.__file__).parents[2] / \"services\" / \"ui\"\n\n    proc = None\n    try:\n        proc = subprocess.check_call([\"npm\", \"install\"], cwd=ui_dir)\n        click.secho(\"UI dependencies installed! \ud83d\udd79\ud83d\udd79\ud83d\udd79\")\n    except:\n        click.secho(\n            \"Exception caught while installing UI dependencies.\", fg=\"white\", bg=\"red\"\n        )\n        if proc:\n            proc.kill()\n        raise\n\n\ndef is_process_group_empty(pgid: int):\n    proc = subprocess.Popen(\n        [\"pgrep\", \"-g\", str(pgid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL\n    )\n    proc.wait()\n    return proc.returncode != 0\n\n\ndef kill_process_group(proc, timeout: int = 3):\n    try:\n        pgid = os.getpgid(proc.pid)\n        os.killpg(pgid, signal.SIGTERM)\n        proc.terminate()\n\n        for _ in range(timeout):\n            if is_process_group_empty(pgid):\n                return\n            click.secho(\"Waiting for process group to exit...\", fg=\"white\", bg=\"blue\")\n            time.sleep(1)\n\n        click.secho(\"Timeout while shutting down, killing!\", fg=\"white\", bg=\"red\")\n        os.killpg(pgid, signal.SIGKILL)\n        proc.kill()\n    except Exception as exc:\n        click.secho(exc)\n\n\n@dev.command()\n@click.option(\n    \"--include\", \"-i\", help=\"A comma-seperated list of serivces that should be run\"\n)\n@click.option(\n    \"--exclude\", \"-e\", help=\"A comma-seperated list of services that should not be run\"\n)\ndef services(include, exclude):\n    \"\"\"\n    This command starts services\n    \"\"\"\n\n    all_services = [\"graphql\", \"scheduler\", \"apollo\", \"ui\"]\n    if not include:\n        include = all_services\n    else:\n        include = include.split(\",\")\n    if not exclude:\n        exclude = \"\"\n    run_services = sorted(set(include).difference(exclude.split(\",\")))\n\n    click.secho(\n        f\"\\n\\nStarting Prefect Server services: {' '.join(run_services)}\\n\\n\",\n        fg=\"green\",\n    )\n\n    procs = []\n    for service in run_services:\n        procs.append(\n            subprocess.Popen(\n                [\"prefect-server\", \"services\", service],\n                env=make_dev_env(),\n                preexec_fn=os.setsid,\n            )\n        )\n\n    try:\n        while True:\n            time.sleep(1)\n    except:\n        click.secho(\"Exception caught; shutting down!\", fg=\"white\", bg=\"red\")\n        for proc in procs:\n            kill_process_group(proc)\n\n\ndef config_to_dict(config):\n    if isinstance(config, (list, tuple, set)):\n        return type(config)([config_to_dict(d) for d in config])\n    elif isinstance(config, prefect.configuration.Config):\n        return dict({k: config_to_dict(v) for k, v in config.items()})\n    return config\n\n\ndef set_nested(dictionary, path: str, value: str):\n    path = path.split(\".\")\n    for level in path[:-1]:\n        dictionary = dictionary.setdefault(level, {})\n    dictionary[path[-1]] = value\n\n\n@dev.command()\n@click.option(\"-m\", \"--migration-message\", required=True)\ndef generate_migration(migration_message):\n    # ensure this is called from the root server directory\n    if Path(prefect_server.__file__).parents[2] != Path(os.getcwd()):\n        raise click.ClickException(\n            \"generate-migration must be run from the server root directory.\"\n        )\n    # find the most recent revision\n    alembic_migrations_path = \"../../../services/postgres/alembic/versions\"\n    versions = glob.glob(\n        os.path.join(os.path.dirname(__file__), alembic_migrations_path, \"*.py\")\n    )\n    versions.sort()\n    most_recent_migration = versions[-1]\n    with open(\n        os.path.join(\n            os.path.dirname(__file__), alembic_migrations_path, most_recent_migration\n        )\n    ) as migration:\n        for line in migration.readlines():\n            if line.startswith(\"Revision ID:\"):\n                revision = line.split(\": \")[1].strip()\n    click.echo(f\"Most recent Alembic revision is {revision}\")\n    # copy metadata to a backup for corresponding revision\n    hasura_migrations_path = \"../../../services/hasura/migrations\"\n    backup_metadata_file = f\"metadata-{revision}.yaml\"\n    backup_metadata_destination = os.path.abspath(\n        os.path.join(\n            os.path.dirname(__file__),\n            hasura_migrations_path,\n            \"versions\",\n            backup_metadata_file,\n        )\n    )\n    shutil.copy(\n        os.path.join(\n            os.path.dirname(__file__), hasura_migrations_path, \"metadata.yaml\"\n        ),\n        backup_metadata_destination,\n    )\n    click.echo(f\"Copied metadata to {backup_metadata_destination}\")\n    # create a new revision\n    click.echo(\n        subprocess.check_output([\"alembic\", \"revision\", \"-m\", migration_message])\n    )\n    click.secho(\"Prefect Server migration generated!\", fg=\"green\")\n", "path": "server/src/prefect_server/cli/dev.py"}], "after_files": [{"content": "# Licensed under the Prefect Community License, available at\n# https://www.prefect.io/legal/prefect-community-license\n\n\nimport glob\nimport os\nimport shutil\nimport signal\nimport subprocess\nimport time\nfrom pathlib import Path\n\nimport click\n\nimport prefect\nimport prefect_server\nfrom prefect_server import api, cli, config, utilities\nfrom prefect_server.database import models\n\n\n@click.group()\ndef dev():\n    \"\"\"\n    Commands for developing Server\n\n    \\b\n    Usage:\n        $ prefect-server ...\n    \n    \\b\n    Arguments:\n        build   builds prefect server, ui, apollo from source\n    \"\"\"\n\n\ndef make_dev_env(fname=None):\n\n    # replace localhost with postgres to use docker-compose dns\n    PREFECT_ENV = dict(\n        DB_CONNECTION_URL=config.database.connection_url.replace(\n            \"localhost\", \"postgres\"\n        )\n    )\n\n    APOLLO_ENV = dict(\n        HASURA_API_URL=f\"http://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql\",\n        HASURA_WS_URL=f\"ws://{config.hasura.host}:{config.hasura.port}/v1alpha1/graphql\",\n        PREFECT_API_URL=f\"http://{config.services.graphql.host}:{config.services.graphql.port}{config.services.graphql.path}\",\n        PREFECT_API_HEALTH_URL=f\"http://{config.services.graphql.host}:{config.services.graphql.port}/health\",\n    )\n\n    POSTGRES_ENV = dict(\n        POSTGRES_USER=config.database.username,\n        POSTGRES_PASSWORD=config.database.password,\n        POSTGRES_DB=config.database.name,\n    )\n\n    HASURA_ENV = dict()\n\n    UI_ENV = dict(GRAPHQL_URL=config.services.ui.graphql_url)\n\n    ENV = os.environ.copy()\n    ENV.update(**PREFECT_ENV, **APOLLO_ENV, **POSTGRES_ENV, **UI_ENV, **HASURA_ENV)\n\n    if fname is not None:\n        list_of_pairs = [\n            f\"{k}={repr(v)}\" if \"\\n\" in v else f\"{k}={v}\" for k, v in ENV.items()\n        ]\n        with open(fname, \"w\") as f:\n            f.write(\"\\n\".join(list_of_pairs))\n    return ENV.copy()\n\n\n@dev.command(hidden=True)\n@click.option(\n    \"--version\",\n    \"-v\",\n    help=\"The server image versions to build (for example, '0.10.0' or 'master')\",\n    # TODO: update this default to use prefect.__version__ logic\n    default=\"latest\",\n)\ndef build(version):\n    \"\"\"\n    foobar\n    \"\"\"\n    docker_dir = Path(prefect_server.__file__).parents[2] / \"docker\"\n\n    env = make_dev_env()\n\n    if \"PREFECT_SERVER_TAG\" not in env:\n        env.update(PREFECT_SERVER_TAG=version)\n\n    proc = None\n    cmd = [\"docker-compose\", \"build\"]\n    proc = subprocess.Popen(cmd, cwd=docker_dir, env=env)\n\n\n@dev.command()\n@click.option(\"--tag\", \"-t\", help=\"The server image/tag to use\", default=\"latest\")\n@click.option(\n    \"--skip-pull\",\n    help=\"Pass this flag to skip pulling new images (if available)\",\n    is_flag=True,\n)\ndef infrastructure(tag, skip_pull):\n    \"\"\"\n    This command:\n        - starts a PostgreSQL database\n        - starts Hasura\n    \"\"\"\n    docker_dir = Path(prefect_server.__file__).parents[2] / \"docker\"\n\n    env = make_dev_env()\n\n    proc = None\n    try:\n        if not skip_pull:\n            subprocess.check_call(\n                [\"docker-compose\", \"pull\", \"postgres\", \"hasura\"],\n                cwd=docker_dir,\n                env=env,\n            )\n        proc = subprocess.Popen(\n            [\"docker-compose\", \"up\", \"postgres\", \"hasura\"], cwd=docker_dir, env=env\n        )\n\n        # if not initialize, just run hasura (and dependencies), which will skip the init step\n        while True:\n            time.sleep(0.5)\n    except:\n        click.secho(\n            \"Exception caught; killing services (press ctrl-C to force)\",\n            fg=\"white\",\n            bg=\"red\",\n        )\n        subprocess.check_output([\"docker-compose\", \"down\"], cwd=docker_dir, env=env)\n        if proc:\n            proc.kill()\n        raise\n\n\n@dev.command()\n@click.option(\"--skip-ui\", help=\"Pass this flag to skip UI dependencies\", is_flag=True)\n@click.option(\n    \"--skip-apollo\", help=\"Pass this flag to skip Apollo dependencies\", is_flag=True\n)\ndef install_dependencies(skip_ui, skip_apollo):\n    \"\"\"\n    This command:\n        - installs Apollo dependencies\n        - install UI dependencies\n    \"\"\"\n    if not skip_ui:\n        click.secho(\"Installing UI dependencies...\")\n        time.sleep(0.5)\n        install_ui_dependencies()\n\n    if not skip_apollo:\n        click.secho(\"Installing Apollo dependencies...\")\n        time.sleep(0.5)\n        install_apollo_dependencies()\n\n    if skip_ui and skip_apollo:\n        click.secho(\"No dependencies were installed because all were skipped.\")\n\n\ndef install_apollo_dependencies():\n    apollo_dir = Path(prefect_server.__file__).parents[2] / \"services\" / \"apollo\"\n\n    proc = None\n    try:\n        proc = subprocess.check_call([\"npm\", \"install\"], cwd=apollo_dir)\n        click.secho(\"Apollo dependencies installed! \ud83d\ude80\ud83d\ude80\ud83d\ude80\")\n    except:\n        click.secho(\n            \"Exception caught while installing Apollo dependencies.\",\n            fg=\"white\",\n            bg=\"red\",\n        )\n        if proc:\n            proc.kill()\n        raise\n\n\ndef install_ui_dependencies():\n    ui_dir = Path(prefect_server.__file__).parents[2] / \"services\" / \"ui\"\n\n    proc = None\n    try:\n        proc = subprocess.check_call([\"npm\", \"install\"], cwd=ui_dir)\n        click.secho(\"UI dependencies installed! \ud83d\udd79\ud83d\udd79\ud83d\udd79\")\n    except:\n        click.secho(\n            \"Exception caught while installing UI dependencies.\", fg=\"white\", bg=\"red\"\n        )\n        if proc:\n            proc.kill()\n        raise\n\n\ndef is_process_group_empty(pgid: int):\n    proc = subprocess.Popen(\n        [\"pgrep\", \"-g\", str(pgid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL\n    )\n    proc.wait()\n    return proc.returncode != 0\n\n\ndef kill_process_group(proc, timeout: int = 3):\n    try:\n        pgid = os.getpgid(proc.pid)\n        os.killpg(pgid, signal.SIGTERM)\n        proc.terminate()\n\n        for _ in range(timeout):\n            if is_process_group_empty(pgid):\n                return\n            click.secho(\"Waiting for process group to exit...\", fg=\"white\", bg=\"blue\")\n            time.sleep(1)\n\n        click.secho(\"Timeout while shutting down, killing!\", fg=\"white\", bg=\"red\")\n        os.killpg(pgid, signal.SIGKILL)\n        proc.kill()\n    except Exception as exc:\n        click.secho(exc)\n\n\n@dev.command()\n@click.option(\n    \"--include\", \"-i\", help=\"A comma-seperated list of serivces that should be run\"\n)\n@click.option(\n    \"--exclude\", \"-e\", help=\"A comma-seperated list of services that should not be run\"\n)\ndef services(include, exclude):\n    \"\"\"\n    This command starts services\n    \"\"\"\n\n    all_services = [\"graphql\", \"scheduler\", \"apollo\", \"ui\"]\n    if not include:\n        include = all_services\n    else:\n        include = include.split(\",\")\n    if not exclude:\n        exclude = \"\"\n    run_services = sorted(set(include).difference(exclude.split(\",\")))\n\n    click.secho(\n        f\"\\n\\nStarting Prefect Server services: {' '.join(run_services)}\\n\\n\",\n        fg=\"green\",\n    )\n\n    procs = []\n    for service in run_services:\n        procs.append(\n            subprocess.Popen(\n                [\"prefect-server\", \"services\", service],\n                env=make_dev_env(),\n                preexec_fn=os.setsid,\n            )\n        )\n\n    try:\n        while True:\n            time.sleep(1)\n    except:\n        click.secho(\"Exception caught; shutting down!\", fg=\"white\", bg=\"red\")\n        for proc in procs:\n            kill_process_group(proc)\n\n\ndef config_to_dict(config):\n    if isinstance(config, (list, tuple, set)):\n        return type(config)([config_to_dict(d) for d in config])\n    elif isinstance(config, prefect.configuration.Config):\n        return dict({k: config_to_dict(v) for k, v in config.items()})\n    return config\n\n\ndef set_nested(dictionary, path: str, value: str):\n    path = path.split(\".\")\n    for level in path[:-1]:\n        dictionary = dictionary.setdefault(level, {})\n    dictionary[path[-1]] = value\n\n\n@dev.command()\n@click.option(\"-m\", \"--migration-message\", required=True)\ndef generate_migration(migration_message):\n    # ensure this is called from the root server directory\n    if Path(prefect_server.__file__).parents[2] != Path(os.getcwd()):\n        raise click.ClickException(\n            \"generate-migration must be run from the server root directory.\"\n        )\n    # find the most recent revision\n    alembic_migrations_path = \"../../../services/postgres/alembic/versions\"\n    versions = glob.glob(\n        os.path.join(os.path.dirname(__file__), alembic_migrations_path, \"*.py\")\n    )\n    versions.sort()\n    most_recent_migration = versions[-1]\n    with open(\n        os.path.join(\n            os.path.dirname(__file__), alembic_migrations_path, most_recent_migration\n        )\n    ) as migration:\n        for line in migration.readlines():\n            if line.startswith(\"Revision ID:\"):\n                revision = line.split(\": \")[1].strip()\n    click.echo(f\"Most recent Alembic revision is {revision}\")\n    # copy metadata to a backup for corresponding revision\n    hasura_migrations_path = \"../../../services/hasura/migrations\"\n    backup_metadata_file = f\"metadata-{revision}.yaml\"\n    backup_metadata_destination = os.path.abspath(\n        os.path.join(\n            os.path.dirname(__file__),\n            hasura_migrations_path,\n            \"versions\",\n            backup_metadata_file,\n        )\n    )\n    shutil.copy(\n        os.path.join(\n            os.path.dirname(__file__), hasura_migrations_path, \"metadata.yaml\"\n        ),\n        backup_metadata_destination,\n    )\n    click.echo(f\"Copied metadata to {backup_metadata_destination}\")\n    # create a new revision\n    click.echo(\n        subprocess.check_output([\"alembic\", \"revision\", \"-m\", migration_message])\n    )\n    click.secho(\"Prefect Server migration generated!\", fg=\"green\")\n", "path": "server/src/prefect_server/cli/dev.py"}]}