diff --git a/.github/workflows/rsr-antipattern.yml b/.github/workflows/rsr-antipattern.yml index 4bca623..ff16d3a 100644 --- a/.github/workflows/rsr-antipattern.yml +++ b/.github/workflows/rsr-antipattern.yml @@ -1,20 +1,30 @@ # SPDX-License-Identifier: PMPL-1.0-or-later # RSR Anti-Pattern CI Check +# SPDX-License-Identifier: PMPL-1.0-or-later # # Enforces: No TypeScript, No Go, No Python (except SaltStack), No npm # Allows: ReScript, Deno, WASM, Rust, OCaml, Haskell, Guile/Scheme name: RSR Anti-Pattern Check + on: push: branches: [main, master, develop] pull_request: branches: [main, master, develop] + + +permissions: + contents: read + jobs: antipattern-check: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Check for TypeScript run: | python3 << 'PYEOF' @@ -128,82 +138,6 @@ jobs: print(f"✅ No TypeScript files outside allowlist ({len(exemption_patterns)} per-repo exemption(s) parsed).") PYEOF - # Universal builtin allowlist — bridges that need no per-repo declaration. - # Files matching any of these patterns are always allowed. - BUILTIN_GLOBS = [ - '*.d.ts', - '**/bindings/**', - '**/tests/**', '**/test/**', - '**/scripts/**', - '**/mcp-adapter/**', - '**/*vscode*/**', - '**/cli/**', - '**/mod.ts', - '**/lsp-server.ts', '**/lsp_server.ts', '**/lsp.ts', '**/*-lsp.ts', - '**/deno-*/**', - '**/node_modules/**', - '**/vendor/**', - '**/examples/**', - '**/ffi/**', - ] - - # Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table. - # Single source of truth — adding a row here unblocks CI for that path. - # Format expected: - # ### TypeScript Exemptions ... - # | Path | Files | Rationale | Unblock condition | - # |---|---|---|---| - # | `path/to/file.ts` | 1 | ... | ... | - # | `dir/*.ts` | 6 | ... | ... | - exemptions = [] - claude_md = pathlib.Path('.claude/CLAUDE.md') - if claude_md.exists(): - in_table = False - for line in claude_md.read_text(encoding='utf-8').splitlines(): - if re.search(r'TypeScript [Ee]xemptions', line): - in_table = True - continue - if in_table and line.startswith(('### ', '## ', '# ')): - break - if in_table and line.startswith('|'): - m = re.match(r'\|\s*`([^`]+)`', line) - if m: - exemptions.append(m.group(1)) - - # Find all .ts and .tsx files - found = [] - for ext in ('ts', 'tsx'): - found.extend(str(p) for p in pathlib.Path('.').rglob(f'*.{ext}')) - - def allowed(path): - p = path.lstrip('./') - for g in BUILTIN_GLOBS + exemptions: - if fnmatch.fnmatchcase(p, g): - return True - # also treat glob ending with / as a directory prefix - base = g.rstrip('/').rstrip('*').rstrip('/') - if base and (p == base or p.startswith(base + '/')): - return True - return False - - bad = sorted(f for f in found if not allowed(f)) - if bad: - print("❌ TypeScript files detected outside the allowlist.\n") - for f in bad: - print(f" {f}") - print() - print("To resolve, either:") - print(" (a) migrate the file to AffineScript") - print(" (see Human_Programming_Guide.adoc migration chapter), OR") - print(" (b) move it to an allowlisted bridge path") - print(" (bindings/, tests/, scripts/, mcp-adapter/, *vscode*/, cli/, deno-*/, etc.), OR") - print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md") - print(" with rationale + unblock condition.") - if exemptions: - print(f"\n(Currently {len(exemptions)} exemption(s) parsed from .claude/CLAUDE.md.)") - sys.exit(1) - print(f"✅ No TypeScript files outside allowlist ({len(exemptions)} per-repo exemption(s) parsed).") - PYEOF - name: Check for Go run: | if find . -name "*.go" | grep -q .; then @@ -212,6 +146,7 @@ jobs: exit 1 fi echo "✅ No Go files" + - name: Check for Python (non-SaltStack) run: | PY_FILES=$(find . -name "*.py" | grep -v salt | grep -v _states | grep -v _modules | grep -v pillar | grep -v venv | grep -v __pycache__ || true) @@ -221,6 +156,7 @@ jobs: exit 1 fi echo "✅ No non-SaltStack Python files" + - name: Check for npm lockfiles run: | if [ -f "package-lock.json" ] || [ -f "yarn.lock" ]; then @@ -228,6 +164,7 @@ jobs: exit 1 fi echo "✅ No npm lockfiles" + - name: Check for tsconfig run: | if [ -f "tsconfig.json" ]; then @@ -235,6 +172,7 @@ jobs: exit 1 fi echo "✅ No tsconfig.json" + - name: Verify Deno presence (if package.json exists) run: | if [ -f "package.json" ]; then @@ -243,6 +181,7 @@ jobs: fi fi echo "✅ Deno configuration check complete" + - name: Summary run: | echo "╔════════════════════════════════════════════════════════════╗" @@ -253,5 +192,3 @@ jobs: echo "║ ║" echo "║ Blocked: TypeScript, Go, npm, Python (non-Salt) ║" echo "╚════════════════════════════════════════════════════════════╝" -permissions: - contents: read