code-foundry 0.25.0 → 0.27.0

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 (47) hide show
  1. package/.githooks/pre-commit +3 -4
  2. package/.github/CONTRIBUTING.md +10 -13
  3. package/.github/actions/setup/action.yml +50 -3
  4. package/.github/code-foundry.yml +6 -1
  5. package/.github/licenses/AGPL-3.0-or-later.txt +661 -0
  6. package/.github/licenses/GPL-3.0-or-later.txt +674 -0
  7. package/.github/licenses/MIT.txt +4 -16
  8. package/.github/workflows/ci.yml +20 -23
  9. package/.github/workflows/ci_self-ci.yml +1 -1
  10. package/.github/workflows/codeql.yml +23 -29
  11. package/.github/workflows/codeql_self-ci.yml +1 -1
  12. package/.github/workflows/draft-pr.yml +2 -2
  13. package/.github/workflows/release-pr.yml +4 -4
  14. package/.github/workflows/release.yml +17 -29
  15. package/.github/workflows/security.yml +24 -19
  16. package/.github/workflows/security_self-ci.yml +1 -1
  17. package/.github/workflows/test.yml +20 -20
  18. package/.github/workflows/test_self-ci.yml +1 -1
  19. package/AGENTS.md +13 -10
  20. package/CHANGELOG.md +28 -0
  21. package/README.md +19 -8
  22. package/docs/CONFIGURATION.md +11 -2
  23. package/docs/INITIALIZATION.md +9 -5
  24. package/docs/RELEASES.md +5 -0
  25. package/docs/WORKFLOWS.md +12 -5
  26. package/package.json +7 -3
  27. package/src/cli.mjs +29 -25
  28. package/src/commands/doctor.mjs +70 -0
  29. package/src/commands/sync.mjs +224 -0
  30. package/src/lib/config.mjs +38 -0
  31. package/src/lib/profile.mjs +114 -0
  32. package/src/runtime.mjs +316 -0
  33. package/.github/scripts/bootstrap.sh +0 -21
  34. package/.github/scripts/changed-files.sh +0 -96
  35. package/.github/scripts/ci.sh +0 -641
  36. package/.github/scripts/codeql-languages.sh +0 -91
  37. package/.github/scripts/doctor.sh +0 -98
  38. package/.github/scripts/format-fast-path.sh +0 -80
  39. package/.github/scripts/init-repo.sh +0 -264
  40. package/.github/scripts/pre-commit.sh +0 -67
  41. package/.github/scripts/profile.sh +0 -186
  42. package/.github/scripts/security.sh +0 -214
  43. package/.github/scripts/sitecustomize.py +0 -17
  44. package/.github/scripts/sync-codeowners.sh +0 -76
  45. package/.github/scripts/sync-protection.sh +0 -138
  46. package/.github/scripts/sync-template.sh +0 -744
  47. package/.github/scripts/turbo-cache-probe.sh +0 -102
