fix(ci): bump erlef/setup-beam SHA for ubuntu24 runner support (#19) #92
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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.0.2 | |
| - name: Check for TypeScript | |
| run: | | |
| python3 << 'PYEOF' | |
| import re, sys, pathlib | |
| # Universal allowlist — bridges and conventions that need no per-repo declaration. | |
| # Implemented as explicit string predicates rather than glob patterns so that | |
| # top-level directories (e.g. tests/foo.ts) are matched the same as nested ones, | |
| # which fnmatch's * cannot do reliably. | |
| DIR_NAMES_ALLOWED = { | |
| 'bindings', 'tests', 'test', 'scripts', | |
| 'mcp-adapter', 'cli', 'vendor', 'examples', 'ffi', | |
| 'node_modules', 'benchmarks', | |
| } | |
| def builtin_allowed(p): | |
| # `p` is a posix-style path with no leading ./ | |
| # 1. Type declaration files | |
| if p.endswith('.d.ts'): | |
| return True | |
| # 2. Canonical Deno entrypoint filenames | |
| base = p.rsplit('/', 1)[-1] | |
| if base == 'mod.ts': | |
| return True | |
| # 3. LSP server files (filename suffixes) | |
| if base in ('lsp-server.ts', 'lsp_server.ts', 'lsp.ts') or base.endswith('-lsp.ts'): | |
| return True | |
| # 4. Benchmark files (filename suffixes) | |
| if base.endswith('.bench.ts') or base.endswith('_bench.ts'): | |
| return True | |
| # 5. Any directory segment (excluding basename) matches an allowed dir | |
| segs = p.split('/') | |
| for s in segs[:-1]: | |
| if s in DIR_NAMES_ALLOWED: | |
| return True | |
| # vscode-anything or anything-vscode | |
| if 'vscode' in s: | |
| return True | |
| # deno-named subprojects | |
| if s.startswith('deno-'): | |
| return True | |
| return False | |
| # Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table. | |
| # This is the documented single source of truth: adding one row here unblocks CI. | |
| # Glob characters: '*' and '**' both mean "any chars including /". This loose | |
| # interpretation matches user intent when an exemption row reads, e.g., | |
| # `affinescript-deno-test/*.ts` (covering nested files too). | |
| def glob_to_regex(g): | |
| out = [] | |
| for c in g.lstrip('./'): | |
| if c == '*': out.append('.*') | |
| elif c == '?': out.append('.') | |
| elif c in '.+(){}[]|^$\\': out.append(re.escape(c)) | |
| else: out.append(c) | |
| return re.compile('^' + ''.join(out) + '$') | |
| exemption_patterns = [] | |
| 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: | |
| exemption_patterns.append((m.group(1), glob_to_regex(m.group(1)))) | |
| def exempt(p): | |
| for raw, regex in exemption_patterns: | |
| if regex.match(p): | |
| return True | |
| # Also allow exact-path matches and prefix matches for paths | |
| # ending in `/` | |
| if p == raw.lstrip('./'): | |
| return True | |
| if raw.endswith('/') and p.startswith(raw.lstrip('./')): | |
| return True | |
| return False | |
| # Find all .ts and .tsx files (excluding common dot-dirs that find normally skips) | |
| found = [] | |
| for ext in ('ts', 'tsx'): | |
| for p in pathlib.Path('.').rglob(f'*.{ext}'): | |
| parts = p.parts | |
| if any(part.startswith('.') and part not in ('.', '..') for part in parts): | |
| continue | |
| found.append(p.as_posix().lstrip('./')) | |
| bad = sorted(f for f in found if not (builtin_allowed(f) or exempt(f))) | |
| if bad: | |
| print("❌ TypeScript files detected outside the allowlist.\n") | |
| for f in bad: | |
| print(f" {f}") | |
| print() | |
| print("To resolve, choose one:") | |
| print(" (a) migrate the file to AffineScript") | |
| print(" (see Human_Programming_Guide.adoc 'Migrating from -script Languages')") | |
| print(" (b) move to an allowlisted bridge path") | |
| print(" (bindings/, tests/, test/, scripts/, benchmarks/, mcp-adapter/,") | |
| print(" *vscode*/, cli/, deno-*/, vendor/, examples/, ffi/)") | |
| print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md") | |
| print(" with rationale + unblock condition") | |
| if exemption_patterns: | |
| print(f"\n(Currently {len(exemption_patterns)} exemption(s) parsed from .claude/CLAUDE.md.)") | |
| sys.exit(1) | |
| print(f"✅ No TypeScript files outside allowlist ({len(exemption_patterns)} per-repo exemption(s) parsed).") | |
| PYEOF | |
| - name: Check for Go | |
| run: | | |
| if find . -name "*.go" | grep -q .; then | |
| echo "❌ Go files detected - use Rust/WASM instead" | |
| find . -name "*.go" | |
| 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) | |
| if [ -n "$PY_FILES" ]; then | |
| echo "❌ Python files detected - only allowed for SaltStack" | |
| echo "$PY_FILES" | |
| exit 1 | |
| fi | |
| echo "✅ No non-SaltStack Python files" | |
| - name: Check for npm lockfiles | |
| run: | | |
| if [ -f "package-lock.json" ] || [ -f "yarn.lock" ]; then | |
| echo "❌ npm/yarn lockfile detected - use Deno instead" | |
| exit 1 | |
| fi | |
| echo "✅ No npm lockfiles" | |
| - name: Check for tsconfig | |
| run: | | |
| if [ -f "tsconfig.json" ]; then | |
| echo "❌ tsconfig.json detected - use ReScript instead" | |
| exit 1 | |
| fi | |
| echo "✅ No tsconfig.json" | |
| - name: Verify Deno presence (if package.json exists) | |
| run: | | |
| if [ -f "package.json" ]; then | |
| if [ ! -f "deno.json" ] && [ ! -f "deno.jsonc" ]; then | |
| echo "⚠️ Warning: package.json without deno.json - migration recommended" | |
| fi | |
| fi | |
| echo "✅ Deno configuration check complete" | |
| - name: Summary | |
| run: | | |
| echo "╔════════════════════════════════════════════════════════════╗" | |
| echo "║ RSR Anti-Pattern Check Passed ✅ ║" | |
| echo "║ ║" | |
| echo "║ Allowed: ReScript, Deno, WASM, Rust, OCaml, Haskell, ║" | |
| echo "║ Guile/Scheme, SaltStack (Python) ║" | |
| echo "║ ║" | |
| echo "║ Blocked: TypeScript, Go, npm, Python (non-Salt) ║" | |
| echo "╚════════════════════════════════════════════════════════════╝" |