rvlib-shipkit 0.38.0 → 1.0.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 +371 -16
- package/README.md +26 -14
- package/package.json +2 -2
- package/src/assets/gen/comfyts-gen.ts +27 -10
- package/src/assets/gen/generate-roll.ts +1 -1
- package/src/assets/kit-config.ts +6 -6
- package/src/assets/run-assets.ts +14 -10
- package/src/assets/server/studio-server.ts +6 -5
- package/src/check.ts +343 -0
- package/src/{rules/SK001-package-json-shape.ts → checks/SK001-package-json-shape.check.ts} +44 -46
- package/src/checks/SK002-contracts-fulfillness.check.ts +24 -0
- package/src/checks/SK003-claude-md-shape.check.ts +66 -0
- package/src/checks/SK004-gitignore-policy.check.ts +309 -0
- package/src/{rules/SK005-rvlib-versions.ts → checks/SK005-rvlib-versions.check.ts} +46 -32
- package/src/{rules/SK006-npm-publish-safety.ts → checks/SK006-npm-publish-safety.check.ts} +25 -24
- package/src/{rules/SK007-github-visibility.ts → checks/SK007-github-visibility.check.ts} +20 -14
- package/src/{rules/SK008-reviewer-agent.ts → checks/SK008-reviewer-agent.check.ts} +29 -14
- package/src/{rules/SK009-commit-ai-tells.ts → checks/SK009-commit-ai-tells.check.ts} +24 -21
- package/src/checks/SK010-undeclared-unit.check.ts +111 -0
- package/src/{rules/SK011-sibling-unit-ranges.ts → checks/SK011-sibling-unit-ranges.check.ts} +21 -20
- package/src/{rules/SK012-config-barrel-import.ts → checks/SK012-config-barrel-import.check.ts} +18 -23
- package/src/{rules/SK013-one-way-in-policy.ts → checks/SK013-one-way-in-policy.check.ts} +58 -50
- package/src/{rules/SK014-prose-ste.ts → checks/SK014-prose-ste.check.ts} +27 -13
- package/src/{rules/SK015-nested-repo.ts → checks/SK015-nested-repo.check.ts} +15 -12
- package/src/checks/SK016-legacy-check-shapes.check.ts +182 -0
- package/src/checks/SK017-vscode-hub-task.check.ts +197 -0
- package/src/checks/SK018-test-files-run.check.ts +28 -0
- package/src/checks/SK019-tests-run-in-units.check.ts +76 -0
- package/src/checks/SK020-self-spawning-check.check.ts +147 -0
- package/src/checks/contracts.check.ts +20 -0
- package/src/cli/cmd_Asks.ts +3 -2
- package/src/cli/cmd_Assets.ts +2 -2
- package/src/cli/cmd_Bump.ts +1 -1
- package/src/cli/cmd_Check.ts +6 -6
- package/src/cli/cmd_Checks.ts +95 -0
- package/src/cli/cmd_Ci.ts +48 -13
- package/src/cli/cmd_Commands.ts +2 -6
- package/src/cli/cmd_ConfigKeys.ts +69 -0
- package/src/cli/cmd_Contracts.ts +1 -1
- package/src/cli/cmd_Fleet.ts +6 -2
- package/src/cli/cmd_Hooks.ts +1 -1
- package/src/cli/cmd_Keys.ts +43 -18
- package/src/cli/cmd_Mv.ts +2 -2
- package/src/cli/cmd_Prose.ts +10 -1
- package/src/cli/cmd_Releases.ts +2 -2
- package/src/cli/cmd_Serve.ts +1 -1
- package/src/cli/cmd_Start.ts +6 -6
- package/src/cli/cmd_Status.ts +25 -0
- package/src/cli/cmd_Tui.ts +2 -2
- package/src/cli.ts +7 -4
- package/src/collections/actions.ts +4 -4
- package/src/collections/checks.ts +258 -0
- package/src/collections/collection-types.ts +254 -1
- package/src/collections/comfy.ts +23 -0
- package/src/collections/md-body.ts +14 -0
- package/src/collections/pings.ts +38 -12
- package/src/collections/posts.ts +19 -3
- package/src/collections/providers.ts +198 -91
- package/src/collections/reflections.ts +41 -0
- package/src/collections/state-rules.ts +73 -0
- package/src/collections/test-time.ts +288 -0
- package/src/collections/tests.ts +181 -0
- package/src/collections/wikis.ts +66 -0
- package/src/comfy/comfy-panel.ts +292 -0
- package/src/contracts/apply-contracts.ts +17 -12
- package/src/contracts/contract-types.ts +16 -4
- package/src/contracts/contracts-engine.ts +78 -42
- package/src/contracts/manifests/discord.contract.ts +6 -6
- package/src/contracts/manifests/identity.contract.ts +3 -3
- package/src/contracts/manifests/itch.contract.ts +12 -15
- package/src/contracts/manifests/social.contract.ts +5 -5
- package/src/contracts/manifests/steam.contract.ts +16 -16
- package/src/contracts/run-contracts.ts +4 -3
- package/src/core/attach.ts +96 -0
- package/src/core/canonical-scripts.ts +25 -0
- package/src/core/check-ctx.ts +204 -14
- package/src/core/check-file-names.ts +112 -0
- package/src/core/check-pass.ts +84 -0
- package/src/core/check-runner.ts +796 -0
- package/src/core/config-keys.ts +256 -0
- package/src/core/config-rewrite.ts +77 -0
- package/src/core/discover-checks.ts +166 -90
- package/src/core/discover-legacy-checks.ts +26 -0
- package/src/core/doc-block.ts +13 -0
- package/src/core/floor-resolve.ts +264 -0
- package/src/core/floor.ts +20 -137
- package/src/core/git-bin.ts +24 -0
- package/src/core/git-root.ts +108 -0
- package/src/core/instance.ts +195 -0
- package/src/core/jobs.ts +118 -0
- package/src/core/layout.ts +52 -0
- package/src/core/legacy-shapes.ts +207 -0
- package/src/core/lib-root.ts +5 -0
- package/src/core/locked-write.ts +110 -39
- package/src/core/machine-commit.ts +5 -4
- package/src/core/migrate-layout.ts +557 -0
- package/src/core/motherhome.ts +2 -8
- package/src/core/nesting.ts +4 -12
- package/src/core/one-way-in-policy.ts +73 -17
- package/src/core/outcome-words.ts +45 -0
- package/src/core/package-manager.ts +86 -0
- package/src/core/parallel.ts +21 -0
- package/src/core/process-tree.ts +44 -0
- package/src/core/raw-access.ts +93 -0
- package/src/core/read-set.ts +152 -0
- package/src/core/run-check.ts +155 -213
- package/src/core/run-ci.ts +153 -189
- package/src/core/run-fix.ts +96 -0
- package/src/core/run-fixers.ts +53 -30
- package/src/core/self-update.ts +9 -3
- package/src/core/slots.ts +83 -0
- package/src/core/spawn-timeout.ts +34 -3
- package/src/core/when-block.ts +401 -0
- package/src/core/when.ts +135 -0
- package/src/core/write-status.ts +5 -2
- package/src/hooks/leak-guard.ts +10 -8
- package/src/hooks/one-way-in-guard.ts +13 -5
- package/src/hooks/root-identity.ts +2 -1
- package/src/hooks/run-hooks.ts +35 -23
- package/src/init-repo.ts +39 -23
- package/src/ledger/check-stats.ts +155 -0
- package/src/ledger/check-table.ts +179 -0
- package/src/ledger/db.ts +255 -0
- package/src/ledger/gate-checks.ts +433 -0
- package/src/ledger/git-key.ts +116 -77
- package/src/ledger/git-scan.ts +96 -35
- package/src/ledger/ledger.ts +116 -42
- package/src/ledger/live-runs.ts +111 -0
- package/src/ledger/live-timeline.ts +97 -0
- package/src/ledger/repo-view.ts +73 -0
- package/src/ledger/run-log.ts +448 -0
- package/src/ledger/run-timeline.ts +173 -0
- package/src/ledger/store.ts +23 -12
- package/src/ledger/suite-log.ts +161 -0
- package/src/ledger/toolchain-identity.ts +7 -1
- package/src/ledger/world-cache.ts +111 -0
- package/src/mv/mv-md.ts +16 -6
- package/src/problems/extract.ts +182 -0
- package/src/problems/problem-board.ts +37 -0
- package/src/problems/problem-line.ts +77 -0
- package/src/prose/prose-view.ts +90 -0
- package/src/prose/run-prose.ts +60 -19
- package/src/prose/ste.ts +140 -24
- package/src/reflections/reflections-queue.ts +81 -0
- package/src/release/git-facts.ts +15 -9
- package/src/release/run-bump.ts +6 -5
- package/src/release/run-release.ts +25 -11
- package/src/release/run-releases.ts +2 -2
- package/src/release/unit-gate.ts +7 -54
- package/src/release/unit-resolve.ts +282 -0
- package/src/release/units.ts +33 -217
- package/src/repo-config.ts +155 -85
- package/src/rules/rule.ts +36 -96
- package/src/rules/untrack-generated.ts +5 -4
- package/src/scaffold/files/vscode-tasks.json +10 -31
- package/src/scaffold/jsonc-spans.ts +113 -0
- package/src/scaffold/project-scaffold.ts +16 -43
- package/src/scan.ts +98 -0
- package/src/serve/gate-trigger.ts +148 -0
- package/src/serve/health-schedule.ts +102 -0
- package/src/serve/hub-app/chart-view.ts +179 -0
- package/src/serve/hub-app/check-view.tsx +345 -0
- package/src/serve/hub-app/check-words.ts +164 -0
- package/src/serve/hub-app/checks-svc.ts +280 -0
- package/src/serve/hub-app/clock-svc.ts +33 -0
- package/src/serve/hub-app/col-card.tsx +23 -39
- package/src/serve/hub-app/cols-svc.ts +44 -20
- package/src/serve/hub-app/comfy-svc.ts +171 -0
- package/src/serve/hub-app/comfy-view.tsx +81 -0
- package/src/serve/hub-app/dur.ts +41 -0
- package/src/serve/hub-app/hash-route.ts +121 -0
- package/src/serve/hub-app/header-bar.tsx +117 -0
- package/src/serve/hub-app/health-svc.ts +23 -28
- package/src/serve/hub-app/help.tsx +29 -0
- package/src/serve/hub-app/hub-app.tsx +122 -119
- package/src/serve/hub-app/hub-st.ts +115 -42
- package/src/serve/hub-app/hub-types.ts +117 -2
- package/src/serve/hub-app/hub.css +534 -192
- package/src/serve/hub-app/index.html +2 -0
- package/src/serve/hub-app/live-svc.ts +174 -0
- package/src/serve/hub-app/md.ts +15 -0
- package/src/serve/hub-app/media-svc.ts +4 -1
- package/src/serve/hub-app/media-view.tsx +15 -0
- package/src/serve/hub-app/next-run.ts +68 -0
- package/src/serve/hub-app/project-gutter.css +65 -0
- package/src/serve/hub-app/project-gutter.tsx +128 -0
- package/src/serve/hub-app/projects-svc.ts +226 -0
- package/src/serve/hub-app/prose-hits-st.ts +124 -0
- package/src/serve/hub-app/prose-hits-view.tsx +176 -0
- package/src/serve/hub-app/prose-hits.css +21 -0
- package/src/serve/hub-app/run-strip.ts +47 -0
- package/src/serve/hub-app/runs-tabs.ts +24 -0
- package/src/serve/hub-app/test-view.tsx +107 -0
- package/src/serve/hub-app/tests-svc.ts +62 -0
- package/src/serve/hub-app/tests-view.tsx +238 -0
- package/src/serve/hub-app/time-ago.tsx +34 -0
- package/src/serve/hub-app/timeline-chart.tsx +274 -0
- package/src/serve/hub-app/timeline-geometry.ts +314 -0
- package/src/serve/hub-app/timeline-svc.ts +62 -0
- package/src/serve/hub-app/timeline-view.tsx +245 -0
- package/src/serve/hub-app/tree-build.ts +410 -0
- package/src/serve/hub-app/tree-row.tsx +42 -0
- package/src/serve/hub-app/tree-svc.ts +174 -0
- package/src/serve/hub-app/wikis-view.tsx +53 -0
- package/src/serve/other-repos.ts +79 -0
- package/src/serve/project-gutter-route.ts +350 -0
- package/src/serve/project-gutter.ts +449 -0
- package/src/serve/run-serve.ts +245 -16
- package/src/serve/same-origin.ts +14 -0
- package/src/serve/serve-hub.ts +213 -18
- package/src/shipkit.ts +19 -12
- package/src/smoke/run-smoke.ts +3 -2
- package/src/start-repo.ts +75 -36
- package/src/suite/judge-suites.ts +216 -18
- package/src/suite/kept-timings.ts +58 -0
- package/src/suite/runner-shapes.ts +88 -0
- package/src/suite/suite-cli.ts +147 -24
- package/src/suite/suite-cpu-preload.ts +92 -0
- package/src/suite/test-units.ts +243 -0
- package/src/ts/config.ts +4 -69
- package/src/ts/jsonc.ts +69 -0
- package/src/ts/lex.ts +143 -0
- package/src/ts/rename.ts +73 -10
- package/src/ts/source-graph.ts +28 -8
- package/src/ts/tsconfig-projects.ts +6 -97
- package/src/ts/tsconfig-resolve.ts +108 -0
- package/src/tui/build-steps-panel.ts +61 -0
- package/src/tui/checks-panel.ts +24 -16
- package/src/tui/run-tui.ts +2 -2
- package/src/tui/tui-panel.ts +5 -4
- package/src/wiki/graph-colors.ts +68 -0
- package/src/wiki/graph-mount.ts +7 -9
- package/src/wiki/graph-page.ts +42 -25
- package/src/wiki/graph-positions.ts +105 -0
- package/src/wiki/run-wiki.ts +1 -1
- package/src/wiki/serve-graph.ts +33 -3
- package/src/cli/cmd_Rules.ts +0 -51
- package/src/cli/cmd_Units.ts +0 -82
- package/src/collections/graphs.ts +0 -32
- package/src/contracts/contracts-once.ts +0 -43
- package/src/core/check-suite.ts +0 -302
- package/src/core/repair-contract-dirs.ts +0 -136
- package/src/core/task-runner.ts +0 -339
- package/src/ledger/gate-units.ts +0 -138
- package/src/ledger/unit-table.ts +0 -105
- package/src/rules/SK002-contracts-fulfillness.ts +0 -24
- package/src/rules/SK003-claude-md-shape.ts +0 -70
- package/src/rules/SK004-gitignore-policy.ts +0 -238
- package/src/rules/SK010-undeclared-release-unit.ts +0 -127
- package/src/rules/rule-registry.ts +0 -64
- package/src/rules/run-fix.ts +0 -72
- package/src/tui/tasks-panel.ts +0 -60
package/src/repo-config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// defineRepo — the ONE import a repo needs: repo.config.ts at the root
|
|
2
|
-
// declares venues/social
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// declares venues/social, and the toolkit derives everything else.
|
|
3
|
+
// Checks are NOT declared here: drop a `<name>.check.ts` file and every
|
|
4
|
+
// surface discovers it (core/discover-checks.ts). The FLOOR
|
|
5
5
|
// (lint/typecheck/test/imports:check/cycles): a package.json script wins;
|
|
6
6
|
// with no script, imports:check/cycles fill from shipkit's own
|
|
7
7
|
// rvlib-ts-tools dep and typecheck from the repo's tsc (O6 zero-config);
|
|
@@ -11,22 +11,22 @@
|
|
|
11
11
|
import { existsSync, readFileSync } from 'fs'
|
|
12
12
|
import { isAbsolute, join, normalize } from 'path'
|
|
13
13
|
import { fileURLToPath } from 'url'
|
|
14
|
-
import { activeContracts, applyContractsResult, contractsConfigFrom } from './contracts/apply-contracts.ts'
|
|
15
14
|
import { ROUTED_SCRIPTS } from './core/canonical-scripts.ts'
|
|
16
15
|
import { FLOOR_IDS, resolveFloor, type FloorResolution } from './core/floor.ts'
|
|
17
16
|
export { resolveFloor, type FloorResolution, type FloorSource } from './core/floor.ts'
|
|
18
|
-
import { evaluateContractsOnce } from './contracts/contracts-once.ts'
|
|
19
17
|
import type { ContractManifest } from './contracts/contract-types.ts'
|
|
20
18
|
import type { ProseConfig } from './prose/run-prose.ts'
|
|
19
|
+
import { proseWordsProblems } from './prose/ste.ts'
|
|
21
20
|
import { validateSmokeConfig, type SmokeConfig } from './smoke/run-smoke.ts'
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
21
|
+
import { RULE_ID_RE, validateRules } from './rules/rule.ts'
|
|
22
|
+
import { builtinCheckIds } from './core/discover-checks.ts'
|
|
23
|
+
import { parseWhen } from './core/when.ts'
|
|
24
|
+
import type { Rule } from './check.ts'
|
|
25
25
|
import { leakAllowProblems } from './hooks/leak-guard.ts'
|
|
26
26
|
import type { HooksConfig } from './hooks/run-hooks.ts'
|
|
27
27
|
import type { ReleaseConfig } from './release/run-release.ts'
|
|
28
|
-
import { UNIT_DECL_KEYS, type
|
|
29
|
-
import { TASK_DEF_KEYS, type TaskDef } from './
|
|
28
|
+
import { TEST_ONLY_UNIT_KEYS, UNIT_DECL_KEYS, type UnitDecl } from './release/unit-resolve.ts'
|
|
29
|
+
import { TASK_DEF_KEYS, type TaskDef } from './check.ts'
|
|
30
30
|
import { testsConfigProblems, type TestsConfig } from './suite/judge-suites.ts'
|
|
31
31
|
import type { TuiPanelDef } from './tui/tui-panel.ts'
|
|
32
32
|
import type { MdGraph } from './wiki/md-graph.ts'
|
|
@@ -35,23 +35,40 @@ import type { MdGraph } from './wiki/md-graph.ts'
|
|
|
35
35
|
// rest by convention: agent/ graph when the dir exists, studio when the
|
|
36
36
|
// kit loads)
|
|
37
37
|
export type ServeConfig = {
|
|
38
|
-
/** hub port
|
|
38
|
+
/** hub port, house range 4800+. The default hashes the repo name into 4800-4979 */
|
|
39
39
|
port?: number
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/** the comfy-ts panel the comfy section embeds runs as its own process on
|
|
41
|
+
* its own port — house range 5800+, default = the hub port plus 1000
|
|
42
|
+
* (build/comfyui.md owns that surface) */
|
|
43
|
+
comfyPort?: number
|
|
44
|
+
/** custom wiki-graph provider for repos with their own link semantics.
|
|
45
|
+
* Default: a generic md scan of the repo root. */
|
|
46
|
+
wikiGraph?: {
|
|
47
|
+
/** the graph header title (default: the repo name) */
|
|
48
|
+
title?: string
|
|
49
|
+
/** builds the graph, called on every page load so a wiki edit shows up */
|
|
50
|
+
load: () => MdGraph
|
|
51
|
+
/** the check ids whose last verdicts are this wiki's verdict: red when
|
|
52
|
+
* one failed, green when all passed. Default: the built-in md lint
|
|
53
|
+
* (orphans and dangling refs) over the repo root */
|
|
54
|
+
lint?: string[]
|
|
55
|
+
}
|
|
42
56
|
/** agent/ graph entrypoint (default: index.md AND README.md) */
|
|
43
57
|
agentIndex?: string
|
|
44
|
-
/** off-machine hostname for the hub
|
|
45
|
-
*
|
|
46
|
-
*
|
|
58
|
+
/** off-machine hostname for the hub, as in a tailnet MagicDNS name. It
|
|
59
|
+
* prints beside localhost and rides on every ping message, because a
|
|
60
|
+
* phone cannot resolve localhost */
|
|
47
61
|
publicHost?: string
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
export type RepoConfig = {
|
|
65
|
+
/** the repo name, as every surface prints it: the gate header, the hub
|
|
66
|
+
* title, the motherhome row, the default hub port hash. The pre-commit
|
|
67
|
+
* root-identity gate compares it to the package.json name. */
|
|
51
68
|
name: string
|
|
52
69
|
/** venue ids this repo ships on — selects which contracts are REQUIRED */
|
|
53
70
|
venues?: string[]
|
|
54
|
-
/** social channels with a .
|
|
71
|
+
/** social channels with a .shipkit/social/<channel>/ post folder */
|
|
55
72
|
social?: string[]
|
|
56
73
|
/** prefix for contract fix: pointers (docs checkout or URL) — written by init, never baked in */
|
|
57
74
|
docsBase?: string
|
|
@@ -62,13 +79,34 @@ export type RepoConfig = {
|
|
|
62
79
|
* restating the command: the read set is repo knowledge, the command is
|
|
63
80
|
* already in package.json, and restating it is a second source of truth. */
|
|
64
81
|
defaults?: Record<string, Partial<TaskDef> | false>
|
|
65
|
-
/**
|
|
82
|
+
/** DEPRECATED, read through 0.40 and rewritten by `shipkit check --fix
|
|
83
|
+
* SK016`: a build step is a `<name>.check.ts` file with `build: true` */
|
|
66
84
|
tasks?: TaskDef[]
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
/** the human action queue: the folder it lives in, and the line the
|
|
86
|
+
* all-clear prints (`shipkit asks`). */
|
|
87
|
+
asks?: {
|
|
88
|
+
/** the queue folder, repo-relative (default '.shipkit/asks') */
|
|
89
|
+
dir?: string
|
|
90
|
+
/** the `shipkit asks` all-clear line, in the repo's own voice */
|
|
91
|
+
emptyText?: string
|
|
92
|
+
}
|
|
93
|
+
/** the secrets this repo needs. The mint artifact rides as data, so a
|
|
94
|
+
* missing one becomes a tile that says where to get it. */
|
|
95
|
+
creds?: {
|
|
96
|
+
/** the secret name the repo asks for, as `credsCommand` spells it */
|
|
97
|
+
name: string
|
|
98
|
+
/** which service issues it, shown as the tile description */
|
|
99
|
+
service?: string
|
|
100
|
+
/** where a human mints it, shown as the tile link */
|
|
101
|
+
mintUrl?: string
|
|
102
|
+
/** the steps to mint it, one line each */
|
|
103
|
+
steps?: string[]
|
|
104
|
+
/** the id of the check that proves it works (the id `--only` takes).
|
|
105
|
+
* Its last recorded run, from a gate or a health pass, is the secret's
|
|
106
|
+
* verdict. Absent: no check probes it, and the row says so */
|
|
107
|
+
probedBy?: string
|
|
108
|
+
}[]
|
|
109
|
+
/** store-command template for a missing secret tile, `{name}` substituted, as in your secret manager's set command */
|
|
72
110
|
credsCommand?: string
|
|
73
111
|
/** READ-command template, the twin of `credsCommand`: prints one field of
|
|
74
112
|
* a secret, `{name}` and `{field}` (get = the value, account = the
|
|
@@ -76,9 +114,16 @@ export type RepoConfig = {
|
|
|
76
114
|
* way a value reaches a command (`shipkit smoke` login), so it never sits
|
|
77
115
|
* in config, argv or env. Required when `smoke.login` is declared. */
|
|
78
116
|
credsReadCommand?: string
|
|
79
|
-
/** notify command for new RED needs while the hub serves, `{message}` substituted
|
|
117
|
+
/** notify command for new RED needs while the hub serves, `{message}` substituted, as in a push-notification CLI */
|
|
80
118
|
notifyCommand?: string
|
|
81
|
-
contracts
|
|
119
|
+
/** the `.shipkit/<venue>/` release contracts: which ones this repo is excused
|
|
120
|
+
* from, and any manifest the published toolkit does not carry. */
|
|
121
|
+
contracts?: {
|
|
122
|
+
/** venue id to the reason it is waived. The only bypass, and it always prints */
|
|
123
|
+
exempt?: Record<string, string>
|
|
124
|
+
/** manifests declared by this repo, beside the built-in venues */
|
|
125
|
+
extra?: ContractManifest[]
|
|
126
|
+
}
|
|
82
127
|
/** repo-contributed .gitignore lines, rendered INSIDE rule SK004's managed
|
|
83
128
|
* block — for ecosystem-specific names the published lib may not carry */
|
|
84
129
|
gitignore?: string[]
|
|
@@ -92,9 +137,18 @@ export type RepoConfig = {
|
|
|
92
137
|
* folder only: it never commits, untracks, tags or pushes on the parent's
|
|
93
138
|
* history. Rule SK015 fails when this and the disk disagree (core/nesting.ts) */
|
|
94
139
|
nested?: boolean
|
|
95
|
-
/**
|
|
96
|
-
*
|
|
97
|
-
* `
|
|
140
|
+
/** WHERE EACH CHECK RUNS, keyed by check id. One declaration for a
|
|
141
|
+
* question that used to have three: `'always'` every gate and every
|
|
142
|
+
* health pass, `'release'` only at `--full` and on the release path,
|
|
143
|
+
* `'health'` only in the health pass, `'off:<reason>'` not at all.
|
|
144
|
+
* `defineRepo` throws on a value it cannot parse, on an `off` with no
|
|
145
|
+
* reason, and on an id no check carries. The hub writes this
|
|
146
|
+
* map through the managed block in this file (core/when-block.ts). */
|
|
147
|
+
when?: Record<string, string>
|
|
148
|
+
/** DEPRECATED, read into `when` as `off:<reason>`: rule id → the reason
|
|
149
|
+
* this repo breaks that invariant on purpose. An exempted rule does not
|
|
150
|
+
* lint and prints a skip line carrying the reason. `defineRepo` throws on
|
|
151
|
+
* an unknown id or a blank reason */
|
|
98
152
|
ruleExempt?: Record<string, string>
|
|
99
153
|
/** declared github visibility INTENT (absent = 'private') — rule SK007
|
|
100
154
|
* fails loud when `gh repo view` disagrees in either direction */
|
|
@@ -105,33 +159,45 @@ export type RepoConfig = {
|
|
|
105
159
|
* read-only allowlist SK008 enforces (default: Read, Grep, Glob, Bash).
|
|
106
160
|
* Rule SK008 lints it, `check --fix SK008` writes it. Absent = no
|
|
107
161
|
* declared reviewer. */
|
|
108
|
-
reviewer?: {
|
|
162
|
+
reviewer?: {
|
|
163
|
+
/** the agent type, kebab-case: `.claude/agents/<name>.md` */
|
|
164
|
+
name: string
|
|
165
|
+
/** path to the standing brief the agent reads first. It can sit outside the repo */
|
|
166
|
+
brief: string
|
|
167
|
+
/** widens the read-only tool allowlist SK008 enforces (default Read, Grep, Glob, Bash) */
|
|
168
|
+
tools?: string[]
|
|
169
|
+
}
|
|
109
170
|
/** ONE WAY IN escape hatch (build/shipkit.md): governed script names this
|
|
110
171
|
* repo keeps as a direct tool call. Declared per NAME, never as a list of
|
|
111
172
|
* banned binaries: the invariant is about which VERBS an agent reaches
|
|
112
173
|
* for, and a binary blocklist goes stale the day a repo changes runner.
|
|
113
174
|
* A name outside ROUTED_SCRIPTS throws here, so a typo can never silently
|
|
114
175
|
* un-govern a script. Rule SK001 reads it. */
|
|
115
|
-
oneWayIn?: {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
176
|
+
oneWayIn?: {
|
|
177
|
+
/** governed script names this repo calls directly: the package.json
|
|
178
|
+
* route and the shell deny both drop for each one named */
|
|
179
|
+
exempt?: string[]
|
|
180
|
+
}
|
|
181
|
+
/** DEPRECATED, read through 0.40: a repo invariant is a `<name>.check.ts`
|
|
182
|
+
* file whose `run` is the old `lint`. SK016 names each entry, and moving
|
|
183
|
+
* one is a hand pass because it holds a function */
|
|
121
184
|
rules?: Rule[]
|
|
122
185
|
/** `shipkit release`, branch, tag shape, changelog, npm + GitHub release.
|
|
123
186
|
* Absent is fine: the defaults (main, `v{version}`, CHANGELOG.md, npm only
|
|
124
187
|
* when public, GitHub release on) fit most repos. release/run-release.ts */
|
|
125
188
|
release?: ReleaseConfig
|
|
126
|
-
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
|
|
189
|
+
/** the units this repo is cut into, beside the mandatory root unit. Each
|
|
190
|
+
* one scopes its own checks (`test:<unit id>` run in its dir, and
|
|
191
|
+
* `typecheck:<unit id>` when it has a tsconfig) and, unless
|
|
192
|
+
* it says `release: false`, is a releasable package with its own version,
|
|
193
|
+
* tag and changelog. Most repos have none and declare nothing. Nothing is
|
|
194
|
+
* scanned into existence: a package nobody declared fails rule SK010 with
|
|
195
|
+
* the entry to paste. release/units.ts */
|
|
196
|
+
units?: UnitDecl[]
|
|
131
197
|
/** repo-relative dirs whose `package.json` is deliberately NOT releasable
|
|
132
198
|
* (an electron shell, a cloudflare worker, a fixture, an examples app).
|
|
133
199
|
* Rule SK010 needs an achievable green: without this, a package that
|
|
134
|
-
* cannot
|
|
200
|
+
* cannot release leaves the gate permanently red. Declaring it
|
|
135
201
|
* here is a statement, so the package still cannot hide. One entry names
|
|
136
202
|
* ONE package dir (SK010 fails an entry with no package.json), and the
|
|
137
203
|
* repo root is refused outright: a single line that excuses everything
|
|
@@ -141,12 +207,14 @@ export type RepoConfig = {
|
|
|
141
207
|
* repo's own commands. `shipkit hooks list` enumerates them, so what blocks
|
|
142
208
|
* a commit is answerable without reading shell. hooks/run-hooks.ts */
|
|
143
209
|
hooks?: HooksConfig
|
|
144
|
-
/** what a
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
|
|
149
|
-
|
|
210
|
+
/** DEPRECATED, read through 0.40: what a legacy `*.check.ts` with no
|
|
211
|
+
* `export const blocking` means. `shipkit check --fix SK016` names the
|
|
212
|
+
* value in each script's port, where `blocking` is required, and drops
|
|
213
|
+
* the key once no script leans on it */
|
|
214
|
+
checkDefaults?: {
|
|
215
|
+
/** what a `*.check.ts` with no `export const blocking` means here */
|
|
216
|
+
blocking?: boolean
|
|
217
|
+
}
|
|
150
218
|
/** house prose in STE (identity/writing-voice.md), gated by rule SK014.
|
|
151
219
|
* `include` is what makes the rule run at all: a repo that declares no
|
|
152
220
|
* scope is skipped rather than failed, because STE binds the prose an
|
|
@@ -161,14 +229,20 @@ export type RepoConfig = {
|
|
|
161
229
|
* slow suites the quick tier leaves out, and the suites that cannot run in
|
|
162
230
|
* parallel, each with its reason. suite/judge-suites.ts */
|
|
163
231
|
tests?: TestsConfig
|
|
164
|
-
/**
|
|
165
|
-
tui?: {
|
|
232
|
+
/** the live dashboard, `shipkit tui`. */
|
|
233
|
+
tui?: {
|
|
234
|
+
/** extra dashboard panels, appended after the built-in build steps and checks panels (`shipkit tui`) */
|
|
235
|
+
panels?: TuiPanelDef[]
|
|
236
|
+
}
|
|
166
237
|
/** the `shipkit serve` hub: port + optional graph hooks */
|
|
167
238
|
serve?: ServeConfig
|
|
239
|
+
/** where a gate run writes its per task logs, repo-relative (default
|
|
240
|
+
* '.shipkit/tmp/shipkit-ci'). Each run owns a subdir of it, so several agents in
|
|
241
|
+
* one checkout never overwrite each other. Keep it gitignored. */
|
|
168
242
|
logDir?: string
|
|
169
243
|
}
|
|
170
244
|
|
|
171
|
-
const KNOWN_KEYS = new Set(['name', 'venues', 'social', 'docsBase', 'defaults', 'tasks', 'asks', 'creds', 'credsCommand', 'credsReadCommand', 'notifyCommand', 'contracts', 'gitignore', 'untrackGenerated', 'nested', 'ruleExempt', 'visibility', 'reviewer', 'oneWayIn', 'rules', 'release', 'units', 'notReleasable', 'hooks', 'checkDefaults', 'tui', 'serve', 'logDir', 'prose', 'smoke', 'tests'])
|
|
245
|
+
const KNOWN_KEYS = new Set(['name', 'venues', 'social', 'docsBase', 'defaults', 'tasks', 'asks', 'creds', 'credsCommand', 'credsReadCommand', 'notifyCommand', 'contracts', 'gitignore', 'untrackGenerated', 'nested', 'when', 'ruleExempt', 'visibility', 'reviewer', 'oneWayIn', 'rules', 'release', 'units', 'notReleasable', 'hooks', 'checkDefaults', 'tui', 'serve', 'logDir', 'prose', 'smoke', 'tests'])
|
|
172
246
|
|
|
173
247
|
export function defineRepo(c: RepoConfig): RepoConfig {
|
|
174
248
|
if (typeof c.name !== 'string' || c.name === '') throw new Error('defineRepo: name is required')
|
|
@@ -190,6 +264,13 @@ export function defineRepo(c: RepoConfig): RepoConfig {
|
|
|
190
264
|
if (!UNIT_DECL_KEYS.includes(k)) throw new Error(`defineRepo: units['${u.dir}'] unknown key '${k}' (known: ${UNIT_DECL_KEYS.join(', ')})`)
|
|
191
265
|
const problems = u.tests != null ? testsConfigProblems(u.tests) : []
|
|
192
266
|
if (problems.length > 0) throw new Error(`defineRepo: units['${u.dir}'] ${problems[0]}`)
|
|
267
|
+
if ('release' in u && u.release !== false) throw new Error(`defineRepo: units['${u.dir}'].release takes only false (a unit that never releases)`)
|
|
268
|
+
if (u.release === false) {
|
|
269
|
+
const idle = Object.keys(u).filter((k) => !TEST_ONLY_UNIT_KEYS.includes(k))
|
|
270
|
+
if (idle.length > 0) throw new Error(`defineRepo: units['${u.dir}'] is release: false, so ${idle.join(', ')} describe(s) a release that never happens (it reads ${TEST_ONLY_UNIT_KEYS.join(', ')})`)
|
|
271
|
+
}
|
|
272
|
+
if (u.deps != null && (!Array.isArray(u.deps) || u.deps.some((d) => typeof d !== 'string' || d === '')))
|
|
273
|
+
throw new Error(`defineRepo: units['${u.dir}'].deps is a list of check ids`)
|
|
193
274
|
}
|
|
194
275
|
for (const d of c.notReleasable ?? []) {
|
|
195
276
|
if (typeof d !== 'string' || d === '') throw new Error(`defineRepo: every notReleasable entry is a repo-relative dir path`)
|
|
@@ -226,14 +307,30 @@ export function defineRepo(c: RepoConfig): RepoConfig {
|
|
|
226
307
|
throw new Error(`defineRepo: oneWayIn.exempt '${n}' is not a governed script name (governed: ${ROUTED_SCRIPTS.join(', ')})`)
|
|
227
308
|
// repo rules are validated against the built-ins, so a bad id, a duplicate,
|
|
228
309
|
// or an SK squatter dies at config load, not silently mid-check
|
|
229
|
-
if (c.rules != null) validateRules(c.rules, { label: 'defineRepo: rules', taken:
|
|
310
|
+
if (c.rules != null) validateRules(c.rules, { label: 'defineRepo: rules', taken: builtinCheckIds() })
|
|
311
|
+
// a `when` moves a check off the gate, so a value it cannot act on dies
|
|
312
|
+
// here. The RULE namespace is the one this file can check: a task is
|
|
313
|
+
// resolved from the floor and a check is discovered on disk, so those two
|
|
314
|
+
// are caught by the gate against the real inventory (core/when.ts
|
|
315
|
+
// unknownWhenIds)
|
|
316
|
+
for (const [id, raw] of Object.entries(c.when ?? {})) {
|
|
317
|
+
if (id.trim() === '') throw new Error(`defineRepo: when has an entry with no check id`)
|
|
318
|
+
const w = parseWhen(raw)
|
|
319
|
+
if (typeof w === 'string') throw new Error(`defineRepo: when.${id} ${w}`)
|
|
320
|
+
if (RULE_ID_RE.test(id) && !builtinCheckIds().includes(id) && !(c.rules ?? []).some((r) => r.id === id))
|
|
321
|
+
throw new Error(`defineRepo: when.${id} names no rule, the ids are in \`shipkit checks\``)
|
|
322
|
+
}
|
|
230
323
|
// an exemption silences a gate, so it names a rule that exists and says why
|
|
231
324
|
for (const [id, reason] of Object.entries(c.ruleExempt ?? {})) {
|
|
232
|
-
if (!
|
|
233
|
-
throw new Error(`defineRepo: ruleExempt.${id} names no rule, the ids are in \`shipkit
|
|
325
|
+
if (!builtinCheckIds().includes(id) && !(c.rules ?? []).some((r) => r.id === id))
|
|
326
|
+
throw new Error(`defineRepo: ruleExempt.${id} names no rule, the ids are in \`shipkit checks\``)
|
|
234
327
|
if (typeof reason !== 'string' || reason.trim() === '')
|
|
235
328
|
throw new Error(`defineRepo: ruleExempt.${id} needs a reason, an exemption nobody can explain is a silenced gate`)
|
|
236
329
|
}
|
|
330
|
+
// a word list entry silences or adds gate hits, so a malformed one dies
|
|
331
|
+
// here rather than matching nothing
|
|
332
|
+
const words = proseWordsProblems(c.prose?.words)
|
|
333
|
+
if (words.length > 0) throw new Error(`defineRepo: ${words[0]}`)
|
|
237
334
|
// a smoke typo would land as a run that visits nothing and exits 0
|
|
238
335
|
if (c.smoke != null) validateSmokeConfig(c.smoke)
|
|
239
336
|
// a leak-guard waiver is the one config row that can make a gate quieter,
|
|
@@ -253,40 +350,13 @@ export function defineRepo(c: RepoConfig): RepoConfig {
|
|
|
253
350
|
return c
|
|
254
351
|
}
|
|
255
352
|
|
|
256
|
-
|
|
353
|
+
/** `fresh` busts the module cache: a process that WROTE the config (the hub's
|
|
354
|
+
* toggle) must read the bytes it just wrote, and an ES module import is
|
|
355
|
+
* cached by path for the life of the process */
|
|
356
|
+
export async function loadRepoConfig(root: string, o: { fresh?: boolean } = {}): Promise<RepoConfig> {
|
|
257
357
|
const path = join(root, 'repo.config.ts')
|
|
258
358
|
if (!existsSync(path)) throw new Error(`no repo.config.ts in ${root} — run: shipkit init`)
|
|
259
|
-
const mod = (await import(path)) as { default?: RepoConfig }
|
|
359
|
+
const mod = (await import(o.fresh === true ? `${path}?v=${Date.now()}-${Math.random().toString(36).slice(2)}` : path)) as { default?: RepoConfig }
|
|
260
360
|
if (mod.default == null) throw new Error(`${path}: must default-export defineRepo({...})`)
|
|
261
361
|
return mod.default
|
|
262
362
|
}
|
|
263
|
-
|
|
264
|
-
// the contracts commit-gate task — venue deploy tasks should dep on it.
|
|
265
|
-
// Same SK002-tagged rendering as `shipkit check` and `shipkit contracts`
|
|
266
|
-
// (contracts/apply-contracts.ts is the one bridge).
|
|
267
|
-
//
|
|
268
|
-
// DELIBERATELY UNDECLARED for the ledger, and this is where that decision
|
|
269
|
-
// lives rather than in a commit message. It runs in process, shares one
|
|
270
|
-
// evaluation with rule SK002 through `withContractsScope`, and measures 0.0s,
|
|
271
|
-
// so a key would be risk against no reward. It is not `never:` either: the
|
|
272
|
-
// contract dirs describe it perfectly well, so claiming nothing can is false,
|
|
273
|
-
// and `never` has to keep meaning "somebody looked and no key exists".
|
|
274
|
-
//
|
|
275
|
-
// Declaring `inputs: ['.']` to clear its burn-down row was TRIED and reverted:
|
|
276
|
-
// a keyable contracts task writes a ledger entry inside every fixture repo,
|
|
277
|
-
// which is six ledger tests asserting an entry count that is really about the
|
|
278
|
-
// task under test. The row it leaves behind is the honest cost of the model
|
|
279
|
-
// having no third state between "nobody looked" and "no key can exist".
|
|
280
|
-
export function contractsTask(c: RepoConfig, root: string): TaskDef {
|
|
281
|
-
return {
|
|
282
|
-
id: 'contracts',
|
|
283
|
-
logic: async (t) => {
|
|
284
|
-
applyContractsResult(t, await evaluateContractsOnce(activeContracts(c), contractsConfigFrom(c, root)), contractsFulfillness)
|
|
285
|
-
},
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function allTasks(c: RepoConfig, root: string): FloorResolution {
|
|
290
|
-
const floor = resolveFloor(c, root)
|
|
291
|
-
return { ...floor, tasks: [...floor.tasks, contractsTask(c, root), ...(c.tasks ?? [])] }
|
|
292
|
-
}
|
package/src/rules/rule.ts
CHANGED
|
@@ -1,103 +1,43 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import
|
|
1
|
+
// The LEGACY rule shape, kept for the compatibility window. A repo that
|
|
2
|
+
// still registers invariants through `repo.config.ts rules: []` imports
|
|
3
|
+
// `Rule` from here, and the engine reads each one as a check whose body is
|
|
4
|
+
// `lint` (check.ts owns the shape now). Both die together, at the first
|
|
5
|
+
// release after `shipkit fleet` shows no repo below 0.40.
|
|
6
|
+
//
|
|
7
|
+
// A new invariant is a `<name>.check.ts` file: `check({ blocking, run })`,
|
|
8
|
+
// dropped anywhere in the repo, discovered with no config edit.
|
|
9
|
+
import { whenOfRule } from '../core/when.ts'
|
|
10
|
+
import { tagCheckLines, checkCacheExtra, type Rule, type RuleEnv } from '../check.ts'
|
|
10
11
|
|
|
11
|
-
export type
|
|
12
|
+
export type { FixResult, Rule, RuleEnv } from '../check.ts'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export type Rule = {
|
|
16
|
-
/** stable PREFIX + 3 digits, never reused or renumbered (the registry throws).
|
|
17
|
-
* `SK###` is reserved for shipkit's built-ins; a repo registering its own
|
|
18
|
-
* rules through `repo.config.ts rules: []` picks its own prefix (`RV###`). */
|
|
19
|
-
id: string
|
|
20
|
-
/** check-suite display name — stable: the check name in STATUS.md, the TUI row, and the suite cache */
|
|
21
|
-
title: string
|
|
22
|
-
/** can this stop you? REQUIRED, no default. true = a failure is about THIS
|
|
23
|
-
* repo and blocks. false = it reports the world (a box that is off, a cred
|
|
24
|
-
* nobody minted), so a failure prints as a warning and never blocks.
|
|
25
|
-
* The only question worth asking about a check, asked once, in one place. */
|
|
26
|
-
blocking: boolean
|
|
27
|
-
/** wiki page owning the invariant — the run-check framework appends `docsBase + owner`
|
|
28
|
-
* as the fix: pointer whenever a failing/warning lint attached none */
|
|
29
|
-
owner: string
|
|
30
|
-
/** clean-pass cache duration (check-suite TTL, e.g. '24h') — for rules that
|
|
31
|
-
* probe the network; skip/WARN/FAIL runs are never cached. Declares the
|
|
32
|
-
* WORLD population (ledger/git-key.ts). */
|
|
33
|
-
ttl?: string
|
|
34
|
-
/** CONTENT population: the repo-relative paths this rule reads. A rule with
|
|
35
|
-
* neither `inputs` nor `ttl` is never cached, which is the safe default.
|
|
36
|
-
* A rule declared by a REPO rather than by shipkit must list the dir its
|
|
37
|
-
* own code lives in too: shipkit's `src` tree id covers the built-ins and
|
|
38
|
-
* cannot see a repo's. Declaring both `inputs` and `ttl` is a registration
|
|
39
|
-
* failure — a rule reads files or it reads the world, never both. */
|
|
40
|
-
inputs?: string[]
|
|
41
|
-
/** repo-relative entry file(s) of this rule's OWN code, walked through the
|
|
42
|
-
* module graph so a shared helper the rule imports is in its key too. A
|
|
43
|
-
* built-in SK rule needs none: the toolchain identity content-hashes
|
|
44
|
-
* shipkit's whole source, so editing any of it moves every key. A rule a
|
|
45
|
-
* REPO declares is invisible to that, so it names its module here, or
|
|
46
|
-
* includes the dir holding it in `inputs`. Without either, fixing a bug
|
|
47
|
-
* in the rule does not move its key. */
|
|
48
|
-
code?: string[]
|
|
49
|
-
/** a DECIDED never-cacheable, with the reason: this rule reads git history,
|
|
50
|
-
* a path that may live outside the repo, or the wall clock, so no key can
|
|
51
|
-
* describe it. Says somebody looked, which is what takes it off the
|
|
52
|
-
* burn-down list. */
|
|
53
|
-
never?: string
|
|
54
|
-
/** cache-identity contribution for TTL'd rules whose verdict depends on
|
|
55
|
-
* config — a changed key busts the cache instantly instead of replaying
|
|
56
|
-
* a stale assertion for the TTL */
|
|
57
|
-
cacheKey?: (env: RuleEnv) => string
|
|
58
|
-
lint: (t: CheckCtx, env: RuleEnv) => void | Promise<void>
|
|
59
|
-
/** idempotent auto-repair; `check --fix` applies it then re-lints — never interactive */
|
|
60
|
-
fix?: (env: RuleEnv) => FixResult | Promise<FixResult>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** the config part of a rule's cache identity */
|
|
14
|
+
/** the config part of a rule's cache identity, including where it runs */
|
|
64
15
|
export function ruleCacheExtra(rule: Rule, env: RuleEnv): string | undefined {
|
|
65
|
-
|
|
66
|
-
const exempt = env.config.ruleExempt?.[rule.id]
|
|
67
|
-
return exempt == null ? key : `${key ?? ''}|exempt:${exempt}`
|
|
16
|
+
return checkCacheExtra(rule, env, whenOfRule(env.config, rule.id))
|
|
68
17
|
}
|
|
69
18
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
t.lines = tagRuleLines(rule, t.lines)
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await rule.lint(t, env)
|
|
83
|
-
} catch (e) {
|
|
84
|
-
t.fail(`${rule.title} lint errored (${String(e).slice(0, 200)}) — network down, or a bug in this rule`)
|
|
85
|
-
}
|
|
86
|
-
if ((t.failed || t.warned) && !t.lines.some((l) => l.kind === 'fix-fail' || l.kind === 'fix-warn'))
|
|
87
|
-
t.lines.push({ kind: t.failed ? 'fix-fail' : 'fix-warn', text: `${env.config.docsBase ?? ''}${rule.owner}` })
|
|
88
|
-
t.lines = tagRuleLines(rule, t.lines)
|
|
89
|
-
}
|
|
19
|
+
/** tag every severity line with the rule id (check.ts owns the rendering) */
|
|
20
|
+
export const tagRuleLines = tagCheckLines
|
|
21
|
+
|
|
22
|
+
// ── the legacy id namespace ──────────────────────────────────────────────
|
|
23
|
+
// A rule id is PREFIX plus exactly 3 digits, and `SK` is reserved for the
|
|
24
|
+
// toolkit's own checks so a repo rule can never shadow one. A `*.check.ts`
|
|
25
|
+
// file takes its id from its NAME and may use plain words, so this shape
|
|
26
|
+
// binds the `rules: []` key only.
|
|
90
27
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
28
|
+
export const RULE_ID_RE = /^[A-Z]{2,4}\d{3}$/
|
|
29
|
+
|
|
30
|
+
/** validate the rules a repo registers. Throws on a malformed id, a
|
|
31
|
+
* duplicate, a missing owner page, or one squatting the reserved SK
|
|
32
|
+
* prefix, so a bad registration dies at config load and never mid gate. */
|
|
33
|
+
export function validateRules(rules: readonly Rule[], o: { label: string; taken?: Iterable<string> } = { label: 'rules' }): void {
|
|
34
|
+
const seen = new Set<string>(o.taken ?? [])
|
|
35
|
+
for (const r of rules) {
|
|
36
|
+
if (!RULE_ID_RE.test(r.id)) throw new Error(`${o.label}: bad id '${r.id}' — rules are PREFIX+3 digits (e.g. SK001, RV001)`)
|
|
37
|
+
if (r.id.startsWith('SK'))
|
|
38
|
+
throw new Error(`${o.label}: id '${r.id}' uses the reserved SK prefix — repo rules pick their own (e.g. RV${r.id.slice(-3)})`)
|
|
39
|
+
if (seen.has(r.id)) throw new Error(`${o.label}: duplicate id ${r.id}`)
|
|
40
|
+
if (r.owner === '') throw new Error(`${o.label}: ${r.id} has no owner page`)
|
|
41
|
+
seen.add(r.id)
|
|
42
|
+
}
|
|
103
43
|
}
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
// it could surprise someone.
|
|
24
24
|
import { existsSync } from 'fs'
|
|
25
25
|
import { join } from 'path'
|
|
26
|
+
import { gitBin } from '../core/git-bin.ts'
|
|
26
27
|
|
|
27
|
-
/** rendered into the SK004 managed block
|
|
28
|
-
* not destructive. */
|
|
28
|
+
/** rendered into the SK004 managed block, by basename so they match wherever
|
|
29
|
+
* they sit. A glob is fine HERE: ignoring is not destructive. */
|
|
29
30
|
export const GENERATED_IGNORE_LINES = [
|
|
30
31
|
'# shipkit writes these on every run — ignored AND untracked (untrack-generated.ts)',
|
|
31
32
|
'STATUS.md',
|
|
@@ -40,7 +41,7 @@ export const GENERATED_IGNORE_LINES = [
|
|
|
40
41
|
* never derived from a pattern: `git rm --cached` driven by a wildcard is how
|
|
41
42
|
* a fixer eats source it was never pointed at. A future entry is added HERE,
|
|
42
43
|
* by hand, spelled out. */
|
|
43
|
-
export const GENERATED_TRACKED_PATHS = ['STATUS.md', 'STATUS.md.lock', 'STATUS.full.md', 'STATUS.full.md.lock'] as const
|
|
44
|
+
export const GENERATED_TRACKED_PATHS = ['.shipkit/STATUS.md', '.shipkit/STATUS.md.lock', '.shipkit/STATUS.full.md', '.shipkit/STATUS.full.md.lock'] as const
|
|
44
45
|
|
|
45
46
|
export type UntrackOutcome =
|
|
46
47
|
| { did: 'nothing' }
|
|
@@ -48,7 +49,7 @@ export type UntrackOutcome =
|
|
|
48
49
|
| { did: 'untracked'; paths: string[]; commit: string }
|
|
49
50
|
|
|
50
51
|
const git = (root: string, args: string[]): { code: number; out: string } => {
|
|
51
|
-
const p = Bun.spawnSync([
|
|
52
|
+
const p = Bun.spawnSync([gitBin(), ...args], { cwd: root })
|
|
52
53
|
return { code: p.exitCode, out: `${p.stdout.toString()}${p.stderr.toString()}`.trim() }
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -1,45 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
-
// one
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// the hub is the one process every wired repo starts, so it opens with the
|
|
3
|
+
// folder itself. It runs every check after each save and its problem
|
|
4
|
+
// matcher fills the problems pane, so there is no type checker task. A
|
|
5
|
+
// stack that earns more long-running watchers (dev server, css) adds a
|
|
6
|
+
// master task with runOn folderOpen whose dependsOn lists them all, the hub
|
|
7
|
+
// included, and moves runOn off the hub.
|
|
6
8
|
"version": "2.0.0",
|
|
7
9
|
"tasks": [
|
|
8
|
-
{
|
|
9
|
-
"label": "starts everything",
|
|
10
|
-
"dependsOn": ["👁 TSC", "🚀 shipkit"],
|
|
11
|
-
"group": { "kind": "build", "isDefault": true },
|
|
12
|
-
"runOptions": { "runOn": "folderOpen", "instanceLimit": 1 },
|
|
13
|
-
"problemMatcher": []
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"label": "👁 TSC",
|
|
17
|
-
"detail": "Get all typescript errors in problems pane",
|
|
18
|
-
"type": "process",
|
|
19
|
-
"command": "${workspaceFolder}/node_modules/.bin/tsc",
|
|
20
|
-
"args": ["--noEmit", "--incremental", "--watch", "--project", "tsconfig.json"],
|
|
21
|
-
"options": {
|
|
22
|
-
"cwd": "${workspaceFolder}",
|
|
23
|
-
"env": { "NODE_OPTIONS": "--max-old-space-size=8192" }
|
|
24
|
-
},
|
|
25
|
-
"isBackground": true,
|
|
26
|
-
"presentation": { "reveal": "never", "clear": true },
|
|
27
|
-
"group": "build",
|
|
28
|
-
"runOptions": { "instanceLimit": 1 },
|
|
29
|
-
"problemMatcher": []
|
|
30
|
-
},
|
|
31
10
|
{
|
|
32
11
|
"label": "🚀 shipkit",
|
|
33
|
-
"detail": "The shipkit hub at http://localhost:{{PORT}} — clickable URLs: the page, /graph/wiki, /graph/agent",
|
|
12
|
+
"detail": "The shipkit hub at http://localhost:{{PORT}}, and every check's findings in the problems pane — clickable URLs: the page, /graph/wiki, /graph/agent",
|
|
34
13
|
"type": "process",
|
|
35
14
|
"command": "bun",
|
|
36
15
|
"args": ["run", "hub"],
|
|
37
16
|
"options": { "cwd": "${workspaceFolder}" },
|
|
38
17
|
"isBackground": true,
|
|
39
18
|
"presentation": { "reveal": "always", "close": false },
|
|
40
|
-
"group": "build",
|
|
41
|
-
"runOptions": { "instanceLimit": 1 },
|
|
42
|
-
"problemMatcher":
|
|
19
|
+
"group": { "kind": "build", "isDefault": true },
|
|
20
|
+
"runOptions": { "runOn": "folderOpen", "instanceLimit": 1 },
|
|
21
|
+
"problemMatcher": {{HUB_PROBLEM_MATCHER}}
|
|
43
22
|
}
|
|
44
23
|
]
|
|
45
24
|
}
|