@@ -1,641 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- source .github/scripts/changed-files.sh
5
-
6
- if [ -d .venv/bin ]; then export PATH="$PWD/.venv/bin:$PATH"; fi
7
-
8
- # Build one tracked-file inventory for the detectors below. These functions are
9
- # called repeatedly by parallel workflow jobs, so avoid starting Git for each
10
- # language check.
11
- tracked_files="$(git ls-files)"
12
-
13
- has_script() {
14
- [ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.[process.argv[1]] ? 0 : 1)' "$1"
15
- }
16
-
17
- has_turbo_script() {
18
- [ -f package.json ] && node -e 'const p=require("./package.json"); const script=p.scripts?.[process.argv[1]] || ""; process.exit(/(^|[\s;&|])turbo(\s|$)/.test(script) ? 0 : 1)' "$1"
19
- }
20
-
21
- has_bun_native_coverage() {
22
- [ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.["test:coverage"]?.includes("bun test") ? 0 : 1)'
23
- }
24
-
25
- has_javascript() {
26
- printf '%s\n' "$tracked_files" | grep -Eq '(^|/)[^/]+\.(js|jsx|mjs|cjs|ts|tsx|mts|cts)$'
27
- }
28
-
29
- has_python() {
30
- [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f requirements-dev.txt ] || \
31
- printf '%s\n' "$tracked_files" | awk '!/^\.github\// && /(^|\/)[^\/]+\.py$/ { found=1; exit } END { exit !found }'
32
- }
33
-
34
- has_python_dependencies() {
35
- [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f requirements-dev.txt ] ||
36
- [ -f Pipfile.lock ] || [ -f poetry.lock ] || [ -f uv.lock ]
37
- }
38
-
39
- needs_python_environment() {
40
- has_python_dependencies || ! command -v ruff >/dev/null 2>&1
41
- }
42
-
43
- has_javascript_dependencies() {
44
- [ -f bun.lock ] || [ -f bun.lockb ] || [ -f pnpm-lock.yaml ] ||
45
- [ -f yarn.lock ] || [ -f package-lock.json ] || {
46
- [ -f package.json ] || return 1
47
- REPO_FOUNDRY_TRACKED_FILES="$tracked_files" node <<'NODE'
48
- const files = (process.env.REPO_FOUNDRY_TRACKED_FILES || '')
49
- .split('\n')
50
- .filter((file) => /(^|\/)package\.json$/.test(file));
51
- const groups = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
52
- const hasDependencies = files.some((file) => {
53
- try {
54
- const packageJson = JSON.parse(require('node:fs').readFileSync(file, 'utf8'));
55
- return groups.some((group) => packageJson[group] && Object.keys(packageJson[group]).length > 0) ||
56
- Array.isArray(packageJson.workspaces) ||
57
- (packageJson.workspaces && typeof packageJson.workspaces === 'object');
58
- } catch {
59
- return true;
60
- }
61
- });
62
- process.exit(hasDependencies ? 0 : 1);
63
- NODE
64
- }
65
- }
66
-
67
- has_graph_project() {
68
- [ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.devDependencies?.["@graphprotocol/graph-cli"] ? 0 : 1)'
69
- }
70
-
71
- has_browser_dependencies() {
72
- if [ -f package.json ] && node <<'NODE'
73
- const fs = require('node:fs');
74
- const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
75
- const groups = [p.dependencies, p.devDependencies, p.optionalDependencies, p.peerDependencies];
76
- const names = groups.flatMap((group) => Object.keys(group || {}));
77
- const scripts = Object.values(p.scripts || {});
78
- process.exit([...names, ...scripts].some((value) => /playwright|cypress|puppeteer/i.test(value)) ? 0 : 1);
79
- NODE
80
- then
81
- return 0
82
- fi
83
-
84
- for manifest in pyproject.toml requirements.txt requirements-dev.txt; do
85
- if [ -f "$manifest" ] && grep -Eiq 'playwright|cypress|puppeteer' "$manifest"; then
86
- return 0
87
- fi
88
- done
89
- return 1
90
- }
91
-
92
- package_manager() {
93
- local configured=""
94
- if [ -x .github/scripts/profile.sh ]; then
95
- configured="$(bash .github/scripts/profile.sh get package_manager 2>/dev/null || true)"
96
- else
97
- config_file=.github/code-foundry.yml
98
- if [ -f "$config_file" ]; then
99
- configured="$(awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file")"
100
- fi
101
- fi
102
- case "$configured" in
103
- bun|pnpm|yarn|npm) echo "$configured"; return ;;
104
- esac
105
- if [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun
106
- elif [ -f pnpm-lock.yaml ]; then echo pnpm
107
- elif [ -f yarn.lock ]; then echo yarn
108
- elif [ -f package-lock.json ]; then echo npm
109
- else echo bun
110
- fi
111
- }
112
-
113
- cargo_run() {
114
- local command="$1"
115
- shift
116
- local -a cargo_args=("$@")
117
- if [ -f Cargo.lock ]; then
118
- cargo_args+=(--locked)
119
- fi
120
- if [ "${CARGO_NET_OFFLINE:-false}" = true ]; then
121
- cargo_args+=(--offline)
122
- cargo "$command" "${cargo_args[@]}"
123
- else
124
- cargo "$command" "${cargo_args[@]}"
125
- fi
126
- }
127
-
128
- run_script() {
129
- if ! has_script "$1"; then echo "Skipping $1 (script not defined)"; return; fi
130
- if [ "${REPO_FOUNDRY_TURBO_REMOTE_ONLY:-false}" = true ] && has_turbo_script "$1"; then
131
- case "$(package_manager)" in
132
- bun) bun run "$1" -- --cache=remote:rw ;;
133
- pnpm) corepack pnpm run "$1" -- --cache=remote:rw ;;
134
- yarn) corepack yarn run "$1" -- --cache=remote:rw ;;
135
- npm) npm run "$1" -- --cache=remote:rw ;;
136
- esac
137
- return
138
- fi
139
- case "$(package_manager)" in
140
- bun) bun run "$1" ;;
141
- pnpm) corepack pnpm run "$1" ;;
142
- yarn) corepack yarn run "$1" ;;
143
- npm) npm run "$1" ;;
144
- esac
145
- }
146
-
147
- run_package_tool() {
148
- case "$(package_manager)" in
149
- bun) bunx --no-install "$@" ;;
150
- pnpm) corepack pnpm exec "$@" ;;
151
- yarn) corepack yarn exec "$@" ;;
152
- npm) npx --no-install "$@" ;;
153
- esac
154
- }
155
-
156
- run_bun_coverage() {
157
- local log_file coverage_line functions lines minimum
158
- log_file="$(mktemp)"
159
- if ! run_script test:coverage 2>&1 | tee "$log_file"; then
160
- rm -f "$log_file"
161
- return 1
162
- fi
163
- coverage_line="$(grep -E '^All files[[:space:]]*\|' "$log_file" | tail -n 1 || true)"
164
- if [ -z "$coverage_line" ]; then
165
- echo "Coverage summary not found in test:coverage output" >&2
166
- rm -f "$log_file"
167
- return 1
168
- fi
169
- read -r functions lines < <(printf '%s\n' "$coverage_line" | awk -F'|' '{for (i = 1; i <= NF; i++) gsub(/[[:space:]]/, "", $i); if (NF >= 5) print $4, $5; else if (NF >= 3) print $2, $3}')
170
- minimum="${BUN_COVERAGE_MIN:-80}"
171
- if ! awk -v functions="$functions" -v lines="$lines" -v minimum="$minimum" \
172
- 'BEGIN { exit !(functions + 0 >= minimum && lines + 0 >= minimum) }'; then
173
- echo "Coverage below ${minimum}%: functions=${functions}% lines=${lines}%" >&2
174
- rm -f "$log_file"
175
- return 1
176
- fi
177
- echo "Coverage threshold passed: functions=${functions}% lines=${lines}% (minimum ${minimum}%)"
178
- rm -f "$log_file"
179
- }
180
-
181
- python_coverage_args() {
182
- if [ -f pyproject.toml ]; then
183
- python - <<'PY'
184
- import tomllib
185
- from pathlib import Path
186
-
187
- config = tomllib.loads(Path("pyproject.toml").read_text())
188
- for source in config.get("tool", {}).get("coverage", {}).get("run", {}).get("source", []):
189
- print(f"--cov={source}")
190
- PY
191
- fi
192
- }
193
-
194
- install_javascript() {
195
- if [ -n "${REPO_FOUNDRY_TASK:-}" ] && ! task_uses_javascript "$REPO_FOUNDRY_TASK"; then
196
- echo "Skipping JavaScript dependency install (no $REPO_FOUNDRY_TASK JavaScript suite)"
197
- return
198
- fi
199
- if [ "${REPO_FOUNDRY_INSTALL_JAVASCRIPT:-true}" = true ] && [ -f package.json ] && has_javascript_dependencies &&
200
- { [ "${REPO_FOUNDRY_JAVASCRIPT_CACHE_HIT:-false}" != true ] || [ ! -d node_modules ]; }; then
201
- case "$(package_manager)" in
202
- bun) bun install --frozen-lockfile ;;
203
- pnpm) corepack pnpm install --frozen-lockfile --prefer-offline ;;
204
- yarn) corepack yarn install --immutable ;;
205
- npm) npm ci --prefer-offline --no-audit --fund=false ;;
206
- esac
207
- elif [ -f package.json ] && ! has_javascript_dependencies; then
208
- echo "Skipping JavaScript dependency install (no dependencies found)"
209
- elif [ -f package.json ]; then
210
- echo "Using cached JavaScript dependencies"
211
- fi
212
- }
213
-
214
- install_rust() {
215
- if [ -n "${REPO_FOUNDRY_TASK:-}" ] && ! task_uses_rust "$REPO_FOUNDRY_TASK"; then
216
- echo "Skipping Rust dependency install (no $REPO_FOUNDRY_TASK Rust suite)"
217
- return
218
- fi
219
- if [ "${REPO_FOUNDRY_INSTALL_RUST:-true}" = true ] && [ -f Cargo.toml ] && [ "${REPO_FOUNDRY_RUST_CACHE_HIT:-false}" != true ]; then
220
- cargo_run fetch
221
- elif [ "${REPO_FOUNDRY_INSTALL_RUST:-true}" = true ] && [ -f Cargo.toml ]; then
222
- echo "Using cached Rust packages"
223
- fi
224
- }
225
-
226
- install_python() {
227
- if [ -n "${REPO_FOUNDRY_TASK:-}" ] && ! task_uses_python "$REPO_FOUNDRY_TASK"; then
228
- echo "Skipping Python dependency install (no $REPO_FOUNDRY_TASK Python suite)"
229
- return
230
- fi
231
- if [ "${REPO_FOUNDRY_INSTALL_PYTHON:-true}" = true ] && has_python &&
232
- needs_python_environment &&
233
- { [ "${REPO_FOUNDRY_PYTHON_CACHE_HIT:-false}" != true ] || [ ! -x .venv/bin/python ]; }; then
234
- install_python_with_uv() {
235
- if [ -f pyproject.toml ] && [ -f uv.lock ]; then
236
- uv sync --frozen --python "$(command -v python)"
237
- return
238
- fi
239
- uv venv --python "$(command -v python)" .venv
240
- local -a python_packages=()
241
- command -v ruff >/dev/null 2>&1 || python_packages+=(ruff)
242
- if [ -f requirements.txt ]; then python_packages+=(-r requirements.txt); fi
243
- if [ -f requirements-dev.txt ]; then python_packages+=(-r requirements-dev.txt); fi
244
- if [ "${#python_packages[@]}" -gt 0 ]; then
245
- uv pip install --python .venv/bin/python "${python_packages[@]}"
246
- fi
247
- }
248
- if command -v uv >/dev/null 2>&1 && install_python_with_uv; then
249
- echo "Installed Python dependencies with uv"
250
- else
251
- python -m venv .venv
252
- local -a python_packages=()
253
- command -v ruff >/dev/null 2>&1 || python_packages+=(ruff)
254
- if [ -f requirements.txt ]; then python_packages+=(-r requirements.txt); fi
255
- if [ -f requirements-dev.txt ]; then python_packages+=(-r requirements-dev.txt); fi
256
- if [ "${#python_packages[@]}" -gt 0 ]; then
257
- .venv/bin/python -m pip install --disable-pip-version-check "${python_packages[@]}"
258
- fi
259
- fi
260
- elif [ "${REPO_FOUNDRY_INSTALL_PYTHON:-true}" = true ] && has_python && needs_python_environment; then
261
- echo "Using cached Python environment"
262
- elif [ "${REPO_FOUNDRY_INSTALL_PYTHON:-true}" = true ] && has_python; then
263
- echo "Using shared Python tools; project environment not required"
264
- fi
265
- }
266
-
267
- task_uses_javascript() {
268
- local task="$1"
269
- case "$task" in
270
- unit) has_script test:unit || has_script test:coverage || { has_script test && ! has_script test:integration; } ;;
271
- integration) has_script test:integration ;;
272
- e2e) has_script test:e2e || has_script e2e ;;
273
- smoke) has_script test:smoke || has_script smoke ;;
274
- *) return 0 ;;
275
- esac
276
- }
277
-
278
- task_uses_rust() {
279
- local task="$1"
280
- [ -f Cargo.toml ] || return 1
281
- case "$task" in
282
- unit) has_rust_target lib || has_rust_target bin ;;
283
- integration) [ -d tests ] ;;
284
- e2e|smoke) return 1 ;;
285
- *) return 0 ;;
286
- esac
287
- }
288
-
289
- task_uses_python() {
290
- local task="$1"
291
- has_python_tests() {
292
- local directory="$1"
293
- [ -d "$directory" ] && find "$directory" -type f -name '*.py' -print -quit | grep -q .
294
- }
295
- has_python || return 1
296
- case "$task" in
297
- unit) has_python_tests tests/unit || { [ ! -d tests/unit ] && [ ! -d tests/integration ] && has_python_tests tests; } ;;
298
- integration) has_python_tests tests/integration ;;
299
- e2e) has_python_tests tests/e2e ;;
300
- smoke) has_python_tests tests/smoke ;;
301
- *) return 0 ;;
302
- esac
303
- }
304
-
305
- task_profile() {
306
- local task="${1:?missing test task}"
307
- local output applicable
308
- case "$task" in
309
- unit|integration|e2e|smoke) ;;
310
- *) echo "unknown test task: $task" >&2; return 2 ;;
311
- esac
312
- output="$(should_run "$task")"
313
- applicable="$(printf '%s\n' "$output" | awk -F= '$1 == "applicable" {print $2; exit}')"
314
- printf 'applicable=%s\n' "${applicable:-false}"
315
- if [ "$task" = e2e ]; then
316
- printf 'browser=%s\n' "$(printf '%s\n' "$output" | awk -F= '$1 == "browser" {print $2; exit}' | awk 'NF {print; found=1} END {if (!found) print "false"}')"
317
- fi
318
- if [ "${applicable:-false}" = true ]; then
319
- task_uses_javascript "$task" && printf 'javascript=true\n' || printf 'javascript=false\n'
320
- task_uses_python "$task" && printf 'python=true\n' || printf 'python=false\n'
321
- task_uses_rust "$task" && printf 'rust=true\n' || printf 'rust=false\n'
322
- else
323
- printf '%s\n' 'javascript=false' 'python=false' 'rust=false'
324
- fi
325
- }
326
-
327
- install() {
328
- run_parallel install_javascript install_rust install_python
329
- }
330
-
331
- rust_component() {
332
- local component="$1"
333
- if command -v rustup >/dev/null 2>&1; then
334
- local toolchain="${RUSTUP_TOOLCHAIN:-$(rustup show active-toolchain | awk '{print $1}')}"
335
- if rustup component list --toolchain "$toolchain" 2>/dev/null |
336
- grep -q "^${component}.*(installed)"; then
337
- return
338
- fi
339
- rustup component add --toolchain "$toolchain" "$component" >/dev/null
340
- fi
341
- }
342
-
343
- has_rust_target() {
344
- local kind="$1"
345
- cargo metadata --no-deps --format-version 1 |
346
- jq -e --arg kind "$kind" 'any(.packages[].targets[]; (.kind | index($kind)) != null)' >/dev/null
347
- }
348
-
349
- run_parallel() {
350
- local status=0 pid task
351
- local -a pids=()
352
- for task in "$@"; do
353
- "$task" &
354
- pids+=("$!")
355
- done
356
- for pid in "${pids[@]}"; do
357
- if ! wait "$pid"; then status=1; fi
358
- done
359
- return "$status"
360
- }
361
-
362
- format_javascript() {
363
- if has_script format:check; then run_script format:check
364
- elif has_javascript; then run_package_tool prettier --check . --cache --cache-strategy content
365
- else echo "Skipping JavaScript/TypeScript formatting (no formatter script or source found)"; fi
366
- }
367
-
368
- format_rust() {
369
- if [ -f Cargo.toml ]; then rust_component rustfmt; cargo fmt --check; fi
370
- }
371
-
372
- format_python() {
373
- if has_python && command -v ruff >/dev/null 2>&1; then ruff format --check .; fi
374
- }
375
-
376
- format() {
377
- run_parallel format_javascript format_rust format_python
378
- }
379
-
380
- lint_javascript() {
381
- if has_script lint; then run_script lint
382
- elif has_javascript; then run_package_tool eslint --cache --cache-strategy content .
383
- else echo "Skipping JavaScript/TypeScript lint (no lint script or source found)"; fi
384
- }
385
-
386
- lint_rust() {
387
- if [ -f Cargo.toml ]; then
388
- rust_component clippy
389
- clippy_args=(--all-targets)
390
- if [ -f Cargo.lock ]; then clippy_args+=(--locked); fi
391
- if [ "${CARGO_NET_OFFLINE:-false}" = true ]; then clippy_args+=(--offline); fi
392
- cargo clippy "${clippy_args[@]}" -- -D warnings
393
- fi
394
- }
395
-
396
- lint_python() {
397
- if has_python && command -v ruff >/dev/null 2>&1; then ruff check .; fi
398
- }
399
-
400
- lint() {
401
- run_parallel lint_javascript lint_rust lint_python
402
- }
403
-
404
- typecheck_javascript() {
405
- if has_graph_project; then
406
- echo "Skipping TypeScript type-check (Graph AssemblyScript project uses graph build/codegen)"
407
- elif has_script type-check; then run_script type-check
408
- elif has_script typecheck; then run_script typecheck
409
- else echo "Skipping type-check (script not defined)"; fi
410
- }
411
-
412
- typecheck_rust() {
413
- if [ -f Cargo.toml ]; then cargo_run check; fi
414
- }
415
-
416
- typecheck_python() {
417
- if has_python && command -v python >/dev/null 2>&1; then
418
- py_dirs=()
419
- for dir in tests src scripts; do [ -d "$dir" ] && py_dirs+=("$dir"); done
420
- if [ "${#py_dirs[@]}" -gt 0 ]; then python -m compileall -q "${py_dirs[@]}"; fi
421
- fi
422
- }
423
-
424
- type_check() {
425
- run_parallel typecheck_javascript typecheck_rust typecheck_python
426
- }
427
-
428
- build_javascript() {
429
- run_script build
430
- }
431
-
432
- build_rust() {
433
- if [ -f Cargo.toml ]; then cargo_run build --all-targets --all-features; fi
434
- }
435
-
436
- build() {
437
- # Some frameworks validate session secrets while statically collecting pages.
438
- # Keep CI builds deterministic without weakening runtime/deployment validation.
439
- if [ "${CI:-}" = true ] && [ -z "${NEXTAUTH_SECRET:-}" ]; then
440
- export NEXTAUTH_SECRET="ci-only-build-secret-not-for-runtime-0123456789"
441
- fi
442
- run_parallel build_javascript build_rust
443
- }
444
-
445
- unit_javascript() {
446
- # Bun repositories should expose test scripts backed by Bun's native runner.
447
- # Specialized repositories may keep their native runner (for example Matchstick or Hardhat).
448
- if has_script test:unit; then
449
- run_script test:unit
450
- elif has_script test:coverage; then
451
- if [ "$(package_manager)" = bun ] && has_bun_native_coverage; then
452
- run_bun_coverage
453
- else
454
- run_script test:coverage
455
- fi
456
- elif has_script test && ! has_script test:integration; then
457
- run_script test
458
- else
459
- echo "Skipping JavaScript/TypeScript unit tests (script not defined)"
460
- fi
461
- }
462
-
463
- unit_rust() {
464
- if [ -f Cargo.toml ]; then
465
- if has_rust_target lib; then cargo_run test --lib --all-features
466
- elif has_rust_target bin; then cargo_run test --bins --all-features
467
- else echo "Skipping Rust unit tests (no library or binary target)"; fi
468
- fi
469
- }
470
-
471
- unit_python() {
472
- if [ -d tests/unit ] && python -c 'import importlib.util; raise SystemExit(importlib.util.find_spec("pytest") is None)' 2>/dev/null; then
473
- coverage_args=()
474
- while IFS= read -r arg; do [ -n "$arg" ] && coverage_args+=("$arg"); done < <(python_coverage_args)
475
- [ "${#coverage_args[@]}" -gt 0 ] || coverage_args=(--cov)
476
- env -u MISE_GITHUB_TOKEN -u MISE_TRUSTED_CONFIG_PATHS -u MISE_YES -u MISE_LOG_LEVEL -u PYTHONHOME PYTHONPATH="$PWD/.github/scripts" python -m pytest -q tests/unit "${coverage_args[@]}" --cov-report=term-missing --cov-fail-under="${PYTHON_COVERAGE_MIN:-80}"
477
- elif [ -d tests ] && [ ! -d tests/integration ] && python -c 'import importlib.util; raise SystemExit(importlib.util.find_spec("pytest") is None)' 2>/dev/null; then
478
- coverage_args=()
479
- while IFS= read -r arg; do [ -n "$arg" ] && coverage_args+=("$arg"); done < <(python_coverage_args)
480
- [ "${#coverage_args[@]}" -gt 0 ] || coverage_args=(--cov)
481
- env -u MISE_GITHUB_TOKEN -u MISE_TRUSTED_CONFIG_PATHS -u MISE_YES -u MISE_LOG_LEVEL -u PYTHONHOME PYTHONPATH="$PWD/.github/scripts" python -m pytest -q tests "${coverage_args[@]}" --cov-report=term-missing --cov-fail-under="${PYTHON_COVERAGE_MIN:-80}"
482
- else
483
- echo "Skipping Python unit tests (no unit suite detected)"
484
- fi
485
- }
486
-
487
- unit() {
488
- run_parallel unit_javascript unit_rust unit_python
489
- }
490
-
491
- integration_javascript() {
492
- if has_script test:integration; then
493
- run_script test:integration
494
- else
495
- echo "Skipping JavaScript/TypeScript integration tests (script not defined)"
496
- fi
497
- }
498
-
499
- integration_rust() {
500
- if [ -f Cargo.toml ] && [ -d tests ]; then cargo_run test --tests --all-features; fi
501
- }
502
-
503
- integration_python() {
504
- if [ -d tests/integration ] && python -c 'import importlib.util; raise SystemExit(importlib.util.find_spec("pytest") is None)' 2>/dev/null; then
505
- python -m pytest -q tests/integration
506
- else
507
- echo "Skipping Python integration tests (tests/integration not found)"
508
- fi
509
- }
510
-
511
- integration() {
512
- run_parallel integration_javascript integration_rust integration_python
513
- }
514
-
515
- e2e_javascript() {
516
- if has_script test:e2e; then
517
- run_script test:e2e
518
- elif has_script e2e; then
519
- run_script e2e
520
- else
521
- echo "Skipping JavaScript/TypeScript E2E tests (script not defined)"
522
- fi
523
- }
524
-
525
- e2e_python() {
526
- if [ -d tests/e2e ] && python -c 'import importlib.util; raise SystemExit(importlib.util.find_spec("pytest") is None)' 2>/dev/null; then
527
- python -m pytest -q tests/e2e
528
- else
529
- echo "Skipping Python E2E tests (tests/e2e not found)"
530
- fi
531
- }
532
-
533
- e2e() {
534
- run_parallel e2e_javascript e2e_python
535
- }
536
-
537
- smoke_javascript() {
538
- if has_script test:smoke; then
539
- run_script test:smoke
540
- elif has_script smoke; then
541
- run_script smoke
542
- else
543
- echo "Skipping JavaScript/TypeScript smoke tests (script not defined)"
544
- fi
545
- }
546
-
547
- smoke_python() {
548
- if [ -d tests/smoke ] && python -c 'import importlib.util; raise SystemExit(importlib.util.find_spec("pytest") is None)' 2>/dev/null; then
549
- python -m pytest -q tests/smoke
550
- else
551
- echo "Skipping Python smoke tests (tests/smoke not found)"
552
- fi
553
- }
554
-
555
- smoke() {
556
- run_parallel smoke_javascript smoke_python
557
- }
558
-
559
- should_run() {
560
- local task="$1"
561
- if repo_foundry_governance_only; then
562
- printf '%s\n' 'applicable=false'
563
- [ "$task" = e2e ] && printf '%s\n' 'browser=false'
564
- return 0
565
- fi
566
- case "$task" in
567
- format)
568
- if has_script format:check || has_javascript || [ -f Cargo.toml ] || has_python; then
569
- printf '%s\n' 'applicable=true'
570
- else
571
- printf '%s\n' 'applicable=false'
572
- fi
573
- ;;
574
- lint)
575
- if has_script lint || has_javascript || [ -f Cargo.toml ] || has_python; then
576
- printf '%s\n' 'applicable=true'
577
- else
578
- printf '%s\n' 'applicable=false'
579
- fi
580
- ;;
581
- type_check)
582
- if has_script type-check || has_script typecheck || [ -f Cargo.toml ] || has_python; then
583
- printf '%s\n' 'applicable=true'
584
- else
585
- printf '%s\n' 'applicable=false'
586
- fi
587
- ;;
588
- build)
589
- if has_script build || [ -f Cargo.toml ]; then
590
- printf '%s\n' 'applicable=true'
591
- else
592
- printf '%s\n' 'applicable=false'
593
- fi
594
- ;;
595
- unit)
596
- if has_script test:unit || has_script test:coverage || has_script test || [ -f Cargo.toml ] ||
597
- [ -d tests ] || [ -d test ]; then
598
- printf '%s\n' 'applicable=true'
599
- else
600
- printf '%s\n' 'applicable=false'
601
- fi
602
- ;;
603
- integration)
604
- if has_script test:integration || [ -f Cargo.toml ] && [ -d tests ] || [ -d tests/integration ]; then
605
- printf '%s\n' 'applicable=true'
606
- else
607
- printf '%s\n' 'applicable=false'
608
- fi
609
- ;;
610
- e2e)
611
- if has_script test:e2e || has_script e2e || [ -d tests/e2e ]; then
612
- printf '%s\n' 'applicable=true'
613
- if has_browser_dependencies; then
614
- printf '%s\n' 'browser=true'
615
- else
616
- printf '%s\n' 'browser=false'
617
- fi
618
- else
619
- printf '%s\n' 'applicable=false'
620
- printf '%s\n' 'browser=false'
621
- fi
622
- ;;
623
- smoke)
624
- if has_script test:smoke || has_script smoke || [ -d tests/smoke ]; then
625
- printf '%s\n' 'applicable=true'
626
- else
627
- printf '%s\n' 'applicable=false'
628
- fi
629
- ;;
630
- *)
631
- echo "unknown CI task: $task" >&2
632
- return 2
633
- ;;
634
- esac
635
- }
636
-
637
- case "${1:-}" in
638
- install|format|lint|type_check|build|unit|integration|e2e|smoke|should_run) "$1" "${2:-}" ;;
639
- task_profile) task_profile "${2:-}" ;;
640
- *) echo "usage: $0 {install|format|lint|type_check|build|unit|integration|e2e|smoke|should_run|task_profile}" >&2; exit 2 ;;
641
- esac