|
1 | | -<<<<<<< HEAD |
2 | 1 | # SPDX-License-Identifier: PMPL-1.0-or-later |
3 | | -======= |
4 | | -# SPDX-License-Identifier: MPL-2.0-or-later |
5 | | ->>>>>>> 1b58383 (chore: sync from parent repo automation) |
6 | | -name: RSR Language Policy |
| 2 | +# RSR Anti-Pattern CI Check |
| 3 | +# SPDX-License-Identifier: PMPL-1.0-or-later |
| 4 | +# |
| 5 | +# Enforces: No TypeScript, No Go, No Python (except SaltStack), No npm |
| 6 | +# Allows: ReScript, Deno, WASM, Rust, OCaml, Haskell, Guile/Scheme |
| 7 | + |
| 8 | +name: RSR Anti-Pattern Check |
7 | 9 |
|
8 | 10 | on: |
9 | 11 | push: |
10 | | - branches: [main] |
| 12 | + branches: [main, master, develop] |
11 | 13 | pull_request: |
12 | | - branches: [main] |
| 14 | + branches: [main, master, develop] |
| 15 | + |
13 | 16 |
|
14 | 17 | permissions: |
15 | 18 | contents: read |
16 | 19 |
|
17 | 20 | jobs: |
18 | | - check-banned-patterns: |
19 | | - name: Check for banned languages/patterns |
| 21 | + antipattern-check: |
20 | 22 | runs-on: ubuntu-latest |
| 23 | + permissions: |
| 24 | + contents: read |
21 | 25 | steps: |
22 | | - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 |
23 | | - |
| 26 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 27 | + |
24 | 28 | - name: Check for TypeScript |
25 | 29 | run: | |
26 | 30 | python3 << 'PYEOF' |
@@ -134,112 +138,57 @@ jobs: |
134 | 138 | print(f"β
No TypeScript files outside allowlist ({len(exemption_patterns)} per-repo exemption(s) parsed).") |
135 | 139 | PYEOF |
136 | 140 |
|
137 | | - # Universal builtin allowlist β bridges that need no per-repo declaration. |
138 | | - # Files matching any of these patterns are always allowed. |
139 | | - BUILTIN_GLOBS = [ |
140 | | - '*.d.ts', |
141 | | - '**/bindings/**', |
142 | | - '**/tests/**', '**/test/**', |
143 | | - '**/scripts/**', |
144 | | - '**/mcp-adapter/**', |
145 | | - '**/*vscode*/**', |
146 | | - '**/cli/**', |
147 | | - '**/mod.ts', |
148 | | - '**/lsp-server.ts', '**/lsp_server.ts', '**/lsp.ts', '**/*-lsp.ts', |
149 | | - '**/deno-*/**', |
150 | | - '**/node_modules/**', |
151 | | - '**/vendor/**', |
152 | | - '**/examples/**', |
153 | | - '**/ffi/**', |
154 | | - ] |
155 | | -
|
156 | | - # Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table. |
157 | | - # Single source of truth β adding a row here unblocks CI for that path. |
158 | | - # Format expected: |
159 | | - # ### TypeScript Exemptions ... |
160 | | - # | Path | Files | Rationale | Unblock condition | |
161 | | - # |---|---|---|---| |
162 | | - # | `path/to/file.ts` | 1 | ... | ... | |
163 | | - # | `dir/*.ts` | 6 | ... | ... | |
164 | | - exemptions = [] |
165 | | - claude_md = pathlib.Path('.claude/CLAUDE.md') |
166 | | - if claude_md.exists(): |
167 | | - in_table = False |
168 | | - for line in claude_md.read_text(encoding='utf-8').splitlines(): |
169 | | - if re.search(r'TypeScript [Ee]xemptions', line): |
170 | | - in_table = True |
171 | | - continue |
172 | | - if in_table and line.startswith(('### ', '## ', '# ')): |
173 | | - break |
174 | | - if in_table and line.startswith('|'): |
175 | | - m = re.match(r'\|\s*`([^`]+)`', line) |
176 | | - if m: |
177 | | - exemptions.append(m.group(1)) |
178 | | -
|
179 | | - # Find all .ts and .tsx files |
180 | | - found = [] |
181 | | - for ext in ('ts', 'tsx'): |
182 | | - found.extend(str(p) for p in pathlib.Path('.').rglob(f'*.{ext}')) |
183 | | -
|
184 | | - def allowed(path): |
185 | | - p = path.lstrip('./') |
186 | | - for g in BUILTIN_GLOBS + exemptions: |
187 | | - if fnmatch.fnmatchcase(p, g): |
188 | | - return True |
189 | | - # also treat glob ending with / as a directory prefix |
190 | | - base = g.rstrip('/').rstrip('*').rstrip('/') |
191 | | - if base and (p == base or p.startswith(base + '/')): |
192 | | - return True |
193 | | - return False |
194 | | -
|
195 | | - bad = sorted(f for f in found if not allowed(f)) |
196 | | - if bad: |
197 | | - print("β TypeScript files detected outside the allowlist.\n") |
198 | | - for f in bad: |
199 | | - print(f" {f}") |
200 | | - print() |
201 | | - print("To resolve, either:") |
202 | | - print(" (a) migrate the file to AffineScript") |
203 | | - print(" (see Human_Programming_Guide.adoc migration chapter), OR") |
204 | | - print(" (b) move it to an allowlisted bridge path") |
205 | | - print(" (bindings/, tests/, scripts/, mcp-adapter/, *vscode*/, cli/, deno-*/, etc.), OR") |
206 | | - print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md") |
207 | | - print(" with rationale + unblock condition.") |
208 | | - if exemptions: |
209 | | - print(f"\n(Currently {len(exemptions)} exemption(s) parsed from .claude/CLAUDE.md.)") |
210 | | - sys.exit(1) |
211 | | - print(f"β
No TypeScript files outside allowlist ({len(exemptions)} per-repo exemption(s) parsed).") |
212 | | - PYEOF |
213 | | - |
214 | 141 | - name: Check for Go |
215 | 142 | run: | |
216 | 143 | if find . -name "*.go" | grep -q .; then |
217 | | - echo "::error::Go files found! Use Rust instead (per RSR policy)" |
| 144 | + echo "β Go files detected - use Rust/WASM instead" |
| 145 | + find . -name "*.go" |
218 | 146 | exit 1 |
219 | 147 | fi |
220 | | - echo "β No Go files found" |
221 | | - |
222 | | - - name: Check for npm/Node artifacts |
| 148 | + echo "β
No Go files" |
| 149 | +
|
| 150 | + - name: Check for Python (non-SaltStack) |
223 | 151 | run: | |
224 | | - if [ -f "package-lock.json" ] || [ -d "node_modules" ]; then |
225 | | - echo "::error::npm artifacts found! Use Deno instead (per RSR policy)" |
| 152 | + PY_FILES=$(find . -name "*.py" | grep -v salt | grep -v _states | grep -v _modules | grep -v pillar | grep -v venv | grep -v __pycache__ || true) |
| 153 | + if [ -n "$PY_FILES" ]; then |
| 154 | + echo "β Python files detected - only allowed for SaltStack" |
| 155 | + echo "$PY_FILES" |
226 | 156 | exit 1 |
227 | 157 | fi |
228 | | - echo "β No npm artifacts found" |
229 | | - |
230 | | - - name: Check for Python (non-SaltStack) |
| 158 | + echo "β
No non-SaltStack Python files" |
| 159 | +
|
| 160 | + - name: Check for npm lockfiles |
231 | 161 | run: | |
232 | | - banned_py=$(find . -name "*.py" | grep -v -E "(salt|pillar|states|_modules|_states)" | head -1) |
233 | | - if [ -n "$banned_py" ]; then |
234 | | - echo "::error::Python files found outside SaltStack! Use ReScript/Rust (per RSR policy)" |
| 162 | + if [ -f "package-lock.json" ] || [ -f "yarn.lock" ]; then |
| 163 | + echo "β npm/yarn lockfile detected - use Deno instead" |
235 | 164 | exit 1 |
236 | 165 | fi |
237 | | - echo "β No banned Python files found" |
238 | | - |
239 | | - - name: Check for Makefiles |
| 166 | + echo "β
No npm lockfiles" |
| 167 | +
|
| 168 | + - name: Check for tsconfig |
240 | 169 | run: | |
241 | | - if [ -f "Makefile" ] || [ -f "makefile" ] || find . -name "*.mk" | grep -q .; then |
242 | | - echo "::error::Makefile found! Use Justfile or Mustfile instead (per RSR policy)" |
| 170 | + if [ -f "tsconfig.json" ]; then |
| 171 | + echo "β tsconfig.json detected - use ReScript instead" |
243 | 172 | exit 1 |
244 | 173 | fi |
245 | | - echo "β No Makefiles found" |
| 174 | + echo "β
No tsconfig.json" |
| 175 | +
|
| 176 | + - name: Verify Deno presence (if package.json exists) |
| 177 | + run: | |
| 178 | + if [ -f "package.json" ]; then |
| 179 | + if [ ! -f "deno.json" ] && [ ! -f "deno.jsonc" ]; then |
| 180 | + echo "β οΈ Warning: package.json without deno.json - migration recommended" |
| 181 | + fi |
| 182 | + fi |
| 183 | + echo "β
Deno configuration check complete" |
| 184 | +
|
| 185 | + - name: Summary |
| 186 | + run: | |
| 187 | + echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
| 188 | + echo "β RSR Anti-Pattern Check Passed β
β" |
| 189 | + echo "β β" |
| 190 | + echo "β Allowed: ReScript, Deno, WASM, Rust, OCaml, Haskell, β" |
| 191 | + echo "β Guile/Scheme, SaltStack (Python) β" |
| 192 | + echo "β β" |
| 193 | + echo "β Blocked: TypeScript, Go, npm, Python (non-Salt) β" |
| 194 | + echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
0 commit comments