218 lines
6.2 KiB
Python
218 lines
6.2 KiB
Python
"""Clean command handler for script runner."""
|
|
|
|
import argparse
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def clean_command(args, workspace_root: Path):
|
|
"""Clean build artifacts and caches.
|
|
|
|
Args:
|
|
args: Command-line arguments
|
|
workspace_root: Path to workspace root
|
|
|
|
Returns:
|
|
Exit code (0 = success, non-zero = failure)
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
prog="./run clean",
|
|
description="Clean build artifacts and caches",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
./run clean # Clean everything (interactive)
|
|
./run clean --all --force # Clean everything (no confirmation)
|
|
./run clean --dist # Clean only dist directories
|
|
./run clean --cache # Clean only cache directories
|
|
|
|
What gets cleaned:
|
|
--dist: dist/, build/ directories in TypeScript packages
|
|
--cache: node_modules/, __pycache__/, .pytest_cache/, *.pyc
|
|
--deps: node_modules/ (requires reinstall after)
|
|
--venv: Python .venv/ directories (requires recreation after)
|
|
""",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--all",
|
|
action="store_true",
|
|
help="Clean everything (dist + cache, excludes deps/venv)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--dist",
|
|
action="store_true",
|
|
help="Clean dist/ and build/ directories",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--cache",
|
|
action="store_true",
|
|
help="Clean cache directories (__pycache__, .pytest_cache)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--deps",
|
|
action="store_true",
|
|
help="Clean dependencies (node_modules/)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--venv",
|
|
action="store_true",
|
|
help="Clean Python virtual environments (.venv/)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--force",
|
|
action="store_true",
|
|
help="Don't ask for confirmation",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
help="Verbose output",
|
|
)
|
|
|
|
parsed_args = parser.parse_args(args)
|
|
|
|
# If no flags, enable --all
|
|
if not any([parsed_args.all, parsed_args.dist, parsed_args.cache, parsed_args.deps, parsed_args.venv]):
|
|
parsed_args.all = True
|
|
|
|
# --all means dist + cache (but not deps/venv)
|
|
if parsed_args.all:
|
|
parsed_args.dist = True
|
|
parsed_args.cache = True
|
|
|
|
print("@image workspace clean\n")
|
|
|
|
# Build list of patterns to clean
|
|
patterns_to_clean = []
|
|
|
|
if parsed_args.dist:
|
|
patterns_to_clean.extend(["**/dist", "**/build", "**/.turbo"])
|
|
|
|
if parsed_args.cache:
|
|
patterns_to_clean.extend([
|
|
"**/__pycache__",
|
|
"**/.pytest_cache",
|
|
"**/*.pyc",
|
|
"**/.ruff_cache",
|
|
"**/.mypy_cache",
|
|
])
|
|
|
|
if parsed_args.deps:
|
|
patterns_to_clean.append("**/node_modules")
|
|
|
|
if parsed_args.venv:
|
|
patterns_to_clean.append("**/.venv")
|
|
|
|
# Find all matching paths
|
|
paths_to_delete = []
|
|
|
|
for pattern in patterns_to_clean:
|
|
# Use glob with recursive pattern
|
|
for path in workspace_root.rglob(pattern.replace("**/", "")):
|
|
# Skip if inside node_modules or .venv (unless we're specifically cleaning those)
|
|
if not parsed_args.deps and "node_modules" in path.parts:
|
|
continue
|
|
if not parsed_args.venv and ".venv" in path.parts:
|
|
continue
|
|
|
|
paths_to_delete.append(path)
|
|
|
|
if not paths_to_delete:
|
|
print("Nothing to clean")
|
|
return 0
|
|
|
|
# Show what will be deleted
|
|
print(f"Found {len(paths_to_delete)} items to clean:\n")
|
|
|
|
if parsed_args.verbose or len(paths_to_delete) < 20:
|
|
for path in sorted(paths_to_delete):
|
|
rel_path = path.relative_to(workspace_root)
|
|
print(f" {rel_path}")
|
|
else:
|
|
# Show summary for many items
|
|
by_type = {}
|
|
for path in paths_to_delete:
|
|
if path.name == "node_modules":
|
|
by_type.setdefault("node_modules", []).append(path)
|
|
elif path.name == ".venv":
|
|
by_type.setdefault(".venv", []).append(path)
|
|
elif path.name in ["dist", "build"]:
|
|
by_type.setdefault("dist/build", []).append(path)
|
|
elif path.name in ["__pycache__", ".pytest_cache", ".ruff_cache", ".mypy_cache"]:
|
|
by_type.setdefault("cache", []).append(path)
|
|
elif path.suffix == ".pyc":
|
|
by_type.setdefault("*.pyc", []).append(path)
|
|
else:
|
|
by_type.setdefault("other", []).append(path)
|
|
|
|
for type_name, paths in sorted(by_type.items()):
|
|
print(f" {type_name}: {len(paths)} items")
|
|
|
|
print()
|
|
|
|
# Confirm
|
|
if not parsed_args.force:
|
|
response = input("Delete these items? [y/N] ")
|
|
if response.lower() != "y":
|
|
print("Cancelled")
|
|
return 0
|
|
|
|
# Delete
|
|
print("\nCleaning...")
|
|
deleted = 0
|
|
errors = 0
|
|
|
|
for path in paths_to_delete:
|
|
try:
|
|
if path.is_dir():
|
|
shutil.rmtree(path)
|
|
else:
|
|
path.unlink()
|
|
deleted += 1
|
|
if parsed_args.verbose:
|
|
rel_path = path.relative_to(workspace_root)
|
|
print(f" ✓ {rel_path}")
|
|
except Exception as e:
|
|
errors += 1
|
|
if parsed_args.verbose:
|
|
rel_path = path.relative_to(workspace_root)
|
|
print(f" ✗ {rel_path}: {e}")
|
|
|
|
print(f"\n✓ Cleaned {deleted} items")
|
|
if errors > 0:
|
|
print(f"✗ {errors} errors")
|
|
return 1
|
|
|
|
# Show next steps if deps/venv were cleaned
|
|
if parsed_args.deps:
|
|
print("\nNote: node_modules cleaned. Reinstall with:")
|
|
print(" cd <package> && npm install")
|
|
|
|
if parsed_args.venv:
|
|
print("\nNote: .venv cleaned. Recreate with:")
|
|
print(" cd <service> && python -m venv .venv && source .venv/bin/activate && pip install -e .")
|
|
|
|
return 0
|
|
|
|
|
|
def register_clean_command(runner):
|
|
"""Register the clean command with the script runner.
|
|
|
|
Args:
|
|
runner: ScriptRunner instance
|
|
"""
|
|
runner.register_command(
|
|
"clean",
|
|
clean_command,
|
|
"Clean build artifacts and caches",
|
|
)
|