diff --git a/CLAUDE.md b/CLAUDE.md index 9c0ee4d..0beba01 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,41 @@ On 2026-01-11, raw `lxc delete` destroyed 8 production services causing complete ## Project Overview -UN CLI Inception - The UN CLI written in every language it can execute. 42 implementations, one unified interface. +UN CLI Inception - The UN CLI written in every language it can execute. 42+ implementations, one unified interface. + +### SDK Architecture (In Growth) + +**Current State**: Root-level implementations (un.py, un.c, un.go, etc.) serving as both CLI + embeddable libraries. + +**Target State**: Migrate to `clients/` directory structure: +``` +clients/ +├── python/ # clients/python/un.py - sync/async client + CLI +├── javascript/ # clients/javascript/un.js - SDK + CLI +├── go/ # clients/go/un.go - SDK + CLI +├── java/ # clients/java/Un.java - SDK + CLI +├── ruby/ # clients/ruby/un.rb - SDK + CLI +├── php/ # clients/php/un.php - SDK + CLI +├── rust/ # clients/rust/un.rs - SDK + CLI +├── {42+ more}/ +``` + +**Each client implementation serves THREE purposes**: +1. **Standalone CLI program** - Argparse/getopt with full command support (execute, session, service) +2. **Importable client library** - Can import and use as an SDK in other code +3. **Embeddable library** - Can be bundled into other language projects + +**Example (Python)**: +```python +# As CLI: python clients/python/un.py test/fib.py +# As library: from clients.python.un import UnsandboxClient +# As embedded: copy un.py into your project, import locally +``` + +**Migration Path**: +- Phase 1 (Current): Grow clients/ directory in parallel with root un.* files +- Phase 2: Root files eventually deprecated in favor of clients/ +- Phase 3: Root files maintained for backwards compatibility only ## Authentication @@ -110,6 +144,8 @@ The test suite in `tests/run_all_tests.sh` currently skips languages without loc ## Running Tests +### Local Testing + ```bash # Set auth export UNSANDBOX_PUBLIC_KEY="unsb-pk-zhi3-b6cv-jvqc-uven" @@ -122,8 +158,32 @@ export UNSANDBOX_SECRET_KEY="unsb-sk-z4a93-a33xy-7u7eh-pngpg" python3 tests/test_un_py.py lua tests/test_un_lua.lua bash tests/test_un_sh.sh + +# Run client-specific tests (after migration to clients/) +make test-python +make test-go +make test-javascript ``` +### CI Testing Strategy (Smart Detection) + +When changes are pushed: +1. **Change Detection** - `detect-changes.sh` identifies which files changed +2. **Per-Language Tests** - Only language tests for CHANGED clients run (e.g., modify clients/python/ → pytest runs) +3. **Cross-Language Tests** - All affected clients validated against API +4. **Science Jobs** - Pool burning with real workloads + +**Example**: Modify `clients/python/un.py`: +``` +✓ Python pytest runs +✓ Python type checking (if applicable) +✓ Python integration tests with API +✓ Python embedding tests (can import in other code) +⚠ Go, Ruby, JavaScript tests SKIP (unchanged) +``` + +See **TESTING-STRATEGY.md** for complete testing matrix. + ## Common Test Fixes ### Bash arithmetic in `set -e` mode diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f02a49 --- /dev/null +++ b/Makefile @@ -0,0 +1,211 @@ +.PHONY: help test test-all test-python test-go test-javascript test-ruby test-php test-rust test-java test-bash test-perl test-lua + +help: + @echo "UN Inception Testing Targets" + @echo "" + @echo "Usage: make [target]" + @echo "" + @echo "Test Targets:" + @echo " test-all Run all available tests" + @echo " test-python Test Python client (un.py)" + @echo " test-go Test Go client (un.go)" + @echo " test-javascript Test JavaScript client (un.js)" + @echo " test-ruby Test Ruby client (un.rb)" + @echo " test-php Test PHP client (un.php)" + @echo " test-rust Test Rust client (un.rs)" + @echo " test-java Test Java client (Un.java)" + @echo " test-bash Test Bash client (un.sh)" + @echo " test-perl Test Perl client (un.pl)" + @echo " test-lua Test Lua client (un.lua)" + @echo "" + @echo "Integration Tests:" + @echo " test-integration Test all clients against API" + @echo "" + @echo "Examples:" + @echo " make test-python # Test only Python" + @echo " make test-all # Test everything" + @echo "" + +test: test-all + +test-all: + @echo "Running all available tests..." + ./tests/run_all_tests.sh + +test-python: + @echo "Testing Python client (clients/python/un.py)..." + @if [ -f clients/python/un.py ]; then \ + python3 -m py_compile clients/python/un.py && \ + python3 clients/python/un.py test/fib.py && \ + [ -f tests/test_un_py.py ] && pytest tests/test_un_py.py -v || true; \ + elif [ -f un.py ]; then \ + python3 -m py_compile un.py && \ + python3 un.py test/fib.py && \ + [ -f tests/test_un_py.py ] && pytest tests/test_un_py.py -v || true; \ + else \ + echo "ERROR: Python client not found"; \ + exit 1; \ + fi + +test-go: + @echo "Testing Go client (clients/go/un.go)..." + @if [ -d clients/go ]; then \ + cd clients/go && \ + go mod tidy 2>/dev/null || true && \ + go test -v && \ + go run un.go ../../test/fib.py; \ + elif [ -f un.go ]; then \ + go run un.go test/fib.py; \ + else \ + echo "Go client not found"; \ + exit 1; \ + fi + +test-javascript: + @echo "Testing JavaScript client (clients/javascript/un.js)..." + @if [ -f clients/javascript/un.js ]; then \ + node clients/javascript/un.js test/fib.py && \ + [ -f tests/test_un_js.js ] && node tests/test_un_js.js || true; \ + elif [ -f un.js ]; then \ + node un.js test/fib.py && \ + [ -f tests/test_un_js.js ] && node tests/test_un_js.js || true; \ + else \ + echo "JavaScript client not found"; \ + exit 1; \ + fi + +test-ruby: + @echo "Testing Ruby client (clients/ruby/un.rb)..." + @if [ -f clients/ruby/un.rb ]; then \ + ruby -w clients/ruby/un.rb test/fib.py && \ + [ -f tests/test_un_rb.rb ] && ruby tests/test_un_rb.rb || true; \ + elif [ -f un.rb ]; then \ + ruby -w un.rb test/fib.py; \ + else \ + echo "Ruby client not found"; \ + exit 1; \ + fi + +test-php: + @echo "Testing PHP client (clients/php/un.php)..." + @if [ -f clients/php/un.php ]; then \ + php -l clients/php/un.php && \ + php clients/php/un.php test/fib.py; \ + elif [ -f un.php ]; then \ + php -l un.php && \ + php un.php test/fib.py; \ + else \ + echo "PHP client not found"; \ + exit 1; \ + fi + +test-rust: + @echo "Testing Rust client (clients/rust/un.rs)..." + @if [ -d clients/rust ]; then \ + cd clients/rust && \ + cargo test --release && \ + cargo run --release -- ../../test/fib.py; \ + elif [ -f un.rs ]; then \ + rustc un.rs -o un && \ + ./un test/fib.py; \ + else \ + echo "Rust client not found"; \ + exit 1; \ + fi + +test-java: + @echo "Testing Java client (clients/java/Un.java)..." + @if [ -f clients/java/Un.java ]; then \ + cd clients/java && \ + javac Un.java && \ + java -cp . Un ../../test/fib.py; \ + elif [ -f Un.java ]; then \ + javac Un.java && \ + java -cp . Un test/fib.py; \ + else \ + echo "Java client not found"; \ + exit 1; \ + fi + +test-bash: + @echo "Testing Bash client (clients/bash/un.sh)..." + @if [ -f clients/bash/un.sh ]; then \ + bash -n clients/bash/un.sh && \ + bash clients/bash/un.sh test/fib.py; \ + elif [ -f un.sh ]; then \ + bash -n un.sh && \ + bash un.sh test/fib.py; \ + else \ + echo "Bash client not found"; \ + exit 1; \ + fi + +test-perl: + @echo "Testing Perl client (clients/perl/un.pl)..." + @if [ -f clients/perl/un.pl ]; then \ + perl -c clients/perl/un.pl && \ + perl clients/perl/un.pl test/fib.py; \ + elif [ -f un.pl ]; then \ + perl -c un.pl && \ + perl un.pl test/fib.py; \ + else \ + echo "Perl client not found"; \ + exit 1; \ + fi + +test-lua: + @echo "Testing Lua client (clients/lua/un.lua)..." + @if [ -f clients/lua/un.lua ]; then \ + lua clients/lua/un.lua test/fib.py; \ + elif [ -f un.lua ]; then \ + lua un.lua test/fib.py; \ + else \ + echo "Lua client not found"; \ + exit 1; \ + fi + +test-integration: + @echo "Testing all clients against API..." + @if [ -f tests/integration-all-clients.sh ]; then \ + bash tests/integration-all-clients.sh; \ + else \ + echo "Integration test script not found"; \ + exit 1; \ + fi + +test-parity: + @echo "Testing feature parity across all clients..." + @if [ -f tests/feature-parity-matrix.sh ]; then \ + bash tests/feature-parity-matrix.sh; \ + else \ + echo "Feature parity test script not found"; \ + exit 1; \ + fi + +test-ci-locally: + @echo "Running CI pipeline locally (detection + build + test)..." + @bash scripts/detect-changes.sh > changes.json + @echo "Changes detected:" + @cat changes.json + @bash scripts/generate-matrix.sh > test-matrix.yml + @echo "Test matrix generated:" + @cat test-matrix.yml + @bash scripts/build-clients.sh || true + @echo "Ready to run tests from test-matrix.yml" + +.PHONY: lint +lint: + @echo "Linting all clients (where applicable)..." + @command -v shellcheck >/dev/null 2>&1 && bash -n un.sh && shellcheck un.sh || echo "Skipping shellcheck" + @command -v python3 >/dev/null 2>&1 && python3 -m pylint un.py 2>/dev/null || echo "Skipping pylint" + @command -v eslint >/dev/null 2>&1 && npx eslint un.js 2>/dev/null || echo "Skipping eslint" + @command -v rubocop >/dev/null 2>&1 && rubocop un.rb 2>/dev/null || echo "Skipping rubocop" + @echo "Linting complete" + +.PHONY: clean +clean: + @echo "Cleaning build artifacts..." + @rm -rf build/ bin/ dist/ *.o un_* + @find . -name "*.pyc" -delete + @find . -name "__pycache__" -type d -delete + @echo "Clean complete" diff --git a/TESTING-STRATEGY.md b/TESTING-STRATEGY.md new file mode 100644 index 0000000..3f0362e --- /dev/null +++ b/TESTING-STRATEGY.md @@ -0,0 +1,407 @@ +# Testing Strategy for Multi-Language UN Clients + +## Overview + +UN Inception implements the UN CLI in 42+ languages. Each implementation serves as: +1. **Standalone CLI** - Full featured command-line tool +2. **Client Library** - Importable SDK for other projects +3. **Embeddable** - Can be bundled/copied into other projects + +**Goal**: Ensure all clients work correctly as the codebase evolves while only running necessary tests. + +--- + +## Smart Change Detection + +The CI pipeline uses **smart change detection** to run only relevant tests: + +### How it Works + +1. **Pre-stage: detect-changes.sh** + - Compares HEAD with origin/main + - Identifies which languages/clients changed + - Generates `changes.json` with affected languages + +2. **Pre-stage: generate-matrix.sh** + - Reads `changes.json` + - Generates `test-matrix.yml` with only needed test jobs + - Includes cross-language validation for all clients + +3. **Test Stage** + - Dynamic job inclusion from `test-matrix.yml` + - Only language-specific tests run when that language changed + - Always includes integration tests (API contract validation) + +### Example: Changes in clients/python/ + +```json +{ + "changed_clients": ["python"], + "root_implementations": [], + "test_matrix": { + "python": { + "test_script": "pytest tests/test_un_py.py -v", + "environment": ["python3"], + "should_run": true + }, + "integration": { + "test_script": "bash tests/integration-all-clients.sh", + "should_run": true + } + } +} +``` + +### Supported Change Patterns + +| Changed File | Triggered Tests | +|---|---| +| `clients/python/*` | Python tests + integration | +| `clients/go/*` | Go tests + integration | +| `clients/javascript/*` | Node.js/TypeScript tests + integration | +| `clients/*/tests/*` | Language tests + integration | +| `test/` (shared tests) | All language tests + integration | +| `un.c` (root) | C tests + integration + all clients (fundamental change) | +| `un.py` (root) | Python tests + integration | +| `CLAUDE.md` | Documentation validation only | + +--- + +## Testing Matrix by Language + +### Tier 1: Compiled Languages (Fast) + +These languages have fast compilation and test cycles. + +#### Go +- **File**: `clients/go/un.go` +- **Tests**: `go test ./...` +- **Integration**: `bash tests/integration-go-client.sh` +- **Time**: ~5 seconds + +```bash +cd clients/go +go test -v +go run un.go test/fib.py +``` + +#### Rust +- **File**: `clients/rust/un.rs` +- **Tests**: `cargo test` +- **Integration**: `bash tests/integration-rust-client.sh` +- **Time**: ~10 seconds (cached) + +```bash +cd clients/rust +cargo test --release +cargo run -- test/fib.py +``` + +#### C/C++ +- **File**: `clients/c/un.c` OR `clients/cpp/un.cpp` +- **Build**: `gcc un.c -o un -lssl -lcrypto` OR `g++ un.cpp -o un` +- **Tests**: `bash tests/test_un_c.sh` +- **Integration**: `./un test/fib.py` +- **Time**: ~2 seconds + +#### Java +- **File**: `clients/java/Un.java` +- **Compile**: `javac Un.java` +- **Tests**: `java -cp . Un test/fib.py` +- **Integration**: Full feature test suite +- **Time**: ~8 seconds (warm JVM) + +### Tier 2: Interpreted Languages (Medium) + +These have runtime interpretation but fast test cycles. + +#### Python +- **File**: `clients/python/un.py` +- **Tests**: + ```bash + pytest tests/test_un_py.py -v + python3 -m py_compile clients/python/un.py # Syntax check + python3 clients/python/un.py test/fib.py # Integration + ``` +- **Async Support**: Tests both sync and async client modes +- **Time**: ~3 seconds + +#### JavaScript/TypeScript +- **File**: `clients/javascript/un.js` (Node.js) +- **Tests**: + ```bash + node tests/test_un_js.js + npx eslint clients/javascript/un.js # Linting + node clients/javascript/un.js test/fib.py + ``` +- **Time**: ~2 seconds + +#### Ruby +- **File**: `clients/ruby/un.rb` +- **Tests**: + ```bash + ruby -w clients/ruby/un.rb test/fib.py # Syntax + execution + ruby tests/test_un_rb.rb + ``` +- **Time**: ~2 seconds + +#### PHP +- **File**: `clients/php/un.php` +- **Tests**: + ```bash + php -l clients/php/un.php # Syntax check + php clients/php/un.php test/fib.py # Integration + php -d display_errors=1 tests/test_un_php.php + ``` +- **Time**: ~2 seconds + +#### Bash/Shell +- **File**: `clients/bash/un.sh` +- **Tests**: + ```bash + bash -n clients/bash/un.sh # Syntax check + shellcheck clients/bash/un.sh # Linting + bash clients/bash/un.sh test/fib.py + ``` +- **Time**: ~1 second + +### Tier 3: Languages Requiring Inception + +These languages may not be installed locally. Use the **Inception Pattern** to test via unsandbox itself. + +#### Haskell, Julia, Clojure, Erlang, etc. +- **Pattern**: Use `un` (C implementation) to execute the language implementation through unsandbox +- **Command**: + ```bash + un -n semitrusted \ + -e UNSANDBOX_PUBLIC_KEY=$UNSANDBOX_PUBLIC_KEY \ + -e UNSANDBOX_SECRET_KEY=$UNSANDBOX_SECRET_KEY \ + clients/haskell/un.hs test/fib.py + ``` +- **Time**: ~6 seconds (network + unsandbox execution) + +--- + +## Test Categories + +### 1. Unit Tests +**Test**: Language-specific syntax and core logic + +```bash +# Each language tests its specific features +pytest tests/test_un_py.py +go test ./clients/go/... +cargo test -p un_client +ruby tests/test_un_rb.rb +``` + +**What fails here**: Syntax errors, missing dependencies, core logic bugs + +### 2. Integration Tests (API Contract) +**Test**: All clients communicate correctly with the API + +```bash +# Test EVERY client can: +# ✓ Authenticate (HMAC signature generation) +# ✓ Execute code (run test/fib.py via API) +# ✓ Handle responses +# ✓ Respect error codes + +bash tests/integration-all-clients.sh +``` + +**What fails here**: Authentication issues, API schema changes, network problems + +### 3. Embedding Tests +**Test**: Client can be imported/embedded in other code + +```python +# Python: Can import the client +from clients.python.un import UnsandboxClient +client = UnsandboxClient() +``` + +```javascript +// JavaScript: Can require the module +const { UnsandboxClient } = require('./clients/javascript/un.js'); +``` + +**What fails here**: Export/import structure, module API incompatibility + +### 4. Parity Tests (CI/Feature Checklist) +**Test**: All clients have feature parity + +Verify all 42+ clients support: +- ✓ Execute: `un file.py` +- ✓ Session: `un session` +- ✓ Service: `un service` +- ✓ All flags: `-e`, `-f`, `-n`, `-a`, `--tmux`, `--screen`, etc. + +```bash +bash tests/feature-parity-matrix.sh +``` + +**What fails here**: Missing features, argument parsing bugs, logic differences + +--- + +## CI Pipeline Structure + +### Stage: Pre (Change Detection) +``` +detect-changes.sh → changes.json +generate-matrix.sh → test-matrix.yml (dynamic jobs) +``` + +### Stage: Build +``` +build-clients.sh → Compile/prepare all changed clients +``` + +### Stage: Test (Dynamic) +``` +include: test-matrix.yml ← Dynamically generated per-language jobs + test-python.sh (only if clients/python/* changed) + test-go.sh (only if clients/go/* changed) + test-javascript.sh (only if clients/javascript/* changed) + ... + integration-all.sh (always runs - validates all clients) +``` + +### Stage: Science (Pool Burning) +``` +lint-all-sdks.sh → Code quality for all clients +benchmark-clients.sh → Performance comparison +validate-examples.sh → Real-world examples work +``` + +### Stage: Validate & Report +``` +Summary of test results, coverage, and quality metrics +``` + +--- + +## Local Development + +### Running All Tests Locally + +```bash +# Set environment +export UNSANDBOX_PUBLIC_KEY="unsb-pk-..." +export UNSANDBOX_SECRET_KEY="unsb-sk-..." + +# Run everything +./tests/run_all_tests.sh + +# Or run specific language +make test-python +make test-go +make test-javascript +make test-ruby +make test-php +``` + +### Testing a Specific Client After Changes + +```bash +# After editing clients/python/un.py +cd clients/python +pytest -v +python3 -m py_compile un.py +python3 un.py test/fib.py +python3 un.py session # Interactive test +python3 un.py service --help +``` + +### Testing Without Local Interpreter (Inception) + +```bash +# Don't have Haskell installed? Test it via unsandbox +un -n semitrusted \ + -e UNSANDBOX_PUBLIC_KEY=$UNSANDBOX_PUBLIC_KEY \ + -e UNSANDBOX_SECRET_KEY=$UNSANDBOX_SECRET_KEY \ + clients/haskell/un.hs test/fib.py + +# Test ALL languages via inception (if needed) +bash tests/inception-test-all.sh +``` + +--- + +## Common Failures & Fixes + +### Python Tests Fail +``` +❌ ModuleNotFoundError: No module named 'X' +✓ Fix: pip install -r clients/python/requirements.txt + +❌ HMAC signature mismatch +✓ Fix: Verify authentication code in un.py matches other clients + +❌ Import error when embedded +✓ Fix: Ensure no absolute imports, use relative imports for bundling +``` + +### Go Tests Fail +``` +❌ go.mod not found +✓ Fix: cd clients/go && go mod init clients/go + +❌ Module not found after changes +✓ Fix: go mod tidy + +❌ Compilation error with crypto +✓ Fix: Ensure Go 1.16+ (crypto/hmac in stdlib) +``` + +### JavaScript Tests Fail +``` +❌ Cannot find module +✓ Fix: npm install in clients/javascript + +❌ Async/await not working +✓ Fix: Ensure Node.js version supports async/await (>7.6.0) +``` + +### Integration Fails (All Languages) +``` +❌ 401 Unauthorized +✓ Fix: HMAC signature generation - compare with C reference (un.c) + +❌ 503 API unavailable +✓ Fix: Check api.unsandbox.com is up and accessible + +❌ Client hangs/timeout +✓ Fix: Check network access, proxy settings, firewall rules +``` + +--- + +## Rollout Schedule + +| Phase | Goal | Timeline | +|---|---|---| +| Phase 1 (Now) | Setup clients/ structure, smart CI | This week | +| Phase 2 | Migrate top 10 languages to clients/ | 2 weeks | +| Phase 3 | Migrate remaining 32 languages | 4 weeks | +| Phase 4 | Deprecate root un.* files | 8 weeks | +| Phase 5 | Archive/document legacy implementations | Optional | + +--- + +## Metrics & Coverage + +**Success Criteria**: +- ✓ All test jobs pass +- ✓ Each language has unit tests +- ✓ Each language has integration tests +- ✓ Feature parity matrix 100% (all 42 features in all 42 languages) +- ✓ CI matrix correctly detects changes and skips irrelevant tests +- ✓ No false positives (unrelated changes don't trigger irrelevant tests) + +**Tracking**: +- Build time by language (identify slow tests) +- Test coverage by client +- Feature parity matrix (visual dashboard) +- Inception test success rate (languages tested via unsandbox) diff --git a/scripts/detect-changes.sh b/scripts/detect-changes.sh index 6bdcb16..7972ba1 100755 --- a/scripts/detect-changes.sh +++ b/scripts/detect-changes.sh @@ -19,10 +19,20 @@ fi # Get all changed files in this commit CHANGED_FILES=$(git diff --name-only "$BASE...HEAD" 2>/dev/null || echo "") -# Extract unique languages from changed SDK files -CHANGED_LANGS=$(echo "$CHANGED_FILES" | grep -E '^un\.' | sed 's/un\.\([^.]*\).*/\1/' | sort -u || echo "") +# Extract unique languages from TWO sources: +# 1. Root-level files (un.py, un.go, etc.) +# 2. Client directory files (clients/python/*, clients/go/*, etc.) -# Map file extensions to language names +CHANGED_LANGS=$( + { + # Root-level implementations + echo "$CHANGED_FILES" | grep -E '^un\.' | sed 's/un\.\([^.]*\).*/\1/' + # Client directory implementations + echo "$CHANGED_FILES" | grep -E '^clients/([^/]+)/' | sed 's|^clients/\([^/]*\)/.*|\1|' + } | sort -u || echo "" +) + +# Map file extensions to language names (for root-level un.* files) declare -A LANG_MAP=( [py]="python" [js]="javascript" @@ -67,18 +77,69 @@ declare -A LANG_MAP=( [raku]="raku" ) +# Map directory names from clients/ to language names (clients/ already has language names) +declare -A DIR_MAP=( + [python]="python" + [javascript]="javascript" + [typescript]="typescript" + [go]="go" + [ruby]="ruby" + [php]="php" + [perl]="perl" + [lua]="lua" + [bash]="bash" + [rust]="rust" + [java]="java" + [csharp]="csharp" + [cpp]="cpp" + [c]="c" + [haskell]="haskell" + [kotlin]="kotlin" + [elixir]="elixir" + [erlang]="erlang" + [crystal]="crystal" + [dart]="dart" + [nim]="nim" + [julia]="julia" + [r]="r" + [groovy]="groovy" + [clojure]="clojure" + [fsharp]="fsharp" + [ocaml]="ocaml" + [objc]="objc" + [d]="d" + [vlang]="vlang" + [zig]="zig" + [fortran]="fortran" + [cobol]="cobol" + [scheme]="scheme" + [lisp]="lisp" + [tcl]="tcl" + [awk]="awk" + [prolog]="prolog" + [forth]="forth" + [powershell]="powershell" + [raku]="raku" +) + # Also check for changes in test files, scripts, or core infra -if echo "$CHANGED_FILES" | grep -qE '^(tests/|scripts/|\.gitlab-ci\.yml)'; then - # If tests or scripts changed, test ALL SDKs +if echo "$CHANGED_FILES" | grep -qE '^(tests/|scripts/|\.gitlab-ci\.yml|clients/\{.*\}/)'; then + # If tests, scripts, or multi-language client templates changed, test ALL SDKs echo '{"changed_langs": ["all"], "reason": "Core infrastructure changed", "test_all": true}' exit 0 fi -# Convert file extensions to language names +# Convert file extensions and directory names to language names LANGS_JSON="[" FIRST=true -for EXT in $CHANGED_LANGS; do - LANG="${LANG_MAP[$EXT]:-$EXT}" +for ITEM in $CHANGED_LANGS; do + # Try directory map first (for clients/python/, etc.) + LANG="${DIR_MAP[$ITEM]}" + # Fallback to extension map (for un.py, etc.) + LANG="${LANG:-${LANG_MAP[$ITEM]}}" + # Fallback to item as-is if not in any map + LANG="${LANG:-$ITEM}" + if [ "$FIRST" = true ]; then LANGS_JSON="$LANGS_JSON\"$LANG\"" FIRST=false