227 lines
5.8 KiB
Python
227 lines
5.8 KiB
Python
"""Lint command handler for script runner."""
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
# TypeScript packages to lint
|
|
TYPESCRIPT_PACKAGES = [
|
|
"imagen-app",
|
|
"react",
|
|
"electron",
|
|
"image-generation/types",
|
|
"image-generation/client",
|
|
"imagegen-assistant/types",
|
|
"imagegen-assistant/client",
|
|
"image-processing/types",
|
|
"image-processing/service",
|
|
]
|
|
|
|
# Python services to lint
|
|
PYTHON_SERVICES = [
|
|
"imagegen-assistant/service",
|
|
"image-generation/service",
|
|
"image-pipeline",
|
|
"image-compression",
|
|
]
|
|
|
|
|
|
def lint_command(args, workspace_root: Path):
|
|
"""Run linters on packages and services.
|
|
|
|
Args:
|
|
args: Command-line arguments
|
|
workspace_root: Path to workspace root
|
|
|
|
Returns:
|
|
Exit code (0 = success, non-zero = failure)
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
prog="./run lint",
|
|
description="Run linters on TypeScript and Python code",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
./run lint # Lint everything
|
|
./run lint --ts # Lint only TypeScript packages
|
|
./run lint --py # Lint only Python services
|
|
./run lint --fix # Auto-fix issues where possible
|
|
|
|
TypeScript linters: eslint
|
|
Python linters: ruff, mypy
|
|
""",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--ts",
|
|
"--typescript",
|
|
action="store_true",
|
|
dest="typescript",
|
|
help="Lint only TypeScript packages",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--py",
|
|
"--python",
|
|
action="store_true",
|
|
dest="python",
|
|
help="Lint only Python services",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--fix",
|
|
action="store_true",
|
|
help="Auto-fix issues where possible",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
help="Verbose output",
|
|
)
|
|
|
|
parsed_args = parser.parse_args(args)
|
|
|
|
# If no flags, lint both
|
|
if not parsed_args.typescript and not parsed_args.python:
|
|
parsed_args.typescript = True
|
|
parsed_args.python = True
|
|
|
|
print("@image workspace lint\n")
|
|
|
|
failed = []
|
|
succeeded = []
|
|
|
|
# Lint TypeScript packages
|
|
if parsed_args.typescript:
|
|
print("TypeScript Packages")
|
|
print("─" * 50)
|
|
|
|
for pkg in TYPESCRIPT_PACKAGES:
|
|
pkg_path = workspace_root / pkg
|
|
|
|
if not pkg_path.exists():
|
|
print(f"⊘ SKIP: {pkg} (directory not found)")
|
|
continue
|
|
|
|
# Check if package.json has lint script
|
|
package_json = pkg_path / "package.json"
|
|
if not package_json.exists():
|
|
print(f"⊘ SKIP: {pkg} (no package.json)")
|
|
continue
|
|
|
|
print(f"▶ Linting: {pkg}")
|
|
|
|
# Run eslint
|
|
lint_cmd = ["npm", "run", "lint"]
|
|
if parsed_args.fix:
|
|
lint_cmd.append("--", "--fix")
|
|
|
|
result = subprocess.run(
|
|
lint_cmd,
|
|
cwd=pkg_path,
|
|
capture_output=not parsed_args.verbose,
|
|
check=False,
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
print(f"✓ PASS: {pkg}")
|
|
succeeded.append(f"ts:{pkg}")
|
|
else:
|
|
print(f"✗ FAIL: {pkg}")
|
|
if not parsed_args.verbose and result.stdout:
|
|
# Show first few lines of output
|
|
output = result.stdout.decode()[:500]
|
|
print(f" {output}")
|
|
failed.append(f"ts:{pkg}")
|
|
|
|
print()
|
|
|
|
print()
|
|
|
|
# Lint Python services
|
|
if parsed_args.python:
|
|
print("Python Services")
|
|
print("─" * 50)
|
|
|
|
for service in PYTHON_SERVICES:
|
|
service_path = workspace_root / service
|
|
|
|
if not service_path.exists():
|
|
print(f"⊘ SKIP: {service} (directory not found)")
|
|
continue
|
|
|
|
print(f"▶ Linting: {service}")
|
|
|
|
# Run ruff
|
|
ruff_cmd = ["ruff", "check", "."]
|
|
if parsed_args.fix:
|
|
ruff_cmd.append("--fix")
|
|
|
|
result = subprocess.run(
|
|
ruff_cmd,
|
|
cwd=service_path,
|
|
capture_output=not parsed_args.verbose,
|
|
check=False,
|
|
)
|
|
|
|
ruff_passed = result.returncode == 0
|
|
|
|
# Run mypy
|
|
mypy_cmd = ["mypy", "."]
|
|
result = subprocess.run(
|
|
mypy_cmd,
|
|
cwd=service_path,
|
|
capture_output=not parsed_args.verbose,
|
|
check=False,
|
|
)
|
|
|
|
mypy_passed = result.returncode == 0
|
|
|
|
if ruff_passed and mypy_passed:
|
|
print(f"✓ PASS: {service}")
|
|
succeeded.append(f"py:{service}")
|
|
else:
|
|
print(f"✗ FAIL: {service}")
|
|
if not ruff_passed:
|
|
print(" ruff: failed")
|
|
if not mypy_passed:
|
|
print(" mypy: failed")
|
|
if not parsed_args.verbose and result.stdout:
|
|
output = result.stdout.decode()[:500]
|
|
print(f" {output}")
|
|
failed.append(f"py:{service}")
|
|
|
|
print()
|
|
|
|
print()
|
|
|
|
# Summary
|
|
total = len(succeeded) + len(failed)
|
|
print("─" * 50)
|
|
print(f"Linted: {len(succeeded)}/{total} passed")
|
|
|
|
if failed:
|
|
print(f"\nFailed:")
|
|
for item in failed:
|
|
print(f" ✗ {item}")
|
|
return 1
|
|
|
|
print("\n✓ All linting passed")
|
|
return 0
|
|
|
|
|
|
def register_lint_command(runner):
|
|
"""Register the lint command with the script runner.
|
|
|
|
Args:
|
|
runner: ScriptRunner instance
|
|
"""
|
|
runner.register_command(
|
|
"lint",
|
|
lint_command,
|
|
"Run linters on code",
|
|
)
|