From de0f4157ceaea3c6403e59760116f4019e8b2cbf Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Tue, 22 Feb 2022 15:37:41 -0700 Subject: [PATCH] Add `yarn dev` command from #7183 to `manage.py` --- netbox/extras/management/commands/dev.py | 22 +++++++++++++ netbox/extras/management/commands/uidev.py | 37 ++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 netbox/extras/management/commands/dev.py create mode 100644 netbox/extras/management/commands/uidev.py diff --git a/netbox/extras/management/commands/dev.py b/netbox/extras/management/commands/dev.py new file mode 100644 index 000000000..9af36a2d3 --- /dev/null +++ b/netbox/extras/management/commands/dev.py @@ -0,0 +1,22 @@ +import typing as t +import threading +from django.core.management import call_command +from django.contrib.staticfiles.management.commands import runserver + + +class Command(runserver.Command): + """Run the NetBox development server. + + Builds UI source files, runs `collectstatic`, starts the Django development web server, and + watches UI source files for changes. + """ + + def handle(self, *args: t.Any, **options: t.Any) -> t.Optional[str]: + # Run the UI development server in a separate thread so file watching doesn't block the + # Django development server running in the main thread. + ui_dev = threading.Thread(name="uidev", target=lambda: call_command("uidev")) + ui_dev.start() + # Run `collectstatic`. + call_command("collectstatic", interactive=False) + # Run the original `runserver` command. + return super().handle(*args, **options) diff --git a/netbox/extras/management/commands/uidev.py b/netbox/extras/management/commands/uidev.py new file mode 100644 index 000000000..6508b52f4 --- /dev/null +++ b/netbox/extras/management/commands/uidev.py @@ -0,0 +1,37 @@ +import shutil +import subprocess +import typing as t +from pathlib import Path +from django.core.management.base import BaseCommand, CommandError + + +class Command(BaseCommand): + """Provide access to the underlying UI development server.""" + + def handle(self, *args: t.Any, **options: t.Any) -> t.Optional[str]: + """Run the `npm run dev` command via a separate process and write the output.""" + + project_root = Path(__file__).parent.parent.parent.parent + project_static = project_root / "project-static" + + npm = shutil.which("npm") + npm_args = tuple(str(i) for i in (npm, "--prefix", project_static, "run", "--silent", "dev")) + + try: + self.stdout.write(self.style.SUCCESS("Watching UI files...")) + proc = subprocess.Popen( + args=npm_args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=project_static, + ) + while True: + out = proc.stdout.readline().decode() + if out == '' and proc.poll() is not None: + break + if out: + self.stdout.write(out.strip()) + + except BaseException as exc: + proc.kill() + raise CommandError("Error while running command '{}'".format(" ".join(npm_args))) from exc