diff --git a/.husky/lint-python.sh b/.husky/lint-python.sh new file mode 100755 index 000000000..311ab3416 --- /dev/null +++ b/.husky/lint-python.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env sh +# Lint staged Python files against each project's own ruff config. strands-py/ and +# harness-py/ each carry their own [tool.ruff]; ruff resolves the nearest config per file, +# so one invocation covers both. Runs ruff only (no mypy) as a fast pre-filter; CI's lint +# job remains the source of truth. Range matches the root pyproject.toml and both SDKs so the +# hook and CI resolve the same ruff. +set -eu + +RUFF_SPEC='ruff>=0.16.0,<0.17.0' + +staged_python_files=$( + git diff --cached --name-only --diff-filter=ACMR -- strands-py harness-py \ + | { grep -E '\.py$' || true; } +) + +if [ -z "$staged_python_files" ]; then + echo "No staged Python files under strands-py/ or harness-py/; skipping ruff." + exit 0 +fi + +if ! command -v uvx >/dev/null 2>&1; then + echo "uvx not found. Install uv (https://docs.astral.sh/uv/) to run the Python lint hook." >&2 + exit 1 +fi + +echo "Linting staged Python files with ruff..." +status=0 +printf '%s\n' "$staged_python_files" | xargs uvx --from "$RUFF_SPEC" ruff check || status=1 +printf '%s\n' "$staged_python_files" | xargs uvx --from "$RUFF_SPEC" ruff format --check || status=1 +if [ "$status" -ne 0 ]; then + echo "Python lint/format failed. Fix with 'ruff check --fix' and 'ruff format'. Commit aborted." >&2 + exit 1 +fi diff --git a/.husky/pre-commit b/.husky/pre-commit index aefffc3a0..a2d2fc069 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,9 @@ echo "Running pre-commit checks..." +# Lint staged Python files (strands-py/, harness-py/) with ruff — a fast pre-filter, not full CI parity +echo "Linting Python..." +npm run lint:py || { echo "Python lint failed. Commit aborted."; exit 1; } + # Build (required for integ type-check: workspace symlink resolves to dist/) echo "Building..." npm run build || { echo "Build failed. Commit aborted."; exit 1; } diff --git a/harness-py/pyproject.toml b/harness-py/pyproject.toml index 4efae0801..2a0569a57 100644 --- a/harness-py/pyproject.toml +++ b/harness-py/pyproject.toml @@ -44,7 +44,7 @@ litellm = ["strands-agents[litellm]"] dev = [ "pytest>=8.0.0,<9.0.0", "pytest-asyncio>=0.24.0,<1.0.0", - "ruff>=0.9.0,<1.0.0", + "ruff>=0.16.0,<0.17.0", "strands-agents[openai,anthropic,gemini,ollama,litellm]", "openai>=2.0.0,<3.0.0", # The MCP test fixtures use mcp v1's FastMCP API; the SDK allows mcp 2.x, which renamed it. diff --git a/package.json b/package.json index 6c946b04b..27c839b1e 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "test:browser:install": "npm run test:browser:install -w strands-ts", "test:package": "npm run test:package -w strands-ts", "lint": "npm run lint -w strands-ts", + "lint:py": "sh .husky/lint-python.sh", "format": "npm run format -w strands-ts", "format:check": "npm run format:check -w strands-ts", "type-check": "npm run type-check -w strands-ts", diff --git a/pyproject.toml b/pyproject.toml index 7139951dc..d87d01b19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,9 @@ version = "0.0.0" description = "Shared Python tooling for the Strands monorepo. Not published." requires-python = ">=3.10" dependencies = [ - # Linter/formatter. - "ruff>=0.13.0,<0.15.0", + # Linter/formatter. Range matches strands-py and harness-py so the whole monorepo + # (and the pre-commit hook) resolves the same ruff. + "ruff>=0.16.0,<0.17.0", # Type checker. "pyright>=1.1.400", # Test runner for the shared venv. diff --git a/strands-py/pyproject.toml b/strands-py/pyproject.toml index df48d5c3c..12ffda807 100644 --- a/strands-py/pyproject.toml +++ b/strands-py/pyproject.toml @@ -109,7 +109,7 @@ dev = [ "pytest-asyncio>=1.0.0,<1.5.0", "pytest-timeout>=2.0.0,<3.0.0", "pytest-xdist>=3.0.0,<4.0.0", - "ruff>=0.13.0,<0.17.0", + "ruff>=0.16.0,<0.17.0", "tenacity>=9.0.0,<10.0.0", ] @@ -135,7 +135,7 @@ installer = "uv" features = ["all"] dependencies = [ "mypy>=1.15.0,<3.0.0", - "ruff>=0.13.0,<0.17.0", + "ruff>=0.16.0,<0.17.0", # Include required package dependencies for mypy "boto3-stubs[bedrock-agent,bedrock-agent-runtime,s3]>=1.26.0,<2.0.0", "strands-agents @ {root:uri}",