From ffdcc979e6f85d122eafd5d76b6b173706eb5d32 Mon Sep 17 00:00:00 2001 From: Daniel Dolezal Date: Mon, 27 Jul 2026 22:01:52 +0200 Subject: [PATCH] fix(cli): ignore generated folders by default - Skip .comments, virtualenvs, env files, VCS folders, and OS metadata in sync. - Apply defaults before project .nanoshareignore and repeatable --ignore rules. - Cover default ignores for local uploads and direct pattern matching. --- cli/nanoshare_client/ignore.py | 22 +++++++++++++++++++++- tests/test_nanoshare_cli.py | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cli/nanoshare_client/ignore.py b/cli/nanoshare_client/ignore.py index b9eca0b..d02542b 100644 --- a/cli/nanoshare_client/ignore.py +++ b/cli/nanoshare_client/ignore.py @@ -4,9 +4,29 @@ from fnmatch import fnmatch from pathlib import Path IGNORE_FILE_NAME = '.nanoshareignore' +DEFAULT_IGNORE_PATTERNS = [ + IGNORE_FILE_NAME, + '.comments/', + '.env', + '.env.*', + '.venv/', + 'venv/', + '__pycache__/', + '.pytest_cache/', + '.mypy_cache/', + '.ruff_cache/', + '.git/', + '.hg/', + '.svn/', + '.DS_Store', + 'Thumbs.db', + 'desktop.ini', + '$RECYCLE.BIN/', + 'System Volume Information/', +] def load_ignore_patterns(root: Path, extra_patterns: list[str] | None = None) -> list[str]: - patterns = [IGNORE_FILE_NAME] + patterns = list(DEFAULT_IGNORE_PATTERNS) ignore_file = root / IGNORE_FILE_NAME if ignore_file.is_file(): for line in ignore_file.read_text().splitlines(): diff --git a/tests/test_nanoshare_cli.py b/tests/test_nanoshare_cli.py index b25a137..a7ac571 100644 --- a/tests/test_nanoshare_cli.py +++ b/tests/test_nanoshare_cli.py @@ -9,7 +9,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'cli')) from nanoshare_client import cli from nanoshare_client.auth import _CallbackServer from nanoshare_client.completion import script as completion_script -from nanoshare_client.ignore import is_ignored +from nanoshare_client.ignore import load_ignore_patterns, is_ignored from nanoshare_client.table import format_table class FakeClient: @@ -210,6 +210,12 @@ def test_sync_ignores_local_files_from_nanoshareignore(tmp_path, monkeypatch): (tmp_path / 'cache').mkdir() (tmp_path / 'cache' / 'ignored.txt').write_text('ignored') (tmp_path / 'scratch.tmp').write_text('ignored') + (tmp_path / '.comments').mkdir() + (tmp_path / '.comments' / 'ignored.xml').write_text('') + (tmp_path / '.env').write_text('SECRET=ignored') + (tmp_path / '.venv').mkdir() + (tmp_path / '.venv' / 'ignored.py').write_text('ignored') + (tmp_path / 'Thumbs.db').write_text('ignored') code = cli.main([ 'sync', @@ -250,6 +256,18 @@ def test_ignore_patterns_match_files_and_directories(): assert is_ignored('docs/page.draft.md', patterns) assert not is_ignored('docs/page.md', patterns) +def test_default_ignore_patterns_skip_common_generated_files(tmp_path): + patterns = load_ignore_patterns(tmp_path) + + assert is_ignored('.comments/comment.xml', patterns) + assert is_ignored('.env', patterns) + assert is_ignored('.env.local', patterns) + assert is_ignored('.venv/bin/python', patterns) + assert is_ignored('venv/bin/python', patterns) + assert is_ignored('__pycache__/mod.pyc', patterns) + assert is_ignored('nested/Thumbs.db', patterns) + assert not is_ignored('docs/note.txt', patterns) + def test_completion_scripts_include_commands(): zsh = completion_script('zsh') bash = completion_script('bash')