Skip to content

Commit e659720

Browse files
committed
feat(download): auto .gitignore in managed .django_tailwind_cli/ dir
The downloaded CLI binary and auto-generated source.css should never end up in the user's repo, but that puts the burden on every project to remember a manual `.gitignore` entry. Drop a single-line `*` .gitignore into the managed directory on first use. Git's own rules then ignore everything in the folder including the .gitignore itself, so `git add .` silently skips the whole thing with zero user action. Gated on TAILWIND_CLI_PATH being unset — we only manage the default path. Existing .gitignore files are preserved so a user who went out of their way to customize things isn't surprised.
1 parent eefec06 commit e659720

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
### 🛠️ Developer Experience
1515
- **Gitignore cleanup**: Trimmed `.gitignore` to project-relevant entries only
16+
- **Managed `.django_tailwind_cli/` is now git-ignored by default**: On first use the directory gets a `.gitignore` containing `*`, so the downloaded CLI binary and the auto-generated `source.css` are silently skipped by `git add .` without any project-level `.gitignore` tweak from the user. Existing `.gitignore` files in that directory are preserved. No-op when `TAILWIND_CLI_PATH` points at a custom location.
1617

1718
### 🐛 Bug Fixes
1819
- **Tox matrix**: Django 4.2/5.2/6.0 factors in `tox.ini` were ignored because `uv sync --locked` reinstalled Django from `uv.lock` after tox's `deps`. The matrix now excludes Django from the sync and installs the factor-specific version via `commands_pre`, so `just test-all` actually covers all supported Django versions.

src/django_tailwind_cli/management/commands/tailwind.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,29 @@ def _setup_tailwind_environment_with_verbose(*, verbose: bool = False) -> None:
12251225
typer.secho("⚙️ Setting up Tailwind environment...", fg=typer.colors.CYAN)
12261226
_download_cli_with_verbose(verbose=verbose)
12271227
_create_standard_config_with_verbose(verbose=verbose)
1228+
_ensure_default_gitignore()
1229+
1230+
1231+
def _ensure_default_gitignore() -> None:
1232+
"""Drop a single-star .gitignore into the managed `.django_tailwind_cli/` dir.
1233+
1234+
The pattern ``*`` ignores every file in the directory — including the
1235+
.gitignore itself — so ``git add .`` silently skips the whole folder
1236+
without the user having to touch their project-level .gitignore.
1237+
1238+
Only acts when ``TAILWIND_CLI_PATH`` is unset (default mode). Custom
1239+
paths are left alone: we don't own them and a stray .gitignore there
1240+
could conflict with whatever the user is doing.
1241+
"""
1242+
if getattr(settings, "TAILWIND_CLI_PATH", None):
1243+
return
1244+
default_dir = Path(settings.BASE_DIR) / ".django_tailwind_cli"
1245+
if not default_dir.exists():
1246+
return
1247+
gitignore = default_dir / ".gitignore"
1248+
if gitignore.exists():
1249+
return # Respect whatever the user put there
1250+
gitignore.write_text("*\n")
12281251

12291252

12301253
def _should_rebuild_css(src_css: Path, dist_css: Path) -> bool:

tests/test_management_commands.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,66 @@ def mock_download(
676676
assert f'@source "{editable_app}";' in written
677677

678678

679+
class TestDefaultGitignore:
680+
"""Tests for _ensure_default_gitignore() — the auto .gitignore drop-in."""
681+
682+
def test_ensure_default_gitignore_creates_star_file(self, settings: LazySettings, tmp_path: Path):
683+
"""In default mode the helper writes '*\\n' into .django_tailwind_cli/.gitignore."""
684+
from django_tailwind_cli.management.commands.tailwind import _ensure_default_gitignore
685+
686+
settings.BASE_DIR = tmp_path
687+
# TAILWIND_CLI_PATH intentionally not set → default mode
688+
default_dir = tmp_path / ".django_tailwind_cli"
689+
default_dir.mkdir()
690+
691+
_ensure_default_gitignore()
692+
693+
gitignore = default_dir / ".gitignore"
694+
assert gitignore.exists()
695+
assert gitignore.read_text() == "*\n"
696+
697+
def test_ensure_default_gitignore_skipped_for_custom_path(self, settings: LazySettings, tmp_path: Path):
698+
"""With a custom TAILWIND_CLI_PATH, the helper is a no-op."""
699+
from django_tailwind_cli.management.commands.tailwind import _ensure_default_gitignore
700+
701+
settings.BASE_DIR = tmp_path
702+
settings.TAILWIND_CLI_PATH = str(tmp_path / "custom" / "tailwindcss")
703+
704+
# The default dir can still exist from a previous run — the helper
705+
# should leave it alone when a custom path is active.
706+
default_dir = tmp_path / ".django_tailwind_cli"
707+
default_dir.mkdir()
708+
709+
_ensure_default_gitignore()
710+
711+
assert not (default_dir / ".gitignore").exists()
712+
713+
def test_ensure_default_gitignore_preserves_existing_file(self, settings: LazySettings, tmp_path: Path):
714+
"""If the user already wrote a .gitignore, we don't overwrite it."""
715+
from django_tailwind_cli.management.commands.tailwind import _ensure_default_gitignore
716+
717+
settings.BASE_DIR = tmp_path
718+
default_dir = tmp_path / ".django_tailwind_cli"
719+
default_dir.mkdir()
720+
existing = default_dir / ".gitignore"
721+
existing.write_text("# hand-written\n*.log\n")
722+
723+
_ensure_default_gitignore()
724+
725+
assert existing.read_text() == "# hand-written\n*.log\n"
726+
727+
def test_ensure_default_gitignore_noop_when_dir_missing(self, settings: LazySettings, tmp_path: Path):
728+
"""If the managed dir was never created, the helper simply returns."""
729+
from django_tailwind_cli.management.commands.tailwind import _ensure_default_gitignore
730+
731+
settings.BASE_DIR = tmp_path
732+
# No default_dir created
733+
734+
_ensure_default_gitignore() # must not raise
735+
736+
assert not (tmp_path / ".django_tailwind_cli").exists()
737+
738+
679739
# Configuration to run tests with appropriate markers
680740
pytestmark = [
681741
pytest.mark.filterwarnings("ignore::DeprecationWarning"),

0 commit comments

Comments
 (0)