From 0ec4ba3cdacd0d6984635838b2e5929b4ed16f80 Mon Sep 17 00:00:00 2001 From: alro1965 Date: Mon, 18 May 2026 07:29:27 -0400 Subject: [PATCH] add(scripts): dev convenience runner (PowerShell + Bash) Wrap the most common day-to-day commands so we stop typing .venv/Scripts/python.exe -m ... by hand. Mirrors the same tasks on both shells; binaries always resolved from the project's .venv so the host machine's globally-installed Python doesn't leak in. Tasks (both shells): install create .venv, install arautopilot[dev] in editable mode test run pytest (extra args forwarded: e.g. test -k roundtrip) test-cov pytest with branch coverage + HTML report lint ruff check (read-only) fix ruff check --fix + ruff format format ruff format typecheck mypy --strict over core/library/shared check full quality gate: lint + typecheck + test demo run examples/sprint0_demo.py clean remove build/cache artefacts + examples/output Usage: .\scripts\dev.ps1 check (Windows PowerShell) bash scripts/dev.sh check (Git Bash / WSL / Linux) Verification: `bash scripts/dev.sh check` runs lint + typecheck + 80 tests all green in ~0.5s on this machine. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/dev.ps1 | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ scripts/dev.sh | 131 +++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 scripts/dev.ps1 create mode 100644 scripts/dev.sh diff --git a/scripts/dev.ps1 b/scripts/dev.ps1 new file mode 100644 index 0000000..d425abe --- /dev/null +++ b/scripts/dev.ps1 @@ -0,0 +1,203 @@ +# ============================================================================= +# AR-Autopilot — developer convenience script (PowerShell, Windows) +# ============================================================================= +# +# Usage (from the repo root): +# +# .\scripts\dev.ps1 [args...] +# +# Tasks: +# +# install Install the package + dev dependencies into .venv (creates venv if needed) +# test Run the pytest suite +# test-cov Run pytest with branch coverage report +# lint Run ruff check (no auto-fix) +# fix Run ruff check --fix (auto-fix safe findings) then ruff format +# format Run ruff format only +# typecheck Run mypy --strict over core/library/shared +# check Run lint + typecheck + test (the full quality gate) +# demo Run examples/sprint0_demo.py +# clean Remove build artefacts, __pycache__/, .pytest_cache/, .mypy_cache/, .ruff_cache/ +# help Show this message +# +# All tasks resolve binaries from .\.venv\Scripts\ — they never depend on +# what's globally installed on the machine. +# +# ============================================================================= + +[CmdletBinding()] +param( + [Parameter(Position = 0)] + [string] $Task = 'help', + + [Parameter(ValueFromRemainingArguments = $true)] + [string[]] $Args +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +# Resolve repo root regardless of where the script was invoked from. +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') +Set-Location $RepoRoot + +$VenvDir = Join-Path $RepoRoot '.venv' +$VenvScripts = Join-Path $VenvDir 'Scripts' +$Python = Join-Path $VenvScripts 'python.exe' +$Pytest = Join-Path $VenvScripts 'pytest.exe' +$Ruff = Join-Path $VenvScripts 'ruff.exe' +$Mypy = Join-Path $VenvScripts 'mypy.exe' + +function Assert-Venv { + if (-not (Test-Path $Python)) { + throw "Virtual environment not found at $VenvDir. Run: .\scripts\dev.ps1 install" + } +} + +function Invoke-Install { + if (-not (Test-Path $Python)) { + Write-Host "[install] Creating virtual environment at $VenvDir ..." + & py -m venv $VenvDir + if ($LASTEXITCODE -ne 0) { throw "venv creation failed (exit $LASTEXITCODE)" } + } + Write-Host "[install] Upgrading pip ..." + & $Python -m pip install --upgrade pip + if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed" } + + Write-Host "[install] Installing arautopilot[dev] in editable mode ..." + & $Python -m pip install -e ".[dev]" + if ($LASTEXITCODE -ne 0) { throw "pip install failed" } + + Write-Host "[install] OK" +} + +function Invoke-Test { + Assert-Venv + & $Pytest @Args + exit $LASTEXITCODE +} + +function Invoke-TestCov { + Assert-Venv + & $Pytest --cov=arautopilot --cov-report=term-missing --cov-report=html @Args + exit $LASTEXITCODE +} + +function Invoke-Lint { + Assert-Venv + & $Ruff check arautopilot/ + exit $LASTEXITCODE +} + +function Invoke-Fix { + Assert-Venv + & $Ruff check arautopilot/ --fix + $check = $LASTEXITCODE + & $Ruff format arautopilot/ + $format = $LASTEXITCODE + if ($check -ne 0) { exit $check } + exit $format +} + +function Invoke-Format { + Assert-Venv + & $Ruff format arautopilot/ + exit $LASTEXITCODE +} + +function Invoke-Typecheck { + Assert-Venv + & $Mypy arautopilot/core arautopilot/library arautopilot/shared + exit $LASTEXITCODE +} + +function Invoke-Check { + Assert-Venv + Write-Host "[check] (1/3) lint ..." + & $Ruff check arautopilot/ + if ($LASTEXITCODE -ne 0) { Write-Host "[check] FAILED at lint"; exit $LASTEXITCODE } + + Write-Host "[check] (2/3) typecheck ..." + & $Mypy arautopilot/core arautopilot/library arautopilot/shared + if ($LASTEXITCODE -ne 0) { Write-Host "[check] FAILED at typecheck"; exit $LASTEXITCODE } + + Write-Host "[check] (3/3) tests ..." + & $Pytest + if ($LASTEXITCODE -ne 0) { Write-Host "[check] FAILED at tests"; exit $LASTEXITCODE } + + Write-Host "[check] OK" +} + +function Invoke-Demo { + Assert-Venv + & $Python examples/sprint0_demo.py + exit $LASTEXITCODE +} + +function Invoke-Clean { + $patterns = @( + '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache', + 'build', 'dist', '*.egg-info', 'htmlcov', '.coverage' + ) + foreach ($p in $patterns) { + Get-ChildItem -Path $RepoRoot -Filter $p -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -notmatch '\\\.venv\\' } | + ForEach-Object { + Write-Host "[clean] removing $($_.FullName)" + Remove-Item -Recurse -Force -LiteralPath $_.FullName -ErrorAction SilentlyContinue + } + } + Get-ChildItem -Path $RepoRoot -Filter '*.pyc' -Recurse -Force -ErrorAction SilentlyContinue | + ForEach-Object { + Remove-Item -Force -LiteralPath $_.FullName -ErrorAction SilentlyContinue + } + if (Test-Path 'examples/output') { + Write-Host "[clean] removing examples/output/" + Remove-Item -Recurse -Force 'examples/output' -ErrorAction SilentlyContinue + } + Write-Host "[clean] OK" +} + +function Show-Help { + @' +AR-Autopilot — dev tasks + +Usage: .\scripts\dev.ps1 [args...] + +Tasks: + install Install package + dev deps into .venv (creates venv if needed) + test Run pytest + test-cov Run pytest with branch coverage + lint Run ruff check (read-only) + fix Run ruff check --fix + ruff format + format Run ruff format + typecheck Run mypy --strict + check lint + typecheck + test (full quality gate) + demo Run examples/sprint0_demo.py + clean Remove build / cache artefacts + help Show this message + +Tip: any extra args are forwarded to the underlying tool, e.g. + .\scripts\dev.ps1 test -k roundtrip + .\scripts\dev.ps1 test -v --tb=short +'@ | Write-Host +} + +switch ($Task.ToLower()) { + 'install' { Invoke-Install } + 'test' { Invoke-Test } + 'test-cov' { Invoke-TestCov } + 'lint' { Invoke-Lint } + 'fix' { Invoke-Fix } + 'format' { Invoke-Format } + 'typecheck' { Invoke-Typecheck } + 'check' { Invoke-Check } + 'demo' { Invoke-Demo } + 'clean' { Invoke-Clean } + 'help' { Show-Help } + default { + Write-Host "Unknown task: $Task" -ForegroundColor Red + Show-Help + exit 2 + } +} diff --git a/scripts/dev.sh b/scripts/dev.sh new file mode 100644 index 0000000..d37c577 --- /dev/null +++ b/scripts/dev.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# ============================================================================= +# AR-Autopilot — developer convenience script (Git Bash / WSL / Linux) +# ============================================================================= +# +# Mirror of scripts/dev.ps1 for Bash environments. From the repo root: +# +# bash scripts/dev.sh [args...] +# +# Tasks: install | test | test-cov | lint | fix | format | typecheck +# check | demo | clean | help +# ============================================================================= + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$REPO_ROOT" + +VENV_DIR="$REPO_ROOT/.venv" +if [[ -x "$VENV_DIR/Scripts/python.exe" ]]; then + # Windows-style venv (Git Bash on Windows) + PYTHON="$VENV_DIR/Scripts/python.exe" + PYTEST="$VENV_DIR/Scripts/pytest.exe" + RUFF="$VENV_DIR/Scripts/ruff.exe" + MYPY="$VENV_DIR/Scripts/mypy.exe" +else + PYTHON="$VENV_DIR/bin/python" + PYTEST="$VENV_DIR/bin/pytest" + RUFF="$VENV_DIR/bin/ruff" + MYPY="$VENV_DIR/bin/mypy" +fi + +assert_venv() { + if [[ ! -x "$PYTHON" ]]; then + echo "Virtual environment not found at $VENV_DIR" >&2 + echo "Run: bash scripts/dev.sh install" >&2 + exit 1 + fi +} + +task_install() { + if [[ ! -x "$PYTHON" ]]; then + echo "[install] Creating virtual environment at $VENV_DIR ..." + python -m venv "$VENV_DIR" || py -m venv "$VENV_DIR" + fi + echo "[install] Upgrading pip ..." + "$PYTHON" -m pip install --upgrade pip + echo "[install] Installing arautopilot[dev] in editable mode ..." + "$PYTHON" -m pip install -e ".[dev]" + echo "[install] OK" +} + +task_test() { assert_venv; "$PYTEST" "$@"; } +task_test_cov() { assert_venv; "$PYTEST" --cov=arautopilot --cov-report=term-missing --cov-report=html "$@"; } +task_lint() { assert_venv; "$RUFF" check arautopilot/; } +task_fix() { assert_venv; "$RUFF" check arautopilot/ --fix; "$RUFF" format arautopilot/; } +task_format() { assert_venv; "$RUFF" format arautopilot/; } +task_typecheck() { assert_venv; "$MYPY" arautopilot/core arautopilot/library arautopilot/shared; } + +task_check() { + assert_venv + echo "[check] (1/3) lint ..." + "$RUFF" check arautopilot/ + echo "[check] (2/3) typecheck ..." + "$MYPY" arautopilot/core arautopilot/library arautopilot/shared + echo "[check] (3/3) tests ..." + "$PYTEST" + echo "[check] OK" +} + +task_demo() { assert_venv; "$PYTHON" examples/sprint0_demo.py; } + +task_clean() { + find . -type d \( \ + -name '__pycache__' -o \ + -name '.pytest_cache' -o \ + -name '.mypy_cache' -o \ + -name '.ruff_cache' -o \ + -name 'build' -o \ + -name 'dist' -o \ + -name '*.egg-info' -o \ + -name 'htmlcov' \ + \) -not -path './.venv/*' -prune -exec rm -rf {} + 2>/dev/null || true + find . -type f -name '*.pyc' -not -path './.venv/*' -delete 2>/dev/null || true + rm -f .coverage + rm -rf examples/output + echo "[clean] OK" +} + +task_help() { + cat <<'HELP' +AR-Autopilot — dev tasks + +Usage: bash scripts/dev.sh [args...] + +Tasks: + install Install package + dev deps into .venv + test Run pytest + test-cov Run pytest with coverage report + lint Run ruff check (read-only) + fix Run ruff check --fix + ruff format + format Run ruff format + typecheck Run mypy --strict + check lint + typecheck + test (full quality gate) + demo Run examples/sprint0_demo.py + clean Remove build / cache artefacts + help Show this message +HELP +} + +task="${1:-help}" +shift || true +case "$task" in + install) task_install "$@" ;; + test) task_test "$@" ;; + test-cov) task_test_cov "$@" ;; + lint) task_lint "$@" ;; + fix) task_fix "$@" ;; + format) task_format "$@" ;; + typecheck) task_typecheck "$@" ;; + check) task_check "$@" ;; + demo) task_demo "$@" ;; + clean) task_clean "$@" ;; + help|-h|--help) task_help ;; + *) + echo "Unknown task: $task" >&2 + task_help + exit 2 + ;; +esac