rvlib-shipkit 1.4.0 → 1.6.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.
- package/CHANGELOG.md +114 -0
- package/README.md +17 -17
- package/package.json +3 -2
- package/src/assets/gen/comfyts-gen.ts +12 -8
- package/src/assets/server/studio-server.ts +14 -2
- package/src/check.ts +73 -103
- package/src/checks/SK001-package-json-shape.check.ts +11 -6
- package/src/checks/SK002-contracts-fulfillness.check.ts +4 -0
- package/src/checks/SK003-claude-md-shape.check.ts +7 -3
- package/src/checks/SK004-gitignore-policy.check.ts +7 -4
- package/src/checks/SK005-rvlib-versions.check.ts +53 -21
- package/src/checks/SK006-npm-publish-safety.check.ts +4 -0
- package/src/checks/SK007-github-visibility.check.ts +4 -2
- package/src/checks/SK008-reviewer-agent.check.ts +6 -4
- package/src/checks/SK009-commit-ai-tells.check.ts +4 -2
- package/src/checks/SK010-undeclared-unit.check.ts +4 -0
- package/src/checks/SK011-sibling-unit-ranges.check.ts +4 -0
- package/src/checks/SK012-config-barrel-import.check.ts +5 -1
- package/src/checks/SK013-one-way-in-policy.check.ts +7 -3
- package/src/checks/SK014-prose-ste.check.ts +4 -0
- package/src/checks/SK015-nested-repo.check.ts +4 -2
- package/src/checks/SK016-legacy-check-shapes.check.ts +13 -12
- package/src/checks/SK017-vscode-hub-task.check.ts +22 -7
- package/src/checks/SK018-test-files-run.check.ts +6 -2
- package/src/checks/SK019-tests-run-in-units.check.ts +8 -4
- package/src/checks/SK020-self-spawning-check.check.ts +4 -0
- package/src/checks/SK022-machine-runs-the-pin.check.ts +6 -4
- package/src/checks/SK024-identity-colors.check.ts +5 -3
- package/src/checks/SK025-release-drift.check.ts +4 -5
- package/src/checks/SK026-asks-autoclose.check.ts +4 -4
- package/src/checks/SK027-agent-wiki-links.check.ts +4 -1
- package/src/checks/SK028-readme.check.ts +5 -1
- package/src/checks/SK029-explicit-check-files.check.ts +95 -0
- package/src/checks/SK030-cache-audit.check.ts +34 -0
- package/src/checks/SK031-trace-audit.check.ts +39 -0
- package/src/checks/SK032-ignored-inputs.check.ts +79 -0
- package/src/cli/cmd_Asks.ts +5 -5
- package/src/cli/cmd_Assets.ts +17 -13
- package/src/cli/cmd_Bump.ts +12 -11
- package/src/cli/cmd_CacheAudit.ts +43 -0
- package/src/cli/cmd_Check.ts +8 -7
- package/src/cli/cmd_Checks.ts +7 -6
- package/src/cli/cmd_Ci.ts +12 -11
- package/src/cli/cmd_Commands.ts +16 -13
- package/src/cli/cmd_ConfigKeys.ts +7 -6
- package/src/cli/cmd_Contracts.ts +3 -3
- package/src/cli/cmd_CrossRepoActivity.ts +8 -6
- package/src/cli/cmd_Fleet.ts +9 -7
- package/src/cli/cmd_GlobalProcesses.ts +120 -7
- package/src/cli/cmd_Home.ts +26 -19
- package/src/cli/cmd_Hooks.ts +6 -5
- package/src/cli/cmd_Init.ts +5 -4
- package/src/cli/cmd_Keys.ts +20 -8
- package/src/cli/cmd_Mv.ts +7 -6
- package/src/cli/cmd_Needs.ts +4 -3
- package/src/cli/cmd_Overview.ts +5 -4
- package/src/cli/cmd_Pb.ts +2 -2
- package/src/cli/cmd_Planned.ts +6 -5
- package/src/cli/cmd_Prose.ts +5 -5
- package/src/cli/cmd_Reflect.ts +7 -7
- package/src/cli/cmd_Release.ts +6 -5
- package/src/cli/cmd_Releases.ts +5 -4
- package/src/cli/cmd_Serve.ts +6 -4
- package/src/cli/cmd_Smoke.ts +5 -4
- package/src/cli/cmd_Start.ts +7 -6
- package/src/cli/cmd_Status.ts +4 -3
- package/src/cli/cmd_TraceAudit.ts +62 -0
- package/src/cli/cmd_Tui.ts +3 -2
- package/src/cli/cmd_When.ts +71 -0
- package/src/cli/cmd_Wiki.ts +4 -4
- package/src/cli.ts +11 -2
- package/src/collections/checks.ts +2 -0
- package/src/collections/collection-types.ts +12 -4
- package/src/collections/providers.ts +5 -3
- package/src/core/app-icon.ts +154 -0
- package/src/core/audit-report.ts +21 -0
- package/src/core/cache-audit.ts +72 -0
- package/src/core/canonical-scripts.ts +2 -2
- package/src/core/check-ctx.ts +14 -5
- package/src/core/check-runner.ts +43 -23
- package/src/core/config-rewrite.ts +37 -24
- package/src/core/discover-checks.ts +23 -2
- package/src/core/floor-resolve.ts +5 -7
- package/src/core/git-root.ts +13 -0
- package/src/core/ignored-block.ts +94 -0
- package/src/core/ignored-inputs.ts +47 -0
- package/src/core/jobs.ts +9 -2
- package/src/core/legacy-shapes.ts +39 -10
- package/src/core/one-lib.ts +59 -4
- package/src/core/one-way-in-policy.ts +2 -2
- package/src/core/outcome-words.ts +18 -2
- package/src/core/port-check-cli.ts +52 -0
- package/src/core/port-check-file.ts +295 -0
- package/src/core/port-check-files.ts +31 -0
- package/src/core/read-set.ts +97 -9
- package/src/core/retention.ts +0 -14
- package/src/core/run-check.ts +12 -9
- package/src/core/run-ci.ts +14 -5
- package/src/core/run-fix.ts +30 -23
- package/src/core/run-fixers.ts +48 -5
- package/src/core/runs-matrix.ts +179 -0
- package/src/core/runs.ts +242 -0
- package/src/core/trace-audit.ts +120 -0
- package/src/core/update-policy.ts +20 -0
- package/src/core/when-block.ts +29 -30
- package/src/core/when.ts +41 -103
- package/src/core/wire.ts +10 -14
- package/src/fleet/scan-fleet.ts +20 -4
- package/src/fleet/update-fleet.ts +6 -6
- package/src/home/gui-app.ts +24 -27
- package/src/home/home-launchd.ts +20 -4
- package/src/home/home-routes.ts +12 -1
- package/src/home/home-start.ts +11 -5
- package/src/home/hubs.ts +5 -5
- package/src/home/open-app.ts +2 -2
- package/src/home/pm.ts +8 -0
- package/src/home/run-pm.ts +1 -0
- package/src/home/serve-web.ts +2 -2
- package/src/home/supervise.ts +24 -1
- package/src/home/via-hub.ts +14 -0
- package/src/hooks/run-hooks.ts +48 -4
- package/src/init-repo.ts +6 -6
- package/src/ledger/db.ts +2 -13
- package/src/ledger/gate-checks.ts +133 -61
- package/src/ledger/git-key.ts +153 -31
- package/src/ledger/git-scan.ts +5 -2
- package/src/ledger/ledger.ts +136 -21
- package/src/ledger/store.ts +14 -0
- package/src/ledger/toolchain-identity.ts +248 -45
- package/src/plan/repo-source.ts +18 -39
- package/src/problems/extract.ts +3 -0
- package/src/release/run-release.ts +5 -0
- package/src/repo-config.ts +46 -54
- package/src/rules/rule.ts +2 -2
- package/src/scaffold/files/agent-coding.md +1 -1
- package/src/scaffold/files/agent-index.md +1 -1
- package/src/scaffold/files/verify-skill.md +2 -2
- package/src/serve/hub-app/activity-st.ts +5 -4
- package/src/serve/hub-app/activity-view.tsx +1 -1
- package/src/serve/hub-app/admin-svc.ts +36 -3
- package/src/serve/hub-app/admin-view.tsx +46 -12
- package/src/serve/hub-app/check-view.tsx +66 -46
- package/src/serve/hub-app/check-words.ts +1 -11
- package/src/serve/hub-app/checks-svc.ts +14 -35
- package/src/serve/hub-app/col-card.tsx +1 -44
- package/src/serve/hub-app/col-tables-view.tsx +84 -11
- package/src/serve/hub-app/col-tables.ts +27 -0
- package/src/serve/hub-app/cols-svc.ts +5 -4
- package/src/serve/hub-app/dock-st.ts +3 -2
- package/src/serve/hub-app/dock-view.tsx +36 -0
- package/src/serve/hub-app/dock.css +6 -0
- package/src/serve/hub-app/float-cards.tsx +30 -0
- package/src/serve/hub-app/floating.tsx +41 -4
- package/src/serve/hub-app/git-st.ts +42 -0
- package/src/serve/hub-app/git-view.tsx +61 -0
- package/src/serve/hub-app/hash-route.ts +45 -1
- package/src/serve/hub-app/header-bar.tsx +62 -72
- package/src/serve/hub-app/health-svc.ts +3 -2
- package/src/serve/hub-app/home-st.ts +14 -2
- package/src/serve/hub-app/home-strip.css +43 -12
- package/src/serve/hub-app/home-strip.tsx +115 -64
- package/src/serve/hub-app/home.html +1 -0
- package/src/serve/hub-app/hub-app.tsx +32 -8
- package/src/serve/hub-app/hub-types.ts +5 -2
- package/src/serve/hub-app/hub.css +89 -63
- package/src/serve/hub-app/icons.tsx +11 -0
- package/src/serve/hub-app/index.html +1 -0
- package/src/serve/hub-app/keys.ts +15 -4
- package/src/serve/hub-app/live-svc.ts +3 -2
- package/src/serve/hub-app/machine-st.ts +18 -22
- package/src/serve/hub-app/plan-view.tsx +51 -0
- package/src/serve/hub-app/planned-st.ts +9 -4
- package/src/serve/hub-app/processes-view.tsx +45 -0
- package/src/serve/hub-app/project-list.css +4 -1
- package/src/serve/hub-app/project-list.tsx +6 -3
- package/src/serve/hub-app/projects-svc.ts +25 -10
- package/src/serve/hub-app/prose-hits-st.ts +3 -2
- package/src/serve/hub-app/reach.ts +36 -0
- package/src/serve/hub-app/readme-svc.ts +3 -2
- package/src/serve/hub-app/repo-sections.ts +131 -0
- package/src/serve/hub-app/repo-st.ts +70 -14
- package/src/serve/hub-app/sidebar.tsx +17 -42
- package/src/serve/hub-app/timeline-svc.ts +3 -2
- package/src/serve/hub-app/tree-build.ts +151 -155
- package/src/serve/hub-app/tree-row.tsx +55 -12
- package/src/serve/hub-app/tree-svc.ts +97 -56
- package/src/serve/hub-app/when-view.tsx +89 -0
- package/src/serve/hub-app/when.css +17 -0
- package/src/serve/hub-runtime.ts +7 -18
- package/src/serve/project-gutter-route.ts +22 -1
- package/src/serve/run-serve.ts +1 -1
- package/src/serve/serve-hub.ts +4 -14
- package/src/suite/judge-suites.ts +6 -0
- package/src/trace/interpose.c +655 -0
- package/src/trace/read-trace.ts +485 -0
- package/src/trace/tracer.ts +282 -0
- package/src/serve/hub-app/processes-svc.ts +0 -63
- package/src/serve/hub-app/repo-sections.tsx +0 -61
- package/src/serve/repo-processes.ts +0 -89
package/src/ledger/db.ts
CHANGED
|
@@ -26,20 +26,9 @@
|
|
|
26
26
|
import { Database } from 'bun:sqlite'
|
|
27
27
|
import { existsSync, mkdirSync } from 'fs'
|
|
28
28
|
import { dirname, join } from 'path'
|
|
29
|
-
import { gitRoot } from '../core/git-root.ts'
|
|
30
|
-
import { LAYOUT } from '../core/layout.ts'
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* `--git-common-dir` rather than `--git-dir`: a linked worktree has its own
|
|
35
|
-
* git dir but shares the common one, and that sharing is safe because every
|
|
36
|
-
* key component is content or a version string. A tree that is not a git
|
|
37
|
-
* repo at all still gets one, under `.shipkit/cache/`, because a scaffold in
|
|
38
|
-
* progress must not crash on it. */
|
|
39
|
-
export function shipkitRoot(root: string): string {
|
|
40
|
-
const dir = gitRoot(root)?.commonDir
|
|
41
|
-
return dir == null ? join(root, LAYOUT.cache) : join(dir, 'shipkit')
|
|
42
|
-
}
|
|
30
|
+
import { shipkitRoot } from '../core/git-root.ts'
|
|
31
|
+
export { shipkitRoot }
|
|
43
32
|
|
|
44
33
|
export const DB_FILE = 'shipkit.sqlite'
|
|
45
34
|
|
|
@@ -11,27 +11,29 @@
|
|
|
11
11
|
// check script, a `*.check.ts` with no check object). The declaration always lives WITH the check, never in a table
|
|
12
12
|
// over here: a table beside the registry is a second source of truth that
|
|
13
13
|
// drifts the first time somebody adds one.
|
|
14
|
-
import {
|
|
14
|
+
import { existsSync } from 'fs'
|
|
15
|
+
import { join, relative, resolve } from 'path'
|
|
15
16
|
import type { CheckCtx } from '../core/check-ctx.ts'
|
|
16
17
|
import { type RepoConfig } from '../repo-config.ts'
|
|
18
|
+
import { auditedExtras, ignoredFor, readAuditReport } from '../core/audit-report.ts'
|
|
17
19
|
import { listRepoFiles, resolveFloor, slotDescription, type FloorResolution } from '../core/floor.ts'
|
|
18
20
|
import { discoverLegacyChecks } from '../core/discover-legacy-checks.ts'
|
|
19
21
|
import { checkFileRunBy } from '../core/legacy-shapes.ts'
|
|
20
22
|
import { ruleCacheExtra } from '../rules/rule.ts'
|
|
21
23
|
import { discoverChecks } from '../core/discover-checks.ts'
|
|
22
|
-
import { checkCacheExtra, describeFirst, type TaskDef, type CheckFrom, type CheckEnv } from '../check.ts'
|
|
24
|
+
import { checkCacheExtra, describeFirst, type TaskDef, type CheckFrom, type CheckEnv, type CheckDef } from '../check.ts'
|
|
23
25
|
import { CHECK_TIMEOUT_MS, type RunnableCheck, type CheckBody } from '../core/check-runner.ts'
|
|
24
|
-
import { ttlMsOf } from '../core/wire.ts'
|
|
26
|
+
import { FOREVER, ttlMsOf } from '../core/wire.ts'
|
|
25
27
|
import { budgetOf } from '../core/budget.ts'
|
|
26
|
-
import {
|
|
27
|
-
import type
|
|
28
|
+
import { repoOff, withRepoOff, whenSummary, type WhenDecl } from '../core/when.ts'
|
|
29
|
+
import { cacheText, filesText, NOWHERE, sourced, type CacheDecl, type ResolvedRuns, type Runs } from '../core/runs.ts'
|
|
30
|
+
import { isNamesInput, type CheckDecl } from './git-key.ts'
|
|
28
31
|
import { populationOf } from '../problems/problem-line.ts'
|
|
29
32
|
import { rawAccessInClosure, rawAccessLine } from '../core/raw-access.ts'
|
|
30
33
|
import type { RvtConfig } from '../ts/config.ts'
|
|
31
34
|
import { LIB_ROOT } from '../core/lib-root.ts'
|
|
32
35
|
import type { DiscoveredCheck } from '../core/discover-checks.ts'
|
|
33
36
|
import { declaredTestUnits, inputsOutsideUnits, isTestFile } from '../suite/test-units.ts'
|
|
34
|
-
import type { PlanDecl } from '../plan/cadence.ts'
|
|
35
37
|
|
|
36
38
|
/** why an undeclared `run` check cannot be keyed on its reads, or null when it
|
|
37
39
|
* can. A built-in is walked inside the toolkit, a repo check inside the repo
|
|
@@ -74,18 +76,6 @@ export async function allTasks(c: RepoConfig, root: string): Promise<FloorResolu
|
|
|
74
76
|
/** one gate check with everything a reader asks about it, not only what the
|
|
75
77
|
* key layer asks. The cache half stays in `decl` so `CheckDecl` never grows
|
|
76
78
|
* fields the key layer must then decide to ignore. */
|
|
77
|
-
/** a check's `planOff`, read now; one that throws stays planned, and says so */
|
|
78
|
-
function planOffOf(id: string, fn: ((env: CheckEnv) => string | null) | undefined, env: CheckEnv): { planOff?: string } {
|
|
79
|
-
if (fn == null) return {}
|
|
80
|
-
try {
|
|
81
|
-
const why = fn(env)
|
|
82
|
-
return why == null ? {} : { planOff: why }
|
|
83
|
-
} catch (e) {
|
|
84
|
-
console.error(`WARN ${id} planOff threw, so it stays planned: ${(e as Error).message}`)
|
|
85
|
-
return {}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
79
|
export type CheckRow = {
|
|
90
80
|
decl: CheckDecl
|
|
91
81
|
/** the display name an operator reads (its title, else the id) */
|
|
@@ -126,16 +116,16 @@ export type CheckRow = {
|
|
|
126
116
|
* then re-runs the check. `shipkit checks --fixable` is the catalog of
|
|
127
117
|
* everything in this repo that repairs itself. */
|
|
128
118
|
fixable: boolean
|
|
129
|
-
/** WHERE IT RUNS,
|
|
130
|
-
*
|
|
119
|
+
/** WHERE IT RUNS, every context resolved: what the check file declared,
|
|
120
|
+
* or what the code deriving the row wrote, with the repo's `off`
|
|
121
|
+
* override laid over it (core/runs.ts, core/when.ts) */
|
|
122
|
+
runs: ResolvedRuns
|
|
123
|
+
/** how its answer is kept, as one short cell (core/runs.ts cacheText) */
|
|
124
|
+
cache: string
|
|
125
|
+
/** the coarse word the runs read as, for the surfaces that show one */
|
|
131
126
|
when: WhenDecl
|
|
132
127
|
/** what it waits on, null when it waits on nothing */
|
|
133
128
|
deps: string[] | null
|
|
134
|
-
/** when the planner runs it on its own (check.ts `every`, `cron`,
|
|
135
|
-
* `whenInactive`). Absent: nothing declared, the planner's defaults */
|
|
136
|
-
plan?: PlanDecl
|
|
137
|
-
/** why the plan leaves this check out in this repo (check.ts `planOff`) */
|
|
138
|
-
planOff?: string
|
|
139
129
|
/** whose check it is: `shipkit` = it ships with the toolkit (a built-in
|
|
140
130
|
* SK check, the contracts pass, a floor slot nothing in the repo filled),
|
|
141
131
|
* `repo` = this repo declared it (a package.json script behind a floor
|
|
@@ -178,7 +168,7 @@ function runnableOf(row: CheckRow, body: CheckBody, o: { fix?: unknown; pointer?
|
|
|
178
168
|
owner: row.owner ?? row.decl.id,
|
|
179
169
|
blocking: row.blocking,
|
|
180
170
|
build: row.build,
|
|
181
|
-
|
|
171
|
+
runs: row.runs,
|
|
182
172
|
deps: row.deps ?? [],
|
|
183
173
|
group: row.group,
|
|
184
174
|
timeoutMs: row.timeoutMs ?? (row.build ? BUILD_TIMEOUT_MS : CHECK_TIMEOUT_MS),
|
|
@@ -211,6 +201,59 @@ function rootTestKey(root: string, c: RepoConfig, floor: FloorResolution): { inp
|
|
|
211
201
|
const taskDescription = (t: TaskDef): string =>
|
|
212
202
|
describeFirst(t.description, t.cmd == null ? null : `\`${t.cmd.join(' ')}\` exits 0`, `the ${t.id} task ${t.cmd == null ? 'passes' : 'exits 0'}`)
|
|
213
203
|
|
|
204
|
+
/** the default range a repo row nothing can key runs on, so it is asked
|
|
205
|
+
* about once a day and never in step with its siblings */
|
|
206
|
+
const DAILY: [string, string] = ['20h', '28h']
|
|
207
|
+
|
|
208
|
+
/** the runs of a row with no file to declare them in (a floor slot, a config
|
|
209
|
+
* task, a legacy rule or script), written out in full with the reason. It
|
|
210
|
+
* keeps what the planner and the gate did with such a row before the
|
|
211
|
+
* declaration was explicit: a ttl is the cadence ('once' for a forever
|
|
212
|
+
* fact), a save moves a keyed row or a fact, a gate build step nothing keys
|
|
213
|
+
* reruns after every save burst, and any other row nothing keys runs about
|
|
214
|
+
* once a day */
|
|
215
|
+
function derivedRuns(o: { gate: boolean; build: boolean; inputs?: string[]; ttl?: string; kind: string; repair: boolean }): ResolvedRuns {
|
|
216
|
+
const keyed = o.inputs != null
|
|
217
|
+
const ttl = o.ttl != null && o.ttl !== '' ? o.ttl : null
|
|
218
|
+
const fact = ttl === FOREVER
|
|
219
|
+
const parked = o.build && !o.gate
|
|
220
|
+
const onClock: Runs['onClock'] = parked ? false : fact ? 'once' : ttl != null ? { every: ttl, inactive: 'wait' } : o.build || keyed ? false : { every: DAILY, inactive: 'wait' }
|
|
221
|
+
const onSave: Runs['onSave'] = parked ? false : fact || (keyed && ttl == null) ? 'moved' : ttl == null && o.build ? 'burst' : false
|
|
222
|
+
const r: Runs = { ...NOWHERE, onCi: o.gate, onFull: o.gate, onRelease: o.gate, onHealth: !o.build, onSave, onClock }
|
|
223
|
+
const fix = !o.repair ? 'none' : o.gate ? 'ci' : 'manual'
|
|
224
|
+
const clock = onClock === false ? '' : onClock === 'once' ? ', once while its fact is unknown' : `, on the clock every ${ttl ?? DAILY.join('..')}`
|
|
225
|
+
const save = onSave === 'moved' ? ', the daemon when its key moves' : onSave === 'burst' ? ', the daemon after every save burst since nothing keys it' : ''
|
|
226
|
+
const where = parked ? 'parked: only --with or --only names it' : `${o.gate ? 'every gate' : 'no gate'}${o.build ? ', never the health pass' : ' and the health pass'}${save}${clock}`
|
|
227
|
+
return sourced(r, fix, 'derived', `${o.kind}: ${where}`)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** the cache half of a check file, as the key layer reads it */
|
|
231
|
+
function declOfCache(cache: CacheDecl, env: CheckEnv): { inputs?: string[]; ttl?: string; never?: string; key?: string; reads?: boolean } {
|
|
232
|
+
switch (cache.by) {
|
|
233
|
+
case 'files': return { inputs: cache.inputs, key: cache.key?.(env) }
|
|
234
|
+
case 'reads': return { reads: true }
|
|
235
|
+
case 'ttl': return { ttl: cache.span, key: cache.key?.(env) }
|
|
236
|
+
case 'fact': return { ttl: FOREVER, key: cache.key(env) }
|
|
237
|
+
case 'never': return { never: cache.why }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** a legacy row's cache, as one cell */
|
|
242
|
+
const legacyCacheText = (d: { inputs?: string[]; ttl?: string; never?: string }): string =>
|
|
243
|
+
d.inputs != null ? filesText(d.inputs) : d.ttl != null && d.ttl !== '' ? (d.ttl === FOREVER ? 'fact' : `ttl ${d.ttl}`) : d.never != null ? `never: ${d.never}` : 'never: nothing declared'
|
|
244
|
+
|
|
245
|
+
/** a check file's own runs, resolved: its declaration, n/a where it does not
|
|
246
|
+
* apply, then the repo's override */
|
|
247
|
+
function fileRuns(def: CheckDef, id: string, c: RepoConfig, env: CheckEnv): ResolvedRuns {
|
|
248
|
+
const own = sourced(def.runs, def.fix, 'check file')
|
|
249
|
+
let na: string | null = null
|
|
250
|
+
if (typeof def.applies === 'function') {
|
|
251
|
+
try { na = def.applies(env) } catch (e) { console.error(`WARN ${id} applies threw, so it applies: ${(e as Error).message}`) }
|
|
252
|
+
}
|
|
253
|
+
const base = na == null ? own : { ...own, off: `n/a here: ${na}` }
|
|
254
|
+
return withRepoOff(base, repoOff(c, id))
|
|
255
|
+
}
|
|
256
|
+
|
|
214
257
|
/** THE enumeration. Everything below reads it, nothing else walks. */
|
|
215
258
|
export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts = {}): Promise<GateCheck[]> {
|
|
216
259
|
const out: GateCheck[] = []
|
|
@@ -249,13 +292,29 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
249
292
|
// what it likes, and a gate that guesses which one is the tests will miss
|
|
250
293
|
// one.
|
|
251
294
|
const rootTest = rootTestKey(root, c, floor)
|
|
295
|
+
// a task that declares nothing is keyed on the whole work tree, plus the
|
|
296
|
+
// gitignored paths it reads. The work tree sees no ignored file, so those
|
|
297
|
+
// are named: by the config, and by the kept traced audit (SK031), whose
|
|
298
|
+
// findings join the key of the task that read them before a config write
|
|
299
|
+
// carries them to other clones. One missing from disk is left out, and
|
|
300
|
+
// its appearing moves the key
|
|
301
|
+
const onDisk = (p: string): boolean => existsSync(join(root, isNamesInput(p) ? p.slice(0, -3) : p))
|
|
302
|
+
const audit = readAuditReport(root)
|
|
303
|
+
const extrasOf = (id: string): string[] => [...new Set([...ignoredFor(c.ignoredInputs, id), ...auditedExtras(audit, id)])].filter(onDisk).sort()
|
|
252
304
|
for (const raw of [...floor.tasks, ...(c.tasks ?? []).filter((t) => foldedInto.get(t.id) !== t)]) {
|
|
253
305
|
const t: TaskDef = full ? { ...raw, env: { ...raw.env, [FULL_TESTS_ENV]: '1' } } : raw
|
|
254
306
|
const own = t.id === 'test' && floor.tasks.includes(raw) ? rootTest : null
|
|
307
|
+
const declared = own?.inputs ?? t.inputs
|
|
308
|
+
// the declared extras join a declared list too: a list names what the
|
|
309
|
+
// task reads in the tree, and the audit found these outside it
|
|
310
|
+
const extras = extrasOf(t.id)
|
|
311
|
+
const inputs = declared == null && t.ttl == null && t.never == null && t.cmd != null ? ['.', ...extras] : declared == null ? declared : [...declared, ...extras]
|
|
312
|
+
const kind = floor.tasks.includes(raw) ? 'a floor slot' : 'a repo.config.ts task'
|
|
313
|
+
const runs = withRepoOff(derivedRuns({ gate: t.optIn !== true, build: true, inputs, ttl: t.ttl, kind, repair: false }), repoOff(c, t.id))
|
|
255
314
|
const row: CheckRow = {
|
|
256
315
|
decl: {
|
|
257
316
|
id: t.id,
|
|
258
|
-
inputs
|
|
317
|
+
inputs,
|
|
259
318
|
ttl: t.ttl,
|
|
260
319
|
never: t.never,
|
|
261
320
|
argv: t.cmd,
|
|
@@ -266,7 +325,7 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
266
325
|
description: taskDescription(t),
|
|
267
326
|
// an optIn task is off the default run, which is the same shape as
|
|
268
327
|
// a check that opted out of the gate
|
|
269
|
-
gate:
|
|
328
|
+
gate: runs.off == null && runs.onCi,
|
|
270
329
|
build: true,
|
|
271
330
|
// a build step IS the build: it has no non-blocking mode
|
|
272
331
|
blocking: true,
|
|
@@ -278,7 +337,9 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
278
337
|
// a build step runs a TOOL, and repairing what a tool reports is
|
|
279
338
|
// that tool's own job (a formatter, a codemod), never the gate's
|
|
280
339
|
fixable: false,
|
|
281
|
-
|
|
340
|
+
runs,
|
|
341
|
+
cache: legacyCacheText({ inputs, ttl: t.ttl, never: t.never }),
|
|
342
|
+
when: whenSummary(runs, repoOff(c, t.id), false),
|
|
282
343
|
deps: t.deps ?? null,
|
|
283
344
|
from: taskFrom(t.id),
|
|
284
345
|
}
|
|
@@ -293,6 +354,7 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
293
354
|
// no command, which is why it declares `never` rather than joining the
|
|
294
355
|
// burn-down of checks nobody has keyed yet.
|
|
295
356
|
for (const id of floor.disabled) {
|
|
357
|
+
const runs = sourced(NOWHERE, 'none', 'repo.config.ts', null, `defaults.${id}: false`)
|
|
296
358
|
const row: CheckRow = {
|
|
297
359
|
decl: { id, never: `turned off by defaults.${id}: false, so there is no command to key` },
|
|
298
360
|
title: id,
|
|
@@ -306,7 +368,9 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
306
368
|
budgetMs: budgetOf(id, undefined, { build: true, cmd: false, world: false }, c.budgets),
|
|
307
369
|
optIn: false,
|
|
308
370
|
fixable: false,
|
|
309
|
-
|
|
371
|
+
runs,
|
|
372
|
+
cache: `never: turned off by defaults.${id}: false`,
|
|
373
|
+
when: whenSummary(runs, { reason: `defaults.${id}: false`, source: 'defaults' }, false),
|
|
310
374
|
deps: null,
|
|
311
375
|
// the repo wrote the line that turned it off
|
|
312
376
|
from: 'repo',
|
|
@@ -319,46 +383,47 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
319
383
|
const configs = new Map<string, RvtConfig>()
|
|
320
384
|
const texts = new Map<string, string>()
|
|
321
385
|
for (const u of checks) {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
// through its context (git-key.ts
|
|
325
|
-
// question: a body can read the env,
|
|
326
|
-
// stand in for a full one
|
|
327
|
-
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
const reads = undeclared && raw == null
|
|
332
|
-
const extra = checkCacheExtra({ id: u.id, cacheKey: u.def.cacheKey }, env, w)
|
|
386
|
+
const runs = fileRuns(u.def, u.id, c, env)
|
|
387
|
+
const cache = declOfCache(u.def.cache, env)
|
|
388
|
+
// a body keyed on what it reads through its context (git-key.ts
|
|
389
|
+
// READS). The tier is part of that question: a body can read the env,
|
|
390
|
+
// and a quick answer must never stand in for a full one. Any module
|
|
391
|
+
// its body can reach that touches the disk or a process around `c`
|
|
392
|
+
// contradicts the declaration (core/raw-access.ts), and says so loud
|
|
393
|
+
const raw = cache.reads === true && u.def.run != null ? rawAccessOf(root, u, configs, texts) : null
|
|
394
|
+
const extra = checkCacheExtra(cache.key, runs.off)
|
|
333
395
|
const row: CheckRow = {
|
|
334
396
|
decl: {
|
|
335
397
|
id: u.id,
|
|
336
|
-
inputs:
|
|
398
|
+
inputs: cache.inputs,
|
|
337
399
|
code: u.rel == null ? undefined : [u.rel],
|
|
338
|
-
|
|
339
|
-
never
|
|
400
|
+
// a built-in's code is the toolkit's: keyed on its own closure
|
|
401
|
+
// there, never on the toolkit as a whole
|
|
402
|
+
libCode: u.rel == null ? [u.path] : undefined,
|
|
403
|
+
ttl: cache.ttl,
|
|
404
|
+
never: cache.never,
|
|
340
405
|
argv: u.def.cmd,
|
|
341
|
-
extra: reads ? `${extra ?? ''}|tier=${full ? 'full' : 'quick'}` : extra,
|
|
406
|
+
extra: cache.reads === true ? `${extra ?? ''}|tier=${full ? 'full' : 'quick'}` : extra,
|
|
342
407
|
writes: u.def.writes,
|
|
343
|
-
...(reads ? { reads: true } : {}),
|
|
344
|
-
...(raw != null ? {
|
|
408
|
+
...(cache.reads === true && raw == null ? { reads: true } : {}),
|
|
409
|
+
...(raw != null ? { declError: `declares cache reads, but ${raw}` } : {}),
|
|
345
410
|
},
|
|
346
411
|
title: u.title,
|
|
347
412
|
description: u.description,
|
|
348
|
-
gate:
|
|
413
|
+
gate: runs.off == null && runs.onCi,
|
|
349
414
|
build: u.def.build === true,
|
|
350
415
|
blocking: u.def.blocking,
|
|
351
416
|
owner: u.owner,
|
|
352
417
|
group: u.def.group ?? null,
|
|
353
418
|
timeoutMs: u.def.timeoutMs ?? null,
|
|
354
|
-
budgetMs: budgetOf(u.id, u.def.budgetMs, { build: u.def.build === true, cmd: u.def.cmd != null, world:
|
|
419
|
+
budgetMs: budgetOf(u.id, u.def.budgetMs, { build: u.def.build === true, cmd: u.def.cmd != null, world: cache.ttl != null || cache.never != null }, c.budgets),
|
|
355
420
|
optIn: false,
|
|
356
|
-
fixable: u.def.
|
|
357
|
-
|
|
421
|
+
fixable: u.def.repair != null,
|
|
422
|
+
runs,
|
|
423
|
+
cache: cacheText(u.def.cache),
|
|
424
|
+
when: whenSummary(runs, repoOff(c, u.id), true),
|
|
358
425
|
deps: u.def.deps ?? null,
|
|
359
426
|
from: u.from,
|
|
360
|
-
...(u.def.every !== undefined || u.def.cron != null || u.def.whenInactive != null ? { plan: { every: u.def.every, cron: u.def.cron, whenInactive: u.def.whenInactive } } : {}),
|
|
361
|
-
...planOffOf(u.id, u.def.planOff, env),
|
|
362
427
|
}
|
|
363
428
|
const body: CheckBody = {
|
|
364
429
|
...(u.def.cmd == null ? {} : { cmd: u.def.cmd }),
|
|
@@ -367,11 +432,12 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
367
432
|
// relative to the root, and an absolute one stands as written
|
|
368
433
|
...(u.def.cwd == null ? {} : { cwd: resolve(root, u.def.cwd) }),
|
|
369
434
|
}
|
|
370
|
-
out.push({ row, runnable: runnableOf(row, body, { fix: u.def.
|
|
435
|
+
out.push({ row, runnable: runnableOf(row, body, { fix: u.def.repair, pointer: u.def.cmd?.join(' ') ?? null }) })
|
|
371
436
|
}
|
|
372
437
|
// LEGACY: `rules: []`, read as a check whose body is its lint. Dies with
|
|
373
438
|
// that config key once `shipkit fleet` shows no repo below 0.40.
|
|
374
439
|
for (const r of c.rules ?? []) {
|
|
440
|
+
const runs = withRepoOff(derivedRuns({ gate: true, build: false, inputs: r.inputs, ttl: r.ttl, kind: 'a legacy repo.config.ts rule', repair: r.fix != null }), repoOff(c, r.id))
|
|
375
441
|
const row: CheckRow = {
|
|
376
442
|
decl: {
|
|
377
443
|
id: r.id,
|
|
@@ -383,7 +449,7 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
383
449
|
},
|
|
384
450
|
title: r.title,
|
|
385
451
|
description: describeFirst(r.title, `rule ${r.id} holds`),
|
|
386
|
-
gate:
|
|
452
|
+
gate: runs.off == null && runs.onCi,
|
|
387
453
|
build: false,
|
|
388
454
|
blocking: r.blocking,
|
|
389
455
|
owner: r.owner,
|
|
@@ -392,7 +458,9 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
392
458
|
budgetMs: budgetOf(r.id, undefined, { build: false, cmd: false, world: r.ttl != null || r.never != null }, c.budgets),
|
|
393
459
|
optIn: false,
|
|
394
460
|
fixable: r.fix != null,
|
|
395
|
-
|
|
461
|
+
runs,
|
|
462
|
+
cache: legacyCacheText(r),
|
|
463
|
+
when: whenSummary(runs, repoOff(c, r.id), false),
|
|
396
464
|
deps: null,
|
|
397
465
|
from: 'repo',
|
|
398
466
|
}
|
|
@@ -409,12 +477,14 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
409
477
|
// file's own cache declaration wins over the task's
|
|
410
478
|
const d = task == null ? found : { ...found, gate: found.gate || task.optIn !== true }
|
|
411
479
|
const cached = d.inputs != null || d.ttl != null || d.never != null
|
|
480
|
+
const li = { inputs: d.inputs ?? (cached ? undefined : task?.inputs), ttl: d.ttl ?? (cached ? undefined : task?.ttl), never: d.never ?? (cached ? undefined : task?.never) }
|
|
481
|
+
const runs = withRepoOff(derivedRuns({ gate: d.gate, build: false, inputs: li.inputs, ttl: li.ttl, kind: 'a legacy check script', repair: false }), repoOff(c, d.name))
|
|
412
482
|
const row: CheckRow = {
|
|
413
483
|
decl: {
|
|
414
484
|
id: d.name,
|
|
415
|
-
inputs:
|
|
416
|
-
ttl:
|
|
417
|
-
never:
|
|
485
|
+
inputs: li.inputs,
|
|
486
|
+
ttl: li.ttl,
|
|
487
|
+
never: li.never,
|
|
418
488
|
code: [rel],
|
|
419
489
|
argv: ['bun', rel],
|
|
420
490
|
// no `extra`, so WHERE it runs stays out of a legacy check's
|
|
@@ -425,7 +495,7 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
425
495
|
},
|
|
426
496
|
title: d.name,
|
|
427
497
|
description: describeFirst(`\`bun ${rel}\` exits 0`, `${d.name} exits 0`),
|
|
428
|
-
gate:
|
|
498
|
+
gate: runs.off == null && runs.onCi,
|
|
429
499
|
build: false,
|
|
430
500
|
// the file wins, then the repo default, then nothing: an
|
|
431
501
|
// unclassified check is an error, never a guess
|
|
@@ -437,7 +507,9 @@ export async function composeChecks(root: string, c: RepoConfig, o: ComposeOpts
|
|
|
437
507
|
optIn: false,
|
|
438
508
|
// the reader is a source scanner, so it cannot carry a function
|
|
439
509
|
fixable: false,
|
|
440
|
-
|
|
510
|
+
runs,
|
|
511
|
+
cache: legacyCacheText(li),
|
|
512
|
+
when: whenSummary(runs, repoOff(c, d.name), false),
|
|
441
513
|
deps: task?.deps ?? null,
|
|
442
514
|
from: 'repo',
|
|
443
515
|
}
|