forgedock 1.28.0 → 1.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/bin/engine/outcome-codes.mjs +29 -0
  2. package/bin/engine-cli.mjs +77 -16
  3. package/bin/lean/batch.test.mjs +2 -2
  4. package/bin/lean/conformance.mjs +47 -2
  5. package/bin/lean/conformance.test.mjs +53 -1
  6. package/bin/lean/context.mjs +356 -0
  7. package/bin/lean/context.test.mjs +307 -0
  8. package/bin/lean/effects.mjs +19 -1
  9. package/bin/lean/lean.mjs +178 -37
  10. package/bin/lean/lean.test.mjs +441 -7
  11. package/bin/lean/orchestrate.mjs +112 -5
  12. package/bin/lean/prefilter.mjs +81 -16
  13. package/bin/lean/prefilter.test.mjs +91 -2
  14. package/bin/lean/prompts.mjs +22 -7
  15. package/bin/lean/recovery.test.mjs +419 -0
  16. package/bin/lean/render.mjs +38 -5
  17. package/bin/lean/testmap.mjs +101 -0
  18. package/bin/lean/testmap.test.mjs +73 -0
  19. package/bin/lean/verify.mjs +48 -3
  20. package/bin/lean/verify.test.mjs +81 -3
  21. package/bin/stats/index.mjs +12 -2
  22. package/bin/stats/local-source.mjs +13 -0
  23. package/bin/stats/query.mjs +27 -0
  24. package/bin/stats/stats.test.mjs +41 -2
  25. package/commands/deploy-pr.md +5 -4
  26. package/commands/orchestrate.md +54 -7
  27. package/commands/review-pr-staging.md +6 -1
  28. package/docs/CONFIG.md +15 -0
  29. package/docs/OUTCOME-CODES.md +14 -0
  30. package/docs/RELEASE-NOTES.md +9 -0
  31. package/package.json +1 -1
  32. package/scripts/check-spec-markers.sh +1 -0
  33. package/scripts/code-index.sh +33 -33
  34. package/scripts/code-index.test.sh +21 -2
  35. package/scripts/fixtures/test-map/go/.github/workflows/ci.yml +25 -0
  36. package/scripts/fixtures/test-map/go/Makefile +5 -0
  37. package/scripts/fixtures/test-map/go/README.md +1 -0
  38. package/scripts/fixtures/test-map/go/go.mod +3 -0
  39. package/scripts/fixtures/test-map/go/internal/util/util.go +3 -0
  40. package/scripts/fixtures/test-map/go/main.go +3 -0
  41. package/scripts/fixtures/test-map/node/package.json +10 -0
  42. package/scripts/fixtures/test-map/node/packages/ui/package.json +4 -0
  43. package/scripts/fixtures/test-map/node/packages/ui/src/button.tsx +1 -0
  44. package/scripts/fixtures/test-map/node/pnpm-lock.yaml +1 -0
  45. package/scripts/fixtures/test-map/node/src/a.ts +1 -0
  46. package/scripts/fixtures/test-map/node/vitest.config.ts +1 -0
  47. package/scripts/fixtures/test-map/python/pkg/__init__.py +0 -0
  48. package/scripts/fixtures/test-map/python/pkg/mod.py +2 -0
  49. package/scripts/fixtures/test-map/python/pyproject.toml +9 -0
  50. package/scripts/fixtures/test-map/python/tests/test_mod.py +5 -0
  51. package/scripts/fixtures/test-map/python/uv.lock +1 -0
  52. package/scripts/repo-facts.sh +146 -0
  53. package/scripts/repo-facts.test.sh +106 -0
  54. package/scripts/review-prepare.sh +28 -6
  55. package/scripts/review-prepare.test.sh +22 -2
  56. package/scripts/test-map.sh +317 -0
  57. package/scripts/test-map.test.sh +353 -0
  58. package/scripts/verify-phase-trail.test.sh +44 -44
  59. package/scripts/wait-ci-green.sh +10 -4
  60. package/scripts/wait-ci-green.test.sh +13 -0
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env bash
2
+ # test-map.test.sh — cases for scripts/test-map.sh over the node, python and go fixtures (forge#4272).
3
+ # SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+ set -uo pipefail
6
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ S="$HERE/test-map.sh"
8
+ SEL="$HERE/../bin/lean/testmap.mjs"
9
+ FIX="$HERE/fixtures/test-map"
10
+ PASS=0; FAILN=0
11
+ TMP="$(mktemp -d "${TMPDIR:-/tmp}/test-map-test.XXXXXX")"; trap 'rm -rf "$TMP"' EXIT
12
+ ok() { PASS=$((PASS+1)); }
13
+ bad() { FAILN=$((FAILN+1)); echo "FAIL: $1"; }
14
+ eq() { if [ "$2" = "$3" ]; then ok; else bad "$1 (got: $2 want: $3)"; fi }
15
+
16
+ # copy a fixture into a fresh git repo and build its map
17
+ setup() {
18
+ rm -rf "${TMP:?}/$1"; cp -R "$FIX/$1" "$TMP/$1"; cd "$TMP/$1" || exit 1
19
+ git init -q -b main; git config user.email t@example.com; git config user.name t; git config commit.gpgsign false
20
+ git add -A; git commit -qm init
21
+ OUTP="$(bash "$S")"; RC=$?
22
+ }
23
+ # forge#4300: workflow-derived entries of the map JSON in $2 carry no bare `**` glob
24
+ nostar() { eq "$1 no bare ** on workflow entries" "$(printf '%s' "$2" | jq '[.entries[] | select(.source | startswith(".github/workflows/")) | .globs[] | select(. == "**")] | length')" 0; }
25
+ # selected commands for changed files, sorted, joined by |
26
+ sel() { node "$SEL" --map .forge/test-map.json --select "$@" | LC_ALL=C sort | paste -sd'|' -; }
27
+
28
+ # ---- 1. node: root scripts for root files, workspace scripts for package files ----
29
+ setup node
30
+ eq "node rc" "$RC" 0
31
+ eq "node output path" "$OUTP" ".forge/test-map.json"
32
+ eq "schema" "$(jq -c '[.version, (.entries | length > 0), (.entries | all(has("globs") and has("command") and has("cwd") and has("source")))]' .forge/test-map.json)" '[1,true,true]'
33
+ eq "non-check script ignored" "$(jq '[.entries[] | select(.command | test("build"))] | length' .forge/test-map.json)" 0
34
+ eq "root file" "$(sel src/a.ts)" "pnpm run lint|pnpm run test"
35
+ eq "package file" "$(sel packages/ui/src/button.tsx)" "cd packages/ui && pnpm run test|cd packages/ui && pnpm run typecheck|pnpm run lint|pnpm run test"
36
+ eq "vitest config noted" "$(jq -r '.entries[] | select(.cwd == "." and .command == "pnpm run test") | .source' .forge/test-map.json)" "package.json#scripts.test (vitest/jest config)"
37
+ eq "unrelated file" "$(sel README.md)" ""
38
+
39
+ # ---- 2. python: pytest + ruff through uv ----
40
+ setup python
41
+ eq "python rc" "$RC" 0
42
+ eq "python file" "$(sel pkg/mod.py)" "uv run pytest|uv run ruff check ."
43
+ eq "python test file" "$(sel tests/test_mod.py)" "uv run pytest|uv run ruff check ."
44
+ eq "python non-py" "$(sel uv.lock)" ""
45
+
46
+ # ---- 3. go: go.mod, Makefile and CI workflow steps; CI-only lint step; install lines skipped ----
47
+ setup go
48
+ eq "go rc" "$RC" 0
49
+ eq "go file" "$(sel main.go)" "go test ./...|go vet ./...|golangci-lint run ./...|make test|make vet"
50
+ eq "nested go file" "$(sel internal/util/util.go)" "go test ./...|go vet ./...|golangci-lint run ./...|make test|make vet"
51
+ eq "unrecognised ci script dropped" "$(jq '[.entries[] | select(.command | test("check-docs"))] | length' .forge/test-map.json)" 0
52
+ eq "ci-only lint source" "$(jq -r '.entries[] | select(.command == "golangci-lint run ./...") | .source' .forge/test-map.json)" ".github/workflows/ci.yml#jobs.lint"
53
+ eq "install line skipped" "$(jq '[.entries[] | select(.command | test("mod download"))] | length' .forge/test-map.json)" 0
54
+ eq "paths filter fallback" "$(jq -c '.entries[] | select(.command == "make vet") | .globs' .forge/test-map.json)" '["**.go","go.mod"]'
55
+ eq "docs file" "$(sel README.md)" "make test"
56
+ eq "go.sum" "$(sel go.sum)" "go test ./...|go vet ./...|make test"
57
+ nostar go "$(cat .forge/test-map.json)"
58
+
59
+ # ---- 4. regenerable: a rerun is byte-identical; --stdout writes nothing; --output ----
60
+ cp .forge/test-map.json "$TMP/first.json"
61
+ bash "$S" >/dev/null
62
+ if cmp -s .forge/test-map.json "$TMP/first.json"; then ok; else bad "rerun not byte-identical"; fi
63
+ rm -rf .forge; OUTS="$(bash "$S" --stdout)"
64
+ eq "stdout same map" "$OUTS" "$(cat "$TMP/first.json")"
65
+ eq "stdout writes nothing" "$([ -e .forge ] && echo yes || echo no)" no
66
+ bash "$S" --repo-path "$TMP/go" --output "$TMP/out/map.json" >/dev/null
67
+ if cmp -s "$TMP/out/map.json" "$TMP/first.json"; then ok; else bad "--output differs"; fi
68
+
69
+ # ---- 5. block runs, continuation lines, setup lines, workflow without paths ----
70
+ mkdir -p "$TMP/wf/.github/workflows"; cd "$TMP/wf" || exit 1
71
+ git init -q -b main
72
+ cat > .github/workflows/lint.yaml <<'EOF'
73
+ on: push
74
+ jobs:
75
+ checks:
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - run: npm ci
79
+ - run: "shellcheck scripts/*.sh"
80
+ - run: >-
81
+ ruff check
82
+ --fix
83
+ - run: |
84
+ echo hi
85
+ bash scripts/verify.sh \
86
+ --strict
87
+ ./custom-check
88
+ EOF
89
+ git add -A
90
+ OUTS="$(bash "$S" --stdout)"
91
+ # forge#4300: ./custom-check and `bash scripts/verify.sh` are not allowlisted checks, so they are dropped
92
+ # forge#4316: `ruff check --fix` rewrites the tree, so it is dropped too
93
+ eq "wf commands" "$(printf '%s' "$OUTS" | jq -r '[.entries[].command] | sort | join("|")')" "shellcheck scripts/*.sh"
94
+ eq "wf globs" "$(printf '%s' "$OUTS" | jq -c '[.entries[] | {(.command): .globs}] | add')" '{"shellcheck scripts/*.sh":["**/*.sh"]}'
95
+ nostar wf "$OUTS"
96
+
97
+ # ---- 5b. forge#4297: `- run: |` then env:/shell: ends the block; deploy/publish/release/push/secret steps dropped ----
98
+ mkdir -p "$TMP/wf2/.github/workflows"; cd "$TMP/wf2" || exit 1
99
+ git init -q -b main
100
+ cat > .github/workflows/ci.yml <<'EOF'
101
+ on:
102
+ push:
103
+ branches: [main]
104
+ jobs:
105
+ test:
106
+ runs-on: ubuntu-latest
107
+ steps:
108
+ - run: |
109
+ npm test
110
+ env:
111
+ CI: true
112
+ shell: bash
113
+ - name: Lint
114
+ run: |
115
+ npm run lint
116
+ working-directory: .
117
+ - name: Publish to npm
118
+ run: npm run ship
119
+ - run: ./scripts/release.sh
120
+ - run: git push origin HEAD
121
+ - run: curl -H "token ${{ secrets.TOKEN }}" https://x
122
+ - run: >-
123
+ tsc
124
+ --noEmit
125
+ shell: bash
126
+ deploy:
127
+ name: Ship it
128
+ runs-on: ubuntu-latest
129
+ steps:
130
+ - run: ./ship.sh
131
+ site:
132
+ name: Deploy site
133
+ runs-on: ubuntu-latest
134
+ steps:
135
+ - run: ./build-site.sh
136
+ EOF
137
+ git add -A
138
+ OUTS="$(bash "$S" --stdout)"
139
+ eq "wf2 commands" "$(printf '%s' "$OUTS" | jq -r '[.entries[].command] | sort | join("|")')" "npm run lint|npm test|tsc --noEmit"
140
+ nostar wf2 "$OUTS"
141
+
142
+ # ---- 5d. forge#4300: allowlist, not a name/substring denylist; names never decide; cd/pushd prefix becomes the cwd ----
143
+ mkdir -p "$TMP/wf3/.github/workflows"; cd "$TMP/wf3" || exit 1
144
+ git init -q -b main
145
+ cat > .github/workflows/ci.yml <<'EOF'
146
+ on: pull_request
147
+ jobs:
148
+ release-tests:
149
+ runs-on: ubuntu-latest
150
+ steps:
151
+ - run: npm test
152
+ - run: pytest tests/test_release.py
153
+ - run: pushd sub && make test
154
+ - run: npm run lint
155
+ name: Deploy
156
+ - name: Run tests
157
+ uses: some/action@v1
158
+ with:
159
+ name: release
160
+ - name: Typecheck
161
+ with:
162
+ name: release
163
+ run: npm run typecheck
164
+ ship:
165
+ runs-on: ubuntu-latest
166
+ steps:
167
+ - run: twine upload dist/*
168
+ - run: terraform apply -auto-approve
169
+ - run: kubectl apply -f k8s/
170
+ - run: ./deploy.sh
171
+ name: test
172
+ - name: Tests
173
+ with:
174
+ name: test
175
+ run: ./publish.sh
176
+ - run: pytest && twine upload dist/*
177
+ - run: cd ../other && make test
178
+ - run: npm run build
179
+ - run: npm test -- --token=${{ secrets.NPM_TOKEN }}
180
+ EOF
181
+ git add -A
182
+ OUTS="$(bash "$S" --stdout)"
183
+ eq "wf3 commands" "$(printf '%s' "$OUTS" | jq -r '[.entries[].command] | sort | join("|")')" "make test|npm run lint|npm run typecheck|npm test|pytest tests/test_release.py"
184
+ eq "wf3 release-tests job kept" "$(printf '%s' "$OUTS" | jq -r '[.entries[].source] | unique | join("|")')" ".github/workflows/ci.yml#jobs.release-tests"
185
+ eq "wf3 pushd cwd + globs" "$(printf '%s' "$OUTS" | jq -c '[.entries[] | select(.command == "make test") | {cwd, globs}]')" '[{"cwd":"sub","globs":["sub/**"]}]'
186
+ eq "wf3 no mutating command" "$(printf '%s' "$OUTS" | jq '[.entries[] | select(.command | test("twine|terraform|kubectl|deploy|publish|build|secrets"))] | length')" 0
187
+ nostar wf3 "$OUTS"
188
+ printf '%s' "$OUTS" > "$TMP/wf3-map.json"
189
+ eq "wf3 pushd entry runs in its dir" "$(node "$SEL" --map "$TMP/wf3-map.json" --select sub/Makefile)" "cd sub && make test"
190
+
191
+ # ---- 5c. forge#4298: a package dir with a space and a non-ASCII name keeps whole globs; workflow paths with spaces ----
192
+ mkdir -p "$TMP/sp/web app" "$TMP/sp/pkg/ü/src" "$TMP/sp/.github/workflows"; cd "$TMP/sp" || exit 1
193
+ git init -q -b main
194
+ echo '{"name":"root","private":true}' > package.json
195
+ echo '{"name":"web","scripts":{"test":"vitest run"}}' > "web app/package.json"
196
+ echo '{"name":"u","scripts":{"lint":"eslint ."}}' > "pkg/ü/package.json"
197
+ echo 'x' > "pkg/ü/src/a.ts"
198
+ cat > .github/workflows/ci.yml <<'EOF'
199
+ on:
200
+ push:
201
+ paths:
202
+ - "docs site/**"
203
+ jobs:
204
+ docs:
205
+ runs-on: ubuntu-latest
206
+ steps:
207
+ - run: ./build-docs.sh
208
+ - run: make check
209
+ EOF
210
+ git add -A
211
+ OUTS="$(bash "$S" --stdout)"
212
+ eq "spaced package globs" "$(printf '%s' "$OUTS" | jq -c '[.entries[] | select(.cwd == "web app") | .globs[]] | map(select(endswith("package.json") or endswith(".js")))')" '["web app/**/*.js","web app/package.json"]'
213
+ eq "non-ASCII package dir" "$(printf '%s' "$OUTS" | jq -r '[.entries[] | select(.command == "npm run lint") | .cwd] | join("|")')" "pkg/ü"
214
+ eq "spaced workflow path filter" "$(printf '%s' "$OUTS" | jq -c '[.entries[] | select(.command == "make check") | .globs]')" '[["docs site/**"]]'
215
+ nostar sp "$OUTS"
216
+ eq "unrecognised docs script dropped" "$(printf '%s' "$OUTS" | jq '[.entries[] | select(.command == "./build-docs.sh")] | length')" 0
217
+ printf '%s' "$OUTS" > "$TMP/sp-map.json"
218
+ eq "spaced changed file selects its package test" "$(node "$SEL" --map "$TMP/sp-map.json" --select "web app/src/x.js")" "cd 'web app' && npm run test"
219
+
220
+ # ---- 5e. forge#4315: real runners per ecosystem are mapped with their globs; mutating siblings stay dropped ----
221
+ # wf_case NAME: the step commands on stdin become .github/workflows/ci.yml of a fresh repo; OUTS = its map
222
+ wf_case() {
223
+ mkdir -p "$TMP/$1/.github/workflows"; cd "$TMP/$1" || exit 1
224
+ git init -q -b main
225
+ { printf 'on: pull_request\njobs:\n ci:\n runs-on: ubuntu-latest\n steps:\n'; sed 's/^/ - run: /'; } > .github/workflows/ci.yml
226
+ git add -A
227
+ OUTS="$(bash "$S" --stdout)"
228
+ nostar "$1" "$OUTS"
229
+ }
230
+ cmds() { printf '%s' "$OUTS" | jq -r '[.entries[].command] | sort | join("|")'; }
231
+ globs_of() { printf '%s' "$OUTS" | jq -r --arg c "$1" '[.entries[] | select(.command == $c) | .globs[]] | join(",")'; }
232
+
233
+ wf_case rust <<'WF'
234
+ cargo test --workspace
235
+ cargo clippy -- -D warnings
236
+ cargo publish
237
+ cargo build --release
238
+ WF
239
+ eq "rust commands" "$(cmds)" "cargo clippy -- -D warnings|cargo test --workspace"
240
+ eq "rust globs" "$(globs_of "cargo test --workspace")" "**/*.rs,**/Cargo.toml,Cargo.lock"
241
+
242
+ wf_case jvm <<'WF'
243
+ ./gradlew test
244
+ ./gradlew :app:check --no-daemon
245
+ mvn -B test
246
+ ./gradlew publish
247
+ ./gradlew test publish
248
+ mvn deploy
249
+ mvn install
250
+ WF
251
+ eq "jvm commands" "$(cmds)" "./gradlew :app:check --no-daemon|./gradlew test|mvn -B test"
252
+ eq "jvm globs" "$(globs_of "mvn -B test")" "**/*.java,**/*.kt,**/*.kts,**/*.gradle,**/*.groovy,**/*.scala,**/pom.xml"
253
+
254
+ wf_case ruby <<'WF'
255
+ bundle exec rspec
256
+ rspec spec/models
257
+ bundle exec rake release
258
+ WF
259
+ eq "ruby commands" "$(cmds)" "bundle exec rspec|rspec spec/models"
260
+ eq "ruby globs" "$(globs_of "bundle exec rspec")" "**/*.rb"
261
+
262
+ wf_case pyrun <<'WF'
263
+ python manage.py test
264
+ python -m unittest discover
265
+ tox
266
+ nox -s lint
267
+ tox -e publish
268
+ nox -s release
269
+ python manage.py migrate
270
+ WF
271
+ eq "python runner commands" "$(cmds)" "nox -s lint|python -m unittest discover|python manage.py test|tox"
272
+ eq "python runner globs" "$(globs_of "tox")" "**/*.py"
273
+
274
+ wf_case js <<'WF'
275
+ deno test
276
+ bun test
277
+ pnpm -r test
278
+ npm --prefix web test
279
+ npm run test:unit
280
+ deno deploy
281
+ npm run deploy:prod
282
+ npm --prefix ../x test
283
+ WF
284
+ eq "js runner commands" "$(cmds)" "bun test|deno test|npm --prefix web test|npm run test:unit|pnpm -r test"
285
+ eq "bun globs" "$(globs_of "bun test")" "**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.mjs,**/*.cjs"
286
+ eq "npm --prefix globs" "$(globs_of "npm --prefix web test")" "web/**/*.js,web/**/*.jsx,web/**/*.ts,web/**/*.tsx,web/**/*.mjs,web/**/*.cjs"
287
+
288
+ # ---- 5f. forge#4314: package.json script suffix allowlist (fixers/watchers/e2e/deploy never run pre-PR) ----
289
+ mkdir -p "$TMP/sfx"; cd "$TMP/sfx" || exit 1
290
+ git init -q -b main
291
+ cat > package.json <<'JSON'
292
+ {"name":"s","scripts":{"test":"x","test:unit":"x","test:ci":"x","test:node":"x","test:types":"x","lint:ts":"x","lint:js":"x","check:static":"x","check:types":"x","tsc":"tsc --noEmit","typecheck":"tsc","lint:check":"x","test:unit:ci":"x","lint:format":"prettier --check .","check:format":"prettier --write .",
293
+ "lint:fix":"eslint --fix .","check:fix":"x","test:watch":"x","test:e2e":"x","test:integration":"x","test:deploy":"x","test:update":"x","test:snapshot":"x","lint:css":"x","test:unit:watch":"x","test:-u":"x","lint:write":"x","test:dev":"x","check:release":"x"}}
294
+ JSON
295
+ git add -A
296
+ SFX="$(bash "$S" --stdout | jq -r '[.entries[].source | sub("^package.json#scripts\\.";"")] | sort | join("|")')"
297
+ eq "script suffix allowlist" "$SFX" "check:static|check:types|lint:check|lint:format|lint:js|lint:ts|test|test:ci|test:node|test:types|test:unit|test:unit:ci|tsc|typecheck"
298
+
299
+ wf_case sfxwf <<'WF'
300
+ npm run lint:fix
301
+ npm run check:fix
302
+ pnpm test:watch
303
+ yarn test:e2e
304
+ bun run test:integration
305
+ npm run test:deploy
306
+ pnpm run lint:fix
307
+ npm run test:unit
308
+ pnpm lint:ts
309
+ yarn check:types
310
+ bun run test:ci
311
+ WF
312
+ eq "workflow suffix commands" "$(cmds)" "bun run test:ci|npm run test:unit|pnpm lint:ts|yarn check:types"
313
+
314
+ wf_case shell <<'WF'
315
+ bash scripts/a.test.sh
316
+ sh tests/b.test.sh
317
+ bash scripts/deploy.sh
318
+ bash scripts/verify.sh
319
+ WF
320
+ eq "shell commands" "$(cmds)" "bash scripts/a.test.sh|sh tests/b.test.sh"
321
+ eq "shell globs" "$(globs_of "bash scripts/a.test.sh")" "**/*.sh"
322
+
323
+ # ---- 5f. forge#4316: redirects, process substitution, $GITHUB_*, extra make targets and fixer forms are dropped ----
324
+ # (make needs a cwd for its globs: a workflow entry never gets a bare **)
325
+ wf_case unsafe <<'WF'
326
+ pytest > out.txt
327
+ pytest 2>&1
328
+ diff <(a) <(b)
329
+ tee >(x)
330
+ cd sub && make test deploy
331
+ eslint --fix .
332
+ prettier --write .
333
+ gofmt -w .
334
+ black .
335
+ ruff format .
336
+ cargo clippy --fix
337
+ pytest --junitxml=$GITHUB_WORKSPACE/r.xml
338
+ cd sub && make test lint
339
+ cd sub && make test V=1
340
+ black --check .
341
+ ruff format --check .
342
+ prettier --check .
343
+ gofmt -l .
344
+ eslint .
345
+ WF
346
+ eq "unsafe forms dropped, check forms kept" "$(cmds)" "black --check .|eslint .|gofmt -l .|make test V=1|make test lint|prettier --check .|ruff format --check ."
347
+
348
+ # ---- 6. usage ----
349
+ bash "$S" --repo-path "$TMP/missing" >/dev/null 2>&1; eq "bad repo path" "$?" 2
350
+ bash "$S" --nope >/dev/null 2>&1; eq "unknown option" "$?" 2
351
+
352
+ echo "test-map.test.sh: $PASS passed, $FAILN failed"
353
+ [ "$FAILN" -eq 0 ]