code-foundry 1.47.5 → 1.48.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.
- package/.github/workflows/ci.yml +12 -0
- package/.github/workflows/cloudflare-delivery.yml +8 -1
- package/.github/workflows/cloudflare-deploy.yml +8 -1
- package/.github/workflows/eval.yml +3 -0
- package/.github/workflows/test.yml +15 -0
- package/.github/workflows/validation-billing.yml +16 -0
- package/CHANGELOG.md +15 -15
- package/docs/CACHING.md +84 -0
- package/docs/CONFIGURATION.md +13 -0
- package/docs/README.md +1 -1
- package/package.json +2 -1
- package/src/cli.mjs +27 -4
- package/src/commands/doctor.mjs +5 -1
- package/src/commands/sync.mjs +1 -0
- package/src/lib/profile.mjs +120 -0
- package/src/lib/task-filters.mjs +380 -0
- package/.github/actions/attest-artifact/action.yml +0 -56
- package/.github/actions/cache/action.yml +0 -47
- package/.github/actions/codeql/action.yml +0 -33
- package/.github/actions/setup/action.yml +0 -890
package/.github/workflows/ci.yml
CHANGED
|
@@ -119,6 +119,9 @@ jobs:
|
|
|
119
119
|
if-no-files-found: error
|
|
120
120
|
retention-days: 14
|
|
121
121
|
|
|
122
|
+
- name: Save Vite+ task cache
|
|
123
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
124
|
+
|
|
122
125
|
lint:
|
|
123
126
|
name: Lint
|
|
124
127
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -180,6 +183,9 @@ jobs:
|
|
|
180
183
|
if-no-files-found: error
|
|
181
184
|
retention-days: 14
|
|
182
185
|
|
|
186
|
+
- name: Save Vite+ task cache
|
|
187
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
188
|
+
|
|
183
189
|
type-check:
|
|
184
190
|
name: Type-Check
|
|
185
191
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -246,6 +252,9 @@ jobs:
|
|
|
246
252
|
if-no-files-found: error
|
|
247
253
|
retention-days: 14
|
|
248
254
|
|
|
255
|
+
- name: Save Vite+ task cache
|
|
256
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
257
|
+
|
|
249
258
|
build:
|
|
250
259
|
name: Build
|
|
251
260
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -311,3 +320,6 @@ jobs:
|
|
|
311
320
|
include-hidden-files: true
|
|
312
321
|
if-no-files-found: error
|
|
313
322
|
retention-days: 14
|
|
323
|
+
|
|
324
|
+
- name: Save Vite+ task cache
|
|
325
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
@@ -243,7 +243,14 @@ jobs:
|
|
|
243
243
|
isAbsolute(workspaceRelative) ||
|
|
244
244
|
workspaceRelative === '..' ||
|
|
245
245
|
workspaceRelative.startsWith(`..${sep}`)
|
|
246
|
-
|
|
246
|
+
// Vite+ repositories are not served by this Turbo-only check.
|
|
247
|
+
const config = resolve(workspace, '.github/code-foundry.yml')
|
|
248
|
+
const optedIntoVitePlus =
|
|
249
|
+
existsSync(config) &&
|
|
250
|
+
/^monorepo_tool:\s*["']?vite-plus["']?\s*(?:#.*)?$/m.test(readFileSync(config, 'utf8'))
|
|
251
|
+
if (optedIntoVitePlus) {
|
|
252
|
+
conservative('monorepo_tool is vite-plus')
|
|
253
|
+
} else if (!existsSync('turbo.json') && !existsSync('turbo.jsonc')) {
|
|
247
254
|
conservative('Turbo config not found')
|
|
248
255
|
} else if (outsideWorkspace) {
|
|
249
256
|
conservative('working-directory is outside the workspace')
|
|
@@ -171,7 +171,14 @@ jobs:
|
|
|
171
171
|
isAbsolute(workspaceRelative) ||
|
|
172
172
|
workspaceRelative === '..' ||
|
|
173
173
|
workspaceRelative.startsWith(`..${sep}`)
|
|
174
|
-
|
|
174
|
+
// Vite+ repositories are not served by this Turbo-only check.
|
|
175
|
+
const config = resolve(workspace, '.github/code-foundry.yml')
|
|
176
|
+
const optedIntoVitePlus =
|
|
177
|
+
existsSync(config) &&
|
|
178
|
+
/^monorepo_tool:\s*["']?vite-plus["']?\s*(?:#.*)?$/m.test(readFileSync(config, 'utf8'))
|
|
179
|
+
if (optedIntoVitePlus) {
|
|
180
|
+
conservative('monorepo_tool is vite-plus')
|
|
181
|
+
} else if (!existsSync('turbo.json') && !existsSync('turbo.jsonc')) {
|
|
175
182
|
conservative('Turbo config not found')
|
|
176
183
|
} else if (outsideWorkspace) {
|
|
177
184
|
conservative('working-directory is outside the workspace')
|
|
@@ -161,6 +161,9 @@ jobs:
|
|
|
161
161
|
if-no-files-found: ignore
|
|
162
162
|
retention-days: 14
|
|
163
163
|
|
|
164
|
+
- name: Save Vite+ task cache
|
|
165
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
166
|
+
|
|
164
167
|
performance:
|
|
165
168
|
name: Performance
|
|
166
169
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -247,6 +250,9 @@ jobs:
|
|
|
247
250
|
if-no-files-found: ignore
|
|
248
251
|
retention-days: 14
|
|
249
252
|
|
|
253
|
+
- name: Save Vite+ task cache
|
|
254
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
255
|
+
|
|
250
256
|
integration:
|
|
251
257
|
name: Integration
|
|
252
258
|
if: vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true
|
|
@@ -317,6 +323,9 @@ jobs:
|
|
|
317
323
|
if-no-files-found: error
|
|
318
324
|
retention-days: 14
|
|
319
325
|
|
|
326
|
+
- name: Save Vite+ task cache
|
|
327
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
328
|
+
|
|
320
329
|
e2e:
|
|
321
330
|
name: E2E
|
|
322
331
|
if: vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true
|
|
@@ -410,6 +419,9 @@ jobs:
|
|
|
410
419
|
if-no-files-found: error
|
|
411
420
|
retention-days: 14
|
|
412
421
|
|
|
422
|
+
- name: Save Vite+ task cache
|
|
423
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
424
|
+
|
|
413
425
|
smoke:
|
|
414
426
|
name: Smoke
|
|
415
427
|
if: vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true
|
|
@@ -479,3 +491,6 @@ jobs:
|
|
|
479
491
|
include-hidden-files: true
|
|
480
492
|
if-no-files-found: error
|
|
481
493
|
retention-days: 14
|
|
494
|
+
|
|
495
|
+
- name: Save Vite+ task cache
|
|
496
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
@@ -469,6 +469,9 @@ jobs:
|
|
|
469
469
|
if-no-files-found: error
|
|
470
470
|
retention-days: 14
|
|
471
471
|
|
|
472
|
+
- name: Save Vite+ task cache
|
|
473
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
474
|
+
|
|
472
475
|
lint:
|
|
473
476
|
name: Lint
|
|
474
477
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -530,6 +533,9 @@ jobs:
|
|
|
530
533
|
if-no-files-found: error
|
|
531
534
|
retention-days: 14
|
|
532
535
|
|
|
536
|
+
- name: Save Vite+ task cache
|
|
537
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
538
|
+
|
|
533
539
|
type-check:
|
|
534
540
|
name: Type-Check
|
|
535
541
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -596,6 +602,9 @@ jobs:
|
|
|
596
602
|
if-no-files-found: error
|
|
597
603
|
retention-days: 14
|
|
598
604
|
|
|
605
|
+
- name: Save Vite+ task cache
|
|
606
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
607
|
+
|
|
599
608
|
unit:
|
|
600
609
|
name: Unit
|
|
601
610
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -682,6 +691,9 @@ jobs:
|
|
|
682
691
|
if-no-files-found: ignore
|
|
683
692
|
retention-days: 14
|
|
684
693
|
|
|
694
|
+
- name: Save Vite+ task cache
|
|
695
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
696
|
+
|
|
685
697
|
e2e:
|
|
686
698
|
name: E2E
|
|
687
699
|
if: vars.CI_BILLING_PAUSED != 'true' && inputs.mode == 'audit'
|
|
@@ -775,11 +787,15 @@ jobs:
|
|
|
775
787
|
if-no-files-found: error
|
|
776
788
|
retention-days: 14
|
|
777
789
|
|
|
790
|
+
- name: Save Vite+ task cache
|
|
791
|
+
uses: ./.github/actions/vite-plus-cache-save
|
|
792
|
+
|
|
778
793
|
# Profile and dependency audit merged into one job. The split orchestrators
|
|
779
794
|
# run a profile job that feeds an audit matrix; here the language blocks run
|
|
780
795
|
# sequentially, driven directly by the profile step's outputs, so the second
|
|
781
796
|
# runner spin-up disappears. Python requirements run in one loop inside the
|
|
782
797
|
# audit step because a matrix cannot follow preceding steps in the same job.
|
|
798
|
+
|
|
783
799
|
audit:
|
|
784
800
|
name: Audit
|
|
785
801
|
if: vars.CI_BILLING_PAUSED != 'true' && inputs.mode == 'audit'
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.48.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.0...v1.48.1) (2026-10-16)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* vite-plus-cache-post-job-save ([#765](https://github.com/0xPlayerOne/code-foundry/issues/765)) ([3f82e85](https://github.com/0xPlayerOne/code-foundry/commit/3f82e850f5c09af6cea4de6f1a791d098bbd2f6e))
|
|
9
|
+
|
|
10
|
+
## [1.48.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.47.5...v1.48.0) (2026-10-16)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add opt-in Vite+ task runner support ([#763](https://github.com/0xPlayerOne/code-foundry/issues/763)) ([f7970cd](https://github.com/0xPlayerOne/code-foundry/commit/f7970cdf1c1880aaee159d2de79ba043f93711a7))
|
|
16
|
+
|
|
3
17
|
## [1.47.5](https://github.com/0xPlayerOne/code-foundry/compare/v1.47.4...v1.47.5) (2026-10-15)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -126,23 +140,9 @@
|
|
|
126
140
|
|
|
127
141
|
* **release:** treat ref-creation errors in the REST fallback as non-fatal ([#727](https://github.com/0xPlayerOne/code-foundry/issues/727)) ([75ea614](https://github.com/0xPlayerOne/code-foundry/commit/75ea61465e7c4cacd39e3aa8c52df93c2ea9bf03))
|
|
128
142
|
|
|
129
|
-
## [1.44.9](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.8...v1.44.9) (2026-10-06)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Bug Fixes
|
|
133
|
-
|
|
134
|
-
* **release:** make the REST fallback draft-aware and idempotent ([#725](https://github.com/0xPlayerOne/code-foundry/issues/725)) ([c29da6e](https://github.com/0xPlayerOne/code-foundry/commit/c29da6efb919d68a568c2328eea2c2f7654334fe))
|
|
135
|
-
|
|
136
|
-
## [1.44.8](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.7...v1.44.8) (2026-10-06)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Bug Fixes
|
|
140
|
-
|
|
141
|
-
* **release:** query the version-PR branch directly in the REST fallback ([#722](https://github.com/0xPlayerOne/code-foundry/issues/722)) ([b38f394](https://github.com/0xPlayerOne/code-foundry/commit/b38f394c0ca6438117fdd2f45bd106b0b145a0f9))
|
|
142
|
-
|
|
143
143
|
<!-- code-foundry: packaged changelog -->
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|
|
147
|
-
This packaged changelog lists the 20 most recent releases;
|
|
147
|
+
This packaged changelog lists the 20 most recent releases; 249 older releases are omitted to keep the package small.
|
|
148
148
|
See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
|
package/docs/CACHING.md
CHANGED
|
@@ -22,6 +22,90 @@ Non-Turborepo repositories do not need either setting. Deployment to Vercel is
|
|
|
22
22
|
not required. Custom workflows can pass these values to Turbo directly when
|
|
23
23
|
the repository's task graph benefits from remote reuse.
|
|
24
24
|
|
|
25
|
+
## Vite+ local task cache (opt-in)
|
|
26
|
+
|
|
27
|
+
Repositories that run their workspace tasks with Vite+ (`vp run`, `vpr`) can
|
|
28
|
+
opt in with `monorepo_tool: vite-plus` in `.github/code-foundry.yml`. Turbo
|
|
29
|
+
stays the default for every repository that does not set it.
|
|
30
|
+
|
|
31
|
+
When opted in, the setup action:
|
|
32
|
+
|
|
33
|
+
- stops treating a `turbo.json` as the active runner, so no Turborepo cache or
|
|
34
|
+
remote-cache step runs;
|
|
35
|
+
- restores `node_modules/.vite/task-cache` as its last step, after Install and
|
|
36
|
+
after every step that archives `node_modules` (Install can replace it);
|
|
37
|
+
- publishes a cache path, key, and save-intent contract for the explicit
|
|
38
|
+
`.github/actions/vite-plus-cache-save` action to use after repository tasks;
|
|
39
|
+
- saves it push-only, so pull requests and scheduled runs cannot poison it;
|
|
40
|
+
- keeps it out of the generic JavaScript build-state archive.
|
|
41
|
+
|
|
42
|
+
The generated CI, test, eval, and billing workflows call the save action at the
|
|
43
|
+
end of each task job. Custom workflows that use `.github/actions/setup` must
|
|
44
|
+
also call it after their Vite+ tasks complete; it reads the setup contract from
|
|
45
|
+
the job environment. A job can disable saving with `cache-save: false` on every
|
|
46
|
+
applicable setup call. With multiple setup calls in one job, save intent is
|
|
47
|
+
combined so an enabled Vite+ task setup is not overridden by a later disabled
|
|
48
|
+
setup. The setup action also exposes `vite-plus-cache-path`,
|
|
49
|
+
`vite-plus-cache-key`, and `vite-plus-cache-save` outputs for workflows that
|
|
50
|
+
prefer to pass these values explicitly to the save action.
|
|
51
|
+
|
|
52
|
+
Keys are `<namespace>-<workflow>-<job>-<lockfiles>-<vite.config>-<run id>-<attempt>`,
|
|
53
|
+
so every push saves a refreshed entry (more storage than a fixed key). Restore
|
|
54
|
+
keys fall back by lockfiles, job, then any job, but never leave the namespace
|
|
55
|
+
`vite-plus-v1-<OS>-<arch>-node<version>-bun<version>-vp<version>`, because Vite
|
|
56
|
+
Task does not fingerprint the Node or Bun binary. If the `vite-plus` version
|
|
57
|
+
cannot be resolved, nothing is restored or saved.
|
|
58
|
+
|
|
59
|
+
If `VITE_CACHE_PATH` (absolute, from any environment source) is set, Vite+
|
|
60
|
+
writes its cache there and the setup action caches that directory too.
|
|
61
|
+
|
|
62
|
+
Vite+ has no remote cache and this repository does not configure one.
|
|
63
|
+
|
|
64
|
+
Known limits (not parity with Turbo):
|
|
65
|
+
|
|
66
|
+
- Vite Task keeps one cache entry per task, so alternating between states
|
|
67
|
+
(A, B, A) re-runs the task instead of hitting the cache.
|
|
68
|
+
- `package.json` scripts are not cached unless the consumer sets
|
|
69
|
+
`run.cache.scripts: true` in its root `vite.config.ts` or passes `--cache` to
|
|
70
|
+
`vp run`. Code Foundry does not change consumer config. Commands that read no
|
|
71
|
+
files, such as a bare `echo`, are never cached.
|
|
72
|
+
- Tasks reading files that only Bun loads may need explicit input globs in the
|
|
73
|
+
consumer's Vite+ config (untested here).
|
|
74
|
+
- `cloudflare-deploy.yml` and `cloudflare-delivery.yml` keep a Turbo-only
|
|
75
|
+
affected check. With `monorepo_tool: vite-plus` they skip it and deploy
|
|
76
|
+
conservatively, even with a leftover `turbo.json`.
|
|
77
|
+
|
|
78
|
+
### Affected packages
|
|
79
|
+
|
|
80
|
+
`npx code-foundry affected --base <ref> --head <ref>` prints the affected
|
|
81
|
+
packages as JSON (`tool`, `packages`, `removedPackages`, `all`, `conservative`,
|
|
82
|
+
`reason`, `filterArgs`). Both refs must be present in the clone (use a
|
|
83
|
+
non-shallow checkout or fetch the base commit).
|
|
84
|
+
|
|
85
|
+
- Turbo repositories use `turbo query affected --packages`; this module does
|
|
86
|
+
not parse their workspace.
|
|
87
|
+
- Vite+ repositories compute the set from `git diff base...head` and the
|
|
88
|
+
workspace graphs read from the committed trees at both refs, because Vite+
|
|
89
|
+
has no equivalent query. Each changed file marks the package that owns it;
|
|
90
|
+
dependents are added transitively over the union of both graphs' edges, so
|
|
91
|
+
deleted, renamed, moved, and re-wired packages still select their dependents.
|
|
92
|
+
`removedPackages` lists selected packages that no longer exist at the head.
|
|
93
|
+
- Any changed file that no workspace package owns selects every package
|
|
94
|
+
(`all: true`): lockfiles, root manifest, config, scripts, docs, templates. A
|
|
95
|
+
build may read any of them. Only `affected_ignore` in `.github/code-foundry.yml`
|
|
96
|
+
(comma-separated globs, for example `README.md, docs/**`) skips unowned files.
|
|
97
|
+
- Anything the module cannot evaluate faithfully makes the result
|
|
98
|
+
conservative (`all: true`, `conservative: true`): no runner, a git or Turbo
|
|
99
|
+
failure, a malformed or unnamed workspace manifest at either ref, duplicate
|
|
100
|
+
package names, or workspace syntax it does not support (brace, bracket, or
|
|
101
|
+
extglob patterns). Supported syntax is `package.json` `workspaces` (array or
|
|
102
|
+
`{ packages }`) and pnpm `packages:` as a block or flow list, with `*`, `**`,
|
|
103
|
+
`?`, and `!` exclusions.
|
|
104
|
+
- `--tool turbo|vite-plus` overrides `monorepo_tool` for one invocation.
|
|
105
|
+
- For Vite+, `filterArgs` is ready to append to `vp run`. When `all` is true
|
|
106
|
+
run with `-r` instead, and when `packages` is empty skip the run: an empty
|
|
107
|
+
filter list would otherwise select every package.
|
|
108
|
+
|
|
25
109
|
## Runner selection
|
|
26
110
|
|
|
27
111
|
Lightweight detection, orchestration, audits, and release metadata jobs can use
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -289,6 +289,19 @@ behavior:
|
|
|
289
289
|
`TURBO_TEAM`.
|
|
290
290
|
- `TURBO_TOKEN` and `TURBO_TEAM` provide the Turborepo remote-cache
|
|
291
291
|
credentials.
|
|
292
|
+
- `monorepo_tool: auto`, `turbo`, or `vite-plus` selects the workspace task
|
|
293
|
+
runner. `auto` (the default) is Turbo when a `turbo.json` or `turbo.jsonc`
|
|
294
|
+
exists and no runner otherwise, exactly as before Vite+ support. Vite+ is
|
|
295
|
+
opt-in: only `monorepo_tool: vite-plus` selects it, and it then takes
|
|
296
|
+
precedence over a turbo config. `doctor` warns when the selected runner has
|
|
297
|
+
no matching evidence in the repository.
|
|
298
|
+
- `cache-vite-plus: auto`, `true`, or `false` is an input of the setup action
|
|
299
|
+
(not a config key). It controls the local Vite+ task cache only when
|
|
300
|
+
`monorepo_tool: vite-plus` is set.
|
|
301
|
+
- Custom workflows using the setup action must add
|
|
302
|
+
`.github/actions/vite-plus-cache-save` after task execution. The generated
|
|
303
|
+
Code Foundry workflows already include this save boundary; see
|
|
304
|
+
[Caching and remote caching](CACHING.md).
|
|
292
305
|
|
|
293
306
|
Use these controls only after measuring a repeatable benefit. See [Caching and
|
|
294
307
|
remote caching](CACHING.md).
|
package/docs/README.md
CHANGED
|
@@ -33,7 +33,7 @@ details before copying any guide elsewhere.
|
|
|
33
33
|
- [Declarative fleet rollouts](fleet-rollouts.md) — inventory, canaries, staged upgrades, and recovery.
|
|
34
34
|
- [Fleet release eligibility](fleet-release-eligibility.md) — require verified release evidence before upgrades.
|
|
35
35
|
- [Verified Cloudflare delivery](cloudflare-delivery.md) — build, verify, canary, and promote Worker versions.
|
|
36
|
-
- [Caching and remote caching](CACHING.md) — configure package, build, and
|
|
36
|
+
- [Caching and remote caching](CACHING.md) — configure package, build, Turborepo, and opt-in Vite+ caching.
|
|
37
37
|
|
|
38
38
|
## Repository-specific documentation
|
|
39
39
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.1",
|
|
4
4
|
"description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
|
|
5
5
|
"homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"src",
|
|
19
19
|
"docs",
|
|
20
20
|
".github",
|
|
21
|
+
"!.github/actions/**",
|
|
21
22
|
".githooks",
|
|
22
23
|
".editorconfig",
|
|
23
24
|
".gitattributes",
|
package/src/cli.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
7
7
|
|
|
8
8
|
const packageRoot = resolve(fileURLToPath(new URL('..', import.meta.url)))
|
|
9
9
|
|
|
10
|
-
/** @typedef {{ target: string, root: string, source: string, dryRun: boolean, force: boolean, github: boolean, createPr: boolean, exclude: string[], base: string, head: string, tag: string, workflow: string, mode: string, version: string, versionExplicit: boolean, repository: string, expectedSha: string, assets: string[], rootProvided: boolean, releaseSubcommand?: string, fleetSubcommand?: string, integritySubcommand?: 'settings'|'release'|'manifest', ciSubcommand?: 'pause'|'resume'|'status' }} Options */
|
|
10
|
+
/** @typedef {{ target: string, root: string, source: string, dryRun: boolean, force: boolean, github: boolean, createPr: boolean, exclude: string[], base: string, head: string, tag: string, workflow: string, mode: string, version: string, versionExplicit: boolean, repository: string, expectedSha: string, assets: string[], rootProvided: boolean, tool: string, headProvided: boolean, releaseSubcommand?: string, fleetSubcommand?: string, integritySubcommand?: 'settings'|'release'|'manifest', ciSubcommand?: 'pause'|'resume'|'status' }} Options */
|
|
11
11
|
/** @typedef {{ command: string, options: Options }} ParsedArgs */
|
|
12
12
|
|
|
13
13
|
const usage = `code-foundry — initialize and maintain agent-ready repositories
|
|
@@ -18,6 +18,7 @@ Usage:
|
|
|
18
18
|
npx code-foundry doctor [--target PATH]
|
|
19
19
|
npx code-foundry plan [--target PATH] [--tier fast|audit] [--changed] [--base REF] [--json]
|
|
20
20
|
npx code-foundry check [--target PATH] [--tier fast|audit] [--changed] [--base REF] [--timeout SECONDS] [--json]
|
|
21
|
+
npx code-foundry affected [--target PATH] [--base REF] [--head REF] [--tool auto|turbo|vite-plus]
|
|
21
22
|
npx code-foundry ci pause|resume|status [--target PATH]
|
|
22
23
|
npx code-foundry release reconcile [--github] [--base BRANCH] [--head BRANCH]
|
|
23
24
|
npx code-foundry release hook --tag TAG --workflow WORKFLOW
|
|
@@ -37,7 +38,8 @@ Options:
|
|
|
37
38
|
--force Replace protected standard documents
|
|
38
39
|
--github Apply a verified fast-forward through GitHub
|
|
39
40
|
--base BRANCH Release source branch (default: main)
|
|
40
|
-
--head BRANCH Branch to reconcile (default: staging)
|
|
41
|
+
--head BRANCH Branch to reconcile (default: staging; HEAD for affected)
|
|
42
|
+
--tool TOOL Task runner for affected: auto, turbo, or vite-plus (default: auto)
|
|
41
43
|
--tag TAG Published release tag for a post-release hook
|
|
42
44
|
--workflow FILE Workflow to dispatch for a post-release hook
|
|
43
45
|
--mode MODE auto, workflow-dispatch, release-event, or disabled
|
|
@@ -78,6 +80,8 @@ function parseArgs(argv) {
|
|
|
78
80
|
tag: '',
|
|
79
81
|
workflow: '',
|
|
80
82
|
mode: 'auto',
|
|
83
|
+
tool: 'auto',
|
|
84
|
+
headProvided: false,
|
|
81
85
|
version: `v${readPackageVersion(packageRoot)}`,
|
|
82
86
|
versionExplicit: false,
|
|
83
87
|
repository: '',
|
|
@@ -128,8 +132,15 @@ function parseArgs(argv) {
|
|
|
128
132
|
else if (arg === '--force') options.force = true
|
|
129
133
|
else if (arg === '--github') options.github = true
|
|
130
134
|
else if (arg === '--base') options.base = argv.shift() ?? fail('--base requires a branch')
|
|
131
|
-
else if (arg === '--head')
|
|
132
|
-
|
|
135
|
+
else if (arg === '--head') {
|
|
136
|
+
options.head = argv.shift() ?? fail('--head requires a branch')
|
|
137
|
+
options.headProvided = true
|
|
138
|
+
} else if (arg === '--tool') {
|
|
139
|
+
if (command !== 'affected') fail('--tool is only supported by the affected command')
|
|
140
|
+
options.tool = argv.shift() ?? fail('--tool requires auto, turbo, or vite-plus')
|
|
141
|
+
if (!['auto', 'turbo', 'vite-plus'].includes(options.tool))
|
|
142
|
+
fail(`unsupported --tool: ${options.tool}; use auto, turbo, or vite-plus`)
|
|
143
|
+
} else if (arg === '--tag') options.tag = argv.shift() ?? fail('--tag requires a tag')
|
|
133
144
|
else if (arg === '--repo') {
|
|
134
145
|
if (command !== 'release-integrity')
|
|
135
146
|
fail('--repo is only supported by release-integrity commands')
|
|
@@ -204,6 +215,18 @@ async function main() {
|
|
|
204
215
|
console.error(`code-foundry: ${error instanceof Error ? error.message : String(error)}`)
|
|
205
216
|
process.exitCode = 1
|
|
206
217
|
}
|
|
218
|
+
} else if (command === 'affected') {
|
|
219
|
+
try {
|
|
220
|
+
const { affectedPackages } = await import('./lib/task-filters.mjs')
|
|
221
|
+
const result = affectedPackages(target, {
|
|
222
|
+
base: options.base,
|
|
223
|
+
head: options.headProvided ? options.head : 'HEAD',
|
|
224
|
+
tool: options.tool,
|
|
225
|
+
})
|
|
226
|
+
console.log(JSON.stringify(result, null, 2))
|
|
227
|
+
} catch (error) {
|
|
228
|
+
fail(error instanceof Error ? error.message : String(error))
|
|
229
|
+
}
|
|
207
230
|
} else if (command === 'release') {
|
|
208
231
|
try {
|
|
209
232
|
const {
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
|
4
4
|
import { join, resolve, isAbsolute } from 'node:path'
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
6
|
import { includesValue, readConfig } from '../lib/config.mjs'
|
|
7
|
-
import { recommendRunners, resolveProfile } from '../lib/profile.mjs'
|
|
7
|
+
import { recommendRunners, resolveMonorepoTool, resolveProfile } from '../lib/profile.mjs'
|
|
8
8
|
import { packageManagerForLockfile } from '../lib/lockfiles.mjs'
|
|
9
9
|
import { doctorGithub } from '../lib/github-doctor.mjs'
|
|
10
10
|
import { isGeneratedEventCaller } from './sync.mjs'
|
|
@@ -38,6 +38,10 @@ export function doctor(root, options = {}) {
|
|
|
38
38
|
: 'Toolchain: native (mise not configured).'
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
|
+
const monorepoTool = resolveMonorepoTool(target, config)
|
|
42
|
+
for (const message of monorepoTool.warnings) warn(message)
|
|
43
|
+
if (monorepoTool.tool === 'vite-plus')
|
|
44
|
+
console.log(`Task runner: vite-plus (${monorepoTool.reason}).`)
|
|
41
45
|
const hooks = git(target, ['config', '--get', 'core.hooksPath'])
|
|
42
46
|
if (hooks !== '.githooks') warn('Git hooks are not enabled; run `npx code-foundry init`')
|
|
43
47
|
// A correctly enabled hook that enforces nothing is the failure this check
|
package/src/commands/sync.mjs
CHANGED
|
@@ -2168,6 +2168,7 @@ function createDefaultConfig(root, source, configuredWorkflow) {
|
|
|
2168
2168
|
coverage_metrics: 'lines',
|
|
2169
2169
|
coverage_report: '',
|
|
2170
2170
|
turbo_remote: 'auto',
|
|
2171
|
+
monorepo_tool: 'auto',
|
|
2171
2172
|
release_type: detectPackageManager(root) === 'none' ? 'auto' : 'node',
|
|
2172
2173
|
npm_publish: 'false',
|
|
2173
2174
|
post_release: 'false',
|
package/src/lib/profile.mjs
CHANGED
|
@@ -160,3 +160,123 @@ function detectReleaseType(root) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
export { includesValue }
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Workspace task runner. `auto` (default) is Turbo when a turbo config exists,
|
|
166
|
+
* else none; Vite+ needs an explicit `monorepo_tool: vite-plus`.
|
|
167
|
+
*/
|
|
168
|
+
export const MONOREPO_TOOLS = ['auto', 'turbo', 'vite-plus']
|
|
169
|
+
|
|
170
|
+
/** @typedef {'turbo'|'vite-plus'|'none'} ResolvedTool */
|
|
171
|
+
/** @typedef {{ configured: string, tool: ResolvedTool, reason: string, warnings: string[] }} ToolResolution */
|
|
172
|
+
|
|
173
|
+
/** @param {string} root @returns {Record<string, any> | null} */
|
|
174
|
+
function readPackageJson(root) {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
177
|
+
} catch {
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @param {string} root */
|
|
183
|
+
export function hasTurboConfig(root) {
|
|
184
|
+
return existsSync(join(root, 'turbo.json')) || existsSync(join(root, 'turbo.jsonc'))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Whether a shell command invokes the Vite+ task runner. `vp run ...` and the
|
|
189
|
+
* `vpr` shorthand populate the task cache; plain `vp build` or `vp test` do
|
|
190
|
+
* not, so they are intentionally not matched.
|
|
191
|
+
* @param {string} command
|
|
192
|
+
*/
|
|
193
|
+
export function commandUsesVitePlus(command) {
|
|
194
|
+
return /(?:^|[\s;&|(])(?:vp\s+(?:-C\s+\S+\s+)?run|vpr)(?:\s|$)/.test(command)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** @param {string} command */
|
|
198
|
+
export function commandUsesTurbo(command) {
|
|
199
|
+
return /\bturbo(?:\s|$)/.test(command)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Evidence that a repository declares Vite+: the `vite-plus` package in any
|
|
204
|
+
* dependency table or a script that runs `vp run` / `vpr`.
|
|
205
|
+
* @param {string} root
|
|
206
|
+
* @returns {{ declared: boolean, via: string[] }}
|
|
207
|
+
*/
|
|
208
|
+
export function detectVitePlus(root) {
|
|
209
|
+
const packageJson = readPackageJson(root)
|
|
210
|
+
/** @type {string[]} */
|
|
211
|
+
const via = []
|
|
212
|
+
if (!packageJson) return { declared: false, via }
|
|
213
|
+
for (const table of ['dependencies', 'devDependencies', 'optionalDependencies']) {
|
|
214
|
+
if (
|
|
215
|
+
packageJson[table] &&
|
|
216
|
+
typeof packageJson[table] === 'object' &&
|
|
217
|
+
packageJson[table]['vite-plus']
|
|
218
|
+
)
|
|
219
|
+
via.push(`${table}.vite-plus`)
|
|
220
|
+
}
|
|
221
|
+
const scripts =
|
|
222
|
+
packageJson.scripts && typeof packageJson.scripts === 'object' ? packageJson.scripts : {}
|
|
223
|
+
for (const [name, command] of Object.entries(scripts)) {
|
|
224
|
+
if (typeof command === 'string' && commandUsesVitePlus(command)) via.push(`scripts.${name}`)
|
|
225
|
+
}
|
|
226
|
+
return { declared: via.length > 0, via }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Resolve which task runner a repository uses. Never throws: invalid or
|
|
231
|
+
* unsatisfied configuration falls back to the legacy behavior and is reported
|
|
232
|
+
* in `warnings` so doctor can surface it.
|
|
233
|
+
* @param {string} root
|
|
234
|
+
* @param {Record<string, string>} [config] Defaults to .github/code-foundry.yml.
|
|
235
|
+
* @returns {ToolResolution}
|
|
236
|
+
*/
|
|
237
|
+
export function resolveMonorepoTool(
|
|
238
|
+
root,
|
|
239
|
+
config = readConfig(join(root, '.github/code-foundry.yml'))
|
|
240
|
+
) {
|
|
241
|
+
/** @type {string[]} */
|
|
242
|
+
const warnings = []
|
|
243
|
+
let selected = (config.monorepo_tool ?? '').trim() || 'auto'
|
|
244
|
+
if (!MONOREPO_TOOLS.includes(selected)) {
|
|
245
|
+
warnings.push(
|
|
246
|
+
`unsupported monorepo_tool: ${selected}; use auto, turbo, or vite-plus. Falling back to auto.`
|
|
247
|
+
)
|
|
248
|
+
selected = 'auto'
|
|
249
|
+
}
|
|
250
|
+
const turbo = hasTurboConfig(root)
|
|
251
|
+
|
|
252
|
+
if (selected === 'vite-plus') {
|
|
253
|
+
const evidence = detectVitePlus(root)
|
|
254
|
+
if (!evidence.declared)
|
|
255
|
+
warnings.push(
|
|
256
|
+
'monorepo_tool: vite-plus but neither a vite-plus dependency nor a `vp run` script was found in package.json.'
|
|
257
|
+
)
|
|
258
|
+
return {
|
|
259
|
+
configured: selected,
|
|
260
|
+
tool: 'vite-plus',
|
|
261
|
+
reason: turbo
|
|
262
|
+
? 'monorepo_tool: vite-plus (turbo config present but ignored)'
|
|
263
|
+
: 'monorepo_tool: vite-plus',
|
|
264
|
+
warnings,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (selected === 'turbo') {
|
|
268
|
+
if (!turbo) warnings.push('monorepo_tool: turbo but no turbo.json or turbo.jsonc was found.')
|
|
269
|
+
return {
|
|
270
|
+
configured: selected,
|
|
271
|
+
tool: turbo ? 'turbo' : 'none',
|
|
272
|
+
reason: 'monorepo_tool: turbo',
|
|
273
|
+
warnings,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
configured: selected,
|
|
278
|
+
tool: turbo ? 'turbo' : 'none',
|
|
279
|
+
reason: turbo ? 'turbo config detected' : 'no task runner configured',
|
|
280
|
+
warnings,
|
|
281
|
+
}
|
|
282
|
+
}
|