@bensandee/tooling 0.64.1 → 0.65.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/README.md +22 -0
- package/dist/bin.mjs +7026 -3939
- package/dist/docker-check/index.d.mts +23 -1
- package/dist/docker-check/index.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/{project-name-CHPvgMTP.mjs → project-name-CnyTIy5a.mjs} +102 -59
- package/dist/{types-CMKT2loB.d.mts → types-C6cLSf_a.d.mts} +16 -0
- package/package.json +6 -6
- package/tooling.schema.json +84 -0
|
@@ -109,7 +109,7 @@ declare function createRealExecutor(): DockerCheckExecutor;
|
|
|
109
109
|
//#endregion
|
|
110
110
|
//#region src/docker-check/check.d.ts
|
|
111
111
|
/** Run the full Docker check lifecycle. */
|
|
112
|
-
declare function runDockerCheck(executor: DockerCheckExecutor,
|
|
112
|
+
declare function runDockerCheck(executor: DockerCheckExecutor, rawConfig: CheckConfig): Promise<CheckResult>;
|
|
113
113
|
//#endregion
|
|
114
114
|
//#region src/docker-check/compose.d.ts
|
|
115
115
|
/** Zod schema for a single container entry from `docker compose ps --format json`. */
|
|
@@ -242,6 +242,12 @@ declare const ToolingConfigSchema: z.ZodObject<{
|
|
|
242
242
|
historyBranch: z.ZodOptional<z.ZodString>;
|
|
243
243
|
tolerance: z.ZodOptional<z.ZodNumber>;
|
|
244
244
|
}, z.core.$strip>>;
|
|
245
|
+
docs: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
246
|
+
enforced: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
|
|
247
|
+
dirs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
248
|
+
staleDays: z.ZodOptional<z.ZodNumber>;
|
|
249
|
+
staleEnforced: z.ZodOptional<z.ZodBoolean>;
|
|
250
|
+
}, z.core.$strict>]>>;
|
|
245
251
|
deploy: z.ZodOptional<z.ZodObject<{
|
|
246
252
|
dispatchRepo: z.ZodString;
|
|
247
253
|
stack: z.ZodOptional<z.ZodString>;
|
|
@@ -250,6 +256,22 @@ declare const ToolingConfigSchema: z.ZodObject<{
|
|
|
250
256
|
}, z.core.$strict>>;
|
|
251
257
|
pnpmWorkspace: z.ZodOptional<z.ZodLiteral<false>>;
|
|
252
258
|
defaultBranch: z.ZodOptional<z.ZodString>;
|
|
259
|
+
unmanagedDevDependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
260
|
+
stack: z.ZodOptional<z.ZodEnum<{
|
|
261
|
+
none: "none";
|
|
262
|
+
typescript: "typescript";
|
|
263
|
+
javascript: "javascript";
|
|
264
|
+
}>>;
|
|
265
|
+
builder: z.ZodOptional<z.ZodEnum<{
|
|
266
|
+
none: "none";
|
|
267
|
+
custom: "custom";
|
|
268
|
+
tsdown: "tsdown";
|
|
269
|
+
vite: "vite";
|
|
270
|
+
}>>;
|
|
271
|
+
checks: z.ZodOptional<z.ZodObject<{
|
|
272
|
+
add: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
273
|
+
skip: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
274
|
+
}, z.core.$strict>>;
|
|
253
275
|
}, z.core.$strict>;
|
|
254
276
|
type ToolingConfig = z.infer<typeof ToolingConfigSchema>;
|
|
255
277
|
//#endregion
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as composeArgs, c as composePs,
|
|
1
|
+
import { a as composeArgs, c as composePs, d as lintComposeFiles, i as getContainerHealth, l as composeUp, n as runDockerCheck, o as composeDown, r as checkHttpHealth, s as composeLogs, t as deriveProjectName, u as createRealExecutor } from "../project-name-CnyTIy5a.mjs";
|
|
2
2
|
export { checkHttpHealth, composeArgs, composeDown, composeLogs, composePs, composeUp, createRealExecutor, deriveProjectName, getContainerHealth, lintComposeFiles, runDockerCheck };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as LegacyConfig, i as GeneratorResult, n as Generator, o as ProjectConfig, r as GeneratorContext, t as DetectedProjectState } from "./types-
|
|
1
|
+
import { a as LegacyConfig, i as GeneratorResult, n as Generator, o as ProjectConfig, r as GeneratorContext, t as DetectedProjectState } from "./types-C6cLSf_a.mjs";
|
|
2
2
|
export type { DetectedProjectState, Generator, GeneratorContext, GeneratorResult, LegacyConfig, ProjectConfig };
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
import * as clack from "@clack/prompts";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { parse } from "yaml";
|
|
7
|
-
//#region src/utils/log.ts
|
|
8
|
-
const out = (msg) => console.log(msg);
|
|
9
|
-
const isCI = Boolean(process.env["CI"]);
|
|
10
|
-
const log = isCI ? {
|
|
11
|
-
info: out,
|
|
12
|
-
warn: (msg) => out(`[warn] ${msg}`),
|
|
13
|
-
error: (msg) => out(`[error] ${msg}`),
|
|
14
|
-
success: (msg) => out(`✓ ${msg}`)
|
|
15
|
-
} : clack.log;
|
|
16
|
-
function note(body, title) {
|
|
17
|
-
if (isCI) {
|
|
18
|
-
if (title) out(`--- ${title} ---`);
|
|
19
|
-
out(body);
|
|
20
|
-
} else clack.note(body, title);
|
|
21
|
-
}
|
|
22
|
-
//#endregion
|
|
23
6
|
//#region src/utils/debug.ts
|
|
24
7
|
const REDACT_PATTERNS = [/x-access-token:[^\s@]+/g, /Authorization:\s*(?:token|Bearer)\s+\S+/gi];
|
|
8
|
+
const debugOut = (msg) => console.error(msg);
|
|
25
9
|
/** Redact secrets (tokens, credentials) from text before logging. */
|
|
26
10
|
function redact(text) {
|
|
27
11
|
let result = text;
|
|
@@ -34,7 +18,7 @@ function isEnvVerbose() {
|
|
|
34
18
|
}
|
|
35
19
|
/** Log a debug message when verbose mode is enabled. */
|
|
36
20
|
function debug(config, message) {
|
|
37
|
-
if (config.verbose)
|
|
21
|
+
if (config.verbose) debugOut(`[debug] ${message}`);
|
|
38
22
|
}
|
|
39
23
|
/** Log the result of an exec call when verbose mode is enabled. */
|
|
40
24
|
function debugExec(config, label, result) {
|
|
@@ -42,7 +26,7 @@ function debugExec(config, label, result) {
|
|
|
42
26
|
const lines = [`[debug] ${redact(label)} (exit code ${String(result.exitCode)})`];
|
|
43
27
|
if (result.stdout.trim()) lines.push(` stdout: ${redact(result.stdout.trim())}`);
|
|
44
28
|
if (result.stderr.trim()) lines.push(` stderr: ${redact(result.stderr.trim())}`);
|
|
45
|
-
|
|
29
|
+
debugOut(lines.join("\n"));
|
|
46
30
|
}
|
|
47
31
|
//#endregion
|
|
48
32
|
//#region src/docker-check/compose-lint.ts
|
|
@@ -54,7 +38,7 @@ const ComposeFileShape = z.looseObject({ name: z.unknown().optional() });
|
|
|
54
38
|
*/
|
|
55
39
|
const COMPOSE_DIR_CANDIDATES = [".", "docker"];
|
|
56
40
|
/** Base compose file names whose project namespace defaults to dir basename. */
|
|
57
|
-
const BASE_COMPOSE_FILES = new Set([
|
|
41
|
+
const BASE_COMPOSE_FILES = /* @__PURE__ */ new Set([
|
|
58
42
|
"docker-compose.yml",
|
|
59
43
|
"docker-compose.yaml",
|
|
60
44
|
"compose.yml",
|
|
@@ -187,47 +171,29 @@ function readTopLevelName(absPath) {
|
|
|
187
171
|
return value;
|
|
188
172
|
}
|
|
189
173
|
//#endregion
|
|
190
|
-
//#region src/utils/exec.ts
|
|
191
|
-
/** Type guard for `execSync` errors that carry stdout/stderr/status. */
|
|
192
|
-
function isExecSyncError(err) {
|
|
193
|
-
return err instanceof Error && "stdout" in err && typeof err.stdout === "string" && "stderr" in err && typeof err.stderr === "string" && "status" in err && typeof err.status === "number";
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
196
174
|
//#region src/docker-check/executor.ts
|
|
197
175
|
/** Create an executor that runs real commands, fetches, and manages process signals. */
|
|
198
176
|
function createRealExecutor() {
|
|
199
177
|
return {
|
|
200
178
|
exec(file, args, options) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
} catch (err) {
|
|
220
|
-
if (isExecSyncError(err)) return {
|
|
221
|
-
stdout: err.stdout,
|
|
222
|
-
stderr: err.stderr,
|
|
223
|
-
exitCode: err.status
|
|
224
|
-
};
|
|
225
|
-
return {
|
|
226
|
-
stdout: "",
|
|
227
|
-
stderr: "",
|
|
228
|
-
exitCode: 1
|
|
229
|
-
};
|
|
230
|
-
}
|
|
179
|
+
const result = spawnSync(file, args, {
|
|
180
|
+
cwd: options?.cwd,
|
|
181
|
+
env: options?.env ? {
|
|
182
|
+
...process.env,
|
|
183
|
+
...options.env
|
|
184
|
+
} : void 0,
|
|
185
|
+
encoding: "utf-8",
|
|
186
|
+
stdio: [
|
|
187
|
+
"pipe",
|
|
188
|
+
"pipe",
|
|
189
|
+
"pipe"
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
stdout: result.stdout ?? "",
|
|
194
|
+
stderr: result.stderr ?? "",
|
|
195
|
+
exitCode: result.status ?? 1
|
|
196
|
+
};
|
|
231
197
|
},
|
|
232
198
|
execInherit(file, args, options) {
|
|
233
199
|
execFileSync(file, args, {
|
|
@@ -270,6 +236,55 @@ function composeArgs(config) {
|
|
|
270
236
|
if (config.envFile) args.push("--env-file", config.envFile);
|
|
271
237
|
return args;
|
|
272
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Compose's unset-interpolation warning. The variable name is quoted, and the
|
|
241
|
+
* quotes arrive backslash-escaped when stderr is a pipe (which it always is
|
|
242
|
+
* here) but bare when it is a TTY — so both forms are accepted.
|
|
243
|
+
*/
|
|
244
|
+
const UNSET_VARIABLE_WARNING = /The\s+\\?"([^"\\]+)\\?"\s+variable is not set/g;
|
|
245
|
+
/** Names of the interpolation variables compose warned were unset, in order, de-duplicated. */
|
|
246
|
+
function parseUnsetVariables(stderr) {
|
|
247
|
+
const names = /* @__PURE__ */ new Set();
|
|
248
|
+
for (const match of stderr.matchAll(UNSET_VARIABLE_WARNING)) {
|
|
249
|
+
const name = match[1];
|
|
250
|
+
if (name) names.add(name);
|
|
251
|
+
}
|
|
252
|
+
return [...names];
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Ask compose which interpolation variables are unset, and map each to an empty
|
|
256
|
+
* string so it can be injected into the child env of every later compose call.
|
|
257
|
+
*
|
|
258
|
+
* Compose warns `The "FOO" variable is not set` once per file per invocation —
|
|
259
|
+
* including on `down` — for a `${FOO}` with no default. Nothing expressible in
|
|
260
|
+
* the check overlay silences that: interpolation happens per-file, before the
|
|
261
|
+
* merge. Setting the variable does silence it, and setting it to the *empty
|
|
262
|
+
* string* is what compose already substitutes, so the value each container sees
|
|
263
|
+
* is unchanged. A non-empty placeholder would not be: a service that dials
|
|
264
|
+
* `${LYRION_HOST}` would start resolving a hostname where today it gets "".
|
|
265
|
+
*
|
|
266
|
+
* Detection is delegated to compose rather than re-implementing its grammar:
|
|
267
|
+
* `config` has already consulted `--env-file` and the process env, so it reports
|
|
268
|
+
* exactly the variables that still need filling, in every position (`image:`,
|
|
269
|
+
* `ports:`, `volumes:`, …) and every form, with `$$` escapes and `${FOO:-def}`
|
|
270
|
+
* defaults already accounted for. If the wording ever changes we fill nothing
|
|
271
|
+
* and the warnings simply come back — a `${FOO:?required}` error is never masked.
|
|
272
|
+
*
|
|
273
|
+
* Variables already present in `config.env` are left alone: the free-port logic
|
|
274
|
+
* owns PORT, and an empty string there would unbind the published port.
|
|
275
|
+
*/
|
|
276
|
+
function fillUnsetVariables(executor, config) {
|
|
277
|
+
const result = executor.exec("docker", [...composeArgs(config), "config"], {
|
|
278
|
+
cwd: config.cwd,
|
|
279
|
+
env: config.env
|
|
280
|
+
});
|
|
281
|
+
const filled = {};
|
|
282
|
+
for (const name of parseUnsetVariables(result.stderr)) {
|
|
283
|
+
if (config.env && name in config.env) continue;
|
|
284
|
+
filled[name] = "";
|
|
285
|
+
}
|
|
286
|
+
return filled;
|
|
287
|
+
}
|
|
273
288
|
/** Run the build command if configured. */
|
|
274
289
|
function buildImages(executor, config) {
|
|
275
290
|
if (!config.buildCommand) return;
|
|
@@ -372,6 +387,33 @@ async function checkHttpHealth(executor, check) {
|
|
|
372
387
|
//#region src/docker-check/check.ts
|
|
373
388
|
const DEFAULT_TIMEOUT_MS = 12e4;
|
|
374
389
|
const DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
390
|
+
/**
|
|
391
|
+
* Fill any compose interpolation variable that is still unset with an empty
|
|
392
|
+
* string, so `${FOO}` with no default stops warning on every compose call —
|
|
393
|
+
* `up` and, after the check has already passed, teardown. See
|
|
394
|
+
* `fillUnsetVariables` for why empty (not a placeholder) and why compose itself
|
|
395
|
+
* decides which variables need it.
|
|
396
|
+
*
|
|
397
|
+
* This runs here rather than in the `docker:check` command so the env reaches
|
|
398
|
+
* every compose invocation *and* the smoketest through one `CheckConfig`, and so
|
|
399
|
+
* programmatic consumers of the `docker-check` export get it too.
|
|
400
|
+
*/
|
|
401
|
+
function withFilledVariables(executor, config) {
|
|
402
|
+
const filled = fillUnsetVariables(executor, config.compose);
|
|
403
|
+
const names = Object.keys(filled);
|
|
404
|
+
if (names.length === 0) return config;
|
|
405
|
+
executor.log(`Unset compose variables set to an empty string: ${names.join(", ")}. To supply real values, put them in ${config.compose.envFile ?? "docker-compose.check.env"} beside the compose file.`);
|
|
406
|
+
return {
|
|
407
|
+
...config,
|
|
408
|
+
compose: {
|
|
409
|
+
...config.compose,
|
|
410
|
+
env: {
|
|
411
|
+
...config.compose.env,
|
|
412
|
+
...filled
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
375
417
|
/** Probe any not-yet-healthy HTTP endpoints, flipping them to healthy in `healthStatus`. */
|
|
376
418
|
async function updateHttpHealth(executor, healthChecks, healthStatus) {
|
|
377
419
|
for (const check of healthChecks) {
|
|
@@ -409,7 +451,8 @@ function runSmoketestStep(executor, config, cleanup, startTime) {
|
|
|
409
451
|
}
|
|
410
452
|
}
|
|
411
453
|
/** Run the full Docker check lifecycle. */
|
|
412
|
-
async function runDockerCheck(executor,
|
|
454
|
+
async function runDockerCheck(executor, rawConfig) {
|
|
455
|
+
const config = withFilledVariables(executor, rawConfig);
|
|
413
456
|
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
414
457
|
const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
415
458
|
const { compose } = config;
|
|
@@ -540,4 +583,4 @@ function sanitize(name) {
|
|
|
540
583
|
return name.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
541
584
|
}
|
|
542
585
|
//#endregion
|
|
543
|
-
export {
|
|
586
|
+
export { composeArgs as a, composePs as c, lintComposeFiles as d, debug as f, redact as h, getContainerHealth as i, composeUp as l, isEnvVerbose as m, runDockerCheck as n, composeDown as o, debugExec as p, checkHttpHealth as r, composeLogs as s, deriveProjectName as t, createRealExecutor as u };
|
|
@@ -9,6 +9,7 @@ declare const PackageJsonSchema: z.ZodObject<{
|
|
|
9
9
|
scripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
10
10
|
dependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
11
11
|
devDependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
12
|
+
peerDependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
12
13
|
bin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
13
14
|
exports: z.ZodOptional<z.ZodUnknown>;
|
|
14
15
|
main: z.ZodOptional<z.ZodString>;
|
|
@@ -37,6 +38,10 @@ type PackageJson = z.infer<typeof PackageJsonSchema>;
|
|
|
37
38
|
type CiPlatform = "github" | "forgejo" | "none";
|
|
38
39
|
type ReleaseStrategy = "release-it" | "simple" | "changesets" | "none";
|
|
39
40
|
type Formatter = "oxfmt" | "prettier";
|
|
41
|
+
/** Whether the repo has TS/JS source at all. Never inferred from absence. */
|
|
42
|
+
type Stack = "typescript" | "javascript" | "none";
|
|
43
|
+
/** What compiles the project, if anything. */
|
|
44
|
+
type Builder = "tsdown" | "vite" | "custom" | "none";
|
|
40
45
|
/** User's answers from the interactive prompt or CLI flags. */
|
|
41
46
|
interface ProjectConfig {
|
|
42
47
|
/** Project name (from package.json name or user input) */
|
|
@@ -47,6 +52,10 @@ interface ProjectConfig {
|
|
|
47
52
|
useEslintPlugin: boolean;
|
|
48
53
|
/** Formatter choice */
|
|
49
54
|
formatter: Formatter;
|
|
55
|
+
/** Whether the repo has TS/JS source at all */
|
|
56
|
+
stack: Stack;
|
|
57
|
+
/** What compiles the project */
|
|
58
|
+
builder: Builder;
|
|
50
59
|
/** Set up vitest with a starter test */
|
|
51
60
|
setupVitest: boolean;
|
|
52
61
|
/** CI platform choice */
|
|
@@ -88,6 +97,13 @@ interface GeneratorContext {
|
|
|
88
97
|
write: (relativePath: string, content: string) => void;
|
|
89
98
|
/** Remove a file from the target directory (no-op if not found) */
|
|
90
99
|
remove: (relativePath: string) => void;
|
|
100
|
+
/**
|
|
101
|
+
* Rewrite generated content the way the target repo's own formatter would — so a
|
|
102
|
+
* generated file is a fixed point of `pnpm format` and can't drift against
|
|
103
|
+
* `repo:sync --check`. Returns the content unchanged when the formatter isn't
|
|
104
|
+
* installed yet. See `utils/format.ts`.
|
|
105
|
+
*/
|
|
106
|
+
format: (relativePath: string, content: string) => string;
|
|
91
107
|
/** Prompt user for conflict resolution on non-mergeable files */
|
|
92
108
|
confirmOverwrite: (relativePath: string) => Promise<"overwrite" | "skip">;
|
|
93
109
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"zod": "^4.4.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@bensandee/config": "0.
|
|
50
|
+
"@bensandee/config": "0.13.1",
|
|
51
51
|
"@changesets/cli": "2.31.0",
|
|
52
52
|
"@release-it/bumper": "7.0.6",
|
|
53
|
-
"@types/node": "24.13.
|
|
53
|
+
"@types/node": "24.13.2",
|
|
54
54
|
"@types/picomatch": "4.0.3",
|
|
55
55
|
"commit-and-tag-version": "12.7.3",
|
|
56
|
-
"prettier": "3.8.
|
|
56
|
+
"prettier": "3.8.4",
|
|
57
57
|
"release-it": "20.2.0",
|
|
58
|
-
"tsdown": "0.22.
|
|
59
|
-
"@bensandee/claude-hooks": "0.
|
|
58
|
+
"tsdown": "0.22.3",
|
|
59
|
+
"@bensandee/claude-hooks": "0.9.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsdown",
|
package/tooling.schema.json
CHANGED
|
@@ -314,6 +314,52 @@
|
|
|
314
314
|
},
|
|
315
315
|
"additionalProperties": false
|
|
316
316
|
},
|
|
317
|
+
"docs": {
|
|
318
|
+
"description": "Plan/spec lifecycle gate (`bst docs:check`). Overrides only — dirs are discovered by convention. Set to false to disable. Violations are advisory until a pair is named in `enforced`.",
|
|
319
|
+
"anyOf": [
|
|
320
|
+
{
|
|
321
|
+
"type": "boolean",
|
|
322
|
+
"const": false
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"type": "object",
|
|
326
|
+
"properties": {
|
|
327
|
+
"enforced": {
|
|
328
|
+
"description": "Which discovered pairs fail the build. true = all; an array names active dirs (e.g. [\"docs/plans\"]); default false = every violation is advisory.",
|
|
329
|
+
"anyOf": [
|
|
330
|
+
{
|
|
331
|
+
"type": "boolean"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"type": "array",
|
|
335
|
+
"items": {
|
|
336
|
+
"type": "string"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"dirs": {
|
|
342
|
+
"description": "Extra active doc dirs beyond convention (e.g. [\"docs/rfcs\"]). Each retires into its own completed/ subdir.",
|
|
343
|
+
"type": "array",
|
|
344
|
+
"items": {
|
|
345
|
+
"type": "string"
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"staleDays": {
|
|
349
|
+
"description": "Days before an untouched active doc is flagged (default 30; 0 disables the rule).",
|
|
350
|
+
"type": "integer",
|
|
351
|
+
"minimum": 0,
|
|
352
|
+
"maximum": 9007199254740991
|
|
353
|
+
},
|
|
354
|
+
"staleEnforced": {
|
|
355
|
+
"description": "Make the staleness rule fatal inside an enforced pair (default false: staleness is always advisory).",
|
|
356
|
+
"type": "boolean"
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
"additionalProperties": false
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
},
|
|
317
363
|
"deploy": {
|
|
318
364
|
"description": "Generate a deploy-dispatch step in the release workflow that triggers a workflow_dispatch event in another repo after a successful release. The resolved release token must have rights on `dispatchRepo`.",
|
|
319
365
|
"type": "object",
|
|
@@ -346,6 +392,44 @@
|
|
|
346
392
|
"defaultBranch": {
|
|
347
393
|
"description": "Repository default branch name. Used by `checks:run` to decide when to enable coverage automatically. Default: detected via git (origin/HEAD), falling back to 'main'.",
|
|
348
394
|
"type": "string"
|
|
395
|
+
},
|
|
396
|
+
"unmanagedDevDependencies": {
|
|
397
|
+
"description": "devDependency names repo:sync must never add or re-pin. Escape hatch for holding a tooling-managed dep (oxlint, vitest, knip, ...) at a deliberately off-canonical version; without it, repo:sync raises any managed devDep whose version has drifted below the canonical pin.",
|
|
398
|
+
"type": "array",
|
|
399
|
+
"items": {
|
|
400
|
+
"type": "string"
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
"stack": {
|
|
404
|
+
"description": "Whether the repo has TS/JS source. Detected from source evidence; TypeScript is never inferred from absence.",
|
|
405
|
+
"type": "string",
|
|
406
|
+
"enum": ["typescript", "javascript", "none"]
|
|
407
|
+
},
|
|
408
|
+
"builder": {
|
|
409
|
+
"description": "What compiles the project. Detected from the package.json build script, then config-file presence.",
|
|
410
|
+
"type": "string",
|
|
411
|
+
"enum": ["tsdown", "vite", "custom", "none"]
|
|
412
|
+
},
|
|
413
|
+
"checks": {
|
|
414
|
+
"description": "Check-pipeline overrides: extra steps to add, and steps to skip (unioned with CI defaults).",
|
|
415
|
+
"type": "object",
|
|
416
|
+
"properties": {
|
|
417
|
+
"add": {
|
|
418
|
+
"description": "Extra package.json scripts to run as first-class check steps, appended to the built-in list (e.g. ['lint:php']). An added step with no matching script is a hard failure.",
|
|
419
|
+
"type": "array",
|
|
420
|
+
"items": {
|
|
421
|
+
"type": "string"
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
"skip": {
|
|
425
|
+
"description": "Check steps to skip, unioned with the built-in CI defaults (never replacing them). Supports glob patterns.",
|
|
426
|
+
"type": "array",
|
|
427
|
+
"items": {
|
|
428
|
+
"type": "string"
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"additionalProperties": false
|
|
349
433
|
}
|
|
350
434
|
},
|
|
351
435
|
"additionalProperties": false
|