code-foundry 1.48.0 → 1.48.2

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.
@@ -1,988 +0,0 @@
1
- name: Repository Setup
2
- description: Select mise or native language tooling and optionally install project dependencies.
3
-
4
- inputs:
5
- install:
6
- description: Install dependencies for the detected project ecosystems.
7
- required: false
8
- default: 'false'
9
- install-javascript:
10
- description: Install JavaScript/TypeScript package dependencies when install is enabled.
11
- required: false
12
- default: 'true'
13
- install-python:
14
- description: Install Python project dependencies when install is enabled.
15
- required: false
16
- default: 'true'
17
- install-rust:
18
- description: Fetch Rust project dependencies when install is enabled.
19
- required: false
20
- default: 'true'
21
- mise-scope:
22
- description: Limit mise installation and language package caches to auto, all, javascript, python, or rust.
23
- required: false
24
- default: auto
25
- cache-build:
26
- description: Restore JavaScript framework, Rust dependency, and incremental compiler caches.
27
- required: false
28
- default: 'true'
29
- cache-environment:
30
- description: Restore materialized Python virtual environments; disable for parallel test jobs with large environments.
31
- required: false
32
- default: 'true'
33
- cache-installed:
34
- description: Cache materialized JavaScript dependencies; auto skips large Bun node_modules trees and keeps compact link-based installs.
35
- required: false
36
- default: auto
37
- cache-save:
38
- description: Save restored caches after the job completes; disable to avoid post-job archive work in parallel jobs.
39
- required: false
40
- default: 'true'
41
- cache-packages:
42
- description: Restore JavaScript package-manager caches; auto skips large Bun lockfiles, or use true/false to override.
43
- required: false
44
- default: auto
45
- cache-lint:
46
- description: Restore the JavaScript/TypeScript lint cache.
47
- required: false
48
- default: 'false'
49
- cache-format:
50
- description: Restore the Prettier format cache.
51
- required: false
52
- default: 'false'
53
- cache-turbo:
54
- description: Cache local Turborepo artifacts; auto skips duplicate archives when TURBO_TOKEN enables remote caching.
55
- required: false
56
- default: auto
57
- cache-vite-plus:
58
- description: Cache the local Vite+ task cache (node_modules/.vite/task-cache); only applies when code-foundry.yml sets monorepo_tool to vite-plus. Use true/false to override.
59
- required: false
60
- default: auto
61
- task:
62
- description: Optional CI task used to scope test dependency installation.
63
- required: false
64
- default: ''
65
- task-javascript:
66
- description: Whether the selected task has a JavaScript suite; auto preserves default detection.
67
- required: false
68
- default: auto
69
- task-python:
70
- description: Whether the selected task has a Python suite; auto preserves default detection.
71
- required: false
72
- default: auto
73
- task-rust:
74
- description: Whether the selected task has a Rust suite; auto preserves default detection.
75
- required: false
76
- default: auto
77
-
78
- runs:
79
- using: composite
80
- steps:
81
- - name: Disable framework telemetry
82
- shell: bash
83
- run: |
84
- echo 'TURBO_TELEMETRY_DISABLED=1' >> "$GITHUB_ENV"
85
- echo 'NEXT_TELEMETRY_DISABLED=1' >> "$GITHUB_ENV"
86
- - name: Detect project profile
87
- id: profile
88
- shell: bash
89
- run: |
90
- tracked_files="$(git ls-files)"
91
-
92
- toolchain="auto"
93
- if [ -f .github/code-foundry.yml ]; then
94
- configured_toolchain="$(awk -F': ' '/^toolchain:/ {print $2; exit}' .github/code-foundry.yml)"
95
- [ -n "$configured_toolchain" ] && toolchain="$configured_toolchain"
96
- for flag in rust_sccache rust_nextest; do
97
- if grep -Eq "^${flag}:\\s*[\"']?true[\"']?\\s*$" .github/code-foundry.yml; then
98
- printf '%s=true\n' "$flag" >> "$GITHUB_OUTPUT"
99
- else
100
- printf '%s=false\n' "$flag" >> "$GITHUB_OUTPUT"
101
- fi
102
- done
103
- else
104
- printf 'rust_sccache=false\nrust_nextest=false\n' >> "$GITHUB_OUTPUT"
105
- fi
106
- if [ "$toolchain" = auto ] && [ -f .mise.toml ]; then
107
- toolchain=mise
108
- fi
109
- printf 'toolchain_mode=%s\n' "$toolchain" >> "$GITHUB_OUTPUT"
110
-
111
- has_tracked_regex() {
112
- printf '%s\n' "$tracked_files" | grep -Eq "$1"
113
- }
114
-
115
- has_python_source() {
116
- awk '!/^\.github\// && /(^|\/)[^\/]+\.py$/ { found=1; exit } END { exit !found }' <<< "$tracked_files"
117
- }
118
-
119
- has_package_script() {
120
- [ -f package.json ] || return 1
121
- node - "$1" <<'NODE'
122
- const fs = require('node:fs');
123
- const script = process.argv[2];
124
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
125
- process.exit(packageJson.scripts?.[script] ? 0 : 1);
126
- NODE
127
- }
128
-
129
- script_uses_runner() {
130
- [ -f package.json ] || return 1
131
- REPO_FOUNDRY_RUNNER="$1" REPO_FOUNDRY_RUNNER_TASK="$REPO_FOUNDRY_TASK" node <<'NODE'
132
- const fs = require('node:fs');
133
- const task = process.env.REPO_FOUNDRY_RUNNER_TASK || '';
134
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
135
- const scripts = packageJson.scripts || {};
136
- const candidates = {
137
- format: ['format:check', 'format', 'fmt'],
138
- lint: ['lint'],
139
- type_check: ['type-check', 'typecheck', 'type:check'],
140
- build: ['build'],
141
- unit: ['test:unit', 'test:coverage', 'test'],
142
- integration: ['test:integration'],
143
- e2e: ['test:e2e', 'e2e'],
144
- smoke: ['test:smoke', 'smoke'],
145
- performance: ['performance:check', 'perf:check'],
146
- }[task] || [];
147
- const selected = candidates.find((name) => scripts[name]);
148
- const pattern = process.env.REPO_FOUNDRY_RUNNER === 'vite-plus'
149
- ? /(?:^|[\s;&|(])(?:vp\s+(?:-C\s+\S+\s+)?run|vpr)(?:\s|$)/
150
- : /\bturbo(?:\s|$)/;
151
- const usesRunner = Boolean(selected && pattern.test(scripts[selected]));
152
- process.exit(usesRunner ? 0 : 1);
153
- NODE
154
- }
155
-
156
- uses_prettier() {
157
- [ -f package.json ] || return 0
158
- node <<'NODE'
159
- const fs = require('node:fs');
160
- const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
161
- const scripts = p.scripts || {};
162
- const dependencies = {
163
- ...p.dependencies,
164
- ...p.devDependencies,
165
- ...p.optionalDependencies,
166
- ...p.peerDependencies,
167
- };
168
- const usesPrettier = Boolean(dependencies.prettier) || Object.values(scripts).some((value) => /\bprettier\b/.test(value));
169
- const hasFormatScript = Boolean(scripts['format:check'] || scripts.format || scripts.fmt);
170
- process.exit(usesPrettier || !hasFormatScript ? 0 : 1);
171
- NODE
172
- }
173
-
174
- uses_eslint() {
175
- [ -f package.json ] || return 1
176
- node <<'NODE'
177
- const fs = require('node:fs');
178
- const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
179
- const scripts = p.scripts || {};
180
- const dependencies = {
181
- ...p.dependencies,
182
- ...p.devDependencies,
183
- ...p.optionalDependencies,
184
- ...p.peerDependencies,
185
- };
186
- const usesEslint = Boolean(dependencies.eslint) || Object.values(scripts).some((value) => /\beslint\b/.test(value));
187
- process.exit(usesEslint ? 0 : 1);
188
- NODE
189
- }
190
-
191
- javascript=false
192
- if [ -f package.json ] || [ -f bun.lock ] || [ -f bun.lockb ] ||
193
- [ -f pnpm-lock.yaml ] || [ -f yarn.lock ] || [ -f package-lock.json ] ||
194
- has_tracked_regex '(^|/)[^/]+\.(js|jsx|mjs|cjs|ts|tsx|mts|cts)$'; then
195
- javascript=true
196
- echo 'javascript=true' >> "$GITHUB_OUTPUT"
197
- else
198
- echo 'javascript=false' >> "$GITHUB_OUTPUT"
199
- fi
200
-
201
- if [ -f bun.lock ] || [ -f bun.lockb ] || [ -f pnpm-lock.yaml ] ||
202
- [ -f yarn.lock ] || [ -f package-lock.json ]; then
203
- javascript_dependencies=true
204
- echo 'javascript_dependencies=true' >> "$GITHUB_OUTPUT"
205
- elif [ -f package.json ] && REPO_FOUNDRY_PACKAGE_FILES="$tracked_files" node <<'NODE'
206
- const fs = require('node:fs');
207
-
208
- const files = (process.env.REPO_FOUNDRY_PACKAGE_FILES || '')
209
- .trim()
210
- .split('\n')
211
- .filter((file) => /(^|\/)package\.json$/.test(file));
212
- const groups = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
213
- const hasDependencies = files.some((file) => {
214
- try {
215
- const packageJson = JSON.parse(fs.readFileSync(file, 'utf8'));
216
- return groups.some((group) => packageJson[group] && Object.keys(packageJson[group]).length > 0) ||
217
- Array.isArray(packageJson.workspaces) ||
218
- (packageJson.workspaces && typeof packageJson.workspaces === 'object');
219
- } catch {
220
- return true;
221
- }
222
- });
223
- process.exit(hasDependencies ? 0 : 1);
224
- NODE
225
- then
226
- javascript_dependencies=true
227
- echo 'javascript_dependencies=true' >> "$GITHUB_OUTPUT"
228
- else
229
- javascript_dependencies=false
230
- echo 'javascript_dependencies=false' >> "$GITHUB_OUTPUT"
231
- fi
232
-
233
- javascript_package_manager=none
234
- configured_package_manager=""
235
- config_file=.github/code-foundry.yml
236
- if [ -f "$config_file" ]; then
237
- configured_package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file")"
238
- fi
239
- case "$configured_package_manager" in
240
- bun|pnpm|yarn|npm) javascript_package_manager="$configured_package_manager" ;;
241
- *)
242
- if [ -f bun.lock ] || [ -f bun.lockb ]; then javascript_package_manager=bun
243
- elif [ -f pnpm-lock.yaml ]; then javascript_package_manager=pnpm
244
- elif [ -f yarn.lock ]; then javascript_package_manager=yarn
245
- elif [ -f package-lock.json ]; then javascript_package_manager=npm
246
- elif [ "$javascript_dependencies" = true ]; then javascript_package_manager=bun
247
- fi
248
- ;;
249
- esac
250
- printf 'javascript_package_manager=%s\n' "$javascript_package_manager" >> "$GITHUB_OUTPUT"
251
-
252
- javascript_packages_cache=true
253
- case "$REPO_FOUNDRY_CACHE_PACKAGES" in
254
- false) javascript_packages_cache=false ;;
255
- true) : ;;
256
- auto)
257
- # Large Bun stores can take longer to transfer and extract than a
258
- # clean registry install. Keep normal projects cached, but avoid
259
- # the expensive archive for workspace graphs and once the tracked
260
- # Bun lockfiles exceed 512 KiB in total. Workspace installs often
261
- # resolve quickly from the registry, while their shared Bun store
262
- # is expensive to archive. Set REPO_FOUNDRY_CACHE_PACKAGES=true
263
- # when a repository has measured a cache win.
264
- if [ "$javascript_package_manager" = bun ]; then
265
- bun_workspace=false
266
- if [ -f package.json ] && node -e '
267
- const p = require("./package.json");
268
- const workspaces = p.workspaces;
269
- process.exit(
270
- Array.isArray(workspaces) ||
271
- Boolean(workspaces && typeof workspaces === "object") ? 0 : 1,
272
- );
273
- ' 2>/dev/null; then
274
- bun_workspace=true
275
- fi
276
- bun_lock_bytes=0
277
- while IFS= read -r lockfile; do
278
- [ -f "$lockfile" ] || continue
279
- lock_bytes="$(wc -c < "$lockfile")"
280
- bun_lock_bytes=$((bun_lock_bytes + lock_bytes))
281
- done < <(printf '%s\n' "$tracked_files" | awk '/(^|\/)bun\.lock(b)?$/')
282
- if [ "$bun_workspace" = true ] || [ "$bun_lock_bytes" -gt 524288 ]; then
283
- javascript_packages_cache=false
284
- fi
285
- fi
286
- ;;
287
- *) echo "Unsupported cache-packages value: $REPO_FOUNDRY_CACHE_PACKAGES" >&2; exit 2 ;;
288
- esac
289
- printf 'javascript_packages_cache=%s\n' "$javascript_packages_cache" >> "$GITHUB_OUTPUT"
290
-
291
- python=false
292
- if has_python_source || [ -f pyproject.toml ] ||
293
- [ -f requirements.txt ] || [ -f requirements-dev.txt ]; then
294
- python=true
295
- echo 'python=true' >> "$GITHUB_OUTPUT"
296
- else
297
- echo 'python=false' >> "$GITHUB_OUTPUT"
298
- fi
299
-
300
- rust=false
301
- if [ -f Cargo.toml ] || [ -f Cargo.lock ]; then
302
- rust=true
303
- echo 'rust=true' >> "$GITHUB_OUTPUT"
304
- else
305
- echo 'rust=false' >> "$GITHUB_OUTPUT"
306
- fi
307
-
308
- # Vite+ is strictly opt-in: only an explicit monorepo_tool: vite-plus
309
- # selects it. Every other value, or none, keeps the Turbo behavior.
310
- vite_plus=false
311
- if [ -f .github/code-foundry.yml ] &&
312
- grep -Eq "^monorepo_tool:\s*[\"']?vite-plus[\"']?\s*(#.*)?$" .github/code-foundry.yml; then
313
- vite_plus=true
314
- fi
315
- printf 'vite_plus=%s\n' "$vite_plus" >> "$GITHUB_OUTPUT"
316
-
317
- turbo=false
318
- if [ "$vite_plus" != true ] &&
319
- { [ -f turbo.json ] || [ -f turbo.jsonc ] || has_tracked_regex '(^|/)turbo\.jsonc?$'; }; then
320
- turbo=true
321
- echo 'turbo=true' >> "$GITHUB_OUTPUT"
322
- else
323
- echo 'turbo=false' >> "$GITHUB_OUTPUT"
324
- fi
325
-
326
- eslint=false
327
- if [ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.cjs ] ||
328
- [ -f .eslintrc.json ] || has_tracked_regex '(^|/)(\.eslintrc[^/]*|eslint\.config\.[^/]*)$' ||
329
- uses_eslint; then
330
- eslint=true
331
- echo 'eslint=true' >> "$GITHUB_OUTPUT"
332
- else
333
- echo 'eslint=false' >> "$GITHUB_OUTPUT"
334
- fi
335
-
336
- prettier=false
337
- if [ "$javascript" = true ]; then
338
- # Cache the fallback formatter for projects without a dedicated
339
- # config, but avoid probing an empty cache when a repository-owned
340
- # format script does not use Prettier.
341
- if uses_prettier; then
342
- prettier=true
343
- fi
344
- fi
345
- printf 'prettier=%s\n' "$prettier" >> "$GITHUB_OUTPUT"
346
-
347
- # Repository-owned format/lint scripts are authoritative and commonly
348
- # do not create the generic tool caches. Avoid recursive cache-path
349
- # scans and post-job archive work unless the fallback runner is used.
350
- prettier_fallback=false
351
- if [ "$prettier" = true ] && ! has_package_script format:check; then
352
- prettier_fallback=true
353
- fi
354
- eslint_fallback=false
355
- if [ "$eslint" = true ] && ! has_package_script lint; then
356
- eslint_fallback=true
357
- fi
358
- printf 'prettier_fallback=%s\n' "$prettier_fallback" >> "$GITHUB_OUTPUT"
359
- printf 'eslint_fallback=%s\n' "$eslint_fallback" >> "$GITHUB_OUTPUT"
360
-
361
- turbo_task=false
362
- if [ -z "$REPO_FOUNDRY_TASK" ] || script_uses_runner turbo; then
363
- turbo_task=true
364
- fi
365
- printf 'turbo_task=%s\n' "$turbo_task" >> "$GITHUB_OUTPUT"
366
-
367
- vite_plus_task=false
368
- if [ "$vite_plus" = true ] &&
369
- { [ -z "$REPO_FOUNDRY_TASK" ] || script_uses_runner vite-plus; }; then
370
- vite_plus_task=true
371
- fi
372
- printf 'vite_plus_task=%s\n' "$vite_plus_task" >> "$GITHUB_OUTPUT"
373
-
374
- build=false
375
- if has_tracked_regex '(^|/)(next\.config\.[^/]*|vite\.config\.[^/]*|tsconfig[^/]*\.json)$'; then
376
- build=true
377
- echo 'build=true' >> "$GITHUB_OUTPUT"
378
- else
379
- echo 'build=false' >> "$GITHUB_OUTPUT"
380
- fi
381
-
382
- python_dependencies=false
383
- if [ -f pyproject.toml ] || [ -f uv.lock ] ||
384
- has_tracked_regex '(^|/)requirements[^/]*\.txt$' ||
385
- [ -f Pipfile.lock ] || [ -f poetry.lock ]; then
386
- python_dependencies=true
387
- echo 'python_dependencies=true' >> "$GITHUB_OUTPUT"
388
- else
389
- echo 'python_dependencies=false' >> "$GITHUB_OUTPUT"
390
- fi
391
-
392
- configured_tool() {
393
- [ -f .mise.toml ] && grep -Eq "^$1[[:space:]]*=" .mise.toml
394
- }
395
-
396
- ruff=false
397
- if [ "$python" = true ] &&
398
- { [ -f ruff.toml ] || configured_tool ruff ||
399
- { [ -f pyproject.toml ] && grep -Eiq 'ruff|\[tool\.ruff' pyproject.toml; } ||
400
- { [ -f requirements.txt ] && grep -Eiq '(^|[-_.])ruff([<>=[:space:]]|$)' requirements.txt; } ||
401
- { [ -f requirements-dev.txt ] && grep -Eiq '(^|[-_.])ruff([<>=[:space:]]|$)' requirements-dev.txt; }; }; then
402
- ruff=true
403
- fi
404
- printf 'ruff=%s\n' "$ruff" >> "$GITHUB_OUTPUT"
405
-
406
- mise_tools=()
407
- add_configured_tool() {
408
- # Optional tools are expected to be absent in single-language repos.
409
- # Keep that a successful probe so Bash strict mode does not abort
410
- # profile detection before the actual setup step runs.
411
- if configured_tool "$1"; then
412
- mise_tools+=("$1")
413
- fi
414
- return 0
415
- }
416
-
417
- case "$REPO_FOUNDRY_MISE_SCOPE" in
418
- all)
419
- for tool in node bun python uv ruff rust; do add_configured_tool "$tool"; done
420
- ;;
421
- auto)
422
- task_language_needed() {
423
- local language="$1" flag
424
- case "$language" in
425
- javascript) flag="$REPO_FOUNDRY_TASK_JAVASCRIPT" ;;
426
- python) flag="$REPO_FOUNDRY_TASK_PYTHON" ;;
427
- rust) flag="$REPO_FOUNDRY_TASK_RUST" ;;
428
- *) return 0 ;;
429
- esac
430
- [ -z "$REPO_FOUNDRY_TASK" ] || [ "$flag" = auto ] || [ "$flag" = true ]
431
- }
432
- if [ "$javascript" = true ] && task_language_needed javascript; then
433
- add_configured_tool node
434
- if { [ -f bun.lock ] || [ -f bun.lockb ] ||
435
- config_file=.github/code-foundry.yml; awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file" 2>/dev/null | grep -qx bun; }; then
436
- add_configured_tool bun
437
- fi
438
- fi
439
- if [ "$python" = true ] && task_language_needed python; then
440
- add_configured_tool python
441
- add_configured_tool uv
442
- add_configured_tool ruff
443
- fi
444
- if [ "$rust" = true ] && task_language_needed rust; then add_configured_tool rust; fi
445
- ;;
446
- javascript)
447
- configured_tool node && mise_tools+=(node)
448
- if { [ -f bun.lock ] || [ -f bun.lockb ] ||
449
- config_file=.github/code-foundry.yml; awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file" 2>/dev/null | grep -qx bun; } && configured_tool bun; then
450
- mise_tools+=(bun)
451
- fi
452
- ;;
453
- python)
454
- configured_tool python && mise_tools+=(python)
455
- configured_tool uv && mise_tools+=(uv)
456
- configured_tool ruff && mise_tools+=(ruff)
457
- ;;
458
- rust) configured_tool rust && mise_tools+=(rust) ;;
459
- *) echo "Unsupported mise-scope: $REPO_FOUNDRY_MISE_SCOPE" >&2; exit 2 ;;
460
- esac
461
- printf 'mise_tools=%s\n' "${mise_tools[*]}" >> "$GITHUB_OUTPUT"
462
- if [ "${#mise_tools[@]}" -gt 0 ]; then
463
- printf 'mise_required=true\n' >> "$GITHUB_OUTPUT"
464
- else
465
- printf 'mise_required=false\n' >> "$GITHUB_OUTPUT"
466
- fi
467
- env:
468
- REPO_FOUNDRY_MISE_SCOPE: ${{ inputs.mise-scope }}
469
- REPO_FOUNDRY_TASK: ${{ inputs.task }}
470
- REPO_FOUNDRY_TASK_JAVASCRIPT: ${{ inputs.task-javascript }}
471
- REPO_FOUNDRY_TASK_PYTHON: ${{ inputs.task-python }}
472
- REPO_FOUNDRY_TASK_RUST: ${{ inputs.task-rust }}
473
- REPO_FOUNDRY_CACHE_PACKAGES: ${{ inputs.cache-packages }}
474
-
475
- - name: Install native test libraries
476
- if: >-
477
- inputs.install == 'true' &&
478
- (inputs.task == 'unit' || inputs.task == 'performance') &&
479
- hashFiles('**/package.json') != '' &&
480
- steps.profile.outputs.javascript == 'true'
481
- shell: bash
482
- run: |
483
- if node -e '
484
- const fs = require("node:fs");
485
- const p = JSON.parse(fs.readFileSync("package.json", "utf8"));
486
- const all = { ...p.dependencies, ...p.devDependencies, ...p.optionalDependencies };
487
- process.exit(all["matchstick-as"] ? 0 : 1);
488
- ' 2>/dev/null; then
489
- sudo apt-get update -qq
490
- sudo apt-get install -y -qq libpq5
491
- fi
492
-
493
- - name: Setup Node
494
- if: >-
495
- steps.profile.outputs.toolchain_mode != 'mise' &&
496
- steps.profile.outputs.javascript == 'true' &&
497
- steps.profile.outputs.javascript_package_manager != 'bun'
498
- uses: actions/setup-node@v5
499
- with:
500
- node-version: 24
501
- cache: ${{ steps.profile.outputs.javascript_package_manager == 'npm' && 'npm' || steps.profile.outputs.javascript_package_manager == 'pnpm' && 'pnpm' || steps.profile.outputs.javascript_package_manager == 'yarn' && 'yarn' || '' }}
502
-
503
- - name: Setup Bun
504
- if: >-
505
- steps.profile.outputs.toolchain_mode != 'mise' &&
506
- steps.profile.outputs.javascript == 'true' &&
507
- steps.profile.outputs.javascript_package_manager == 'bun'
508
- uses: oven-sh/setup-bun@v2
509
- with:
510
- bun-version: 1.4.0
511
-
512
- - name: Enable Corepack
513
- if: >-
514
- steps.profile.outputs.toolchain_mode != 'mise' &&
515
- (steps.profile.outputs.javascript_package_manager == 'pnpm' ||
516
- steps.profile.outputs.javascript_package_manager == 'yarn')
517
- shell: bash
518
- run: corepack enable
519
-
520
- - name: Setup Python
521
- if: steps.profile.outputs.toolchain_mode != 'mise' && steps.profile.outputs.python == 'true'
522
- uses: actions/setup-python@v6
523
- with:
524
- python-version: '3.13'
525
-
526
- - name: Setup Rust
527
- if: steps.profile.outputs.toolchain_mode != 'mise' && steps.profile.outputs.rust == 'true'
528
- uses: dtolnay/rust-toolchain@stable
529
-
530
- - name: Setup uv
531
- if: >-
532
- steps.profile.outputs.python == 'true' &&
533
- (inputs.mise-scope == 'python' ||
534
- (inputs.install == 'true' && inputs.install-python == 'true')) &&
535
- !contains(steps.profile.outputs.mise_tools, 'uv')
536
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
537
- with:
538
- version: 0.11.32
539
- enable-cache: false
540
-
541
- - name: Cache installed JavaScript dependencies
542
- id: javascript-dependencies
543
- if: >-
544
- inputs.install == 'true' &&
545
- inputs.install-javascript == 'true' &&
546
- inputs.cache-installed != 'false' &&
547
- steps.profile.outputs.javascript_dependencies == 'true' &&
548
- steps.profile.outputs.javascript_package_manager != 'npm' &&
549
- (inputs.cache-installed == 'true' ||
550
- steps.profile.outputs.javascript_package_manager == 'pnpm' ||
551
- steps.profile.outputs.javascript_package_manager == 'yarn') &&
552
- hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') != ''
553
- uses: actions/cache/restore@v5
554
- with:
555
- path: |
556
- **/node_modules
557
- key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') }}
558
- - name: Restore Bun dependencies
559
- id: javascript-bun-cache-restore
560
- if: >-
561
- inputs.install == 'true' &&
562
- inputs.install-javascript == 'true' &&
563
- inputs.cache-installed != 'false' &&
564
- steps.profile.outputs.javascript_dependencies == 'true' &&
565
- steps.profile.outputs.javascript_package_manager == 'bun' &&
566
- hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') != ''
567
- uses: actions/cache/restore@v5
568
- with:
569
- path: |
570
- **/node_modules
571
- key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') }}
572
- - name: Detect Bun dependencies
573
- id: javascript-bun-dependencies
574
- if: >-
575
- inputs.install == 'true' &&
576
- inputs.install-javascript == 'true' &&
577
- inputs.cache-installed != 'false' &&
578
- steps.profile.outputs.javascript_dependencies == 'true' &&
579
- steps.profile.outputs.javascript_package_manager == 'bun'
580
- shell: bash
581
- run: |
582
- if find . -path './.git' -prune -o -type d -name node_modules -print -quit | grep -q .; then
583
- echo 'restored=true' >> "$GITHUB_OUTPUT"
584
- else
585
- echo 'restored=false' >> "$GITHUB_OUTPUT"
586
- fi
587
- - name: Cache Corepack managers
588
- if: (inputs.mise-scope == 'javascript' || steps.javascript-dependencies.outputs.cache-hit == 'true') && inputs.install-javascript == 'true' && steps.profile.outputs.javascript == 'true' && hashFiles('**/pnpm-lock.yaml', '**/yarn.lock') != ''
589
- uses: ./.github/actions/cache
590
- with:
591
- save: ${{ inputs.cache-save }}
592
- path: ~/.cache/node/corepack
593
- # Corepack shims are independent of the project toolchain; keep this
594
- # cache warm when Node, Bun, or other mise-managed tools change.
595
- key: ${{ runner.os }}-corepack-${{ hashFiles('**/package.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}
596
- restore-keys: |
597
- ${{ runner.os }}-corepack-
598
- - name: Cache JavaScript packages
599
- id: javascript-packages
600
- if: inputs.install == 'true' && inputs.install-javascript == 'true' && steps.profile.outputs.javascript_packages_cache == 'true' && steps.profile.outputs.javascript == 'true' && hashFiles('**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json') != '' && steps.javascript-dependencies.outputs.cache-hit != 'true' && steps.javascript-bun-dependencies.outputs.restored != 'true'
601
- uses: actions/cache/restore@v5
602
- with:
603
- path: |
604
- ~/.bun/install/cache
605
- ~/.cache/node/corepack
606
- ~/.npm
607
- ~/.cache/yarn
608
- ~/.local/share/pnpm/store
609
- key: ${{ runner.os }}-javascript-${{ hashFiles('**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json') }}
610
- restore-keys: |
611
- ${{ runner.os }}-javascript-
612
- - name: Cache JavaScript lint state
613
- # Fallback ESLint mode is valid without a dedicated config file; use the
614
- # manifest/lockfile as the cache identity in that case as well.
615
- if: inputs.install == 'true' && inputs.cache-lint == 'true' && steps.profile.outputs.eslint_fallback == 'true' && hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.eslintrc*', '**/eslint.config.*') != ''
616
- uses: ./.github/actions/cache
617
- with:
618
- save: ${{ inputs.cache-save }}
619
- path: |
620
- **/.eslintcache
621
- key: ${{ runner.os }}-javascript-lint-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.eslintrc*', '**/eslint.config.*') }}
622
- restore-keys: |
623
- ${{ runner.os }}-javascript-lint-${{ github.workflow }}-${{ github.job }}-
624
- - name: Cache JavaScript format state
625
- if: inputs.install == 'true' && inputs.cache-format == 'true' && steps.profile.outputs.prettier_fallback == 'true' && hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.prettierrc*', '**/prettier.config.*') != ''
626
- uses: ./.github/actions/cache
627
- with:
628
- save: ${{ inputs.cache-save }}
629
- path: |
630
- **/node_modules/.cache/prettier
631
- key: ${{ runner.os }}-javascript-format-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.prettierrc*', '**/prettier.config.*') }}
632
- # Keep primary saves isolated so parallel jobs cannot race. A cache
633
- # from another workflow job is still a valid warm fallback.
634
- restore-keys: |
635
- ${{ runner.os }}-javascript-format-
636
- - name: Cache JavaScript build state
637
- if: >-
638
- inputs.install == 'true' &&
639
- inputs.cache-build == 'true' &&
640
- (steps.profile.outputs.build == 'true' || inputs.task == 'build') &&
641
- (inputs.task != 'type_check' ||
642
- steps.profile.outputs.turbo != 'true' ||
643
- steps.profile.outputs.turbo_task != 'true') &&
644
- (env.TURBO_TOKEN == '' ||
645
- env.TURBO_TEAM == '' ||
646
- steps.profile.outputs.turbo != 'true' ||
647
- steps.profile.outputs.turbo_task != 'true')
648
- uses: ./.github/actions/cache
649
- with:
650
- save: ${{ inputs.cache-save }}
651
- # Remote Turbo already stores task outputs for Turbo-backed builds.
652
- # Avoid a second, often-empty framework cache archive in that mode;
653
- # Turbo Type-Check tasks do not need framework build state either.
654
- path: |
655
- **/.next/cache
656
- **/node_modules/.vite
657
- !**/node_modules/.vite/task-cache
658
- **/*.tsbuildinfo
659
- key: ${{ runner.os }}-javascript-build-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/next.config.*', '**/vite.config.*', '**/tsconfig*.json', '.mise.toml', 'mise.lock') }}
660
- # Keep primary saves isolated so parallel jobs cannot race. A cache
661
- # from another workflow job is still a valid warm fallback.
662
- restore-keys: |
663
- ${{ runner.os }}-javascript-build-
664
- - name: Cache Python environment
665
- id: python-environment
666
- if: inputs.install == 'true' && inputs.install-python == 'true' && inputs.cache-environment == 'true' && steps.profile.outputs.python == 'true' && hashFiles('**/pyproject.toml', '**/uv.lock', '**/requirements*.txt', '**/Pipfile.lock', '**/poetry.lock', '.mise.toml', 'mise.lock') != ''
667
- uses: ./.github/actions/cache
668
- with:
669
- save: ${{ inputs.cache-save }}
670
- path: |
671
- .venv
672
- **/.venv
673
- key: ${{ runner.os }}-python-environment-${{ hashFiles('**/pyproject.toml', '**/uv.lock', '**/requirements*.txt', '**/Pipfile.lock', '**/poetry.lock', '.mise.toml', 'mise.lock') }}
674
- - name: Cache Python packages
675
- if: inputs.install == 'true' && (inputs.mise-scope == 'auto' || inputs.mise-scope == 'all' || inputs.mise-scope == 'python') && inputs.install-python == 'true' && steps.profile.outputs.python_dependencies == 'true' && hashFiles('**/pyproject.toml', '**/uv.lock', '**/requirements*.txt', '**/Pipfile.lock', '**/poetry.lock') != '' && steps.python-environment.outputs.cache-hit != 'true'
676
- uses: ./.github/actions/cache
677
- with:
678
- save: ${{ inputs.cache-save }}
679
- path: |
680
- ~/.cache/pip
681
- ~/.cache/uv
682
- key: ${{ runner.os }}-python-${{ hashFiles('**/pyproject.toml', '**/uv.lock', '**/requirements*.txt', '**/Pipfile.lock', '**/poetry.lock') }}
683
- restore-keys: |
684
- ${{ runner.os }}-python-
685
- - name: Cache Python audit tools
686
- id: python-audit-tools
687
- if: inputs.install == 'false' && (inputs.mise-scope == 'auto' || inputs.mise-scope == 'all' || inputs.mise-scope == 'python') && inputs.install-python == 'true' && steps.profile.outputs.python_dependencies == 'true' && hashFiles('**/pyproject.toml', '**/uv.lock', '**/requirements*.txt', '**/Pipfile.lock', '**/poetry.lock') != ''
688
- uses: ./.github/actions/cache
689
- with:
690
- save: ${{ inputs.cache-save }}
691
- path: ~/.cache/uv-audit
692
- # Keep scanner wheels and metadata warm across application and script
693
- # changes. Bump v2 only when the pinned scanner version changes.
694
- key: ${{ runner.os }}-python-audit-v2
695
- restore-keys: |
696
- ${{ runner.os }}-python-audit-
697
- - name: Enable offline Python audit
698
- if: steps.python-audit-tools.outputs.cache-hit == 'true'
699
- shell: bash
700
- run: echo 'REPO_FOUNDRY_UV_AUDIT_OFFLINE=true' >> "$GITHUB_ENV"
701
- - name: Cache Python lint state
702
- if: inputs.install == 'true' && inputs.cache-lint == 'true' && steps.profile.outputs.ruff == 'true' && hashFiles('**/pyproject.toml', '**/ruff.toml', '.mise.toml', 'mise.lock') != ''
703
- uses: ./.github/actions/cache
704
- with:
705
- save: ${{ inputs.cache-save }}
706
- path: |
707
- **/.ruff_cache
708
- key: ${{ runner.os }}-python-lint-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/pyproject.toml', '**/ruff.toml', '.mise.toml', 'mise.lock') }}
709
- # Primary saves remain job-specific; another job's Ruff cache is a
710
- # useful warm fallback because Ruff validates entries by file state.
711
- restore-keys: |
712
- ${{ runner.os }}-python-lint-${{ github.workflow }}-
713
- - name: Cache Turborepo
714
- if: >-
715
- inputs.install == 'true' &&
716
- inputs.install-javascript == 'true' &&
717
- inputs.cache-turbo != 'false' &&
718
- steps.profile.outputs.turbo == 'true' &&
719
- steps.profile.outputs.turbo_task == 'true' &&
720
- hashFiles('**/turbo.json', '**/turbo.jsonc') != '' &&
721
- (inputs.cache-turbo == 'true' || env.TURBO_TOKEN == '' || env.TURBO_TEAM == '')
722
- uses: ./.github/actions/cache
723
- with:
724
- save: ${{ inputs.cache-save }}
725
- path: |
726
- .turbo/cache
727
- **/.turbo/cache
728
- key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/turbo.json', '**/turbo.jsonc', '.mise.toml', 'mise.lock') }}
729
- restore-keys: |
730
- ${{ runner.os }}-turbo-
731
- - name: Configure remote Turbo
732
- if: >-
733
- inputs.install == 'true' &&
734
- inputs.cache-turbo != 'true' &&
735
- env.TURBO_TOKEN != '' &&
736
- env.TURBO_TEAM != '' &&
737
- steps.profile.outputs.turbo_task == 'true' &&
738
- steps.profile.outputs.turbo == 'true'
739
- shell: bash
740
- run: echo 'REPO_FOUNDRY_TURBO_REMOTE_ONLY=true' >> "$GITHUB_ENV"
741
- - name: Cache Rust packages
742
- id: rust-packages
743
- if: (inputs.mise-scope == 'auto' || inputs.mise-scope == 'all' || inputs.mise-scope == 'rust') && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && hashFiles('**/Cargo.toml', '**/Cargo.lock') != ''
744
- uses: ./.github/actions/cache
745
- with:
746
- save: ${{ inputs.cache-save }}
747
- path: |
748
- ~/.cargo/registry
749
- ~/.cargo/git
750
- key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }}
751
- restore-keys: |
752
- ${{ runner.os }}-rust-
753
- - name: Cache Rust advisory database
754
- if: inputs.install == 'false' && (inputs.mise-scope == 'auto' || inputs.mise-scope == 'all' || inputs.mise-scope == 'rust') && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && hashFiles('**/Cargo.toml', '**/Cargo.lock') != ''
755
- uses: ./.github/actions/cache
756
- with:
757
- save: ${{ inputs.cache-save }}
758
- path: ~/.cargo/advisory-db
759
- # The advisory database is independent of project dependencies. Keep
760
- # it warm for Security without invalidating it on every lockfile edit.
761
- key: ${{ runner.os }}-rust-advisory-code-foundry-v1
762
- restore-keys: |
763
- ${{ runner.os }}-rust-advisory-
764
- - name: Cache Rust builds
765
- if: inputs.install == 'true' && inputs.install-rust == 'true' && inputs.cache-build == 'true' && steps.profile.outputs.rust == 'true' && hashFiles('**/Cargo.toml', '**/Cargo.lock') != ''
766
- uses: ./.github/actions/cache
767
- with:
768
- save: ${{ inputs.cache-save }}
769
- # Keep reusable dependency/build-script artifacts for the Build job;
770
- # exclude target/incremental and release output, which are large and
771
- # provide little value on a fresh hosted runner.
772
- path: |
773
- target/.rustc_info.json
774
- target/debug/deps
775
- target/debug/build
776
- target/debug/.fingerprint
777
- target/debug/*.d
778
- key: ${{ runner.os }}-rust-build-v2-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock', '.mise.toml', 'mise.lock') }}
779
- # Cargo fingerprints validate restored artifacts; keep job-specific
780
- # primary saves but reuse a prior workflow's dependency artifacts.
781
- restore-keys: |
782
- ${{ runner.os }}-rust-build-v2-
783
- # sccache is opt-in (`rust_sccache: true` in code-foundry.yml): it caches
784
- # per-compilation objects keyed by content, so dependency artifacts
785
- # survive Cargo.toml/Cargo.lock churn that busts the target/ cache above.
786
- - name: Cache sccache artifacts
787
- if: inputs.install == 'true' && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && steps.profile.outputs.rust_sccache == 'true'
788
- uses: ./.github/actions/cache
789
- with:
790
- save: ${{ inputs.cache-save }}
791
- path: ~/.cache/sccache
792
- key: ${{ runner.os }}-rust-sccache-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('rust-toolchain.toml', 'rust-toolchain', '.cargo/config*', '.mise.toml', 'mise.lock') }}
793
- restore-keys: |
794
- ${{ runner.os }}-rust-sccache-
795
- - name: Install sccache
796
- if: inputs.install == 'true' && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && steps.profile.outputs.rust_sccache == 'true'
797
- uses: taiki-e/install-action@cb33e69fad06166ca28a42b2575e4dadabf62ee8 # v2
798
- with:
799
- tool: sccache
800
- - name: Wrap rustc with sccache
801
- if: inputs.install == 'true' && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && steps.profile.outputs.rust_sccache == 'true'
802
- shell: bash
803
- run: |
804
- echo 'RUSTC_WRAPPER=sccache' >> "$GITHUB_ENV"
805
- echo "SCCACHE_DIR=$HOME/.cache/sccache" >> "$GITHUB_ENV"
806
- # cargo-nextest is opt-in (`rust_nextest: true`): the runtime uses
807
- # `cargo nextest run` when the binary is present.
808
- - name: Install cargo-nextest
809
- if: inputs.install == 'true' && inputs.install-rust == 'true' && steps.profile.outputs.rust == 'true' && steps.profile.outputs.rust_nextest == 'true'
810
- uses: taiki-e/install-action@cb33e69fad06166ca28a42b2575e4dadabf62ee8 # v2
811
- with:
812
- tool: cargo-nextest
813
- - name: Setup
814
- if: ${{ steps.profile.outputs.mise_required == 'true' }}
815
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4
816
- with:
817
- version: 2026.9.2
818
- cache: true
819
- install_args: ${{ steps.profile.outputs.mise_tools }}
820
- - name: Setup Rust components
821
- if: >-
822
- steps.profile.outputs.rust == 'true' &&
823
- (steps.profile.outputs.mise_required == 'true' ||
824
- steps.profile.outputs.toolchain_mode != 'mise')
825
- shell: bash
826
- run: rustup component add rustfmt clippy
827
- - name: Use offline Cargo cache
828
- if: inputs.install == 'true' && inputs.install-rust == 'true' && steps.rust-packages.outputs.cache-hit == 'true' && hashFiles('**/Cargo.lock') != ''
829
- shell: bash
830
- run: echo 'CARGO_NET_OFFLINE=true' >> "$GITHUB_ENV"
831
- - name: Install
832
- if: inputs.install == 'true'
833
- shell: bash
834
- env:
835
- REPO_FOUNDRY_JAVASCRIPT_CACHE_HIT: ${{ steps.javascript-dependencies.outputs.cache-hit == 'true' || steps.javascript-bun-dependencies.outputs.restored == 'true' }}
836
- REPO_FOUNDRY_PYTHON_CACHE_HIT: ${{ steps.python-environment.outputs.cache-hit }}
837
- REPO_FOUNDRY_RUST_CACHE_HIT: ${{ steps.rust-packages.outputs.cache-hit }}
838
- REPO_FOUNDRY_INSTALL_JAVASCRIPT: ${{ inputs.install-javascript }}
839
- REPO_FOUNDRY_INSTALL_PYTHON: ${{ inputs.install-python }}
840
- REPO_FOUNDRY_INSTALL_RUST: ${{ inputs.install-rust }}
841
- REPO_FOUNDRY_TASK: ${{ inputs.task }}
842
- run: node $RUNNER_TEMP/code-foundry/src/runtime.mjs ci install
843
- - name: Measure Bun dependencies
844
- id: javascript-bun-cache
845
- if: >-
846
- inputs.cache-save == 'true' &&
847
- inputs.install == 'true' &&
848
- inputs.install-javascript == 'true' &&
849
- inputs.cache-installed != 'false' &&
850
- steps.profile.outputs.javascript_package_manager == 'bun' &&
851
- steps.profile.outputs.javascript_dependencies == 'true' &&
852
- steps.javascript-bun-dependencies.outputs.restored != 'true' &&
853
- !cancelled()
854
- shell: bash
855
- run: |
856
- total_kib="$(find . -path './.git' -prune -o -type d -name node_modules -print0 |
857
- xargs -0 -r du -sk |
858
- awk '{ total += $1 } END { print total + 0 }')"
859
- # Small Bun installs benefit from reuse; large monorepos are faster
860
- # with the package cache and a clean linker pass.
861
- if [ "$total_kib" -le 262144 ]; then
862
- echo 'cacheable=true' >> "$GITHUB_OUTPUT"
863
- else
864
- echo "Skipping Bun dependency cache (${total_kib} KiB > 256 MiB)"
865
- echo 'cacheable=false' >> "$GITHUB_OUTPUT"
866
- fi
867
- - name: Save Bun dependencies
868
- if: >-
869
- inputs.cache-save == 'true' &&
870
- steps.javascript-bun-cache.outputs.cacheable == 'true' &&
871
- !cancelled()
872
- continue-on-error: true
873
- uses: actions/cache/save@v5
874
- with:
875
- path: |
876
- **/node_modules
877
- key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') }}
878
- - name: Save installed JavaScript dependencies
879
- if: >-
880
- inputs.cache-save == 'true' &&
881
- inputs.install == 'true' &&
882
- inputs.install-javascript == 'true' &&
883
- steps.javascript-dependencies.outputs.cache-hit != 'true' &&
884
- steps.profile.outputs.javascript_dependencies == 'true' &&
885
- (steps.profile.outputs.javascript_package_manager == 'pnpm' ||
886
- steps.profile.outputs.javascript_package_manager == 'yarn') &&
887
- !cancelled()
888
- continue-on-error: true
889
- uses: actions/cache/save@v5
890
- with:
891
- path: |
892
- **/node_modules
893
- key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.mise.toml', 'mise.lock') }}
894
- - name: Save JavaScript packages
895
- if: >-
896
- inputs.cache-save == 'true' &&
897
- inputs.install == 'true' &&
898
- inputs.install-javascript == 'true' &&
899
- steps.javascript-packages.outputs.cache-hit != 'true' &&
900
- steps.profile.outputs.javascript_packages_cache == 'true' &&
901
- steps.profile.outputs.javascript == 'true' &&
902
- hashFiles('**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json') != '' &&
903
- steps.javascript-dependencies.outputs.cache-hit != 'true' &&
904
- steps.javascript-bun-dependencies.outputs.restored != 'true' &&
905
- !cancelled()
906
- continue-on-error: true
907
- uses: actions/cache/save@v5
908
- with:
909
- path: |
910
- ~/.bun/install/cache
911
- ~/.cache/node/corepack
912
- ~/.npm
913
- ~/.cache/yarn
914
- ~/.local/share/pnpm/store
915
- key: ${{ runner.os }}-javascript-${{ hashFiles('**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json') }}
916
- # Restored last: Install can replace node_modules, and the node_modules
917
- # archives above must not contain task output.
918
- - name: Detect Vite+ toolchain
919
- id: vite-plus-toolchain
920
- if: >-
921
- inputs.install == 'true' &&
922
- inputs.install-javascript == 'true' &&
923
- inputs.cache-vite-plus != 'false' &&
924
- steps.profile.outputs.vite_plus == 'true' &&
925
- steps.profile.outputs.vite_plus_task == 'true' &&
926
- hashFiles('**/package.json') != ''
927
- shell: bash
928
- env:
929
- REPO_FOUNDRY_RUNNER_OS: ${{ runner.os }}
930
- REPO_FOUNDRY_RUNNER_ARCH: ${{ runner.arch }}
931
- run: |
932
- node --input-type=module <<'NODE'
933
- import { appendFileSync, readFileSync } from 'node:fs'
934
- import { execFileSync } from 'node:child_process'
935
- import { createRequire } from 'node:module'
936
- import { resolve } from 'node:path'
937
- const clean = (value) => String(value).replace(/[^A-Za-z0-9._-]/g, '_')
938
- const tryVersion = (command) => {
939
- try {
940
- return execFileSync(command, ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
941
- } catch {
942
- return 'none'
943
- }
944
- }
945
- let vitePlus = 'unknown'
946
- try {
947
- vitePlus = createRequire(resolve('package.json'))('vite-plus/package.json').version
948
- } catch {
949
- try {
950
- vitePlus = JSON.parse(readFileSync('node_modules/vite-plus/package.json', 'utf8')).version
951
- } catch {
952
- // Not installed at the root; the namespace stays distinct.
953
- }
954
- }
955
- // Vite Task does not fingerprint the Node or Bun binary, so partition
956
- // by OS, architecture, and exact runtime and Vite+ versions.
957
- const namespace = [
958
- 'vite-plus-v1',
959
- process.env.REPO_FOUNDRY_RUNNER_OS,
960
- process.env.REPO_FOUNDRY_RUNNER_ARCH,
961
- `node${process.version}`,
962
- `bun${tryVersion('bun')}`,
963
- `vp${vitePlus}`,
964
- ].map(clean).join('-')
965
- // VITE_CACHE_PATH relocates the cache; it must be absolute.
966
- const custom = process.env.VITE_CACHE_PATH
967
- const paths = custom
968
- ? [resolve(custom)]
969
- : ['node_modules/.vite/task-cache', '**/node_modules/.vite/task-cache']
970
- // Without a resolvable version the namespace cannot isolate releases.
971
- appendFileSync(process.env.GITHUB_OUTPUT, `cacheable=${vitePlus !== 'unknown'}\n`)
972
- appendFileSync(process.env.GITHUB_OUTPUT, `namespace=${namespace}\n`)
973
- appendFileSync(process.env.GITHUB_OUTPUT, `cache_paths<<CACHE_PATHS_EOF\n${paths.join('\n')}\nCACHE_PATHS_EOF\n`)
974
- console.log(`Vite+ cache namespace: ${namespace}`)
975
- NODE
976
- - name: Cache Vite+ tasks
977
- if: steps.vite-plus-toolchain.outputs.cacheable == 'true'
978
- uses: ./.github/actions/cache
979
- with:
980
- save: ${{ inputs.cache-save }}
981
- path: ${{ steps.vite-plus-toolchain.outputs.cache_paths }}
982
- # The run/attempt suffix saves a refreshed entry on every push; restore
983
- # keys never leave the compatibility namespace.
984
- key: ${{ steps.vite-plus-toolchain.outputs.namespace }}-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '.mise.toml', 'mise.lock') }}-${{ hashFiles('**/vite.config.*') }}-${{ github.run_id }}-${{ github.run_attempt }}
985
- restore-keys: |
986
- ${{ steps.vite-plus-toolchain.outputs.namespace }}-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '.mise.toml', 'mise.lock') }}-
987
- ${{ steps.vite-plus-toolchain.outputs.namespace }}-${{ github.workflow }}-${{ github.job }}-
988
- ${{ steps.vite-plus-toolchain.outputs.namespace }}-