diff --git a/src/django_tailwind_cli/management/commands/tailwind.py b/src/django_tailwind_cli/management/commands/tailwind.py index e26be10..d2ebfea 100644 --- a/src/django_tailwind_cli/management/commands/tailwind.py +++ b/src/django_tailwind_cli/management/commands/tailwind.py @@ -36,11 +36,15 @@ def init(self): # Before running the actual subcommand, we need to make sure that the CLI is installed and # the config file exists. - self._download_cli_if_not_exists() + if self.config.automatic_download: + self.download_cli() self._create_tailwind_config_if_not_exists() @command(help="Build a minified production ready CSS file.") def build(self): + if not self.config.get_full_cli_path().exists(): + raise CommandError("Tailwind CSS CLI not found.") + build_cmd = [ str(self.config.get_full_cli_path()), "--output", @@ -63,6 +67,9 @@ def build(self): @command(help="Start Tailwind CLI in watch mode during development.") def watch(self): + if not self.config.get_full_cli_path().exists(): + raise CommandError("Tailwind CSS CLI not found.") + watch_cmd = [ str(self.config.get_full_cli_path()), "--output", @@ -234,22 +241,27 @@ def _runserver(self, debug_server_cmd: List[str]) -> None: watch_process.terminate() debugserver_process.terminate() - def _download_cli_if_not_exists(self) -> None: + @command(name="download_cli", help="Download the Tailwind CSS CLI to .") + def download_cli(self) -> None: dest_file = self.config.get_full_cli_path() - if not dest_file.exists() and self.config.automatic_download: - download_url = self.config.get_download_url() - self.stdout.write(self.style.ERROR("Tailwind CSS CLI not found.")) - self.stdout.write(self.style.WARNING(f"Downloading Tailwind CSS CLI from '{download_url}'")) - dest_file.parent.mkdir(parents=True, exist_ok=True) - certifi_context = ssl.create_default_context(cafile=certifi.where()) - with urllib.request.urlopen(download_url, context=certifi_context) as source, dest_file.open( # noqa: S310 - mode="wb" - ) as dest: - shutil.copyfileobj(source, dest) - # make cli executable - dest_file.chmod(0o755) - self.stdout.write(self.style.SUCCESS(f"Downloaded Tailwind CSS CLI to '{dest_file}'")) + if dest_file.exists(): + self._write_success(f"Tailwind CSS CLI already exists at '{dest_file}'") + return + + download_url = self.config.get_download_url() + self._write_error("Tailwind CSS CLI not found.") + self._write_success(f"Downloading Tailwind CSS CLI from '{download_url}'") + dest_file.parent.mkdir(parents=True, exist_ok=True) + certifi_context = ssl.create_default_context(cafile=certifi.where()) + with ( + urllib.request.urlopen(download_url, context=certifi_context) as source, + dest_file.open(mode="wb") as dest, # noqa: S310 + ): + shutil.copyfileobj(source, dest) + # make cli executable + dest_file.chmod(0o755) + self._write_success(f"Downloaded Tailwind CSS CLI to '{dest_file}'") def _create_tailwind_config_if_not_exists(self) -> None: tailwind_config_file = self.config.get_full_config_file_path() diff --git a/tests/test_management_commands.py b/tests/test_management_commands.py index 439769f..f0d6a7c 100644 --- a/tests/test_management_commands.py +++ b/tests/test_management_commands.py @@ -35,7 +35,7 @@ def test_download_cli(settings, tmp_path, config): settings.BASE_DIR = tmp_path settings.TAILWIND_CLI_PATH = str(tmp_path) assert not config.get_full_cli_path().exists() - call_command("tailwind", "build") + call_command("tailwind", "download_cli") assert config.get_full_cli_path().exists() @@ -43,10 +43,31 @@ def test_download_cli_without_tailwind_cli_path(settings, tmp_path, config): settings.BASE_DIR = tmp_path settings.TAILWIND_CLI_PATH = None assert not config.get_full_cli_path().exists() + call_command("tailwind", "download_cli") + assert config.get_full_cli_path().exists() + + +def test_automatic_download_enabled(settings, tmp_path, config): + settings.BASE_DIR = tmp_path + settings.TAILWIND_CLI_PATH = str(tmp_path) + settings.TAILWIND_CLI_AUTOMATIC_DOWNLOAD = True + assert not config.get_full_cli_path().exists() call_command("tailwind", "build") assert config.get_full_cli_path().exists() +def test_automatic_download_disabled(settings, tmp_path, config): + settings.BASE_DIR = tmp_path + settings.TAILWIND_CLI_PATH = str(tmp_path) + settings.TAILWIND_CLI_AUTOMATIC_DOWNLOAD = False + assert not config.get_full_cli_path().exists() + with pytest.raises(CommandError, match="Tailwind CSS CLI not found."): + call_command("tailwind", "build") + with pytest.raises(CommandError, match="Tailwind CSS CLI not found."): + call_command("tailwind", "watch") + assert not config.get_full_cli_path().exists() + + def test_create_tailwind_config_if_non_exists(settings, tmp_path, config): settings.BASE_DIR = tmp_path settings.TAILWIND_CLI_PATH = str(tmp_path) @@ -80,6 +101,7 @@ def test_build_output_of_first_run(settings, tmp_path, capsys): call_command("tailwind", "build") captured = capsys.readouterr() assert "Tailwind CSS CLI not found." in captured.out + assert "Tailwind CSS CLI already exists at" not in captured.out assert "Downloading Tailwind CSS CLI from " in captured.out assert "Built production stylesheet" in captured.out @@ -92,6 +114,7 @@ def test_build_output_of_second_run(settings, tmp_path, capsys): call_command("tailwind", "build") captured = capsys.readouterr() assert "Tailwind CSS CLI not found." not in captured.out + assert "Tailwind CSS CLI already exists at" in captured.out assert "Downloading Tailwind CSS CLI from " not in captured.out assert "Built production stylesheet" in captured.out