devcontour-harness 0.1.0-alpha.5 → 0.1.0-alpha.7
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 +42 -2
- package/docs/compatibility-versions.md +56 -0
- package/docs/compatibility.md +2 -2
- package/docs/connectors.md +323 -0
- package/docs/contracts.md +33 -5
- package/docs/integrations/hindsight.md +148 -0
- package/docs/quickstart.md +1 -1
- package/docs/release-notes/0.1.0-alpha.6.md +238 -0
- package/docs/release-notes/0.1.0-alpha.7.md +71 -0
- package/docs/review-probes.md +199 -0
- package/docs/review-protocol.md +251 -0
- package/docs/toolchains.md +167 -0
- package/docs/turbo-starter.md +269 -0
- package/docs/upgrade.md +77 -24
- package/examples/README.md +1 -0
- package/examples/toolchains/README.md +32 -0
- package/examples/toolchains/go/bad/label/label.go +9 -0
- package/examples/toolchains/go/good/label/label.go +9 -0
- package/examples/toolchains/go/policy.json +79 -0
- package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/go/repo/_gitignore +1 -0
- package/examples/toolchains/go/repo/go.mod +5 -0
- package/examples/toolchains/go/repo/go.sum +2 -0
- package/examples/toolchains/go/repo/label/label.go +9 -0
- package/examples/toolchains/go/repo/label/label_test.go +27 -0
- package/examples/toolchains/go/repo/label/version.go +9 -0
- package/examples/toolchains/go/task.json +25 -0
- package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/policy.json +126 -0
- package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
- package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-go/task.json +25 -0
- package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/policy.json +121 -0
- package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
- package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
- package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
- package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
- package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-python/task.json +25 -0
- package/examples/toolchains/npm/bad/src/label.js +4 -0
- package/examples/toolchains/npm/good/src/label.js +4 -0
- package/examples/toolchains/npm/policy.json +82 -0
- package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/npm/repo/_gitignore +4 -0
- package/examples/toolchains/npm/repo/package-lock.json +22 -0
- package/examples/toolchains/npm/repo/package.json +13 -0
- package/examples/toolchains/npm/repo/src/label.js +4 -0
- package/examples/toolchains/npm/repo/test/label.test.js +13 -0
- package/examples/toolchains/npm/task.json +25 -0
- package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/policy.json +85 -0
- package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/pnpm/repo/_gitignore +4 -0
- package/examples/toolchains/pnpm/repo/package.json +10 -0
- package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
- package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
- package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/pnpm/task.json +25 -0
- package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/policy.json +80 -0
- package/examples/toolchains/python/repo/.python-version +1 -0
- package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/python/repo/_gitignore +5 -0
- package/examples/toolchains/python/repo/pyproject.toml +19 -0
- package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/repo/tests/test_label.py +13 -0
- package/examples/toolchains/python/repo/uv.lock +108 -0
- package/examples/toolchains/python/task.json +25 -0
- package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/policy.json +110 -0
- package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/turbo/repo/_gitignore +4 -0
- package/examples/toolchains/turbo/repo/package.json +13 -0
- package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
- package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
- package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/turbo/repo/turbo.json +7 -0
- package/examples/toolchains/turbo/task.json +25 -0
- package/examples/turbo-starter/README.md +16 -0
- package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
- package/examples/turbo-starter/changes/issued-at.patch +130 -0
- package/examples/turbo-starter/cleanup.mjs +58 -0
- package/examples/turbo-starter/graph.json +32 -0
- package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
- package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
- package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
- package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
- package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
- package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
- package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
- package/examples/turbo-starter/harness/env/backend.env.template +7 -0
- package/examples/turbo-starter/harness/env/compose.env.template +6 -0
- package/examples/turbo-starter/policy.json +109 -0
- package/examples/turbo-starter/policy.lifecycle.json +114 -0
- package/examples/turbo-starter/setup.mjs +352 -0
- package/lib/agents/cli-adapter.js +10 -1
- package/lib/agents/inspect.d.ts +56 -2
- package/lib/agents/inspect.js +264 -14
- package/lib/agents/review-probe.d.ts +229 -7
- package/lib/agents/review-probe.js +830 -72
- package/lib/agents/schemas.d.ts +3 -1
- package/lib/agents/schemas.js +7 -3
- package/lib/cli.js +167 -7
- package/lib/contracts/cli.d.ts +820 -11
- package/lib/contracts/cli.js +243 -4
- package/lib/contracts/errors.d.ts +3 -1
- package/lib/contracts/errors.js +2 -0
- package/lib/contracts/events.d.ts +37 -5
- package/lib/contracts/events.js +9 -0
- package/lib/contracts/execution.d.ts +389 -15
- package/lib/contracts/execution.js +76 -4
- package/lib/contracts/failure.js +5 -0
- package/lib/contracts/index.d.ts +1 -0
- package/lib/contracts/index.js +1 -0
- package/lib/contracts/policy.d.ts +15 -0
- package/lib/contracts/policy.js +10 -0
- package/lib/contracts/ports.d.ts +50 -0
- package/lib/contracts/profiles.d.ts +14 -0
- package/lib/contracts/profiles.js +4 -0
- package/lib/contracts/published.d.ts +279 -8
- package/lib/contracts/result.d.ts +171 -0
- package/lib/contracts/result.js +39 -1
- package/lib/contracts/review.d.ts +231 -0
- package/lib/contracts/review.js +326 -2
- package/lib/contracts/toolchain.d.ts +96 -0
- package/lib/contracts/toolchain.js +89 -0
- package/lib/control/doctor.d.ts +16 -1
- package/lib/control/doctor.js +45 -3
- package/lib/control/harness.d.ts +52 -6
- package/lib/control/harness.js +28 -1
- package/lib/control/recovery.d.ts +3 -1
- package/lib/control/review.js +23 -11
- package/lib/engine/budget.d.ts +3 -3
- package/lib/engine/classify.js +1 -0
- package/lib/engine/engine.d.ts +67 -17
- package/lib/engine/prompt.d.ts +29 -1
- package/lib/engine/prompt.js +57 -0
- package/lib/engine/property-recovery.js +2 -0
- package/lib/engine/result.js +52 -0
- package/lib/engine/reuse.js +6 -1
- package/lib/engine/runner.d.ts +25 -0
- package/lib/engine/runner.js +500 -123
- package/lib/environment/lifecycle.js +6 -0
- package/lib/environment/live-calls.js +3 -2
- package/lib/environment/os-tools.d.ts +16 -0
- package/lib/environment/os-tools.js +35 -0
- package/lib/environment/process.d.ts +8 -0
- package/lib/environment/process.js +13 -6
- package/lib/integrations/connector/commands.d.ts +358 -0
- package/lib/integrations/connector/commands.js +428 -0
- package/lib/integrations/connector/config.d.ts +215 -0
- package/lib/integrations/connector/config.js +214 -0
- package/lib/integrations/connector/delivery.d.ts +107 -0
- package/lib/integrations/connector/delivery.js +591 -0
- package/lib/integrations/connector/fake.d.ts +291 -0
- package/lib/integrations/connector/fake.js +786 -0
- package/lib/integrations/connector/history.d.ts +124 -0
- package/lib/integrations/connector/history.js +497 -0
- package/lib/integrations/connector/index.d.ts +16 -0
- package/lib/integrations/connector/index.js +16 -0
- package/lib/integrations/connector/redact.d.ts +19 -0
- package/lib/integrations/connector/redact.js +91 -0
- package/lib/integrations/connector/registry.d.ts +15 -0
- package/lib/integrations/connector/registry.js +43 -0
- package/lib/integrations/connector/snapshot.d.ts +131 -0
- package/lib/integrations/connector/snapshot.js +422 -0
- package/lib/integrations/connector/source.d.ts +37 -0
- package/lib/integrations/connector/source.js +73 -0
- package/lib/integrations/connector/steps.d.ts +71 -0
- package/lib/integrations/connector/steps.js +188 -0
- package/lib/integrations/connector/store.d.ts +140 -0
- package/lib/integrations/connector/store.js +277 -0
- package/lib/integrations/connector/summary.d.ts +38 -0
- package/lib/integrations/connector/summary.js +59 -0
- package/lib/integrations/connector/testing-worker.d.ts +1 -0
- package/lib/integrations/connector/testing-worker.js +81 -0
- package/lib/integrations/connector/testing.d.ts +205 -0
- package/lib/integrations/connector/testing.js +1615 -0
- package/lib/integrations/connector/transport.d.ts +240 -0
- package/lib/integrations/connector/transport.js +28 -0
- package/lib/integrations/memory/context.d.ts +120 -0
- package/lib/integrations/memory/context.js +239 -0
- package/lib/integrations/memory/index.d.ts +8 -0
- package/lib/integrations/memory/index.js +8 -0
- package/lib/integrations/memory/provider.d.ts +79 -0
- package/lib/integrations/memory/provider.js +22 -0
- package/lib/integrations/memory/sink.d.ts +56 -0
- package/lib/integrations/memory/sink.js +116 -0
- package/lib/integrations/memory/testing.d.ts +44 -0
- package/lib/integrations/memory/testing.js +61 -0
- package/lib/integrations/relay-v1/index.d.ts +122 -0
- package/lib/integrations/relay-v1/index.js +860 -0
- package/lib/integrations/relay-v2/index.d.ts +145 -0
- package/lib/integrations/relay-v2/index.js +752 -0
- package/lib/integrations/relay-v2/process.d.ts +32 -0
- package/lib/integrations/relay-v2/process.js +112 -0
- package/lib/integrations/relay-v2/protocol.d.ts +172 -0
- package/lib/integrations/relay-v2/protocol.js +142 -0
- package/lib/profiles/resolve.d.ts +5 -1
- package/lib/profiles/resolve.js +31 -0
- package/lib/storage/document.d.ts +116 -16
- package/lib/toolchains/index.d.ts +9 -0
- package/lib/toolchains/index.js +9 -0
- package/lib/toolchains/inspect.d.ts +105 -0
- package/lib/toolchains/inspect.js +831 -0
- package/lib/toolchains/manifests.d.ts +67 -0
- package/lib/toolchains/manifests.js +238 -0
- package/lib/toolchains/probe.d.ts +28 -0
- package/lib/toolchains/probe.js +54 -0
- package/lib/toolchains/recipes.d.ts +33 -0
- package/lib/toolchains/recipes.js +51 -0
- package/lib/verification/acceptance.d.ts +16 -0
- package/lib/verification/acceptance.js +16 -0
- package/lib/verification/gates.d.ts +38 -0
- package/lib/verification/gates.js +108 -26
- package/package.json +22 -1
- package/profiles/go-gotestsum.json +21 -0
- package/profiles/npm-ci.json +22 -0
- package/profiles/pnpm-frozen.json +24 -0
- package/profiles/python-uv.json +26 -0
- package/schemas/execution-document.schema.json +345 -4
- package/schemas/execution-policy.schema.json +48 -0
- package/schemas/result-envelope.schema.json +213 -1
- package/schemas/run-event.schema.json +93 -0
- package/schemas/run-result.schema.json +207 -0
- package/schemas/stack-profile.schema.json +43 -0
- package/templates/controller-mode/AGENTS.controller.md +58 -0
- package/templates/controller-mode/README.md +44 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Lockfile → менеджер пакетов, которому он принадлежит. */
|
|
2
|
+
export declare const lockfiles: {
|
|
3
|
+
readonly 'package-lock.json': "npm";
|
|
4
|
+
readonly 'npm-shrinkwrap.json': "npm";
|
|
5
|
+
readonly 'pnpm-lock.yaml': "pnpm";
|
|
6
|
+
readonly 'yarn.lock': "yarn";
|
|
7
|
+
readonly 'bun.lock': "bun";
|
|
8
|
+
readonly 'bun.lockb': "bun";
|
|
9
|
+
};
|
|
10
|
+
export type LockManager = (typeof lockfiles)[keyof typeof lockfiles];
|
|
11
|
+
export declare class Manifests {
|
|
12
|
+
readonly root: string;
|
|
13
|
+
private cache;
|
|
14
|
+
constructor(root: string);
|
|
15
|
+
/** Путь `rel` относительно корня; `.` — корень. */
|
|
16
|
+
abs(rel: string): string;
|
|
17
|
+
/** Относительный путь каталога; вне корня — undefined. */
|
|
18
|
+
rel(abs: string): string | undefined;
|
|
19
|
+
/** Каталог внутри корня без symlink на пути. */
|
|
20
|
+
isDir(rel: string): boolean;
|
|
21
|
+
isFile(rel: string): boolean;
|
|
22
|
+
private stat;
|
|
23
|
+
/** Текст файла внутри корня (до 4 MiB) или undefined. */
|
|
24
|
+
text(rel: string): string | undefined;
|
|
25
|
+
json(rel: string): Record<string, unknown> | undefined;
|
|
26
|
+
/** Каталог и его предки до корня включительно, от ближнего. */
|
|
27
|
+
ancestors(dir: string): string[];
|
|
28
|
+
/** Ближайший к `dir` каталог (до корня), где есть файл `name`. */
|
|
29
|
+
nearest(dir: string, name: string): string | undefined;
|
|
30
|
+
lockfilesIn(dir: string): string[];
|
|
31
|
+
list(rel: string): string[];
|
|
32
|
+
}
|
|
33
|
+
export declare const join2: (dir: string, name: string) => string;
|
|
34
|
+
/** Скрипты package.json каталога. */
|
|
35
|
+
export declare function scriptsOf(m: Manifests, dir: string): Record<string, string> | undefined;
|
|
36
|
+
/** `packageManager` из package.json: `pnpm@10.12.3+sha512…` → { name, version }. */
|
|
37
|
+
export declare function packageManagerField(m: Manifests, dir: string): {
|
|
38
|
+
name: string;
|
|
39
|
+
version: string;
|
|
40
|
+
raw: string;
|
|
41
|
+
} | {
|
|
42
|
+
name: string;
|
|
43
|
+
raw: string;
|
|
44
|
+
version?: undefined;
|
|
45
|
+
} | undefined;
|
|
46
|
+
/** Пакеты workspace pnpm: `packages:` из pnpm-workspace.yaml (простые шаблоны `dir/*`). */
|
|
47
|
+
export declare function workspacePackages(m: Manifests, workspaceRoot: string): {
|
|
48
|
+
dir: string;
|
|
49
|
+
name: string | undefined;
|
|
50
|
+
scripts: Record<string, string>;
|
|
51
|
+
}[];
|
|
52
|
+
/** Задачи turbo.json (`tasks` v2 либо `pipeline` v1). */
|
|
53
|
+
export declare function turboTasks(m: Manifests, dir: string): string[] | undefined;
|
|
54
|
+
/** go.mod: директивы `go`, `toolchain` и наличие require. */
|
|
55
|
+
export declare function goModule(m: Manifests, dir: string): {
|
|
56
|
+
go: string | undefined;
|
|
57
|
+
toolchain: string | undefined;
|
|
58
|
+
requires: boolean;
|
|
59
|
+
} | undefined;
|
|
60
|
+
/** Пакеты, закреплённые uv.lock (`name = "…"` записей `[[package]]`). */
|
|
61
|
+
export declare function uvLocked(m: Manifests, dir: string): Map<string, string> | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Точная версия зависимости `name` корневого пакета каталога, закреплённая
|
|
64
|
+
* lockfile: `importers['.']` pnpm-lock.yaml либо `packages['node_modules/<name>']`
|
|
65
|
+
* package-lock.json. Нет закрепления — undefined.
|
|
66
|
+
*/
|
|
67
|
+
export declare function lockedVersion(m: Manifests, dir: string, name: string): string | undefined;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Чтение манифестов целевого репозитория — только чтение файлов внутри его
|
|
5
|
+
* корня, без symlink наружу и без исполнения чего-либо. Всё, что здесь
|
|
6
|
+
* разбирается (package.json, pnpm-workspace.yaml, turbo.json, go.mod,
|
|
7
|
+
* pyproject.toml, uv.lock), читается как данные.
|
|
8
|
+
*/
|
|
9
|
+
const FILE_LIMIT = 4 * 1024 * 1024;
|
|
10
|
+
/** Lockfile → менеджер пакетов, которому он принадлежит. */
|
|
11
|
+
export const lockfiles = {
|
|
12
|
+
'package-lock.json': 'npm',
|
|
13
|
+
'npm-shrinkwrap.json': 'npm',
|
|
14
|
+
'pnpm-lock.yaml': 'pnpm',
|
|
15
|
+
'yarn.lock': 'yarn',
|
|
16
|
+
'bun.lock': 'bun',
|
|
17
|
+
'bun.lockb': 'bun',
|
|
18
|
+
};
|
|
19
|
+
export class Manifests {
|
|
20
|
+
root;
|
|
21
|
+
cache = new Map();
|
|
22
|
+
constructor(root) {
|
|
23
|
+
this.root = realpathSync(root);
|
|
24
|
+
}
|
|
25
|
+
/** Путь `rel` относительно корня; `.` — корень. */
|
|
26
|
+
abs(rel) {
|
|
27
|
+
return rel === '.' ? this.root : join(this.root, rel);
|
|
28
|
+
}
|
|
29
|
+
/** Относительный путь каталога; вне корня — undefined. */
|
|
30
|
+
rel(abs) {
|
|
31
|
+
const r = relative(this.root, resolve(abs));
|
|
32
|
+
if (r === '')
|
|
33
|
+
return '.';
|
|
34
|
+
if (r === '..' || r.startsWith('..' + sep) || r.startsWith(sep))
|
|
35
|
+
return undefined;
|
|
36
|
+
return r.split(sep).join('/');
|
|
37
|
+
}
|
|
38
|
+
/** Каталог внутри корня без symlink на пути. */
|
|
39
|
+
isDir(rel) {
|
|
40
|
+
const stat = this.stat(rel);
|
|
41
|
+
return stat?.isDirectory() ?? false;
|
|
42
|
+
}
|
|
43
|
+
isFile(rel) {
|
|
44
|
+
const stat = this.stat(rel);
|
|
45
|
+
return stat?.isFile() ?? false;
|
|
46
|
+
}
|
|
47
|
+
stat(rel) {
|
|
48
|
+
if (rel === '.')
|
|
49
|
+
return lstatSync(this.root);
|
|
50
|
+
let cursor = this.root;
|
|
51
|
+
let stat;
|
|
52
|
+
for (const part of rel.split('/')) {
|
|
53
|
+
if (!part || part === '..')
|
|
54
|
+
return undefined;
|
|
55
|
+
if (part === '.')
|
|
56
|
+
continue;
|
|
57
|
+
cursor = join(cursor, part);
|
|
58
|
+
try {
|
|
59
|
+
stat = lstatSync(cursor);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (stat.isSymbolicLink())
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return stat;
|
|
68
|
+
}
|
|
69
|
+
/** Текст файла внутри корня (до 4 MiB) или undefined. */
|
|
70
|
+
text(rel) {
|
|
71
|
+
if (this.cache.has(rel))
|
|
72
|
+
return this.cache.get(rel);
|
|
73
|
+
const stat = this.stat(rel);
|
|
74
|
+
const value = stat?.isFile() && stat.size <= FILE_LIMIT ? readFileSync(this.abs(rel), 'utf8') : undefined;
|
|
75
|
+
this.cache.set(rel, value);
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
json(rel) {
|
|
79
|
+
const text = this.text(rel);
|
|
80
|
+
if (text === undefined)
|
|
81
|
+
return undefined;
|
|
82
|
+
try {
|
|
83
|
+
const value = JSON.parse(text);
|
|
84
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
85
|
+
? value
|
|
86
|
+
: undefined;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Каталог и его предки до корня включительно, от ближнего. */
|
|
93
|
+
ancestors(dir) {
|
|
94
|
+
const out = [];
|
|
95
|
+
let cursor = dir;
|
|
96
|
+
for (;;) {
|
|
97
|
+
out.push(cursor);
|
|
98
|
+
if (cursor === '.')
|
|
99
|
+
return out;
|
|
100
|
+
const up = dirname(cursor);
|
|
101
|
+
cursor = up === '' || up === '.' ? '.' : up;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Ближайший к `dir` каталог (до корня), где есть файл `name`. */
|
|
105
|
+
nearest(dir, name) {
|
|
106
|
+
return this.ancestors(dir).find((d) => this.isFile(join2(d, name)));
|
|
107
|
+
}
|
|
108
|
+
lockfilesIn(dir) {
|
|
109
|
+
return Object.keys(lockfiles).filter((f) => this.isFile(join2(dir, f)));
|
|
110
|
+
}
|
|
111
|
+
list(rel) {
|
|
112
|
+
if (!this.isDir(rel))
|
|
113
|
+
return [];
|
|
114
|
+
return readdirSync(this.abs(rel), { withFileTypes: true })
|
|
115
|
+
.filter((e) => e.isDirectory())
|
|
116
|
+
.map((e) => e.name)
|
|
117
|
+
.sort();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export const join2 = (dir, name) => (dir === '.' ? name : `${dir}/${name}`);
|
|
121
|
+
/** Скрипты package.json каталога. */
|
|
122
|
+
export function scriptsOf(m, dir) {
|
|
123
|
+
const pkg = m.json(join2(dir, 'package.json'));
|
|
124
|
+
if (!pkg)
|
|
125
|
+
return undefined;
|
|
126
|
+
const scripts = pkg.scripts;
|
|
127
|
+
return scripts && typeof scripts === 'object' ? scripts : {};
|
|
128
|
+
}
|
|
129
|
+
/** `packageManager` из package.json: `pnpm@10.12.3+sha512…` → { name, version }. */
|
|
130
|
+
export function packageManagerField(m, dir) {
|
|
131
|
+
const value = m.json(join2(dir, 'package.json'))?.packageManager;
|
|
132
|
+
if (typeof value !== 'string')
|
|
133
|
+
return undefined;
|
|
134
|
+
const match = /^(npm|pnpm|yarn|bun)@(\d+\.\d+\.\d+[^+]*)/.exec(value);
|
|
135
|
+
return match ? { name: match[1], version: match[2], raw: value } : { name: value, raw: value };
|
|
136
|
+
}
|
|
137
|
+
/** Пакеты workspace pnpm: `packages:` из pnpm-workspace.yaml (простые шаблоны `dir/*`). */
|
|
138
|
+
export function workspacePackages(m, workspaceRoot) {
|
|
139
|
+
const text = m.text(join2(workspaceRoot, 'pnpm-workspace.yaml'));
|
|
140
|
+
const patterns = [];
|
|
141
|
+
if (text) {
|
|
142
|
+
let inPackages = false;
|
|
143
|
+
for (const line of text.split('\n')) {
|
|
144
|
+
if (/^packages\s*:/.test(line)) {
|
|
145
|
+
inPackages = true;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (inPackages) {
|
|
149
|
+
const item = /^\s+-\s*['"]?([^'"#]+?)['"]?\s*(?:#.*)?$/.exec(line);
|
|
150
|
+
if (item)
|
|
151
|
+
patterns.push(item[1].trim());
|
|
152
|
+
else if (/^\S/.test(line))
|
|
153
|
+
inPackages = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
const workspaces = m.json(join2(workspaceRoot, 'package.json'))?.workspaces;
|
|
159
|
+
if (Array.isArray(workspaces))
|
|
160
|
+
patterns.push(...workspaces.filter((w) => typeof w === 'string'));
|
|
161
|
+
}
|
|
162
|
+
const dirs = new Set([workspaceRoot]);
|
|
163
|
+
for (const pattern of patterns) {
|
|
164
|
+
if (pattern.startsWith('!'))
|
|
165
|
+
continue;
|
|
166
|
+
const clean = pattern.replace(/\/$/, '');
|
|
167
|
+
if (clean.endsWith('/*') || clean.endsWith('/**')) {
|
|
168
|
+
const base = join2(workspaceRoot, clean.replace(/\/\*\*?$/, ''));
|
|
169
|
+
for (const child of m.list(base))
|
|
170
|
+
dirs.add(join2(base, child));
|
|
171
|
+
}
|
|
172
|
+
else
|
|
173
|
+
dirs.add(join2(workspaceRoot, clean));
|
|
174
|
+
}
|
|
175
|
+
return [...dirs]
|
|
176
|
+
.filter((d) => m.isFile(join2(d, 'package.json')))
|
|
177
|
+
.map((dir) => {
|
|
178
|
+
const pkg = m.json(join2(dir, 'package.json')) ?? {};
|
|
179
|
+
return {
|
|
180
|
+
dir,
|
|
181
|
+
name: typeof pkg.name === 'string' ? pkg.name : undefined,
|
|
182
|
+
scripts: scriptsOf(m, dir) ?? {},
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Задачи turbo.json (`tasks` v2 либо `pipeline` v1). */
|
|
187
|
+
export function turboTasks(m, dir) {
|
|
188
|
+
const turbo = m.json(join2(dir, 'turbo.json'));
|
|
189
|
+
if (!turbo)
|
|
190
|
+
return undefined;
|
|
191
|
+
const tasks = (turbo.tasks ?? turbo.pipeline);
|
|
192
|
+
return tasks && typeof tasks === 'object' ? Object.keys(tasks) : [];
|
|
193
|
+
}
|
|
194
|
+
/** go.mod: директивы `go`, `toolchain` и наличие require. */
|
|
195
|
+
export function goModule(m, dir) {
|
|
196
|
+
const text = m.text(join2(dir, 'go.mod'));
|
|
197
|
+
if (text === undefined)
|
|
198
|
+
return undefined;
|
|
199
|
+
return {
|
|
200
|
+
go: /^go\s+(\d+\.\d+(?:\.\d+)?)\s*$/m.exec(text)?.[1],
|
|
201
|
+
toolchain: /^toolchain\s+go(\d+\.\d+(?:\.\d+)?)\s*$/m.exec(text)?.[1],
|
|
202
|
+
requires: /^require\b/m.test(text),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/** Пакеты, закреплённые uv.lock (`name = "…"` записей `[[package]]`). */
|
|
206
|
+
export function uvLocked(m, dir) {
|
|
207
|
+
const text = m.text(join2(dir, 'uv.lock'));
|
|
208
|
+
if (text === undefined)
|
|
209
|
+
return undefined;
|
|
210
|
+
const names = new Map();
|
|
211
|
+
for (const block of text.split(/^\[\[package\]\]\s*$/m).slice(1)) {
|
|
212
|
+
const name = /^name\s*=\s*"([^"]+)"/m.exec(block)?.[1];
|
|
213
|
+
const version = /^version\s*=\s*"([^"]+)"/m.exec(block)?.[1];
|
|
214
|
+
if (name)
|
|
215
|
+
names.set(name.toLowerCase(), version ?? '');
|
|
216
|
+
}
|
|
217
|
+
return names;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Точная версия зависимости `name` корневого пакета каталога, закреплённая
|
|
221
|
+
* lockfile: `importers['.']` pnpm-lock.yaml либо `packages['node_modules/<name>']`
|
|
222
|
+
* package-lock.json. Нет закрепления — undefined.
|
|
223
|
+
*/
|
|
224
|
+
export function lockedVersion(m, dir, name) {
|
|
225
|
+
const pnpm = m.text(join2(dir, 'pnpm-lock.yaml'));
|
|
226
|
+
if (pnpm !== undefined) {
|
|
227
|
+
const importers = pnpm.split(/^importers:\s*$/m)[1]?.split(/^\S/m)[0] ?? '';
|
|
228
|
+
const root = /^ {2}\.:\s*$([\s\S]*?)(?=^ {2}\S|(?![\s\S]))/m.exec(importers)?.[1] ?? '';
|
|
229
|
+
const quoted = name.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
230
|
+
const entry = new RegExp(`^ {6}'?${quoted}'?:\\s*$\\n {8}specifier:[^\\n]*\\n {8}version: ([^\\s(]+)`, 'm').exec(root);
|
|
231
|
+
if (entry && /^\d+\.\d+\.\d+/.test(entry[1]))
|
|
232
|
+
return entry[1];
|
|
233
|
+
}
|
|
234
|
+
const npm = m.json(join2(dir, 'package-lock.json'));
|
|
235
|
+
const packages = npm?.packages;
|
|
236
|
+
const version = packages?.[`node_modules/${name}`]?.version;
|
|
237
|
+
return version && /^\d+\.\d+\.\d+/.test(version) ? version : undefined;
|
|
238
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Проба версии — единственный процесс, который запускает диагностика
|
|
3
|
+
* toolchain: `<tool> --version` (у go — `go version`, у собранного без метки
|
|
4
|
+
* версии Go-бинарника — `go version -m <файл>`, который только читает файл).
|
|
5
|
+
* Каталог пробы — нейтральный временный: менеджер пакетов не должен увидеть
|
|
6
|
+
* `packageManager` проекта и переключиться на другую версию.
|
|
7
|
+
*/
|
|
8
|
+
export interface ToolProbe {
|
|
9
|
+
(argv: string[], options: {
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
env: NodeJS.ProcessEnv;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
code: number;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
timedOut: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare const PROBE_TIMEOUT_MS = 10000;
|
|
20
|
+
export declare const defaultProbe: ToolProbe;
|
|
21
|
+
/** Исполняемый файл из PATH (как его нашёл бы spawn) или undefined. */
|
|
22
|
+
export declare function resolveExecutable(name: string, path: string | undefined): string | undefined;
|
|
23
|
+
/** Версия из вывода пробы: первое `1.2.3`/`1.2` (в том числе `go1.24.5`, `v24.3.0`). */
|
|
24
|
+
export declare function versionOf(output: string): string | undefined;
|
|
25
|
+
/** Версия модуля в build info Go-бинарника (`go version -m`). */
|
|
26
|
+
export declare function moduleVersionOf(output: string): string | undefined;
|
|
27
|
+
/** Требование версии: `1.12.3` — названные компоненты совпадают, `>=1.24` — не ниже. */
|
|
28
|
+
export declare function satisfies(version: string, required: string): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { accessSync, constants, statSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
4
|
+
import { command } from "../environment/process.js";
|
|
5
|
+
export const PROBE_TIMEOUT_MS = 10000;
|
|
6
|
+
export const defaultProbe = async (argv, options) => {
|
|
7
|
+
const r = await command(argv, tmpdir(), { timeoutMs: options.timeoutMs, env: options.env });
|
|
8
|
+
return { code: r.code, stdout: r.stdout, stderr: r.stderr, timedOut: r.timedOut };
|
|
9
|
+
};
|
|
10
|
+
/** Исполняемый файл из PATH (как его нашёл бы spawn) или undefined. */
|
|
11
|
+
export function resolveExecutable(name, path) {
|
|
12
|
+
if (isAbsolute(name))
|
|
13
|
+
return executable(name) ? name : undefined;
|
|
14
|
+
for (const dir of (path ?? '').split(delimiter).filter(Boolean)) {
|
|
15
|
+
const file = join(dir, name);
|
|
16
|
+
if (executable(file))
|
|
17
|
+
return file;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
function executable(file) {
|
|
22
|
+
try {
|
|
23
|
+
accessSync(file, constants.X_OK);
|
|
24
|
+
return statSync(file).isFile();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Версия из вывода пробы: первое `1.2.3`/`1.2` (в том числе `go1.24.5`, `v24.3.0`). */
|
|
31
|
+
export function versionOf(output) {
|
|
32
|
+
return /(?:^|[^\d.])v?(?:go)?(\d+\.\d+(?:\.\d+)?)/.exec(output)?.[1];
|
|
33
|
+
}
|
|
34
|
+
/** Версия модуля в build info Go-бинарника (`go version -m`). */
|
|
35
|
+
export function moduleVersionOf(output) {
|
|
36
|
+
return /^\s*mod\s+\S+\s+v(\d+\.\d+\.\d+)/m.exec(output)?.[1];
|
|
37
|
+
}
|
|
38
|
+
/** Требование версии: `1.12.3` — названные компоненты совпадают, `>=1.24` — не ниже. */
|
|
39
|
+
export function satisfies(version, required) {
|
|
40
|
+
const parse = (v) => v.split('.').map((n) => Number(n));
|
|
41
|
+
if (required.startsWith('>=')) {
|
|
42
|
+
const want = parse(required.slice(2));
|
|
43
|
+
const have = parse(version);
|
|
44
|
+
for (let i = 0; i < want.length; i++) {
|
|
45
|
+
const h = have[i] ?? 0;
|
|
46
|
+
if (h !== want[i])
|
|
47
|
+
return h > want[i];
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
const want = parse(required);
|
|
52
|
+
const have = parse(version);
|
|
53
|
+
return want.every((n, i) => have[i] === n);
|
|
54
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { gateSchema, repositoryPolicySchema } from '../contracts/index.ts';
|
|
3
|
+
type Gate = z.input<typeof gateSchema>;
|
|
4
|
+
type RepositoryInput = z.input<typeof repositoryPolicySchema>;
|
|
5
|
+
/**
|
|
6
|
+
* Рецепты установки зависимостей: точные locked-команды менеджера, каталог —
|
|
7
|
+
* собственным флагом инструмента (шаг `prepare` исполняется в корне
|
|
8
|
+
* worktree). Рецепт не меняет lockfile: несовместимый lockfile — отказ шага.
|
|
9
|
+
*/
|
|
10
|
+
export declare const installRecipes: {
|
|
11
|
+
readonly npm: (dir: string) => string[];
|
|
12
|
+
readonly pnpm: (dir: string) => string[];
|
|
13
|
+
readonly uv: (dir: string) => string[];
|
|
14
|
+
readonly go: (dir: string) => string[];
|
|
15
|
+
};
|
|
16
|
+
export type InstallRecipe = keyof typeof installRecipes;
|
|
17
|
+
export interface ToolchainComponent {
|
|
18
|
+
/** Каталог компонента в репозитории (`.` — корень); gates исполняются в нём. */
|
|
19
|
+
dir: string;
|
|
20
|
+
install?: InstallRecipe;
|
|
21
|
+
installTimeoutMs?: number;
|
|
22
|
+
/** Gates компонента; `cwd` по умолчанию — каталог компонента. */
|
|
23
|
+
gates: Gate[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Описание репозитория политики из компонентов: у каждого свой рецепт
|
|
27
|
+
* установки, каталог и gates; один Git root — одна запись репозитория.
|
|
28
|
+
* Никакого «типа проекта»: Node-компонент на pnpm и Go-компонент — просто два
|
|
29
|
+
* компонента с разными рецептами. Для node-компонента выбор менеджера
|
|
30
|
+
* записывается явно (`toolchain.packageManagers`).
|
|
31
|
+
*/
|
|
32
|
+
export declare function composeRepository(own: Pick<RepositoryInput, 'id' | 'path' | 'targetRef'> & Partial<RepositoryInput>, components: ToolchainComponent[], tools?: Record<string, string>): RepositoryInput;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Рецепты установки зависимостей: точные locked-команды менеджера, каталог —
|
|
3
|
+
* собственным флагом инструмента (шаг `prepare` исполняется в корне
|
|
4
|
+
* worktree). Рецепт не меняет lockfile: несовместимый lockfile — отказ шага.
|
|
5
|
+
*/
|
|
6
|
+
export const installRecipes = {
|
|
7
|
+
npm: (dir) => (dir === '.' ? ['npm', 'ci'] : ['npm', '--prefix', dir, 'ci']),
|
|
8
|
+
pnpm: (dir) => [
|
|
9
|
+
'pnpm',
|
|
10
|
+
...(dir === '.' ? [] : ['--dir', dir]),
|
|
11
|
+
'install',
|
|
12
|
+
'--frozen-lockfile',
|
|
13
|
+
],
|
|
14
|
+
uv: (dir) => ['uv', 'sync', '--locked', ...(dir === '.' ? [] : ['--directory', dir])],
|
|
15
|
+
go: (dir) => ['go', ...(dir === '.' ? [] : ['-C', dir]), 'mod', 'download'],
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Описание репозитория политики из компонентов: у каждого свой рецепт
|
|
19
|
+
* установки, каталог и gates; один Git root — одна запись репозитория.
|
|
20
|
+
* Никакого «типа проекта»: Node-компонент на pnpm и Go-компонент — просто два
|
|
21
|
+
* компонента с разными рецептами. Для node-компонента выбор менеджера
|
|
22
|
+
* записывается явно (`toolchain.packageManagers`).
|
|
23
|
+
*/
|
|
24
|
+
export function composeRepository(own, components, tools = {}) {
|
|
25
|
+
const prepare = [];
|
|
26
|
+
const gates = [];
|
|
27
|
+
const packageManagers = {};
|
|
28
|
+
for (const c of components) {
|
|
29
|
+
if (c.install) {
|
|
30
|
+
const id = (c.dir === '.' ? 'root' : c.dir.replace(/[^A-Za-z0-9_-]/g, '-')) + '-install';
|
|
31
|
+
prepare.push({
|
|
32
|
+
id,
|
|
33
|
+
command: [...installRecipes[c.install](c.dir)],
|
|
34
|
+
timeoutMs: c.installTimeoutMs ?? 600000,
|
|
35
|
+
});
|
|
36
|
+
if (c.install === 'npm' || c.install === 'pnpm')
|
|
37
|
+
packageManagers[c.dir] = c.install;
|
|
38
|
+
}
|
|
39
|
+
for (const g of c.gates)
|
|
40
|
+
gates.push({ ...g, ...(g.cwd === undefined && c.dir !== '.' ? { cwd: c.dir } : {}) });
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...own,
|
|
44
|
+
prepare: [...(own.prepare ?? []), ...prepare],
|
|
45
|
+
gates: [...(own.gates ?? []), ...gates],
|
|
46
|
+
toolchain: {
|
|
47
|
+
tools: { ...own.toolchain?.tools, ...tools },
|
|
48
|
+
packageManagers: { ...own.toolchain?.packageManagers, ...packageManagers },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -229,6 +229,20 @@ export declare const receiptTrust: {
|
|
|
229
229
|
readonly 'evidence[].reviewer.runtime': readonly ["expected", "reviewer роли в политике получателя"];
|
|
230
230
|
readonly 'evidence[].reviewer.model': readonly ["informative", "модель reviewer"];
|
|
231
231
|
readonly 'evidence[].reviewer.sameVendor': readonly ["consistency", "вендоры writer и reviewer"];
|
|
232
|
+
readonly 'evidence[].review': readonly ["informative", "раунд протокола ревью кода"];
|
|
233
|
+
readonly 'evidence[].review.round': readonly ["informative", "раунд протокола ревью кода"];
|
|
234
|
+
readonly 'evidence[].review.call': readonly ["informative", "раунд протокола ревью кода"];
|
|
235
|
+
readonly 'evidence[].review.protocol': readonly ["informative", "раунд протокола ревью кода"];
|
|
236
|
+
readonly 'evidence[].review.issues': readonly ["informative", "раунд протокола ревью кода"];
|
|
237
|
+
readonly 'evidence[].review.issues[]': readonly ["informative", "раунд протокола ревью кода"];
|
|
238
|
+
readonly 'evidence[].review.issues[].path': readonly ["informative", "раунд протокола ревью кода"];
|
|
239
|
+
readonly 'evidence[].review.issues[].reason': readonly ["informative", "раунд протокола ревью кода"];
|
|
240
|
+
readonly 'evidence[].review.issues[].message': readonly ["informative", "раунд протокола ревью кода"];
|
|
241
|
+
readonly 'evidence[].review.transitions': readonly ["informative", "раунд протокола ревью кода"];
|
|
242
|
+
readonly 'evidence[].review.transitions[]': readonly ["informative", "раунд протокола ревью кода"];
|
|
243
|
+
readonly 'evidence[].review.transitions[].id': readonly ["informative", "раунд протокола ревью кода"];
|
|
244
|
+
readonly 'evidence[].review.transitions[].from': readonly ["informative", "раунд протокола ревью кода"];
|
|
245
|
+
readonly 'evidence[].review.transitions[].to': readonly ["informative", "раунд протокола ревью кода"];
|
|
232
246
|
readonly findings: readonly ["informative", "сводка находок может относиться к отклонённым раундам"];
|
|
233
247
|
readonly 'findings[]': readonly ["informative", "сводка находок"];
|
|
234
248
|
readonly 'findings[].severity': readonly ["informative", "сводка находок"];
|
|
@@ -236,6 +250,8 @@ export declare const receiptTrust: {
|
|
|
236
250
|
readonly 'findings[].path': readonly ["informative", "сводка находок"];
|
|
237
251
|
readonly 'findings[].line': readonly ["informative", "сводка находок"];
|
|
238
252
|
readonly 'findings[].rule': readonly ["informative", "сводка находок"];
|
|
253
|
+
readonly 'findings[].id': readonly ["informative", "сводка находок"];
|
|
254
|
+
readonly 'findings[].status': readonly ["informative", "сводка находок"];
|
|
239
255
|
readonly limitations: readonly ["informative", "чего результат не доказывает"];
|
|
240
256
|
readonly 'limitations[]': readonly ["informative", "чего результат не доказывает"];
|
|
241
257
|
readonly finishedAt: readonly ["informative", "время окончания"];
|
|
@@ -257,6 +257,20 @@ export const receiptTrust = {
|
|
|
257
257
|
'evidence[].reviewer.runtime': ['expected', 'reviewer роли в политике получателя'],
|
|
258
258
|
'evidence[].reviewer.model': ['informative', 'модель reviewer'],
|
|
259
259
|
'evidence[].reviewer.sameVendor': ['consistency', 'вендоры writer и reviewer'],
|
|
260
|
+
'evidence[].review': ['informative', 'раунд протокола ревью кода'],
|
|
261
|
+
'evidence[].review.round': ['informative', 'раунд протокола ревью кода'],
|
|
262
|
+
'evidence[].review.call': ['informative', 'раунд протокола ревью кода'],
|
|
263
|
+
'evidence[].review.protocol': ['informative', 'раунд протокола ревью кода'],
|
|
264
|
+
'evidence[].review.issues': ['informative', 'раунд протокола ревью кода'],
|
|
265
|
+
'evidence[].review.issues[]': ['informative', 'раунд протокола ревью кода'],
|
|
266
|
+
'evidence[].review.issues[].path': ['informative', 'раунд протокола ревью кода'],
|
|
267
|
+
'evidence[].review.issues[].reason': ['informative', 'раунд протокола ревью кода'],
|
|
268
|
+
'evidence[].review.issues[].message': ['informative', 'раунд протокола ревью кода'],
|
|
269
|
+
'evidence[].review.transitions': ['informative', 'раунд протокола ревью кода'],
|
|
270
|
+
'evidence[].review.transitions[]': ['informative', 'раунд протокола ревью кода'],
|
|
271
|
+
'evidence[].review.transitions[].id': ['informative', 'раунд протокола ревью кода'],
|
|
272
|
+
'evidence[].review.transitions[].from': ['informative', 'раунд протокола ревью кода'],
|
|
273
|
+
'evidence[].review.transitions[].to': ['informative', 'раунд протокола ревью кода'],
|
|
260
274
|
findings: ['informative', 'сводка находок может относиться к отклонённым раундам'],
|
|
261
275
|
'findings[]': ['informative', 'сводка находок'],
|
|
262
276
|
'findings[].severity': ['informative', 'сводка находок'],
|
|
@@ -264,6 +278,8 @@ export const receiptTrust = {
|
|
|
264
278
|
'findings[].path': ['informative', 'сводка находок'],
|
|
265
279
|
'findings[].line': ['informative', 'сводка находок'],
|
|
266
280
|
'findings[].rule': ['informative', 'сводка находок'],
|
|
281
|
+
'findings[].id': ['informative', 'сводка находок'],
|
|
282
|
+
'findings[].status': ['informative', 'сводка находок'],
|
|
267
283
|
limitations: ['informative', 'чего результат не доказывает'],
|
|
268
284
|
'limitations[]': ['informative', 'чего результат не доказывает'],
|
|
269
285
|
finishedAt: ['informative', 'время окончания'],
|
|
@@ -16,6 +16,16 @@ export declare function portableTest<T extends {
|
|
|
16
16
|
* Изменённое так имя не совпадёт с названным критерием, и подтверждения
|
|
17
17
|
* не будет: для секрета в имени теста это правильный исход.
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* Значение атрибута XML: парсер работает без обработки сущностей (DOCTYPE
|
|
21
|
+
* запрещён, внешних сущностей нет), поэтому предопределённые сущности и
|
|
22
|
+
* числовые ссылки раскрываются здесь — один раз, без рекурсии. Иначе testcase
|
|
23
|
+
* `a["b"]` сохранялся как `a["b"]` и не совпадал с названным
|
|
24
|
+
* testId требования или критерия. Репортер JUnit Node.js экранирует `"`
|
|
25
|
+
* дважды (`&quot;`); оставшийся после раскрытия `"` поэтому тоже
|
|
26
|
+
* читается как `"`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function xmlAttribute(value: string): string;
|
|
19
29
|
export declare function junitSummary(xml: string, redact?: (value: string) => string): {
|
|
20
30
|
tests: number;
|
|
21
31
|
failures: number;
|
|
@@ -69,14 +79,36 @@ export declare function runCheck(h: {
|
|
|
69
79
|
readable: string[];
|
|
70
80
|
controller: string[];
|
|
71
81
|
settingsDir: string;
|
|
82
|
+
/**
|
|
83
|
+
* Запуск проверки подтверждён: без песочницы — порождён её процесс, в
|
|
84
|
+
* песочнице — обёртка создала метку старта перед exec. Запуск самой
|
|
85
|
+
* обёртки песочницы запуском проверки не считается. Вызывается не более
|
|
86
|
+
* одного раза, в том числе когда проверка затем завершилась исключением.
|
|
87
|
+
*/
|
|
88
|
+
onStart?: () => void;
|
|
72
89
|
}): Promise<{
|
|
73
90
|
attempts: number;
|
|
91
|
+
started: boolean;
|
|
74
92
|
code: number;
|
|
75
93
|
stdout: string;
|
|
76
94
|
stderr: string;
|
|
77
95
|
timedOut: boolean;
|
|
78
96
|
diagnostics: import("../contracts/execution.ts").RuntimeDiagnostics;
|
|
79
97
|
}>;
|
|
98
|
+
/** Исход проверки, завершившейся до исключения (например, отказа уборки), если он есть. */
|
|
99
|
+
export declare function completedCheck(error: unknown): {
|
|
100
|
+
attempts: number;
|
|
101
|
+
started: boolean;
|
|
102
|
+
code: number;
|
|
103
|
+
stdout: string;
|
|
104
|
+
stderr: string;
|
|
105
|
+
timedOut: boolean;
|
|
106
|
+
diagnostics: import("../contracts/execution.ts").RuntimeDiagnostics;
|
|
107
|
+
} | undefined;
|
|
108
|
+
/** Уборка каталогов проверки; тест подменяет её, чтобы проверить отказ после исхода. */
|
|
109
|
+
export declare const checkCleanup: {
|
|
110
|
+
remove: (dir: string) => Promise<void>;
|
|
111
|
+
};
|
|
80
112
|
/** Проверка механизма песочницы; тест подменяет её, чтобы проверить отказ. */
|
|
81
113
|
export declare const isolationBackend: {
|
|
82
114
|
check: typeof isolationSupport;
|
|
@@ -91,6 +123,12 @@ export declare const isolationBackend: {
|
|
|
91
123
|
* тело ответа песочницы) и URL хоста, которого нет в `isolation.domains`.
|
|
92
124
|
*/
|
|
93
125
|
export declare function sandboxBlockedHosts(output: string, allowed: string[]): string[];
|
|
126
|
+
/**
|
|
127
|
+
* Окружение свежести test gate: те же переменные у gate движка и у gate,
|
|
128
|
+
* запущенного проверкой reviewer, — иначе probe мог бы прочитать cache там,
|
|
129
|
+
* где gate исполняет тесты заново.
|
|
130
|
+
*/
|
|
131
|
+
export declare function freshnessEnv(kind: GatePolicy['kind'], env: NodeJS.ProcessEnv): Record<string, string>;
|
|
94
132
|
/** Что нужно для запуска gate попытки. */
|
|
95
133
|
export interface GateRun {
|
|
96
134
|
policy: Pick<ExecutionPolicy, 'isolation'>;
|